From 883ab99ab021be1adc6dc5689368f899ea436e54 Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期五, 31 十月 2025 20:01:57 +0800
Subject: [PATCH] 完成
---
 src/pages/routingInspection/upload.vue         |  359 ++++++++++++++++++
 src/pages/routingInspection/detail/indexJX.vue |  147 +-----
 src/pages/routingInspection/detail/indexLS.vue |  591 +++++++++++++-----------------
 3 files changed, 652 insertions(+), 445 deletions(-)
diff --git a/src/pages/routingInspection/detail/indexJX.vue b/src/pages/routingInspection/detail/indexJX.vue
index 46fdd5f..f82336a 100644
--- a/src/pages/routingInspection/detail/indexJX.vue
+++ b/src/pages/routingInspection/detail/indexJX.vue
@@ -138,6 +138,7 @@
         style="padding-bottom: 10px"
       >
         <wd-form-item
+          prop="structureItemsGroup"
           :label="formatValue(item.structureName)"
           label-width="400rpx"
           style="color: red"
@@ -184,6 +185,7 @@
           :label="formatValue(item.twistName)"
           label-width="400rpx"
           style="color: red"
+          prop="inspectTwistGroup"
           required
         ></wd-form-item>
         <wd-form-item label="缁炲悜" prop="direction" required>
@@ -290,40 +292,15 @@
       <view style="margin: 10rpx">
         <text class="title">{{ "闄勪欢" }}</text>
       </view>
-      <view class="attachment-grid">
-        <!-- 宸蹭笂浼犻檮浠讹紙鍖呭惈鏂颁笂浼犵殑锛� -->
-        <wd-col v-for="(file, index) in allFiles" :key="file.id || index" class="attachment-item">
-          <wd-img :width="80" :height="80" :src="file.url" @click="previewImage(file.url)">
-            <template #error>
-              <view class="error-wrap">鍔犺浇澶辫触</view>
-            </template>
-            <template #loading>
-              <view class="loading-wrap">
-                <wd-loading />
-              </view>
-            </template>
-          </wd-img>
-          <!-- 缂栬緫妯″紡涓嬫樉绀哄垹闄ゆ寜閽� -->
-          <!-- <wd-icon
-            v-if="isEdit"
-            name="close-circle"
-            class="delete-icon"
-            @click.stop="deleteFile(file.id || index)"
-          ></wd-icon> -->
-        </wd-col>
-        <!-- 涓婁紶鎸夐挳锛堜粎缂栬緫妯″紡鏄剧ず锛� -->
-        <wd-col v-if="isEdit" class="attachment-item upload-btn">
-          <wd-upload
-            :multiple="true"
-            :max-count="5"
-            :before-upload="beforeUpload"
-            @success="handleUploadSuccess"
-            @fail="handleUploadFail"
-          >
-            <view class="upload-icon">+</view>
-          </wd-upload>
-        </wd-col>
-      </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">
@@ -342,8 +319,9 @@
 import RoutingInspectionApi from "@/api/routingInspection/routingInspection";
 import Scan from "@/components/scan/index.vue";
 import { useToast } from "wot-design-uni";
+import AttachmentUpload from "../upload.vue";
 
-// 鏍稿績鐘舵��
+const paramsType = ref("");
 const paramsId = ref("");
 const recordData = ref<any>({ structureInfo: { files: [], structureRecordResult: {} } });
 const show = ref(false);
@@ -353,8 +331,10 @@
 const deviceUid = ref("");
 const scanRef = ref();
 const toast = useToast();
+const attachmentRef = ref<any>(null);
+const detailData = reactive<any>({});
+const detailDataLoaded = ref(false);
 
-// 琛ㄥ崟涓存椂鏁版嵁锛堢紪杈戞ā寮忕粦瀹氾級
 const formData = reactive({
   twistDiameter: "", // 缁炲埗澶栧緞
   structureFormula: "", // 鎴愬搧缁撴瀯
@@ -365,7 +345,6 @@
   sampleComplete: "", // 鏍峰搧鏄惁榻愬叏
 });
 
-// 閫夐」鏁版嵁
 const twistDirectionOptions = [
   { label: "宸﹀悜", value: "宸﹀悜" },
   { label: "鍙冲悜", value: "鍙冲悜" },
@@ -387,47 +366,40 @@
   { label: "鍚�", value: "鍚�" },
 ];
 
-// 鍚堝苟鍘熸湁闄勪欢鍜屾柊涓婁紶闄勪欢
-const allFiles = computed(() => {
-  return [...(recordData.value.structureInfo?.files || []), ...tempFiles.value];
-});
-
-// 鍒濆鍖栬〃鍗曟暟鎹紙鎺ュ彛鏁版嵁鍚屾鍒拌〃鍗曪級
 const initFormData = () => {
   const structureResult = recordData.value.structureInfo?.structureRecordResult || {};
   const inspectionResult = recordData.value.inspectionResult || {};
 
-  // 鍩虹鍙紪杈戝瓧娈�
   formData.twistDiameter = inspectionResult.twistDiameter || "";
   formData.structureFormula = structureResult.inspectStructure?.structureFormula || "";
   formData.sampleComplete = inspectionResult.sampleComplete || "";
   formData.conclusion = structureResult.conclusion || "";
-  // 淇浜у搧澶栬鍜岀粨璁虹殑鏁版嵁鏍煎紡
   formData.productAppearance = Array.isArray(structureResult.productAppearance)
     ? structureResult.productAppearance
     : structureResult.productAppearance
     ? [structureResult.productAppearance]
     : [];
 
-  // 寰幆绫诲瓧娈碉紙娣辨嫹璐濋伩鍏嶅師鏁版嵁姹℃煋锛�
   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;
+    console.log("detailData.value", detailData.value);
     tempFiles.value = []; // 娓呯┖涓存椂鏂囦欢
     initFormData(); // 鏁版嵁杩斿洖鍚庡垵濮嬪寲琛ㄥ崟
+    detailDataLoaded.value = true; // 鏁版嵁鍔犺浇瀹屾垚鍚庯紝娓叉煋瀛愮粍浠�
+    console.log("鐖剁粍浠�-鏁版嵁灏辩华鍚庢墦鍗�");
   } catch (error) {
     console.error("鑾峰彇璇︽儏澶辫触:", error);
     uni.showToast({ title: "鍔犺浇澶辫触", icon: "error" });
@@ -436,8 +408,14 @@
 
 // 椤甸潰鍔犺浇
 onLoad((options: any) => {
-  paramsId.value = options.id;
-  getDetailData(options.id, options.deviceType);
+  try {
+    paramsId.value = options.id;
+    paramsType.value = options.deviceType;
+    getDetailData(options.id, options.deviceType);
+  } catch (error) {
+    console.error("鑾峰彇璇︽儏澶辫触:", error);
+    uni.showToast({ title: "鍔犺浇澶辫触", icon: "error" });
+  }
 });
 
 // 缂栬緫妯″紡鍒囨崲
@@ -448,8 +426,8 @@
 // 鍙栨秷缂栬緫锛堥噸缃〃鍗曪級
 const close = () => {
   isEdit.value = false;
-  tempFiles.value = []; // 鍙栨秷鏃舵竻绌轰复鏃朵笂浼犵殑鏂囦欢
-  initFormData(); // 鎭㈠鍘熷鏁版嵁
+  tempFiles.value = [];
+  initFormData();
 };
 
 // 淇濆瓨缂栬緫锛堝惈蹇呭~椤规牎楠岋級
@@ -482,7 +460,9 @@
   }
   console.log("1111", deviceUid.value);
   if (!deviceUid.value) return uni.showToast({ title: "璇锋壂鎻忎簩缁寸爜", icon: "none" });
-  // 4. 鎻愪氦鏁版嵁鍒版帴鍙�
+  const { newFiles } = attachmentRef.value.getSubmitFiles();
+  console.log("newFiles", newFiles);
+  const allFileIds = [...newFiles];
   try {
     const res = await RoutingInspectionApi.strandedPatrolCheckInspection({
       deviceUid: deviceUid.value,
@@ -499,13 +479,13 @@
       inspectionResult: {
         sampleComplete: formData.sampleComplete,
       },
-      processInspectionAttachmentList: tempFiles.value.map((f) => f.url), // 鏂颁笂浼犵殑闄勪欢
+      processInspectionAttachmentList: allFileIds,
     });
 
     if (res.code === 200) {
       uni.showToast({ title: "淇濆瓨鎴愬姛", icon: "success" });
       isEdit.value = false;
-      getDetailData(paramsId.value, recordData.value.deviceType); // 鍒锋柊鏁版嵁
+      getDetailData(paramsId.value, paramsType.value);
     } else {
       uni.showModal({ title: res.msg || "淇濆瓨澶辫触", icon: "error" });
     }
@@ -513,61 +493,6 @@
     console.error("淇濆瓨澶辫触:", e);
     uni.showModal({ title: e.message || "淇濆瓨澶辫触", icon: "error" });
   }
-};
-
-// 闄勪欢涓婁紶鍓嶇疆鏍¢獙
-const beforeUpload = (file: any) => {
-  // 闄愬埗鍥剧墖澶у皬涓嶈秴杩�2M
-  const maxSize = 2 * 1024 * 1024;
-  if (file.size > maxSize) {
-    uni.showToast({ title: "鍥剧墖澶у皬涓嶈兘瓒呰繃2M", icon: "none" });
-    return false;
-  }
-  return true;
-};
-
-// 闄勪欢涓婁紶鎴愬姛鍥炶皟
-const handleUploadSuccess = (res: any) => {
-  // 鍋囪鎺ュ彛杩斿洖鏍煎紡: { url: 'xxx', id: 'xxx' }
-  if (Array.isArray(res)) {
-    tempFiles.value = [
-      ...tempFiles.value,
-      ...res.map((file) => ({
-        ...file,
-        id: `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, // 鐢熸垚涓存椂ID
-      })),
-    ];
-  } else {
-    tempFiles.value.push({
-      ...res,
-      id: `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
-    });
-  }
-};
-
-// 闄勪欢涓婁紶澶辫触鍥炶皟
-const handleUploadFail = (err: any) => {
-  uni.showToast({ title: "闄勪欢涓婁紶澶辫触", icon: "error" });
-};
-
-// 鍒犻櫎闄勪欢
-const deleteFile = (id: string | number) => {
-  // 鍒犻櫎涓存椂鏂囦欢
-  tempFiles.value = tempFiles.value.filter((file) => file.id !== id);
-
-  // 濡傛灉鏄師鏈夋枃浠讹紝闇�瑕佹爣璁板垹闄わ紙瀹為檯椤圭洰涓彲鑳介渶瑕佹帴鍙d氦浜掞級
-  if (typeof id !== "string" || !id.startsWith("temp-")) {
-    // 杩欓噷鍙互瀹炵幇鍒犻櫎鍘熸湁鏂囦欢鐨勯�昏緫
-    recordData.value.structureInfo.files = recordData.value.structureInfo.files.filter(
-      (file: any, index: number) => index !== id
-    );
-  }
-};
-
-// 鍥剧墖棰勮
-const previewImage = (url: string) => {
-  previewImageUrl.value = url;
-  show.value = true;
 };
 
 const handleClose = () => {
@@ -639,7 +564,7 @@
   } catch (err) {
     console.error("JSON瑙f瀽澶辫触锛�", err);
     toast.error("鎵爜鏁版嵁寮傚父");
-    return; // 瑙f瀽澶辫触鐩存帴杩斿洖锛岄伩鍏嶅悗缁敊璇�
+    return;
   }
   deviceUid.value = codeObj?.uid;
   toast.success("鎵爜鎴愬姛");
diff --git a/src/pages/routingInspection/detail/indexLS.vue b/src/pages/routingInspection/detail/indexLS.vue
index 07bfd70..f0f4a6b 100644
--- a/src/pages/routingInspection/detail/indexLS.vue
+++ b/src/pages/routingInspection/detail/indexLS.vue
@@ -40,287 +40,254 @@
     </view>
   </view>
   <view class="list">
-    <div class="inspection-report">
-      <!-- 鍩烘湰淇℃伅妯″潡 -->
-      <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="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="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-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="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="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">
-              <wd-checkbox-group
-                v-model="formData.appearance"
-                inline
-                v-for="(opt, idx) in appearanceOptions"
-                :key="idx"
-                style="text-align: justify"
-              >
-                <wd-checkbox :modelValue="opt.value" style="width: 100px">
-                  {{ opt.label }}
-                </wd-checkbox>
-              </wd-checkbox-group>
-            </template>
-            <template v-else>
-              {{
-                formatProductAppearance(detailData.inspectionResult?.appearance).join("銆�") || "-"
-              }}
-            </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 v-if="detailData.processInspectionResult?.isFully">
-        <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>
-              <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" v-if="detailData.files && detailData.files.length > 0">
-        <view style="margin: 10rpx">
-          <text class="title">{{ "闄勪欢" }}</text>
-        </view>
-        <view class="attachment-grid">
-          <wd-col v-for="(file, index) in detailData.files" :key="index" class="attachment-item">
-            <wd-img :width="80" :height="80" :src="file.url" @click="previewImage(file.url)">
-              <template #error><view class="error-wrap">鍔犺浇澶辫触</view></template>
-              <template #loading>
-                <view class="loading-wrap"><wd-loading /></view>
-              </template>
-            </wd-img>
-            <!-- <wd-icon
-              v-if="isEdit"
-              name="close-circle"
-              class="delete-icon"
-              @click.stop="deleteFile(index)"
-            ></wd-icon> -->
-          </wd-col>
-          <wd-col v-if="isEdit" class="attachment-item upload-btn">
-            <wd-upload
-              :multiple="true"
-              :max-count="5"
-              :before-upload="beforeUpload"
-              @success="handleUploadSuccess"
-              @fail="handleUploadFail"
-              accept="all"
+        <wd-form-item label="澶栬" prop="appearance" required>
+          <template v-if="isEdit">
+            <wd-checkbox-group
+              v-model="formData.appearance"
+              inline
+              v-for="(opt, idx) in appearanceOptions"
+              :key="idx"
+              style="text-align: justify"
             >
-              <view class="upload-icon">+</view>
-            </wd-upload>
-          </wd-col>
-        </view>
-      </wd-row>
-    </div>
+              <wd-checkbox :modelValue="opt.value" style="width: 100px">
+                {{ opt.label }}
+              </wd-checkbox>
+            </wd-checkbox-group>
+          </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 v-if="detailData.processInspectionResult?.isFully">
+      <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>
+            <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" v-if="detailData.files && detailData.files.length > 0">
+      <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" />
@@ -337,8 +304,11 @@
 import RoutingInspectionApi from "@/api/routingInspection/routingInspection";
 import Scan from "@/components/scan/index.vue";
 import { useToast } from "wot-design-uni";
+import AttachmentUpload from "../upload.vue";
+
 // 鏍稿績鐘舵��
 const paramsId = ref("");
+const paramsType = ref("");
 const detailData = ref<any>({});
 const show = ref(false);
 const previewImageUrl = ref("");
@@ -347,13 +317,14 @@
 const deviceUid = ref("");
 const scanRef = ref();
 const toast = useToast();
+const attachmentRef = ref<any>(null);
 
 // 琛ㄥ崟鏁版嵁
 const formData = reactive({
   dia: "",
   maxDia: "",
   minDia: "",
-  appearance: "",
+  appearance: [],
   windingTightness: "",
   arrangementNeatness: "",
   aluminumWireDistance: "",
@@ -413,8 +384,8 @@
 };
 
 // 鏍煎紡鍖栧伐鍏�
-const formatProductAppearance = (productAppearance: string) => {
-  return !productAppearance ? "-" : productAppearance;
+const formatProductAppearance = (productAppearance: string[]) => {
+  return !productAppearance.length ? "-" : productAppearance.join("銆�");
 };
 
 const formatValue = (value: any, unit?: string) => {
@@ -438,7 +409,7 @@
   formData.dia = inspectionResult.dia || "";
   formData.maxDia = inspectionResult.maxDia || "";
   formData.minDia = inspectionResult.minDia || "";
-  formData.appearance = inspectionResult.appearance || "";
+  formData.appearance = inspectionResult.appearance || [];
   formData.windingTightness = inspectionResult.windingTightness || "";
   formData.arrangementNeatness = inspectionResult.arrangementNeatness || "";
   formData.aluminumWireDistance = inspectionResult.aluminumWireDistance || "";
@@ -462,6 +433,7 @@
 // 椤甸潰鍔犺浇
 onLoad((options: any) => {
   paramsId.value = options.id;
+  paramsType.value = options.deviceType;
   getDetailData(options.id, options.deviceType);
 });
 
@@ -482,7 +454,7 @@
   // 鏍¢獙
   if (!formData.maxDia) return uni.showToast({ title: "鏈�澶х洿寰勪负蹇呭~椤�", icon: "none" });
   if (!formData.minDia) return uni.showToast({ title: "鏈�灏忕洿寰勪负蹇呭~椤�", icon: "none" });
-  if (!formData.appearance) 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" });
@@ -493,6 +465,9 @@
   if (!formData.conclusion) return uni.showToast({ title: "缁撹涓哄繀濉」", icon: "none" });
   if (!formData.isFully) return uni.showToast({ title: "閾濇潌鏍峰搧鏄惁榻愬叏涓哄繀濉」", icon: "none" });
   if (!deviceUid.value) return uni.showToast({ title: "璇锋壂鎻忎簩缁寸爜", icon: "none" });
+  const { newFiles } = attachmentRef.value.getSubmitFiles();
+  console.log("newFiles", newFiles);
+  const allFileIds = [...newFiles];
   // 鎻愪氦
   try {
     const res = await RoutingInspectionApi.drawPatrolCheckInspection({
@@ -510,12 +485,12 @@
         conclusion: formData.conclusion,
       },
       inspectionResult: { isFully: formData.isFully },
-      processInspectionAttachmentList: tempFiles.value.map((f) => f.url),
+      processInspectionAttachmentList: allFileIds,
     });
     if (res.code === 200) {
       uni.showToast({ title: "淇濆瓨鎴愬姛", icon: "success" });
       isEdit.value = false;
-      getDetailData(paramsId.value, detailData.value.deviceType);
+      getDetailData(paramsId.value, paramsType.value);
     } else {
       uni.showModal({ title: res.msg || "淇濆瓨澶辫触", icon: "error" });
     }
@@ -523,56 +498,6 @@
     console.error("淇濆瓨澶辫触:", e);
     uni.showModal({ title: e.message || "淇濆瓨澶辫触", icon: "error" });
   }
-};
-
-// 闄勪欢涓婁紶鏍¢獙
-const beforeUpload = (file: any) => {
-  const maxSize = 2 * 1024 * 1024;
-  if (file.size > maxSize) {
-    uni.showToast({ title: "鍥剧墖澶у皬涓嶈兘瓒呰繃2M", icon: "none" });
-    return false;
-  }
-  return true;
-};
-
-// 闄勪欢涓婁紶鎴愬姛
-const handleUploadSuccess = (res: any) => {
-  if (Array.isArray(res)) {
-    tempFiles.value = [
-      ...tempFiles.value,
-      ...res.map((file) => ({
-        ...file,
-        id: `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
-      })),
-    ];
-  } else {
-    tempFiles.value.push({
-      ...res,
-      id: `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
-    });
-  }
-};
-
-// 闄勪欢涓婁紶澶辫触
-const handleUploadFail = (err: any) => {
-  uni.showToast({ title: "闄勪欢涓婁紶澶辫触", icon: "error" });
-};
-
-// 鍒犻櫎闄勪欢
-const deleteFile = (index: number) => {
-  if (index >= detailData.value.files.length) {
-    tempFiles.value = tempFiles.value.filter(
-      (_, idx) => idx !== index - detailData.value.files.length
-    );
-  } else {
-    detailData.value.files = detailData.value.files.filter((_, idx) => idx !== index);
-  }
-};
-
-// 鍥剧墖棰勮
-const previewImage = (url: string) => {
-  previewImageUrl.value = url;
-  show.value = true;
 };
 
 const handleClose = () => {
@@ -583,12 +508,10 @@
   scanRef.value.triggerScan();
 };
 const getScanCode = (params: any) => {
-  console.log("瀹屾暣鍙傛暟锛�", params);
   let codeObj = {};
   try {
     codeObj = JSON.parse(params.code);
   } catch (err) {
-    console.error("JSON瑙f瀽澶辫触锛�", err);
     toast.error("鎵爜鏁版嵁寮傚父");
     return; // 瑙f瀽澶辫触鐩存帴杩斿洖锛岄伩鍏嶅悗缁敊璇�
   }
diff --git a/src/pages/routingInspection/upload.vue b/src/pages/routingInspection/upload.vue
new file mode 100644
index 0000000..70e3ad0
--- /dev/null
+++ b/src/pages/routingInspection/upload.vue
@@ -0,0 +1,359 @@
+<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="鏆傛棤闄勪欢" />
+
+      <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>
+          </view>
+          <view class="attachment-actions" @click.stop v-if="isEdit">
+            <wd-icon name="delete" color="#ff4757" @click="deleteAttachment(item.id)" />
+          </view>
+        </view>
+      </wd-card>
+    </view>
+
+    <wd-toast />
+  </view>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from "vue";
+import { useToast } from "wot-design-uni";
+import AttachmentAPI from "@/api/product/attachment";
+
+// 澶栭儴鍙傛暟
+const props = defineProps({
+  detailData: { type: Object, default: () => ({}) },
+  isEdit: { type: Boolean, default: false },
+  deviceType: { type: String, default: "" },
+});
+
+const toast = useToast();
+const attachmentList =
+  props.deviceType == "1"
+    ? ref<any[]>(props.detailData.value.files || [])
+    : ref<any[]>(props.detailData.files || []);
+const attachmentIds =
+  props.deviceType == "1"
+    ? ref<string[]>(props.detailData.value.attachmentId || [])
+    : ref<string[]>(props.detailData.attachmentId || []);
+
+// 鏂板闄勪欢
+const addAttachment = () => {
+  // 鏄剧ず閫夋嫨鏂囦欢绫诲瀷鐨勫脊绐�
+  uni.showActionSheet({
+    itemList: ["閫夋嫨鍥剧墖", "閫夋嫨瑙嗛", "鎷嶇収", "褰曞儚"],
+    success: (res) => {
+      switch (res.tapIndex) {
+        case 0: // 閫夋嫨鍥剧墖
+          chooseImages();
+          break;
+        case 1: // 閫夋嫨瑙嗛
+          chooseVideos();
+          break;
+        case 2: // 鎷嶇収
+          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;
+    });
+    console.log("result", result);
+
+    // 鏇存柊闄勪欢鍒楄〃
+    const flattenedResult = result.flat();
+    attachmentList.value.push(...flattenedResult);
+    console.log(attachmentList.value);
+
+    // 鎻愬彇闄勪欢ID
+    attachmentIds.value = attachmentList.value.map((item: any) => item.id);
+    toast.show("涓婁紶鎴愬姛");
+    console.log("111", attachmentIds.value.attachmentId);
+  } 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).join(",");
+          toast.show("鍒犻櫎鎴愬姛");
+        }
+      },
+    });
+    console.log("111", attachmentIds.value.attachmentId);
+  } catch (error) {
+    console.error("鍒犻櫎澶辫触:", error);
+    toast.show("鍒犻櫎澶辫触");
+  }
+};
+
+// 棰勮闄勪欢
+const previewAttachment = (item: any) => {
+  // 鏍规嵁鏂囦欢绫诲瀷杩涜棰勮
+  const fileName = item.bucketFileName || item.name;
+  const fileType = getFileType(fileName);
+
+  if (fileType.startsWith("image")) {
+    // 鍥剧墖棰勮
+    uni.previewImage({
+      urls: [item.url],
+      current: item.url,
+    });
+  } else {
+    // 鍏朵粬鏂囦欢绫诲瀷锛屽彲浠ヤ笅杞芥垨鎵撳紑
+    uni.downloadFile({
+      url: item.url,
+      success: (res) => {
+        uni.openDocument({
+          filePath: res.tempFilePath,
+          success: () => {
+            console.log("鎵撳紑鏂囨。鎴愬姛");
+          },
+          fail: (error) => {
+            console.error("鎵撳紑鏂囨。澶辫触:", error);
+            toast.show("鏃犳硶棰勮姝ゆ枃浠剁被鍨�");
+          },
+        });
+      },
+      fail: (error) => {
+        console.error("涓嬭浇鏂囦欢澶辫触:", error);
+        toast.show("涓嬭浇鏂囦欢澶辫触");
+      },
+    });
+  }
+};
+
+// 鑾峰彇鏂囦欢绫诲瀷
+const getFileType = (fileName: string) => {
+  if (!fileName) return "unknown";
+  const extension = fileName.split(".").pop()?.toLowerCase();
+  switch (extension) {
+    case "jpg":
+    case "jpeg":
+    case "png":
+    case "gif":
+    case "bmp":
+    case "webp":
+      return "image";
+    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 {
+  .attachment-card {
+    margin-bottom: 12px;
+    border-radius: 4px;
+  }
+}
+
+.attachment-item {
+  display: flex;
+  align-items: center;
+  padding: 12px;
+
+  .attachment-info {
+    flex: 1;
+
+    .attachment-name {
+      font-size: 16px;
+      font-weight: 500;
+      color: #333;
+      margin-bottom: 4px;
+      word-break: break-all;
+    }
+
+    .attachment-meta {
+      display: flex;
+      gap: 12px;
+      font-size: 12px;
+      color: #999;
+    }
+  }
+
+  .attachment-actions {
+    margin-left: 12px;
+
+    :deep(.wd-icon) {
+      font-size: 20px;
+      cursor: pointer;
+    }
+  }
+}
+</style>
--
Gitblit v1.9.3