ry_app/package_a/device/device.vue

432 lines
9.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="container">
<!-- 设备列表 -->
<scroll-view scroll-y class="device-list">
<view class="empty-state" v-if="deviceList.length === 0 && !loading">
<text class="empty-text">暂无设备</text>
</view>
<view class="device-item" v-for="(item, index) in deviceList" :key="index">
<view class="device-header">
<text class="device-name">{{ item.deviceName }}</text>
<text class="device-id">ID: {{ item.deviceCode || item.id }}</text>
</view>
<view class="device-info">
<view class="info-row">
<text class="label">设备品牌:</text>
<text class="value">{{ item.deviceBrand || '-' }}</text>
</view>
<view class="info-row">
<text class="label">设备型号:</text>
<text class="value">{{ getDeviceModelLabel(item.deviceModel) || '-' }}</text>
</view>
</view>
</view>
<view v-if="loading" class="loading">
<text>加载中...</text>
</view>
</scroll-view>
<!-- 悬浮新增按钮 -->
<view class="fab-button" @click="openAddDialog">
<uni-icons type="plus" size="24" color="#fff"></uni-icons>
<!-- <text class="fab-text">新增</text>-->
</view>
<!-- 新增弹窗 -->
<uni-popup ref="addPopup" type="center" :mask-click="false">
<view class="dialog-content">
<view class="dialog-title">新增设备</view>
<scroll-view scroll-y class="form-container">
<view class="form-item">
<text class="form-label"><text class="required">*</text>设备编号</text>
<input class="form-input" v-model="form.deviceCode" placeholder="请输入设备编号/ID" />
</view>
<view class="form-item">
<text class="form-label"><text class="required">*</text>设备名称</text>
<input class="form-input" v-model="form.deviceName" placeholder="请输入设备名称" />
</view>
<view class="form-item">
<text class="form-label"><text class="required">*</text>设备类型</text>
<picker class="form-picker" @change="onTypeChange" :value="typeIndex" :range="deviceTypes" range-key="label">
<view class="picker-value">{{ typeIndex !== -1 ? deviceTypes[typeIndex].label : '请选择设备类型' }}</view>
</picker>
</view>
<view class="form-item">
<text class="form-label">设备品牌</text>
<input class="form-input" v-model="form.deviceBrand" placeholder="请输入设备品牌" />
</view>
<view class="form-item">
<text class="form-label">设备型号</text>
<picker class="form-picker" @change="onModelChange" :value="modelIndex" :range="deviceModels" range-key="label">
<view class="picker-value">{{ modelIndex !== -1 ? deviceModels[modelIndex].label : '请选择设备型号' }}</view>
</picker>
</view>
<view class="form-item">
<text class="form-label">通道</text>
<input class="form-input" v-model="form.channel" placeholder="请输入通道" />
</view>
<view class="form-item">
<text class="form-label">备注</text>
<textarea class="form-textarea" v-model="form.remark" placeholder="请输入备注信息"></textarea>
</view>
</scroll-view>
<view class="dialog-footer">
<button class="btn btn-cancel" @click="closeAddDialog">取消</button>
<button class="btn btn-confirm" @click="submitAdd" :loading="submitting"></button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import { addDevice, getDeviceList } from '@/api/device'
import { getStoreId } from '@/utils/auth'
export default {
data() {
return {
storeId: null,
deviceList: [],
loading: false,
submitting: false,
// 设备类型1-摄像头 2-扫码枪 3-打印机 4-收银机
deviceTypes: [
{ label: '摄像头', value: 1 },
{ label: '扫码枪', value: 2 },
{ label: '打印机', value: 3 },
{ label: '收银机', value: 4 }
],
typeIndex: -1,
deviceModels: [
{ label: '单机(IPC)', value: '1' },
{ label: '主机(NVR)', value: '2' }
],
modelIndex: -1,
form: {
deviceCode: '',
deviceName: '',
deviceType: '',
deviceBrand: '',
deviceModel: '',
channel: '',
remark: ''
}
}
},
onLoad() {
this.storeId = getStoreId();
if (!this.storeId) {
uni.showToast({
title: '未获取到门店信息,请重新登录',
icon: 'none'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
return;
}
this.fetchDeviceList();
},
methods: {
goBack() {
uni.navigateBack();
},
getDeviceModelLabel(modelValue) {
if (!modelValue && modelValue !== 0) return '';
const match = this.deviceModels.find(m => m.value == modelValue);
return match ? match.label : modelValue;
},
fetchDeviceList() {
this.loading = true;
getDeviceList(this.storeId).then(res => {
if (res.code === 200) {
this.deviceList = res.data || [];
} else {
uni.showToast({
title: res.msg || '获取设备列表失败',
icon: 'none'
});
}
}).catch(err => {
console.error(err);
uni.showToast({
title: '获取失败,请重试',
icon: 'none'
});
}).finally(() => {
this.loading = false;
});
},
openAddDialog() {
// 重置表单
this.form = {
deviceCode: '',
deviceName: '',
deviceType: '',
deviceBrand: '',
deviceModel: '',
channel: '',
remark: ''
};
this.typeIndex = -1;
this.modelIndex = -1;
this.$refs.addPopup.open();
},
closeAddDialog() {
this.$refs.addPopup.close();
},
onTypeChange(e) {
this.typeIndex = e.detail.value;
this.form.deviceType = this.deviceTypes[this.typeIndex].value;
},
onModelChange(e) {
this.modelIndex = e.detail.value;
this.form.deviceModel = this.deviceModels[this.modelIndex].value;
},
submitAdd() {
if (!this.form.deviceCode) {
return uni.showToast({ title: '请输入设备编号', icon: 'none' });
}
if (!this.form.deviceName) {
return uni.showToast({ title: '请输入设备名称', icon: 'none' });
}
if (!this.form.deviceType) {
return uni.showToast({ title: '请选择设备类型', icon: 'none' });
}
this.submitting = true;
const submitData = {
...this.form,
storeId: this.storeId
};
addDevice(submitData).then(res => {
if (res.code === 200) {
uni.showToast({ title: '添加成功', icon: 'success' });
this.closeAddDialog();
this.fetchDeviceList();
} else {
uni.showToast({ title: res.msg || '添加失败', icon: 'none' });
}
}).catch(err => {
console.error(err);
uni.showToast({ title: '网络错误,添加失败', icon: 'none' });
}).finally(() => {
this.submitting = false;
});
}
}
}
</script>
<style scoped lang="scss">
.container {
min-height: 100vh;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
position: relative;
}
.device-list {
flex: 1;
padding: 20rpx;
}
.device-item {
background-color: #fff;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 20rpx;
}
.device-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
padding-bottom: 16rpx;
border-bottom: 1rpx solid #eee;
}
.device-name {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.device-id {
font-size: 24rpx;
color: #999;
}
.device-info {
display: flex;
flex-direction: column;
gap: 12rpx;
}
.info-row {
display: flex;
font-size: 26rpx;
}
.info-row .label {
color: #666;
width: 140rpx;
}
.info-row .value {
color: #333;
flex: 1;
}
.empty-state {
padding: 100rpx 0;
text-align: center;
}
.empty-text {
color: #999;
font-size: 28rpx;
}
.loading {
text-align: center;
padding: 30rpx;
color: #999;
font-size: 28rpx;
}
/* 悬浮按钮样式 */
.fab-button {
position: fixed;
right: 40rpx;
bottom: 100rpx;
width: 60rpx;
height: 60rpx;
background-color: #e62318;
border-radius: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 12rpx rgba(230, 35, 24, 0.4);
z-index: 99;
}
.fab-text {
color: #fff;
font-size: 22rpx;
margin-top: 4rpx;
}
/* 弹窗样式 */
.dialog-content {
background-color: #fff;
width: 650rpx;
border-radius: 16rpx;
overflow: hidden;
display: flex;
flex-direction: column;
max-height: 80vh;
}
.dialog-title {
font-size: 32rpx;
font-weight: bold;
text-align: center;
padding: 30rpx 0;
border-bottom: 1px solid #eee;
}
.form-container {
padding: 30rpx;
max-height: 60vh;
}
.form-item {
margin-bottom: 24rpx;
}
.form-label {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 12rpx;
}
.required {
color: #e62318;
margin-right: 4rpx;
}
.form-input, .form-picker {
width: 100%;
height: 72rpx;
border: 1px solid #ddd;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 26rpx;
box-sizing: border-box;
}
.form-picker {
display: flex;
align-items: center;
}
.picker-value {
flex: 1;
}
.form-textarea {
width: 100%;
height: 160rpx;
border: 1px solid #ddd;
border-radius: 8rpx;
padding: 20rpx;
font-size: 26rpx;
box-sizing: border-box;
}
.dialog-footer {
display: flex;
border-top: 1px solid #eee;
}
.btn {
flex: 1;
height: 88rpx;
line-height: 88rpx;
text-align: center;
font-size: 30rpx;
border: none;
border-radius: 0;
background-color: #fff;
}
.btn::after {
border: none;
}
.btn-cancel {
color: #666;
border-right: 1px solid #eee;
}
.btn-confirm {
color: #e62318;
}
</style>