From af4f45eaa2703ecf991bd10f07f6df179f2677d9 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期三, 19 十一月 2025 10:04:45 +0800
Subject: [PATCH] Merge branch 'refs/heads/yyb'

---
 src/pages.json                                            |   22 
 src/pages/production/twist/receive/monofil.vue            |   96 
 src/api/routingInspection/routingInspection.ts            |   63 
 src/pages/production/detail/wireDetail.vue                |    3 
 src/pages/production/twist/report/form.vue                |  184 +
 src/pages/production/twist/components/MonofilCard.vue     |  152 +
 src/pages/production/twist/report/draw.vue                |    2 
 src/pages/production/wire/attachment/index.vue            |  232 ++
 src/pages/production/twist/report/index.vue               |  205 ++
 src/pages/production/components/ProductionCard.vue        |    2 
 src/pages/production/twist/attachment/index.vue           |  221 ++
 src/pages/routingInspection/detail/indexJX.vue            |  867 +++++++++++
 src/manifest.json                                         |    2 
 src/pages/production/list/index.vue                       |   17 
 src/pages/routingInspection/product_card/index.vue        |  202 ++
 src/utils/request.ts                                      |    3 
 src/pages/production/twist/components/TwistReportCard.vue |   37 
 src/composables/useScanCode.ts                            |  249 +++
 src/pages/index/index.vue                                 |   35 
 src/pages/production/twist/receive/index.vue              |    8 
 src/pages/routingInspection/upload.vue                    |  587 +++++++
 src/pages/production/components/Statistics.vue            |    4 
 src/api/product/twist.ts                                  |   18 
 src/pages/routingInspection/index.vue                     |  164 ++
 src/pages/production/twist/receive/steelCore/edit.vue     |    5 
 src/pages/routingInspection/list/index.vue                |  130 +
 src/pages/production/detail/twistDetail.vue               |    3 
 src/App.vue                                               |   59 
 src/pages/production/index.vue                            |   40 
 src/pages/production/twist/receive/steelCore/index.vue    |   82 +
 src/components/product_card/index.vue                     |   13 
 src/pages/production/twist/receive/steelCore/form.vue     |   80 +
 src/static/icons/routingInspection.png                    |    0 
 src/utils/cache.ts                                        |   17 
 src/pages/routingInspection/detail/indexLS.vue            |  788 ++++++++++
 35 files changed, 4,328 insertions(+), 264 deletions(-)

diff --git a/src/App.vue b/src/App.vue
index 220abd9..e1a44ed 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,22 +1,81 @@
 <script setup lang="ts">
 import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
 import { useThemeStore } from "@/store";
+import { ref } from "vue";
 
 // 涓婚鍒濆鍖�
 const themeStore = useThemeStore();
+
+// 鍏ㄥ眬鎵爜骞挎挱鎺ユ敹鍣�
+let main: any = null;
+let receiver: any = null;
+let filter: any = null;
+
+// 鍒濆鍖栨壂鐮佸箍鎾帴鏀�
+const initGlobalScan = () => {
+  // #ifdef APP-PLUS
+  try {
+    main = plus.android.runtimeMainActivity();
+    const IntentFilter = plus.android.importClass("android.content.IntentFilter");
+    filter = new IntentFilter();
+    filter.addAction("com.dwexample.ACTION");
+    
+    receiver = plus.android.implements("io.dcloud.feature.internal.reflect.BroadcastReceiver", {
+      onReceive: (context: any, intent: any) => {
+        console.log("馃攳 [鍏ㄥ眬Scan] onReceive 瑙﹀彂:", context, intent);
+        plus.android.importClass(intent);
+        const scanResult = intent.getStringExtra("com.motorolasolutions.emdk.datawedge.data_string");
+        console.log("馃攳 [鍏ㄥ眬Scan] 鎵弿缁撴灉:", scanResult);
+        
+        // 鍙戦�佸埌鎵�鏈夊彲鑳界殑浜嬩欢
+        const eventNames = ["scan", "scanIndex", "scanJX", "scanLS"];
+        eventNames.forEach((eventName) => {
+          uni.$emit(eventName, { code: scanResult });
+          console.log(`馃攳 [鍏ㄥ眬Scan] 宸插彂閫� ${eventName} 浜嬩欢`);
+        });
+      },
+    });
+    
+    // 娉ㄥ唽骞挎挱鎺ユ敹鍣�
+    main.registerReceiver(receiver, filter);
+    console.log("馃攳 [鍏ㄥ眬Scan] 鍏ㄥ眬鎵爜骞挎挱鎺ユ敹鍣ㄥ凡鍚姩");
+  } catch (error) {
+    console.error("馃攳 [鍏ㄥ眬Scan] 鍒濆鍖栧け璐�:", error);
+  }
+  // #endif
+};
+
+// 鍋滄鎵爜骞挎挱鎺ユ敹
+const stopGlobalScan = () => {
+  // #ifdef APP-PLUS
+  try {
+    if (main && receiver) {
+      main.unregisterReceiver(receiver);
+      console.log("馃攳 [鍏ㄥ眬Scan] 鍏ㄥ眬鎵爜骞挎挱鎺ユ敹鍣ㄥ凡鍋滄");
+    }
+  } catch (error) {
+    console.error("馃攳 [鍏ㄥ眬Scan] 鍋滄澶辫触:", error);
+  }
+  // #endif
+};
 
 onLaunch(() => {
   console.log("App Launch");
   // 鍒濆鍖栦富棰�
   themeStore.initTheme();
+  // 鍒濆鍖栧叏灞�鎵爜骞挎挱鎺ユ敹鍣�
+  initGlobalScan();
 });
 
 onShow(() => {
   console.log("App Show");
+  // 搴旂敤鏄剧ず鏃堕噸鏂板惎鍔ㄥ箍鎾帴鏀跺櫒
+  initGlobalScan();
 });
 
 onHide(() => {
   console.log("App Hide");
+  // 搴旂敤闅愯棌鏃朵笉鍋滄骞挎挱锛堜繚鎸佸悗鍙版帴鏀讹級
 });
 </script>
 
diff --git a/src/api/product/twist.ts b/src/api/product/twist.ts
index 3360277..086f1d3 100644
--- a/src/api/product/twist.ts
+++ b/src/api/product/twist.ts
@@ -79,6 +79,24 @@
       data: data,
     });
   },
+
+  // 鍒犻櫎鍗曚笣棰嗙敤
+  deleteStrandedWireDish(id: number) {
+    return request<BaseResult<any>>({
+      url: `/strandedWire/deleteStrandedWireDish/${id}`,
+      method: "DELETE",
+    });
+  },
+
+  // 鍒犻櫎缁炵嚎鎶ュ伐
+  deleteWireOutput(params: { id: number }) {
+    // 灏嗗弬鏁版嫾鎺ュ埌 URL 浣滀负 query 鍙傛暟
+    const queryString = `?id=${params.id}`;
+    return request<BaseResult<any>>({
+      url: `/strandedWire/deleteWireOutput${queryString}`,
+      method: "DELETE",
+    });
+  },
 };
 
 export default TwistApi;
diff --git a/src/api/routingInspection/routingInspection.ts b/src/api/routingInspection/routingInspection.ts
new file mode 100644
index 0000000..1e2488a
--- /dev/null
+++ b/src/api/routingInspection/routingInspection.ts
@@ -0,0 +1,63 @@
+import request from "@/utils/request";
+import { BaseResult } from "@/models/base";
+
+const RoutingInspectionApi = {
+  // 鏌ヨ宸℃璁板綍
+  getDeviceInspectListByPatrol(params: any) {
+    return request<BaseResult<any>>({
+      url: "/wireInspection/getDeviceInspectListByPatrol",
+      method: "GET",
+      data: params,
+    });
+  },
+  // 鑾峰彇宸℃鏁版嵁
+  getInspectListByPatrol(data: any) {
+    return request<BaseResult<any>>({
+      url: "/wireInspection/getInspectListByPatrol",
+      method: "POST",
+      data: data,
+    });
+  },
+  // 鑾峰彇鎷変笣鍗曚釜缁撴瀯璁板綍 0
+  getDrawInspectInfoById(params: any) {
+    return request<BaseResult<any>>({
+      url: "/wireInspection/getDrawInspectInfoById/" + params.id,
+      method: "GET",
+      // data: params,
+    });
+  },
+  // 鑾峰彇缁炵嚎鍗曚釜缁撴瀯璁板綍 1
+  getStrandedInspectionStructureInfoById(params: any) {
+    return request<BaseResult<any>>({
+      url: "/wireInspection/getStrandedInspectionStructureInfoById/" + params.id,
+      method: "GET",
+      // data: params,
+    });
+  },
+  // 鎷変笣宸℃淇濆瓨
+  drawPatrolCheckInspection(data: any) {
+    return request<BaseResult<any>>({
+      url: "/wireInspection/drawPatrolCheckInspection?deviceUid=" + data.deviceUid,
+      method: "POST",
+      data: data,
+    });
+  },
+  // 缁炵嚎宸℃淇濆瓨
+  strandedPatrolCheckInspection(data: any) {
+    return request<BaseResult<any>>({
+      url: "/wireInspection/strandedPatrolCheckInspection?deviceUid=" + data.deviceUid,
+      method: "POST",
+      data: data,
+    });
+  },
+
+  // 楠岃瘉浜岀淮鐮�
+  assertScanQR(params: { deviceUid: string }) {
+    return request<BaseResult<any>>({
+      url: "/wireInspection/assertScanQR?deviceUid=" + params.deviceUid,
+      method: "GET",
+    });
+  },
+};
+
+export default RoutingInspectionApi;
diff --git a/src/components/product_card/index.vue b/src/components/product_card/index.vue
index 9fb6b8c..24ff6e9 100644
--- a/src/components/product_card/index.vue
+++ b/src/components/product_card/index.vue
@@ -8,6 +8,19 @@
         </wd-tag>
       </view>
     </template>
+    <wd-row class="my-2" v-if="data[map.systemNo]">
+      <wd-col :span="24">
+        <view class="flex">
+          <view class="icon_box">
+            <wd-icon name="folder" color="#0D867F"></wd-icon>
+          </view>
+          <text class="text-[#646874] mx-2">
+            璐ㄩ噺杩芥函鍙�:
+            <text class="text-[#252525]">{{ data[map.systemNo] }}</text>
+          </text>
+        </view>
+      </wd-col>
+    </wd-row>
     <wd-row class="my-2">
       <wd-col :span="12">
         <view class="flex">
diff --git a/src/composables/useScanCode.ts b/src/composables/useScanCode.ts
new file mode 100644
index 0000000..2c77588
--- /dev/null
+++ b/src/composables/useScanCode.ts
@@ -0,0 +1,249 @@
+import { ref, computed, onUnmounted } from "vue";
+import { onShow, onHide } from "@dcloudio/uni-app";
+import RoutingInspectionApi from "@/api/routingInspection/routingInspection";
+
+/**
+ * 鎵爜鏁版嵁鎺ュ彛
+ */
+interface ScanCodeData {
+  uid?: string;
+  deviceModel?: string;
+  [key: string]: any;
+}
+
+// 鍏ㄥ眬鐩戝惉鍣ㄧ姸鎬佹槧灏勶紙纭繚姣忎釜浜嬩欢鍚嶅彧鏈変竴涓洃鍚櫒锛�
+const globalListeners = new Map<string, boolean>();
+
+// 娣诲姞涓�涓叏灞�鐨勬崟鑾锋墍鏈夋壂鐮佷簨浠剁殑鐩戝惉鍣紙鐢ㄤ簬璋冭瘯锛�
+let debugListenerInitialized = false;
+
+const initDebugListener = () => {
+  if (debugListenerInitialized) return;
+
+  // 鐩戝惉鎵�鏈夊彲鑳界殑鎵爜浜嬩欢
+  const eventNames = ["scan", "scanIndex", "scanJX", "scanLS"];
+  eventNames.forEach((eventName) => {
+    uni.$on(eventName, (data: any) => {
+      console.log(`馃攳 [鍏ㄥ眬璋冭瘯] 鎹曡幏鍒� ${eventName} 浜嬩欢:`, data);
+    });
+  });
+
+  debugListenerInitialized = true;
+  console.log("馃攳 [鍏ㄥ眬璋冭瘯] 璋冭瘯鐩戝惉鍣ㄥ凡鍒濆鍖�");
+};
+
+/**
+ * 鎵爜绠$悊 Composable
+ * 缁熶竴绠$悊鎵爜浜嬩欢鐩戝惉銆佺紦瀛樺瓨鍌ㄥ拰鏁版嵁璇诲彇
+ * 鍏ㄥ眬鐩戝惉鍣紝涓嶉殢椤甸潰鍒囨崲鑰屽叧闂�
+ * @param eventName 鐩戝惉鐨勪簨浠跺悕绉帮紝榛樿涓� "scan"
+ */
+export function useScanCode(eventName: string = "scan") {
+  // 褰撳墠鎵爜鐨勮澶� UID
+  const deviceUid = ref<string>("");
+  // 褰撳墠鎵爜鐨勬満鍙板瀷鍙�
+  const deviceModel = ref<string>("");
+  // 瀹屾暣鐨勬壂鐮佹暟鎹�
+  const scanData = ref<ScanCodeData>({});
+  // 淇濆瓨浜嬩欢鍚嶇О
+  const currentEventName = eventName;
+
+  /**
+   * 浠庢湰鍦扮紦瀛樺姞杞芥壂鐮佹暟鎹�
+   */
+  const loadFromCache = () => {
+    try {
+      const cachedData = uni.getStorageSync("scanCodeData");
+      if (cachedData) {
+        scanData.value = cachedData;
+        deviceUid.value = cachedData.uid || "";
+        deviceModel.value = cachedData.deviceModel || "";
+        console.log("[useScanCode] 浠庣紦瀛樺姞杞芥壂鐮佹暟鎹�:", cachedData);
+        return cachedData;
+      }
+    } catch (error) {
+      console.error("[useScanCode] 璇诲彇缂撳瓨澶辫触:", error);
+    }
+    return null;
+  };
+
+  /**
+   * 淇濆瓨鎵爜鏁版嵁鍒扮紦瀛�
+   */
+  const saveToCache = (data: ScanCodeData) => {
+    try {
+      uni.setStorageSync("scanCodeData", data);
+      console.log("[useScanCode] 宸蹭繚瀛樺埌缂撳瓨:", data);
+      return true;
+    } catch (error) {
+      console.error("[useScanCode] 淇濆瓨缂撳瓨澶辫触:", error);
+      return false;
+    }
+  };
+
+  /**
+   * 娓呯┖鎵爜鏁版嵁鍜岀紦瀛�
+   */
+  const clearScanData = () => {
+    try {
+      uni.removeStorageSync("scanCodeData");
+      deviceUid.value = "";
+      deviceModel.value = "";
+      scanData.value = {};
+      console.log("[useScanCode] 宸叉竻绌烘壂鐮佹暟鎹�");
+    } catch (error) {
+      console.error("[useScanCode] 娓呯┖缂撳瓨澶辫触:", error);
+    }
+  };
+
+  /**
+   * 楠岃瘉浜岀淮鐮侊紙浠呰皟鐢ㄦ帴鍙o紝涓嶅鐞嗚繑鍥炵粨鏋滐級
+   */
+  const validateQRCode = async (uid: string): Promise<void> => {
+    try {
+      console.log("[useScanCode] 璋冪敤楠岃瘉浜岀淮鐮佹帴鍙�, deviceUid:", uid);
+      await RoutingInspectionApi.assertScanQR({ deviceUid: uid });
+      console.log("[useScanCode] 楠岃瘉鎺ュ彛璋冪敤瀹屾垚");
+    } catch (error: any) {
+      console.error("[useScanCode] 楠岃瘉鎺ュ彛璋冪敤寮傚父:", error);
+      // 鍗充娇寮傚父涔熶笉褰卞搷鍚庣画娴佺▼
+    }
+  };
+
+  /**
+   * 澶勭悊鎵爜浜嬩欢
+   */
+  const handleScanEvent = async (params: any) => {
+    console.log(`========== [useScanCode][${currentEventName}] 鏀跺埌鎵爜浜嬩欢 ==========`);
+    console.log(`[useScanCode][${currentEventName}] 鎺ユ敹鍙傛暟:`, params);
+    console.log(`[useScanCode][${currentEventName}] 瑙﹀彂鏃堕棿:`, new Date().toLocaleTimeString());
+
+    try {
+      if (!params?.code) {
+        console.warn("[useScanCode] 鎵爜鍐呭涓虹┖");
+        return;
+      }
+
+      let codeObj: ScanCodeData = {};
+      try {
+        codeObj = JSON.parse(params.code);
+        console.log("[useScanCode] 瑙f瀽鍚庣殑瀵硅薄:", codeObj);
+      } catch (err) {
+        console.error("[useScanCode] JSON 瑙f瀽澶辫触:", err);
+        console.log("[useScanCode] 鍘熷瀛楃涓�:", params.code);
+        // 濡傛灉涓嶆槸 JSON锛屽皾璇曚綔涓烘櫘閫氬瓧绗︿覆澶勭悊
+        codeObj = { code: params.code };
+      }
+
+      // 妫�鏌ユ槸鍚︽湁蹇呰鐨勫瓧娈碉紙uid 鎴� deviceModel锛�
+      if (!codeObj.uid && !codeObj.deviceModel) {
+        console.warn("[useScanCode] 鎵爜鏁版嵁缂哄皯 uid 鍜� deviceModel锛屼笉淇濆瓨缂撳瓨");
+        uni.showToast({
+          title: "鎵爜鏁版嵁鏃犳晥",
+          icon: "none",
+        });
+        return;
+      }
+
+      // 鏇存柊鏈湴鐘舵��
+      scanData.value = codeObj;
+      deviceUid.value = codeObj.uid || "";
+      deviceModel.value = codeObj.deviceModel || "";
+
+      // 淇濆瓨鍒扮紦瀛�
+      saveToCache(codeObj);
+
+      // 濡傛灉鏈� uid锛岃皟鐢ㄩ獙璇佹帴鍙o紙涓嶇瓑寰呯粨鏋滐級
+      if (codeObj.uid) {
+        validateQRCode(codeObj.uid);
+      }
+
+      // 鏄剧ず鎴愬姛鎻愮ず
+      uni.showToast({
+        title: "鎵爜鎴愬姛",
+        icon: "success",
+      });
+
+      console.log("[useScanCode] 鎵爜鏁版嵁宸叉洿鏂�:", {
+        uid: deviceUid.value,
+        deviceModel: deviceModel.value,
+      });
+    } catch (error) {
+      console.error("[useScanCode] 澶勭悊鎵爜鏁版嵁寮傚父:", error);
+    }
+  };
+
+  /**
+   * 鍚敤鎵爜鐩戝惉锛堝叏灞�锛屾瘡娆¢兘閲嶆柊娉ㄥ唽浠ョ‘淇濇湁鏁堬級
+   */
+  const enableListener = () => {
+    // 鍏堢Щ闄ゅ彲鑳藉瓨鍦ㄧ殑鏃х洃鍚櫒
+    uni.$off(currentEventName, handleScanEvent);
+    // 娣诲姞鏂扮殑鐩戝惉鍣�
+    uni.$on(currentEventName, handleScanEvent);
+    // 鏍囪涓哄叏灞�宸插惎鐢�
+    globalListeners.set(currentEventName, true);
+    console.log(
+      `[useScanCode][${currentEventName}] 鉁� 鍏ㄥ眬鐩戝惉鍣ㄥ凡鍚敤/鍒锋柊锛堜笉浼氶殢椤甸潰鍒囨崲鍏抽棴锛塦
+    );
+  };
+
+  /**
+   * 绂佺敤鎵爜鐩戝惉锛堜粎鐢ㄤ簬搴旂敤閫�鍑烘椂娓呯悊锛屾甯搁〉闈㈠垏鎹笉璋冪敤锛�
+   */
+  const disableListener = () => {
+    if (!globalListeners.get(currentEventName)) {
+      console.log(`[useScanCode][${currentEventName}] 鐩戝惉鍣ㄦ湭鍚敤锛岃烦杩嘸);
+      return;
+    }
+
+    uni.$off(currentEventName, handleScanEvent);
+    globalListeners.delete(currentEventName);
+    console.log(`[useScanCode][${currentEventName}] 鉂� 鍏ㄥ眬鐩戝惉鍣ㄥ凡绂佺敤`);
+  };
+
+  /**
+   * 璁剧疆椤甸潰鐢熷懡鍛ㄦ湡閽╁瓙锛堝叏灞�鐩戝惉鍣ㄦā寮忥級
+   * 鐩戝惉鍣ㄥ叏灞�鍚敤涓�娆★紝涓嶉殢椤甸潰鍒囨崲鍏抽棴
+   * @param options 閰嶇疆閫夐」
+   */
+  const setupLifecycle = (
+    options: {
+      loadCacheOnShow?: boolean; // 鏄惁鍦� onShow 鏃跺姞杞界紦瀛�
+    } = {}
+  ) => {
+    const { loadCacheOnShow = true } = options;
+
+    // 鍙湪 onShow 鏃跺姞杞界紦瀛橈紝涓嶇鐢ㄧ洃鍚櫒
+    if (loadCacheOnShow) {
+      onShow(() => {
+        console.log(`[useScanCode][${currentEventName}] onShow 瑙﹀彂`);
+        loadFromCache();
+      });
+    }
+
+    // 娉ㄦ剰锛氫笉鍐嶅湪 onHide 鍜� onUnmounted 涓鐢ㄧ洃鍚櫒
+    // 鐩戝惉鍣ㄤ繚鎸佸叏灞�婵�娲荤姸鎬�
+  };
+
+  // 椤甸潰鍔犺浇鏃惰嚜鍔ㄤ粠缂撳瓨璇诲彇
+  loadFromCache();
+
+  return {
+    // 鐘舵��
+    deviceUid,
+    deviceModel,
+    scanData,
+    // 璁$畻灞炴��
+    hasScanned: computed(() => !!deviceUid.value),
+    displayText: computed(() => deviceModel.value || "鏈壂鐮�"),
+    // 鏂规硶
+    loadFromCache,
+    saveToCache,
+    clearScanData,
+    validateQRCode,
+    enableListener,
+    disableListener,
+    setupLifecycle,
+  };
+}
diff --git a/src/manifest.json b/src/manifest.json
index 280f32b..1054727 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -2,7 +2,7 @@
     "name" : "绾跨紗涓婃姤",
     "appid" : "__UNI__F64E0A4",
     "description" : "",
-    "versionName" : "1.0.12",
+    "versionName" : "1.0.15",
     "versionCode" : "100",
     "transformPx" : false,
     /* 5+App鐗规湁鐩稿叧 */
diff --git a/src/pages.json b/src/pages.json
index 55210a1..6841877 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -6,7 +6,6 @@
       "^cu-(.*)": "@/components/cu-$1/index.vue"
     }
   },
-
   "pages": [
     {
       "path": "pages/index/index",
@@ -98,7 +97,6 @@
         "navigationBarTitleText": "涓汉璧勬枡"
       }
     },
-
     {
       "path": "pages/work/user/index",
       "style": {
@@ -284,6 +282,24 @@
       "style": {
         "navigationBarTitleText": "鏃舵晥鎶ュ伐"
       }
+    },
+    {
+      "path": "pages/routingInspection/index",
+      "style": {
+        "navigationBarTitleText": "宸℃"
+      }
+    },
+    {
+      "path": "pages/routingInspection/detail/indexJX",
+      "style": {
+        "navigationBarTitleText": "缁炵嚎宸℃璇︽儏"
+      }
+    },
+    {
+      "path": "pages/routingInspection/detail/indexLS",
+      "style": {
+        "navigationBarTitleText": "鎷変笣宸℃璇︽儏"
+      }
     }
   ],
   "globalStyle": {
@@ -317,4 +333,4 @@
       }
     ]
   }
-}
+}
\ No newline at end of file
diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue
index 2df7101..46174fc 100644
--- a/src/pages/index/index.vue
+++ b/src/pages/index/index.vue
@@ -18,8 +18,8 @@
     </wd-notice-bar>
     <!-- 蹇嵎瀵艰埅 -->
     <wd-grid clickable :column="1" class="mt-2">
-      <view v-for="(item, index) in navList">
-        <wd-grid-item v-if="item.show" :key="index" use-slot link-type="navigateTo" :url="item.url">
+      <view v-for="(item, index) in navList" :key="index">
+        <wd-grid-item v-if="item.show" use-slot link-type="navigateTo" :url="item.url">
           <view class="p-2">
             <image class="w-72rpx h-72rpx rounded-8rpx" :src="item.icon" />
           </view>
@@ -87,11 +87,12 @@
 </template>
 
 <script setup lang="ts">
-import { reactive } from "vue";
+import { reactive, computed } from "vue";
 import { dayjs, useMessage, useToast } from "wot-design-uni";
 import LogAPI, { VisitStatsVO } from "@/api/system/log";
 import WorkerCallingCard from "@/components/worker-calling-card/index.vue";
 import HomeApi from "@/api/home";
+import { useUserStore } from "@/store/modules/user";
 
 const visitStatsData = ref<VisitStatsVO>({
   todayUvCount: 0,
@@ -104,6 +105,22 @@
 
 const message = useMessage();
 const toast = useToast();
+
+// 鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛淇℃伅
+const userStore = useUserStore();
+const userInfo: any = computed(() => userStore.userInfo);
+
+// 鍒ゆ柇鏄惁涓哄贰妫�鍛樿鑹�
+const isInspector = computed(() => {
+  if (!userInfo.value || !userInfo.value.roles || !Array.isArray(userInfo.value.roles)) {
+    return false;
+  }
+  console.log(
+    "userInfo.value.roles",
+    userInfo.value.roles.some((role: any) => role.roleKey === "qualitative-inspector")
+  );
+  return userInfo.value.roles.some((role: any) => role.roleKey === "qualitative-inspector");
+});
 
 const fileProgress = reactive({
   show: false,
@@ -177,6 +194,12 @@
     url: "/pages/timely/index",
     show: false,
   },
+  {
+    icon: "/static/icons/routingInspection.png",
+    title: "宸℃",
+    url: "/pages/routingInspection/index",
+    show: false,
+  },
 ]);
 
 // 鍔犺浇璁块棶缁熻鏁版嵁
@@ -225,11 +248,17 @@
 const init = async () => {
   checkVersion();
   const { data } = await HomeApi.getIndex();
+
+  // 鍒ゆ柇鏄惁涓哄贰妫�鍛樿鑹�
   if (data.deviceGroupName == "鏃舵晥缁�") {
     navList[1].show = true;
   } else {
     navList[0].show = true;
   }
+  if (isInspector.value) {
+    // 濡傛灉鏄贰妫�鍛橈紝鏄剧ず宸℃鑿滃崟
+    navList[2].show = true;
+  }
 };
 
 /**
diff --git a/src/pages/production/components/ProductionCard.vue b/src/pages/production/components/ProductionCard.vue
index f5a9a84..108eae9 100644
--- a/src/pages/production/components/ProductionCard.vue
+++ b/src/pages/production/components/ProductionCard.vue
@@ -1,6 +1,6 @@
 <template>
   <wd-row>
-    <wd-col v-for="(item, index) in data" :key="index" :span="item.span ?? 12" class="my-1">
+    <wd-col v-for="(item, index) in data" :key="index" :span="24" class="my-1">
       <view class="flex w-full h-[20px]">
         <view class="text-[#646874] pl-1 mr-3">{{ item.label }}</view>
         <view class="font-medium pr-1" :style="{ color: item.color ?? color }">
diff --git a/src/pages/production/components/Statistics.vue b/src/pages/production/components/Statistics.vue
index 094e0e2..b35822d 100644
--- a/src/pages/production/components/Statistics.vue
+++ b/src/pages/production/components/Statistics.vue
@@ -1,6 +1,6 @@
 <template>
   <view class="statistics_box">
-    <wd-row>
+    <!-- <wd-row>
       <wd-col :span="12">
         <view class="h_48 px-4 flex items-center">
           <view class="icon_box">
@@ -19,7 +19,7 @@
           <text class="text-lg text-[#339599] ml-2 font-semibold">87%</text>
         </view>
       </wd-col>
-    </wd-row>
+    </wd-row> -->
   </view>
 </template>
 <style scoped lang="scss">
diff --git a/src/pages/production/detail/twistDetail.vue b/src/pages/production/detail/twistDetail.vue
index 6ccb078..fede20a 100644
--- a/src/pages/production/detail/twistDetail.vue
+++ b/src/pages/production/detail/twistDetail.vue
@@ -5,6 +5,7 @@
       :map="{
         deviceModel: 'deviceModel',
         model: 'model',
+        systemNo: 'systemNo',
         totalAmount: 'totalAmount',
         amount: 'amount',
         unAmount: 'unAmount',
@@ -50,6 +51,7 @@
 const cardData = reactive({
   deviceModel: undefined,
   model: undefined,
+  systemNo: undefined,
   totalAmount: undefined,
   amount: undefined,
   unAmount: undefined,
@@ -61,6 +63,7 @@
   });
   cardData.deviceModel = data.deviceModel;
   cardData.model = data.model;
+  cardData.systemNo = data.systemNo;
   cardData.totalAmount = data.totalLength;
   cardData.amount = data.length;
   cardData.unAmount = data.unLength;
diff --git a/src/pages/production/detail/wireDetail.vue b/src/pages/production/detail/wireDetail.vue
index 305e6e0..29f6caf 100644
--- a/src/pages/production/detail/wireDetail.vue
+++ b/src/pages/production/detail/wireDetail.vue
@@ -5,6 +5,7 @@
       :map="{
         deviceModel: 'deviceModel',
         model: 'model',
+        systemNo: 'systemNo',
         totalAmount: 'totalAmount',
         amount: 'amount',
         unAmount: 'unAmount',
@@ -50,6 +51,7 @@
 const cardData = reactive({
   deviceModel: undefined,
   model: undefined,
+  systemNo: undefined,
   totalAmount: undefined,
   amount: undefined,
   unAmount: undefined,
@@ -62,6 +64,7 @@
   });
   cardData.deviceModel = data.deviceModel;
   cardData.model = data.model;
+  cardData.systemNo = data.systemNo;
   cardData.totalAmount = data.totalAmount;
   cardData.amount = data.amount;
   cardData.unAmount = data.unAmount;
diff --git a/src/pages/production/index.vue b/src/pages/production/index.vue
index 017062f..1fbe803 100644
--- a/src/pages/production/index.vue
+++ b/src/pages/production/index.vue
@@ -2,7 +2,14 @@
   <view>
     <wd-row>
       <wd-col :span="21">
-        <wd-search placeholder-left hide-cancel></wd-search>
+        <wd-search
+          v-model="searchKeyword"
+          placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
+          placeholder-left
+          hide-cancel
+          @search="handleSearch"
+          @clear="handleClear"
+        ></wd-search>
       </wd-col>
       <wd-col :span="3">
         <view class="scan_box" @click="openScan">
@@ -14,16 +21,30 @@
       <wd-tab :title="`寰呯敓浜�(${total.wait})`" class="tab_bg">
         <ProductList
           ref="waitRef"
+          :key="`wait-${searchKey}`"
           :api="ManageApi.getProductList"
           state="寰呭畬鎴�"
+          :model="searchKeyword"
           @ok="changeWait"
+        />
+      </wd-tab>
+      <wd-tab :title="`閮ㄥ垎瀹屾垚(${total.partial})`" class="tab_bg">
+        <ProductList
+          ref="partialRef"
+          :key="`partial-${searchKey}`"
+          :api="ManageApi.getProductList"
+          state="閮ㄥ垎瀹屾垚"
+          :model="searchKeyword"
+          @ok="changePartial"
         />
       </wd-tab>
       <wd-tab :title="`宸茬敓浜�(${total.already})`" class="tab_bg">
         <ProductList
           ref="alreadyRef"
+          :key="`already-${searchKey}`"
           :api="ManageApi.getProductList"
           state="宸插畬鎴�"
+          :model="searchKeyword"
           @ok="changeAlready"
         />
       </wd-tab>
@@ -48,10 +69,14 @@
 const toast = useToast();
 
 const waitRef = ref();
+const partialRef = ref();
 const alreadyRef = ref();
 const tab = ref<number>(0);
+const searchKeyword = ref("");
+const searchKey = ref(0);
 const total = reactive({
   wait: 0,
+  partial: 0,
   already: 0,
 });
 
@@ -59,10 +84,23 @@
   total.wait = num;
 };
 
+const changePartial = (num: number) => {
+  total.partial = num;
+};
+
 const changeAlready = (num: number) => {
   total.already = num;
 };
 
+const handleSearch = () => {
+  searchKey.value++;
+};
+
+const handleClear = () => {
+  searchKeyword.value = "";
+  handleSearch();
+};
+
 const openScan = () => {
   scanRef.value.triggerScan();
 };
diff --git a/src/pages/production/list/index.vue b/src/pages/production/list/index.vue
index 92862f8..79b8d0e 100644
--- a/src/pages/production/list/index.vue
+++ b/src/pages/production/list/index.vue
@@ -36,6 +36,7 @@
 const map = reactive({
   deviceModel: "deviceModel",
   model: "model",
+  systemNo: "systemNo",
   totalAmount: "totalAmount",
   amount: "amount",
   unAmount: "unAmount",
@@ -50,13 +51,17 @@
     type: String,
     default: "",
   },
+  model: {
+    type: String,
+    default: "",
+  },
 });
 
 const emits = defineEmits(["ok"]);
 const list = ref<any[]>([]);
 
 const toDetail = (id: number, type: string) => {
-  toast.show("鐐瑰嚮鍗$墖");
+  // toast.show("鐐瑰嚮鍗$墖");
   if (type == "鎷変笣") {
     uni.navigateTo({
       url: `/pages/production/detail/wireDetail?id=${id}`,
@@ -69,22 +74,28 @@
 };
 
 const getList = async (pageNo: number, pageSize: number) => {
-  const { code, data } = await props.api({
+  const params: any = {
     userName: userInfo.value.userName,
     state: props.state,
     current: pageNo,
     size: pageSize,
-  });
+  };
+  if (props.model) {
+    params.model = props.model;
+  }
+  const { code, data } = await props.api(params);
   if (code == 200) {
     if (data.type == "缁炵嚎") {
       map.deviceModel = "deviceModel";
       map.model = "model";
+      map.systemNo = "systemNo";
       map.totalAmount = "totalLength";
       map.amount = "length";
       map.unAmount = "unLength";
     } else if (data.type == "鎷変笣") {
       map.deviceModel = "deviceModel";
       map.model = "model";
+      map.systemNo = "systemNo";
       map.totalAmount = "totalAmount";
       map.amount = "amount";
       map.unAmount = "unAmount";
diff --git a/src/pages/production/twist/attachment/index.vue b/src/pages/production/twist/attachment/index.vue
index c654299..0d17e10 100644
--- a/src/pages/production/twist/attachment/index.vue
+++ b/src/pages/production/twist/attachment/index.vue
@@ -17,26 +17,54 @@
     <view class="attachment-list">
       <wd-status-tip v-if="attachmentList.length === 0" image="content" tip="鏆傛棤闄勪欢" />
 
-      <wd-card
-        v-for="item in attachmentList"
-        :key="item.id"
-        type="rectangle"
-        custom-class="attachment-card"
-        :border="false"
-      >
-        <view class="attachment-item" @click="previewAttachment(item)">
-          <view class="attachment-info">
-            <view class="attachment-name">{{ item.bucketFileName || item.name }}</view>
-            <view class="attachment-meta">
-              <text class="file-type">{{ getFileType(item.bucketFileName) }}</text>
-              <text class="upload-time">{{ formatTime(item.createTime) }}</text>
+      <view v-for="item in attachmentList" :key="item.id" class="attachment-card">
+        <view class="media-wrapper" @click="previewAttachment(item)">
+          <!-- 鍥剧墖棰勮 -->
+          <template v-if="isImageType(item.url)">
+            <image
+              v-if="!item.loadError"
+              :src="getFullUrl(item.url)"
+              mode="aspectFill"
+              class="media-preview"
+              @error="onImageError(item)"
+              @load="onImageLoad(item)"
+            />
+            <!-- 鍥剧墖鍔犺浇澶辫触鏄剧ず榛樿鍥炬爣 -->
+            <view v-else class="file-icon-wrapper">
+              <wd-icon name="picture" size="48px" color="#ccc" />
+              <text class="file-name error-text">鍔犺浇澶辫触</text>
             </view>
+          </template>
+
+          <!-- 瑙嗛棰勮 -->
+          <template v-else-if="isVideoType(item.url)">
+            <video
+              v-if="!item.loadError"
+              :src="getFullUrl(item.url)"
+              class="media-preview"
+              :controls="false"
+              :show-center-play-btn="false"
+              @error="onVideoError(item)"
+            />
+            <!-- 瑙嗛鍔犺浇澶辫触鏄剧ず榛樿鍥炬爣 -->
+            <view v-else class="file-icon-wrapper">
+              <wd-icon name="video" size="48px" color="#ccc" />
+              <text class="file-name error-text">鍔犺浇澶辫触</text>
+            </view>
+          </template>
+
+          <!-- 鍏朵粬鏂囦欢绫诲瀷鏄剧ず鍥炬爣 -->
+          <view v-else class="file-icon-wrapper">
+            <wd-icon name="file-outline" size="48px" color="#999" />
+            <text class="file-name">鏂囦欢</text>
           </view>
-          <view class="attachment-actions" @click.stop>
-            <wd-icon name="delete" color="#ff4757" @click="deleteAttachment(item.id)" />
+
+          <!-- 鍒犻櫎鎸夐挳 -->
+          <view class="delete-btn" @click.stop="deleteAttachment(item.id)">
+            <wd-icon name="delete" color="#fff" size="20px" />
           </view>
         </view>
-      </wd-card>
+      </view>
     </view>
 
     <wd-toast />
@@ -48,6 +76,12 @@
 import { useToast } from "wot-design-uni";
 import AttachmentAPI from "@/api/product/attachment";
 
+// H5 浣跨敤 VITE_APP_BASE_API 浣滀负浠g悊璺緞锛屽叾浠栧钩鍙颁娇鐢� VITE_APP_API_URL 浣滀负璇锋眰璺緞
+let baseUrl = import.meta.env.VITE_APP_API_URL;
+// #ifdef H5
+baseUrl = import.meta.env.VITE_APP_BASE_API;
+// #endif
+
 const toast = useToast();
 
 // 椤甸潰鍙傛暟
@@ -56,6 +90,57 @@
 const attachmentList = ref<any[]>([]);
 
 const detailData = ref<any>({});
+
+// 鑾峰彇瀹屾暣鐨勫浘鐗�/瑙嗛 URL
+const getFullUrl = (url: string) => {
+  if (!url) return "";
+  // 濡傛灉宸茬粡鏄畬鏁寸殑 URL锛坔ttp 鎴� https 寮�澶达級锛岀洿鎺ヨ繑鍥�
+  if (url.startsWith("http://") || url.startsWith("https://")) {
+    return url;
+  }
+  // 濡傛灉鏄浉瀵硅矾寰勶紝鎷兼帴鍩虹 URL
+  return `${baseUrl}${url.startsWith("/") ? "" : "/"}${url}`;
+};
+
+// 浠� URL 鎴栨枃浠跺悕涓彁鍙栨墿灞曞悕
+const getExtension = (urlOrFileName: string) => {
+  if (!urlOrFileName) return "";
+  // 绉婚櫎鏌ヨ鍙傛暟鍜屽搱甯�
+  const cleanUrl = urlOrFileName.split("?")[0].split("#")[0];
+  // 鑾峰彇鏈�鍚庝竴涓偣鍚庨潰鐨勫唴瀹�
+  const extension = cleanUrl.split(".").pop()?.toLowerCase();
+  return extension || "";
+};
+
+// 鍒ゆ柇鏄惁涓哄浘鐗囩被鍨�
+const isImageType = (urlOrFileName: string) => {
+  const extension = getExtension(urlOrFileName);
+  return ["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(extension);
+};
+
+// 鍒ゆ柇鏄惁涓鸿棰戠被鍨�
+const isVideoType = (urlOrFileName: string) => {
+  const extension = getExtension(urlOrFileName);
+  return ["mp4", "mov", "avi", "wmv", "flv", "mkv", "webm"].includes(extension);
+};
+
+// 鍥剧墖鍔犺浇鎴愬姛
+const onImageLoad = (item: any) => {
+  item.loadError = false;
+};
+
+// 鍥剧墖鍔犺浇澶辫触
+const onImageError = (item: any) => {
+  console.error("鍥剧墖鍔犺浇澶辫触:", item.url);
+  item.loadError = true;
+};
+
+// 瑙嗛鍔犺浇澶辫触
+const onVideoError = (item: any) => {
+  console.error("瑙嗛鍔犺浇澶辫触:", item.url);
+  item.loadError = true;
+};
+
 // 鑾峰彇闄勪欢鍒楄〃
 const getAttachmentList = async (data: any) => {
   try {
@@ -252,24 +337,24 @@
 // 棰勮闄勪欢
 const previewAttachment = (item: any) => {
   // 鏍规嵁鏂囦欢绫诲瀷杩涜棰勮
-  const fileName = item.bucketFileName || item.name;
-  const fileType = getFileType(fileName);
+  const fileType = getFileType(item.url);
+  const fullUrl = getFullUrl(item.url);
 
   if (fileType.startsWith("image")) {
     // 鍥剧墖棰勮
     uni.previewImage({
-      urls: [item.url],
-      current: item.url,
+      urls: [fullUrl],
+      current: fullUrl,
     });
   } else {
     // 鍏朵粬鏂囦欢绫诲瀷锛屽彲浠ヤ笅杞芥垨鎵撳紑
     uni.downloadFile({
-      url: item.url,
+      url: fullUrl,
       success: (res) => {
         uni.openDocument({
           filePath: res.tempFilePath,
           success: () => {
-            console.log("鎵撳紑鏂囨。鎴愬姛");
+            // 鎵撳紑鏂囨。鎴愬姛
           },
           fail: (error) => {
             console.error("鎵撳紑鏂囨。澶辫触:", error);
@@ -286,9 +371,9 @@
 };
 
 // 鑾峰彇鏂囦欢绫诲瀷
-const getFileType = (fileName: string) => {
-  if (!fileName) return "unknown";
-  const extension = fileName.split(".").pop()?.toLowerCase();
+const getFileType = (urlOrFileName: string) => {
+  if (!urlOrFileName) return "unknown";
+  const extension = getExtension(urlOrFileName);
   switch (extension) {
     case "jpg":
     case "jpeg":
@@ -297,6 +382,14 @@
     case "bmp":
     case "webp":
       return "image";
+    case "mp4":
+    case "mov":
+    case "avi":
+    case "wmv":
+    case "flv":
+    case "mkv":
+    case "webm":
+      return "video";
     case "pdf":
       return "pdf";
     case "doc":
@@ -357,43 +450,69 @@
 }
 
 .attachment-list {
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  gap: 8px;
+
   .attachment-card {
-    margin-bottom: 12px;
-    border-radius: 4px;
+    width: 100%;
+    aspect-ratio: 1;
   }
 }
 
-.attachment-item {
-  display: flex;
-  align-items: center;
-  padding: 12px;
+.media-wrapper {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  border-radius: 8px;
+  overflow: hidden;
+  background: #f5f5f5;
 
-  .attachment-info {
-    flex: 1;
+  .media-preview {
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+  }
 
-    .attachment-name {
-      font-size: 16px;
-      font-weight: 500;
-      color: #333;
-      margin-bottom: 4px;
-      word-break: break-all;
-    }
+  .file-icon-wrapper {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+    height: 100%;
+    padding: 8px;
+    text-align: center;
 
-    .attachment-meta {
-      display: flex;
-      gap: 12px;
+    .file-name {
+      margin-top: 8px;
       font-size: 12px;
-      color: #999;
+      color: #666;
+      word-break: break-all;
+      display: -webkit-box;
+      -webkit-line-clamp: 2;
+      line-clamp: 2;
+      -webkit-box-orient: vertical;
+      overflow: hidden;
+
+      &.error-text {
+        color: #ff4757;
+      }
     }
   }
 
-  .attachment-actions {
-    margin-left: 12px;
-
-    :deep(.wd-icon) {
-      font-size: 20px;
-      cursor: pointer;
-    }
+  .delete-btn {
+    position: absolute;
+    top: 4px;
+    right: 4px;
+    width: 28px;
+    height: 28px;
+    border-radius: 50%;
+    background: rgba(0, 0, 0, 0.5);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    z-index: 10;
   }
 }
 </style>
diff --git a/src/pages/production/twist/components/MonofilCard.vue b/src/pages/production/twist/components/MonofilCard.vue
index 68268d5..972c910 100644
--- a/src/pages/production/twist/components/MonofilCard.vue
+++ b/src/pages/production/twist/components/MonofilCard.vue
@@ -1,21 +1,153 @@
 <template>
-  <wd-card>
-    <wd-cell-group :border="true">
-      <wd-cell title="鍗曚笣缂栧彿" :value="data.monofilamentNumber" />
-      <wd-cell title="鐞嗚闀垮害" :value="data.amount + ' (m)'" />
-      <wd-cell title="鐢熶骇闀垮害" :value="data.actuallyLength + ' (m)'" />
-      <wd-cell title="閲嶉噺" :value="data.actuallyWeight + ' (kg)'" />
-    </wd-cell-group>
-  </wd-card>
+  <view class="swipe-container">
+    <view
+      class="swipe-content"
+      :style="{ transform: `translateX(${translateX}px)` }"
+      @touchstart="handleTouchStart"
+      @touchmove="handleTouchMove"
+      @touchend="handleTouchEnd"
+    >
+      <wd-card>
+        <wd-cell-group :border="true">
+          <wd-cell title="鍗曚笣缂栧彿" :value="data.monofilamentNumber" />
+          <wd-cell title="鐞嗚闀垮害" :value="data.amount + ' (m)'" />
+          <wd-cell title="鐢熶骇闀垮害" :value="data.actuallyLength + ' (m)'" />
+          <wd-cell title="閲嶉噺" :value="data.actuallyWeight + ' (kg)'" />
+        </wd-cell-group>
+      </wd-card>
+    </view>
+    <view class="swipe-delete" @click="handleDelete">
+      <text class="delete-text">鍒犻櫎</text>
+    </view>
+  </view>
 </template>
 
 <script setup lang="ts">
-defineProps({
+import { ref } from "vue";
+
+const props = defineProps({
   data: {
     type: Object,
     default: () => {},
   },
 });
+
+const emit = defineEmits(["delete", "swipe-open"]);
+
+const translateX = ref(0);
+const startX = ref(0);
+const startY = ref(0);
+const currentX = ref(0);
+const isSwipeOpen = ref(false);
+const deleteWidth = 80; // 鍒犻櫎鎸夐挳瀹藉害
+const isHorizontalSwipe = ref(false);
+
+const handleTouchStart = (e: any) => {
+  startX.value = e.touches[0].clientX;
+  startY.value = e.touches[0].clientY;
+  currentX.value = translateX.value;
+  isHorizontalSwipe.value = false;
+};
+
+const handleTouchMove = (e: any) => {
+  const moveX = e.touches[0].clientX - startX.value;
+  const moveY = e.touches[0].clientY - startY.value;
+
+  // 鍒ゆ柇鏄惁涓烘按骞虫粦鍔紙姘村钩绉诲姩璺濈澶т簬鍨傜洿绉诲姩璺濈锛�
+  if (!isHorizontalSwipe.value && Math.abs(moveX) > Math.abs(moveY) && Math.abs(moveX) > 10) {
+    isHorizontalSwipe.value = true;
+  }
+
+  // 鍙湁姘村钩婊戝姩鏃舵墠澶勭悊鍒犻櫎婊戝姩
+  if (isHorizontalSwipe.value) {
+    e.stopPropagation();
+    const newTranslateX = currentX.value + moveX;
+
+    // 闄愬埗婊戝姩鑼冨洿锛氬彧鑳藉悜宸︽粦鍔紝鏈�澶ф粦鍔ㄨ窛绂讳负鍒犻櫎鎸夐挳瀹藉害
+    if (newTranslateX <= 0 && newTranslateX >= -deleteWidth) {
+      translateX.value = newTranslateX;
+    } else if (newTranslateX < -deleteWidth) {
+      translateX.value = -deleteWidth;
+    } else if (newTranslateX > 0) {
+      translateX.value = 0;
+    }
+  }
+};
+
+const handleTouchEnd = (e: any) => {
+  // 鍙湁姘村钩婊戝姩鏃舵墠澶勭悊缁撴潫閫昏緫
+  if (isHorizontalSwipe.value) {
+    e.stopPropagation();
+    // 鍒ゆ柇鏄惁搴旇鎵撳紑鎴栧叧闂垹闄ゆ寜閽�
+    if (translateX.value < -deleteWidth / 2) {
+      // 婊戝姩瓒呰繃涓�鍗婏紝鎵撳紑鍒犻櫎鎸夐挳
+      translateX.value = -deleteWidth;
+      isSwipeOpen.value = true;
+      emit("swipe-open", props.data);
+    } else {
+      // 婊戝姩涓嶈冻涓�鍗婏紝鍏抽棴鍒犻櫎鎸夐挳
+      translateX.value = 0;
+      isSwipeOpen.value = false;
+    }
+  }
+  isHorizontalSwipe.value = false;
+};
+
+const handleDelete = () => {
+  // 鍏堝叧闂粦鍔�
+  translateX.value = 0;
+  isSwipeOpen.value = false;
+  emit("delete", props.data);
+};
+
+// 鍏抽棴婊戝姩鐨勬柟娉曪紝渚涘閮ㄨ皟鐢�
+const closeSwipe = () => {
+  if (isSwipeOpen.value) {
+    translateX.value = 0;
+    isSwipeOpen.value = false;
+  }
+};
+
+// 鏆撮湶鏂规硶渚涚埗缁勪欢璋冪敤
+defineExpose({
+  closeSwipe,
+  isSwipeOpen,
+});
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+.swipe-container {
+  position: relative;
+  overflow: hidden;
+  margin-bottom: 8px;
+}
+
+.swipe-content {
+  position: relative;
+  transition: transform 0.3s ease;
+  z-index: 2;
+  background: #fff;
+  touch-action: pan-y;
+}
+
+.swipe-delete {
+  position: absolute;
+  right: 0;
+  top: 12px;
+  bottom: 12px;
+  width: 80px;
+  background: #ff4444;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 1;
+  border-radius: 4px;
+  box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.05);
+
+  .delete-text {
+    color: #fff;
+    font-size: 14px;
+    font-weight: 500;
+  }
+}
+</style>
diff --git a/src/pages/production/twist/components/TwistReportCard.vue b/src/pages/production/twist/components/TwistReportCard.vue
new file mode 100644
index 0000000..ee3f754
--- /dev/null
+++ b/src/pages/production/twist/components/TwistReportCard.vue
@@ -0,0 +1,37 @@
+<template>
+  <wd-row>
+    <wd-col v-for="(item, index) in data" :key="index" :span="item.span || 24" class="my-1">
+      <view class="flex w-full h-[20px]">
+        <view class="text-[#646874] pl-1 mr-3">{{ item.label }}</view>
+        <view class="font-medium pr-1" :style="{ color: item.color ?? color }">
+          {{ value[item.prop] }} {{ value[item.unitProp] }} {{ item.unit }}
+        </view>
+      </view>
+    </wd-col>
+  </wd-row>
+</template>
+
+<script lang="ts" setup>
+defineProps({
+  data: {
+    type: Array as any,
+    default: () => {
+      return [];
+    },
+  },
+  value: {
+    type: Object,
+    default: () => {
+      return {};
+    },
+  },
+  color: {
+    type: String,
+    default: "#333333",
+  },
+  unit: {
+    type: String,
+    default: "",
+  },
+});
+</script>
diff --git a/src/pages/production/twist/receive/index.vue b/src/pages/production/twist/receive/index.vue
index 277e46e..d4cede7 100644
--- a/src/pages/production/twist/receive/index.vue
+++ b/src/pages/production/twist/receive/index.vue
@@ -1,16 +1,16 @@
 <template>
   <wd-tabs v-model="tab" auto-line-width>
     <wd-tab title="鍗曚笣棰嗙敤" name="鍗曚笣棰嗙敤">
-      <Monofil />
+      <Monofil v-if="tab === '鍗曚笣棰嗙敤'" />
     </wd-tab>
     <wd-tab title="鐩樺叿棰嗙敤" name="鐩樺叿棰嗙敤">
       <view class="content">
-        <Plate />
+        <Plate v-if="tab === '鐩樺叿棰嗙敤'" />
       </view>
     </wd-tab>
-    <wd-tab title="閽㈣姱棰嗙敤" name="閽㈣姱棰嗙敤">
+    <wd-tab title="鑺嚎棰嗙敤" name="鑺嚎棰嗙敤">
       <view class="content">
-        <SteelCore />
+        <SteelCore v-if="tab === '鑺嚎棰嗙敤'" />
       </view>
     </wd-tab>
   </wd-tabs>
diff --git a/src/pages/production/twist/receive/monofil.vue b/src/pages/production/twist/receive/monofil.vue
index 49b261e..b4998fc 100644
--- a/src/pages/production/twist/receive/monofil.vue
+++ b/src/pages/production/twist/receive/monofil.vue
@@ -25,7 +25,12 @@
         <block v-for="item in nodeList" :key="item">
           <wd-tab :title="item.twistedLayer" :name="item.twistedLayer">
             <scroll-view class="content" scroll-y>
-              <MonofilCard v-for="(m, i) in item.strandedWireDish" :key="i" :data="m" />
+              <MonofilCard
+                v-for="(m, i) in item.strandedWireDish"
+                :key="i"
+                :data="m"
+                @delete="handleDeleteCard(item, m)"
+              />
             </scroll-view>
           </wd-tab>
         </block>
@@ -79,7 +84,7 @@
 import MonofilCard from "../components/MonofilCard.vue";
 import StatisticsModal from "../components/StatisticsModal.vue";
 import { useToast } from "wot-design-uni";
-import { onLoad, onUnload } from "@dcloudio/uni-app";
+import { onLoad, onUnload, onShow, onHide } from "@dcloudio/uni-app";
 import Scan from "@/components/scan/index.vue";
 import ManageApi from "@/api/product/manage";
 import TwistApi from "@/api/product/twist";
@@ -95,20 +100,21 @@
 const showStatisticsModal = ref(false);
 const showManualInput = ref(false);
 const manualOutPutId = ref("");
+const isPageVisible = ref(false); // 鏍囪椤甸潰鏄惁鍙
 
 // 鐩戝惉鏍囩鍒囨崲
 watch(tab, () => {
   if (tab.value) {
-    console.log("tab.value:===========1", tab.value);
     getList();
   }
 });
 
 const getScanCode = async (code: any) => {
-  console.log("鑷畾涔夋壂鎻忕殑缁撴灉鍥炶皟鍑芥暟:", code);
-  // let parseData = code.trim();
-  console.log("code:===========", JSON.parse(code.code));
-  console.log("id:=============", JSON.parse(code.code).id);
+  // 妫�鏌ラ〉闈㈡槸鍚﹀彲瑙侊紝濡傛灉涓嶅彲瑙佸垯涓嶅鐞嗘壂鐮佹暟鎹�
+  if (!isPageVisible.value) {
+    return;
+  }
+
   try {
     // 妫�鏌ユ槸鍚﹀凡閫夋嫨鏍囩
     if (!tab.value) {
@@ -117,24 +123,25 @@
     }
 
     // 鎵惧埌褰撳墠閫変腑鐨勫眰
-    console.log("tab.value:===========2", tab.value);
     const currentLayer = nodeList.value.find((node) => node.twistedLayer === tab.value);
     if (!currentLayer) {
       toast.error("鏈壘鍒板綋鍓嶉�変腑鐨勫眰");
       return;
     }
-    console.log("tab.value:===========3", currentLayer);
-    // 鍦ㄥ彂璧疯姹傚墠锛屽厛鏍¢獙璇ュ崟涓濇槸鍚﹀凡鍦ㄥ綋鍓嶆垨鍏朵粬灞傜骇琚鐢�
-    const scannedOutputId = JSON.parse(code.code).id;
-    const alreadyUsed = nodeList.value.some((node) =>
-      (node.strandedWireDish || []).some((item: any) => item.outputId === scannedOutputId)
-    );
-    if (alreadyUsed) {
-      toast.error("璇ュ崟涓濆凡棰嗙敤锛岃鍕块噸澶嶆壂鐮�");
-      return;
+
+    // 瑙f瀽鎵爜鏁版嵁
+    const scanData = JSON.parse(code.code);
+
+    // 鍒ゆ柇灞傜骇鏄惁鍖归厤
+    if (scanData.layer && scanData.layer !== currentLayer.twistedLayer) {
+      toast.error(
+        `棰嗙敤灞傜骇涓嶅锛屽綋鍓嶅眰鏄細${currentLayer.twistedLayer}锛岄鐢ㄥ崟涓濆眰鏄細${scanData.layer}`
+      );
+      // return;
     }
+
     const { data } = await TwistApi.getScarn({
-      outPutId: scannedOutputId,
+      outPutId: scanData.id,
       twistId: currentLayer.twistId,
     });
 
@@ -268,7 +275,6 @@
     // 璁剧疆榛樿绗竴灞�
     if (nodeList.value && nodeList.value.length > 0 && !tab.value) {
       tab.value = nodeList.value[0].twistedLayer;
-      console.log("璁剧疆榛樿绗竴灞�:", tab.value);
       // 璁剧疆榛樿鏍囩鍚庯紝鍔犺浇绗竴灞傜殑鏁版嵁
       getList();
     }
@@ -344,6 +350,44 @@
   }
 };
 
+// 鍒犻櫎鍗$墖
+const handleDeleteCard = async (layer: any, cardData: any) => {
+  // 鏄剧ず纭鎻愮ず
+  uni.showModal({
+    title: "鎻愮ず",
+    content: "纭畾瑕佸垹闄よ鍗曚笣鍚楋紵",
+    success: async (res) => {
+      if (res.confirm) {
+        try {
+          // 濡傛灉鏈塱d锛岃皟鐢ㄦ帴鍙e垹闄�
+          if (cardData.id !== undefined && cardData.id !== null) {
+            const { code, msg } = await TwistApi.deleteStrandedWireDish(cardData.id);
+            if (code !== 200) {
+              toast.error(msg || "鍒犻櫎澶辫触");
+              return;
+            }
+          }
+
+          // 鍓嶇鐩存帴鍒犻櫎锛堟棤璁烘槸鍚︽湁id锛岄兘浠庡墠绔垹闄わ級
+          if (layer.strandedWireDish && Array.isArray(layer.strandedWireDish)) {
+            const index = layer.strandedWireDish.findIndex(
+              (item: any) => item.monofilamentNumber === cardData.monofilamentNumber
+            );
+            if (index !== -1) {
+              layer.strandedWireDish.splice(index, 1);
+              toast.success("鍒犻櫎鎴愬姛");
+              // 鍒锋柊褰撳墠灞傜殑鏁版嵁鏄剧ず
+              getList();
+            }
+          }
+        } catch (error: any) {
+          toast.error(error.msg || "鍒犻櫎澶辫触");
+        }
+      }
+    },
+  });
+};
+
 onLoad(async (options: any) => {
   // 寮�鍚箍鎾洃鍚簨浠�
   uni.$on("scanMono", getScanCode);
@@ -353,9 +397,21 @@
   getRootNumber(options.id);
   // getRootNumber(118);
 });
+
+onShow(() => {
+  // 椤甸潰鏄剧ず鏃舵爣璁颁负鍙
+  isPageVisible.value = true;
+});
+
+onHide(() => {
+  // 椤甸潰闅愯棌鏃舵爣璁颁负涓嶅彲瑙�
+  isPageVisible.value = false;
+});
+
 onUnload(() => {
-  // 寮�鍚箍鎾洃鍚簨浠�
+  // 鍙栨秷骞挎挱鐩戝惉浜嬩欢
   uni.$off("scanMono", getScanCode);
+  isPageVisible.value = false;
 });
 </script>
 
diff --git a/src/pages/production/twist/receive/steelCore/edit.vue b/src/pages/production/twist/receive/steelCore/edit.vue
index fb7ae58..cbca0fe 100644
--- a/src/pages/production/twist/receive/steelCore/edit.vue
+++ b/src/pages/production/twist/receive/steelCore/edit.vue
@@ -1,6 +1,6 @@
 <template>
   <view>
-    <CardTitle title="缁炵嚎閽㈣姱棰嗙敤" :hideAction="false" />
+    <CardTitle title="缁炵嚎鑺嚎棰嗙敤" :hideAction="false" />
     <SteelCoreForm ref="formRef" class="mx-4" />
     <view class="footer">
       <wd-button
@@ -37,13 +37,10 @@
 
 // 鎺ユ敹鍒楄〃椤典紶閫掔殑鏁版嵁
 const receiveEditData = (data: any) => {
-  console.log("receiveEditData 鎺ユ敹鍒扮殑鏁版嵁:", data);
   if (data && formRef.value) {
     // 纭繚 list 鍜� editId 閮藉瓨鍦�
     if (data.list && data.editId) {
       formRef.value.setFormData(data.list, data.editId);
-    } else {
-      console.error("鏁版嵁鏍煎紡閿欒:", data);
     }
   }
 };
diff --git a/src/pages/production/twist/receive/steelCore/form.vue b/src/pages/production/twist/receive/steelCore/form.vue
index 616e48f..4561801 100644
--- a/src/pages/production/twist/receive/steelCore/form.vue
+++ b/src/pages/production/twist/receive/steelCore/form.vue
@@ -1,6 +1,16 @@
 <template>
   <wd-form ref="form" :model="model" class="relative form_box">
     <wd-cell-group :border="true">
+      <wd-picker
+        v-model="diskMaterialValue"
+        :columns="diskMaterialOptions"
+        label="鑺嚎绫诲瀷"
+        label-width="100px"
+        prop="diskMaterial"
+        placeholder="璇烽�夋嫨鑺嚎绫诲瀷"
+        clearable
+        @confirm="handleDiskMaterialChange"
+      />
       <wd-input
         v-model="model.model"
         label="瑙勬牸鍨嬪彿"
@@ -48,6 +58,7 @@
 <script lang="ts" setup>
 import useFormData from "@/hooks/useFormData";
 import TwistApi from "@/api/product/twist";
+import ManageApi from "@/api/product/manage";
 import { useToast } from "wot-design-uni";
 
 const props = defineProps({
@@ -59,6 +70,10 @@
     type: Object,
     default: null,
   },
+  wireId: {
+    type: [String, Number],
+    default: undefined,
+  },
 });
 
 const emits = defineEmits(["refresh"]);
@@ -67,6 +82,7 @@
 const allListData = ref<any[]>([]); // 瀛樺偍瀹屾暣鍒楄〃鏁版嵁
 const toast = useToast();
 const { form: model } = useFormData({
+  diskMaterial: undefined, // 鑺嚎绫诲瀷
   model: undefined, // 瑙勬牸鍨嬪彿
   monofilamentNumber: undefined, // 鏍峰搧缂栧彿
   amount: undefined, // 鏁伴噺
@@ -75,11 +91,45 @@
   type: "閽㈣姱",
 });
 
+// 鑺嚎绫诲瀷瀛楀吀鏁版嵁
+const diskMaterialOptions = ref<Array<{ label: string; value: string }>>([]);
+const diskMaterialValue = ref("");
+
+// 鍔犺浇鑺嚎绫诲瀷瀛楀吀鏁版嵁
+const loadDiskMaterialDict = async () => {
+  try {
+    const res = await ManageApi.dictAPI("core_wire_type");
+    if (res.data && Array.isArray(res.data)) {
+      diskMaterialOptions.value = res.data.map((item: any) => ({
+        label: item.dictLabel || "",
+        value: item.dictValue || "",
+      }));
+    }
+  } catch (error) {
+    // 鍔犺浇瀛楀吀澶辫触锛岄潤榛樺鐞�
+  }
+};
+
+// 澶勭悊鑺嚎绫诲瀷閫夋嫨
+const handleDiskMaterialChange = (val: any) => {
+  model.diskMaterial = val.value;
+};
+
+// 鐩戝惉 model.diskMaterial 鍙樺寲锛屽悓姝ラ�夋嫨鍣ㄦ樉绀�
+watch(
+  () => model.diskMaterial,
+  (newValue) => {
+    diskMaterialValue.value = newValue || "";
+  },
+  { immediate: true }
+);
+
 // 鏂板鎻愪氦
 const submit = async () => {
+  const currentWireId = props.wireId || paramsId.value;
   const { code } = await TwistApi.addStrandedWireDish([
     {
-      wireId: paramsId.value,
+      wireId: currentWireId,
       ...model,
     },
   ]);
@@ -107,6 +157,7 @@
       // 淇濈暀鍘熸湁鏁版嵁锛岀劧鍚庢洿鏂颁慨鏀圭殑瀛楁
       const updatedItem = {
         ...item, // 鍏堜繚鐣欏師鏈夌殑鎵�鏈夋暟鎹�
+        diskMaterial: model.diskMaterial,
         model: model.model,
         monofilamentNumber: model.monofilamentNumber,
         amount: model.amount,
@@ -133,7 +184,6 @@
 const setFormData = (list: any[], currentEditId: number) => {
   // 瀹夊叏妫�鏌ワ細纭繚list鏄暟缁�
   if (!Array.isArray(list)) {
-    console.error("setFormData: list 鍙傛暟涓嶆槸鏁扮粍", list);
     return;
   }
 
@@ -144,12 +194,15 @@
   // 鎵惧埌褰撳墠缂栬緫椤瑰苟鍥炴樉鍒拌〃鍗�
   const currentItem = list.find((item) => item.id === currentEditId);
   if (currentItem) {
+    model.diskMaterial = currentItem.diskMaterial;
     model.model = currentItem.model;
     model.monofilamentNumber = currentItem.monofilamentNumber;
     model.amount = currentItem.amount;
     model.weight = currentItem.weight;
     model.supplier = currentItem.supplier;
     model.type = currentItem.type || "閽㈣姱";
+    // 璁剧疆鑺嚎绫诲瀷鐨勫洖鏄惧��
+    diskMaterialValue.value = currentItem.diskMaterial || "";
   }
 };
 
@@ -158,12 +211,14 @@
   () => props.editData,
   (newData) => {
     if (newData && props.mode === "edit") {
+      model.diskMaterial = newData.diskMaterial || "";
       model.model = newData.model || "";
       model.monofilamentNumber = newData.monofilamentNumber || "";
       model.amount = newData.amount || "";
       model.weight = newData.weight || "";
       model.supplier = newData.supplier || "";
       model.type = newData.type || "閽㈣姱";
+      diskMaterialValue.value = newData.diskMaterial || "";
     }
   },
   { immediate: true, deep: true }
@@ -171,16 +226,36 @@
 
 // 閲嶇疆琛ㄥ崟鏁版嵁
 const resetFormData = () => {
+  model.diskMaterial = undefined;
   model.model = undefined;
   model.monofilamentNumber = undefined;
   model.amount = undefined;
   model.weight = undefined;
   model.supplier = undefined;
   model.type = "閽㈣姱";
+  diskMaterialValue.value = "";
+};
+
+// 濉厖琛ㄥ崟鏁版嵁锛堢敤浜庢壂鐮佸悗鍥炴樉锛�
+const fillFormData = (data: any) => {
+  if (data) {
+    model.diskMaterial = data.diskMaterial || "";
+    model.model = data.model || "";
+    model.monofilamentNumber = data.monofilamentNumber || "";
+    model.amount = data.oneLength || data.amount || "";
+    model.weight = data.weight || "";
+    model.supplier = data.supplier || "";
+    model.type = data.type || "閽㈣姱";
+    diskMaterialValue.value = data.diskMaterial || "";
+  }
 };
 
 onLoad((options: any) => {
   paramsId.value = options.id;
+});
+
+onMounted(async () => {
+  await loadDiskMaterialDict();
 });
 
 defineExpose({
@@ -188,6 +263,7 @@
   submitEdit,
   setFormData,
   resetFormData,
+  fillFormData,
 });
 </script>
 
diff --git a/src/pages/production/twist/receive/steelCore/index.vue b/src/pages/production/twist/receive/steelCore/index.vue
index 85da5f2..1e6b91c 100644
--- a/src/pages/production/twist/receive/steelCore/index.vue
+++ b/src/pages/production/twist/receive/steelCore/index.vue
@@ -8,7 +8,12 @@
       @query="getList"
     >
       <template #top>
-        <CardTitle title="閽㈣姱棰嗙敤" :hideAction="true" :full="false" @action="addReport" />
+        <CardTitle title="鑺嚎棰嗙敤" :hideAction="false" :full="false">
+          <template #action>
+            <wd-button type="icon" icon="scan" color="#0D867F" @click="openScan"></wd-button>
+            <wd-button type="icon" icon="add-circle" color="#0D867F" @click="addReport"></wd-button>
+          </template>
+        </CardTitle>
       </template>
       <wd-card v-for="(item, index) in cardList" :key="index" type="rectangle" custom-class="round">
         <template #title>
@@ -28,7 +33,7 @@
         <wd-button type="text" @click="cancelAdd">鍙栨秷</wd-button>
         <wd-button type="text" @click="submitAdd">纭畾</wd-button>
       </view>
-      <SteelCore ref="addFormRef" mode="add" @refresh="reloadList" />
+      <SteelCore ref="addFormRef" mode="add" :wireId="paramsId" @refresh="reloadList" />
     </wd-popup>
     <wd-popup v-model="editDialog.visible" position="bottom" custom-class="yl-popup">
       <view class="action px-3">
@@ -38,10 +43,12 @@
       <SteelCore
         ref="editFormRef"
         mode="edit"
+        :wireId="paramsId"
         :editData="editDialog.currentItem"
         @refresh="reloadList"
       />
     </wd-popup>
+    <Scan ref="scanRef" emitName="scanSteelCore" />
     <wd-toast />
   </view>
 </template>
@@ -51,15 +58,19 @@
 import ProductionCard from "../../../components/ProductionCard.vue";
 import { useToast } from "wot-design-uni";
 import SteelCore from "./form.vue";
-import { onLoad } from "@dcloudio/uni-app";
+import { onLoad, onUnload, onShow, onHide } from "@dcloudio/uni-app";
 import ManageApi from "@/api/product/manage";
+import TwistApi from "@/api/product/twist";
 import zPaging from "@/components/z-paging/z-paging.vue";
+import Scan from "@/components/scan/index.vue";
 
 const paramsId = ref();
 const pagingRef = ref();
 const addFormRef = ref();
 const editFormRef = ref();
+const scanRef = ref();
 const toast = useToast();
+const isPageVisible = ref(false); // 鏍囪椤甸潰鏄惁鍙
 const addDialog = reactive({
   visible: false,
 });
@@ -147,8 +158,69 @@
   pagingRef.value.refresh();
 };
 
+// 鎵爜鐩稿叧鏂规硶
+const openScan = () => {
+  scanRef.value.triggerScan();
+};
+
+const getScanCode = async (code: any) => {
+  // 妫�鏌ラ〉闈㈡槸鍚﹀彲瑙侊紝濡傛灉涓嶅彲瑙佸垯涓嶅鐞嗘壂鐮佹暟鎹�
+  if (!isPageVisible.value) {
+    return;
+  }
+
+  try {
+    const parseData = JSON.parse(code.code);
+
+    // 妫�鏌ュ繀闇�瀛楁锛歮odel銆乻upplier銆乨iskMaterial
+    const requiredFields = ["model", "supplier", "diskMaterial"];
+    const missingFields = requiredFields.filter((field) => !parseData[field]);
+
+    if (missingFields.length > 0) {
+      toast.error(`浜岀淮鐮侀敊璇紝璇锋洿鎹簩缁寸爜锛乣);
+      return;
+    }
+
+    // 鎵撳紑鏂板寮规骞跺~鍏呮壂鐮佽幏鍙栫殑淇℃伅
+    addDialog.visible = true;
+
+    // 绛夊緟寮规鎵撳紑鍚庡~鍏呰〃鍗曟暟鎹�
+    // 浣跨敤鍙岄噸绛夊緟锛歯extTick + setTimeout 纭繚缁勪欢宸插畬鍏ㄦ寕杞�
+    nextTick(() => {
+      setTimeout(() => {
+        if (addFormRef.value) {
+          addFormRef.value.fillFormData(parseData);
+          toast.success("鎵爜鎴愬姛锛岃纭淇℃伅");
+        } else {
+          toast.error("琛ㄥ崟鍔犺浇澶辫触锛岃閲嶈瘯");
+        }
+      }, 200); // 寤惰繜200ms纭繚寮规鍜岀粍浠跺凡瀹屽叏娓叉煋
+    });
+  } catch (error) {
+    toast.error("浜岀淮鐮佸紓甯革紝璇锋洿鎹簩缁寸爜锛�");
+  }
+};
+
 onLoad((options: any) => {
+  // 寮�鍚箍鎾洃鍚簨浠�
+  uni.$on("scanSteelCore", getScanCode);
   paramsId.value = options.id;
+});
+
+onShow(() => {
+  // 椤甸潰鏄剧ず鏃舵爣璁颁负鍙
+  isPageVisible.value = true;
+});
+
+onHide(() => {
+  // 椤甸潰闅愯棌鏃舵爣璁颁负涓嶅彲瑙�
+  isPageVisible.value = false;
+});
+
+onUnload(() => {
+  // 鍙栨秷骞挎挱鐩戝惉浜嬩欢
+  uni.$off("scanSteelCore", getScanCode);
+  isPageVisible.value = false;
 });
 </script>
 
@@ -169,4 +241,8 @@
   display: flex;
   justify-content: space-between;
 }
+
+:deep(.wd-button__content) {
+  color: #0d867f;
+}
 </style>
diff --git a/src/pages/production/twist/report/draw.vue b/src/pages/production/twist/report/draw.vue
index 56eb4bc..b8e2a1e 100644
--- a/src/pages/production/twist/report/draw.vue
+++ b/src/pages/production/twist/report/draw.vue
@@ -62,7 +62,7 @@
             </template>
           </view>
         </wd-tab>
-        <wd-tab title="閽㈣姱棰嗙敤鑷" name="steel">
+        <wd-tab title="鑺嚎棰嗙敤鑷" name="steel">
           <view class="form-section">
             <wd-form :model="localSteelData">
               <wd-form-item label="瑙勬牸鍨嬪彿" prop="model" required>
diff --git a/src/pages/production/twist/report/form.vue b/src/pages/production/twist/report/form.vue
index 2e62f0c..cb804bb 100644
--- a/src/pages/production/twist/report/form.vue
+++ b/src/pages/production/twist/report/form.vue
@@ -2,103 +2,139 @@
   <wd-form ref="form" :model="model" class="relative form_box">
     <wd-cell-group :border="true">
       <wd-input
-        v-model="model.contractNo"
-        label="棰嗙敤鏉嗗彿"
-        label-width="100px"
-        prop="contractNo"
-        clearable
-        placeholder="璇疯緭鍏ラ鐢ㄦ潌鍙�"
-      />
-      <wd-input
-        v-model="model.status"
-        label="鏉嗛噸(kg)"
-        label-width="100px"
-        prop="status"
-        clearable
-        placeholder="璇疯緭鍏ユ潌閲�"
-      />
-      <wd-input
-        v-model="model.clientName"
-        label="鍗曚笣鐩樺彿"
-        label-width="100px"
-        prop="clientName"
-        clearable
-        placeholder="璇疯緭鍏ュ崟涓濈洏鍙�"
-      />
-      <wd-input
-        v-model="model.workbench"
-        label="瀹為檯閲嶉噺(kg)"
-        label-width="100px"
-        prop="workbench"
-        clearable
-        placeholder="璇疯緭鍏ュ疄闄呴噸閲�"
-      />
-      <wd-input
-        v-model="model.quality"
-        label="鐩橀暱(m)"
-        label-width="100px"
-        prop="quality"
-        clearable
-        placeholder="璇疯緭鍏ョ洏闀�"
-      />
-      <wd-input
-        v-model="model.specification"
-        label="鐞嗚閲嶉噺(kg)"
-        label-width="100px"
-        prop="specification"
-        clearable
-        placeholder="璇疯緭鍏ョ悊璁洪噸閲�"
-      />
-      <wd-input
-        v-model="model.disc"
-        label="瑙勬牸鍨嬪彿"
-        label-width="100px"
-        prop="disc"
-        clearable
-        placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
-      />
-      <wd-input
         v-model="model.actuallyLength"
-        label="瀹為檯鐩橀暱(m)"
+        label="鐢熶骇闀垮害(m)"
         label-width="100px"
         prop="actuallyLength"
         clearable
-        placeholder="璇疯緭鍏ュ疄闄呯洏闀�"
-      />
+        placeholder="璇疯緭鍏ョ敓浜ч暱搴�"
+        type="digit"
+      >
+        <template #label>
+          <span style="color: #f56c6c">鐢熶骇闀垮害(m)</span>
+        </template>
+      </wd-input>
+      <wd-input
+        v-model="model.tare"
+        label="鐩樺叿鐨噸(kg)"
+        label-width="100px"
+        prop="tare"
+        :disabled="!isFirstReport"
+        :clearable="isFirstReport"
+        :placeholder="isFirstReport ? '璇疯緭鍏ョ洏鍏风毊閲�' : '鐩樺叿鐨噸鑷姩甯﹀嚭'"
+        type="digit"
+      >
+        <template #label>
+          <span style="color: #f56c6c">鐩樺叿鐨噸(kg)</span>
+        </template>
+      </wd-input>
     </wd-cell-group>
   </wd-form>
 </template>
 
 <script lang="ts" setup>
+import { computed, watch } from "vue";
 import useFormData from "@/hooks/useFormData";
-import { useToast } from "wot-design-uni";
+import { useToast, dayjs } from "wot-design-uni";
 import TwistApi from "@/api/product/twist";
+
+// 瀹氫箟 props
+const props = defineProps<{
+  firstTareValue?: number;
+  teamId?: string | number | null;
+  isFirstReport?: boolean; // 鏄惁鏄涓�鏉℃姤宸�
+}>();
+
+// 璁$畻鏄惁鏄涓�鏉℃姤宸�
+const isFirstReport = computed(() => props.isFirstReport ?? true);
 
 const paramsId = ref();
 const toast = useToast();
-const { form: model } = useFormData({
-  poleNumber: undefined, // 棰嗙敤鏉嗗彿
-  poleWeight: undefined, // 鏉嗛噸(kg)
-  monofilamentNumber: undefined, // 鍗曚笣鐩樺彿
-  actuallyWeight: undefined, // 瀹為檯閲嶉噺(kg)
-  oneLength: undefined, // 鐩橀暱(m)
-  theoryWeight: undefined, // 鐞嗚閲嶉噺(kg)
-  model: undefined, // 瑙勬牸鍨嬪彿
-  actuallyLength: undefined, // 瀹為檯鐩橀暱(m)
+const { form: model, resetForm } = useFormData({
+  actuallyLength: undefined, // 鐢熶骇闀垮害(m)
+  tare: undefined, // 鐩樺叿鐨噸(kg)
 });
 
+// 涓昏〃鏁版嵁锛堜粠鐖剁粍浠朵紶鍏ワ級
+const mainTableData = ref<any>({});
+
+// 璁剧疆涓昏〃鏁版嵁
+const setMainTableData = (data: any) => {
+  mainTableData.value = data;
+};
+
+// 鐩戝惉 firstTareValue 鍙樺寲锛屽鏋滀笉鏄涓�鏉★紝鑷姩濉厖
+watch(
+  () => props.firstTareValue,
+  (newVal) => {
+    if (!isFirstReport.value && newVal !== undefined) {
+      model.tare = newVal;
+    }
+  },
+  { immediate: true }
+);
+
 const submit = async () => {
-  const { code } = await TwistApi.addTwistOutput({
+  // 鑾峰彇绗竴鏉℃暟鎹殑鐨噸鍊硷紝鐢ㄤ簬鍚庣画鏂板鐨勬姤宸�
+  const firstTareValue = props.firstTareValue;
+
+  // 濡傛灉涓昏〃鏁版嵁鏈幏鍙栵紝灏濊瘯閲嶆柊鑾峰彇
+  if (!mainTableData.value.model) {
+    try {
+      const { data } = await TwistApi.getTwistDetailById({
+        id: paramsId.value,
+      });
+      mainTableData.value = {
+        model: data.model,
+        totalLength: data.totalLength,
+        systemNo: data.systemNo,
+      };
+    } catch (error) {
+      console.error("鑾峰彇涓昏〃鏁版嵁澶辫触:", error);
+      toast.error("鑾峰彇瑙勬牸鍨嬪彿鏁版嵁澶辫触锛岃閲嶈瘯");
+      return false;
+    }
+  }
+
+  // 鍐嶆妫�鏌ヤ富琛ㄦ暟鎹�
+  if (!mainTableData.value.model) {
+    toast.error("瑙勬牸鍨嬪彿鏁版嵁鏈幏鍙栵紝璇烽噸璇�");
+    return false;
+  }
+
+  const submitData = {
+    teamId: props.teamId || null,
     wireId: paramsId.value,
-    ...model,
-  });
+    type: "缁炵嚎",
+    actuallyLength: model.actuallyLength,
+    tare: model.tare || firstTareValue,
+    // 浠庝富琛ㄨ幏鍙栫殑瀛楁
+    model: mainTableData.value.model, // 瑙勬牸鍨嬪彿
+    oneLength: mainTableData.value.totalLength,
+    systemNo: mainTableData.value.systemNo,
+    monofilamentNumber: undefined, // 鎵规鍙凤紙鍚庣鑷姩鐢熸垚锛�
+    // 鐢熶骇鏃ユ湡鑷姩璁剧疆涓哄綋鍓嶆椂闂�
+    productTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
+  };
+
+  // 璋冭瘯鏃ュ織
+  console.log("鎻愪氦鏁版嵁:", submitData);
+  console.log("涓昏〃鏁版嵁:", mainTableData.value);
+
+  const { code } = await TwistApi.addTwistOutput(submitData);
   if (code == 200) {
     toast.success("鎻愪氦鎴愬姛");
+    resetForm();
     return true;
   } else {
     toast.error("鎻愪氦澶辫触");
     return false;
   }
+};
+
+// 鑾峰彇琛ㄥ崟鏁版嵁
+const getFormData = () => {
+  return { ...model };
 };
 
 onLoad((options: any) => {
@@ -107,12 +143,12 @@
 
 defineExpose({
   submit,
+  getFormData,
+  setMainTableData,
 });
 </script>
 
 <style lang="scss" scoped>
-.form_box {
-}
 .submit_btn {
   position: absolute;
   bottom: 0;
diff --git a/src/pages/production/twist/report/index.vue b/src/pages/production/twist/report/index.vue
index f100b78..b9244b1 100644
--- a/src/pages/production/twist/report/index.vue
+++ b/src/pages/production/twist/report/index.vue
@@ -12,15 +12,23 @@
               <text class="text-[#0D867F] ml-2 font-medium">鐢熶骇浜�</text>
               <text class="text-[#333333] ml-2">{{ item.productUser }}</text>
             </view>
-            <view class="text-[#A8A8A8]" @click="toEdit">缂栬緫</view>
+            <!-- <view class="text-[#A8A8A8]" @click="toEdit">缂栬緫</view> -->
           </view>
         </template>
-        <ProductionCard :data="cardAttr" :value="item" />
+        <TwistReportCard :data="cardAttr" :value="item" />
         <template #footer>
           <wd-button size="small" plain style="margin-right: 10px" @click="toAttachment(item)">
             闄勪欢
           </wd-button>
-          <wd-button size="small" plain @click="handleSelfCheck(item.id)">鑷</wd-button>
+          <wd-button
+            size="small"
+            plain
+            style="margin-right: 10px"
+            @click="handleSelfCheck(item.id)"
+          >
+            鑷
+          </wd-button>
+          <wd-button size="small" plain type="error" @click="handleDelete(item)">鍒犻櫎</wd-button>
         </template>
       </wd-card>
     </z-paging>
@@ -30,7 +38,12 @@
         <wd-button type="text" @click="cancel">鍙栨秷</wd-button>
         <wd-button type="text" @click="submit">纭畾</wd-button>
       </view>
-      <TwistForm ref="twistFormRef" />
+      <TwistForm
+        ref="twistFormRef"
+        :first-tare-value="twistReportList.length > 0 ? twistReportList[0].tare : undefined"
+        :team-id="teamId"
+        :is-first-report="twistReportList.length === 0"
+      />
     </wd-popup>
     <wd-popup v-model="drawFormRef.visible" position="bottom" custom-class="yl-popup">
       <Draw
@@ -47,13 +60,15 @@
 <script setup lang="ts">
 import CardTitle from "@/components/card-title/index.vue";
 import TwistForm from "./form.vue";
-import { useToast } from "wot-design-uni";
-import ProductionCard from "../../components/ProductionCard.vue";
+import { useToast, dayjs } from "wot-design-uni";
+import TwistReportCard from "../components/TwistReportCard.vue";
 import { onLoad } from "@dcloudio/uni-app";
-import { ref, reactive } from "vue";
+import { ref, reactive, nextTick } from "vue";
 import ManageApi from "@/api/product/manage";
 import TwistApi from "@/api/product/twist";
 import Draw from "./draw.vue";
+import HomeApi from "@/api/home";
+import { setTeamId, getTeamId } from "@/utils/cache";
 
 const drawFormRef = reactive({
   visible: false,
@@ -95,36 +110,65 @@
 
 const cardAttr = ref<any[]>([
   {
-    label: "棰嗙敤鏉嗗彿",
-    prop: "poleNumber",
-  },
-  {
-    label: "鏉嗛噸(kg)",
-    prop: "poleWeight",
-  },
-  {
-    label: "鍗曚笣鐩樺彿",
+    label: "鎵规鍙�",
     prop: "monofilamentNumber",
+    span: 24,
   },
   {
-    label: "瀹為檯閲嶉噺(kg)",
-    prop: "actuallyWeight",
+    label: "璐ㄩ噺杩芥函鍙�",
+    prop: "systemNo",
+    span: 24,
   },
   {
     label: "鐩橀暱(m)",
     prop: "oneLength",
   },
   {
-    label: "鐞嗚閲嶉噺(kg)",
-    prop: "theoryWeight",
-  },
-  {
     label: "瑙勬牸鍨嬪彿",
     prop: "model",
+  },
+  {
+    label: "鐢熶骇闀垮害(m)",
+    prop: "actuallyLength",
+  },
+  {
+    label: "鐩樺叿鐨噸(kg)",
+    prop: "tare",
+  },
+  {
+    label: "鐢熶骇鏃ユ湡",
+    prop: "productTime",
+    span: 24,
+  },
+  {
+    label: "鍔犲伐鏃堕棿(h)",
+    prop: "processHour",
   },
 ]);
 
 const twistReportList = ref<any[]>([]);
+const teamId = ref<string | number | null>(null);
+
+// 鑾峰彇骞剁紦瀛樼彮缁処D
+const initTeamId = async () => {
+  // 鍏堝皾璇曚粠缂撳瓨鑾峰彇
+  const cachedTeamId = getTeamId();
+  if (cachedTeamId) {
+    teamId.value = cachedTeamId;
+    return;
+  }
+
+  // 濡傛灉缂撳瓨涓病鏈夛紝鍒欒皟鐢ㄦ帴鍙h幏鍙�
+  try {
+    const { data } = await HomeApi.getIndex();
+    if (data && data.team) {
+      teamId.value = data.team;
+      setTeamId(data.team);
+    }
+  } catch (error) {
+    console.error("鑾峰彇鐝粍ID澶辫触:", error);
+  }
+};
 
 const toEdit = () => {
   uni.navigateTo({
@@ -132,23 +176,83 @@
   });
 };
 
+// 涓昏〃鏁版嵁
+const mainTableData = ref<any>({});
+
+// 鑾峰彇涓昏〃鏁版嵁
+const getMainTableData = async () => {
+  try {
+    const { data } = await TwistApi.getTwistDetailById({
+      id: paramsId.value,
+    });
+    mainTableData.value = {
+      model: data.model,
+      totalLength: data.totalLength,
+      systemNo: data.systemNo,
+    };
+    // 璁剧疆涓昏〃鏁版嵁鍒拌〃鍗曠粍浠�
+    if (twistFormRef.value) {
+      twistFormRef.value.setMainTableData(mainTableData.value);
+    }
+  } catch (error) {
+    console.error("鑾峰彇涓昏〃鏁版嵁澶辫触:", error);
+  }
+};
+
 const addReport = async () => {
-  dialog.visible = true;
-  // 鎵撳紑鏂板寮圭獥鏃惰嚜鍔ㄦ墽琛�
-  // await showDrawPopup();
+  // 妫�鏌ユ槸鍚︽墍鏈夋暟鎹兘宸蹭繚瀛橈紙閮芥湁id锛�
+  if (twistReportList.value.length > 0 && twistReportList.value.every((item) => item.id)) {
+    // 纭繚涓昏〃鏁版嵁宸茶幏鍙�
+    if (!mainTableData.value.model) {
+      await getMainTableData();
+    }
+    dialog.visible = true;
+    // 绛夊緟寮圭獥鎵撳紑鍚庤缃暟鎹�
+    await nextTick();
+    if (twistFormRef.value) {
+      twistFormRef.value.setMainTableData(mainTableData.value);
+    }
+  } else if (twistReportList.value.length === 0) {
+    // 纭繚涓昏〃鏁版嵁宸茶幏鍙�
+    if (!mainTableData.value.model) {
+      await getMainTableData();
+    }
+    dialog.visible = true;
+    // 绛夊緟寮圭獥鎵撳紑鍚庤缃暟鎹�
+    await nextTick();
+    if (twistFormRef.value) {
+      twistFormRef.value.setMainTableData(mainTableData.value);
+    }
+  } else {
+    toast.warning("璇峰厛淇濆瓨鏈潯鏁版嵁锛屽啀鏂板");
+  }
 };
 
 const submit = async () => {
+  // 楠岃瘉蹇呭~瀛楁 - 鏍规嵁鍙傝�冧唬鐮侊紝闇�瑕佹鏌ョ敓浜ч暱搴﹀拰鐩樺叿鐨噸
+  const formData = twistFormRef.value?.getFormData?.() || {};
+  const firstTareValue =
+    twistReportList.value.length > 0 ? twistReportList.value[0].tare : undefined;
+
+  if (!formData.actuallyLength) {
+    toast.warning("璇疯緭鍏ョ敓浜ч暱搴﹀悗鍐嶆彁浜�");
+    return;
+  }
+
+  if (!formData.tare && !firstTareValue) {
+    toast.warning("璇疯緭鍏ョ洏鍏风毊閲嶅悗鍐嶆彁浜�");
+    return;
+  }
+
   const isSuccess = await twistFormRef.value.submit();
-  dialog.visible = !isSuccess; // 濡傛灉鎻愪氦鎴愬姛锛屽叧闂脊绐�
   if (isSuccess) {
-    // 鎻愪氦鎴愬姛鍚庢墽琛�
-    // showDrawPopup();
+    dialog.visible = false;
+    // 鎻愪氦鎴愬姛鍚庡埛鏂板垪琛�
+    pagingRef.value?.reload();
   }
 };
 
 const cancel = () => {
-  toast.show("鍙栨秷");
   dialog.visible = false;
 };
 
@@ -197,6 +301,35 @@
   });
 };
 
+// 鍒犻櫎鎶ュ伐璁板綍
+const handleDelete = (item: any) => {
+  uni.showModal({
+    title: "鎻愮ず",
+    content: "纭畾鍒犻櫎鍚楋紵",
+    success: async (res) => {
+      if (res.confirm) {
+        try {
+          if (item.id) {
+            const { code } = await TwistApi.deleteWireOutput({ id: item.id });
+            if (code == 200) {
+              toast.success("鍒犻櫎鎴愬姛");
+              // 鍒锋柊鍒楄〃
+              pagingRef.value?.reload();
+            } else {
+              toast.error("鍒犻櫎澶辫触");
+            }
+          } else {
+            toast.warning("璇ヨ褰曞皻鏈繚瀛橈紝鏃犳硶鍒犻櫎");
+          }
+        } catch (error) {
+          console.error("鍒犻櫎澶辫触:", error);
+          toast.error("鍒犻櫎澶辫触锛岃閲嶈瘯");
+        }
+      }
+    },
+  });
+};
+
 // 淇濈暀鍘熸湁鐨刢onfirm鍑芥暟锛岀敤浜庡叾浠栧湴鏂硅皟鐢�
 // const confirm = async () => {
 //   await showDrawPopup();
@@ -208,11 +341,23 @@
     wireId: paramsId.value,
     type: "缁炵嚎",
   });
+  // 鏍煎紡鍖栫敓浜ф棩鏈�
+  if (Array.isArray(data)) {
+    data.forEach((item: any) => {
+      if (item.productTime) {
+        item.productTime = dayjs(item.productTime).format("YYYY-MM-DD HH:mm:ss");
+      }
+    });
+  }
   pagingRef.value.complete(data);
 };
 
-onLoad((options: any) => {
+onLoad(async (options: any) => {
   paramsId.value = options.id;
+  // 鑾峰彇骞剁紦瀛樼彮缁処D
+  await initTeamId();
+  // 鑾峰彇涓昏〃鏁版嵁
+  await getMainTableData();
   showDrawPopup();
 });
 </script>
diff --git a/src/pages/production/wire/attachment/index.vue b/src/pages/production/wire/attachment/index.vue
index c654299..d6394e8 100644
--- a/src/pages/production/wire/attachment/index.vue
+++ b/src/pages/production/wire/attachment/index.vue
@@ -17,26 +17,54 @@
     <view class="attachment-list">
       <wd-status-tip v-if="attachmentList.length === 0" image="content" tip="鏆傛棤闄勪欢" />
 
-      <wd-card
-        v-for="item in attachmentList"
-        :key="item.id"
-        type="rectangle"
-        custom-class="attachment-card"
-        :border="false"
-      >
-        <view class="attachment-item" @click="previewAttachment(item)">
-          <view class="attachment-info">
-            <view class="attachment-name">{{ item.bucketFileName || item.name }}</view>
-            <view class="attachment-meta">
-              <text class="file-type">{{ getFileType(item.bucketFileName) }}</text>
-              <text class="upload-time">{{ formatTime(item.createTime) }}</text>
+      <view v-for="item in attachmentList" :key="item.id" class="attachment-card">
+        <view class="media-wrapper" @click="previewAttachment(item)">
+          <!-- 鍥剧墖棰勮 -->
+          <template v-if="isImageType(item.url)">
+            <image
+              v-if="!item.loadError"
+              :src="getFullUrl(item.url)"
+              mode="aspectFill"
+              class="media-preview"
+              @error="onImageError(item)"
+              @load="onImageLoad(item)"
+            />
+            <!-- 鍥剧墖鍔犺浇澶辫触鏄剧ず榛樿鍥炬爣 -->
+            <view v-else class="file-icon-wrapper">
+              <wd-icon name="picture" size="48px" color="#ccc" />
+              <text class="file-name error-text">鍔犺浇澶辫触</text>
             </view>
+          </template>
+
+          <!-- 瑙嗛棰勮 -->
+          <template v-else-if="isVideoType(item.url)">
+            <video
+              v-if="!item.loadError"
+              :src="getFullUrl(item.url)"
+              class="media-preview"
+              :controls="false"
+              :show-center-play-btn="false"
+              @error="onVideoError(item)"
+            />
+            <!-- 瑙嗛鍔犺浇澶辫触鏄剧ず榛樿鍥炬爣 -->
+            <view v-else class="file-icon-wrapper">
+              <wd-icon name="video" size="48px" color="#ccc" />
+              <text class="file-name error-text">鍔犺浇澶辫触</text>
+            </view>
+          </template>
+
+          <!-- 鍏朵粬鏂囦欢绫诲瀷鏄剧ず鍥炬爣 -->
+          <view v-else class="file-icon-wrapper">
+            <wd-icon name="file-outline" size="48px" color="#999" />
+            <text class="file-name">鏂囦欢</text>
           </view>
-          <view class="attachment-actions" @click.stop>
-            <wd-icon name="delete" color="#ff4757" @click="deleteAttachment(item.id)" />
+
+          <!-- 鍒犻櫎鎸夐挳 -->
+          <view class="delete-btn" @click.stop="deleteAttachment(item.id)">
+            <wd-icon name="delete" color="#fff" size="20px" />
           </view>
         </view>
-      </wd-card>
+      </view>
     </view>
 
     <wd-toast />
@@ -48,6 +76,12 @@
 import { useToast } from "wot-design-uni";
 import AttachmentAPI from "@/api/product/attachment";
 
+// H5 浣跨敤 VITE_APP_BASE_API 浣滀负浠g悊璺緞锛屽叾浠栧钩鍙颁娇鐢� VITE_APP_API_URL 浣滀负璇锋眰璺緞
+let baseUrl = import.meta.env.VITE_APP_API_URL;
+// #ifdef H5
+baseUrl = import.meta.env.VITE_APP_BASE_API;
+// #endif
+
 const toast = useToast();
 
 // 椤甸潰鍙傛暟
@@ -56,6 +90,57 @@
 const attachmentList = ref<any[]>([]);
 
 const detailData = ref<any>({});
+
+// 鑾峰彇瀹屾暣鐨勫浘鐗�/瑙嗛 URL
+const getFullUrl = (url: string) => {
+  if (!url) return "";
+  // 濡傛灉宸茬粡鏄畬鏁寸殑 URL锛坔ttp 鎴� https 寮�澶达級锛岀洿鎺ヨ繑鍥�
+  if (url.startsWith("http://") || url.startsWith("https://")) {
+    return url;
+  }
+  // 濡傛灉鏄浉瀵硅矾寰勶紝鎷兼帴鍩虹 URL
+  return `${baseUrl}${url.startsWith("/") ? "" : "/"}${url}`;
+};
+
+// 浠� URL 鎴栨枃浠跺悕涓彁鍙栨墿灞曞悕
+const getExtension = (urlOrFileName: string) => {
+  if (!urlOrFileName) return "";
+  // 绉婚櫎鏌ヨ鍙傛暟鍜屽搱甯�
+  const cleanUrl = urlOrFileName.split("?")[0].split("#")[0];
+  // 鑾峰彇鏈�鍚庝竴涓偣鍚庨潰鐨勫唴瀹�
+  const extension = cleanUrl.split(".").pop()?.toLowerCase();
+  return extension || "";
+};
+
+// 鍒ゆ柇鏄惁涓哄浘鐗囩被鍨�
+const isImageType = (urlOrFileName: string) => {
+  const extension = getExtension(urlOrFileName);
+  return ["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(extension);
+};
+
+// 鍒ゆ柇鏄惁涓鸿棰戠被鍨�
+const isVideoType = (urlOrFileName: string) => {
+  const extension = getExtension(urlOrFileName);
+  return ["mp4", "mov", "avi", "wmv", "flv", "mkv", "webm"].includes(extension);
+};
+
+// 鍥剧墖鍔犺浇鎴愬姛
+const onImageLoad = (item: any) => {
+  item.loadError = false;
+};
+
+// 鍥剧墖鍔犺浇澶辫触
+const onImageError = (item: any) => {
+  console.error("鍥剧墖鍔犺浇澶辫触:", item.url);
+  item.loadError = true;
+};
+
+// 瑙嗛鍔犺浇澶辫触
+const onVideoError = (item: any) => {
+  console.error("瑙嗛鍔犺浇澶辫触:", item.url);
+  item.loadError = true;
+};
+
 // 鑾峰彇闄勪欢鍒楄〃
 const getAttachmentList = async (data: any) => {
   try {
@@ -252,24 +337,24 @@
 // 棰勮闄勪欢
 const previewAttachment = (item: any) => {
   // 鏍规嵁鏂囦欢绫诲瀷杩涜棰勮
-  const fileName = item.bucketFileName || item.name;
-  const fileType = getFileType(fileName);
+  const fileType = getFileType(item.url);
+  const fullUrl = getFullUrl(item.url);
 
   if (fileType.startsWith("image")) {
     // 鍥剧墖棰勮
     uni.previewImage({
-      urls: [item.url],
-      current: item.url,
+      urls: [fullUrl],
+      current: fullUrl,
     });
   } else {
     // 鍏朵粬鏂囦欢绫诲瀷锛屽彲浠ヤ笅杞芥垨鎵撳紑
     uni.downloadFile({
-      url: item.url,
+      url: fullUrl,
       success: (res) => {
         uni.openDocument({
           filePath: res.tempFilePath,
           success: () => {
-            console.log("鎵撳紑鏂囨。鎴愬姛");
+            // 鎵撳紑鏂囨。鎴愬姛
           },
           fail: (error) => {
             console.error("鎵撳紑鏂囨。澶辫触:", error);
@@ -286,9 +371,9 @@
 };
 
 // 鑾峰彇鏂囦欢绫诲瀷
-const getFileType = (fileName: string) => {
-  if (!fileName) return "unknown";
-  const extension = fileName.split(".").pop()?.toLowerCase();
+const getFileType = (urlOrFileName: string) => {
+  if (!urlOrFileName) return "unknown";
+  const extension = getExtension(urlOrFileName);
   switch (extension) {
     case "jpg":
     case "jpeg":
@@ -297,6 +382,14 @@
     case "bmp":
     case "webp":
       return "image";
+    case "mp4":
+    case "mov":
+    case "avi":
+    case "wmv":
+    case "flv":
+    case "mkv":
+    case "webm":
+      return "video";
     case "pdf":
       return "pdf";
     case "doc":
@@ -357,43 +450,80 @@
 }
 
 .attachment-list {
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  gap: 8px;
+
   .attachment-card {
-    margin-bottom: 12px;
-    border-radius: 4px;
+    width: 100%;
+    aspect-ratio: 1;
   }
 }
 
-.attachment-item {
-  display: flex;
-  align-items: center;
-  padding: 12px;
+.media-wrapper {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  border-radius: 8px;
+  overflow: hidden;
+  background: #f5f5f5;
 
-  .attachment-info {
-    flex: 1;
+  .media-preview {
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+  }
 
-    .attachment-name {
-      font-size: 16px;
-      font-weight: 500;
-      color: #333;
-      margin-bottom: 4px;
-      word-break: break-all;
-    }
+  .file-icon-wrapper {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+    height: 100%;
+    padding: 8px;
+    text-align: center;
 
-    .attachment-meta {
-      display: flex;
-      gap: 12px;
+    .file-name {
+      margin-top: 8px;
       font-size: 12px;
-      color: #999;
+      color: #666;
+      word-break: break-all;
+      display: -webkit-box;
+      -webkit-line-clamp: 2;
+      line-clamp: 2;
+      -webkit-box-orient: vertical;
+      overflow: hidden;
+
+      &.error-text {
+        color: #ff4757;
+      }
     }
   }
 
-  .attachment-actions {
-    margin-left: 12px;
-
-    :deep(.wd-icon) {
-      font-size: 20px;
-      cursor: pointer;
-    }
+  .delete-btn {
+    position: absolute;
+    top: 4px;
+    right: 4px;
+    width: 28px;
+    height: 28px;
+    border-radius: 50%;
+    background: rgba(0, 0, 0, 0.5);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    z-index: 10;
   }
 }
 </style>
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/routingInspection/detail/indexJX.vue b/src/pages/routingInspection/detail/indexJX.vue
new file mode 100644
index 0000000..f655cb6
--- /dev/null
+++ b/src/pages/routingInspection/detail/indexJX.vue
@@ -0,0 +1,867 @@
+<template>
+  <view class="fixed-header">
+    <view class="header-container">
+      <wd-button
+        icon="file-add"
+        :round="false"
+        size="small"
+        custom-class="add_btn"
+        @click="editList"
+        v-if="!isEdit"
+      >
+        缂栬緫
+      </wd-button>
+      <wd-button
+        icon="close"
+        type="info"
+        :round="false"
+        size="small"
+        custom-class="add_btn"
+        @click="close"
+        v-if="isEdit"
+      >
+        鍙栨秷
+      </wd-button>
+      <wd-button
+        icon="check"
+        type="success"
+        :round="false"
+        size="small"
+        custom-class="add_btn"
+        @click="saveList"
+        v-if="isEdit"
+      >
+        淇濆瓨
+      </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>
+    </view>
+  </view>
+  <view class="list">
+    <!-- 鍩烘湰淇℃伅妯″潡 -->
+    <wd-row>
+      <view style="margin: 10rpx">
+        <text class="title">{{ "鍩烘湰淇℃伅" }}</text>
+      </view>
+      <wd-col :span="24">
+        <wd-form-item label="鏃ユ湡" prop="recordDate">
+          {{ formatDate(recordData.fixedInfo?.recordDate) }}
+        </wd-form-item>
+        <wd-form-item label="鐝" prop="workShift">
+          {{ formatValue(recordData.fixedInfo?.workShift) }}
+        </wd-form-item>
+        <wd-form-item label="鍨嬪彿瑙勬牸" prop="model">
+          {{ formatValue(recordData.fixedInfo?.model) }}
+        </wd-form-item>
+        <wd-form-item label="鎴愬搧绾跨洏鍙�" prop="systemNo">
+          {{ formatValue(recordData.fixedInfo?.systemNo) }}
+        </wd-form-item>
+        <wd-form-item label="璁板綍浜�" prop="createUserName">
+          {{ formatValue(recordData.fixedInfo?.createUserName) }}
+        </wd-form-item>
+        <wd-form-item label="鏈哄彴" prop="deviceModel">
+          {{ formatValue(recordData.fixedInfo?.deviceModel) }}
+        </wd-form-item>
+        <wd-form-item label="浜у搧绫诲埆" prop="productType">
+          {{ formatValue(recordData.fixedInfo?.productType) }}
+        </wd-form-item>
+        <wd-form-item label="鐢熶骇闀垮害" prop="actuallyLength">
+          {{ formatValue(recordData.fixedInfo?.actuallyLength, "m") }}
+        </wd-form-item>
+        <wd-form-item label="寮犲姏璁剧疆" prop="tensionSetting">
+          {{ formatValue(recordData.fixedInfo?.tensionSetting, "N/m") }}
+        </wd-form-item>
+        <!-- 缁炲埗澶栧緞锛堝彲缂栬緫锛� -->
+        <wd-form-item label="缁炲悎澶栧緞" prop="twistedOuterDiameter" required>
+          <template v-if="isEdit">
+            <wd-input
+              v-model="formData.twistedOuterDiameter"
+              placeholder="璇疯緭鍏ョ粸鍚堝寰勶紙mm锛�"
+              type="number"
+            />
+          </template>
+          <template v-else>
+            {{ formatValue(formData.twistedOuterDiameter, "mm") }}
+          </template>
+        </wd-form-item>
+      </wd-col>
+    </wd-row>
+
+    <!-- 宸ヨ壓璁板綍璇︽儏妯″潡 -->
+    <wd-row>
+      <view style="margin: 10rpx">
+        <text class="title">{{ "宸ヨ壓璁板綍璇︽儏" }}</text>
+      </view>
+      <wd-col :span="24">
+        <wd-form-item label="璁板綍浣嶇疆" prop="recordPosition">
+          {{ recordData.structureInfo?.recordPosition || "-" }}
+        </wd-form-item>
+        <wd-form-item label="璁板綍浜�" prop="createUserName">
+          {{ recordData.structureInfo?.createUserName || "-" }}
+        </wd-form-item>
+        <wd-form-item label="鐘舵��" prop="status">
+          <wd-tag custom-class="space" :type="getStatusType(recordData.structureInfo?.status)">
+            {{ getStatusText(recordData.structureInfo?.status) }}
+          </wd-tag>
+        </wd-form-item>
+      </wd-col>
+    </wd-row>
+
+    <!-- 缁撴瀯妫�鏌ユā鍧� -->
+    <wd-row>
+      <view style="margin: 10rpx">
+        <text class="title">{{ "缁撴瀯妫�鏌�" }}</text>
+      </view>
+      <wd-col :span="24">
+        <wd-form-item label="鎴愬搧缁撴瀯" prop="structureFormula" required>
+          <template v-if="isEdit">
+            <wd-input v-model="formData.structureFormula" placeholder="璇疯緭鍏ユ垚鍝佺粨鏋�" />
+          </template>
+          <template v-else>
+            {{ formData.structureFormula || "-" }}
+          </template>
+        </wd-form-item>
+      </wd-col>
+    </wd-row>
+
+    <!-- 缁撴瀯鏍囧噯鍊煎拰瀹炴祴锛堝彲缂栬緫锛� -->
+    <wd-row v-if="formData.structureItems.length">
+      <view style="margin: 10rpx">
+        <text class="title">{{ "缁撴瀯鏍囧噯鍊煎拰瀹炴祴" }}</text>
+      </view>
+      <wd-col
+        :span="24"
+        v-for="(item, index) in formData.structureItems"
+        :key="index"
+        style="padding-bottom: 10px"
+      >
+        <wd-form-item
+          prop="structureItemsGroup"
+          :label="formatValue(item.structureName)"
+          label-width="400rpx"
+          style="color: red"
+          required
+        ></wd-form-item>
+        <wd-form-item label="鏍囧噯鍊�" prop="structureValue" required>
+          {{ formatValue(item.structureValue) }}
+        </wd-form-item>
+        <wd-form-item label="瀹炴祴鏍规暟" prop="actualValue1" required>
+          <template v-if="isEdit">
+            <wd-input v-model="item.actualValue1" placeholder="璇疯緭鍏ュ疄娴嬫牴鏁�" type="number" />
+          </template>
+          <template v-else>
+            {{ formatValue(item.actualValue1, "鏍�") }}
+          </template>
+        </wd-form-item>
+        <wd-form-item label="瀹炴祴鐩村緞" prop="actualValue2" required>
+          <template v-if="isEdit">
+            <wd-input
+              v-model="item.actualValue2"
+              placeholder="璇疯緭鍏ュ疄娴嬬洿寰勶紙mm锛�"
+              type="number"
+            />
+          </template>
+          <template v-else>
+            {{ formatValue(item.actualValue2, "mm") }}
+          </template>
+        </wd-form-item>
+      </wd-col>
+    </wd-row>
+
+    <!-- 缁炵嚎宸ヨ壓璐ㄩ噺鎺у埗锛堝彲缂栬緫锛� -->
+    <wd-row v-if="formData.inspectTwist.length">
+      <view style="margin: 10rpx">
+        <text class="title">{{ "缁炵嚎宸ヨ壓璐ㄩ噺鎺у埗" }}</text>
+      </view>
+      <wd-col
+        :span="24"
+        v-for="(item, index) in formData.inspectTwist"
+        :key="index"
+        style="padding-bottom: 10px"
+      >
+        <wd-form-item
+          :label="formatValue(item.twistName)"
+          label-width="400rpx"
+          style="color: red"
+          prop="inspectTwistGroup"
+          required
+        ></wd-form-item>
+        <wd-form-item label="缁炲悜" prop="direction" required>
+          <template v-if="isEdit">
+            <wd-select-picker
+              label=""
+              v-model="item.direction"
+              :columns="twistDirectionOptions"
+              type="radio"
+              placeholder="璇烽�夋嫨缁炲悜"
+              :clearable="false"
+            ></wd-select-picker>
+          </template>
+          <template v-else>
+            {{ formatValue(item.direction) }}
+          </template>
+        </wd-form-item>
+        <wd-form-item label="鑺傝窛" prop="pitch" required>
+          <template v-if="isEdit">
+            <wd-input
+              v-model="item.pitch"
+              placeholder="璇疯緭鍏ヨ妭璺濓紙mm锛�"
+              type="number"
+              @input="updatePitchRatio(item)"
+            />
+          </template>
+          <template v-else>
+            {{ formatValue(item.pitch, "mm") }}
+          </template>
+        </wd-form-item>
+        <wd-form-item label="鑺傚緞姣�" prop="pitchRatio" required>
+          {{ formatValue(item.pitchRatio) }}
+        </wd-form-item>
+      </wd-col>
+    </wd-row>
+
+    <!-- 澶栬鍜岀粨璁猴紙鍙紪杈戯級 -->
+    <wd-row>
+      <view style="margin: 10rpx">
+        <text class="title">{{ "澶栬鍜岀粨璁�" }}</text>
+      </view>
+      <wd-col :span="24">
+        <wd-form-item label="浜у搧澶栬" prop="productAppearance" required>
+          <template v-if="isEdit">
+            <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>
+            </view>
+          </template>
+          <template v-else>
+            {{ formatProductAppearance(formData.productAppearance) }}
+          </template>
+        </wd-form-item>
+        <wd-form-item label="缁撹" prop="conclusion" required>
+          <template v-if="isEdit">
+            <wd-radio-group v-model="formData.conclusion" inline class="conclusion-radio-group">
+              <wd-radio
+                v-for="(opt, idx) in conclusionOptions"
+                :key="idx"
+                :value="opt.value"
+                shape="dot"
+              >
+                {{ opt.label }}
+              </wd-radio>
+            </wd-radio-group>
+          </template>
+          <template v-else>
+            {{ formatValue(formData.conclusion) }}
+          </template>
+        </wd-form-item>
+      </wd-col>
+    </wd-row>
+
+    <!-- 宸℃缁撴灉妯″潡 -->
+    <wd-row>
+      <view style="margin: 10rpx">
+        <text class="title">{{ "宸℃缁撴灉" }}</text>
+      </view>
+      <wd-col :span="24">
+        <wd-form-item label="鏍峰搧鏄惁榻愬叏" prop="isFully" required>
+          <template v-if="isEdit">
+            <wd-radio-group v-model="formData.isFully" inline class="conclusion-radio-group">
+              <wd-radio
+                v-for="(opt, idx) in sampleCompleteOptions"
+                :key="idx"
+                :value="opt.value"
+                shape="dot"
+              >
+                {{ opt.label }}
+              </wd-radio>
+            </wd-radio-group>
+          </template>
+          <template v-else>
+            {{ formatValue(formData.isFully) }}
+          </template>
+        </wd-form-item>
+      </wd-col>
+    </wd-row>
+
+    <!-- 闄勪欢妯″潡锛堝惈涓婁紶鍔熻兘锛� -->
+    <wd-row class="attachment-section">
+      <view style="margin: 10rpx">
+        <text class="title">{{ "闄勪欢" }}</text>
+      </view>
+      <wd-col :span="24">
+        <AttachmentUpload
+          :detailData="detailData"
+          :isEdit="isEdit"
+          :deviceType="paramsType"
+          ref="attachmentRef"
+          v-if="detailDataLoaded"
+        />
+      </wd-col>
+    </wd-row>
+
+    <wd-popup v-model="show" custom-style="border-radius:32rpx;" @close="handleClose">
+      <div class="image-preview">
+        <img :src="previewImageUrl" alt="棰勮鍥剧墖" style="width: 100%; height: auto" />
+      </div>
+    </wd-popup>
+    <wd-toast />
+  </view>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, computed, onUnmounted } from "vue";
+import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
+import RoutingInspectionApi from "@/api/routingInspection/routingInspection";
+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 recordData = ref<any>({ structureInfo: { files: [], structureRecordResult: {} } });
+const show = ref(false);
+const previewImageUrl = ref("");
+const isEdit = ref(false);
+const tempFiles = ref<any[]>([]); // 涓存椂瀛樺偍鏂颁笂浼犵殑闄勪欢
+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({
+  twistedOuterDiameter: "", // 缁炲埗澶栧緞
+  structureFormula: "", // 鎴愬搧缁撴瀯
+  structureItems: [], // 缁撴瀯鏍囧噯鍊煎拰瀹炴祴
+  inspectTwist: [], // 缁炵嚎宸ヨ壓璐ㄩ噺鎺у埗
+  productAppearance: [] as string[], // 浜у搧澶栬锛堟敼涓烘暟缁勫瓨鍌ㄩ�変腑鍊硷級
+  conclusion: "", // 缁撹锛堟敼涓烘暟缁勫瓨鍌ㄩ�変腑鍊硷級
+  isFully: "", // 鏍峰搧鏄惁榻愬叏
+});
+
+const twistDirectionOptions = [
+  { label: "宸﹀悜", value: "宸﹀悜" },
+  { label: "鍙冲悜", value: "鍙冲悜" },
+];
+
+const appearanceOptions = [
+  { label: "鏃犲瑙傞棶棰�", value: "鏃犲瑙傞棶棰�" },
+  { label: "琛ㄩ潰鍒掍激", value: "琛ㄩ潰鍒掍激" },
+  { label: "鐩村緞涓嶅潎", value: "鐩村緞涓嶅潎" },
+  { label: "鍏朵粬缂洪櫡", value: "鍏朵粬缂洪櫡" },
+];
+
+const conclusionOptions = [
+  { label: "鍚堟牸", value: "鍚堟牸" },
+  { label: "涓嶅悎鏍�", value: "涓嶅悎鏍�" },
+];
+const sampleCompleteOptions = [
+  { label: "鏄�", value: "鏄�" },
+  { label: "鍚�", value: "鍚�" },
+];
+
+const initFormData = () => {
+  const structureResult = recordData.value.structureInfo?.structureRecordResult || {};
+  const inspectionResult = recordData.value.inspectionResult || {};
+
+  formData.twistedOuterDiameter =
+    recordData.value.structureInfo.structureRecordResult.twistedOuterDiameter || "";
+  formData.structureFormula = structureResult.inspectStructure?.structureFormula || "";
+  formData.isFully = inspectionResult.isFully || "";
+  formData.conclusion = structureResult.conclusion || "";
+
+  // 鍒濆鍖栦骇鍝佸瑙�
+  const appearance = Array.isArray(structureResult.productAppearance)
+    ? structureResult.productAppearance
+    : structureResult.productAppearance
+      ? [structureResult.productAppearance]
+      : [];
+  formData.productAppearance = appearance;
+
+  formData.structureItems = JSON.parse(
+    JSON.stringify(structureResult.inspectStructure?.structureItems || [])
+  );
+  formData.inspectTwist = JSON.parse(JSON.stringify(structureResult.inspectTwist || []));
+
+  formData.inspectTwist.forEach((item: any) => {
+    if (!item.direction) item.direction = "";
+  });
+};
+
+const getDetailData = async (id: string, deviceType: string) => {
+  try {
+    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(); // 鏁版嵁杩斿洖鍚庡垵濮嬪寲琛ㄥ崟
+    detailDataLoaded.value = true; // 鏁版嵁鍔犺浇瀹屾垚鍚庯紝娓叉煋瀛愮粍浠�
+    console.log("鐖剁粍浠�-鏁版嵁灏辩华鍚庢墦鍗�");
+  } catch (error) {
+    console.error("鑾峰彇璇︽儏澶辫触:", error);
+    uni.showToast({ title: "鍔犺浇澶辫触", icon: "error" });
+  }
+};
+
+// 椤甸潰鍔犺浇
+onLoad((options: any) => {
+  try {
+    paramsId.value = options.id;
+    paramsType.value = options.deviceType;
+    getDetailData(options.id, options.deviceType);
+  } catch (error) {
+    console.error("鑾峰彇璇︽儏澶辫触:", error);
+    uni.showToast({ title: "鍔犺浇澶辫触", icon: "error" });
+  }
+});
+
+// 缂栬緫妯″紡鍒囨崲
+const editList = () => {
+  isEdit.value = true;
+};
+
+// 鍙栨秷缂栬緫锛堥噸缃〃鍗曪級
+const close = () => {
+  isEdit.value = false;
+  tempFiles.value = [];
+  initFormData();
+};
+
+// 淇濆瓨缂栬緫锛堝惈蹇呭~椤规牎楠岋級
+const saveList = async () => {
+  // 1. 鍩虹瀛楁鏍¢獙
+  if (!formData.structureFormula) 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 (!formData.isFully) return uni.showToast({ title: "鏍峰搧鏄惁榻愬叏涓哄繀濉」", icon: "none" });
+  // 2. 缁撴瀯椤瑰惊鐜牎楠�
+  for (const item of formData.structureItems) {
+    if (!item.structureValue)
+      return uni.showToast({ title: `${item.structureName}鏍囧噯鍊间负蹇呭~椤筦, icon: "none" });
+    if (!item.actualValue1)
+      return uni.showToast({ title: `${item.structureName}瀹炴祴鏍规暟涓哄繀濉」`, icon: "none" });
+    if (!item.actualValue2)
+      return uni.showToast({ title: `${item.structureName}瀹炴祴鐩村緞涓哄繀濉」`, icon: "none" });
+  }
+
+  // 3. 缁炵嚎宸ヨ壓椤瑰惊鐜牎楠�
+  for (const item of formData.inspectTwist) {
+    if (!item.direction)
+      return uni.showToast({ title: `${item.twistName}缁炲悜涓哄繀濉」`, icon: "none" });
+    if (!item.pitch) return uni.showToast({ title: `${item.twistName}鑺傝窛涓哄繀濉」`, icon: "none" });
+    if (!item.pitchRatio)
+      return uni.showToast({ title: `${item.twistName}鑺傚緞姣斾负蹇呭~椤筦, 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];
+  try {
+    const res = await RoutingInspectionApi.strandedPatrolCheckInspection({
+      deviceUid: deviceUid.value,
+      id: paramsId.value,
+      inspectionResult: {
+        twistedOuterDiameter: formData.twistedOuterDiameter,
+        structureFormula: formData.structureFormula,
+        structureItems: formData.structureItems,
+        inspectTwist: formData.inspectTwist,
+        productAppearance: formData.productAppearance,
+        conclusion: formData.conclusion,
+        isFully: formData.isFully,
+      },
+      result: {
+        isFully: formData.isFully,
+      },
+      processInspectionAttachmentList: allFileIds,
+    });
+
+    if (res.code === 200) {
+      // 璁剧疆鍒锋柊鏍囪锛屽憡璇夊垪琛ㄩ〉闇�瑕佸埛鏂�
+      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" });
+    }
+  } catch (e) {
+    console.error("淇濆瓨澶辫触:", e);
+    uni.showModal({ title: e.message || "淇濆瓨澶辫触", icon: "error" });
+  }
+};
+
+const handleClose = () => {
+  show.value = false;
+};
+
+// 鐘舵�佺被鍨嬫槧灏�
+const getStatusType = (status: number) => {
+  switch (status) {
+    case 0:
+      return "warning"; // 寰呭贰妫�
+    case 1:
+      return "danger"; // 宸查┏鍥�
+    case 2:
+      return "primary"; // 寰呭鏍�
+    case 3:
+      return "success"; // 閫氳繃
+    default:
+      return "default";
+  }
+};
+
+// 鐘舵�佹枃鏈槧灏�
+const getStatusText = (status: number) => {
+  switch (status) {
+    case 0:
+      return "寰呭贰妫�";
+    case 1:
+      return "宸查┏鍥�";
+    case 2:
+      return "寰呭鏍�";
+    case 3:
+      return "閫氳繃";
+    default:
+      return "鏈煡";
+  }
+};
+
+// 鏍煎紡鍖栦骇鍝佸瑙傛樉绀�
+const formatProductAppearance = (productAppearance: string[]) => {
+  if (!productAppearance || productAppearance.length === 0) return "-";
+  return productAppearance.join("銆�");
+};
+
+// 鏍煎紡鍖栨暟鍊兼樉绀�
+const formatValue = (value: any, unit?: string) => {
+  if (value === null || value === undefined || value === "") return "-";
+  return unit ? `${value}${unit}` : value;
+};
+
+// 鏍煎紡鍖栨棩鏈熸樉绀�
+const formatDate = (date: string) => {
+  if (!date) return "-";
+  return new Date(date).toLocaleDateString("zh-CN", {
+    year: "numeric",
+    month: "2-digit",
+    day: "2-digit",
+  });
+};
+
+// 璁$畻鑺傚緞姣�
+const calculatePitchRatio = (pitch: string, dia: string) => {
+  // 濡傛灉pitch鎴杁ia涓虹┖锛屽垯杩斿洖"-"
+  if (!pitch || !dia) return "-";
+
+  // 灏唒itch鍜宒ia杞崲涓烘诞鐐规暟
+  const pitchNum = parseFloat(pitch);
+  const diaNum = parseFloat(dia);
+
+  // 濡傛灉pitchNum鎴杁iaNum鏄疦aN锛屾垨鑰卍iaNum涓�0锛屽垯杩斿洖"-"
+  if (isNaN(pitchNum) || isNaN(diaNum) || diaNum === 0) return "-";
+
+  // 璁$畻pitchNum鍜宒iaNum鐨勬瘮鍊硷紝骞朵繚鐣欎袱浣嶅皬鏁�
+  return (pitchNum / diaNum).toFixed(2);
+};
+
+// 鏇存柊鑺傚緞姣旓紙褰撹妭璺濆彉鍖栨椂鑷姩璁$畻锛�
+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];
+    }
+  }
+
+  formData.productAppearance = newSelection;
+};
+
+const openScan = () => {
+  console.log("indexJX - 鐐瑰嚮鎵爜鎸夐挳锛堝叏灞�鎵爜妯″紡锛屾棤闇�鎵嬪姩瑙﹀彂锛�");
+  // 鍏ㄥ眬鎵爜妯″紡涓嬶紝纭欢鎵爜浼氳嚜鍔ㄨЕ鍙戯紝鏃犻渶鎵嬪姩璋冪敤
+  uni.showToast({
+    title: "璇蜂娇鐢ㄦ壂鐮佹灙鎵弿",
+    icon: "none",
+  });
+};
+
+// 椤甸潰鏄剧ず鏃剁殑澶勭悊
+onShow(() => {
+  console.log("========== indexJX - onShow 瑙﹀彂 ==========");
+  // 閲嶆柊鍚敤鐩戝惉鍣紙纭繚鐩戝惉鍣ㄦ湁鏁堬級
+  enableListener();
+  // 鍔犺浇缂撳瓨锛堟洿鏂癠I鏄剧ず锛�
+  const cachedData = loadFromCache();
+
+  // 濡傛灉娌℃湁缂撳瓨鏁版嵁锛屾彁绀虹敤鎴烽渶瑕佹壂鐮�
+  if (!cachedData || !cachedData.uid) {
+    console.log("鈿狅笍 鏈娴嬪埌鎵爜缂撳瓨锛岀敤鎴烽渶瑕佹壂鎻忚澶囦簩缁寸爜");
+    // 鍦ㄧ紪杈戞ā寮忎笅鎵嶆彁绀�
+    if (isEdit.value) {
+      setTimeout(() => {
+        uni.showToast({
+          title: "璇锋壂鎻忚澶囦簩缁寸爜鍚庡啀淇濆瓨",
+          icon: "none",
+          duration: 2000,
+        });
+      }, 500);
+    }
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.fixed-header {
+  position: fixed;
+  top: 44;
+  left: 0;
+  right: 0;
+  background: #f3f9f8;
+  z-index: 999;
+  padding: 12px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  min-height: 60px;
+  box-sizing: border-box;
+  overflow: visible;
+}
+
+.header-container {
+  display: flex;
+  align-items: center;
+  width: 100%;
+  gap: 10px;
+}
+
+.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 {
+  width: 38px;
+  height: 38px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 6px;
+  flex-shrink: 0;
+}
+
+.list {
+  padding: 12px;
+  padding-top: 84px;
+  background: #f3f9f8;
+  min-height: 100vh;
+  box-sizing: border-box;
+  overflow-y: auto;
+}
+
+.title {
+  position: relative;
+  margin-left: 10px;
+  font-size: 16px;
+  font-weight: 500;
+  color: #0d867f;
+}
+
+.title::after {
+  position: absolute;
+  content: "";
+  top: 4px;
+  left: -10px;
+  width: 4px;
+  height: 16px;
+  background: #0d867f;
+  border-radius: 2px;
+}
+
+// 浜у搧澶栬鍜岀粨璁洪�夋嫨鍣ㄦ牱寮忥紙涓�琛屼袱涓級
+.checkbox-group {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 16rpx;
+  padding: 8rpx 0;
+}
+
+.checkbox-item {
+  width: calc(50% - 8rpx);
+  margin-bottom: 8rpx;
+}
+
+// 闄勪欢鐩稿叧鏍峰紡
+.attachment-section {
+  width: 100%;
+}
+
+.attachment-grid {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+  padding: 10px 0;
+}
+
+.attachment-item {
+  width: calc(25% - 10px);
+  box-sizing: border-box;
+  position: relative;
+}
+
+.upload-btn {
+  width: 80px;
+  height: 80px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  border: 1px dashed #ccc;
+  border-radius: 4px;
+  box-sizing: border-box;
+}
+
+.upload-icon {
+  font-size: 32px;
+  color: #0d867f;
+}
+
+// 闄勪欢鍒犻櫎鍥炬爣
+.delete-icon {
+  position: absolute;
+  top: -8px;
+  right: -8px;
+  width: 24px;
+  height: 24px;
+  background-color: rgba(255, 0, 0, 0.8);
+  color: white;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 10;
+}
+
+@media (max-width: 768px) {
+  .attachment-item {
+    width: calc(25% - 10px);
+  }
+}
+
+// 缂栬緫妯″紡涓嬭〃鍗曠粍浠舵牱寮忎紭鍖�
+:deep(.wd-form-item) {
+  margin-bottom: 8rpx;
+}
+
+:deep(.wd-input, .wd-select, .wd-radio-group, .wd-checkbox-group) {
+  width: 100%;
+  box-sizing: border-box;
+}
+
+:deep(.wd-form-item__label) {
+  &::after {
+    content: "*";
+    color: red;
+    margin-left: 4rpx;
+  }
+}
+
+// 淇閫夋嫨鍣ㄦ牱寮�
+:deep(.wd-select) {
+  width: 100%;
+}
+
+:deep(.wd-checkbox) {
+  margin-right: 0;
+}
+.conclusion-radio-group {
+  display: flex;
+  align-items: flex-start; // 鍨傜洿鏂瑰悜椤堕儴瀵归綈锛堜笂绉诲叧閿級
+  gap: 20rpx; // 閫夐」涔嬮棿鐨勯棿璺�
+}
+</style>
diff --git a/src/pages/routingInspection/detail/indexLS.vue b/src/pages/routingInspection/detail/indexLS.vue
new file mode 100644
index 0000000..d2d46a6
--- /dev/null
+++ b/src/pages/routingInspection/detail/indexLS.vue
@@ -0,0 +1,788 @@
+<template>
+  <view class="fixed-header">
+    <view class="header-container">
+      <wd-button
+        icon="file-add"
+        :round="false"
+        size="small"
+        custom-class="add_btn"
+        @click="editList"
+        v-if="!isEdit"
+      >
+        缂栬緫
+      </wd-button>
+      <wd-button
+        icon="close"
+        type="info"
+        :round="false"
+        size="small"
+        custom-class="add_btn"
+        @click="close"
+        v-if="isEdit"
+      >
+        鍙栨秷
+      </wd-button>
+      <wd-button
+        icon="check"
+        type="success"
+        :round="false"
+        size="small"
+        custom-class="add_btn"
+        @click="saveList"
+        v-if="isEdit"
+      >
+        淇濆瓨
+      </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>
+    </view>
+  </view>
+  <view class="list">
+    <!-- 鍩烘湰淇℃伅妯″潡 -->
+    <wd-row>
+      <view style="margin: 10rpx">
+        <text class="title">{{ "鍩烘湰淇℃伅" }}</text>
+      </view>
+      <wd-col :span="24">
+        <wd-form-item label="鏃ユ湡" prop="recordDate">
+          {{ formatDate(detailData.fixedInfo?.recordDate) }}
+        </wd-form-item>
+        <wd-form-item label="鏈哄彴" prop="deviceModel">
+          {{ formatValue(detailData.fixedInfo?.deviceModel) }}
+        </wd-form-item>
+        <wd-form-item label="鐝" prop="workShift">
+          {{ formatValue(detailData.fixedInfo?.workShift) }}
+        </wd-form-item>
+        <wd-form-item label="鐝粍" prop="teamName">
+          {{ formatValue(detailData.fixedInfo?.teamName) }}
+        </wd-form-item>
+        <wd-form-item label="鍗曚笣瑙勬牸" prop="model">
+          {{ formatValue(detailData.fixedInfo?.model) }}
+        </wd-form-item>
+        <wd-form-item label="鐢熶骇杞存暟" prop="outputNumber">
+          {{ formatValue(detailData.fixedInfo?.outputNumber, "杞�") }}
+        </wd-form-item>
+        <wd-form-item label="鍨嬪彿" prop="poleModel">
+          {{ formatValue(detailData.fixedInfo?.poleModel) }}
+        </wd-form-item>
+        <wd-form-item label="鎵规" prop="poleNumber">
+          {{ formatValue(detailData.fixedInfo?.poleNumber) }}
+        </wd-form-item>
+        <wd-form-item label="璁板綍浜�" prop="createUserName">
+          {{ formatValue(detailData.fixedInfo?.createUserName) }}
+        </wd-form-item>
+        <wd-form-item label="棣栨鐩樺彿" prop="firstNo">
+          {{ formatValue(detailData.fixedInfo?.firstNo) }}
+        </wd-form-item>
+      </wd-col>
+    </wd-row>
+
+    <!-- 宸ヨ壓璁板綍璇︽儏妯″潡 -->
+    <wd-row>
+      <view style="margin: 10rpx">
+        <text class="title">{{ "宸ヨ壓璁板綍璇︽儏" }}</text>
+      </view>
+      <wd-col :span="24">
+        <wd-form-item label="宸℃鍛�" prop="processInspectionUserName">
+          {{ detailData.processInspectionUserName || "-" }}
+        </wd-form-item>
+        <wd-form-item label="鐘舵��" prop="status">
+          <wd-tag custom-class="space" :type="getStatusType(detailData.status)">
+            {{ getStatusText(detailData.status) }}
+          </wd-tag>
+        </wd-form-item>
+      </wd-col>
+    </wd-row>
+
+    <!-- 妫�楠岀粨鏋� -->
+    <wd-row>
+      <view style="margin: 10rpx">
+        <text class="title">{{ "妫�楠岀粨鏋�" }}</text>
+      </view>
+      <wd-col :span="24">
+        <wd-form-item label="鍗曚笣鐩村緞" prop="dia">
+          {{ formatValue(detailData.inspectionResult?.dia, "mm") || "-" }}
+        </wd-form-item>
+
+        <wd-form-item label="鏈�澶х洿寰�" prop="maxDia" required>
+          <template v-if="isEdit">
+            <wd-input v-model="formData.maxDia" placeholder="璇疯緭鍏ユ渶澶х洿寰�(mm)" type="number" />
+          </template>
+          <template v-else>
+            {{ formatValue(detailData.inspectionResult?.maxDia, "mm") || "-" }}
+          </template>
+        </wd-form-item>
+
+        <wd-form-item label="鏈�灏忕洿寰�" prop="minDia" required>
+          <template v-if="isEdit">
+            <wd-input v-model="formData.minDia" placeholder="璇疯緭鍏ユ渶灏忕洿寰�(mm)" type="number" />
+          </template>
+          <template v-else>
+            {{ formatValue(detailData.inspectionResult?.minDia, "mm") || "-" }}
+          </template>
+        </wd-form-item>
+
+        <wd-form-item label="澶栬" prop="appearance" required>
+          <template v-if="isEdit">
+            <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>
+            </view>
+          </template>
+          <template v-else>
+            {{ formatProductAppearance(formData.appearance) }}
+          </template>
+        </wd-form-item>
+
+        <wd-form-item label="鍗风粫绱у瘑" prop="windingTightness" required>
+          <template v-if="isEdit">
+            <wd-radio-group
+              v-model="formData.windingTightness"
+              inline
+              class="conclusion-radio-group"
+            >
+              <wd-radio
+                v-for="(opt, idx) in sampleCompleteOptions"
+                :key="idx"
+                :value="opt.value"
+                shape="dot"
+              >
+                {{ opt.label }}
+              </wd-radio>
+            </wd-radio-group>
+          </template>
+          <template v-else>
+            {{ formatValue(detailData.inspectionResult?.windingTightness) }}
+          </template>
+        </wd-form-item>
+
+        <wd-form-item label="鎺掑垪鏁撮綈" prop="arrangementNeatness" required>
+          <template v-if="isEdit">
+            <wd-radio-group
+              v-model="formData.arrangementNeatness"
+              inline
+              class="conclusion-radio-group"
+            >
+              <wd-radio
+                v-for="(opt, idx) in sampleCompleteOptions"
+                :key="idx"
+                :value="opt.value"
+                shape="dot"
+              >
+                {{ opt.label }}
+              </wd-radio>
+            </wd-radio-group>
+          </template>
+          <template v-else>
+            {{ formatValue(detailData.inspectionResult?.arrangementNeatness) }}
+          </template>
+        </wd-form-item>
+
+        <wd-form-item
+          label="澶栧眰閾濈嚎绂讳晶鏉胯竟缂樿窛绂�"
+          prop="aluminumWireDistance"
+          label-width="360rpx"
+          required
+        >
+          <template v-if="isEdit">
+            <wd-input
+              v-model="formData.aluminumWireDistance"
+              placeholder="璇疯緭鍏ヨ窛绂�(mm)"
+              type="number"
+            />
+          </template>
+          <template v-else>
+            {{ formatValue(detailData.inspectionResult?.aluminumWireDistance, "mm") || "-" }}
+          </template>
+        </wd-form-item>
+
+        <wd-form-item label="鎴愬搧妯″悗鎺ュご鎯呭喌" prop="jointCondition" label-width="280rpx" required>
+          <template v-if="isEdit">
+            <wd-radio-group v-model="formData.jointCondition" inline class="conclusion-radio-group">
+              <wd-radio
+                v-for="(opt, idx) in jointConditionOptions"
+                :key="idx"
+                :value="opt.value"
+                shape="dot"
+              >
+                {{ opt.label }}
+              </wd-radio>
+            </wd-radio-group>
+          </template>
+          <template v-else>
+            {{ formatValue(detailData.inspectionResult?.jointCondition) || "-" }}
+          </template>
+        </wd-form-item>
+
+        <wd-form-item label="缁撹" prop="conclusion" required>
+          <template v-if="isEdit">
+            <wd-radio-group v-model="formData.conclusion" inline class="conclusion-radio-group">
+              <wd-radio
+                v-for="(opt, idx) in conclusionOptions"
+                :key="idx"
+                :value="opt.value"
+                shape="dot"
+              >
+                {{ opt.label }}
+              </wd-radio>
+            </wd-radio-group>
+          </template>
+          <template v-else>
+            {{ formatValue(detailData.inspectionResult?.conclusion) || "-" }}
+          </template>
+        </wd-form-item>
+      </wd-col>
+    </wd-row>
+
+    <!-- 宸℃缁撴灉 -->
+    <wd-row>
+      <view style="margin: 10rpx">
+        <text class="title">{{ "宸℃缁撴灉" }}</text>
+      </view>
+      <wd-col :span="24">
+        <wd-form-item
+          label="閾濇潌鍓嶃�佷腑銆佸熬鏍峰搧鏄惁榻愬叏"
+          prop="isFully"
+          required
+          label-width="420rpx"
+        >
+          <template v-if="isEdit">
+            <wd-radio-group v-model="formData.isFully" inline class="conclusion-radio-group">
+              <wd-radio
+                v-for="(opt, idx) in sampleCompleteOptions"
+                :key="idx"
+                :value="opt.value"
+                shape="dot"
+              >
+                {{ opt.label }}
+              </wd-radio>
+            </wd-radio-group>
+          </template>
+          <template v-else>
+            <wd-tag
+              custom-class="space"
+              :type="detailData.processInspectionResult?.isFully ? 'success' : 'danger'"
+            >
+              {{ detailData.processInspectionResult?.isFully ? "鏄�" : "鍚�" }}
+            </wd-tag>
+          </template>
+        </wd-form-item>
+      </wd-col>
+    </wd-row>
+
+    <!-- 闄勪欢妯″潡 -->
+    <wd-row class="attachment-section">
+      <view style="margin: 10rpx">
+        <text class="title">{{ "闄勪欢" }}</text>
+      </view>
+      <wd-col :span="24">
+        <AttachmentUpload
+          :detailData="detailData"
+          :isEdit="isEdit"
+          :deviceType="paramsType"
+          ref="attachmentRef"
+        />
+      </wd-col>
+    </wd-row>
+    <wd-popup v-model="show" custom-style="border-radius:32rpx;" @close="handleClose">
+      <div class="image-preview">
+        <img :src="previewImageUrl" alt="棰勮鍥剧墖" style="width: 100%; height: auto" />
+      </div>
+    </wd-popup>
+    <wd-toast />
+  </view>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, computed, onUnmounted } from "vue";
+import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
+import RoutingInspectionApi from "@/api/routingInspection/routingInspection";
+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 paramsType = ref("");
+const detailData = ref<any>({});
+const show = ref(false);
+const previewImageUrl = ref("");
+const isEdit = ref(false);
+const tempFiles = ref<any[]>([]);
+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: [] as string[],
+  windingTightness: "",
+  arrangementNeatness: "",
+  aluminumWireDistance: "",
+  jointCondition: "",
+  conclusion: "",
+  isFully: "",
+});
+
+// 澶栬閫夐」
+const appearanceOptions = [
+  { label: "鏃犲瑙傞棶棰�", value: "鏃犲瑙傞棶棰�" },
+  { label: "琛ㄩ潰鍒掍激", value: "琛ㄩ潰鍒掍激" },
+  { label: "鐩村緞涓嶅潎", value: "鐩村緞涓嶅潎" },
+  { label: "鍏朵粬缂洪櫡", value: "鍏朵粬缂洪櫡" },
+];
+const sampleCompleteOptions = [
+  { label: "鏄�", value: "鏄�" },
+  { label: "鍚�", value: "鍚�" },
+];
+const jointConditionOptions = [
+  { label: "鏈�", value: "鏈�" },
+  { label: "鏃�", value: "鏃�" },
+];
+const conclusionOptions = [
+  { label: "鍚堟牸", value: "鍚堟牸" },
+  { label: "涓嶅悎鏍�", value: "涓嶅悎鏍�" },
+];
+// 鐘舵�佹槧灏�
+const getStatusType = (status: number) => {
+  switch (status) {
+    case 0:
+      return "warning";
+    case 1:
+      return "danger";
+    case 2:
+      return "primary";
+    case 3:
+      return "success";
+    default:
+      return "default";
+  }
+};
+
+const getStatusText = (status: number) => {
+  switch (status) {
+    case 0:
+      return "寰呭贰妫�";
+    case 1:
+      return "宸查┏鍥�";
+    case 2:
+      return "寰呭鏍�";
+    case 3:
+      return "閫氳繃";
+    default:
+      return "鏈煡";
+  }
+};
+
+// 鏍煎紡鍖栧伐鍏�
+const formatProductAppearance = (productAppearance: string[]) => {
+  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) => {
+  if (value === null || value === undefined || value === "") return "-";
+  return unit ? `${value}${unit}` : value;
+};
+
+const formatDate = (date: string) => {
+  if (!date) return "-";
+  return new Date(date).toLocaleDateString("zh-CN", {
+    year: "numeric",
+    month: "2-digit",
+    day: "2-digit",
+  });
+};
+
+// 鍒濆鍖栬〃鍗�
+const initFormData = () => {
+  const inspectionResult = detailData.value.inspectionResult || {};
+  const processInspectionResult = detailData.value.processInspectionResult || {};
+  formData.dia = inspectionResult.dia || "";
+  formData.maxDia = inspectionResult.maxDia || "";
+  formData.minDia = inspectionResult.minDia || "";
+  // 纭繚 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 || "";
+  formData.jointCondition = inspectionResult.jointCondition || "";
+  formData.conclusion = inspectionResult.conclusion || "";
+  formData.isFully = processInspectionResult.isFully ? "鏄�" : "鍚�";
+};
+
+// 鑾峰彇璇︽儏
+const getDetailData = async (id: string, deviceType: string) => {
+  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) {
+    console.error("鑾峰彇璇︽儏澶辫触:", error);
+  }
+};
+
+// 椤甸潰鍔犺浇
+onLoad((options: any) => {
+  paramsId.value = options.id;
+  paramsType.value = options.deviceType;
+  getDetailData(options.id, options.deviceType);
+});
+
+// 缂栬緫鍒囨崲
+const editList = () => {
+  isEdit.value = true;
+};
+
+// 鍙栨秷缂栬緫
+const close = () => {
+  isEdit.value = false;
+  tempFiles.value = [];
+  initFormData();
+};
+
+// 淇濆瓨缂栬緫
+const saveList = async () => {
+  // 鏍¢獙
+  if (!formData.maxDia) return uni.showToast({ title: "鏈�澶х洿寰勪负蹇呭~椤�", icon: "none" });
+  if (!formData.minDia) return uni.showToast({ title: "鏈�灏忕洿寰勪负蹇呭~椤�", icon: "none" });
+  if (!formData.appearance.length) return uni.showToast({ title: "澶栬涓哄繀濉」", icon: "none" });
+  if (!formData.windingTightness) return uni.showToast({ title: "鍗风粫绱у瘑涓哄繀濉」", icon: "none" });
+  if (!formData.arrangementNeatness)
+    return uni.showToast({ title: "鎺掑垪鏁撮綈涓哄繀濉」", icon: "none" });
+  if (!formData.aluminumWireDistance)
+    return uni.showToast({ title: "澶栧眰閾濈嚎绂讳晶鏉胯竟缂樿窛绂讳负蹇呭~椤�", icon: "none" });
+  if (!formData.jointCondition)
+    return uni.showToast({ title: "鎴愬搧妯″悗鎺ュご鎯呭喌涓哄繀濉」", icon: "none" });
+  if (!formData.conclusion) return uni.showToast({ title: "缁撹涓哄繀濉」", icon: "none" });
+  if (!formData.isFully) 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];
+  // 鎻愪氦
+  try {
+    const res = await RoutingInspectionApi.drawPatrolCheckInspection({
+      deviceUid: deviceUid.value,
+      id: paramsId.value,
+      inspectionResult: {
+        dia: formData.dia,
+        maxDia: formData.maxDia,
+        minDia: formData.minDia,
+        appearance: formData.appearance,
+        windingTightness: formData.windingTightness,
+        arrangementNeatness: formData.arrangementNeatness,
+        aluminumWireDistance: formData.aluminumWireDistance,
+        jointCondition: formData.jointCondition,
+        conclusion: formData.conclusion,
+      },
+      result: { isFully: formData.isFully },
+      processInspectionAttachmentList: allFileIds,
+    });
+    if (res.code === 200) {
+      // 璁剧疆鍒锋柊鏍囪锛屽憡璇夊垪琛ㄩ〉闇�瑕佸埛鏂�
+      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" });
+    }
+  } catch (e) {
+    console.error("淇濆瓨澶辫触:", e);
+    uni.showModal({ title: e.message || "淇濆瓨澶辫触", icon: "error" });
+  }
+};
+
+const handleClose = () => {
+  show.value = false;
+};
+
+const openScan = () => {
+  console.log("indexLS - 鐐瑰嚮鎵爜鎸夐挳锛堝叏灞�鎵爜妯″紡锛屾棤闇�鎵嬪姩瑙﹀彂锛�");
+  // 鍏ㄥ眬鎵爜妯″紡涓嬶紝纭欢鎵爜浼氳嚜鍔ㄨЕ鍙戯紝鏃犻渶鎵嬪姩璋冪敤
+  uni.showToast({
+    title: "璇蜂娇鐢ㄦ壂鐮佹灙鎵弿",
+    icon: "none",
+  });
+};
+
+// 椤甸潰鏄剧ず鏃剁殑澶勭悊
+onShow(() => {
+  console.log("========== indexLS - onShow 瑙﹀彂 ==========");
+  // 閲嶆柊鍚敤鐩戝惉鍣紙纭繚鐩戝惉鍣ㄦ湁鏁堬級
+  enableListener();
+  // 鍔犺浇缂撳瓨锛堟洿鏂癠I鏄剧ず锛�
+  const cachedData = loadFromCache();
+
+  // 濡傛灉娌℃湁缂撳瓨鏁版嵁锛屾彁绀虹敤鎴烽渶瑕佹壂鐮�
+  if (!cachedData || !cachedData.uid) {
+    console.log("鈿狅笍 鏈娴嬪埌鎵爜缂撳瓨锛岀敤鎴烽渶瑕佹壂鎻忚澶囦簩缁寸爜");
+    // 鍦ㄧ紪杈戞ā寮忎笅鎵嶆彁绀�
+    if (isEdit.value) {
+      setTimeout(() => {
+        uni.showToast({
+          title: "璇锋壂鎻忚澶囦簩缁寸爜鍚庡啀淇濆瓨",
+          icon: "none",
+          duration: 2000,
+        });
+      }, 500);
+    }
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.fixed-header {
+  position: fixed;
+  top: 44;
+  left: 0;
+  right: 0;
+  background: #f3f9f8;
+  z-index: 999;
+  padding: 12px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  min-height: 60px;
+  box-sizing: border-box;
+  overflow: visible;
+}
+
+.header-container {
+  display: flex;
+  align-items: center;
+  width: 100%;
+  gap: 10px;
+}
+
+.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 {
+  width: 38px;
+  height: 38px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 6px;
+  flex-shrink: 0;
+}
+
+.list {
+  padding: 12px;
+  padding-top: 84px;
+  background: #f3f9f8;
+  min-height: 100vh;
+  box-sizing: border-box;
+  overflow-y: auto;
+}
+
+.title {
+  position: relative;
+  margin-left: 10px;
+  font-size: 16px;
+  font-weight: 500;
+  color: #0d867f;
+}
+
+.title::after {
+  position: absolute;
+  content: "";
+  top: 4px;
+  left: -10px;
+  width: 4px;
+  height: 16px;
+  background: #0d867f;
+  border-radius: 2px;
+}
+
+.attachment-section {
+  width: 100%;
+}
+
+.attachment-grid {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+  padding: 10px 0;
+}
+
+.attachment-item {
+  width: calc(25% - 10px);
+  box-sizing: border-box;
+  position: relative;
+}
+
+.upload-btn {
+  width: 80px;
+  height: 80px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  border: 1px dashed #ccc;
+  border-radius: 4px;
+  box-sizing: border-box;
+}
+
+.upload-icon {
+  font-size: 32px;
+  color: #0d867f;
+}
+
+.delete-icon {
+  position: absolute;
+  top: -8px;
+  right: -8px;
+  width: 24px;
+  height: 24px;
+  background-color: rgba(255, 0, 0, 0.8);
+  color: white;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 10;
+}
+
+@media (max-width: 768px) {
+  .attachment-item {
+    width: calc(25% - 10px);
+    margin: 10;
+  }
+}
+
+:deep(.wd-form-item) {
+  margin-bottom: 8rpx;
+}
+
+:deep(.wd-input, .wd-select, .wd-radio-group, .wd-checkbox-group) {
+  width: 100%;
+  box-sizing: border-box;
+}
+
+:deep(.wd-form-item__label)::after {
+  content: "*";
+  color: red;
+  margin-left: 4rpx;
+}
+
+:deep(.wd-select) {
+  width: 100%;
+}
+
+:deep(.wd-checkbox) {
+  margin-right: 0;
+}
+.conclusion-radio-group {
+  display: flex;
+  align-items: flex-start; // 鍨傜洿鏂瑰悜椤堕儴瀵归綈锛堜笂绉诲叧閿級
+  gap: 20rpx; // 閫夐」涔嬮棿鐨勯棿璺�
+}
+</style>
diff --git a/src/pages/routingInspection/index.vue b/src/pages/routingInspection/index.vue
new file mode 100644
index 0000000..ddad4be
--- /dev/null
+++ b/src/pages/routingInspection/index.vue
@@ -0,0 +1,164 @@
+<template>
+  <view>
+    <wd-row>
+      <wd-col :span="21">
+        <wd-search
+          v-model="searchKeyword"
+          placeholder="璇疯緭鍏ョ彮缁勫悕绉�"
+          placeholder-left
+          hide-cancel
+          @search="handleSearch"
+          @clear="handleClear"
+        ></wd-search>
+      </wd-col>
+      <wd-col :span="3">
+        <view class="scan_box" @click="openScan">
+          <wd-icon name="scan" size="24px" color="#0D867F"></wd-icon>
+        </view>
+      </wd-col>
+    </wd-row>
+    <wd-tabs v-model="tab" auto-line-width slidable="always" :map-num="patrolList.length">
+      <wd-tab
+        v-for="(item, index) in patrolList"
+        :key="index"
+        :title="`${item.deviceModel}锛堝緟妫�鏌�${item.pendingNum}鏉★級`"
+        class="tab_bg"
+      >
+        <ProductList
+          :key="searchKey"
+          :api="RoutingInspectionApi.getInspectListByPatrol"
+          :ProList="{ ...item, teamName: searchKeyword }"
+        />
+      </wd-tab>
+    </wd-tabs>
+    <wd-toast />
+  </view>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, computed, onMounted, onUnmounted } from "vue";
+import { onShow, onHide } from "@dcloudio/uni-app";
+import ProductList from "./list/index.vue";
+import { useUserStore } from "@/store/modules/user";
+import reportApi from "@/api/work/report";
+import { useToast } from "wot-design-uni";
+import RoutingInspectionApi from "@/api/routingInspection/routingInspection";
+import { useScanCode } from "@/composables/useScanCode";
+
+const userStore = useUserStore();
+const userInfo: any = computed(() => userStore.userInfo);
+const toast = useToast();
+const tab = ref<number>(0);
+const patrolList = ref<any[]>([]); // 宸℃璁惧鍒楄〃鏁版嵁
+const searchKeyword = ref<string>(""); // 鎼滅储鍏抽敭璇嶏紙鐝粍鍚嶇О锛�
+const searchKey = ref<number>(0); // 鐢ㄤ簬寮哄埗鍒锋柊鍒楄〃
+
+// 浣跨敤鎵爜绠$悊 composable锛堝叏灞�鐩戝惉鍣紝涓嶉殢椤甸潰鍒囨崲鍏抽棴锛�
+const { deviceUid, deviceModel, hasScanned, displayText, loadFromCache, enableListener } =
+  useScanCode("scanIndex");
+
+const handlePatrolData = (index: number, count: number) => {
+  // 鍙互鍦ㄨ繖閲屾洿鏂扮壒瀹氬贰妫�璁惧鐨勫緟妫�鏌ユ暟閲�
+  // 渚嬪锛歱atrolList.value[index].pendingNum = count;
+};
+
+// 澶勭悊鎼滅储
+const handleSearch = (value: string) => {
+  console.log("鎼滅储鐝粍:", value);
+  searchKey.value++; // 鏇存柊 key 寮哄埗鍒锋柊鍒楄〃
+};
+
+// 澶勭悊娓呯┖鎼滅储
+const handleClear = () => {
+  console.log("娓呯┖鎼滅储");
+  searchKeyword.value = "";
+  searchKey.value++; // 鏇存柊 key 寮哄埗鍒锋柊鍒楄〃
+};
+
+const openScan = () => {
+  console.log("index.vue - 鐐瑰嚮鎵爜鎸夐挳锛堝叏灞�鎵爜妯″紡锛屾棤闇�鎵嬪姩瑙﹀彂锛�");
+  // 鍏ㄥ眬鎵爜妯″紡涓嬶紝纭欢鎵爜浼氳嚜鍔ㄨЕ鍙戯紝鏃犻渶鎵嬪姩璋冪敤
+  uni.showToast({
+    title: "璇蜂娇鐢ㄦ壂鐮佹灙鎵弿",
+    icon: "none",
+  });
+};
+
+// 鑾峰彇宸℃璁惧鍒楄〃
+const loadPatrolList = async () => {
+  try {
+    const { data } = await RoutingInspectionApi.getDeviceInspectListByPatrol({});
+    if (data) {
+      patrolList.value = data;
+    }
+  } catch (error) {
+    toast.error("鑾峰彇宸℃璁惧鍒楄〃澶辫触");
+  }
+};
+
+onMounted(() => {
+  // 椤甸潰鍔犺浇鏃惰幏鍙栧贰妫�璁惧鍒楄〃
+  loadPatrolList();
+  // 鍚敤鍏ㄥ眬鐩戝惉鍣�
+  enableListener();
+  console.log("index.vue - onMounted");
+});
+
+onShow(() => {
+  console.log("========== index.vue - onShow 瑙﹀彂 ==========");
+  // 椤甸潰鏄剧ず鏃堕噸鏂板惎鐢ㄧ洃鍚櫒锛堢‘淇濈洃鍚櫒鏈夋晥锛�
+  enableListener();
+  // 鍔犺浇缂撳瓨锛堟洿鏂癠I鏄剧ず锛�
+  loadFromCache();
+
+  // 妫�鏌ユ槸鍚﹂渶瑕佸埛鏂板垪琛紙鍙湁鎻愪氦鎴愬姛鍚庢墠鍒锋柊锛�
+  const needRefresh = uni.getStorageSync("needRefreshInspectionList");
+  if (needRefresh) {
+    console.log("妫�娴嬪埌闇�瑕佸埛鏂板垪琛紝寮�濮嬪埛鏂�...");
+    // 閲嶆柊鍔犺浇宸℃璁惧鍒楄〃锛堝埛鏂板緟妫�鏌ユ暟閲忥級
+    loadPatrolList();
+    // 寮哄埗鍒锋柊 ProductList 缁勪欢
+    searchKey.value++;
+    // 娓呴櫎鍒锋柊鏍囪
+    uni.removeStorageSync("needRefreshInspectionList");
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+::v-deep .wd-search__block {
+  border-radius: unset;
+}
+.scan_box {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 38px;
+  height: 38px;
+  padding: 6px;
+  background: #fff;
+}
+::v-deep .wd-tabs__line {
+  background: #0d867f;
+}
+::v-deep .wd-tabs__nav {
+  border-bottom: 1px #dddddd solid;
+}
+.tab_bg {
+  background: #f3f9f8;
+}
+
+.icon_box {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 20px;
+  height: 20px;
+  background: #e7f4ec99;
+  border-radius: 50%;
+}
+
+.statistics_box {
+  margin: 15px;
+}
+</style>
diff --git a/src/pages/routingInspection/list/index.vue b/src/pages/routingInspection/list/index.vue
new file mode 100644
index 0000000..43d1041
--- /dev/null
+++ b/src/pages/routingInspection/list/index.vue
@@ -0,0 +1,130 @@
+<template>
+  <view class="card_box">
+    <z-paging
+      ref="pagingRef"
+      v-model="list"
+      :fixed="false"
+      :auto-show-back-to-top="true"
+      @query="getList"
+    >
+      <ProductCard
+        v-for="(item, index) in list"
+        :key="index"
+        :data="item"
+        :map="map"
+        @click="toDetail(item.id, item.deviceType)"
+      />
+    </z-paging>
+    <wd-toast />
+  </view>
+</template>
+
+<script setup lang="ts">
+import ProductCard from "../product_card/index.vue";
+import { useUserStore } from "@/store/modules/user";
+import zPaging from "@/components/z-paging/z-paging.vue";
+import { useToast } from "wot-design-uni";
+
+const toast = useToast();
+const userStore = useUserStore();
+const userInfo: any = computed(() => userStore.userInfo);
+const pagingRef = ref();
+const map = reactive({
+  deviceModel: "deviceModel",
+  model: "model",
+  firstNo: "firstNo",
+  recordDate: "recordDate",
+  workShift: "workShift",
+  teamName: "teamName",
+  poleModel: "poleModel",
+  poleNumber: "poleNumber",
+  outputNumber: "outputNumber",
+  inspectPerson: "inspectPerson",
+  status: "status",
+  productType: "productType",
+  recordPosition: "recordPosition",
+  rejectList: [
+    {
+      rejectPerson: "rejectPerson",
+      rejectTime: "rejectTime",
+      rejectReason: {
+        reason: "reason",
+      },
+    },
+  ], // 鏀逛负瀵硅薄锛屽寘鍚墍闇�鐨勫祵濂楀睘鎬�
+});
+const props = defineProps({
+  api: {
+    type: Function,
+    default: () => {},
+  },
+  ProList: {
+    type: Object,
+    default: () => {},
+  },
+});
+
+const list = ref<any[]>([]);
+
+const toDetail = (id: number, deviceType: number) => {
+  console.log("鐐瑰嚮鍗$墖", id, deviceType);
+  if (deviceType == 1) {
+    // 缁炵嚎
+    uni.navigateTo({
+      url: `/pages/routingInspection/detail/indexJX?id=${id}&deviceType=${deviceType}`,
+    });
+  } else if (deviceType == 0) {
+    // 鎷変笣
+    uni.navigateTo({
+      url: `/pages/routingInspection/detail/indexLS?id=${id}&deviceType=${deviceType}`,
+    });
+  }
+};
+
+const getList = async (pageNo = 1, pageSize = 10) => {
+  const { code, data } = await props.api({
+    deviceModel: props.ProList.deviceModel,
+    status: "0",
+    deviceType: props.ProList.deviceType,
+    teamName: props.ProList.teamName || "", // 鐝粍鍚嶇О鎼滅储
+    current: pageNo,
+    size: pageSize,
+  });
+  if (code == 200) {
+    map.deviceModel = "deviceModel";
+    map.model = "model";
+    map.firstNo = "firstNo";
+    map.recordDate = "recordDate";
+    map.workShift = "workShift";
+    map.teamName = "teamName";
+    map.poleModel = "poleModel";
+    map.poleNumber = "poleNumber";
+    map.outputNumber = "outputNumber";
+    map.inspectPerson = "inspectPerson";
+    map.productType = "productType";
+    map.recordPosition = "recordPosition";
+    map.rejectList = [
+      {
+        rejectPerson: "rejectPerson",
+        rejectTime: "rejectTime",
+        rejectReason: {
+          reason: "reason",
+        },
+      },
+    ];
+    map.status = "status";
+    if (data.total == 0) {
+      pagingRef.value.complete(true);
+    } else {
+      console.log("data.records", data.records);
+      pagingRef.value.complete(data.records);
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.card_box {
+  height: calc(100vh - 120px);
+}
+</style>
diff --git a/src/pages/routingInspection/product_card/index.vue b/src/pages/routingInspection/product_card/index.vue
new file mode 100644
index 0000000..dd95bd5
--- /dev/null
+++ b/src/pages/routingInspection/product_card/index.vue
@@ -0,0 +1,202 @@
+<template>
+  <wd-card class="card_bg" @click="handleCardClick">
+    <template #title>
+      <view class="flex justify-between w-full">
+        <text class="font-medium text-[#252525]">璁板綍浣嶇疆: {{ data[map.recordPosition] }}</text>
+        <wd-tag color="#0D867F" bg-color="#E7F4EC">
+          <text class="text-xs">{{ data[map.model] }}</text>
+        </wd-tag>
+      </view>
+    </template>
+    <wd-row class="my-2">
+      <wd-col :span="24">
+        <view class="flex">
+          <view class="icon_box">
+            <wd-icon name="folder" color="#0D867F"></wd-icon>
+          </view>
+          <text class="text-[#646874] mx-2">
+            鐝:
+            <text class="text-[#252525]">{{ data[map.workShift] }}</text>
+          </text>
+        </view>
+      </wd-col>
+    </wd-row>
+    <wd-row class="my-2">
+      <wd-col :span="24">
+        <view class="flex">
+          <view class="icon_box">
+            <wd-icon name="folder" color="#0D867F"></wd-icon>
+          </view>
+          <text class="text-[#646874] mx-2">
+            鐝粍:
+            <text class="text-[#252525]">
+              {{ data[map.teamName]?.slice(-2) || data[map.teamName] }}
+            </text>
+          </text>
+        </view>
+      </wd-col>
+    </wd-row>
+    <wd-row class="my-2" v-if="data[map.productType]">
+      <wd-col :span="24">
+        <view class="flex">
+          <view class="icon_box">
+            <wd-icon name="folder" color="#0D867F"></wd-icon>
+          </view>
+          <text class="text-[#646874] mx-2">
+            浜у搧绫诲埆:
+            <text class="text-[#252525]">{{ data[map.productType] }}</text>
+          </text>
+        </view>
+      </wd-col>
+    </wd-row>
+    <wd-row class="my-2">
+      <wd-col :span="12">
+        <view class="flex">
+          <view class="icon_box">
+            <wd-icon name="folder" color="#0D867F"></wd-icon>
+          </view>
+          <text class="text-[#646874] mx-2">
+            鑷浜�:
+            <text class="text-[#252525]">{{ data[map.inspectPerson] }}</text>
+          </text>
+        </view>
+      </wd-col>
+      <wd-col :span="12">
+        <view class="flex">
+          <view class="icon_box">
+            <wd-icon name="folder" color="#0D867F"></wd-icon>
+          </view>
+          <text class="text-[#646874] mx-2">
+            鐘舵��:
+            <text class="text-[#252525]">{{ data[map.status] == 1 ? "琚┏鍥�" : "宸℃" }}</text>
+          </text>
+        </view>
+      </wd-col>
+    </wd-row>
+    <wd-row class="my-2">
+      <wd-col :span="16">
+        <view class="flex">
+          <view class="icon_box">
+            <wd-icon name="folder" color="#0D867F"></wd-icon>
+          </view>
+          <text class="text-[#646874] mx-2">
+            璁板綍鏃堕棿:
+            <text class="text-[#252525]">{{ data[map.recordDate] }}</text>
+          </text>
+        </view>
+      </wd-col>
+      <wd-col :span="8">
+        <view class="flex" @click.stop>
+          <wd-button
+            v-if="data[map.status] == 1"
+            size="small"
+            type="primary"
+            @click="showRejectPopup = true"
+            style="margin-left: auto"
+          >
+            鏌ョ湅椹冲洖淇℃伅
+          </wd-button>
+        </view>
+      </wd-col>
+    </wd-row>
+  </wd-card>
+  <wd-popup
+    v-model="showRejectPopup"
+    title="椹冲洖淇℃伅"
+    custom-style="border-radius:32rpx;height: 800rpx;width: 600rpx;"
+  >
+    <wd-card
+      v-for="(item, index) in data.rejectList"
+      :key="index"
+      :class="index % 2 === 0 ? 'reject-card-bg-1' : 'reject-card-bg-2'"
+      style="margin-bottom: 8px; padding: 10px; border-radius: 8px"
+    >
+      <view class="content">
+        <view>
+          <view
+            style="
+              display: flex;
+              justify-content: space-between;
+              align-items: center;
+              color: rgba(0, 0, 0, 0.85);
+              font-size: 14px;
+              margin-bottom: 8px;
+            "
+          >
+            <view>{{ item.rejectPerson }}</view>
+            <view>{{ item.rejectTime }}</view>
+          </view>
+          <view
+            style="
+              color: rgba(0, 0, 0, 0.85);
+              font-size: 14px;
+              word-break: break-word;
+              overflow-wrap: break-word;
+              max-width: 100%;
+              padding: 5px 0;
+            "
+          >
+            {{ item.rejectReason.reason }}
+          </view>
+        </view>
+      </view>
+    </wd-card>
+  </wd-popup>
+</template>
+
+<script setup lang="ts">
+import { ref } from "vue";
+const emit = defineEmits(["click"]);
+defineProps({
+  data: {
+    type: Object,
+    default: () => {},
+  },
+  map: {
+    type: Object,
+    default: () => {},
+  },
+});
+const showRejectPopup = ref<boolean>(false);
+const handleCardClick = () => {
+  emit("click");
+};
+</script>
+
+<style lang="scss" scoped>
+.card_bg {
+  box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.05);
+  padding-bottom: 10px;
+}
+
+// 娣诲姞锛氫袱绉嶄笉鍚岀殑鑳屾櫙鑹叉牱寮�
+.reject-card-bg-1 {
+  background-color: #f5f7fa;
+}
+
+.reject-card-bg-2 {
+  background-color: #eef2f7;
+}
+
+.page-class {
+  :deep() {
+    .custom-shadow {
+      box-shadow:
+        0 3px 1px -2px rgb(0 0 0 / 20%),
+        0 2px 2px 0 rgb(0 0 0 / 14%),
+        0 1px 5px 0 rgb(0 0 0 / 12%);
+    }
+  }
+}
+
+.header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+// 淇敼锛氳皟鏁村唴瀹瑰尯鍩熺殑鏍峰紡
+.content {
+  padding: 5px;
+}
+</style>
diff --git a/src/pages/routingInspection/upload.vue b/src/pages/routingInspection/upload.vue
new file mode 100644
index 0000000..1a50117
--- /dev/null
+++ b/src/pages/routingInspection/upload.vue
@@ -0,0 +1,587 @@
+<template>
+  <view class="attachment-container">
+    <!-- 澶撮儴鎿嶄綔鍖� -->
+    <view class="header-actions">
+      <wd-button
+        icon="file-add"
+        :round="false"
+        size="small"
+        custom-class="add_btn"
+        @click="addAttachment"
+        v-if="isEdit"
+      >
+        鏂板
+      </wd-button>
+    </view>
+
+    <!-- 闄勪欢鍒楄〃 -->
+    <view class="attachment-list">
+      <wd-status-tip
+        v-if="attachmentList.length === 0"
+        image="content"
+        tip="鏆傛棤闄勪欢"
+        custom-class="status-tip-full"
+      />
+
+      <view v-for="(item, index) in attachmentList" :key="item.id || index" class="attachment-card">
+        <view class="media-wrapper" @click="previewAttachment(item)">
+          <!-- 鍥剧墖棰勮 -->
+          <template v-if="isImageType(item.url)">
+            <image
+              :src="getFullUrl(item.url)"
+              mode="aspectFill"
+              class="media-preview"
+              style="width: 100%; height: 100%"
+              @error="onImageError(item, index)"
+              @load="onImageLoad(item, index)"
+              :show-menu-by-longpress="true"
+            />
+            <!-- 鍔犺浇涓伄缃� -->
+            <view v-if="item.loading" class="loading-mask">
+              <text class="loading-text">鍔犺浇涓�...</text>
+            </view>
+            <!-- 鍥剧墖鍔犺浇澶辫触鏄剧ず榛樿鍥炬爣 -->
+            <view v-if="item.loadError" class="file-icon-wrapper error-overlay">
+              <wd-icon name="picture" size="48px" color="#ccc" />
+              <text class="file-name error-text">鍔犺浇澶辫触</text>
+            </view>
+          </template>
+
+          <!-- 瑙嗛棰勮 -->
+          <template v-else-if="isVideoType(item.url)">
+            <video
+              :src="getFullUrl(item.url)"
+              class="media-preview"
+              :controls="false"
+              :show-center-play-btn="true"
+              @error="onVideoError(item, index)"
+              object-fit="cover"
+            />
+            <!-- 瑙嗛鍔犺浇澶辫触鏄剧ず榛樿鍥炬爣 -->
+            <view v-if="item.loadError" class="file-icon-wrapper error-overlay">
+              <wd-icon name="video" size="48px" color="#ccc" />
+              <text class="file-name error-text">鍔犺浇澶辫触</text>
+            </view>
+          </template>
+
+          <!-- 鍏朵粬鏂囦欢绫诲瀷鏄剧ず鍥炬爣 -->
+          <view v-else class="file-icon-wrapper">
+            <wd-icon name="file-outline" size="48px" color="#999" />
+            <text class="file-name">鏂囦欢</text>
+          </view>
+
+          <!-- 鍒犻櫎鎸夐挳 -->
+          <view class="delete-btn" @click.stop="deleteAttachment(item.id)" v-if="isEdit">
+            <wd-icon name="delete" color="#fff" size="20px" />
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <wd-toast />
+  </view>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, watch } from "vue";
+import { useToast } from "wot-design-uni";
+import AttachmentAPI from "@/api/product/attachment";
+
+// H5 浣跨敤 VITE_APP_BASE_API 浣滀负浠g悊璺緞锛屽叾浠栧钩鍙颁娇鐢� VITE_APP_API_URL 浣滀负璇锋眰璺緞
+let baseUrlValue = import.meta.env.VITE_APP_API_URL || "";
+// #ifdef H5
+baseUrlValue = import.meta.env.VITE_APP_BASE_API || "";
+// #endif
+
+const baseUrl = ref(baseUrlValue); // 浣跨敤ref浣垮叾鍦ㄦā鏉夸腑鍙闂�
+
+// 澶栭儴鍙傛暟
+const props = defineProps({
+  detailData: { type: Object, default: () => ({}) },
+  isEdit: { type: Boolean, default: false },
+  deviceType: { type: String, default: "" },
+});
+
+const toast = useToast();
+
+// 鑾峰彇鍒濆鏁版嵁
+const getInitialData = () => {
+  // 澶勭悊涓嶅悓鐨勬暟鎹粨鏋�
+  let data = props.detailData;
+
+  // 濡傛灉鏄� ref 瀵硅薄锛岃幏鍙栧叾 value
+  if (data && typeof data === "object" && "value" in data) {
+    data = data.value;
+  }
+
+  // 濡傛灉鏄暟缁勶紝鐩存帴杩斿洖
+  if (Array.isArray(data)) {
+    return data.map((item) => ({
+      ...item,
+      loading: false,
+      loadError: false,
+    }));
+  }
+
+  // 濡傛灉鏈� files 灞炴��
+  if (data && data.files) {
+    const files = Array.isArray(data.files) ? data.files : [];
+    return files.map((item) => ({
+      ...item,
+      loading: false,
+      loadError: false,
+    }));
+  }
+
+  return [];
+};
+
+const attachmentList = ref<any[]>(getInitialData());
+const attachmentIds = ref<string[]>(attachmentList.value.map((item: any) => item.id) || []);
+
+// 鐩戝惉 props.detailData 鍙樺寲
+watch(
+  () => props.detailData,
+  (newVal) => {
+    const newData = getInitialData();
+    if (newData.length > 0) {
+      attachmentList.value = newData.map((item) => ({
+        ...item,
+        loading: false,
+        loadError: false,
+      }));
+      attachmentIds.value = newData.map((item: any) => item.id);
+    }
+  },
+  { deep: true, immediate: false }
+);
+
+// 鑾峰彇瀹屾暣鐨勫浘鐗�/瑙嗛 URL
+const getFullUrl = (url: string) => {
+  if (!url) return "";
+
+  // 濡傛灉宸茬粡鏄畬鏁寸殑 URL锛坔ttp 鎴� https 寮�澶达級锛岀洿鎺ヨ繑鍥�
+  if (url.startsWith("http://") || url.startsWith("https://")) {
+    return url;
+  }
+
+  // 妫�鏌� baseUrl 鏄惁鏈夋晥
+  if (!baseUrl.value) {
+    console.error("鉂� baseUrl鏈厤缃紝url:", url);
+    return url;
+  }
+
+  // 濡傛灉鏄浉瀵硅矾寰勶紝鎷兼帴鍩虹 URL
+  const separator = url.startsWith("/") || baseUrl.value.endsWith("/") ? "" : "/";
+  return `${baseUrl.value}${separator}${url}`;
+};
+
+// 鍥剧墖鍔犺浇鎴愬姛
+const onImageLoad = (item: any, index: number) => {
+  item.loading = false;
+  item.loadError = false;
+  attachmentList.value = [...attachmentList.value];
+};
+
+// 鍥剧墖鍔犺浇澶辫触
+const onImageError = (item: any, index: number) => {
+  console.error(`鍥剧墖鍔犺浇澶辫触 [${index}]:`, item.url);
+  item.loading = false;
+  item.loadError = true;
+  attachmentList.value = [...attachmentList.value];
+};
+
+// 瑙嗛鍔犺浇澶辫触
+const onVideoError = (item: any, index: number) => {
+  console.error(`瑙嗛鍔犺浇澶辫触 [${index}]:`, item.url);
+  item.loading = false;
+  item.loadError = true;
+  attachmentList.value = [...attachmentList.value];
+};
+
+// 鏂板闄勪欢
+const addAttachment = () => {
+  // 鏄剧ず閫夋嫨鏂囦欢绫诲瀷鐨勫脊绐�
+  uni.showActionSheet({
+    itemList: ["閫夋嫨鍥剧墖", /* "閫夋嫨瑙嗛", */ "鎷嶇収" /* , "褰曞儚" */],
+    success: (res) => {
+      switch (res.tapIndex) {
+        case 0: // 閫夋嫨鍥剧墖
+          chooseImages();
+          break;
+        // case 1: // 閫夋嫨瑙嗛
+        //   chooseVideos();
+        //   break;
+        case 1: // 鎷嶇収
+          takePhoto();
+          break;
+        // case 3: // 褰曞儚
+        //   recordVideo();
+        //   break;
+      }
+    },
+    fail: (error) => {
+      console.error("閫夋嫨鏂囦欢绫诲瀷澶辫触:", error);
+      toast.show("閫夋嫨鏂囦欢绫诲瀷澶辫触");
+    },
+  });
+};
+
+// 閫夋嫨鍥剧墖
+const chooseImages = () => {
+  uni.chooseImage({
+    count: 9,
+    sizeType: ["original", "compressed"],
+    sourceType: ["album"],
+    success: async (res) => {
+      const filePaths = Array.isArray(res.tempFilePaths) ? res.tempFilePaths : [res.tempFilePaths];
+      await handleFileUpload(filePaths);
+    },
+    fail: (error) => {
+      console.error("閫夋嫨鍥剧墖澶辫触:", error);
+      toast.show("閫夋嫨鍥剧墖澶辫触");
+    },
+  });
+};
+
+// 閫夋嫨瑙嗛
+const chooseVideos = () => {
+  uni.chooseVideo({
+    sourceType: ["album"],
+    maxDuration: 60,
+    camera: "back",
+    success: async (res) => {
+      await handleFileUpload([res.tempFilePath]);
+    },
+    fail: (error) => {
+      console.error("閫夋嫨瑙嗛澶辫触:", error);
+      toast.show("閫夋嫨瑙嗛澶辫触");
+    },
+  });
+};
+
+// 鎷嶇収
+const takePhoto = () => {
+  uni.chooseImage({
+    count: 1,
+    sizeType: ["original", "compressed"],
+    sourceType: ["camera"],
+    success: async (res) => {
+      const filePaths = Array.isArray(res.tempFilePaths) ? res.tempFilePaths : [res.tempFilePaths];
+      await handleFileUpload(filePaths);
+    },
+    fail: (error) => {
+      console.error("鎷嶇収澶辫触:", error);
+      toast.show("鎷嶇収澶辫触");
+    },
+  });
+};
+
+// 褰曞儚
+const recordVideo = () => {
+  uni.chooseVideo({
+    sourceType: ["camera"],
+    maxDuration: 60,
+    camera: "back",
+    success: async (res) => {
+      await handleFileUpload([res.tempFilePath]);
+    },
+    fail: (error) => {
+      console.error("褰曞儚澶辫触:", error);
+      toast.show("褰曞儚澶辫触");
+    },
+  });
+};
+
+// 澶勭悊鏂囦欢涓婁紶
+const handleFileUpload = async (filePaths: string[]) => {
+  try {
+    toast.show("姝e湪涓婁紶...");
+
+    // 涓婁紶鏂囦欢
+    const uploadResults: any = await AttachmentAPI.uploadAttachmentFiles(filePaths);
+    const result = uploadResults.map((it: any) => {
+      return it.data;
+    });
+
+    // 鏇存柊闄勪欢鍒楄〃
+    const flattenedResult = result.flat();
+    attachmentList.value.push(...flattenedResult);
+
+    // 鎻愬彇闄勪欢ID
+    attachmentIds.value = attachmentList.value.map((item: any) => item.id);
+    toast.show("涓婁紶鎴愬姛");
+  } catch (error) {
+    console.error("涓婁紶澶辫触:", error);
+    toast.show("涓婁紶澶辫触");
+  }
+};
+
+// 鍒犻櫎闄勪欢
+const deleteAttachment = async (aid: number) => {
+  try {
+    uni.showModal({
+      title: "纭鍒犻櫎",
+      content: "纭畾瑕佸垹闄よ繖涓檮浠跺悧锛�",
+      success: async (res) => {
+        if (res.confirm) {
+          // 鍓嶇鎵嬪姩鍒犻櫎锛氱洿鎺ヤ粠鍒楄〃涓Щ闄よ繖鏉℃暟鎹�
+          attachmentList.value = attachmentList.value.filter((item) => item.id !== aid);
+
+          // 鑾峰彇鍓╀綑鐨勯檮浠禝D
+          attachmentIds.value = attachmentList.value.map((item) => item.id);
+          toast.show("鍒犻櫎鎴愬姛");
+        }
+      },
+    });
+  } catch (error) {
+    console.error("鍒犻櫎澶辫触:", error);
+    toast.show("鍒犻櫎澶辫触");
+  }
+};
+
+// 棰勮闄勪欢
+const previewAttachment = (item: any) => {
+  // 鏍规嵁鏂囦欢绫诲瀷杩涜棰勮
+  const fileType = getFileType(item.url);
+  const fullUrl = getFullUrl(item.url);
+
+  if (fileType.startsWith("image")) {
+    // 鍥剧墖棰勮
+    uni.previewImage({
+      urls: [fullUrl],
+      current: fullUrl,
+    });
+  } else {
+    // 鍏朵粬鏂囦欢绫诲瀷锛屽彲浠ヤ笅杞芥垨鎵撳紑
+    uni.downloadFile({
+      url: fullUrl,
+      success: (res) => {
+        uni.openDocument({
+          filePath: res.tempFilePath,
+          success: () => {
+            // 鎵撳紑鏂囨。鎴愬姛
+          },
+          fail: (error) => {
+            console.error("鎵撳紑鏂囨。澶辫触:", error);
+            toast.show("鏃犳硶棰勮姝ゆ枃浠剁被鍨�");
+          },
+        });
+      },
+      fail: (error) => {
+        console.error("涓嬭浇鏂囦欢澶辫触:", error);
+        toast.show("涓嬭浇鏂囦欢澶辫触");
+      },
+    });
+  }
+};
+
+// 浠� URL 鎴栨枃浠跺悕涓彁鍙栨墿灞曞悕
+const getExtension = (urlOrFileName: string) => {
+  if (!urlOrFileName) return "";
+  // 绉婚櫎鏌ヨ鍙傛暟鍜屽搱甯�
+  const cleanUrl = urlOrFileName.split("?")[0].split("#")[0];
+  // 鑾峰彇鏈�鍚庝竴涓偣鍚庨潰鐨勫唴瀹�
+  const extension = cleanUrl.split(".").pop()?.toLowerCase();
+  return extension || "";
+};
+
+// 鍒ゆ柇鏄惁涓哄浘鐗囩被鍨�
+const isImageType = (urlOrFileName: string) => {
+  const extension = getExtension(urlOrFileName);
+  return ["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(extension);
+};
+
+// 鍒ゆ柇鏄惁涓鸿棰戠被鍨�
+const isVideoType = (urlOrFileName: string) => {
+  const extension = getExtension(urlOrFileName);
+  return ["mp4", "mov", "avi", "wmv", "flv", "mkv", "webm"].includes(extension);
+};
+
+// 鑾峰彇鏂囦欢绫诲瀷
+const getFileType = (urlOrFileName: string) => {
+  if (!urlOrFileName) return "unknown";
+  const extension = getExtension(urlOrFileName);
+  switch (extension) {
+    case "jpg":
+    case "jpeg":
+    case "png":
+    case "gif":
+    case "bmp":
+    case "webp":
+      return "image";
+    case "mp4":
+    case "mov":
+    case "avi":
+    case "wmv":
+    case "flv":
+    case "mkv":
+    case "webm":
+      return "video";
+    case "pdf":
+      return "pdf";
+    case "doc":
+    case "docx":
+      return "word";
+    case "xls":
+    case "xlsx":
+      return "excel";
+    case "ppt":
+    case "pptx":
+      return "powerpoint";
+    case "txt":
+      return "text";
+    case "zip":
+    case "rar":
+      return "archive";
+    default:
+      return "file";
+  }
+};
+
+// 鏍煎紡鍖栨枃浠跺ぇ灏�
+const formatFileSize = (size: number) => {
+  if (size < 1024) return size + " B";
+  if (size < 1024 * 1024) return (size / 1024).toFixed(1) + " KB";
+  return (size / (1024 * 1024)).toFixed(1) + " MB";
+};
+
+// 鏍煎紡鍖栨椂闂�
+const formatTime = (time: string) => {
+  const date = new Date(time);
+  return date.toLocaleString();
+};
+// 瀵瑰鏆撮湶鏂规硶锛氳幏鍙栨墍鏈夐渶鎻愪氦鐨勬枃浠�
+const getSubmitFiles = () => ({
+  newFiles: attachmentIds.value || [],
+});
+defineExpose({ getSubmitFiles });
+</script>
+
+<style lang="scss" scoped>
+.attachment-container {
+  padding: 12px;
+  background: #f3f9f8;
+  min-height: 100vh;
+}
+
+.header-actions {
+  margin-bottom: 12px;
+
+  :deep(.add_btn) {
+    background: #0d867f;
+    color: white;
+    border: none;
+  }
+}
+
+.attachment-list {
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  gap: 8px;
+
+  :deep(.status-tip-full) {
+    grid-column: 1 / -1;
+    width: 100%;
+  }
+
+  .attachment-card {
+    width: 100%;
+    position: relative;
+
+    // 浣跨敤 padding-top 瀹炵幇姝f柟褰紙鍏煎鎬ф洿濂斤級
+    &::before {
+      content: "";
+      display: block;
+      padding-top: 100%; // 楂樺害绛変簬瀹藉害
+    }
+  }
+}
+
+.media-wrapper {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  border-radius: 8px;
+  overflow: hidden;
+  background: #f5f5f5;
+
+  .media-preview {
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+    display: block;
+  }
+
+  .loading-mask {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background: rgba(0, 0, 0, 0.3);
+    z-index: 5;
+
+    .loading-text {
+      font-size: 12px;
+      color: #fff;
+    }
+  }
+
+  .file-icon-wrapper {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+    height: 100%;
+    padding: 8px;
+    text-align: center;
+
+    .file-name {
+      margin-top: 8px;
+      font-size: 12px;
+      color: #666;
+      word-break: break-all;
+      display: -webkit-box;
+      -webkit-line-clamp: 2;
+      line-clamp: 2;
+      -webkit-box-orient: vertical;
+      overflow: hidden;
+
+      &.error-text {
+        color: #ff4757;
+      }
+    }
+
+    &.error-overlay {
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+      bottom: 0;
+      background: rgba(255, 255, 255, 0.9);
+      z-index: 5;
+    }
+  }
+
+  .delete-btn {
+    position: absolute;
+    top: 4px;
+    right: 4px;
+    width: 28px;
+    height: 28px;
+    border-radius: 50%;
+    background: rgba(0, 0, 0, 0.5);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    z-index: 10;
+  }
+}
+</style>
diff --git a/src/static/icons/routingInspection.png b/src/static/icons/routingInspection.png
new file mode 100644
index 0000000..e8bb5a4
--- /dev/null
+++ b/src/static/icons/routingInspection.png
Binary files differ
diff --git a/src/utils/cache.ts b/src/utils/cache.ts
index 93b1a42..ed9ab16 100644
--- a/src/utils/cache.ts
+++ b/src/utils/cache.ts
@@ -1,6 +1,7 @@
 const TOKEN_KEY = "app-token";
 const USER_INFO_KEY = "user-info";
 const DICT_KEY = "dict";
+const TEAM_ID_KEY = "team-id";
 import { type DictData } from "@/api/system/dict";
 
 // 璁剧疆 token
@@ -48,9 +49,25 @@
   uni.removeStorageSync(DICT_KEY);
 }
 
+// 璁剧疆鐝粍ID
+export function setTeamId(teamId: string | number) {
+  uni.setStorageSync(TEAM_ID_KEY, teamId);
+}
+
+// 鑾峰彇鐝粍ID
+export function getTeamId(): string | number | null {
+  return uni.getStorageSync(TEAM_ID_KEY) || null;
+}
+
+// 娓呴櫎鐝粍ID
+export function clearTeamId() {
+  uni.removeStorageSync(TEAM_ID_KEY);
+}
+
 // 娓呴櫎鎵�鏈夌紦瀛樹俊鎭�
 export function clearAll() {
   clearToken();
   clearUserInfo();
   clearDictCache();
+  clearTeamId();
 }
diff --git a/src/utils/request.ts b/src/utils/request.ts
index 8fa1c60..804150b 100644
--- a/src/utils/request.ts
+++ b/src/utils/request.ts
@@ -36,10 +36,12 @@
           uni.showToast({
             title: resData.msg || "涓氬姟澶勭悊澶辫触",
             icon: "none",
+            duration: 2000,
           });
           reject({
             message: resData.msg || "涓氬姟澶勭悊澶辫触",
             code: resData.code,
+            duration: 2000,
           });
         }
       },
@@ -53,6 +55,7 @@
         reject({
           message: "缃戠粶璇锋眰澶辫触",
           error,
+          duration: 2000,
         });
       },
     });

--
Gitblit v1.9.3