|  |  | 
 |  |  |         保存 | 
 |  |  |       </wd-button> | 
 |  |  |       <view class="placeholder"></view> | 
 |  |  |       <view class="scan-info"> | 
 |  |  |         <text class="scan-device-text">当前扫码机台: {{ scannedDeviceModel || "未扫码" }}</text> | 
 |  |  |       </view> | 
 |  |  |       <view class="scan-wrapper" @click="openScan"> | 
 |  |  |         <wd-icon name="scan" size="24px" color="#0D867F"></wd-icon> | 
 |  |  |       </view> | 
 |  |  | 
 |  |  |  | 
 |  |  |         <wd-form-item label="外观" prop="appearance" required> | 
 |  |  |           <template v-if="isEdit"> | 
 |  |  |             <wd-checkbox-group | 
 |  |  |               v-model="formData.appearance" | 
 |  |  |               inline | 
 |  |  |               v-for="(opt, idx) in appearanceOptions" | 
 |  |  |               :key="idx" | 
 |  |  |               style="text-align: justify" | 
 |  |  |             > | 
 |  |  |               <wd-checkbox :modelValue="opt.value" style="width: 100px"> | 
 |  |  |             <view style="display: flex; flex-wrap: wrap; gap: 10px"> | 
 |  |  |               <wd-checkbox | 
 |  |  |                 v-for="(opt, idx) in appearanceOptions" | 
 |  |  |                 :key="idx" | 
 |  |  |                 :value="opt.value" | 
 |  |  |                 :modelValue="formData.appearance?.includes(opt.value) || false" | 
 |  |  |                 @click="handleAppearanceClick(opt.value)" | 
 |  |  |                 style="width: 100px" | 
 |  |  |               > | 
 |  |  |                 {{ opt.label }} | 
 |  |  |               </wd-checkbox> | 
 |  |  |             </wd-checkbox-group> | 
 |  |  |             </view> | 
 |  |  |           </template> | 
 |  |  |           <template v-else> | 
 |  |  |             {{ formatProductAppearance(formData.appearance) }} | 
 |  |  | 
 |  |  |         <img :src="previewImageUrl" alt="预览图片" style="width: 100%; height: auto" /> | 
 |  |  |       </div> | 
 |  |  |     </wd-popup> | 
 |  |  |     <Scan ref="scanRef" emitName="scan" /> | 
 |  |  |     <wd-toast /> | 
 |  |  |   </view> | 
 |  |  | </template> | 
 |  |  |  | 
 |  |  | <script setup lang="ts"> | 
 |  |  | import { ref, reactive } from "vue"; | 
 |  |  | import { onLoad } from "@dcloudio/uni-app"; | 
 |  |  | import { ref, reactive, computed, onUnmounted } from "vue"; | 
 |  |  | import { onLoad, onShow, onHide } from "@dcloudio/uni-app"; | 
 |  |  | import RoutingInspectionApi from "@/api/routingInspection/routingInspection"; | 
 |  |  | import Scan from "@/components/scan/index.vue"; | 
 |  |  | import { useToast } from "wot-design-uni"; | 
 |  |  | import AttachmentUpload from "../upload.vue"; | 
 |  |  | import { useUserStore } from "@/store/modules/user"; | 
 |  |  | import { useScanCode } from "@/composables/useScanCode"; | 
 |  |  |  | 
 |  |  | // 核心状态 | 
 |  |  | const paramsId = ref(""); | 
 |  |  | 
 |  |  | const previewImageUrl = ref(""); | 
 |  |  | const isEdit = ref(false); | 
 |  |  | const tempFiles = ref<any[]>([]); | 
 |  |  | const deviceUid = ref(""); | 
 |  |  | const scanRef = ref(); | 
 |  |  | const toast = useToast(); | 
 |  |  | const attachmentRef = ref<any>(null); | 
 |  |  |  | 
 |  |  | // 获取当前登录用户信息 | 
 |  |  | const userStore = useUserStore(); | 
 |  |  | const userInfo: any = computed(() => userStore.userInfo); | 
 |  |  |  | 
 |  |  | // 使用扫码管理 composable(全局监听器,不随页面切换关闭) | 
 |  |  | const { | 
 |  |  |   deviceUid, | 
 |  |  |   deviceModel: scannedDeviceModel, | 
 |  |  |   loadFromCache, | 
 |  |  |   enableListener, | 
 |  |  | } = useScanCode("scanLS"); | 
 |  |  |  | 
 |  |  | // 表单数据 | 
 |  |  | const formData = reactive({ | 
 |  |  |   dia: "", | 
 |  |  |   maxDia: "", | 
 |  |  |   minDia: "", | 
 |  |  |   appearance: [], | 
 |  |  |   appearance: [] as string[], | 
 |  |  |   windingTightness: "", | 
 |  |  |   arrangementNeatness: "", | 
 |  |  |   aluminumWireDistance: "", | 
 |  |  | 
 |  |  |     case 1: | 
 |  |  |       return "danger"; | 
 |  |  |     case 2: | 
 |  |  |       return "info"; | 
 |  |  |       return "primary"; | 
 |  |  |     case 3: | 
 |  |  |       return "success"; | 
 |  |  |     default: | 
 |  |  |       return "info"; | 
 |  |  |       return "default"; | 
 |  |  |   } | 
 |  |  | }; | 
 |  |  |  | 
 |  |  | 
 |  |  |  | 
 |  |  | // 格式化工具 | 
 |  |  | const formatProductAppearance = (productAppearance: string[]) => { | 
 |  |  |   return !productAppearance.length ? "-" : productAppearance.join("、"); | 
 |  |  |   if (!productAppearance || !Array.isArray(productAppearance) || !productAppearance.length) { | 
 |  |  |     return "-"; | 
 |  |  |   } | 
 |  |  |   return productAppearance.join("、"); | 
 |  |  | }; | 
 |  |  |  | 
 |  |  | // 处理外观选择的互斥逻辑 | 
 |  |  | const handleAppearanceClick = (value: string) => { | 
 |  |  |   // 确保 appearance 是数组 | 
 |  |  |   if (!Array.isArray(formData.appearance)) { | 
 |  |  |     formData.appearance = []; | 
 |  |  |   } | 
 |  |  |  | 
 |  |  |   const currentValues = [...formData.appearance]; | 
 |  |  |   const isCurrentlyChecked = currentValues.includes(value); | 
 |  |  |  | 
 |  |  |   let newSelection: string[] = []; | 
 |  |  |  | 
 |  |  |   if (value === "无外观问题") { | 
 |  |  |     if (isCurrentlyChecked) { | 
 |  |  |       // 取消选中"无外观问题" | 
 |  |  |       newSelection = []; | 
 |  |  |     } else { | 
 |  |  |       // 选中"无外观问题",清空其他选项 | 
 |  |  |       newSelection = ["无外观问题"]; | 
 |  |  |     } | 
 |  |  |   } else { | 
 |  |  |     // 点击其他选项 | 
 |  |  |     if (isCurrentlyChecked) { | 
 |  |  |       // 取消选中该选项 | 
 |  |  |       newSelection = currentValues.filter((v) => v !== value); | 
 |  |  |     } else { | 
 |  |  |       // 选中该选项,移除"无外观问题" | 
 |  |  |       const filteredValues = currentValues.filter((v) => v !== "无外观问题"); | 
 |  |  |       newSelection = [...filteredValues, value]; | 
 |  |  |     } | 
 |  |  |   } | 
 |  |  |  | 
 |  |  |   formData.appearance = newSelection; | 
 |  |  | }; | 
 |  |  |  | 
 |  |  | const formatValue = (value: any, unit?: string) => { | 
 |  |  | 
 |  |  |   formData.dia = inspectionResult.dia || ""; | 
 |  |  |   formData.maxDia = inspectionResult.maxDia || ""; | 
 |  |  |   formData.minDia = inspectionResult.minDia || ""; | 
 |  |  |   formData.appearance = inspectionResult.appearance || []; | 
 |  |  |   // 确保 appearance 是数组 | 
 |  |  |   formData.appearance = Array.isArray(inspectionResult.appearance) | 
 |  |  |     ? inspectionResult.appearance | 
 |  |  |     : inspectionResult.appearance | 
 |  |  |       ? [inspectionResult.appearance] | 
 |  |  |       : []; | 
 |  |  |   formData.windingTightness = inspectionResult.windingTightness || ""; | 
 |  |  |   formData.arrangementNeatness = inspectionResult.arrangementNeatness || ""; | 
 |  |  |   formData.aluminumWireDistance = inspectionResult.aluminumWireDistance || ""; | 
 |  |  | 
 |  |  |   try { | 
 |  |  |     const response = await RoutingInspectionApi.getDrawInspectInfoById({ id }); | 
 |  |  |     detailData.value = response.data; | 
 |  |  |  | 
 |  |  |     // 如果巡检员为空,默认设置为当前登录用户 | 
 |  |  |     if (!detailData.value.processInspectionUserName) { | 
 |  |  |       detailData.value.processInspectionUserName = | 
 |  |  |         userInfo.value?.nickName || userInfo.value?.userName || ""; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     tempFiles.value = []; | 
 |  |  |     initFormData(); | 
 |  |  |   } catch (error) { | 
 |  |  | 
 |  |  |     return uni.showToast({ title: "成品模后接头情况为必填项", icon: "none" }); | 
 |  |  |   if (!formData.conclusion) return uni.showToast({ title: "结论为必填项", icon: "none" }); | 
 |  |  |   if (!formData.isFully) return uni.showToast({ title: "铝杆样品是否齐全为必填项", icon: "none" }); | 
 |  |  |   if (!deviceUid.value) return uni.showToast({ title: "请扫描二维码", icon: "none" }); | 
 |  |  |  | 
 |  |  |   // 验证扫码数据(从缓存或新扫码获取) | 
 |  |  |   console.log("保存前检查 deviceUid:", deviceUid.value); | 
 |  |  |   if (!deviceUid.value) { | 
 |  |  |     return uni.showToast({ | 
 |  |  |       title: "请先扫描设备二维码", | 
 |  |  |       icon: "none", | 
 |  |  |       duration: 2000, | 
 |  |  |     }); | 
 |  |  |   } | 
 |  |  |   const { newFiles } = attachmentRef.value.getSubmitFiles(); | 
 |  |  |   console.log("newFiles", newFiles); | 
 |  |  |   const allFileIds = [...newFiles]; | 
 |  |  | 
 |  |  |       processInspectionAttachmentList: allFileIds, | 
 |  |  |     }); | 
 |  |  |     if (res.code === 200) { | 
 |  |  |       uni.showToast({ title: "保存成功", icon: "success" }); | 
 |  |  |       isEdit.value = false; | 
 |  |  |       getDetailData(paramsId.value, paramsType.value); | 
 |  |  |       // 设置刷新标记,告诉列表页需要刷新 | 
 |  |  |       uni.setStorageSync("needRefreshInspectionList", true); | 
 |  |  |  | 
 |  |  |       uni.showToast({ | 
 |  |  |         title: "保存成功", | 
 |  |  |         icon: "success", | 
 |  |  |         duration: 1500, | 
 |  |  |       }); | 
 |  |  |       // 延迟返回列表页,让用户看到成功提示 | 
 |  |  |       setTimeout(() => { | 
 |  |  |         uni.navigateBack({ | 
 |  |  |           delta: 1, | 
 |  |  |         }); | 
 |  |  |       }, 1500); | 
 |  |  |     } else { | 
 |  |  |       uni.showModal({ title: res.msg || "保存失败", icon: "error" }); | 
 |  |  |     } | 
 |  |  | 
 |  |  | }; | 
 |  |  |  | 
 |  |  | const openScan = () => { | 
 |  |  |   scanRef.value.triggerScan(); | 
 |  |  |   console.log("indexLS - 点击扫码按钮(全局扫码模式,无需手动触发)"); | 
 |  |  |   // 全局扫码模式下,硬件扫码会自动触发,无需手动调用 | 
 |  |  |   uni.showToast({ | 
 |  |  |     title: "请使用扫码枪扫描", | 
 |  |  |     icon: "none", | 
 |  |  |   }); | 
 |  |  | }; | 
 |  |  | const getScanCode = (params: any) => { | 
 |  |  |   let codeObj = {}; | 
 |  |  |   try { | 
 |  |  |     codeObj = JSON.parse(params.code); | 
 |  |  |   } catch (err) { | 
 |  |  |     toast.error("扫码数据异常"); | 
 |  |  |     return; // 解析失败直接返回,避免后续错误 | 
 |  |  |  | 
 |  |  | // 页面显示时的处理 | 
 |  |  | onShow(() => { | 
 |  |  |   console.log("========== indexLS - onShow 触发 =========="); | 
 |  |  |   // 重新启用监听器(确保监听器有效) | 
 |  |  |   enableListener(); | 
 |  |  |   // 加载缓存(更新UI显示) | 
 |  |  |   const cachedData = loadFromCache(); | 
 |  |  |  | 
 |  |  |   // 如果没有缓存数据,提示用户需要扫码 | 
 |  |  |   if (!cachedData || !cachedData.uid) { | 
 |  |  |     console.log("⚠️ 未检测到扫码缓存,用户需要扫描设备二维码"); | 
 |  |  |     // 在编辑模式下才提示 | 
 |  |  |     if (isEdit.value) { | 
 |  |  |       setTimeout(() => { | 
 |  |  |         uni.showToast({ | 
 |  |  |           title: "请扫描设备二维码后再保存", | 
 |  |  |           icon: "none", | 
 |  |  |           duration: 2000, | 
 |  |  |         }); | 
 |  |  |       }, 500); | 
 |  |  |     } | 
 |  |  |   } | 
 |  |  |   deviceUid.value = codeObj?.uid; | 
 |  |  |   toast.success("扫码成功"); | 
 |  |  | }; | 
 |  |  | // 确保先移除再添加监听 | 
 |  |  | const setupScanListener = () => { | 
 |  |  |   uni.$off("scan", getScanCode); // 先移除旧的 | 
 |  |  |   uni.$on("scan", getScanCode); // 再添加新的 | 
 |  |  | }; | 
 |  |  | onUnmounted(() => { | 
 |  |  |   // 开启广播监听事件 | 
 |  |  |   uni.$off("scan", getScanCode); | 
 |  |  |   console.log("离开1"); | 
 |  |  | }); | 
 |  |  | onMounted(() => { | 
 |  |  |   // 开启广播监听事件 | 
 |  |  |   setupScanListener(); | 
 |  |  |   console.log("显示1"); | 
 |  |  | }); | 
 |  |  | </script> | 
 |  |  |  | 
 |  |  | 
 |  |  |  | 
 |  |  | .placeholder { | 
 |  |  |   flex: 1; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .scan-info { | 
 |  |  |   display: flex; | 
 |  |  |   align-items: center; | 
 |  |  |   margin-right: 10px; | 
 |  |  |  | 
 |  |  |   .scan-device-text { | 
 |  |  |     font-size: 14px; | 
 |  |  |     color: #0d867f; | 
 |  |  |     font-weight: 500; | 
 |  |  |   } | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .scan-wrapper { | 
 |  |  | 
 |  |  |   align-items: flex-start; // 垂直方向顶部对齐(上移关键) | 
 |  |  |   gap: 20rpx; // 选项之间的间距 | 
 |  |  | } | 
 |  |  | </style> | 
 |  |  | </style> |