From 4a407279f0c9757f0714eaf385fdd5cd68c038c2 Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期四, 21 五月 2026 16:55:43 +0800
Subject: [PATCH] Merge branch 'dev_NEW_pro' of http://114.132.189.42:9002/r/product-inventory-management into dev_NEW_pro

---
 src/views/index.vue                                                           |   54 +
 src/views/productionManagement/processRoute/index.vue                         |    4 
 src/store/modules/user.js                                                     |  133 ++--
 src/components/AIChatSidebar/assistants/purchaseAssistant.js                  |    2 
 src/api/viewIndex.js                                                          |   21 
 src/views/productionManagement/processRoute/processRouteItem/index.vue        |  131 ++++
 src/views/productionManagement/productionOrder/index.vue                      |    1 
 src/components/AIChatSidebar/index.vue                                        |  171 ++++-
 src/views/equipmentManagement/inspectionManagement/index.vue                  |   27 
 src/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue |  680 ++++++++++++++++++++++++++
 src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue   |  335 +++++++-----
 11 files changed, 1,289 insertions(+), 270 deletions(-)

diff --git a/src/api/viewIndex.js b/src/api/viewIndex.js
index 9cae219..dafe6e8 100644
--- a/src/api/viewIndex.js
+++ b/src/api/viewIndex.js
@@ -347,18 +347,30 @@
   });
 };
 
+const HOME_PROGRESS_STATUS_LIST = ["all", "waiting", "inProgress", "completed", "paused", "1", "2", "3", "4"];
+const HOME_PROGRESS_TAB_LIST = ["all", "inProgress", "completed", "paused"];
+const HOME_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
+
+const normalizeDateParam = (value) => {
+  const dateText = typeof value === "string" ? value.trim() : "";
+  return HOME_DATE_PATTERN.test(dateText) ? dateText : undefined;
+};
+
 export const productionOrderProgress = (params = {}) => {
   const safePageNum = Math.max(1, Number(params.pageNum || 1));
   const safePageSize = Math.min(50, Math.max(1, Number(params.pageSize || 10)));
-  const safeTab = ["all", "inProgress", "completed", "paused"].includes(params.tab)
-    ? params.tab
-    : "all";
+  const rawStatus = String(params.status ?? "").trim();
+  const safeStatus = HOME_PROGRESS_STATUS_LIST.includes(rawStatus) ? rawStatus : undefined;
+  const safeTab = HOME_PROGRESS_TAB_LIST.includes(params.tab) ? params.tab : "all";
+  const normalizedTab = safeStatus && HOME_PROGRESS_TAB_LIST.includes(safeStatus) ? safeStatus : safeTab;
   return request({
     url: "/home/productionOrderProgress",
     method: "get",
     params: {
       ...params,
-      tab: safeTab,
+      status: safeStatus,
+      tab: normalizedTab,
+      bizDate: normalizeDateParam(params.bizDate),
       pageNum: safePageNum,
       pageSize: safePageSize,
     },
@@ -376,6 +388,7 @@
     params: {
       ...params,
       limit: safeLimit,
+      planDate: normalizeDateParam(params.planDate),
     },
     headers: {
       handleAuthError: false,
diff --git a/src/components/AIChatSidebar/assistants/purchaseAssistant.js b/src/components/AIChatSidebar/assistants/purchaseAssistant.js
index 8f0ae0a..4f2e72c 100644
--- a/src/components/AIChatSidebar/assistants/purchaseAssistant.js
+++ b/src/components/AIChatSidebar/assistants/purchaseAssistant.js
@@ -19,7 +19,7 @@
     '鏈湀閲囪喘閲戦鎺掑悕鍓嶅崄鐨勭墿鏂欐湁鍝簺锛�',
     '鍝簺閲囪喘璁㈠崟杩樻湭鍏ュ簱锛�',
     '鏈�杩�7澶╀緵搴斿晢鍒拌揣寮傚父鏈夊摢浜涳紵',
-    '甯垜缁熻寰呬粯娆鹃噰璐崟',
+    '甯垜缁熻寰呬粯娆鹃噰璐崟锛�',
     '鍒楀嚭鏈湀閲囪喘閫�璐ф儏鍐�'
   ]
 }
diff --git a/src/components/AIChatSidebar/index.vue b/src/components/AIChatSidebar/index.vue
index 9836854..c9db563 100644
--- a/src/components/AIChatSidebar/index.vue
+++ b/src/components/AIChatSidebar/index.vue
@@ -489,6 +489,56 @@
                   </div>
                 </div>
 
+                <div v-if="message.purchaseData" class="sales-structured-card">
+                  <div class="sales-structured-card__title">{{ getPurchaseTypeLabel(message.type) }}</div>
+
+                  <div v-if="message.purchaseData.summaryEntries?.length" class="sales-summary-grid">
+                    <div
+                        v-for="(entry, entryIndex) in message.purchaseData.summaryEntries"
+                        :key="`purchase-summary-${entry.key}-${entryIndex}`"
+                        class="sales-summary-item"
+                    >
+                      <span class="sales-summary-label">{{ entry.label }}</span>
+                      <strong class="sales-summary-value">{{ entry.value }}</strong>
+                    </div>
+                  </div>
+
+                  <div
+                      v-if="message.purchaseData.listItems?.length && message.purchaseData.columns?.length"
+                      class="table-wrapper manufacturing-table-wrapper"
+                  >
+                    <el-table :data="message.purchaseData.listItems" border stripe size="small" style="width: 100%">
+                      <el-table-column
+                          v-for="col in message.purchaseData.columns"
+                          :key="col"
+                          :label="getStructuredFieldLabel(col)"
+                          min-width="140"
+                          show-overflow-tooltip
+                      >
+                        <template #default="{ row }">
+                          {{ formatStructuredValue(row[col]) }}
+                        </template>
+                      </el-table-column>
+                    </el-table>
+                  </div>
+                </div>
+
+                <div v-if="message.purchaseIntentData?.quickPrompts?.length" class="purchase-intent-quick-prompt-wrap">
+                  <div class="purchase-intent-quick-prompt-title">璇曡瘯浠ヤ笅鎻愰棶</div>
+                  <div class="quick-prompt-list purchase-intent-quick-prompt-list">
+                    <button
+                        v-for="prompt in message.purchaseIntentData.quickPrompts"
+                        :key="`purchase-intent-${prompt}`"
+                        type="button"
+                        class="quick-prompt-btn"
+                        :disabled="isSending"
+                        @click="sendQuickPrompt(prompt)"
+                    >
+                      {{ prompt }}
+                    </button>
+                  </div>
+                </div>
+
                 <div v-if="message.purchaseAnalysisData" class="purchase-confirm-card">
                   <div class="purchase-confirm-header">
                     <span>{{ businessTypeLabelMap[message.purchaseAnalysisData.businessType] || message.purchaseAnalysisData.businessType || '閲囪喘涓氬姟' }}</span>
@@ -906,6 +956,10 @@
   sales_customer_churn_risk: '瀹㈡埛娴佸け椋庨櫓鍒嗘瀽',
   sales_collection_quote_strategy: '鍥炴涓庢姤浠风瓥鐣ュ缓璁�'
 }
+const purchaseTypeLabelMap = {
+  purchase_material_rank: '閲囪喘鐗╂枡閲戦鎺掕',
+  purchase_pending_payment_list: '寰呬粯娆鹃噰璐崟'
+}
 const manufacturingStructuredTypeSet = new Set([
   'manufacturing_site_snapshot',
   'manufacturing_plan_list',
@@ -991,7 +1045,11 @@
   contractAmountTotal: '鍚堝悓鎬婚',
   receivedAmountTotal: '宸插洖娆鹃噾棰�',
   pendingAmountTotal: '寰呭洖娆炬�婚',
-  shipRate: '鍙戣揣鐜�'
+  shipRate: '鍙戣揣鐜�',
+  pendingOrderCount: '寰呬粯娆捐鍗曟暟',
+  totalContractAmount: '寰呬粯娆惧悎鍚屾�婚',
+  totalPaidAmount: '宸蹭粯娆炬�婚',
+  totalPendingAmount: '寰呬粯娆炬�婚'
 })
 const purchasePayloadFieldLabelMap = {
   purchaseLedgers: '閲囪喘鍙拌处',
@@ -1415,6 +1473,25 @@
 
 const getSalesTypeLabel = (type = '') => salesTypeLabelMap[String(type || '')] || '閿�鍞煡璇㈢粨鏋�'
 
+const buildPurchaseStructuredData = (parsedData) => {
+  if (parsedData?.success !== true) return null
+
+  const type = String(parsedData?.type || '')
+  if (!type.startsWith('purchase_')) return null
+
+  const rawData = isPlainObject(parsedData?.data) ? parsedData.data : {}
+  const listItems = normalizeSalesListItems(rawData.items)
+
+  return {
+    type,
+    summaryEntries: normalizeManufacturingSummaryEntries(parsedData?.summary),
+    listItems,
+    columns: inferSalesColumns(listItems)
+  }
+}
+
+const getPurchaseTypeLabel = (type = '') => purchaseTypeLabelMap[String(type || '')] || '閲囪喘鏌ヨ缁撴灉'
+
 const isSalesFocusType = (type = '') => salesFocusTypeSet.has(String(type || ''))
 
 const getSalesLevelTagType = (level = '') => {
@@ -1446,6 +1523,13 @@
       .filter(Boolean)
   }
   return []
+}
+
+const normalizePurchaseIntentNotRecognizedData = (parsedData) => {
+  if (String(parsedData?.type || '') !== 'purchase_intent_not_recognized') return null
+  const quickPrompts = toStructuredStringArray(parsedData?.data?.quickPrompts)
+  if (!quickPrompts.length) return null
+  return { quickPrompts }
 }
 
 const getManufacturingWarningLevelType = (level = '') => {
@@ -1827,6 +1911,8 @@
           purchaseAnalysisData: null,
           manufacturingData: null,
           salesData: null,
+          purchaseData: null,
+          purchaseIntentData: null,
           localUploadFiles: isUser ? mapHistoryFilePathsToSnapshots(msg.filePaths, uuid.value, idx) : []
         }
 
@@ -2045,7 +2131,7 @@
           const candidate = text.slice(i, j + 1)
           try {
             const parsed = JSON.parse(candidate)
-            if (parsed?.success === true) {
+            if (typeof parsed?.success === 'boolean') {
               return {
                 data: parsed,
                 startIdx: i,
@@ -2064,7 +2150,8 @@
 }
 
 const applyStructuredMessageData = (messageObj, parsedData, msgIndex, shouldRenderCharts = true) => {
-  if (!messageObj || !parsedData?.success) return
+  const isPurchaseIntentNotRecognized = String(parsedData?.type || '') === 'purchase_intent_not_recognized'
+  if (!messageObj || (parsedData?.success !== true && !isPurchaseIntentNotRecognized)) return
 
   const previousManufacturingData = messageObj.manufacturingData
   messageObj.type = parsedData.type || ''
@@ -2072,6 +2159,15 @@
   messageObj.purchaseAnalysisData = null
   messageObj.manufacturingData = null
   messageObj.salesData = null
+  messageObj.purchaseData = null
+  messageObj.purchaseIntentData = null
+
+  if (isPurchaseIntentNotRecognized) {
+    messageObj.purchaseIntentData = normalizePurchaseIntentNotRecognizedData(parsedData)
+    messageObj.chartOptions = null
+    messageObj.chartRenderReady = false
+    return
+  }
 
   if (messageObj.type === 'todo_list' && parsedData.data) {
     messageObj.tableData = parsedData.data
@@ -2087,11 +2183,17 @@
     messageObj.manufacturingData = manufacturingData
   }
 
+  const purchaseData = buildPurchaseStructuredData(parsedData)
+  if (purchaseData) {
+    messageObj.purchaseData = purchaseData
+  }
+
   if (parsedData.action === 'confirm_required' && parsedData.businessType) {
     messageObj.type = 'purchase_analysis_confirm'
     messageObj.purchaseAnalysisData = parsedData
     messageObj.manufacturingData = null
     messageObj.salesData = null
+    messageObj.purchaseData = null
     if (!Array.isArray(messageObj.payloadTreeData) || !messageObj.payloadTreeData.length) {
       initializePurchasePayloadTree(messageObj, parsedData.payload || {})
     }
@@ -2136,6 +2238,7 @@
 const getStructuredFallbackText = (parsedData) => {
   if (!parsedData) return '姝e湪涓烘偍灞曠ず鍒嗘瀽缁撴灉...'
   if (parsedData.type === 'todo_list') return '宸蹭负鎮ㄦ暣鐞嗗ソ鐩稿叧鏁版嵁銆�'
+  if (parsedData.type === 'purchase_intent_not_recognized') return '鏈瘑鍒埌鍙墽琛岀殑閲囪喘鏌ヨ鏉′欢锛岃琛ュ厖鏌ヨ鏉′欢鍚庡啀璇曘��'
   if (salesStructuredTypeSet.has(parsedData.type)) {
     if (parsedData.type === 'sales_customer_churn_risk') return '宸蹭负鎮ㄧ敓鎴愬鎴锋祦澶遍闄╁垎鏋愩��'
     if (parsedData.type === 'sales_collection_quote_strategy') return '宸蹭负鎮ㄧ敓鎴愬洖娆句笌鎶ヤ环绛栫暐寤鸿銆�'
@@ -2148,6 +2251,7 @@
     if (parsedData.type === 'manufacturing_analysis') return '宸蹭负鎮ㄧ敓鎴愬埗閫犲垎鏋愮粨鏋溿��'
     return '宸茶繑鍥炲埗閫犳煡璇㈢粨鏋溿��'
   }
+  if (String(parsedData.type || '').startsWith('purchase_')) return '宸茶繑鍥為噰璐煡璇㈢粨鏋溿��'
   if (parsedData.charts && Object.keys(parsedData.charts).length > 0) return '宸蹭负鎮ㄧ敓鎴愬垎鏋愬浘琛ㄣ��'
   return '姝e湪涓烘偍灞曠ず鍒嗘瀽缁撴灉...'
 }
@@ -3278,7 +3382,9 @@
     payloadHiddenData: null,
     purchaseAnalysisData: null,
     manufacturingData: null,
-    salesData: null
+    salesData: null,
+    purchaseData: null,
+    purchaseIntentData: null
   })
 
   outputState.value[botMsgIndex] = {
@@ -3404,7 +3510,9 @@
     payloadHiddenData: null,
     purchaseAnalysisData: null,
     manufacturingData: null,
-    salesData: null
+    salesData: null,
+    purchaseData: null,
+    purchaseIntentData: null
   }
   messages.value.push(botMsg)
 
@@ -3436,28 +3544,6 @@
           const extracted = extractEmbeddedSuccessJson(fullText)
           if (extracted) {
             applyStructuredMessageData(currentMsg, extracted.data, botMsgIndex)
-          } else {
-            const extractJson = (text) => {
-              const startIdx = text.indexOf('{"success": true')
-              if (startIdx === -1) return null
-
-              // 浠庡悗寰�鍓嶆壘鏈�鍚庝竴涓� '}'
-              const lastBraceIdx = text.lastIndexOf('}')
-              if (lastBraceIdx === -1 || lastBraceIdx < startIdx) return null
-
-              const potentialJson = text.substring(startIdx, lastBraceIdx + 1)
-              try {
-                return JSON.parse(potentialJson)
-              } catch (err) {
-                return null
-              }
-            }
-
-            const parsedData = extractJson(fullText)
-            if (parsedData) {
-              applyStructuredMessageData(currentMsg, parsedData, botMsgIndex, true)
-            }
-
           }
 
           updateOutputState(fullText, botMsgIndex)
@@ -3475,24 +3561,6 @@
     const extracted = extractEmbeddedSuccessJson(currentMsg.content)
     if (extracted) {
       applyStructuredMessageData(currentMsg, extracted.data, botMsgIndex)
-    } else {
-      const extractJson = (text) => {
-        const startIdx = text.indexOf('{"success": true')
-        if (startIdx === -1) return null
-        const lastBraceIdx = text.lastIndexOf('}')
-        if (lastBraceIdx === -1 || lastBraceIdx < startIdx) return null
-        const potentialJson = text.substring(startIdx, lastBraceIdx + 1)
-        try {
-          return JSON.parse(potentialJson)
-        } catch (err) {
-          return null
-        }
-      }
-
-      const finalParsed = extractJson(currentMsg.content)
-      if (finalParsed) {
-        applyStructuredMessageData(currentMsg, finalParsed, botMsgIndex)
-      }
     }
   }).catch(err => {
     if (err.name === 'CanceledError' || err.name === 'AbortError') {
@@ -4889,6 +4957,19 @@
   color: $deep-blue;
 }
 
+.purchase-intent-quick-prompt-wrap {
+  margin-top: 12px;
+}
+
+.purchase-intent-quick-prompt-title {
+  font-size: 12px;
+  color: #4b5563;
+}
+
+.purchase-intent-quick-prompt-list {
+  margin-top: 8px;
+}
+
 .purchase-confirm-card {
   margin-top: 12px;
   width: 100%;
diff --git a/src/store/modules/user.js b/src/store/modules/user.js
index dd31b26..b7714f6 100644
--- a/src/store/modules/user.js
+++ b/src/store/modules/user.js
@@ -1,12 +1,12 @@
-import {login, logout, getInfo, loginCheck, loginCheckFactory,tideLogin} from '@/api/login'
-import { getToken, setToken, removeToken } from '@/utils/auth'
-import { isHttp, isEmpty } from "@/utils/validate"
-import defAva from '@/assets/images/profile.jpg'
-import { defineStore } from 'pinia'
-
-const useUserStore = defineStore(
-  'user',
-  {
+import {login, logout, getInfo, loginCheck, loginCheckFactory,tideLogin} from '@/api/login'
+import { getToken, setToken, removeToken } from '@/utils/auth'
+import { isHttp, isEmpty } from "@/utils/validate"
+import defAva from '@/assets/images/profile.jpg'
+import { defineStore } from 'pinia'
+
+const useUserStore = defineStore(
+  'user',
+  {
     state: () => ({
       token: getToken(),
       id: '',
@@ -15,9 +15,9 @@
       roles: [],
       permissions: [],
       aiEnabled: 0
-    }),
-    actions: {
-      // 鐧诲綍
+    }),
+    actions: {
+      // 鐧诲綍
       login(userInfo) {
         const username = userInfo.username.trim()
         const password = userInfo.password
@@ -37,33 +37,34 @@
             reject(error)
           })
         })
-      },
-      getCurrentTime() {
-        const now = new Date();
-        const year = now.getFullYear();       // 鑾峰彇骞翠唤
-        const month = String(now.getMonth() + 1).padStart(2, '0');  // 鏈堜唤浠�0寮�濮嬶紝瑕�+1锛屽苟琛ラ浂
-        const day = String(now.getDate()).padStart(2, '0');         // 鏃ユ湡琛ラ浂
-        const hours = String(now.getHours()).padStart(2, '0');      // 灏忔椂琛ラ浂
-        const minutes = String(now.getMinutes()).padStart(2, '0');  // 鍒嗛挓琛ラ浂
-        const seconds = String(now.getSeconds()).padStart(2, '0');  // 绉掓暟琛ラ浂
-        return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
-      },
-      // 鑾峰彇鐢ㄦ埛淇℃伅
-      getInfo() {
-        return new Promise((resolve, reject) => {
-          getInfo().then(res => {
+      },
+      getCurrentTime() {
+        const now = new Date();
+        const year = now.getFullYear();       // 鑾峰彇骞翠唤
+        const month = String(now.getMonth() + 1).padStart(2, '0');  // 鏈堜唤浠�0寮�濮嬶紝瑕�+1锛屽苟琛ラ浂
+        const day = String(now.getDate()).padStart(2, '0');         // 鏃ユ湡琛ラ浂
+        const hours = String(now.getHours()).padStart(2, '0');      // 灏忔椂琛ラ浂
+        const minutes = String(now.getMinutes()).padStart(2, '0');  // 鍒嗛挓琛ラ浂
+        const seconds = String(now.getSeconds()).padStart(2, '0');  // 绉掓暟琛ラ浂
+        return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+      },
+      // 鑾峰彇鐢ㄦ埛淇℃伅
+      getInfo() {
+        return new Promise((resolve, reject) => {
+          getInfo().then(res => {
+            res  = res.data
             const user = res.user || {}
-            let avatar = user.avatar || ""
-            avatar = import.meta.env.VITE_APP_BASE_API + '/profile/' + avatar
-            if (res.roles && res.roles.length > 0) { // 楠岃瘉杩斿洖鐨剅oles鏄惁鏄竴涓潪绌烘暟缁�
-              this.roles = res.roles
-              this.permissions = res.permissions
-            } else {
-              this.roles = ['ROLE_DEFAULT']
-            }
+            let avatar = user.avatar || ""
+            avatar = import.meta.env.VITE_APP_BASE_API + '/profile/' + avatar
+            if (res.roles && res.roles.length > 0) { // 楠岃瘉杩斿洖鐨剅oles鏄惁鏄竴涓潪绌烘暟缁�
+              this.roles = res.roles
+              this.permissions = res.permissions
+            } else {
+              this.roles = ['ROLE_DEFAULT']
+            }
             this.id = user.userId || ''
             this.name = user.userName || ''
-            this.avatar = avatar
+            this.avatar = avatar
             this.currentFactoryName = user.currentFactoryName || ''
             this.nickName = user.nickName || ''
             this.roleName = Array.isArray(user.roles) && user.roles.length > 0 ? (user.roles[0].roleName || '') : ''
@@ -75,11 +76,11 @@
             reject(error)
           })
         })
-      },
-      // 閫�鍑虹郴缁�
-      logOut() {
-        return new Promise((resolve, reject) => {
-          logout(this.token).then(() => {
+      },
+      // 閫�鍑虹郴缁�
+      logOut() {
+        return new Promise((resolve, reject) => {
+          logout(this.token).then(() => {
             this.token = ''
             this.roles = []
             this.permissions = []
@@ -89,21 +90,21 @@
           }).catch(error => {
             reject(error)
           })
-        })
-      },
-      // 鐧诲綍鏍¢獙
-      loginCheck(userInfo) {
-        const username = userInfo.username.trim()
-        const password = userInfo.password
-        return new Promise((resolve, reject) => {
-          loginCheck(username, password).then(res => {
-            resolve(res)
-          }).catch(error => {
-            reject(error)
-          })
-        })
-      },
-      // 閮ㄩ棬鐧诲綍
+        })
+      },
+      // 鐧诲綍鏍¢獙
+      loginCheck(userInfo) {
+        const username = userInfo.username.trim()
+        const password = userInfo.password
+        return new Promise((resolve, reject) => {
+          loginCheck(username, password).then(res => {
+            resolve(res)
+          }).catch(error => {
+            reject(error)
+          })
+        })
+      },
+      // 閮ㄩ棬鐧诲綍
       loginCheckFactory(userInfo) {
         const username = userInfo.username.trim()
         const password = userInfo.password
@@ -121,7 +122,7 @@
             reject(error)
           })
         })
-      },
+      },
       TideLogin(code) {
         return new Promise((resolve, reject) => {
           tideLogin(code)
@@ -138,12 +139,12 @@
                 };
                 resolve();
               })
-              .catch((error) => {
-                reject(error);
-              });
-        });
-      },
-    }
-  })
-
-export default useUserStore
+              .catch((error) => {
+                reject(error);
+              });
+        });
+      },
+    }
+  })
+
+export default useUserStore
diff --git a/src/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue b/src/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue
new file mode 100644
index 0000000..36256c8
--- /dev/null
+++ b/src/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue
@@ -0,0 +1,680 @@
+<template>
+  <FormDialog
+    v-model="dialogVisible"
+    title="涓婁紶宸℃璁板綍"
+    width="980px"
+    @close="handleClose"
+    @cancel="handleClose"
+  >
+    <main class="upload-content">
+      <el-card v-if="taskInfo" class="section-card">
+        <el-descriptions :column="1" border>
+          <el-descriptions-item label="宸℃浠诲姟鍚嶇О">
+            {{ taskInfo.taskName || "-" }}
+          </el-descriptions-item>
+          <el-descriptions-item label="宸℃椤圭洰">
+            {{ taskInfo.inspectionProject || "-" }}
+          </el-descriptions-item>
+          <el-descriptions-item label="澶囨敞">
+            {{ taskInfo.remarks || "-" }}
+          </el-descriptions-item>
+        </el-descriptions>
+      </el-card>
+
+      <el-card class="section-card">
+        <h3>宸℃鐘舵��</h3>
+        <el-radio-group v-model="hasException">
+          <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">
+        <h3>寮傚父鎻忚堪</h3>
+        <el-input
+          v-model="abnormalDescription"
+          type="textarea"
+          maxlength="500"
+          show-word-limit
+          :rows="4"
+          placeholder="璇锋弿杩板紓甯告儏鍐�..."
+        />
+      </el-card>
+
+      <el-card v-if="hasException === true" class="section-card">
+        <el-tabs v-model="currentUploadType">
+          <el-tab-pane label="鐢熶骇鍓�" name="before" />
+          <el-tab-pane label="鐢熶骇涓�" name="after" />
+          <el-tab-pane label="鐢熶骇鍚�" name="issue" />
+        </el-tabs>
+
+        <div class="upload-buttons">
+          <el-upload
+            :show-file-list="false"
+            :http-request="uploadFile"
+            :disabled="getCurrentFiles().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="getCurrentFiles().length >= uploadConfig.limit || uploading"
+            accept="video/*"
+          >
+            <el-button type="success" :loading="uploading">
+              <el-icon><VideoCamera /></el-icon>
+              閫夋嫨瑙嗛
+            </el-button>
+          </el-upload>
+        </div>
+
+        <el-progress
+          v-if="uploading"
+          :percentage="uploadProgress"
+          class="upload-progress"
+        />
+
+        <div v-if="getCurrentFiles().length" class="file-list">
+          <div
+            v-for="(file, index) in getCurrentFiles()"
+            :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-icon><VideoCamera /></el-icon>
+                <span>瑙嗛</span>
+              </div>
+
+              <el-button
+                class="delete-btn"
+                type="danger"
+                circle
+                size="small"
+                @click="removeFile(index)"
+              >
+                <el-icon><Close /></el-icon>
+              </el-button>
+            </div>
+
+            <div class="file-info">
+              <div class="file-name">
+                {{ file.bucketFilename || file.name || (file.type === "image" ? "鍥剧墖" : "瑙嗛") }}
+              </div>
+              <div class="file-size">{{ formatFileSize(file.size) }}</div>
+            </div>
+          </div>
+        </div>
+
+        <el-empty
+          v-else
+          :description="`璇烽�夋嫨瑕佷笂浼犵殑${getUploadTypeText()}鍥剧墖鎴栬棰慲"
+        />
+
+        <el-alert
+          class="upload-summary"
+          type="info"
+          :closable="false"
+          :title="`鐢熶骇鍓嶏細${beforeModelValue.length}涓枃浠� | 鐢熶骇涓細${afterModelValue.length}涓枃浠� | 鐢熶骇鍚庯細${issueModelValue.length}涓枃浠禶"
+        />
+      </el-card>
+
+      <el-result
+        v-if="hasException === false"
+        icon="success"
+        title="璁惧杩愯姝e父"
+        sub-title="鏃犻渶涓婁紶鐓х墖"
+      />
+    </main>
+
+    <template #footer>
+      <footer class="footer-buttons">
+        <el-button type="primary" @click="submitUpload">鎻愪氦</el-button>
+        <el-button v-if="hasException === true" type="warning" @click="goToRepair">
+          鏂板鎶ヤ慨
+        </el-button>
+        <el-button @click="handleClose">鍙栨秷</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>
+</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 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 dialogVisible = ref(false);
+const taskInfo = ref(null);
+const uploading = ref(false);
+const uploadProgress = ref(0);
+
+const beforeModelValue = ref([]);
+const afterModelValue = ref([]);
+const issueModelValue = ref([]);
+
+const currentUploadType = ref("before");
+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}`;
+    }
+  }
+
+  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 = [];
+  afterModelValue.value = [];
+  issueModelValue.value = [];
+  currentUploadType.value = "before";
+  hasException.value = null;
+  abnormalDescription.value = "";
+  uploading.value = false;
+  uploadProgress.value = 0;
+  showVideoDialog.value = false;
+  currentVideoFile.value = null;
+};
+
+const openDialog = row => {
+  const raw = JSON.parse(JSON.stringify(row?.__raw || row || {}));
+  taskInfo.value = raw;
+
+  beforeModelValue.value = normalizeList(
+    raw.commonFileListBeforeVO || raw.commonFileListBefore || [],
+    "image"
+  );
+  afterModelValue.value = normalizeList(
+    raw.commonFileListVO || raw.commonFileList || [],
+    "image"
+  );
+  issueModelValue.value = normalizeList(
+    raw.commonFileListAfterVO || raw.commonFileListAfter || [],
+    "image"
+  );
+
+  abnormalDescription.value = raw.abnormalDescription || "";
+
+  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;
+  }
+
+  if (
+    hasException.value !== true &&
+    (beforeModelValue.value.length || afterModelValue.value.length || issueModelValue.value.length)
+  ) {
+    hasException.value = true;
+  }
+
+  dialogVisible.value = true;
+};
+
+const handleClose = () => {
+  dialogVisible.value = false;
+  resetState();
+  emit("closeDia");
+};
+
+const getCurrentFiles = () => {
+  if (currentUploadType.value === "before") return beforeModelValue.value;
+  if (currentUploadType.value === "after") return afterModelValue.value;
+  if (currentUploadType.value === "issue") return issueModelValue.value;
+  return [];
+};
+
+const getUploadTypeText = () => {
+  if (currentUploadType.value === "before") return "鐢熶骇鍓�";
+  if (currentUploadType.value === "after") return "鐢熶骇涓�";
+  if (currentUploadType.value === "issue") return "鐢熶骇鍚�";
+  return "";
+};
+
+const getTabType = () => {
+  if (currentUploadType.value === "before") return 10;
+  if (currentUploadType.value === "after") return 11;
+  if (currentUploadType.value === "issue") return 12;
+  return 10;
+};
+
+const previewVideo = file => {
+  currentVideoFile.value = file;
+  showVideoDialog.value = true;
+};
+
+const uploadFile = async uploadRequest => {
+  const rawFile = uploadRequest.file;
+
+  if (getCurrentFiles().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", getTabType());
+
+  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()}`,
+    };
+
+    getCurrentFiles().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 submitUpload = async () => {
+  if (hasException.value === null) {
+    ElMessage.warning("璇烽�夋嫨宸℃鐘舵��");
+    return;
+  }
+
+  if (hasException.value === true) {
+    const totalFiles =
+      beforeModelValue.value.length +
+      afterModelValue.value.length +
+      issueModelValue.value.length;
+
+    if (!totalFiles) {
+      ElMessage.warning("璇蜂笂浼犲紓甯哥収鐗囨垨瑙嗛");
+      return;
+    }
+
+    if (!abnormalDescription.value.trim()) {
+      ElMessage.warning("璇峰~鍐欏紓甯告弿杩�");
+      return;
+    }
+  }
+
+  const loading = ElLoading.service({
+    text: "鎻愪氦涓�...",
+    background: "rgba(0, 0, 0, 0.3)",
+  });
+
+  try {
+    const allFiles = [
+      ...beforeModelValue.value,
+      ...afterModelValue.value,
+      ...issueModelValue.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: afterModelValue.value.map(buildFileItem),
+      commonFileListAfterDTO: issueModelValue.value.map(buildFileItem),
+      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",
+    });
+    getCurrentFiles().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: afterModelValue.value.map(buildFileItem),
+    commonFileListAfterDTO: issueModelValue.value.map(buildFileItem),
+    uploadedFiles: {
+      before: beforeModelValue.value,
+      after: afterModelValue.value,
+      issue: issueModelValue.value,
+    },
+  };
+
+  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;
+}
+
+.upload-content {
+  max-width: 960px;
+  margin: 20px auto 0;
+}
+
+.section-card {
+  margin-bottom: 16px;
+}
+
+.section-card h3 {
+  margin: 0 0 16px;
+  font-size: 16px;
+}
+
+.upload-buttons {
+  display: flex;
+  gap: 12px;
+  margin: 16px 0;
+}
+
+.upload-progress {
+  margin-bottom: 16px;
+}
+
+.file-list {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
+  gap: 12px;
+}
+
+.file-preview-container {
+  position: relative;
+  aspect-ratio: 1;
+  border-radius: 8px;
+  overflow: hidden;
+  background: #f2f3f5;
+}
+
+.file-preview {
+  width: 100%;
+  height: 100%;
+}
+
+.video-preview {
+  width: 100%;
+  height: 100%;
+  background: #303133;
+  color: #fff;
+  display: flex;
+  gap: 6px;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+}
+
+.delete-btn {
+  position: absolute;
+  top: 6px;
+  right: 6px;
+}
+
+.file-info {
+  margin-top: 6px;
+  font-size: 12px;
+}
+
+.file-name {
+  color: #606266;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.file-size {
+  color: #909399;
+  margin-top: 2px;
+}
+
+.upload-summary {
+  margin-top: 16px;
+}
+
+.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>
diff --git a/src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue b/src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue
index 66867e3..b5604fe 100644
--- a/src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue
+++ b/src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue
@@ -1,210 +1,226 @@
 <template>
   <div>
-    <el-dialog title="鏌ョ湅闄勪欢"
-               v-model="dialogVisitable" width="800px" @close="cancel">
+    <el-dialog title="鏌ョ湅闄勪欢" v-model="dialogVisitable" width="800px" @close="cancel">
       <div class="upload-container">
-        <!-- 鐢熶骇鍓� -->
         <div class="form-container">
           <div class="title">鐢熶骇鍓�</div>
-          
-          <!-- 鍥剧墖鍒楄〃 -->
-          <div style="display: flex; flex-wrap: wrap;">
-            <img v-for="(item, index) in beforeProductionImgs" :key="index"
-                 @click="showMedia(beforeProductionImgs, index, 'image')"
-                 :src="item" style="max-width: 100px; height: 100px; margin: 5px;" alt="">
+
+          <div class="media-list">
+            <img
+              v-for="(item, index) in beforeProductionImgs"
+              :key="`before-img-${index}`"
+              :src="item"
+              alt=""
+              class="media-image"
+              @click="showMedia(beforeProductionImgs, index, 'image')"
+            />
           </div>
-          
-          <!-- 瑙嗛鍒楄〃 -->
-          <div style="display: flex; flex-wrap: wrap;">
+
+          <div class="media-list">
             <div
-                v-for="(videoUrl, index) in beforeProductionVideos"
-                :key="index"
-                @click="showMedia(beforeProductionVideos, index, 'video')"
-                style="position: relative; margin: 10px; cursor: pointer;"
+              v-for="(videoUrl, index) in beforeProductionVideos"
+              :key="`before-video-${index}`"
+              class="video-item"
+              @click="showMedia(beforeProductionVideos, index, 'video')"
             >
-              <div style="width: 160px; height: 90px; background-color: #333; display: flex; align-items: center; justify-content: center;">
-                <img src="@/assets/images/video.png" alt="鎾斁" style="width: 30px; height: 30px; opacity: 0.8;" />
+              <div class="video-thumb">
+                <img src="@/assets/images/video.png" alt="鎾斁" class="video-icon" />
               </div>
-              <div style="text-align: center; font-size: 12px; color: #666;">鐐瑰嚮鎾斁</div>
+              <div class="video-text">鐐瑰嚮鎾斁</div>
             </div>
           </div>
         </div>
-        
-        <!-- 鐢熶骇鍚� -->
+
+        <div class="form-container">
+          <div class="title">鐢熶骇涓�</div>
+
+          <div class="media-list">
+            <img
+              v-for="(item, index) in afterProductionImgs"
+              :key="`during-img-${index}`"
+              :src="item"
+              alt=""
+              class="media-image"
+              @click="showMedia(afterProductionImgs, index, 'image')"
+            />
+          </div>
+
+          <div class="media-list">
+            <div
+              v-for="(videoUrl, index) in afterProductionVideos"
+              :key="`during-video-${index}`"
+              class="video-item"
+              @click="showMedia(afterProductionVideos, index, 'video')"
+            >
+              <div class="video-thumb">
+                <img src="@/assets/images/video.png" alt="鎾斁" class="video-icon" />
+              </div>
+              <div class="video-text">鐐瑰嚮鎾斁</div>
+            </div>
+          </div>
+        </div>
+
         <div class="form-container">
           <div class="title">鐢熶骇鍚�</div>
-          
-          <!-- 鍥剧墖鍒楄〃 -->
-          <div style="display: flex; flex-wrap: wrap;">
-            <img v-for="(item, index) in afterProductionImgs" :key="index"
-                 @click="showMedia(afterProductionImgs, index, 'image')"
-                 :src="item" style="max-width: 100px; height: 100px; margin: 5px;" alt="">
+
+          <div class="media-list">
+            <img
+              v-for="(item, index) in productionIssuesImgs"
+              :key="`after-img-${index}`"
+              :src="item"
+              alt=""
+              class="media-image"
+              @click="showMedia(productionIssuesImgs, index, 'image')"
+            />
           </div>
-          
-          <!-- 瑙嗛鍒楄〃 -->
-          <div style="display: flex; flex-wrap: wrap;">
+
+          <div class="media-list">
             <div
-                v-for="(videoUrl, index) in afterProductionVideos"
-                :key="index"
-                @click="showMedia(afterProductionVideos, index, 'video')"
-                style="position: relative; margin: 10px; cursor: pointer;"
+              v-for="(videoUrl, index) in productionIssuesVideos"
+              :key="`after-video-${index}`"
+              class="video-item"
+              @click="showMedia(productionIssuesVideos, index, 'video')"
             >
-              <div style="width: 160px; height: 90px; background-color: #333; display: flex; align-items: center; justify-content: center;">
-                <img src="@/assets/images/video.png" alt="鎾斁" style="width: 30px; height: 30px; opacity: 0.8;" />
+              <div class="video-thumb">
+                <img src="@/assets/images/video.png" alt="鎾斁" class="video-icon" />
               </div>
-              <div style="text-align: center; font-size: 12px; color: #666;">鐐瑰嚮鎾斁</div>
-            </div>
-          </div>
-        </div>
-        
-        <!-- 鐢熶骇闂 -->
-        <div class="form-container">
-          <div class="title">鐢熶骇闂</div>
-          
-          <!-- 鍥剧墖鍒楄〃 -->
-          <div style="display: flex; flex-wrap: wrap;">
-            <img v-for="(item, index) in productionIssuesImgs" :key="index"
-                 @click="showMedia(productionIssuesImgs, index, 'image')"
-                 :src="item" style="max-width: 100px; height: 100px; margin: 5px;" alt="">
-          </div>
-          
-          <!-- 瑙嗛鍒楄〃 -->
-          <div style="display: flex; flex-wrap: wrap;">
-            <div
-                v-for="(videoUrl, index) in productionIssuesVideos"
-                :key="index"
-                @click="showMedia(productionIssuesVideos, index, 'video')"
-                style="position: relative; margin: 10px; cursor: pointer;"
-            >
-              <div style="width: 160px; height: 90px; background-color: #333; display: flex; align-items: center; justify-content: center;">
-                <img src="@/assets/images/video.png" alt="鎾斁" style="width: 30px; height: 30px; opacity: 0.8;" />
-              </div>
-              <div style="text-align: center; font-size: 12px; color: #666;">鐐瑰嚮鎾斁</div>
+              <div class="video-text">鐐瑰嚮鎾斁</div>
             </div>
           </div>
         </div>
       </div>
     </el-dialog>
-    
-    <!-- 缁熶竴濯掍綋鏌ョ湅鍣� -->
+
     <div v-if="isMediaViewerVisible" class="media-viewer-overlay" @click.self="closeMediaViewer">
       <div class="media-viewer-content" @click.stop>
-        <!-- 鍥剧墖 -->
         <vue-easy-lightbox
-            v-if="mediaType === 'image'"
-            :visible="isMediaViewerVisible"
-            :imgs="mediaList"
-            :index="currentMediaIndex"
-            @hide="closeMediaViewer"
-        ></vue-easy-lightbox>
-        
-        <!-- 瑙嗛 -->
-        <div v-else-if="mediaType === 'video'" style="position: relative;">
-          <video
-              :src="mediaList[currentMediaIndex]"
-              autoplay
-              controls
-              style="max-width: 90vw; max-height: 80vh;"
-          />
+          v-if="mediaType === 'image'"
+          :visible="isMediaViewerVisible"
+          :imgs="mediaList"
+          :index="currentMediaIndex"
+          @hide="closeMediaViewer"
+        />
+
+        <div v-else-if="mediaType === 'video'" class="video-player-wrap">
+          <video :src="mediaList[currentMediaIndex]" autoplay controls class="video-player" />
         </div>
       </div>
     </div>
   </div>
 </template>
-<script setup>
-import { ref } from 'vue';
-import VueEasyLightbox from 'vue-easy-lightbox';
-const { proxy } = getCurrentInstance();
 
-// 鎺у埗寮圭獥鏄剧ず
+<script setup>
+import { ref } from "vue";
+import VueEasyLightbox from "vue-easy-lightbox";
+
 const dialogVisitable = ref(false);
 
-// 鍥剧墖鏁扮粍
 const beforeProductionImgs = ref([]);
 const afterProductionImgs = ref([]);
 const productionIssuesImgs = ref([]);
 
-// 瑙嗛鏁扮粍
 const beforeProductionVideos = ref([]);
 const afterProductionVideos = ref([]);
 const productionIssuesVideos = ref([]);
 
-// 濯掍綋鏌ョ湅鍣ㄧ姸鎬�
 const isMediaViewerVisible = ref(false);
 const currentMediaIndex = ref(0);
-const mediaList = ref([]); // 瀛樺偍褰撳墠瑕佹煡鐪嬬殑濯掍綋鍒楄〃锛堝惈鍥剧墖鍜岃棰戝璞★級
-const mediaType = ref('image'); // image | video
+const mediaList = ref([]);
+const mediaType = ref("image");
 
-// 澶勭悊姣忎竴绫绘暟鎹細鍒嗙鍥剧墖鍜岃棰�
-function processItems(items) {
+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}`;
+    }
+  }
+
+  if (currentUrl && !currentUrl.startsWith("http")) {
+    if (!currentUrl.startsWith("/")) {
+      currentUrl = `/${currentUrl}`;
+    }
+    currentUrl = __BASE_API__ + currentUrl;
+  }
+
+  return currentUrl;
+};
+
+const processItems = items => {
   const images = [];
   const videos = [];
-  
-  // 妫�鏌� items 鏄惁瀛樺湪涓斾负鏁扮粍
-  if (!items || !Array.isArray(items)) {
+
+  if (!Array.isArray(items)) {
     return { images, videos };
   }
-  
+
   items.forEach(item => {
-    if (!item || !item.previewURL || !item.contentType) return;
+    if (!item) return;
 
-    
-    // 澶勭悊鏂囦欢 URL
-    const fileUrl = item.previewURL;
-    const contentType = String(item.contentType).toLowerCase();
+    const fileUrl = processFileUrl(
+      item.previewURL || item.url || item.downloadUrl || item.path || ""
+    );
+    const contentType = String(item.contentType || "").toLowerCase();
 
-    // 鏍规嵁 contentType 鍒ゆ柇鏄浘鐗囪繕鏄棰�
-    if (contentType.startsWith('image/')) {
-      images.push(fileUrl);
-    } else if (contentType.startsWith('video/')) {
+    if (!fileUrl) return;
+
+    if (contentType.startsWith("video/")) {
       videos.push(fileUrl);
+      return;
     }
-  });
-  
-  return { images, videos };
-}
 
-// 鎵撳紑寮圭獥骞跺姞杞芥暟鎹�
-const openDialog = async (row) => {
-  // 浣跨敤姝g‘鐨勫瓧娈靛悕锛歝ommonFileListBefore, commonFileListAfter
-  const { images: beforeImgs, videos: beforeVids } = processItems(row.commonFileListBeforeVO || []);
-  const { images: afterImgs, videos: afterVids } = processItems(row.commonFileListAfterVO || []);
-  const { images: issueImgs, videos: issueVids } = processItems(row.commonFileListVO || []);
-  
+    images.push(fileUrl);
+  });
+
+  return { images, videos };
+};
+
+const openDialog = row => {
+  const { images: beforeImgs, videos: beforeVids } = processItems(
+    row.commonFileListBeforeVO || []
+  );
+  const { images: afterImgs, videos: afterVids } = processItems(
+    row.commonFileListVO || []
+  );
+  const { images: issueImgs, videos: issueVids } = processItems(
+    row.commonFileListAfterVO || []
+  );
+
   beforeProductionImgs.value = beforeImgs;
   beforeProductionVideos.value = beforeVids;
-  
   afterProductionImgs.value = afterImgs;
   afterProductionVideos.value = afterVids;
-  
   productionIssuesImgs.value = issueImgs;
   productionIssuesVideos.value = issueVids;
-  
   dialogVisitable.value = true;
 };
 
-// 鏄剧ず濯掍綋锛堝浘鐗� or 瑙嗛锛�
-function showMedia(mediaArray, index, type) {
-  mediaList.value = mediaArray;
+const showMedia = (items, index, type) => {
+  mediaList.value = items;
   currentMediaIndex.value = index;
   mediaType.value = type;
   isMediaViewerVisible.value = true;
-}
+};
 
-// 鍏抽棴濯掍綋鏌ョ湅鍣�
-function closeMediaViewer() {
+const closeMediaViewer = () => {
   isMediaViewerVisible.value = false;
   mediaList.value = [];
-  mediaType.value = 'image';
-}
+  mediaType.value = "image";
+};
 
-// 琛ㄥ崟鍏抽棴鏂规硶
 const cancel = () => {
   dialogVisitable.value = false;
 };
 
 defineExpose({ openDialog });
 </script>
+
 <style scoped lang="scss">
 .upload-container {
   display: flex;
@@ -213,7 +229,7 @@
   padding: 20px;
   border: 1px solid #dcdfe6;
   box-sizing: border-box;
-  
+
   .form-container {
     flex: 1;
     width: 100%;
@@ -229,7 +245,7 @@
   padding-left: 10px;
   position: relative;
   margin: 6px 0;
-  
+
   &::before {
     content: "";
     position: absolute;
@@ -241,12 +257,48 @@
   }
 }
 
+.media-list {
+  display: flex;
+  flex-wrap: wrap;
+}
+
+.media-image {
+  max-width: 100px;
+  height: 100px;
+  margin: 5px;
+  cursor: pointer;
+}
+
+.video-item {
+  position: relative;
+  margin: 10px;
+  cursor: pointer;
+}
+
+.video-thumb {
+  width: 160px;
+  height: 90px;
+  background-color: #333;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.video-icon {
+  width: 30px;
+  height: 30px;
+  opacity: 0.8;
+}
+
+.video-text {
+  text-align: center;
+  font-size: 12px;
+  color: #666;
+}
+
 .media-viewer-overlay {
   position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
+  inset: 0;
   background-color: rgba(0, 0, 0, 0.8);
   z-index: 9999;
   display: flex;
@@ -260,4 +312,13 @@
   max-height: 90vh;
   overflow: hidden;
 }
-</style>
\ No newline at end of file
+
+.video-player-wrap {
+  position: relative;
+}
+
+.video-player {
+  max-width: 90vw;
+  max-height: 80vh;
+}
+</style>
diff --git a/src/views/equipmentManagement/inspectionManagement/index.vue b/src/views/equipmentManagement/inspectionManagement/index.vue
index 809fd4f..fa531f1 100644
--- a/src/views/equipmentManagement/inspectionManagement/index.vue
+++ b/src/views/equipmentManagement/inspectionManagement/index.vue
@@ -82,6 +82,9 @@
     <form-dia ref="formDia"
               @closeDia="handleQuery"></form-dia>
     <view-files ref="viewFiles"></view-files>
+    <upload-files ref="uploadFiles"
+                  @success="handleQuery"
+                  @closeDia="handleQuery"></upload-files>
   </div>
 </template>
 
@@ -93,6 +96,7 @@
   // 缁勪欢寮曞叆
   import PIMTable from "@/components/PIMTable/PIMTable.vue";
   import FormDia from "@/views/equipmentManagement/inspectionManagement/components/formDia.vue";
+  import UploadFiles from "@/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue";
   import ViewFiles from "@/views/equipmentManagement/inspectionManagement/components/viewFiles.vue";
 
   // 鎺ュ彛寮曞叆
@@ -106,6 +110,7 @@
   const { proxy } = getCurrentInstance();
   const formDia = ref();
   const viewFiles = ref();
+  const uploadFiles = ref();
 
   // 鏌ヨ鍙傛暟
   const queryParams = reactive({
@@ -211,8 +216,9 @@
 
     const operationConfig = {
       label: "鎿嶄綔",
-      width: 130,
+      width: operations.length > 1 ? 180 : 130,
       fixed: "right",
+			align: 'center',
       dataType: "action",
       operation: operations
         .map(op => {
@@ -221,6 +227,12 @@
               return {
                 name: "缂栬緫",
                 clickFun: handleAdd,
+                color: "#409EFF",
+              };
+            case "upload":
+              return {
+                name: "涓婁紶",
+                clickFun: openUploadDialog,
                 color: "#409EFF",
               };
             case "viewFile":
@@ -253,14 +265,14 @@
       ];
       operationsArr.value = ["edit"];
     } else if (value === "task") {
-      const operationColumn = getOperationColumn(["viewFile"]);
+      const operationColumn = getOperationColumn(["upload", "viewFile"]);
       // 瀹氭椂浠诲姟璁板綍涓嶅睍绀�"鏄惁鍚敤"鍒�
       const taskColumns = columns.value.filter(col => col.prop !== "isEnabled");
       tableColumns.value = [
         ...taskColumns,
         ...(operationColumn ? [operationColumn] : []),
       ];
-      operationsArr.value = ["viewFile"];
+      operationsArr.value = ["upload", "viewFile"];
     }
     pageNum.value = 1;
     pageSize.value = 10;
@@ -302,6 +314,7 @@
         // 澶勭悊 inspector 瀛楁锛屽皢瀛楃涓茶浆鎹负鏁扮粍锛堥�傜敤浜庢墍鏈夋儏鍐碉級
         tableData.value = rawData.map(item => {
           const processedItem = { ...item };
+          processedItem.__raw = { ...item };
 
           // 澶勭悊 inspector 瀛楁
           if (processedItem.inspector) {
@@ -351,6 +364,12 @@
   const viewFile = row => {
     nextTick(() => {
       viewFiles.value?.openDialog(row);
+    });
+  };
+
+  const openUploadDialog = row => {
+    nextTick(() => {
+      uploadFiles.value?.openDialog(row);
     });
   };
 
@@ -419,4 +438,4 @@
     color: #909399;
     font-size: 14px;
   }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/views/index.vue b/src/views/index.vue
index 3176042..0d71970 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -114,6 +114,7 @@
             <div class="panel-title">鐢熶骇璁㈠崟杩涘害</div>
             <el-radio-group v-model="orderFilter" size="small">
               <el-radio-button label="all">鍏ㄩ儴({{ orderProgressMeta.total }})</el-radio-button>
+              <el-radio-button label="waiting">寰呭紑濮�({{ orderProgressMeta.waitingCount }})</el-radio-button>
               <el-radio-button label="inProgress">杩涜涓�({{ orderProgressMeta.inProgressCount }})</el-radio-button>
               <el-radio-button label="completed">宸插畬鎴�({{ orderProgressMeta.completedCount }})</el-radio-button>
               <el-radio-button label="paused">宸叉殏鍋�({{ orderProgressMeta.pausedCount }})</el-radio-button>
@@ -454,10 +455,13 @@
 });
 
 const orderProgressMeta = ref({
+  status: "all",
   tab: "all",
+  bizDate: null,
   total: 0,
   pageNum: 1,
   pageSize: 10,
+  waitingCount: 0,
   inProgressCount: 0,
   completedCount: 0,
   pausedCount: 0,
@@ -832,8 +836,36 @@
 
 const productionOrders = ref([]);
 
+const orderFilterOptions = ["all", "waiting", "inProgress", "completed", "paused"];
+const orderFilterAliasMap = {
+  1: "waiting",
+  2: "inProgress",
+  3: "completed",
+  4: "paused",
+};
 const orderFilter = ref("all");
 const filteredOrders = computed(() => productionOrders.value);
+
+const normalizeOrderFilter = (value, fallback = "all") => {
+  const safeFallback = orderFilterOptions.includes(fallback) ? fallback : "all";
+  const text = String(value ?? "").trim();
+  if (orderFilterAliasMap[text]) {
+    return orderFilterAliasMap[text];
+  }
+  return orderFilterOptions.includes(text) ? text : safeFallback;
+};
+
+const parseCount = (value) => {
+  if (value === null || value === undefined || value === "") return null;
+  const num = Number(value);
+  return Number.isFinite(num) ? num : null;
+};
+
+const resolveProgressCount = (rawValue, currentStatus, targetStatus, total) => {
+  const count = parseCount(rawValue);
+  if (count !== null) return count;
+  return currentStatus === targetStatus ? total : 0;
+};
 
 const getCompareTrend = (value) => {
   const num = Number(value || 0);
@@ -1198,27 +1230,36 @@
 const refreshProductionOrderProgress = async () => {
   try {
     const res = await productionOrderProgress({
+      status: orderFilter.value,
       tab: orderFilter.value,
       pageNum: 1,
       pageSize: 10,
     });
     const data = res?.data || {};
+    const statusValue = normalizeOrderFilter(data.status, orderFilter.value);
+    const total = Number(data.total || 0);
     orderProgressMeta.value = {
+      status: statusValue,
       tab: data.tab || orderFilter.value,
-      total: Number(data.total || 0),
+      bizDate: data.bizDate || null,
+      total,
       pageNum: Number(data.pageNum || 1),
       pageSize: Number(data.pageSize || 10),
-      inProgressCount: Number(data.inProgressCount || 0),
-      completedCount: Number(data.completedCount || 0),
-      pausedCount: Number(data.pausedCount || 0),
+      waitingCount: resolveProgressCount(data.waitingCount, statusValue, "waiting", total),
+      inProgressCount: resolveProgressCount(data.inProgressCount, statusValue, "inProgress", total),
+      completedCount: resolveProgressCount(data.completedCount, statusValue, "completed", total),
+      pausedCount: resolveProgressCount(data.pausedCount, statusValue, "paused", total),
     };
     productionOrders.value = (data.records || []).map(mapOrderProgressRecord);
   } catch {
     orderProgressMeta.value = {
+      status: orderFilter.value,
       tab: orderFilter.value,
+      bizDate: null,
       total: 0,
       pageNum: 1,
       pageSize: 10,
+      waitingCount: 0,
       inProgressCount: 0,
       completedCount: 0,
       pausedCount: 0,
@@ -1229,7 +1270,10 @@
 
 const refreshTodayProductionPlan = async () => {
   try {
-    const res = await todayProductionPlan({ limit: 4 });
+    const res = await todayProductionPlan({
+      limit: 4,
+      planDate: nowDate.value,
+    });
     const data = res?.data || {};
     todayPlanTotal.value = Number(data.total || 0);
     todayPlanList.value = (data.records || []).map(mapTodayPlanRecord);
diff --git a/src/views/productionManagement/processRoute/index.vue b/src/views/productionManagement/processRoute/index.vue
index 43425c6..1ef5b9d 100644
--- a/src/views/productionManagement/processRoute/index.vue
+++ b/src/views/productionManagement/processRoute/index.vue
@@ -61,7 +61,9 @@
   import EditProcess from "@/views/productionManagement/processRoute/Edit.vue";
   import RouteItemForm from "@/views/productionManagement/processRoute/ItemsForm.vue";
   import { listPage, del } from "@/api/productionManagement/processRoute.js";
-  const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
+  const FileList = defineAsyncComponent(() =>
+    import("@/components/Dialog/FileList.vue")
+  );
 
   import { useRouter } from "vue-router";
   import { ElMessage, ElMessageBox } from "element-plus";
diff --git a/src/views/productionManagement/processRoute/processRouteItem/index.vue b/src/views/productionManagement/processRoute/processRouteItem/index.vue
index eaf6397..3c52410 100644
--- a/src/views/productionManagement/processRoute/processRouteItem/index.vue
+++ b/src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -47,16 +47,45 @@
             <span class="info-value">{{ routeInfo.quantity || '-' }}</span>
           </div>
         </div>
-        <div class="info-item full-width"
-             v-if="routeInfo.description">
+        <div class="info-item">
           <div class="info-label-wrapper">
-            <span class="info-label">鎻忚堪</span>
+            <span class="info-label">澶囨敞</span>
           </div>
           <div class="info-value-wrapper">
             <span class="info-value">{{ routeInfo.description }}</span>
           </div>
         </div>
       </div>
+    </el-card>
+    <!-- 闄勪欢妯″潡 -->
+    <div v-if="pageType === 'order'"
+         class="section-header">
+      <div class="section-title">闄勪欢</div>
+    </div>
+    <el-card v-if="pageType === 'order'"
+             class="attachment-card"
+             shadow="hover"
+             style="margin-top: 10px; margin-bottom: 20px;">
+      <el-table :data="attachmentTableData"
+                border
+                class="attachment-table">
+        <el-table-column label="闄勪欢鍚嶇О"
+                         prop="originalFilename"
+                         show-overflow-tooltip />
+        <el-table-column fixed="right"
+                         label="鎿嶄綔"
+                         width="200"
+                         align="center">
+          <template #default="scope">
+            <el-button link
+                       type="primary"
+                       size="small"
+                       @click="downloadAttachmentFile(scope.row.downloadURL)">
+              涓嬭浇
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
     </el-card>
     <!-- 琛ㄦ牸瑙嗗浘 -->
     <div v-if="viewMode === 'table'"
@@ -382,6 +411,18 @@
                          v-model="bomDataValue.showProductDialog"
                          :single="true"
                          @confirm="handleBomProduct" />
+    <!-- 涓婁紶缁勪欢寮圭獥 -->
+    <el-dialog v-model="uploadDialogVisible"
+               title="涓婁紶闄勪欢"
+               width="50%"
+               @close="closeAttachmentUpload">
+      <AttachmentUpload v-model:file-list="newFileList" />
+      <template #footer>
+        <el-button @click="saveAttachmentUpload"
+                   type="primary">淇濆瓨</el-button>
+        <el-button @click="closeAttachmentUpload">鍏抽棴</el-button>
+      </template>
+    </el-dialog>
     <!-- 鏂板/缂栬緫寮圭獥 -->
     <el-dialog v-model="dialogVisible"
                :title="operationType === 'add' ? '鏂板宸ヨ壓璺嚎椤圭洰' : '缂栬緫宸ヨ壓璺嚎椤圭洰'"
@@ -518,6 +559,12 @@
     queryList2,
     add2,
   } from "@/api/productionManagement/productStructure.js";
+  import AttachmentUpload from "@/components/AttachmentUpload/file/index.vue";
+  import {
+    attachmentList,
+    deleteAttachment,
+    createAttachment,
+  } from "@/api/basicData/storageAttachment.js";
 
   import { useRoute } from "vue-router";
   import { ElMessageBox, ElMessage } from "element-plus";
@@ -530,6 +577,7 @@
   const orderId = computed(() => route.query.orderId);
   const pageType = computed(() => route.query.type);
   const editable = computed(() => route.query.editable !== "false");
+  const technologyRoutingId = computed(() => route.query.technologyRoutingId);
 
   const tableLoading = ref(false);
   const tableData = ref([]);
@@ -548,7 +596,66 @@
     bomNo: "",
     description: "",
     quantity: 0,
+    technologyRoutingId: "",
   });
+
+  // 闄勪欢鐩稿叧
+  const attachmentTableData = ref([]);
+  const uploadDialogVisible = ref(false);
+  const newFileList = ref([]);
+
+  const getAttachmentList = () => {
+    if (!technologyRoutingId.value) return;
+    attachmentList({
+      recordType: "technology_routing",
+      recordId: technologyRoutingId.value,
+    }).then(res => {
+      attachmentTableData.value = (res && res.data) || [];
+    });
+  };
+
+  const handleUploadAttachment = () => {
+    uploadDialogVisible.value = true;
+  };
+
+  const saveAttachmentUpload = async () => {
+    if (newFileList.value.length > 0) {
+      createAttachment({
+        application: "file",
+        recordType: "technology_routing",
+        recordId: technologyRoutingId.value,
+        storageBlobDTOs: [...newFileList.value, ...attachmentTableData.value],
+      })
+        .then(res => {
+          if (res && res.code === 200) {
+            proxy?.$modal?.msgSuccess("涓婁紶鎴愬姛");
+            newFileList.value = [];
+            getAttachmentList();
+          }
+        })
+        .finally(() => {
+          uploadDialogVisible.value = false;
+        });
+    }
+  };
+
+  const closeAttachmentUpload = () => {
+    newFileList.value = [];
+    uploadDialogVisible.value = false;
+  };
+
+  const handleDeleteAttachment = async row => {
+    deleteAttachment([row.storageAttachmentId]).then(res => {
+      if (res && res.code === 200) {
+        proxy?.$modal?.msgSuccess("鍒犻櫎鎴愬姛");
+        getAttachmentList();
+      }
+    });
+  };
+
+  const downloadAttachmentFile = url => {
+    window.open(url, "_blank");
+  };
 
   const processOptions = ref([]);
   const showProductSelectDialog = ref(false);
@@ -680,6 +787,7 @@
       bomId: route.query.bomId || "",
       description: route.query.description || "",
       quantity: route.query.quantity || 0,
+      technologyRoutingId: route.query.technologyRoutingId || "",
       status: !(route.query.status == 1 || route.query.status === "false"),
     };
     bomTableData.value[0].productName = routeInfo.value.productName;
@@ -1165,12 +1273,16 @@
   const handleBomProcessChange = (row, value) => {
     row.processId = value || "";
     syncProcessOperationFields(row);
-    
+
     // 妫�鏌ュ悓涓�灞傜骇鏄惁宸茬粡鏈夊叾浠栦笉鍚岀殑宸ュ簭琚�変腑
     const siblings = findSiblings(bomDataValue.value.dataList, row.tempId);
     if (siblings && value) {
       const hasDifferentProcess = siblings.some(sibling => {
-        return sibling.tempId !== row.tempId && sibling.processId && sibling.processId !== value;
+        return (
+          sibling.tempId !== row.tempId &&
+          sibling.processId &&
+          sibling.processId !== value
+        );
       });
       if (hasDifferentProcess) {
         ElMessage.warning("鍚屼竴灞傜骇宸插瓨鍦ㄤ笉鍚岀殑宸ュ簭锛岃鍏堢粺涓�宸ュ簭鍚庡啀杩涜淇敼");
@@ -1392,11 +1504,13 @@
     };
 
     // 鏍¢獙鍚屼竴灞傜骇鐨勫伐搴忔槸鍚︿竴鑷�
-    const validateProcessConsistency = (items) => {
+    const validateProcessConsistency = items => {
       if (!items || items.length === 0) return;
 
       // 妫�鏌ュ綋鍓嶅眰绾�
-      const processes = items.filter(item => item.processId).map(item => item.processId);
+      const processes = items
+        .filter(item => item.processId)
+        .map(item => item.processId);
       if (processes.length > 1) {
         const uniqueProcesses = [...new Set(processes)];
         if (uniqueProcesses.length > 1) {
@@ -1474,6 +1588,9 @@
     getList();
     getProcessList();
     fetchBomData();
+    if (pageType.value === "order") {
+      getAttachmentList();
+    }
   };
 
   onMounted(() => {
diff --git a/src/views/productionManagement/productionOrder/index.vue b/src/views/productionManagement/productionOrder/index.vue
index a9f61aa..100b94c 100644
--- a/src/views/productionManagement/productionOrder/index.vue
+++ b/src/views/productionManagement/productionOrder/index.vue
@@ -724,6 +724,7 @@
           bomNo: row.bomNo || "",
           description: data.description || "",
           quantity: row.quantity || 0,
+          technologyRoutingId: data.technologyRoutingId,
           orderId,
           type: "order",
           editable: !row.endOrder,

--
Gitblit v1.9.3