From 9fae4a56c15392babde83d68db4073762900082d Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期五, 16 十月 2026 22:39:32 +0800
Subject: [PATCH] feat: 设备保养和巡检优化,项目拆分

---
 src/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue | 1167 +++++++++++++++++++++++++++++++---------------------------
 1 files changed, 622 insertions(+), 545 deletions(-)

diff --git a/src/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue b/src/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue
index 481ea85..de550dfb 100644
--- a/src/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue
+++ b/src/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue
@@ -1,27 +1,29 @@
 <template>
-  <FormDialog v-model="dialogVisible"
-              :title="operationType === 'view' ? '宸℃璁板綍璇︽儏' : '涓婁紶宸℃璁板綍'"
-              width="980px"
-              :operation-type="operationType"
-              @close="handleClose"
-              @cancel="handleClose">
+  <FormDialog
+    v-model="dialogVisible"
+    :title="operationType === 'view' ? '宸℃璁板綍璇︽儏' : '涓婁紶宸℃璁板綍'"
+    width="980px"
+    :operation-type="operationType"
+    @close="handleClose"
+    @cancel="handleClose"
+  >
     <main class="upload-content">
-      <el-card v-if="taskInfo"
-               class="section-card">
-        <el-descriptions :column="2"
-                         border>
+      <el-card v-if="taskInfo" class="section-card">
+        <el-descriptions :column="2" border>
           <el-descriptions-item label="宸℃浠诲姟鍚嶇О">
             {{ taskInfo.taskName || "-" }}
           </el-descriptions-item>
           <el-descriptions-item label="宸℃椤圭洰">
-            {{ taskInfo.inspectionProject || "-" }}
+            {{ inspectionProjectSummary || "-" }}
           </el-descriptions-item>
           <el-descriptions-item label="鎵ц宸℃浜�">
             <template v-if="formattedInspector && formattedInspector.length">
-              <el-tag v-for="tag in formattedInspector"
-                      :key="tag"
-                      size="small"
-                      style="margin-right: 4px">
+              <el-tag
+                v-for="tag in formattedInspector"
+                :key="tag"
+                size="small"
+                style="margin-right: 4px"
+              >
                 {{ tag }}
               </el-tag>
             </template>
@@ -44,47 +46,66 @@
           </el-descriptions-item>
         </el-descriptions>
       </el-card>
+      <el-card v-if="inspectionItems.length" class="section-card inspection-items-card">
+        <div class="section-heading">
+          <h3>宸℃鍐呭</h3>
+          <span>宸插畬鎴� {{ completedItemIds.length }} / {{ inspectionItems.length }} 椤�</span>
+        </div>
+        <div v-for="(item, index) in inspectionItems" :key="item.id" class="inspection-item-row">
+          <div class="inspection-item-copy">
+            <div class="inspection-item-title">{{ index + 1 }}. {{ item.itemProject }}</div>
+            <div class="inspection-item-content">{{ item.itemContent }}</div>
+          </div>
+          <el-checkbox
+            :model-value="isItemCompleted(item)"
+            :disabled="operationType === 'view'"
+            @change="(checked) => toggleItem(item, checked)"
+          >
+            {{ isItemCompleted(item) ? "宸插畬鎴�" : "鏈畬鎴�" }}
+          </el-checkbox>
+        </div>
+      </el-card>
       <el-card class="section-card">
         <h3>宸℃鐘舵��</h3>
-        <el-radio-group v-model="hasException"
-                        :disabled="operationType === 'view'">
+        <el-radio-group v-model="hasException" :disabled="operationType === 'view'">
           <el-radio-button :value="false">姝e父</el-radio-button>
           <el-radio-button :value="true">瀛樺湪寮傚父</el-radio-button>
         </el-radio-group>
       </el-card>
-      <el-card v-if="hasException === true"
-               class="section-card">
+      <el-card v-if="hasException === true" class="section-card">
         <h3>寮傚父鎻忚堪</h3>
-        <el-input v-model="abnormalDescription"
-                  type="textarea"
-                  maxlength="500"
-                  show-word-limit
-                  :rows="4"
-                  :disabled="operationType === 'view'"
-                  placeholder="璇锋弿杩板紓甯告儏鍐�..." />
+        <el-input
+          v-model="abnormalDescription"
+          type="textarea"
+          maxlength="500"
+          show-word-limit
+          :rows="4"
+          :disabled="operationType === 'view'"
+          placeholder="璇锋弿杩板紓甯告儏鍐�..."
+        />
       </el-card>
-      <el-card v-if="hasException === true"
-               class="section-card">
-        <div class="upload-buttons"
-             v-if="operationType !== 'view'">
-          <el-upload :show-file-list="false"
-                     :http-request="uploadFile"
-                     :disabled="beforeModelValue.length >= uploadConfig.limit || uploading"
-                     accept="image/*">
-            <el-button type="primary"
-                       :loading="uploading">
+      <el-card v-if="hasException === true" class="section-card">
+        <div class="upload-buttons" v-if="operationType !== 'view'">
+          <el-upload
+            :show-file-list="false"
+            :http-request="uploadFile"
+            :disabled="beforeModelValue.length >= uploadConfig.limit || uploading"
+            accept="image/*"
+          >
+            <el-button type="primary" :loading="uploading">
               <el-icon>
                 <Camera />
               </el-icon>
               閫夋嫨鍥剧墖
             </el-button>
           </el-upload>
-          <el-upload :show-file-list="false"
-                     :http-request="uploadFile"
-                     :disabled="beforeModelValue.length >= uploadConfig.limit || uploading"
-                     accept="video/*">
-            <el-button type="success"
-                       :loading="uploading">
+          <el-upload
+            :show-file-list="false"
+            :http-request="uploadFile"
+            :disabled="beforeModelValue.length >= uploadConfig.limit || uploading"
+            accept="video/*"
+          >
+            <el-button type="success" :loading="uploading">
               <el-icon>
                 <VideoCamera />
               </el-icon>
@@ -92,35 +113,36 @@
             </el-button>
           </el-upload>
         </div>
-        <el-progress v-if="uploading"
-                     :percentage="uploadProgress"
-                     class="upload-progress" />
-        <div v-if="beforeModelValue.length"
-             class="file-list">
-          <div v-for="(file, index) in beforeModelValue"
-               :key="file.uid || file.id || index"
-               class="file-item">
+        <el-progress v-if="uploading" :percentage="uploadProgress" class="upload-progress" />
+        <div v-if="beforeModelValue.length" class="file-list">
+          <div
+            v-for="(file, index) in beforeModelValue"
+            :key="file.uid || file.id || index"
+            class="file-item"
+          >
             <div class="file-preview-container">
-              <el-image v-if="file.type === 'image' || !file.type"
-                        :src="file.url || file.tempFilePath || file.path || file.downloadUrl"
-                        fit="cover"
-                        class="file-preview"
-                        :preview-src-list="[file.url || file.tempFilePath || file.path || file.downloadUrl]"
-                        preview-teleported />
-              <div v-else
-                   class="video-preview"
-                   @click="previewVideo(file)">
+              <el-image
+                v-if="file.type === 'image' || !file.type"
+                :src="file.url || file.tempFilePath || file.path || file.downloadUrl"
+                fit="cover"
+                class="file-preview"
+                :preview-src-list="[file.url || file.tempFilePath || file.path || file.downloadUrl]"
+                preview-teleported
+              />
+              <div v-else class="video-preview" @click="previewVideo(file)">
                 <el-icon>
                   <VideoCamera />
                 </el-icon>
                 <span>瑙嗛</span>
               </div>
-              <el-button class="delete-btn"
-                         type="danger"
-                         circle
-                         size="small"
-                         v-if="operationType !== 'view'"
-                         @click="removeFile(index)">
+              <el-button
+                class="delete-btn"
+                type="danger"
+                circle
+                size="small"
+                v-if="operationType !== 'view'"
+                @click="removeFile(index)"
+              >
                 <el-icon>
                   <Close />
                 </el-icon>
@@ -134,559 +156,614 @@
             </div>
           </div>
         </div>
-        <el-empty v-else
-                  :description="operationType === 'view' ? '鏆傛棤寮傚父鐓х墖鎴栬棰�' : '璇烽�夋嫨瑕佷笂浼犵殑宸℃鍥剧墖鎴栬棰�'" />
+        <el-empty
+          v-else
+          :description="
+            operationType === 'view' ? '鏆傛棤寮傚父鐓х墖鎴栬棰�' : '璇烽�夋嫨瑕佷笂浼犵殑宸℃鍥剧墖鎴栬棰�'
+          "
+        />
       </el-card>
-      <el-result v-if="hasException === false"
-                 icon="success"
-                 title="璁惧杩愯姝e父"
-                 :sub-title="operationType === 'view' ? '' : '鏃犻渶涓婁紶鐓х墖'" />
+      <el-result
+        v-if="hasException === false"
+        icon="success"
+        title="璁惧杩愯姝e父"
+        :sub-title="operationType === 'view' ? '' : '鏃犻渶涓婁紶鐓х墖'"
+      />
     </main>
     <template #footer>
       <footer class="footer-buttons">
-        <el-button type="primary"
-                   v-if="operationType !== 'view'"
-                   @click="submitUpload">鎻愪氦</el-button>
-        <el-button v-if="hasException === true && operationType !== 'view'"
-                   type="warning"
-                   @click="goToRepair">
+        <el-button type="primary" v-if="operationType !== 'view'" @click="submitUpload"
+          >鎻愪氦</el-button
+        >
+        <el-button
+          v-if="hasException === true && operationType !== 'view'"
+          type="warning"
+          @click="goToRepair"
+        >
           鏂板鎶ヤ慨
         </el-button>
-        <el-button @click="handleClose">{{ operationType === 'view' ? '鍏抽棴' : '鍙栨秷' }}</el-button>
+        <el-button @click="handleClose">{{ operationType === "view" ? "鍏抽棴" : "鍙栨秷" }}</el-button>
       </footer>
     </template>
   </FormDialog>
-  <el-dialog v-model="showVideoDialog"
-             :title="currentVideoFile?.originalFilename || currentVideoFile?.name || '瑙嗛棰勮'"
-             width="720px">
-    <video v-if="currentVideoFile"
-           :src="currentVideoFile.url || currentVideoFile.downloadUrl"
-           class="video-player"
-           controls
-           autoplay />
+  <el-dialog
+    v-model="showVideoDialog"
+    :title="currentVideoFile?.originalFilename || currentVideoFile?.name || '瑙嗛棰勮'"
+    width="720px"
+  >
+    <video
+      v-if="currentVideoFile"
+      :src="currentVideoFile.url || currentVideoFile.downloadUrl"
+      class="video-player"
+      controls
+      autoplay
+    />
   </el-dialog>
 </template>
 
 <script setup>
-  import { computed, ref } from "vue";
-  import { useRouter } from "vue-router";
-  import { ElLoading, ElMessage, ElMessageBox } from "element-plus";
-  import { Camera, Close, VideoCamera } from "@element-plus/icons-vue";
-  import axios from "axios";
-  import dayjs from "dayjs";
-  import FormDialog from "@/components/Dialog/FormDialog.vue";
-  import { uploadInspectionTask } from "@/api/inspectionManagement/index.js";
-  import { getToken } from "@/utils/auth";
+import { computed, ref } from "vue";
+import { useRouter } from "vue-router";
+import { ElLoading, ElMessage, ElMessageBox } from "element-plus";
+import { Camera, Close, VideoCamera } from "@element-plus/icons-vue";
+import axios from "axios";
+import dayjs from "dayjs";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+import { uploadInspectionTask } from "@/api/inspectionManagement/index.js";
+import { getToken } from "@/utils/auth";
 
-  const emit = defineEmits(["closeDia", "success"]);
-  const router = useRouter();
+const emit = defineEmits(["closeDia", "success"]);
+const router = useRouter();
 
-  const dialogVisible = ref(false);
-  const taskInfo = ref(null);
-  const uploading = ref(false);
-  const uploadProgress = ref(0);
-  const operationType = ref("add"); // add, view
+const dialogVisible = ref(false);
+const taskInfo = ref(null);
+const uploading = ref(false);
+const uploadProgress = ref(0);
+const operationType = ref("add"); // add, view
 
-  const beforeModelValue = ref([]);
+const beforeModelValue = ref([]);
+const inspectionItems = ref([]);
+const completedItemIds = ref([]);
+const inspectionProjectSummary = computed(() =>
+  [...new Set(inspectionItems.value.map((item) => item.itemProject).filter(Boolean))].join("銆�")
+);
 
-  const formattedInspector = computed(() => {
-    if (!taskInfo.value?.inspector) return [];
-    if (Array.isArray(taskInfo.value.inspector)) return taskInfo.value.inspector;
-    if (typeof taskInfo.value.inspector === "string") {
-      return taskInfo.value.inspector
-        .split(",")
-        .map(s => s.trim())
-        .filter(s => s);
+const formattedInspector = computed(() => {
+  if (!taskInfo.value?.inspector) return [];
+  if (Array.isArray(taskInfo.value.inspector)) return taskInfo.value.inspector;
+  if (typeof taskInfo.value.inspector === "string") {
+    return taskInfo.value.inspector
+      .split(",")
+      .map((s) => s.trim())
+      .filter((s) => s);
+  }
+  return [taskInfo.value.inspector];
+});
+
+const formatFrequencyType = (type) => {
+  const mapping = {
+    DAILY: "姣忔棩",
+    WEEKLY: "姣忓懆",
+    MONTHLY: "姣忔湀",
+    QUARTERLY: "瀛e害",
+  };
+  return mapping[type] || type;
+};
+
+const formatFrequencyDetail = (detail) => {
+  if (typeof detail !== "string") return detail;
+  const replacements = {
+    MON: "鍛ㄤ竴",
+    TUE: "鍛ㄤ簩",
+    WED: "鍛ㄤ笁",
+    THU: "鍛ㄥ洓",
+    FRI: "鍛ㄤ簲",
+    SAT: "鍛ㄥ叚",
+    SUN: "鍛ㄦ棩",
+  };
+  return detail.replace(/MON|TUE|WED|THU|FRI|SAT|SUN/g, (match) => replacements[match]);
+};
+
+const formatDateTime = (date) => {
+  if (!date) return "-";
+  return dayjs(date).format("YYYY-MM-DD");
+};
+
+const hasException = ref(null);
+const abnormalDescription = ref("");
+
+const showVideoDialog = ref(false);
+const currentVideoFile = ref(null);
+
+const uploadConfig = {
+  action: "/common/upload",
+  limit: 10,
+  fileSize: 50,
+  fileType: ["jpg", "jpeg", "png", "mp4", "mov"],
+};
+
+const uploadFileUrl = computed(() => `${import.meta.env.VITE_APP_BASE_API}${uploadConfig.action}`);
+
+const processFileUrl = (fileUrl) => {
+  if (!fileUrl) return "";
+
+  let currentUrl = String(fileUrl);
+  if (currentUrl.includes("\\")) {
+    const uploadsIndex = currentUrl.toLowerCase().indexOf("uploads");
+    if (uploadsIndex > -1) {
+      currentUrl = `/${currentUrl.substring(uploadsIndex).replace(/\\/g, "/")}`;
+    } else {
+      const fileName = currentUrl.split("\\").pop();
+      currentUrl = `/uploads/${fileName}`;
     }
-    return [taskInfo.value.inspector];
+  }
+
+  if (currentUrl && !currentUrl.startsWith("http")) {
+    if (!currentUrl.startsWith("/")) {
+      currentUrl = `/${currentUrl}`;
+    }
+    currentUrl = __BASE_API__ + currentUrl;
+  }
+
+  return currentUrl;
+};
+
+const normalizeList = (list, fileType) => {
+  if (!Array.isArray(list)) return [];
+
+  return list.filter(Boolean).map((item) => {
+    let currentType = item.type;
+    if (!currentType && item.contentType) {
+      currentType = item.contentType.startsWith("video") ? "video" : "image";
+    } else if (!currentType) {
+      currentType = fileType || "image";
+    }
+
+    return {
+      ...item,
+      url: processFileUrl(item.url || item.previewURL || item.downloadUrl || item.path || ""),
+      downloadUrl: processFileUrl(
+        item.downloadUrl || item.url || item.previewURL || item.path || ""
+      ),
+      name: item.name || item.originalFilename || item.bucketFilename,
+      tempId: item.tempId || item.id || item.tempFileId,
+      tempFileId: item.tempFileId || item.tempId || item.id,
+      size: item.size || item.byteSize || 0,
+      type: currentType,
+      status: "success",
+      uid: item.uid || `${Date.now()}-${Math.random()}`,
+    };
   });
+};
 
-  const formatFrequencyType = type => {
-    const mapping = {
-      DAILY: "姣忔棩",
-      WEEKLY: "姣忓懆",
-      MONTHLY: "姣忔湀",
-      QUARTERLY: "瀛e害",
-    };
-    return mapping[type] || type;
-  };
+const resetState = () => {
+  taskInfo.value = null;
+  beforeModelValue.value = [];
+  inspectionItems.value = [];
+  completedItemIds.value = [];
+  hasException.value = null;
+  abnormalDescription.value = "";
+  uploading.value = false;
+  uploadProgress.value = 0;
+  showVideoDialog.value = false;
+  currentVideoFile.value = null;
+  operationType.value = "add";
+};
 
-  const formatFrequencyDetail = detail => {
-    if (typeof detail !== "string") return detail;
-    const replacements = {
-      MON: "鍛ㄤ竴",
-      TUE: "鍛ㄤ簩",
-      WED: "鍛ㄤ笁",
-      THU: "鍛ㄥ洓",
-      FRI: "鍛ㄤ簲",
-      SAT: "鍛ㄥ叚",
-      SUN: "鍛ㄦ棩",
-    };
-    return detail.replace(
-      /MON|TUE|WED|THU|FRI|SAT|SUN/g,
-      match => replacements[match]
-    );
-  };
+const openDialog = (type, row) => {
+  operationType.value = type || "add";
+  const raw = JSON.parse(JSON.stringify(row?.__raw || row || {}));
+  taskInfo.value = raw;
+  inspectionItems.value = Array.isArray(raw.inspectionItems) ? raw.inspectionItems : [];
+  completedItemIds.value = inspectionItems.value
+    .filter((item) => Number(item.itemStatus) === 1)
+    .map((item) => item.id);
 
-  const formatDateTime = date => {
-    if (!date) return "-";
-    return dayjs(date).format("YYYY-MM-DD");
-  };
-
-  const hasException = ref(null);
-  const abnormalDescription = ref("");
-
-  const showVideoDialog = ref(false);
-  const currentVideoFile = ref(null);
-
-  const uploadConfig = {
-    action: "/common/upload",
-    limit: 10,
-    fileSize: 50,
-    fileType: ["jpg", "jpeg", "png", "mp4", "mov"],
-  };
-
-  const uploadFileUrl = computed(
-    () => `${import.meta.env.VITE_APP_BASE_API}${uploadConfig.action}`
+  beforeModelValue.value = normalizeList(
+    raw.commonFileListBeforeVO || raw.commonFileListBefore || [],
+    "image"
   );
 
-  const processFileUrl = fileUrl => {
-    if (!fileUrl) return "";
+  abnormalDescription.value = raw.abnormalDescription || "";
 
-    let currentUrl = String(fileUrl);
-    if (currentUrl.includes("\\")) {
-      const uploadsIndex = currentUrl.toLowerCase().indexOf("uploads");
-      if (uploadsIndex > -1) {
-        currentUrl = `/${currentUrl.substring(uploadsIndex).replace(/\\/g, "/")}`;
-      } else {
-        const fileName = currentUrl.split("\\").pop();
-        currentUrl = `/uploads/${fileName}`;
-      }
-    }
-
-    if (currentUrl && !currentUrl.startsWith("http")) {
-      if (!currentUrl.startsWith("/")) {
-        currentUrl = `/${currentUrl}`;
-      }
-      currentUrl = __BASE_API__ + currentUrl;
-    }
-
-    return currentUrl;
-  };
-
-  const normalizeList = (list, fileType) => {
-    if (!Array.isArray(list)) return [];
-
-    return list.filter(Boolean).map(item => {
-      let currentType = item.type;
-      if (!currentType && item.contentType) {
-        currentType = item.contentType.startsWith("video") ? "video" : "image";
-      } else if (!currentType) {
-        currentType = fileType || "image";
-      }
-
-      return {
-        ...item,
-        url: processFileUrl(
-          item.url || item.previewURL || item.downloadUrl || item.path || ""
-        ),
-        downloadUrl: processFileUrl(
-          item.downloadUrl || item.url || item.previewURL || item.path || ""
-        ),
-        name: item.name || item.originalFilename || item.bucketFilename,
-        tempId: item.tempId || item.id || item.tempFileId,
-        tempFileId: item.tempFileId || item.tempId || item.id,
-        size: item.size || item.byteSize || 0,
-        type: currentType,
-        status: "success",
-        uid: item.uid || `${Date.now()}-${Math.random()}`,
-      };
-    });
-  };
-
-  const resetState = () => {
-    taskInfo.value = null;
-    beforeModelValue.value = [];
+  if (raw.hasException !== undefined && raw.hasException !== null) {
+    hasException.value = raw.hasException;
+  } else if (raw.inspectionResult !== undefined && raw.inspectionResult !== null) {
+    hasException.value = String(raw.inspectionResult) === "0";
+  } else {
     hasException.value = null;
-    abnormalDescription.value = "";
-    uploading.value = false;
-    uploadProgress.value = 0;
-    showVideoDialog.value = false;
-    currentVideoFile.value = null;
-    operationType.value = "add";
-  };
+  }
 
-  const openDialog = (type, row) => {
-    operationType.value = type || "add";
-    const raw = JSON.parse(JSON.stringify(row?.__raw || row || {}));
-    taskInfo.value = raw;
+  if (hasException.value !== true && beforeModelValue.value.length) {
+    hasException.value = true;
+  }
 
-    beforeModelValue.value = normalizeList(
-      raw.commonFileListBeforeVO || raw.commonFileListBefore || [],
-      "image"
+  dialogVisible.value = true;
+};
+
+const isItemCompleted = (item) => completedItemIds.value.includes(item.id);
+const toggleItem = (item, checked) => {
+  const completed = new Set(completedItemIds.value);
+  if (checked) completed.add(item.id);
+  else completed.delete(item.id);
+  completedItemIds.value = [...completed];
+};
+
+const handleClose = () => {
+  dialogVisible.value = false;
+  resetState();
+  emit("closeDia");
+};
+
+const previewVideo = (file) => {
+  currentVideoFile.value = file;
+  showVideoDialog.value = true;
+};
+
+const uploadFile = async (uploadRequest) => {
+  const rawFile = uploadRequest.file;
+
+  if (beforeModelValue.value.length >= uploadConfig.limit) {
+    ElMessage.warning(`鏈�澶氬彧鑳介�夋嫨${uploadConfig.limit}涓枃浠禶);
+    return;
+  }
+
+  const ext = rawFile.name.split(".").pop()?.toLowerCase();
+  if (!uploadConfig.fileType.includes(ext)) {
+    ElMessage.warning(`鏂囦欢鏍煎紡涓嶆敮鎸侊紝璇蜂笂浼� ${uploadConfig.fileType.join("/")} 鏍煎紡`);
+    return;
+  }
+
+  if (rawFile.size > uploadConfig.fileSize * 1024 * 1024) {
+    ElMessage.warning(`鏂囦欢澶у皬涓嶈兘瓒呰繃 ${uploadConfig.fileSize}MB`);
+    return;
+  }
+
+  const token = getToken();
+  if (!token) {
+    ElMessage.warning("鐢ㄦ埛鏈櫥褰�");
+    return;
+  }
+
+  const formData = new FormData();
+  formData.append("files", rawFile);
+  formData.append("type", 10); // 鐢熶骇鍓嶅浐瀹氫负10
+
+  uploading.value = true;
+  uploadProgress.value = 0;
+
+  try {
+    const { data } = await axios.post(uploadFileUrl.value, formData, {
+      headers: {
+        Authorization: `Bearer ${token}`,
+        "Content-Type": "multipart/form-data",
+      },
+      onUploadProgress: (event) => {
+        if (event.total) {
+          uploadProgress.value = Math.round((event.loaded / event.total) * 100);
+        }
+      },
+    });
+
+    if (data.code !== 200) {
+      ElMessage.error(data.msg || "涓婁紶澶辫触");
+      return;
+    }
+
+    const resultData = Array.isArray(data.data) ? data.data[0] : data.data;
+    const finalUrl = processFileUrl(
+      resultData.url || resultData.previewURL || resultData.downloadUrl || ""
     );
+    const finalName = resultData.name || resultData.originalFilename || resultData.bucketFilename;
+    const finalId = resultData.tempId || resultData.id || resultData.tempFileId;
 
-    abnormalDescription.value = raw.abnormalDescription || "";
+    const uploadedFile = {
+      ...resultData,
+      url: finalUrl,
+      downloadUrl: finalUrl,
+      name: finalName,
+      tempId: finalId,
+      tempFileId: resultData.tempFileId || finalId,
+      size: rawFile.size || resultData.size || resultData.byteSize || 0,
+      type: rawFile.type?.startsWith("video") ? "video" : "image",
+      status: "success",
+      uid: `${Date.now()}-${Math.random()}`,
+    };
 
-    if (raw.hasException !== undefined && raw.hasException !== null) {
-      hasException.value = raw.hasException;
-    } else if (
-      raw.inspectionResult !== undefined &&
-      raw.inspectionResult !== null
-    ) {
-      hasException.value = String(raw.inspectionResult) === "0";
-    } else {
-      hasException.value = null;
-    }
+    beforeModelValue.value.push(uploadedFile);
+    ElMessage.success("涓婁紶鎴愬姛");
+  } catch (error) {
+    ElMessage.error(error?.message || "涓婁紶澶辫触");
+  } finally {
+    uploading.value = false;
+  }
+};
 
-    if (hasException.value !== true && beforeModelValue.value.length) {
-      hasException.value = true;
-    }
+const buildFileItem = (item) => ({
+  id: item?.id,
+  tempId: item?.tempId,
+  tempFileId: item?.tempFileId,
+  url: item?.downloadUrl || item?.url || "",
+  downloadUrl: item?.downloadUrl || item?.url || "",
+  name: item?.name,
+  bucketFilename: item?.bucketFilename || item?.name,
+  originalFilename: item?.originalFilename || item?.name,
+  size: item?.size || 0,
+  byteSize: item?.byteSize || item?.size || 0,
+  contentType: item?.contentType || "",
+  type: item?.type,
+});
 
-    dialogVisible.value = true;
-  };
+const submitUpload = async () => {
+  if (hasException.value === null) {
+    ElMessage.warning("璇烽�夋嫨宸℃鐘舵��");
+    return;
+  }
 
-  const handleClose = () => {
-    dialogVisible.value = false;
-    resetState();
-    emit("closeDia");
-  };
+  if (hasException.value === true) {
+    const totalFiles = beforeModelValue.value.length;
 
-  const previewVideo = file => {
-    currentVideoFile.value = file;
-    showVideoDialog.value = true;
-  };
-
-  const uploadFile = async uploadRequest => {
-    const rawFile = uploadRequest.file;
-
-    if (beforeModelValue.value.length >= uploadConfig.limit) {
-      ElMessage.warning(`鏈�澶氬彧鑳介�夋嫨${uploadConfig.limit}涓枃浠禶);
+    if (!totalFiles) {
+      ElMessage.warning("璇蜂笂浼犲紓甯哥収鐗囨垨瑙嗛");
       return;
     }
 
-    const ext = rawFile.name.split(".").pop()?.toLowerCase();
-    if (!uploadConfig.fileType.includes(ext)) {
-      ElMessage.warning(
-        `鏂囦欢鏍煎紡涓嶆敮鎸侊紝璇蜂笂浼� ${uploadConfig.fileType.join("/")} 鏍煎紡`
-      );
+    if (!abnormalDescription.value.trim()) {
+      ElMessage.warning("璇峰~鍐欏紓甯告弿杩�");
       return;
     }
+  }
 
-    if (rawFile.size > uploadConfig.fileSize * 1024 * 1024) {
-      ElMessage.warning(`鏂囦欢澶у皬涓嶈兘瓒呰繃 ${uploadConfig.fileSize}MB`);
-      return;
-    }
-
-    const token = getToken();
-    if (!token) {
-      ElMessage.warning("鐢ㄦ埛鏈櫥褰�");
-      return;
-    }
-
-    const formData = new FormData();
-    formData.append("files", rawFile);
-    formData.append("type", 10); // 鐢熶骇鍓嶅浐瀹氫负10
-
-    uploading.value = true;
-    uploadProgress.value = 0;
-
-    try {
-      const { data } = await axios.post(uploadFileUrl.value, formData, {
-        headers: {
-          Authorization: `Bearer ${token}`,
-          "Content-Type": "multipart/form-data",
-        },
-        onUploadProgress: event => {
-          if (event.total) {
-            uploadProgress.value = Math.round((event.loaded / event.total) * 100);
-          }
-        },
-      });
-
-      if (data.code !== 200) {
-        ElMessage.error(data.msg || "涓婁紶澶辫触");
-        return;
-      }
-
-      const resultData = Array.isArray(data.data) ? data.data[0] : data.data;
-      const finalUrl = processFileUrl(
-        resultData.url || resultData.previewURL || resultData.downloadUrl || ""
-      );
-      const finalName =
-        resultData.name ||
-        resultData.originalFilename ||
-        resultData.bucketFilename;
-      const finalId = resultData.tempId || resultData.id || resultData.tempFileId;
-
-      const uploadedFile = {
-        ...resultData,
-        url: finalUrl,
-        downloadUrl: finalUrl,
-        name: finalName,
-        tempId: finalId,
-        tempFileId: resultData.tempFileId || finalId,
-        size: rawFile.size || resultData.size || resultData.byteSize || 0,
-        type: rawFile.type?.startsWith("video") ? "video" : "image",
-        status: "success",
-        uid: `${Date.now()}-${Math.random()}`,
-      };
-
-      beforeModelValue.value.push(uploadedFile);
-      ElMessage.success("涓婁紶鎴愬姛");
-    } catch (error) {
-      ElMessage.error(error?.message || "涓婁紶澶辫触");
-    } finally {
-      uploading.value = false;
-    }
-  };
-
-  const buildFileItem = item => ({
-    id: item?.id,
-    tempId: item?.tempId,
-    tempFileId: item?.tempFileId,
-    url: item?.downloadUrl || item?.url || "",
-    downloadUrl: item?.downloadUrl || item?.url || "",
-    name: item?.name,
-    bucketFilename: item?.bucketFilename || item?.name,
-    originalFilename: item?.originalFilename || item?.name,
-    size: item?.size || 0,
-    byteSize: item?.byteSize || item?.size || 0,
-    contentType: item?.contentType || "",
-    type: item?.type,
+  const loading = ElLoading.service({
+    text: "鎻愪氦涓�...",
+    background: "rgba(0, 0, 0, 0.3)",
   });
 
-  const submitUpload = async () => {
-    if (hasException.value === null) {
-      ElMessage.warning("璇烽�夋嫨宸℃鐘舵��");
-      return;
-    }
+  try {
+    const allFiles = [...beforeModelValue.value];
 
-    if (hasException.value === true) {
-      const totalFiles = beforeModelValue.value.length;
+    const tempFileIds = allFiles
+      .map((item) => item?.tempId ?? item?.tempFileId ?? item?.id)
+      .filter(Boolean);
 
-      if (!totalFiles) {
-        ElMessage.warning("璇蜂笂浼犲紓甯哥収鐗囨垨瑙嗛");
-        return;
-      }
+    const {
+      createTime,
+      updateTime,
+      storageBlobDTO,
+      commonFileListAfterVO,
+      commonFileListVO,
+      commonFileListBeforeVO,
+      commonFileListAfter,
+      commonFileList,
+      commonFileListBefore,
+      __raw,
+      ...baseTaskInfo
+    } = taskInfo.value || {};
 
-      if (!abnormalDescription.value.trim()) {
-        ElMessage.warning("璇峰~鍐欏紓甯告弿杩�");
-        return;
-      }
-    }
-
-    const loading = ElLoading.service({
-      text: "鎻愪氦涓�...",
-      background: "rgba(0, 0, 0, 0.3)",
-    });
-
-    try {
-      const allFiles = [...beforeModelValue.value];
-
-      const tempFileIds = allFiles
-        .map(item => item?.tempId ?? item?.tempFileId ?? item?.id)
-        .filter(Boolean);
-
-      const {
-        createTime,
-        updateTime,
-        storageBlobDTO,
-        commonFileListAfterVO,
-        commonFileListVO,
-        commonFileListBeforeVO,
-        commonFileListAfter,
-        commonFileList,
-        commonFileListBefore,
-        __raw,
-        ...baseTaskInfo
-      } = taskInfo.value || {};
-
-      const submitData = {
-        ...baseTaskInfo,
-        commonFileListBeforeDTO: beforeModelValue.value.map(buildFileItem),
-        commonFileListDTO: [],
-        commonFileListAfterDTO: [],
-        hasException: hasException.value,
-        inspectionResult: hasException.value ? 0 : 1,
-        abnormalDescription: abnormalDescription.value,
-        tempFileIds,
-      };
-
-      const result = await uploadInspectionTask(submitData);
-
-      if (result && (result.code === 200 || result.success)) {
-        ElMessage.success("鎻愪氦鎴愬姛");
-        dialogVisible.value = false;
-        resetState();
-        emit("success");
-        emit("closeDia");
-      } else {
-        ElMessage.error(result?.msg || result?.message || "鎻愪氦澶辫触");
-      }
-    } catch (error) {
-      ElMessage.error(error?.message || "鎻愪氦澶辫触");
-    } finally {
-      loading.close();
-    }
-  };
-
-  const removeFile = async index => {
-    try {
-      await ElMessageBox.confirm("纭畾瑕佸垹闄よ繖涓枃浠跺悧锛�", "纭鍒犻櫎", {
-        type: "warning",
-      });
-      beforeModelValue.value.splice(index, 1);
-    } catch {}
-  };
-
-  const goToRepair = () => {
-    const taskData = {
-      taskId: taskInfo.value?.taskId || taskInfo.value?.id,
-      taskName: taskInfo.value?.taskName,
-      inspectionLocation: taskInfo.value?.inspectionLocation,
-      inspector: taskInfo.value?.inspector,
-      hasException: hasException.value,
-      inspectionResult: hasException.value ? 0 : 1,
+    const submitData = {
+      ...baseTaskInfo,
+      inspectionItems: inspectionItems.value.map((item) => ({
+        id: item.id,
+        itemStatus: completedItemIds.value.includes(item.id) ? 1 : 0,
+        remarks: item.remarks || "",
+      })),
       commonFileListBeforeDTO: beforeModelValue.value.map(buildFileItem),
       commonFileListDTO: [],
       commonFileListAfterDTO: [],
-      uploadedFiles: {
-        before: beforeModelValue.value,
-        after: [],
-        issue: [],
-      },
+      hasException: hasException.value,
+      inspectionResult: hasException.value ? 0 : 1,
+      abnormalDescription: abnormalDescription.value,
+      tempFileIds,
     };
 
-    sessionStorage.setItem("repairTaskInfo", JSON.stringify(taskData));
-    router.push("/equipmentManagement/repair/add");
-  };
+    const result = await uploadInspectionTask(submitData);
 
-  const formatFileSize = size => {
-    if (!size) return "0 B";
-
-    const units = ["B", "KB", "MB", "GB"];
-    let index = 0;
-    let fileSize = size;
-
-    while (fileSize >= 1024 && index < units.length - 1) {
-      fileSize /= 1024;
-      index += 1;
+    if (result && (result.code === 200 || result.success)) {
+      ElMessage.success("鎻愪氦鎴愬姛");
+      dialogVisible.value = false;
+      resetState();
+      emit("success");
+      emit("closeDia");
+    } else {
+      ElMessage.error(result?.msg || result?.message || "鎻愪氦澶辫触");
     }
+  } catch (error) {
+    ElMessage.error(error?.message || "鎻愪氦澶辫触");
+  } finally {
+    loading.close();
+  }
+};
 
-    return `${fileSize.toFixed(2)} ${units[index]}`;
+const removeFile = async (index) => {
+  try {
+    await ElMessageBox.confirm("纭畾瑕佸垹闄よ繖涓枃浠跺悧锛�", "纭鍒犻櫎", {
+      type: "warning",
+    });
+    beforeModelValue.value.splice(index, 1);
+  } catch {}
+};
+
+const goToRepair = () => {
+  const taskData = {
+    taskId: taskInfo.value?.taskId || taskInfo.value?.id,
+    taskName: taskInfo.value?.taskName,
+    inspectionLocation: taskInfo.value?.inspectionLocation,
+    inspector: taskInfo.value?.inspector,
+    hasException: hasException.value,
+    inspectionResult: hasException.value ? 0 : 1,
+    commonFileListBeforeDTO: beforeModelValue.value.map(buildFileItem),
+    commonFileListDTO: [],
+    commonFileListAfterDTO: [],
+    uploadedFiles: {
+      before: beforeModelValue.value,
+      after: [],
+      issue: [],
+    },
   };
 
-  defineExpose({
-    openDialog,
-  });
+  sessionStorage.setItem("repairTaskInfo", JSON.stringify(taskData));
+  router.push("/equipmentManagement/repair/add");
+};
+
+const formatFileSize = (size) => {
+  if (!size) return "0 B";
+
+  const units = ["B", "KB", "MB", "GB"];
+  let index = 0;
+  let fileSize = size;
+
+  while (fileSize >= 1024 && index < units.length - 1) {
+    fileSize /= 1024;
+    index += 1;
+  }
+
+  return `${fileSize.toFixed(2)} ${units[index]}`;
+};
+
+defineExpose({
+  openDialog,
+});
 </script>
 
 <style scoped>
-  .inspection-upload-page {
-    min-height: 70vh;
-    background: #f5f7fa;
-    padding: 20px 20px 90px;
-    box-sizing: border-box;
-  }
+.inspection-upload-page {
+  min-height: 70vh;
+  background: #f5f7fa;
+  padding: 20px 20px 90px;
+  box-sizing: border-box;
+}
 
-  .upload-content {
-    max-width: 960px;
-    margin: 20px auto 0;
-  }
+.upload-content {
+  max-width: 960px;
+  margin: 20px auto 0;
+}
 
-  .section-card {
-    margin-bottom: 16px;
-  }
+.section-card {
+  margin-bottom: 16px;
+}
 
-  .section-card h3 {
-    margin: 0 0 16px;
-    font-size: 16px;
-  }
+.section-card h3 {
+  margin: 0 0 16px;
+  font-size: 16px;
+}
 
-  .upload-buttons {
-    display: flex;
-    gap: 12px;
-    margin: 16px 0;
-  }
+.section-heading {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 10px;
+  color: #909399;
+}
+.section-heading h3 {
+  margin: 0;
+  color: #303133;
+}
+.inspection-item-row {
+  display: flex;
+  align-items: flex-start;
+  justify-content: space-between;
+  gap: 20px;
+  padding: 14px 8px;
+  border-top: 1px solid #ebeef5;
+}
+.inspection-item-copy {
+  min-width: 0;
+  flex: 1;
+}
+.inspection-item-title {
+  font-weight: 600;
+  color: #303133;
+}
+.inspection-item-content {
+  margin: 6px 0 0 20px;
+  color: #606266;
+  white-space: pre-wrap;
+  overflow-wrap: anywhere;
+}
 
-  .upload-progress {
-    margin-bottom: 16px;
-  }
+.upload-buttons {
+  display: flex;
+  gap: 12px;
+  margin: 16px 0;
+}
 
-  .file-list {
-    display: grid;
-    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
-    gap: 12px;
-  }
+.upload-progress {
+  margin-bottom: 16px;
+}
 
-  .file-preview-container {
-    position: relative;
-    aspect-ratio: 1;
-    border-radius: 8px;
-    overflow: hidden;
-    background: #f2f3f5;
-  }
+.file-list {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
+  gap: 12px;
+}
 
-  .file-preview {
-    width: 100%;
-    height: 100%;
-  }
+.file-preview-container {
+  position: relative;
+  aspect-ratio: 1;
+  border-radius: 8px;
+  overflow: hidden;
+  background: #f2f3f5;
+}
 
-  .video-preview {
-    width: 100%;
-    height: 100%;
-    background: #303133;
-    color: #fff;
-    display: flex;
-    gap: 6px;
-    align-items: center;
-    justify-content: center;
-    cursor: pointer;
-  }
+.file-preview {
+  width: 100%;
+  height: 100%;
+}
 
-  .delete-btn {
-    position: absolute;
-    top: 6px;
-    right: 6px;
-  }
+.video-preview {
+  width: 100%;
+  height: 100%;
+  background: #303133;
+  color: #fff;
+  display: flex;
+  gap: 6px;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+}
 
-  .file-info {
-    margin-top: 6px;
-    font-size: 12px;
-  }
+.delete-btn {
+  position: absolute;
+  top: 6px;
+  right: 6px;
+}
 
-  .file-name {
-    color: #606266;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-  }
+.file-info {
+  margin-top: 6px;
+  font-size: 12px;
+}
 
-  .file-size {
-    color: #909399;
-    margin-top: 2px;
-  }
+.file-name {
+  color: #606266;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
 
-  .upload-summary {
-    margin-top: 16px;
-  }
+.file-size {
+  color: #909399;
+  margin-top: 2px;
+}
 
-  .footer-buttons {
-    position: sticky;
-    left: 0;
-    right: 0;
-    bottom: 0;
-    padding: 14px 20px 0;
-    background: #f5f7fa;
-    display: flex;
-    justify-content: center;
-    gap: 12px;
-  }
+.upload-summary {
+  margin-top: 16px;
+}
 
-  .video-player {
-    width: 100%;
-    max-height: 70vh;
-    background: #000;
-  }
+.footer-buttons {
+  position: sticky;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  padding: 14px 20px 0;
+  background: #f5f7fa;
+  display: flex;
+  justify-content: center;
+  gap: 12px;
+}
+
+.video-player {
+  width: 100%;
+  max-height: 70vh;
+  background: #000;
+}
 </style>

--
Gitblit v1.9.3