570 lines
12 KiB
Vue
570 lines
12 KiB
Vue
|
|
<template>
|
|||
|
|
<view class="page">
|
|||
|
|
<base-background />
|
|||
|
|
<!-- 使用自定义导航栏组件 -->
|
|||
|
|
<custom-navbar title="添加牌位" />
|
|||
|
|
|
|||
|
|
<view class="content">
|
|||
|
|
<!-- 扫码获取SN和MAC -->
|
|||
|
|
<view class="scan-section">
|
|||
|
|
<view class="section-title">设备信息</view>
|
|||
|
|
<view class="scan-container">
|
|||
|
|
<view class="scan-btn" @click="handleScanCode">
|
|||
|
|
<view class="qrcode-icon">
|
|||
|
|
<image
|
|||
|
|
mode="aspectFit"
|
|||
|
|
src="https://api.ccttiot.com/smartmeter/img/static/uy7BNwAMIKwvstqFnRhs"
|
|||
|
|
/>
|
|||
|
|
</view>
|
|||
|
|
<view class="scan-text">扫码获取设备信息</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 显示SN和MAC信息 -->
|
|||
|
|
<view v-if="deviceInfo.sn || deviceInfo.mac" class="device-info">
|
|||
|
|
<view v-if="deviceInfo.name" class="info-item">
|
|||
|
|
<view class="info-label">设备名称:</view>
|
|||
|
|
<view class="info-value">{{ deviceInfo.name }}</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 牌位信息表单 -->
|
|||
|
|
<view class="form-section">
|
|||
|
|
<view class="section-title">牌位信息</view>
|
|||
|
|
<view class="form-container">
|
|||
|
|
<!-- 牌位编号 -->
|
|||
|
|
<view class="form-item">
|
|||
|
|
<view class="label"
|
|||
|
|
>牌位编号
|
|||
|
|
<text class="required">*</text>
|
|||
|
|
</view>
|
|||
|
|
<input
|
|||
|
|
v-model="formData.code"
|
|||
|
|
class="input"
|
|||
|
|
maxlength="20"
|
|||
|
|
placeholder="请输入牌位编号"
|
|||
|
|
/>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 区域选择 -->
|
|||
|
|
<view class="form-item">
|
|||
|
|
<view class="label"
|
|||
|
|
>所属区域
|
|||
|
|
<text class="required">*</text>
|
|||
|
|
</view>
|
|||
|
|
<picker
|
|||
|
|
:range="regionOptions"
|
|||
|
|
:value="regionIndex"
|
|||
|
|
class="picker"
|
|||
|
|
@change="onRegionChange"
|
|||
|
|
>
|
|||
|
|
<view class="picker-text">
|
|||
|
|
{{ regionOptions[regionIndex] || "请选择区域" }}
|
|||
|
|
</view>
|
|||
|
|
</picker>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 操作按钮 -->
|
|||
|
|
<view class="button-container">
|
|||
|
|
<view class="btn cancel-btn" @click="handleCancel">取消</view>
|
|||
|
|
<view
|
|||
|
|
:class="{ disabled: !canSubmit }"
|
|||
|
|
class="btn confirm-btn"
|
|||
|
|
@click="handleConfirm"
|
|||
|
|
>确认添加
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
export default {
|
|||
|
|
data() {
|
|||
|
|
return {
|
|||
|
|
// 设备信息
|
|||
|
|
deviceInfo: {
|
|||
|
|
sn: "",
|
|||
|
|
mac: "",
|
|||
|
|
name: "",
|
|||
|
|
id: "",
|
|||
|
|
},
|
|||
|
|
// 表单数据
|
|||
|
|
formData: {
|
|||
|
|
code: "",
|
|||
|
|
name: "",
|
|||
|
|
regionId: "",
|
|||
|
|
orderNum: "",
|
|||
|
|
remark: "",
|
|||
|
|
},
|
|||
|
|
// 区域选项
|
|||
|
|
regionOptions: ["A区", "B区", "C区", "D区"],
|
|||
|
|
regionIndex: -1,
|
|||
|
|
// 加载状态
|
|||
|
|
loading: false,
|
|||
|
|
};
|
|||
|
|
},
|
|||
|
|
computed: {
|
|||
|
|
// 是否可以提交
|
|||
|
|
canSubmit() {
|
|||
|
|
return (
|
|||
|
|
this.deviceInfo.sn &&
|
|||
|
|
this.deviceInfo.mac &&
|
|||
|
|
this.formData.code.trim() &&
|
|||
|
|
this.formData.regionId
|
|||
|
|
);
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
methods: {
|
|||
|
|
// 扫码获取SN
|
|||
|
|
async handleScanCode() {
|
|||
|
|
try {
|
|||
|
|
uni.showLoading({ title: "扫码中...", mask: true });
|
|||
|
|
|
|||
|
|
// 调用扫码功能
|
|||
|
|
const res = await new Promise((resolve, reject) => {
|
|||
|
|
uni.scanCode({
|
|||
|
|
onlyFromCamera: true,
|
|||
|
|
scanType: ["qrCode"],
|
|||
|
|
success: (result) => {
|
|||
|
|
resolve(result);
|
|||
|
|
},
|
|||
|
|
fail: (error) => {
|
|||
|
|
reject(error);
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
uni.hideLoading();
|
|||
|
|
|
|||
|
|
if (res.result) {
|
|||
|
|
// 解析二维码内容,提取SN参数
|
|||
|
|
let sn = null;
|
|||
|
|
let queryParams = res.result.split("?")[1];
|
|||
|
|
if (queryParams) {
|
|||
|
|
let params = queryParams.split("&");
|
|||
|
|
params.forEach((param) => {
|
|||
|
|
let [key, value] = param.split("=");
|
|||
|
|
if (key === "s") {
|
|||
|
|
sn = value;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果没有从URL参数中获取到SN,尝试直接使用结果
|
|||
|
|
if (!sn) {
|
|||
|
|
sn = res.result.trim();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log("扫码获取到SN:", sn);
|
|||
|
|
console.log("原始扫码结果:", res.result);
|
|||
|
|
|
|||
|
|
if (sn) {
|
|||
|
|
// 通过SN获取设备信息
|
|||
|
|
await this.getDeviceInfoBySn(sn);
|
|||
|
|
} else {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: "未找到有效的SN码",
|
|||
|
|
icon: "none",
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
uni.hideLoading();
|
|||
|
|
console.error("扫码失败:", error);
|
|||
|
|
|
|||
|
|
if (error.errMsg && error.errMsg.includes("cancel")) {
|
|||
|
|
// 用户取消扫码,不显示错误提示
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
uni.showToast({
|
|||
|
|
title: "扫码失败,请重试",
|
|||
|
|
icon: "none",
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 通过SN获取设备信息
|
|||
|
|
async getDeviceInfoBySn(sn) {
|
|||
|
|
try {
|
|||
|
|
uni.showLoading({ title: "获取设备信息...", mask: true });
|
|||
|
|
|
|||
|
|
// 调用API获取设备信息
|
|||
|
|
const res = await this.$request.get("/bst/memorial/getBySn", {
|
|||
|
|
sn: sn,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
uni.hideLoading();
|
|||
|
|
|
|||
|
|
console.log("@@@@@@@@@@@@@@@@@@@@", res);
|
|||
|
|
|
|||
|
|
if (res) {
|
|||
|
|
const deviceData = res;
|
|||
|
|
this.deviceInfo = {
|
|||
|
|
sn: deviceData.sn || sn,
|
|||
|
|
mac: deviceData.mac || "",
|
|||
|
|
name: deviceData.name || "",
|
|||
|
|
id: deviceData.id || "",
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
uni.showToast({
|
|||
|
|
title: "设备信息获取成功",
|
|||
|
|
icon: "success",
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log("设备信息:", this.deviceInfo);
|
|||
|
|
} else {
|
|||
|
|
throw new Error(res?.msg || "获取设备信息失败");
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
uni.hideLoading();
|
|||
|
|
console.error("获取设备信息失败:", error);
|
|||
|
|
|
|||
|
|
uni.showToast({
|
|||
|
|
title: error.message || "获取设备信息失败",
|
|||
|
|
icon: "none",
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 区域选择
|
|||
|
|
onRegionChange(e) {
|
|||
|
|
this.regionIndex = e.detail.value;
|
|||
|
|
this.formData.regionId = this.regionOptions[this.regionIndex];
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 取消操作
|
|||
|
|
handleCancel() {
|
|||
|
|
uni.navigateBack();
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 确认添加
|
|||
|
|
async handleConfirm() {
|
|||
|
|
if (!this.canSubmit) {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: "请完善必填信息",
|
|||
|
|
icon: "none",
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 表单验证
|
|||
|
|
if (!this.validateForm()) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
uni.showLoading({ title: "添加中...", mask: true });
|
|||
|
|
|
|||
|
|
// 构建请求数据
|
|||
|
|
const requestData = {
|
|||
|
|
id: this.deviceInfo.id, // 使用设备ID
|
|||
|
|
code: this.formData.code,
|
|||
|
|
name: this.formData.name,
|
|||
|
|
regionId: this.getRegionId(this.formData.regionId),
|
|||
|
|
orderNum: this.formData.orderNum || "1",
|
|||
|
|
sn: this.deviceInfo.sn,
|
|||
|
|
mac: this.deviceInfo.mac,
|
|||
|
|
remark: this.formData.remark,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
console.log("提交数据:", requestData);
|
|||
|
|
|
|||
|
|
// 调用API添加牌位
|
|||
|
|
const res = await this.$request.put("/bst/memorial", requestData);
|
|||
|
|
|
|||
|
|
uni.hideLoading();
|
|||
|
|
|
|||
|
|
if (res && (res.code === 200 || res.status === 200)) {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: "添加成功",
|
|||
|
|
icon: "success",
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 延迟返回上一页
|
|||
|
|
setTimeout(() => {
|
|||
|
|
uni.navigateBack();
|
|||
|
|
}, 1500);
|
|||
|
|
} else {
|
|||
|
|
throw new Error(res?.msg || "添加失败");
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
uni.hideLoading();
|
|||
|
|
console.error("添加牌位失败:", error);
|
|||
|
|
uni.showToast({
|
|||
|
|
title: error.message || "添加失败,请重试",
|
|||
|
|
icon: "none",
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 表单验证
|
|||
|
|
validateForm() {
|
|||
|
|
if (!this.formData.code.trim()) {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: "请输入牌位编号",
|
|||
|
|
icon: "none",
|
|||
|
|
});
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!this.formData.regionId) {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: "请选择所属区域",
|
|||
|
|
icon: "none",
|
|||
|
|
});
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 获取区域ID(根据实际业务逻辑调整)
|
|||
|
|
getRegionId(regionName) {
|
|||
|
|
const regionMap = {
|
|||
|
|
A区: "1",
|
|||
|
|
B区: "2",
|
|||
|
|
C区: "3",
|
|||
|
|
D区: "4",
|
|||
|
|
};
|
|||
|
|
return regionMap[regionName] || "1";
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
.page {
|
|||
|
|
width: 100%;
|
|||
|
|
min-height: 100vh;
|
|||
|
|
background: #f5f5f5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.content {
|
|||
|
|
padding: 20rpx;
|
|||
|
|
padding-bottom: 120rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.section-title {
|
|||
|
|
font-size: 32rpx;
|
|||
|
|
font-weight: bold;
|
|||
|
|
color: #333;
|
|||
|
|
margin-bottom: 20rpx;
|
|||
|
|
padding-left: 10rpx;
|
|||
|
|
border-left: 6rpx solid #4a90e2;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.scan-section {
|
|||
|
|
background: #fff;
|
|||
|
|
border-radius: 16rpx;
|
|||
|
|
padding: 30rpx;
|
|||
|
|
margin-bottom: 30rpx;
|
|||
|
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.scan-container {
|
|||
|
|
margin-bottom: 30rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.scan-btn {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
height: 200rpx;
|
|||
|
|
border: 3rpx dashed #4a90e2;
|
|||
|
|
border-radius: 16rpx;
|
|||
|
|
background: #f8fbff;
|
|||
|
|
transition: all 0.3s ease;
|
|||
|
|
|
|||
|
|
&:active {
|
|||
|
|
background: #e8f4ff;
|
|||
|
|
transform: scale(0.98);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.qrcode-icon {
|
|||
|
|
width: 60rpx;
|
|||
|
|
height: 60rpx;
|
|||
|
|
margin-bottom: 10rpx;
|
|||
|
|
|
|||
|
|
image {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.scan-text {
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
color: #4a90e2;
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.device-info {
|
|||
|
|
background: #f8f9fa;
|
|||
|
|
border-radius: 12rpx;
|
|||
|
|
padding: 20rpx;
|
|||
|
|
border: 2rpx solid #e9ecef;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.info-item {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
margin-bottom: 15rpx;
|
|||
|
|
|
|||
|
|
&:last-child {
|
|||
|
|
margin-bottom: 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.info-label {
|
|||
|
|
font-size: 26rpx;
|
|||
|
|
color: #666;
|
|||
|
|
width: 140rpx;
|
|||
|
|
flex-shrink: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.info-value {
|
|||
|
|
font-size: 26rpx;
|
|||
|
|
color: #333;
|
|||
|
|
font-weight: 500;
|
|||
|
|
flex: 1;
|
|||
|
|
word-break: break-all;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-section {
|
|||
|
|
background: #fff;
|
|||
|
|
border-radius: 16rpx;
|
|||
|
|
padding: 30rpx;
|
|||
|
|
margin-bottom: 30rpx;
|
|||
|
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-container {
|
|||
|
|
// 表单容器样式
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-item {
|
|||
|
|
margin-bottom: 30rpx;
|
|||
|
|
|
|||
|
|
&:last-child {
|
|||
|
|
margin-bottom: 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.label {
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
color: #666;
|
|||
|
|
margin-bottom: 12rpx;
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.required {
|
|||
|
|
color: #ff4757;
|
|||
|
|
margin-left: 4rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.input {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 80rpx;
|
|||
|
|
border: 2rpx solid #e5e5e5;
|
|||
|
|
border-radius: 8rpx;
|
|||
|
|
padding: 0 20rpx;
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
color: #333;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
background: #fff;
|
|||
|
|
|
|||
|
|
&:focus {
|
|||
|
|
border-color: #4a90e2;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.picker {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 80rpx;
|
|||
|
|
border: 2rpx solid #e5e5e5;
|
|||
|
|
border-radius: 8rpx;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 0 20rpx;
|
|||
|
|
background: #fff;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.picker-text {
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
color: #333;
|
|||
|
|
flex: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.textarea {
|
|||
|
|
width: 100%;
|
|||
|
|
min-height: 120rpx;
|
|||
|
|
border: 2rpx solid #e5e5e5;
|
|||
|
|
border-radius: 8rpx;
|
|||
|
|
padding: 20rpx;
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
color: #333;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
background: #fff;
|
|||
|
|
resize: none;
|
|||
|
|
|
|||
|
|
&:focus {
|
|||
|
|
border-color: #4a90e2;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.button-container {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 20rpx;
|
|||
|
|
padding: 0 20rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn {
|
|||
|
|
flex: 1;
|
|||
|
|
height: 88rpx;
|
|||
|
|
border-radius: 12rpx;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
font-size: 32rpx;
|
|||
|
|
font-weight: bold;
|
|||
|
|
transition: all 0.3s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cancel-btn {
|
|||
|
|
background: #f5f5f5;
|
|||
|
|
color: #666;
|
|||
|
|
border: 2rpx solid #e5e5e5;
|
|||
|
|
|
|||
|
|
&:active {
|
|||
|
|
background: #e8e8e8;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.confirm-btn {
|
|||
|
|
background: #4a90e2;
|
|||
|
|
color: #fff;
|
|||
|
|
box-shadow: 0 4rpx 12rpx rgba(74, 144, 226, 0.3);
|
|||
|
|
|
|||
|
|
&:active {
|
|||
|
|
background: #357abd;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&.disabled {
|
|||
|
|
background: #ccc;
|
|||
|
|
color: #999;
|
|||
|
|
box-shadow: none;
|
|||
|
|
|
|||
|
|
&:active {
|
|||
|
|
background: #ccc;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|