|  |  | 
 |  |  |         保存 | 
 |  |  |       </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="actuallyLength"> | 
 |  |  |           {{ formatValue(recordData.fixedInfo?.actuallyLength, "m") }} | 
 |  |  |         </wd-form-item> | 
 |  |  |         <wd-form-item label="张力设置" prop="twistTension"> | 
 |  |  |           {{ formatValue(recordData.fixedInfo?.twistTension, "N/m") }} | 
 |  |  |         <wd-form-item label="张力设置" prop="tensionSetting"> | 
 |  |  |           {{ formatValue(recordData.fixedInfo?.tensionSetting, "N/m") }} | 
 |  |  |         </wd-form-item> | 
 |  |  |         <!-- 绞制外径(可编辑) --> | 
 |  |  |         <wd-form-item label="绞制外径" prop="twistDiameter" required> | 
 |  |  |         <wd-form-item label="绞合外径" prop="twistedOuterDiameter" required> | 
 |  |  |           <template v-if="isEdit"> | 
 |  |  |             <wd-input | 
 |  |  |               v-model="formData.twistDiameter" | 
 |  |  |               placeholder="请输入绞制外径(mm)" | 
 |  |  |               v-model="formData.twistedOuterDiameter" | 
 |  |  |               placeholder="请输入绞合外径(mm)" | 
 |  |  |               type="number" | 
 |  |  |             /> | 
 |  |  |           </template> | 
 |  |  |           <template v-else> | 
 |  |  |             {{ formatValue(formData.twistDiameter, "mm") }} | 
 |  |  |             {{ formatValue(formData.twistedOuterDiameter, "mm") }} | 
 |  |  |           </template> | 
 |  |  |         </wd-form-item> | 
 |  |  |       </wd-col> | 
 |  |  | 
 |  |  |         </wd-form-item> | 
 |  |  |         <wd-form-item label="节距" prop="pitch" required> | 
 |  |  |           <template v-if="isEdit"> | 
 |  |  |             <wd-input v-model="item.pitch" placeholder="请输入节距(mm)" type="number" /> | 
 |  |  |             <wd-input | 
 |  |  |               v-model="item.pitch" | 
 |  |  |               placeholder="请输入节距(mm)" | 
 |  |  |               type="number" | 
 |  |  |               @input="updatePitchRatio(item)" | 
 |  |  |             /> | 
 |  |  |           </template> | 
 |  |  |           <template v-else> | 
 |  |  |             {{ formatValue(item.pitch, "mm") }} | 
 |  |  | 
 |  |  |       <wd-col :span="24"> | 
 |  |  |         <wd-form-item label="产品外观" prop="productAppearance" required> | 
 |  |  |           <template v-if="isEdit"> | 
 |  |  |             <wd-checkbox-group | 
 |  |  |               v-model="formData.productAppearance" | 
 |  |  |               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.productAppearance.includes(opt.value)" | 
 |  |  |                 @click="handleAppearanceClick(opt.value)" | 
 |  |  |                 style="width: 100px" | 
 |  |  |               > | 
 |  |  |                 {{ opt.label }} | 
 |  |  |               </wd-checkbox> | 
 |  |  |             </wd-checkbox-group> | 
 |  |  |             </view> | 
 |  |  |           </template> | 
 |  |  |           <template v-else> | 
 |  |  |             {{ formatProductAppearance(formData.productAppearance) }} | 
 |  |  | 
 |  |  |         <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, computed } 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 paramsType = ref(""); | 
 |  |  | 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 detailData = reactive<any>({}); | 
 |  |  | const detailDataLoaded = ref(false); | 
 |  |  |  | 
 |  |  | // 获取当前登录用户信息 | 
 |  |  | const userStore = useUserStore(); | 
 |  |  | const userInfo: any = computed(() => userStore.userInfo); | 
 |  |  |  | 
 |  |  | // 使用扫码管理 composable(全局监听器,不随页面切换关闭) | 
 |  |  | const { | 
 |  |  |   deviceUid, | 
 |  |  |   deviceModel: scannedDeviceModel, | 
 |  |  |   loadFromCache, | 
 |  |  |   enableListener, | 
 |  |  | } = useScanCode("scanJX"); | 
 |  |  |  | 
 |  |  | const formData = reactive({ | 
 |  |  |   twistDiameter: "", // 绞制外径 | 
 |  |  |   twistedOuterDiameter: "", // 绞制外径 | 
 |  |  |   structureFormula: "", // 成品结构 | 
 |  |  |   structureItems: [], // 结构标准值和实测 | 
 |  |  |   inspectTwist: [], // 绞线工艺质量控制 | 
 |  |  | 
 |  |  |   const structureResult = recordData.value.structureInfo?.structureRecordResult || {}; | 
 |  |  |   const inspectionResult = recordData.value.inspectionResult || {}; | 
 |  |  |  | 
 |  |  |   formData.twistDiameter = inspectionResult.twistDiameter || ""; | 
 |  |  |   formData.twistedOuterDiameter = | 
 |  |  |     recordData.value.structureInfo.structureRecordResult.twistedOuterDiameter || ""; | 
 |  |  |   formData.structureFormula = structureResult.inspectStructure?.structureFormula || ""; | 
 |  |  |   formData.sampleComplete = inspectionResult.sampleComplete || ""; | 
 |  |  |   formData.conclusion = structureResult.conclusion || ""; | 
 |  |  |   formData.productAppearance = Array.isArray(structureResult.productAppearance) | 
 |  |  |  | 
 |  |  |   // 初始化产品外观 | 
 |  |  |   const appearance = Array.isArray(structureResult.productAppearance) | 
 |  |  |     ? structureResult.productAppearance | 
 |  |  |     : structureResult.productAppearance | 
 |  |  |     ? [structureResult.productAppearance] | 
 |  |  |     : []; | 
 |  |  |       ? [structureResult.productAppearance] | 
 |  |  |       : []; | 
 |  |  |   formData.productAppearance = appearance; | 
 |  |  |  | 
 |  |  |   formData.structureItems = JSON.parse( | 
 |  |  |     JSON.stringify(structureResult.inspectStructure?.structureItems || []) | 
 |  |  | 
 |  |  |     const response = await RoutingInspectionApi.getStrandedInspectionStructureInfoById({ id }); | 
 |  |  |     recordData.value = response.data; | 
 |  |  |     detailData.value = response.data.structureInfo; | 
 |  |  |  | 
 |  |  |     // 如果记录人为空,默认设置为当前登录用户 | 
 |  |  |     if (recordData.value.structureInfo && !recordData.value.structureInfo.createUserName) { | 
 |  |  |       recordData.value.structureInfo.createUserName = | 
 |  |  |         userInfo.value?.nickName || userInfo.value?.userName || ""; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     console.log("detailData.value", detailData.value); | 
 |  |  |     tempFiles.value = []; // 清空临时文件 | 
 |  |  |     initFormData(); // 数据返回后初始化表单 | 
 |  |  | 
 |  |  | const saveList = async () => { | 
 |  |  |   // 1. 基础字段校验 | 
 |  |  |   if (!formData.structureFormula) return uni.showToast({ title: "成品结构为必填项", icon: "none" }); | 
 |  |  |   if (!formData.twistDiameter) return uni.showToast({ title: "绞制外径为必填项", icon: "none" }); | 
 |  |  |   if (!formData.twistedOuterDiameter) | 
 |  |  |     return uni.showToast({ title: "绞制外径为必填项", icon: "none" }); | 
 |  |  |   if (!formData.productAppearance.length) | 
 |  |  |     return uni.showToast({ title: "产品外观为必填项", icon: "none" }); | 
 |  |  |   if (!formData.conclusion) return uni.showToast({ title: "结论为必填项", icon: "none" }); | 
 |  |  | 
 |  |  |     if (!item.pitchRatio) | 
 |  |  |       return uni.showToast({ title: `${item.twistName}节径比为必填项`, icon: "none" }); | 
 |  |  |   } | 
 |  |  |   console.log("1111", deviceUid.value); | 
 |  |  |   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]; | 
 |  |  | 
 |  |  |       deviceUid: deviceUid.value, | 
 |  |  |       id: paramsId.value, | 
 |  |  |       result: { | 
 |  |  |         twistDiameter: formData.twistDiameter, | 
 |  |  |         twistedOuterDiameter: formData.twistedOuterDiameter, | 
 |  |  |         structureFormula: formData.structureFormula, | 
 |  |  |         structureItems: formData.structureItems, | 
 |  |  |         inspectTwist: formData.inspectTwist, | 
 |  |  | 
 |  |  |     }); | 
 |  |  |  | 
 |  |  |     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" }); | 
 |  |  |     } | 
 |  |  | 
 |  |  |     case 1: | 
 |  |  |       return "danger"; // 已驳回 | 
 |  |  |     case 2: | 
 |  |  |       return "info"; // 待审核 | 
 |  |  |       return "primary"; // 待审核 | 
 |  |  |     case 3: | 
 |  |  |       return "success"; // 通过 | 
 |  |  |     default: | 
 |  |  |       return "info"; | 
 |  |  |       return "default"; | 
 |  |  |   } | 
 |  |  | }; | 
 |  |  |  | 
 |  |  | 
 |  |  |   }); | 
 |  |  | }; | 
 |  |  |  | 
 |  |  | const openScan = () => { | 
 |  |  |   scanRef.value.triggerScan(); | 
 |  |  | // 计算节径比 | 
 |  |  | const calculatePitchRatio = (pitch: string, dia: string) => { | 
 |  |  |   // 如果pitch或dia为空,则返回"-" | 
 |  |  |   if (!pitch || !dia) return "-"; | 
 |  |  |  | 
 |  |  |   // 将pitch和dia转换为浮点数 | 
 |  |  |   const pitchNum = parseFloat(pitch); | 
 |  |  |   const diaNum = parseFloat(dia); | 
 |  |  |  | 
 |  |  |   // 如果pitchNum或diaNum是NaN,或者diaNum为0,则返回"-" | 
 |  |  |   if (isNaN(pitchNum) || isNaN(diaNum) || diaNum === 0) return "-"; | 
 |  |  |  | 
 |  |  |   // 计算pitchNum和diaNum的比值,并保留两位小数 | 
 |  |  |   return (pitchNum / diaNum).toFixed(2); | 
 |  |  | }; | 
 |  |  | const getScanCode = (params: any) => { | 
 |  |  |   console.log("完整参数:", params); | 
 |  |  |   let codeObj = {}; | 
 |  |  |   try { | 
 |  |  |     codeObj = JSON.parse(params.code); | 
 |  |  |   } catch (err) { | 
 |  |  |     console.error("JSON解析失败:", err); | 
 |  |  |     toast.error("扫码数据异常"); | 
 |  |  |     return; | 
 |  |  |  | 
 |  |  | // 更新节径比(当节距变化时自动计算) | 
 |  |  | const updatePitchRatio = (item: any) => { | 
 |  |  |   // 使用绞合外径作为直径来计算节径比 | 
 |  |  |   const dia = item.dia; | 
 |  |  |   item.pitchRatio = calculatePitchRatio(item.pitch, dia); | 
 |  |  | }; | 
 |  |  |  | 
 |  |  | // 处理产品外观选择的互斥逻辑 | 
 |  |  | const handleAppearanceClick = (value: string) => { | 
 |  |  |   const currentValues = [...formData.productAppearance]; | 
 |  |  |   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]; | 
 |  |  |     } | 
 |  |  |   } | 
 |  |  |   deviceUid.value = codeObj?.uid; | 
 |  |  |   toast.success("扫码成功"); | 
 |  |  |  | 
 |  |  |   formData.productAppearance = newSelection; | 
 |  |  | }; | 
 |  |  | // 确保先移除再添加监听 | 
 |  |  | const setupScanListener = () => { | 
 |  |  |   uni.$off("scan", getScanCode); // 先移除旧的 | 
 |  |  |   uni.$on("scan", getScanCode); // 再添加新的 | 
 |  |  |  | 
 |  |  | const openScan = () => { | 
 |  |  |   console.log("indexJX - 点击扫码按钮(全局扫码模式,无需手动触发)"); | 
 |  |  |   // 全局扫码模式下,硬件扫码会自动触发,无需手动调用 | 
 |  |  |   uni.showToast({ | 
 |  |  |     title: "请使用扫码枪扫描", | 
 |  |  |     icon: "none", | 
 |  |  |   }); | 
 |  |  | }; | 
 |  |  | onUnmounted(() => { | 
 |  |  |   // 开启广播监听事件 | 
 |  |  |   uni.$off("scan", getScanCode); | 
 |  |  |   console.log("离开1"); | 
 |  |  | }); | 
 |  |  | onMounted(() => { | 
 |  |  |   // 开启广播监听事件 | 
 |  |  |   setupScanListener(); | 
 |  |  |   console.log("显示1"); | 
 |  |  |  | 
 |  |  | // 页面显示时的处理 | 
 |  |  | onShow(() => { | 
 |  |  |   console.log("========== indexJX - onShow 触发 =========="); | 
 |  |  |   // 重新启用监听器(确保监听器有效) | 
 |  |  |   enableListener(); | 
 |  |  |   // 加载缓存(更新UI显示) | 
 |  |  |   const cachedData = loadFromCache(); | 
 |  |  |  | 
 |  |  |   // 如果没有缓存数据,提示用户需要扫码 | 
 |  |  |   if (!cachedData || !cachedData.uid) { | 
 |  |  |     console.log("⚠️ 未检测到扫码缓存,用户需要扫描设备二维码"); | 
 |  |  |     // 在编辑模式下才提示 | 
 |  |  |     if (isEdit.value) { | 
 |  |  |       setTimeout(() => { | 
 |  |  |         uni.showToast({ | 
 |  |  |           title: "请扫描设备二维码后再保存", | 
 |  |  |           icon: "none", | 
 |  |  |           duration: 2000, | 
 |  |  |         }); | 
 |  |  |       }, 500); | 
 |  |  |     } | 
 |  |  |   } | 
 |  |  | }); | 
 |  |  | </script> | 
 |  |  |  | 
 |  |  | 
 |  |  |   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 { | 
 |  |  |   width: 38px; | 
 |  |  |   height: 38px; |