From d78815179b7d49509e6637d547ca8bbd525f7fcf Mon Sep 17 00:00:00 2001
From: yuan <123@>
Date: 星期一, 07 九月 2026 11:45:41 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_pro_河南鹤壁' into dev_pro_河南鹤壁

---
 src/views/collaborativeApproval/approvalProcess/components/viewDetailDia.vue           |  630 ++++++++++++
 multiple/assets/favicon/JMTMfavicon.ico                                                |    0 
 multiple/assets/logo/HMCLLogo.png                                                      |    0 
 src/views/basicData/product/ProductSelectDialog.vue                                    |   31 
 multiple/config.json                                                                   |   84 +
 src/views/productionManagement/processRoute/processRouteItem/index.vue                 |   76 -
 multiple/assets/logo/JMTMLogo.png                                                      |    0 
 src/views/collaborativeApproval/approvalProcess/index.vue                              |   21 
 src/views/qualityManagement/rawMaterialInspection/index.vue                            |   12 
 src/views/salesManagement/salesLedger/index.vue                                        |   99 +
 src/views/qualityManagement/finalInspection/index.vue                                  |   12 
 src/views/monitor/logininfor/index.vue                                                 |    6 
 src/views/monitor/operlog/index.vue                                                    |    7 
 src/views/qualityManagement/processInspection/index.vue                                |   12 
 src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue     |  433 +++++--
 src/views/procurementManagement/procurementPlan/index.vue                              |    2 
 src/views/productionManagement/productionTraceability/index.vue                        |    2 
 multiple/assets/logo/SDKSLogo.png                                                      |    0 
 src/views/equipmentManagement/ledger/index.vue                                         |   20 
 multiple/assets/logo/CHMYLogo.png                                                      |    0 
 multiple/assets/logo/SJDQLogo.png                                                      |    0 
 multiple/assets/favicon/HMCLfavicon.ico                                                |    0 
 src/views/productionManagement/productionProcess/ProcessSelectDialog.vue               |  159 +++
 src/views/inventoryManagement/stockManagement/New.vue                                  |   12 
 multiple/assets/favicon/CHMYfavicon.ico                                                |    0 
 multiple/assets/favicon/TXDQfavicon.ico                                                |    0 
 src/views/productionManagement/productionOrder/components/PurchaseLedgerAddDialog.vue  |  856 ++++++++++++++++
 multiple/assets/favicon/SJDQfavicon.ico                                                |    0 
 src/api/productionManagement/productionProcess.js                                      |    8 
 src/views/equipmentManagement/repair/index.vue                                         |    9 
 src/views/qualityManagement/nonconformingManagement/components/detailModal.vue         |   98 +
 src/views/procurementManagement/paymentLedger/index.vue                                |   14 
 src/views/productionManagement/productStructure/Detail/index.vue                       |  283 +++++
 src/views/equipmentManagement/repair/Modal/RepairDetailModal.vue                       |   96 +
 src/views/procurementManagement/procurementLedger/index.vue                            |   12 
 multiple/assets/favicon/SDKSfavicon.ico                                                |    0 
 src/views/productionManagement/productionOrder/index.vue                               |   16 
 src/views/productionPlan/productionPlan/index.vue                                      |   28 
 src/api/qualityManagement/nonconformingManagement.js                                   |    9 
 src/components/filePreview/index.vue                                                   |   14 
 src/views/qualityManagement/nonconformingManagement/index.vue                          |   12 
 multiple/assets/logo/TXDQLogo.png                                                      |    0 
 src/views/productionManagement/productionOrder/components/PrintMaterialRequisition.vue |    2 
 43 files changed, 2,756 insertions(+), 319 deletions(-)

diff --git a/multiple/assets/favicon/CHMYfavicon.ico b/multiple/assets/favicon/CHMYfavicon.ico
new file mode 100644
index 0000000..47e0a72
--- /dev/null
+++ b/multiple/assets/favicon/CHMYfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/HMCLfavicon.ico b/multiple/assets/favicon/HMCLfavicon.ico
new file mode 100644
index 0000000..29633d6
--- /dev/null
+++ b/multiple/assets/favicon/HMCLfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/JMTMfavicon.ico b/multiple/assets/favicon/JMTMfavicon.ico
new file mode 100644
index 0000000..ef5b373
--- /dev/null
+++ b/multiple/assets/favicon/JMTMfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/SDKSfavicon.ico b/multiple/assets/favicon/SDKSfavicon.ico
new file mode 100644
index 0000000..3b2f908
--- /dev/null
+++ b/multiple/assets/favicon/SDKSfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/SJDQfavicon.ico b/multiple/assets/favicon/SJDQfavicon.ico
new file mode 100644
index 0000000..c87460c
--- /dev/null
+++ b/multiple/assets/favicon/SJDQfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/TXDQfavicon.ico b/multiple/assets/favicon/TXDQfavicon.ico
new file mode 100644
index 0000000..92c8b17
--- /dev/null
+++ b/multiple/assets/favicon/TXDQfavicon.ico
Binary files differ
diff --git a/multiple/assets/logo/CHMYLogo.png b/multiple/assets/logo/CHMYLogo.png
new file mode 100644
index 0000000..d3ade0e
--- /dev/null
+++ b/multiple/assets/logo/CHMYLogo.png
Binary files differ
diff --git a/multiple/assets/logo/HMCLLogo.png b/multiple/assets/logo/HMCLLogo.png
new file mode 100644
index 0000000..3710fcf
--- /dev/null
+++ b/multiple/assets/logo/HMCLLogo.png
Binary files differ
diff --git a/multiple/assets/logo/JMTMLogo.png b/multiple/assets/logo/JMTMLogo.png
new file mode 100644
index 0000000..ad8e952
--- /dev/null
+++ b/multiple/assets/logo/JMTMLogo.png
Binary files differ
diff --git a/multiple/assets/logo/SDKSLogo.png b/multiple/assets/logo/SDKSLogo.png
new file mode 100644
index 0000000..46b7f3c
--- /dev/null
+++ b/multiple/assets/logo/SDKSLogo.png
Binary files differ
diff --git a/multiple/assets/logo/SJDQLogo.png b/multiple/assets/logo/SJDQLogo.png
new file mode 100644
index 0000000..3657807
--- /dev/null
+++ b/multiple/assets/logo/SJDQLogo.png
Binary files differ
diff --git a/multiple/assets/logo/TXDQLogo.png b/multiple/assets/logo/TXDQLogo.png
new file mode 100644
index 0000000..a1e4efb
--- /dev/null
+++ b/multiple/assets/logo/TXDQLogo.png
Binary files differ
diff --git a/multiple/config.json b/multiple/config.json
index 0ed35b4..777276a 100644
--- a/multiple/config.json
+++ b/multiple/config.json
@@ -153,8 +153,17 @@
   "DDGW": {
     "env": {
       "VITE_APP_TITLE": "娌冲崡涓滃ぇ楂樻俯鑺傝兘鏉愭枡鏈夐檺鍏徃",
-      "VITE_BASE_API": "http://103.189.141.42:9001",
-      "VITE_JAVA_API": "http://103.189.141.42:9002"
+      "VITE_BASE_API": "http://36.133.40.190:81",
+      "VITE_JAVA_API": "http://36.133.40.190:82"
+    },
+    "logo": "logo/DDGWLogo.png",
+    "favicon": "favicon/DDGWfavicon.ico"
+  },
+  "DDGW-TEST": {
+    "env": {
+      "VITE_APP_TITLE": "娌冲崡涓滃ぇ楂樻俯鑺傝兘鏉愭枡鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.133.40.190:83",
+      "VITE_JAVA_API": "http://36.133.40.190:84"
     },
     "logo": "logo/DDGWLogo.png",
     "favicon": "favicon/DDGWfavicon.ico"
@@ -167,6 +176,60 @@
     },
     "logo": "logo/XHZGLogo.png",
     "favicon": "favicon/XHZGfavicon.ico"
+  },
+  "SDKS": {
+    "env": {
+      "VITE_APP_TITLE": "楣ゅ甯傚洓杈剧熆灞辫澶囨湁闄愬叕鍙�",
+      "VITE_BASE_API": "http://36.213.180.217:9520",
+      "VITE_JAVA_API": "http://36.213.180.217:9521"
+    },
+    "logo": "logo/SDKSLogo.png",
+    "favicon": "favicon/SDKSfavicon.ico"
+  },
+  "JMTM": {
+    "env": {
+      "VITE_APP_TITLE": "娌冲崡鎹风編鐗归晛绉戞妧鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.133.40.61:81",
+      "VITE_JAVA_API": "http://36.133.40.61:82"
+    },
+    "logo": "logo/JMTMLogo.png",
+    "favicon": "favicon/JMTMfavicon.ico"
+  },
+  "CHMY": {
+    "env": {
+      "VITE_APP_TITLE": "楣ゅ鏄屽畯闀佷笟鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.133.39.224:81",
+      "VITE_JAVA_API": "http://36.133.39.224:82"
+    },
+    "logo": "logo/CHMYLogo.png",
+    "favicon": "favicon/CHMYfavicon.ico"
+  },
+  "SJDQ": {
+    "env": {
+      "VITE_APP_TITLE": "楣ゅ甯傚北鍩庡尯鐩涗箙鐢靛櫒鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.139.201.235:81",
+      "VITE_JAVA_API": "http://36.139.201.235:82"
+    },
+    "logo": "logo/SJDQLogo.png",
+    "favicon": "favicon/SJDQfavicon.ico"
+  },
+  "TXDQ": {
+    "env": {
+      "VITE_APP_TITLE": "楣ゅ娉颁俊鐢垫皵鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.133.46.171:81",
+      "VITE_JAVA_API": "http://36.133.46.171:82"
+    },
+    "logo": "logo/TXDQLogo.png",
+    "favicon": "favicon/TXDQfavicon.ico"
+  },
+  "HMCL": {
+    "env": {
+      "VITE_APP_TITLE": "楣ゅ娴╅晛鏉愭枡鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.213.90.123:9522",
+      "VITE_JAVA_API": "http://36.213.90.123:9523"
+    },
+    "logo": "logo/HMCLLogo.png",
+    "favicon": "favicon/HMCLfavicon.ico"
   },
   "DTZG": {
     "env": {
@@ -185,15 +248,16 @@
     },
     "logo": "logo/XLZNLogo.png",
     "favicon": "favicon/XLZNfavicon.ico"
-  },"SKJS": {
-  "env": {
-    "VITE_APP_TITLE": "寰愬窞浠�鍏嬮噾灞炲埗鍝佹湁闄愬叕鍙�",
-    "VITE_BASE_API": "http://36.213.90.123:9514",
-    "VITE_JAVA_API": "http://36.213.90.123:9515"
   },
-  "logo": "logo/SKJSLogo.png",
-  "favicon": "favicon/SKJSFavicon.ico"
-},
+  "SKJS": {
+    "env": {
+      "VITE_APP_TITLE": "寰愬窞浠�鍏嬮噾灞炲埗鍝佹湁闄愬叕鍙�",
+      "VITE_BASE_API": "http://36.213.90.123:9514",
+      "VITE_JAVA_API": "http://36.213.90.123:9515"
+    },
+    "logo": "logo/SKJSLogo.png",
+    "favicon": "favicon/SKJSFavicon.ico"
+  },
   "YJYY": {
     "env": {
       "VITE_APP_TITLE": "寰愬窞姘镐匠娑插帇璁惧鏈夐檺鍏徃",
diff --git a/src/api/productionManagement/productionProcess.js b/src/api/productionManagement/productionProcess.js
index 783f584..09cbff9 100644
--- a/src/api/productionManagement/productionProcess.js
+++ b/src/api/productionManagement/productionProcess.js
@@ -26,6 +26,14 @@
     params: query,
   });
 }
+
+export function listAll() {
+  return request({
+    url: "/technologyOperation/listPage",
+    method: "get",
+    params: { current: -1, size: -1 },
+  });
+}
 export function add(data) {
   return request({
     url: "/technologyOperation/add",
diff --git a/src/api/qualityManagement/nonconformingManagement.js b/src/api/qualityManagement/nonconformingManagement.js
index 50a1b74..2433091 100644
--- a/src/api/qualityManagement/nonconformingManagement.js
+++ b/src/api/qualityManagement/nonconformingManagement.js
@@ -48,3 +48,12 @@
     data: query,
   });
 }
+
+// 鏌ヨ涓嶅悎鏍艰鎯�
+export function getQualityUnqualifiedDetail(query) {
+  return request({
+    url: "/quality/qualityUnqualified/detail/" + query,
+    method: "get",
+    data: query,
+  });
+}
diff --git a/src/components/filePreview/index.vue b/src/components/filePreview/index.vue
index d8720c3..cd29fbc 100644
--- a/src/components/filePreview/index.vue
+++ b/src/components/filePreview/index.vue
@@ -1,8 +1,8 @@
 <template>
-  <el-dialog v-model="dialogVisible" title="棰勮" width="100%" fullscreen align-center :before-close="handleClose" append-to-body>
+  <el-dialog v-model="dialogVisible" title="棰勮" :width="isImage ? '60%' : '100%'" :fullscreen="!isImage" align-center :before-close="handleClose" append-to-body>
     <div>
       <!-- 鍥剧墖棰勮 -->
-      <div v-if="isImage">
+      <div v-if="isImage" class="image-preview-container">
         <img :src="imgUrl" alt="Image Preview" />
       </div>
       
@@ -188,8 +188,18 @@
 </script>
 
 <style scoped>
+.image-preview-container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  max-height: calc(100vh - 200px);
+  width: 100%;
+}
+
 img {
   max-width: 100%;
+  max-height: calc(100vh - 200px);
+  object-fit: contain;
   display: block;
   margin: 0 auto;
 }
diff --git a/src/views/basicData/product/ProductSelectDialog.vue b/src/views/basicData/product/ProductSelectDialog.vue
index 590f8ca..1586f58 100644
--- a/src/views/basicData/product/ProductSelectDialog.vue
+++ b/src/views/basicData/product/ProductSelectDialog.vue
@@ -1,12 +1,18 @@
 <template>
   <el-dialog v-model="visible" title="閫夋嫨浜у搧" width="900px" destroy-on-close :close-on-click-modal="false">
     <el-form :inline="true" :model="query" class="mb-2">
+      <el-form-item label="浜у搧澶х被">
+        <el-select v-model="query.topProductParentId" placeholder="閫夋嫨浜у搧澶х被" clearable @change="onSearch" style="width: 150px">
+          <el-option v-for="item in categoryList" :key="item.id" :label="item.label || item.productName" :value="item.id" />
+        </el-select>
+      </el-form-item>
+
       <el-form-item label="浜у搧鍚嶇О">
-        <el-input v-model="query.productName" placeholder="杈撳叆浜у搧鍚嶇О" clearable @keyup.enter="onSearch" style="width: 200px" />
+        <el-input v-model="query.productName" placeholder="杈撳叆浜у搧鍚嶇О" clearable @keyup.enter="onSearch" style="width: 150px" />
       </el-form-item>
 
       <el-form-item label="浜у搧鍨嬪彿">
-        <el-input v-model="query.model" placeholder="杈撳叆浜у搧鍨嬪彿" clearable @keyup.enter="onSearch" style="width: 200px" />
+        <el-input v-model="query.model" placeholder="杈撳叆浜у搧鍨嬪彿" clearable @keyup.enter="onSearch" style="width: 150px" />
       </el-form-item>
 
       <el-form-item>
@@ -45,6 +51,7 @@
 import { computed, onMounted, reactive, ref, watch, nextTick } from "vue";
 import { ElMessage } from "element-plus";
 import { productModelList, productModelListByUrl } from '@/api/basicData/productModel'
+import { productTreeList } from '@/api/basicData/product'
 
 export type ProductRow = {
   id: number;
@@ -70,7 +77,19 @@
 const query = reactive({
   productName: "",
   model: "",
+  topProductParentId: undefined as number | undefined,
 });
+
+const categoryList = ref<any[]>([]);
+
+async function loadCategory() {
+  try {
+    const res = await productTreeList();
+    categoryList.value = res || [];
+  } catch (e) {
+    console.error("Failed to load product categories", e);
+  }
+}
 
 const page = reactive({
   pageNum: 1,
@@ -132,6 +151,7 @@
 function onReset() {
   query.productName = "";
   query.model = "";
+  query.topProductParentId = props.topProductParentId;
   page.pageNum = 1;
   loadData();
 }
@@ -162,7 +182,7 @@
       model: query.model.trim(),
       current: page.pageNum,
       size: page.pageSize,
-      topProductParentId: props.topProductParentId,
+      topProductParentId: query.topProductParentId ?? props.topProductParentId,
     };
     const res: any = props.requestUrl
       ? await productModelListByUrl(props.requestUrl, params)
@@ -179,10 +199,13 @@
 watch(() => props.modelValue, (visible) => {
   if (visible) {
     multipleSelection.value = [];
+    query.topProductParentId = props.topProductParentId;
   }
 });
 
 onMounted(() => {
-  loadData()
+  query.topProductParentId = props.topProductParentId;
+  loadCategory();
+  loadData();
 })
 </script>
diff --git a/src/views/collaborativeApproval/approvalProcess/components/viewDetailDia.vue b/src/views/collaborativeApproval/approvalProcess/components/viewDetailDia.vue
new file mode 100644
index 0000000..196efde
--- /dev/null
+++ b/src/views/collaborativeApproval/approvalProcess/components/viewDetailDia.vue
@@ -0,0 +1,630 @@
+<template>
+  <div>
+    <el-dialog v-model="dialogFormVisible"
+               :title="operationType === 'approval' ? '瀹℃壒' : '璇︽儏'"
+               width="700px"
+               @close="closeDia">
+      <el-descriptions border :column="1" class="approval-detail">
+        <el-descriptions-item label="娴佺▼缂栧彿">{{ form.approveId }}</el-descriptions-item>
+        <el-descriptions-item label="鐢宠閮ㄩ棬">{{ form.approveDeptName }}</el-descriptions-item>
+        <el-descriptions-item :label="props.approveType == 5 ? '閲囪喘鍚堝悓鍙�' : '瀹℃壒浜嬬敱'" v-if="!isQuotationApproval && !isPurchaseApproval">
+          {{ form.approveReason }}
+        </el-descriptions-item>
+        <el-descriptions-item label="鎶ラ攢閲戦" v-if="Number(props.approveType) === 4">
+          {{ form.price }}
+        </el-descriptions-item>
+      </el-descriptions>
+      <!-- 鎶ヤ环瀹℃壒锛氬睍绀烘姤浠疯鎯咃紙澶嶇敤閿�鍞姤浠�"鏌ョ湅璇︽儏瀵硅瘽妗�"鍐呭缁撴瀯锛� -->
+      <div v-if="isQuotationApproval"
+           style="margin: 10px 0 18px;">
+        <el-divider content-position="left">鎶ヤ环璇︽儏</el-divider>
+        <el-skeleton :loading="quotationLoading"
+                     animated>
+          <template #template>
+            <el-skeleton-item variant="h3"
+                              style="width: 30%" />
+            <el-skeleton-item variant="text"
+                              style="width: 100%" />
+            <el-skeleton-item variant="text"
+                              style="width: 100%" />
+          </template>
+          <template #default>
+            <el-empty v-if="!currentQuotation || !currentQuotation.quotationNo"
+                      description="鏈煡璇㈠埌瀵瑰簲鎶ヤ环璇︽儏" />
+            <template v-else>
+              <el-descriptions :column="2"
+                               border>
+                <el-descriptions-item label="鎶ヤ环鍗曞彿">{{ currentQuotation.quotationNo }}</el-descriptions-item>
+                <el-descriptions-item label="瀹㈡埛鍚嶇О">{{ currentQuotation.customer }}</el-descriptions-item>
+                <el-descriptions-item label="涓氬姟鍛�">{{ currentQuotation.salesperson }}</el-descriptions-item>
+                <el-descriptions-item label="鎶ヤ环鏃ユ湡">{{ currentQuotation.quotationDate }}</el-descriptions-item>
+                <el-descriptions-item label="鏈夋晥鏈熻嚦">{{ currentQuotation.validDate }}</el-descriptions-item>
+                <el-descriptions-item label="浠樻鏂瑰紡">{{ currentQuotation.paymentMethod }}</el-descriptions-item>
+                <el-descriptions-item label="鎶ヤ环鎬婚"
+                                      :span="2">
+                  <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">
+                    {{ formatCurrency(currentQuotation.totalAmount) }}
+                  </span>
+                </el-descriptions-item>
+              </el-descriptions>
+              <div style="margin-top: 20px;">
+                <h4>浜у搧鏄庣粏</h4>
+                <el-table :data="currentQuotation.products || []"
+                          border
+                          style="width: 100%">
+                  <el-table-column prop="product"
+                                   label="浜у搧鍚嶇О" />
+                  <el-table-column prop="specification"
+                                   label="瑙勬牸鍨嬪彿" />
+                  <el-table-column prop="unit"
+                                   label="鍗曚綅" />
+                  <el-table-column prop="unitPrice"
+                                   label="鍗曚环">
+                    <template #default="scope">{{ formatCurrency(scope.row.unitPrice) }}</template>
+                  </el-table-column>
+                </el-table>
+              </div>
+              <div v-if="currentQuotation.remark"
+                   style="margin-top: 20px;">
+                <h4>澶囨敞</h4>
+                <p>{{ currentQuotation.remark }}</p>
+              </div>
+            </template>
+          </template>
+        </el-skeleton>
+      </div>
+      <!-- 閲囪喘瀹℃壒锛氬睍绀洪噰璐鎯� -->
+      <div v-if="isPurchaseApproval"
+           style="margin: 10px 0 18px;">
+        <el-divider content-position="left">閲囪喘璇︽儏</el-divider>
+        <el-skeleton :loading="purchaseLoading"
+                     animated>
+          <template #template>
+            <el-skeleton-item variant="h3"
+                              style="width: 30%" />
+            <el-skeleton-item variant="text"
+                              style="width: 100%" />
+            <el-skeleton-item variant="text"
+                              style="width: 100%" />
+          </template>
+          <template #default>
+            <el-empty v-if="!currentPurchase || !currentPurchase.purchaseContractNumber"
+                      description="鏈煡璇㈠埌瀵瑰簲閲囪喘璇︽儏" />
+            <template v-else>
+              <el-descriptions :column="2"
+                               border>
+                <el-descriptions-item label="閲囪喘鍚堝悓鍙�">{{ currentPurchase.purchaseContractNumber }}</el-descriptions-item>
+                <el-descriptions-item label="渚涘簲鍟嗗悕绉�">{{ currentPurchase.supplierName }}</el-descriptions-item>
+                <el-descriptions-item label="椤圭洰鍚嶇О">{{ currentPurchase.projectName }}</el-descriptions-item>
+                <el-descriptions-item label="閿�鍞悎鍚屽彿">{{ currentPurchase.salesContractNo }}</el-descriptions-item>
+                <el-descriptions-item label="绛捐鏃ユ湡">{{ currentPurchase.executionDate }}</el-descriptions-item>
+                <el-descriptions-item label="褰曞叆鏃ユ湡">{{ currentPurchase.entryDate }}</el-descriptions-item>
+                <el-descriptions-item label="浠樻鏂瑰紡">{{ currentPurchase.paymentMethod }}</el-descriptions-item>
+                <el-descriptions-item label="鍚堝悓閲戦"
+                                      :span="2">
+                  <span style="font-size: 18px; color: #e6a23c; font-weight: bold;">
+                    {{ formatCurrency(currentPurchase.contractAmount) }}
+                  </span>
+                </el-descriptions-item>
+              </el-descriptions>
+              <div style="margin-top: 20px;">
+                <h4>浜у搧鏄庣粏</h4>
+                <el-table :data="currentPurchase.productData || []"
+                          border
+                          style="width: 100%">
+                  <el-table-column prop="productCategory"
+                                   label="浜у搧鍚嶇О" />
+                  <el-table-column prop="specificationModel"
+                                   label="瑙勬牸鍨嬪彿" />
+                  <el-table-column prop="unit"
+                                   label="鍗曚綅" />
+                  <el-table-column prop="quantity"
+                                   label="鏁伴噺" />
+                  <el-table-column prop="taxInclusiveUnitPrice"
+                                   label="鍚◣鍗曚环">
+                    <template #default="scope">{{ formatCurrency(scope.row.taxInclusiveUnitPrice) }}</template>
+                  </el-table-column>
+                  <el-table-column prop="taxInclusiveTotalPrice"
+                                   label="鍚◣鎬讳环">
+                    <template #default="scope">{{ formatCurrency(scope.row.taxInclusiveTotalPrice) }}</template>
+                  </el-table-column>
+                </el-table>
+              </div>
+            </template>
+          </template>
+        </el-skeleton>
+      </div>
+      <!-- 鍙戣揣瀹℃壒锛氬睍绀哄彂璐ц鎯� -->
+      <div v-if="isDeliveryApproval"
+           style="margin: 10px 0 18px;">
+        <el-divider content-position="left">鍙戣揣璇︽儏</el-divider>
+        <el-skeleton :loading="deliveryLoading"
+                     animated>
+          <template #template>
+            <el-skeleton-item variant="h3"
+                              style="width: 30%" />
+            <el-skeleton-item variant="text"
+                              style="width: 100%" />
+            <el-skeleton-item variant="text"
+                              style="width: 100%" />
+          </template>
+          <template #default>
+            <el-empty v-if="!currentDelivery || !currentDelivery.shippingInfo"
+                      description="鏈煡璇㈠埌瀵瑰簲鍙戣揣璇︽儏" />
+            <template v-else>
+              <el-descriptions :column="2"
+                               border>
+                <el-descriptions-item label="閿�鍞鍗�">{{ currentDelivery.shippingInfo.salesContractNo || '--' }}</el-descriptions-item>
+                <el-descriptions-item label="鍙戣揣璁㈠崟鍙�">{{ currentDelivery.shippingInfo.shippingNo || '--' }}</el-descriptions-item>
+                <el-descriptions-item label="瀹㈡埛鍚嶇О">{{ currentDelivery.shippingInfo.customerName || '--' }}</el-descriptions-item>
+                <el-descriptions-item label="鍙戣揣绫诲瀷">{{ currentDelivery.shippingInfo.type || '--' }}</el-descriptions-item>
+                <el-descriptions-item label="鍙戣揣鏃ユ湡">{{ currentDelivery.shippingInfo.shippingDate || '--' }}</el-descriptions-item>
+                <el-descriptions-item label="瀹℃牳鐘舵��">{{ currentDelivery.shippingInfo.status || '--' }}</el-descriptions-item>
+                <el-descriptions-item label="鍙戣揣杞︾墝鍙�">{{ currentDelivery.shippingInfo.shippingCarNumber || '--' }}</el-descriptions-item>
+                <el-descriptions-item label="蹇�掑叕鍙�">{{ currentDelivery.shippingInfo.expressCompany || '--' }}</el-descriptions-item>
+                <el-descriptions-item label="蹇�掑崟鍙�"
+                                      :span="2">{{ currentDelivery.shippingInfo.expressNumber || '--' }}</el-descriptions-item>
+              </el-descriptions>
+              <div style="margin-top: 20px;">
+                <h4>浜у搧鏄庣粏</h4>
+                <el-table :data="deliveryProductList"
+                          border
+                          size="small"
+                          style="width: 100%">
+                  <el-table-column prop="batchNo"
+                                   label="鎵瑰彿"
+                                   show-overflow-tooltip />
+                  <el-table-column prop="productName"
+                                   label="浜у搧鍚嶇О"
+                                   show-overflow-tooltip />
+                  <el-table-column prop="specificationModel"
+                                   label="瑙勬牸鍨嬪彿"
+                                   show-overflow-tooltip />
+                  <el-table-column prop="deliveryQuantity"
+                                   label="鍙戣揣鏁伴噺"
+                                   align="center" />
+                </el-table>
+              </div>
+              <div v-if="currentDelivery.shippingInfo.storageBlobVOs && currentDelivery.shippingInfo.storageBlobVOs.length"
+                   style="margin-top: 20px;">
+                <h4>鍙戣揣鍥剧墖</h4>
+                <ImagePreview :file-list="currentDelivery.shippingInfo.storageBlobVOs" />
+              </div>
+            </template>
+          </template>
+        </el-skeleton>
+      </div>
+      <div class="activities-wrapper" style="margin-top: 20px;">
+        <el-steps :active="getActiveStep()"
+                  finish-status="success"
+                  process-status="process"
+                  align-center
+                  direction="vertical">
+          <el-step v-for="(activity, index) in activities"
+                   :key="index"
+                   finish-status="success"
+                   :title="getNodeTitle(index, activities.length)"
+                   :description="activity.approveNodeUser"
+                   :icon="getNodeIcon(activity, index)">
+            <template #icon>
+              <el-icon v-if="activity.approveNodeStatus === 2"
+                       color="red"
+                       :size="22">
+                <WarningFilled />
+              </el-icon>
+              <el-icon v-else-if="activity.isShen"
+                       color="#1890ff"
+                       :size="22">
+                <Edit />
+              </el-icon>
+              <el-icon v-else-if="activity.approveNodeStatus === 1"
+                       color="#67C23A"
+                       :size="26">
+                <Check />
+              </el-icon>
+              <el-icon v-else
+                       color="#C0C4CC"
+                       :size="22">
+                <MoreFilled />
+              </el-icon>
+            </template>
+            <template #title>
+              <span style="color: #000000">{{ getNodeTitle(index, activities.length) }}</span>
+            </template>
+            <template #description>
+              <div class="node-user">
+                <div class="avatar-wrapper">
+                  <img :src="userStore.avatar"
+                       class="user-avatar"
+                       alt="" />
+                </div>
+                <span style="color: #000000">{{ activity.approveNodeUser }}</span>
+              </div>
+              <div class="node-reason" v-if="activity.approveNodeReason">
+                <span>瀹℃壒鎰忚锛�</span>{{ activity.approveNodeReason }}
+              </div>
+            </template>
+          </el-step>
+        </el-steps>
+      </div>
+
+      <el-descriptions border :column="1" style="margin-top: 20px;" class="approval-detail">
+        <el-descriptions-item label="闄勪欢">
+          <ImageUpload v-if="attachments && attachments.length > 0" v-model:file-list="attachments" :disabled="true" />
+          <span v-else style="color: #999;">鏃犻檮浠�</span>
+        </el-descriptions-item>
+      </el-descriptions>
+      
+      <el-form ref="formRef" :model="form" label-width="100px" style="margin-top: 20px;">
+        <el-form-item label="瀹℃壒鎰忚" prop="approveReason" v-if="operationType === 'approval'">
+          <el-input v-model="form.approveReason" type="textarea" placeholder="璇疯緭鍏ュ鎵规剰瑙�" />
+        </el-form-item>
+      </el-form>
+
+      <template #footer
+                v-if="operationType === 'approval'">
+        <div class="dialog-footer">
+          <el-button type="primary"
+                     :loading="submitLoading"
+                     @click="submitForm(2)">涓嶉�氳繃</el-button>
+          <el-button type="primary"
+                     :loading="submitLoading"
+                     @click="submitForm(1)">閫氳繃</el-button>
+          <el-button @click="closeDia">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+  import {
+    computed,
+    getCurrentInstance,
+    nextTick,
+    reactive,
+    ref,
+    toRefs,
+  } from "vue";
+  import {
+    approveProcessDetails,
+    getDept,
+    updateApproveNode,
+  } from "@/api/collaborativeApproval/approvalProcess.js";
+  import useUserStore from "@/store/modules/user.js";
+  import {
+    WarningFilled,
+    Edit,
+    Check,
+    MoreFilled,
+  } from "@element-plus/icons-vue";
+  import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
+  import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger.js";
+  import { getDeliveryDetailByShippingNo } from "@/api/salesManagement/deliveryLedger.js";
+  import ImagePreview from "@/components/AttachmentPreview/image/index.vue";
+  import { formatCurrency } from "@/utils/numberFormat";
+  import ImageUpload from "@/components/AttachmentUpload/image/index.vue";
+  import { attachmentList } from "@/api/basicData/storageAttachment.js";
+
+  const emit = defineEmits(["close"]);
+  const { proxy } = getCurrentInstance();
+
+  const props = defineProps({
+    approveType: {
+      type: [Number, String],
+      default: 0,
+    },
+  });
+
+  const dialogFormVisible = ref(false);
+  const operationType = ref("");
+  const activities = ref([]);
+  const attachments = ref([]);
+  const formRef = ref(null);
+  const userStore = useUserStore();
+  const productOptions = ref([]);
+  const quotationLoading = ref(false);
+  const currentQuotation = ref({});
+  const purchaseLoading = ref(false);
+  const currentPurchase = ref({});
+  const deliveryLoading = ref(false);
+  const currentDelivery = ref({});
+  const deliveryProductList = ref([]);
+  const submitLoading = ref(false);
+  const isQuotationApproval = computed(() => Number(props.approveType) === 6);
+  const isPurchaseApproval = computed(() => Number(props.approveType) === 5);
+  const isDeliveryApproval = computed(() => Number(props.approveType) === 7);
+
+  const data = reactive({
+    form: {
+      approveId: "",
+      approveDeptId: "",
+      approveReason: "",
+      checkResult: "",
+    },
+  });
+  const { form } = toRefs(data);
+
+  // 鑺傜偣鏍囬
+  const getNodeTitle = (index, len) => {
+    if (index === len - 1) return "缁撴潫";
+    return "瀹℃壒";
+  };
+
+  // 鑾峰彇褰撳墠婵�娲绘楠�
+  const getActiveStep = () => {
+    // 濡傛灉鎵�鏈� isShen 閮戒负 false锛岃繑鍥炴渶鍚庝竴涓楠わ紙鍏ㄩ儴瀹屾垚锛�
+    const hasActive = activities.value.some(a => a.isShen === true);
+    if (!hasActive) return activities.value.length;
+    // 褰撳墠鑺傜偣绱㈠紩
+    return activities.value.findIndex(a => a.isShen == true);
+  };
+  // 姝ラicon
+  const getNodeIcon = (activity, index) => {
+    if (activity.approveNodeStatus === 2) return "el-icon-warning"; // 涓嶉�氳繃
+    if (activity.isShen) return "Edit";
+    return "";
+  };
+
+  // 鎵撳紑寮规
+  const openDialog = (type, row) => {
+    operationType.value = type;
+    dialogFormVisible.value = true;
+    currentQuotation.value = {};
+    currentPurchase.value = {};
+    form.value = { ...row };
+    
+    // 鑾峰彇闄勪欢
+    attachments.value = [];
+    attachmentList({ recordId: row.id, recordType: 'approve_process' }).then(res => {
+      attachments.value = res.data || [];
+    });
+    // 绔嬪嵆娓呴櫎琛ㄥ崟楠岃瘉鐘舵�侊紙鍥犱负瀛楁鏄痙isabled鐨勶紝涓嶉渶瑕侀獙璇侊級
+    nextTick(() => {
+      if (formRef.value) {
+        formRef.value.clearValidate();
+      }
+    });
+    // 纭繚閫夐」鍔犺浇瀹屾垚鍚庡啀鍖归厤鍊肩被鍨�
+    getProductOptions().then(() => {
+      // 纭繚鍊肩被鍨嬪尮閰嶏紙濡傛灉閫夐」宸插姞杞斤級
+      if (productOptions.value.length > 0 && form.value.approveDeptId) {
+        const matchedOption = productOptions.value.find(
+          opt =>
+            opt.deptId == form.value.approveDeptId ||
+            String(opt.deptId) === String(form.value.approveDeptId)
+        );
+        if (matchedOption) {
+          form.value.approveDeptId = matchedOption.deptId;
+        }
+      }
+      // 鍐嶆娓呴櫎楠岃瘉锛岀‘淇濋�夐」鍔犺浇鍚庡�煎尮閰嶆纭�
+      nextTick(() => {
+        if (formRef.value) {
+          formRef.value.clearValidate();
+        }
+      });
+    });
+
+    // 鎶ヤ环瀹℃壒锛氱敤瀹℃壒浜嬬敱瀛楁鎵胯浇鐨�"鎶ヤ环鍗曞彿"鍘绘煡鎶ヤ环鍒楄〃
+    if (isQuotationApproval.value) {
+      const quotationNo = row?.approveReason;
+      if (quotationNo) {
+        quotationLoading.value = true;
+        getQuotationList({ quotationNo })
+          .then(res => {
+            const records = res?.data?.records || [];
+            currentQuotation.value = records[0] || {};
+          })
+          .finally(() => {
+            quotationLoading.value = false;
+          });
+      }
+    }
+
+    // 閲囪喘瀹℃壒锛氱敤瀹℃壒浜嬬敱瀛楁鎵胯浇鐨�"閲囪喘鍚堝悓鍙�"鍘绘煡閲囪喘璇︽儏
+    if (isPurchaseApproval.value) {
+      const purchaseContractNumber = row?.approveReason;
+      if (purchaseContractNumber) {
+        purchaseLoading.value = true;
+        getPurchaseByCode({ purchaseContractNumber })
+          .then(res => {
+            currentPurchase.value = res;
+          })
+          .catch(err => {
+            console.error("鏌ヨ閲囪喘璇︽儏澶辫触:", err);
+            proxy.$modal.msgError("鏌ヨ閲囪喘璇︽儏澶辫触");
+          })
+          .finally(() => {
+            purchaseLoading.value = false;
+          });
+      }
+    }
+    // 鍙戣揣瀹℃壒锛氱敤瀹℃壒浜嬬敱瀛楁鎵胯浇鐨�"鍙戣揣鍗曞彿"鍘绘煡鍙戣揣璇︽儏
+    if (isDeliveryApproval.value) {
+      const deliveryNo = row?.approveReason;
+      if (deliveryNo) {
+        deliveryLoading.value = true;
+        currentDelivery.value = {};
+        deliveryProductList.value = [];
+        getDeliveryDetailByShippingNo({ shippingNo: deliveryNo })
+          .then(res => {
+            const detailData = res?.data || res || {};
+            currentDelivery.value = detailData;
+            deliveryProductList.value =
+              detailData.shippingProductDetailDtoList || [];
+          })
+          .catch(err => {
+            console.error("鏌ヨ鍙戣揣璇︽儏澶辫触:", err);
+            proxy.$modal.msgError("鏌ヨ鍙戣揣璇︽儏澶辫触");
+          })
+          .finally(() => {
+            deliveryLoading.value = false;
+          });
+      }
+    }
+
+    approveProcessDetails(row.approveId).then(res => {
+      activities.value = res.data;
+      // 澧炲姞isApproval瀛楁
+      activities.value.forEach(item => {
+        if (item.url && item.url.includes("word")) {
+          item.urlTem = item.url.replaceAll("word", "img");
+        } else {
+          item.urlTem = item.url;
+        }
+        if (item.approveNodeStatus === 2) {
+          item.isApproval = "宸查┏鍥�";
+        } else if (item.approveNodeStatus === 1) {
+          item.isApproval = "宸插悓鎰�";
+        } else {
+          item.isApproval = "鏈鎵�";
+        }
+      });
+    });
+  };
+
+  const getDeliveryProductInfoList = () => {
+    const row = currentDelivery.value;
+    if (!row) return [];
+    const normalizeBatchNoList = value => {
+      if (Array.isArray(value)) return value;
+      if (typeof value === "string" && value.includes(",")) {
+        return value
+          .split(",")
+          .map(item => item.trim())
+          .filter(Boolean);
+      }
+      return value ? [value] : [];
+    };
+    const detailList = deliveryProductList.value.length
+      ? deliveryProductList.value
+      : [
+          row.batchNoDetailList,
+          row.batchNoList,
+          row.shippingBatchList,
+          row.shippingInfoDetailList,
+          row.detailList,
+          row.batchDetailList,
+        ].find(value => Array.isArray(value) && value.length);
+    const batchNoList = normalizeBatchNoList(row.batchNo);
+    const toTableRow = (item = {}) => ({
+      batchNo:
+        typeof item === "string" || typeof item === "number"
+          ? item
+          : item.batchNo ?? item.batchNumber ?? row.batchNo ?? "--",
+      productName: item.productName ?? row.productName ?? "--",
+      specificationModel:
+        item.specificationModel ?? item.model ?? row.specificationModel ?? "--",
+      deliveryQuantity:
+        item.deliveryQuantity ??
+        item.quantity ??
+        item.shippingQuantity ??
+        row.deliveryQuantity ??
+        row.quantity ??
+        "--",
+    });
+    if (detailList?.length) {
+      return detailList.map(toTableRow);
+    }
+    if (batchNoList.length) {
+      return batchNoList.map(batchNo => toTableRow({ batchNo }));
+    }
+    return [toTableRow()];
+  };
+  const getApprovalStatusText = status => {
+    const statusMap = {
+      0: "寰呭鏍�",
+      1: "瀹℃牳閫氳繃",
+      2: "瀹℃牳鎷掔粷",
+      3: "瀹℃牳涓�",
+    };
+    return statusMap[status] || "寰呭鏍�";
+  };
+
+  const getProductOptions = () => {
+    return getDept().then(res => {
+      productOptions.value = res.data;
+    });
+  };
+  // 鎻愪氦瀹℃壒
+  const submitForm = status => {
+    const filteredActivities = activities.value.filter(
+      activity => activity.isShen
+    );
+    if (!filteredActivities || filteredActivities.length === 0) {
+      proxy.$modal.msgError("鏈壘鍒板緟瀹℃壒鐨勮妭鐐�");
+      return;
+    }
+    const currentActivity = filteredActivities[0];
+    if (!currentActivity) {
+      proxy.$modal.msgError("鏈壘鍒板緟瀹℃壒鐨勮妭鐐�");
+      return;
+    }
+    currentActivity.approveNodeStatus = status;
+    // 鍒ゆ柇鏄惁涓烘渶鍚庝竴姝�
+    const isLast =
+      activities.value.findIndex(a => a.isShen) === activities.value.length - 1;
+    submitLoading.value = true;
+    updateApproveNode({ ...currentActivity, isLast })
+      .then(() => {
+        proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+        closeDia();
+      })
+      .finally(() => {
+        submitLoading.value = false;
+      });
+  };
+  // 鍏抽棴寮规
+  const closeDia = () => {
+    proxy.resetForm("formRef");
+    dialogFormVisible.value = false;
+    quotationLoading.value = false;
+    currentQuotation.value = {};
+    purchaseLoading.value = false;
+    currentPurchase.value = {};
+    submitLoading.value = false;
+    emit("close");
+  };
+  defineExpose({
+    openDialog,
+  });
+</script>
+
+<style scoped>
+  .node-user {
+    margin: 10px 0;
+    font-size: 16px;
+    font-weight: 600;
+    display: flex;
+    align-items: center;
+    gap: 8px;
+  }
+  .node-status {
+    color: #1890ff;
+    margin-left: 8px;
+    font-size: 14px;
+  }
+  .node-reason {
+    font-size: 15px;
+    color: #333;
+    margin: 10px 0;
+  }
+  .user-avatar {
+    cursor: pointer;
+    width: 30px;
+    height: 30px;
+    border-radius: 50px;
+  }
+  .signImg {
+    cursor: pointer;
+    width: 200px;
+    height: 60px;
+  }
+  .approval-detail :deep(.el-upload) {
+    display: none !important;
+  }
+  .approval-detail :deep(.upload-tip) {
+    display: none !important;
+  }
+</style>
\ No newline at end of file
diff --git a/src/views/collaborativeApproval/approvalProcess/index.vue b/src/views/collaborativeApproval/approvalProcess/index.vue
index 158916c..b3c6daa 100644
--- a/src/views/collaborativeApproval/approvalProcess/index.vue
+++ b/src/views/collaborativeApproval/approvalProcess/index.vue
@@ -124,8 +124,9 @@
     </el-card>
 
     <!-- 寮圭獥缁勪欢 -->
-    <info-form-dia ref="infoFormDia" @close="handleQuery" :approveType="currentApproveType"></info-form-dia>
-    <approval-dia ref="approvalDia" @close="handleQuery" :approveType="currentApproveType"></approval-dia>
+    <info-form-dia ref="infoFormDia" @close="handleDialogClose" :approveType="currentApproveType"></info-form-dia>
+    <approval-dia ref="approvalDia" @close="handleDialogClose" :approveType="currentApproveType"></approval-dia>
+    <view-detail-dia ref="viewDetailDia" @close="handleDialogClose" :approveType="currentApproveType"></view-detail-dia>
     <FileList v-if="fileDialogVisible"
               v-model:visible="fileDialogVisible"
               record-type="approve_process"
@@ -140,6 +141,7 @@
 import { useRoute } from 'vue-router';
 import InfoFormDia from "@/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue";
 import ApprovalDia from "@/views/collaborativeApproval/approvalProcess/components/approvalDia.vue";
+import ViewDetailDia from "@/views/collaborativeApproval/approvalProcess/components/viewDetailDia.vue";
 import {approveProcessDelete, approveProcessListPage} from "@/api/collaborativeApproval/approvalProcess.js";
 import useUserStore from "@/store/modules/user";
 const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
@@ -393,12 +395,17 @@
 });
 const infoFormDia = ref()
 const approvalDia = ref()
+const viewDetailDia = ref()
 const { proxy } = getCurrentInstance()
 
 // 鏌ヨ鍒楄〃
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   page.current = 1;
+  getList();
+};
+
+const handleDialogClose = () => {
   getList();
 };
 
@@ -425,6 +432,10 @@
     page.total = res.data.total;
     // 鏇存柊褰撳墠绫诲瀷鏁伴噺
     typeCounts.value[activeTab.value] = res.data.total;
+    if (res.data.records.length === 0 && page.current > 1) {
+      page.current = Math.max(1, Math.ceil(res.data.total / page.size));
+      getList();
+    }
   }).catch(err => {
     tableLoading.value = false;
   })
@@ -470,7 +481,11 @@
 // 鎵撳紑鏂板妫�楠屽脊妗�
 const openApprovalDia = (type, row) => {
   nextTick(() => {
-    approvalDia.value?.openDialog(type, row)
+    if (type === 'view') {
+      viewDetailDia.value?.openDialog(type, row)
+    } else {
+      approvalDia.value?.openDialog(type, row)
+    }
   })
 };
 
diff --git a/src/views/equipmentManagement/ledger/index.vue b/src/views/equipmentManagement/ledger/index.vue
index af10532..6a70d46 100644
--- a/src/views/equipmentManagement/ledger/index.vue
+++ b/src/views/equipmentManagement/ledger/index.vue
@@ -115,10 +115,10 @@
       <el-descriptions :column="2" border>
         <el-descriptions-item label="璁惧鍚嶇О">{{ detailData.deviceName }}</el-descriptions-item>
         <el-descriptions-item label="瑙勬牸鍨嬪彿">{{ detailData.deviceModel }}</el-descriptions-item>
-        <el-descriptions-item label="璁惧鍝佺墝">{{ detailData.deviceBrand }}</el-descriptions-item>
+        <!-- <el-descriptions-item label="璁惧鍝佺墝">{{ detailData.deviceBrand }}</el-descriptions-item> -->
         <el-descriptions-item label="璁惧绫诲瀷">{{ detailData.type }}</el-descriptions-item>
         <el-descriptions-item label="渚涘簲鍟�">{{ detailData.supplierName }}</el-descriptions-item>
-        <el-descriptions-item label="瀛樻斁浣嶇疆">{{ detailData.storageLocation }}</el-descriptions-item>
+        <!-- <el-descriptions-item label="瀛樻斁浣嶇疆">{{ detailData.storageLocation }}</el-descriptions-item> -->
         <el-descriptions-item label="鍗曚綅">{{ detailData.unit }}</el-descriptions-item>
         <el-descriptions-item label="鏁伴噺">{{ detailData.number }}</el-descriptions-item>
         <el-descriptions-item label="鍚敤鎶樻棫">{{ detailData.isDepr === 1 ? '鏄�' : '鍚�' }}</el-descriptions-item>
@@ -219,10 +219,10 @@
       label: "瑙勬牸鍨嬪彿",
       prop: "deviceModel",
     },
-    {
-      label: "璁惧鍝佺墝",
-      prop: "deviceBrand",
-    },
+    // {
+    //   label: "璁惧鍝佺墝",
+    //   prop: "deviceBrand",
+    // },
     {
       label: "璁惧绫诲瀷",
       prop: "type",
@@ -231,10 +231,10 @@
       label: "渚涘簲鍟�",
       prop: "supplierName",
     },
-    {
-      label: "瀛樻斁浣嶇疆",
-      prop: "storageLocation",
-    },
+    // {
+    //   label: "瀛樻斁浣嶇疆",
+    //   prop: "storageLocation",
+    // },
     {
       label: "鏁伴噺",
       prop: "number",
diff --git a/src/views/equipmentManagement/repair/Modal/RepairDetailModal.vue b/src/views/equipmentManagement/repair/Modal/RepairDetailModal.vue
new file mode 100644
index 0000000..fab6dd7
--- /dev/null
+++ b/src/views/equipmentManagement/repair/Modal/RepairDetailModal.vue
@@ -0,0 +1,96 @@
+<template>
+  <el-dialog
+    v-model="visible"
+    title="璁惧鎶ヤ慨璇︽儏"
+    width="800px"
+    destroy-on-close
+    :close-on-click-modal="false"
+    @close="handleClose"
+  >
+    <el-descriptions border :column="2" class="repair-detail">
+      <el-descriptions-item label="璁惧鍚嶇О">{{ form.deviceName }}</el-descriptions-item>
+      <el-descriptions-item label="瑙勬牸鍨嬪彿">{{ form.deviceModel }}</el-descriptions-item>
+      
+      <el-descriptions-item label="鎶ヤ慨鏃ユ湡">
+        {{ form.repairTime ? dayjs(form.repairTime).format("YYYY-MM-DD") : "" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="鎶ヤ慨浜�">{{ form.repairName }}</el-descriptions-item>
+      
+      <el-descriptions-item label="鎶ヤ慨椤圭洰">{{ form.machineryCategory }}</el-descriptions-item>
+      <el-descriptions-item label="缁翠慨浜�">{{ form.maintenanceName }}</el-descriptions-item>
+      
+      <el-descriptions-item label="鎶ヤ慨鐘舵��">
+        <el-tag v-if="form.status === 2" type="danger">澶辫触</el-tag>
+        <el-tag v-else-if="form.status === 1" type="success">宸查獙鏀�</el-tag>
+        <el-tag v-else-if="form.status === 3" type="info">寰呴獙鏀�</el-tag>
+        <el-tag v-else-if="form.status === 0" type="warning">寰呯淮淇�</el-tag>
+      </el-descriptions-item>
+      <el-descriptions-item label="鏁呴殰鐜拌薄">{{ form.remark }}</el-descriptions-item>
+
+      <!-- 楠屾敹淇℃伅 -->
+      <template v-if="form.status === 1 || form.status === 3">
+        <el-descriptions-item label="楠屾敹浜�">{{ form.acceptanceName }}</el-descriptions-item>
+        <el-descriptions-item label="楠屾敹鏃堕棿">{{ form.acceptanceTime }}</el-descriptions-item>
+        <el-descriptions-item label="楠屾敹澶囨敞" :span="2">{{ form.acceptanceRemark }}</el-descriptions-item>
+      </template>
+
+      <!-- 闄勪欢 -->
+      <el-descriptions-item label="闄勪欢" :span="2">
+        <ImageUpload
+          v-if="form.storageBlobDTOs && form.storageBlobDTOs.length > 0"
+          v-model:file-list="form.storageBlobDTOs"
+          :disabled="true"
+        />
+        <span v-else style="color: #999;">鏃犻檮浠�</span>
+      </el-descriptions-item>
+    </el-descriptions>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="handleClose">鍏抽棴</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref, nextTick } from "vue";
+import dayjs from "dayjs";
+import { getRepairById } from "@/api/equipmentManagement/repair";
+import ImageUpload from "@/components/AttachmentUpload/image/index.vue";
+
+const visible = ref(false);
+const form = ref({});
+
+const openView = async (id) => {
+  visible.value = true;
+  await nextTick();
+  try {
+    const { data } = await getRepairById(id);
+    // 纭繚闄勪欢瀛楁鍚嶇О鍖归厤缁勪欢闇�瑕�
+    data.storageBlobDTOs = data.storageBlobVOs || [];
+    form.value = data || {};
+  } catch (error) {
+    console.error("Failed to load repair details:", error);
+  }
+};
+
+const handleClose = () => {
+  visible.value = false;
+  form.value = {};
+};
+
+defineExpose({
+  openView,
+});
+</script>
+
+<style scoped>
+.repair-detail :deep(.el-descriptions__label) {
+  width: 120px;
+  justify-content: flex-end;
+}
+.repair-detail :deep(.el-upload) {
+  display: none !important;
+}
+</style>
diff --git a/src/views/equipmentManagement/repair/index.vue b/src/views/equipmentManagement/repair/index.vue
index f1573cb..7cbff88 100644
--- a/src/views/equipmentManagement/repair/index.vue
+++ b/src/views/equipmentManagement/repair/index.vue
@@ -136,6 +136,7 @@
     </div>
     <RepairModal ref="repairModalRef"
                  @ok="getTableData" />
+    <RepairDetailModal ref="repairDetailModalRef" />
     <MaintainModal ref="maintainModalRef"
                    @ok="getTableData" />
     <AcceptanceModal ref="acceptanceModalRef"
@@ -158,6 +159,7 @@
   import { usePaginationApi } from "@/hooks/usePaginationApi";
   import { getRepairPage, delRepair } from "@/api/equipmentManagement/repair";
   import RepairModal from "./Modal/RepairModal.vue";
+  import RepairDetailModal from "./Modal/RepairDetailModal.vue";
   import { ElMessageBox, ElMessage } from "element-plus";
   import dayjs from "dayjs";
   import MaintainModal from "./Modal/MaintainModal.vue";
@@ -174,6 +176,7 @@
 
   // 妯℃�佹瀹炰緥
   const repairModalRef = ref();
+  const repairDetailModalRef = ref();
   const maintainModalRef = ref();
   const acceptanceModalRef = ref();
 
@@ -274,14 +277,14 @@
     return multipleList.value.some(item => item.status === 1);
   });
 
-  // 鏂板鎶ヤ慨
+  // 鏂板璁惧鎶ヤ慨
   const addRepair = () => {
     repairModalRef.value.openAdd();
   };
 
-  // 璇︽儏鏌ョ湅
+  // 鏌ョ湅璇︽儏
   const viewRepair = id => {
-    repairModalRef.value.openView(id);
+    repairDetailModalRef.value.openView(id);
   };
 
   // 缂栬緫鎶ヤ慨
diff --git a/src/views/inventoryManagement/stockManagement/New.vue b/src/views/inventoryManagement/stockManagement/New.vue
index 3d4b398..5608cd0 100644
--- a/src/views/inventoryManagement/stockManagement/New.vue
+++ b/src/views/inventoryManagement/stockManagement/New.vue
@@ -52,8 +52,9 @@
         <el-form-item label="搴撳瓨鏁伴噺"
                       prop="qualitity">
           <el-input-number v-model="formState.qualitity"
-                           :step="1"
-                           :min="1"
+                           :step="0.00000001"
+                           :min="0"
+                           :precision="8"
                            style="width: 100%" />
         </el-form-item>
         <el-form-item label="鎵瑰彿"
@@ -65,8 +66,9 @@
                       label="搴撳瓨棰勮鏁伴噺"
                       prop="warnNum">
           <el-input-number v-model="formState.warnNum"
-                           :step="1"
+                           :step="0.00000001"
                            :min="0"
+                           :precision="8"
                            :max="formState.qualitity"
                            style="width: 100%" />
         </el-form-item>
@@ -119,7 +121,7 @@
     productName: "",
     productModelName: "",
     unit: "",
-    type: undefined,
+    type: "qualified",
     qualitity: 0,
     batchNo: null,
     warnNum: 0,
@@ -157,7 +159,7 @@
       productName: "",
       productModelName: "",
       unit: "",
-      type: undefined,
+      type: "qualified",
       qualitity: 0,
       batchNo: null,
       warnNum: 0,
diff --git a/src/views/monitor/logininfor/index.vue b/src/views/monitor/logininfor/index.vue
index aa42b52..5e969f7 100644
--- a/src/views/monitor/logininfor/index.vue
+++ b/src/views/monitor/logininfor/index.vue
@@ -95,7 +95,11 @@
 
       <el-table ref="logininforRef" v-loading="loading" :data="logininforList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange">
          <el-table-column type="selection" width="55" align="center" />
-         <el-table-column label="璁块棶缂栧彿" align="center" prop="infoId" />
+         <el-table-column label="璁块棶缂栧彿" align="center">
+            <template #default="scope">
+               <span>{{ (queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1 }}</span>
+            </template>
+         </el-table-column>
          <el-table-column label="鐢ㄦ埛鍚嶇О" align="center" prop="userName" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']" />
          <el-table-column label="鍦板潃" align="center" prop="ipaddr" :show-overflow-tooltip="true" />
          <el-table-column label="鐧诲綍鍦扮偣" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
diff --git a/src/views/monitor/operlog/index.vue b/src/views/monitor/operlog/index.vue
index 53979da..70ae4cd 100644
--- a/src/views/monitor/operlog/index.vue
+++ b/src/views/monitor/operlog/index.vue
@@ -109,7 +109,11 @@
 
       <el-table ref="operlogRef" v-loading="loading" :data="operlogList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange">
          <el-table-column type="selection" width="50" align="center" />
-         <el-table-column label="鏃ュ織缂栧彿" align="center" prop="operId" />
+         <el-table-column label="鏃ュ織缂栧彿" align="center">
+            <template #default="scope">
+               <span>{{ (queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1 }}</span>
+            </template>
+         </el-table-column>
          <el-table-column label="绯荤粺妯″潡" align="center" prop="title" :show-overflow-tooltip="true" />
          <el-table-column label="鎿嶄綔绫诲瀷" align="center" prop="businessType">
             <template #default="scope">
@@ -145,6 +149,7 @@
          :total="total"
          v-model:page="queryParams.pageNum"
          v-model:limit="queryParams.pageSize"
+         :page-sizes="[10, 20, 30, 50, 100, 200]"
          @pagination="getList"
       />
 
diff --git a/src/views/procurementManagement/paymentLedger/index.vue b/src/views/procurementManagement/paymentLedger/index.vue
index b913032..19816a4 100644
--- a/src/views/procurementManagement/paymentLedger/index.vue
+++ b/src/views/procurementManagement/paymentLedger/index.vue
@@ -202,6 +202,14 @@
     page.size = obj.limit;
     getList();
   };
+  // 灏嗘湭鍏ュ簱閲戦骞跺叆宸插叆搴撻噾棰濓紝鏈叆搴撻噾棰濈疆0
+  const mergeUnshippedAmount = row => {
+    if (row && row.unshippedAmount) {
+      row.shippedAmount = (Number(row.shippedAmount) || 0) + (Number(row.unshippedAmount) || 0);
+      row.unshippedAmount = 0;
+    }
+    return row;
+  };
   const getList = () => {
     tableLoading.value = true;
     paymentLedgerList({
@@ -210,7 +218,7 @@
     }).then(res => {
       let result = res.data;
       tableLoading.value = false;
-      tableData.value = result.records || [];
+      tableData.value = (result.records || []).map(mergeUnshippedAmount);
       total.value = result.total || 0;
       if (tableData.value.length > 0) {
         currentSupplierId.value = tableData.value[0].supplierId;
@@ -231,11 +239,11 @@
         tableLoadingSon.value = false;
         let result = res.data;
         if (Array.isArray(result)) {
-          tableDataSon.value = result;
+          tableDataSon.value = result.map(mergeUnshippedAmount);
           sonPage.total = result.length;
           handlePagination({ page: sonPage.current, limit: sonPage.size });
         } else {
-          originalTableDataSon.value = result.records || [];
+          originalTableDataSon.value = (result.records || []).map(mergeUnshippedAmount);
           sonPage.total = result.total || 0;
         }
       })
diff --git a/src/views/procurementManagement/procurementLedger/index.vue b/src/views/procurementManagement/procurementLedger/index.vue
index 6167e6c..f59ed95 100644
--- a/src/views/procurementManagement/procurementLedger/index.vue
+++ b/src/views/procurementManagement/procurementLedger/index.vue
@@ -90,7 +90,7 @@
         <el-table-column label="鍏ュ簱鐘舵��" prop="stockInStatus" width="100" show-overflow-tooltip>
           <template #default="scope">
             <el-tag :type="getStockInStatusType(scope.row.stockInStatus)" size="small">
-              {{ scope.row.stockInStatus || '--' }}
+              {{ formatStockInStatus(scope.row.stockInStatus) || '--' }}
             </el-tag>
           </template>
         </el-table-column>
@@ -137,7 +137,7 @@
           <el-col :span="12">
             <el-form-item label="渚涘簲鍟嗗悕绉帮細" prop="supplierId">
               <el-select v-model="form.supplierId" placeholder="璇烽�夋嫨" filterable clearable>
-                <el-option v-for="item in supplierList" :key="item.id" :label="item.supplierName" :value="item.id">{{ item.supplierType ? item.supplierName + '---' + item.supplierType : item.supplierName }}</el-option>
+<el-option v-for="item in supplierList" :key="item.id" :label="item.supplierName" :value="item.id">{{ item.supplierName }}</el-option>
               </el-select>
             </el-form-item>
           </el-col>
@@ -480,11 +480,17 @@
   return typeMap[status] || "";
 };
 
+// 鍏ュ簱鐘舵�佹樉绀猴細鍏ュ簱涓篃灞曠ず涓哄畬鍏ㄥ叆搴�
+const formatStockInStatus = status => {
+  if (status === "鍏ュ簱涓�") return "瀹屽叏鍏ュ簱";
+  return status;
+};
+
 // 鑾峰彇鍏ュ簱鐘舵�佹爣绛剧被鍨�
 const getStockInStatusType = status => {
   const typeMap = {
     "寰呭叆搴�": "info", // 寰呭叆搴� - 鐏拌壊
-    "鍏ュ簱涓�": "warning", // 鍏ュ簱涓� - 姗欒壊
+    "鍏ュ簱涓�": "success", // 鍏ュ簱涓� - 缁胯壊
     "瀹屽叏鍏ュ簱": "success", // 瀹屽叏鍏ュ簱 - 缁胯壊
   };
   return typeMap[status] || "";
diff --git a/src/views/procurementManagement/procurementPlan/index.vue b/src/views/procurementManagement/procurementPlan/index.vue
index 42a1bcf..7280a6e 100644
--- a/src/views/procurementManagement/procurementPlan/index.vue
+++ b/src/views/procurementManagement/procurementPlan/index.vue
@@ -183,7 +183,7 @@
           <div class="checkbox-group">
             <el-checkbox v-model="formData.summaryMaterial">鐗╂枡</el-checkbox>
             <el-checkbox v-model="formData.summaryAuxAttributes">杈呭姪灞炴��</el-checkbox>
-            <el-checkbox v-model="formData.summaryDemandDate">闇�姹傛棩鏈�</el-checkbox>
+            <el-checkbox v-model="formData.summaryDemandDate">浜よ揣鏃ユ湡</el-checkbox>
           </div>
         </div>
 
diff --git a/src/views/productionManagement/processRoute/processRouteItem/index.vue b/src/views/productionManagement/processRoute/processRouteItem/index.vue
index 41e2109..befab45 100644
--- a/src/views/productionManagement/processRoute/processRouteItem/index.vue
+++ b/src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -343,7 +343,7 @@
                                      :step="1"
                                      controls-position="right"
                                      style="width: 100%"
-                                     @change="handleUnitQuantityChange"
+                                     @change="handleUnitQuantityChange(row)"
                                      :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)" />
                   </el-form-item>
                 </template>
@@ -1196,52 +1196,6 @@
     });
   };
 
-  const toQuantityNumber = value => {
-    const numberValue = Number(value);
-    if (!Number.isFinite(numberValue)) {
-      return 0;
-    }
-    return Number(numberValue.toFixed(4));
-  };
-
-  const syncDemandedQuantityTree = (items, parentDemandedQuantity = null) => {
-    items.forEach(item => {
-      if (parentDemandedQuantity !== null) {
-        item.demandedQuantity = toQuantityNumber(
-          parentDemandedQuantity * toQuantityNumber(item.unitQuantity)
-        );
-      }
-
-      if (Array.isArray(item.children) && item.children.length > 0) {
-        syncDemandedQuantityTree(
-          item.children,
-          toQuantityNumber(item.demandedQuantity)
-        );
-      }
-    });
-  };
-
-  const recalculateDemandedQuantities = () => {
-    if (pageType.value !== "order") {
-      return;
-    }
-
-    const rootDemandedQuantity = routeInfo.value.quantity;
-    if (
-      rootDemandedQuantity === undefined ||
-      rootDemandedQuantity === null ||
-      rootDemandedQuantity === ""
-    ) {
-      syncDemandedQuantityTree(bomDataValue.value.dataList);
-      return;
-    }
-
-    syncDemandedQuantityTree(
-      bomDataValue.value.dataList,
-      toQuantityNumber(rootDemandedQuantity)
-    );
-  };
-
   const processChange = value => {
     processOptions.value.forEach(item => {
       if (item.id == value) {
@@ -1266,6 +1220,24 @@
       }
     }
     return null;
+  };
+
+  const toQuantityNumber = value => {
+    const numberValue = Number(value);
+    if (!Number.isFinite(numberValue)) {
+      return 0;
+    }
+    return Number(numberValue.toFixed(4));
+  };
+
+  const handleUnitQuantityChange = row => {
+    if (pageType.value !== "order") {
+      return;
+    }
+
+    row.demandedQuantity = toQuantityNumber(
+      toQuantityNumber(row.unitQuantity) * toQuantityNumber(routeInfo.value.quantity)
+    );
   };
 
   const handleBomProcessChange = (row, value) => {
@@ -1301,7 +1273,6 @@
       );
       bomDataValue.value.dataList = data || [];
       normalizeTreeData(bomDataValue.value.dataList);
-      recalculateDemandedQuantities();
     } catch (err) {
       console.error("鑾峰彇BOM鏁版嵁澶辫触锛�", err);
     }
@@ -1397,10 +1368,6 @@
     });
   };
 
-  const handleUnitQuantityChange = () => {
-    recalculateDemandedQuantities();
-  };
-
   const addchildItem = (item, tempId) => {
     if (item.tempId === tempId) {
       if (!item.children) {
@@ -1424,7 +1391,6 @@
         unit: "",
         tempId: new Date().getTime(),
       });
-      recalculateDemandedQuantities();
       return true;
     }
     if (item.children && item.children.length > 0) {
@@ -1461,7 +1427,6 @@
           children: [],
           tempId: new Date().getTime(),
         });
-        recalculateDemandedQuantities();
         return;
       }
       addchildItem(item, tempId);
@@ -1556,7 +1521,6 @@
     console.log(bomDataValue.value.dataList, "bomDataValue.value.dataList");
 
     normalizeTreeData(bomDataValue.value.dataList);
-    recalculateDemandedQuantities();
 
     const valid = validateAllBom();
     if (valid) {
@@ -1859,4 +1823,4 @@
     line-height: 1.5;
     word-break: break-all;
   }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/views/productionManagement/productStructure/Detail/index.vue b/src/views/productionManagement/productStructure/Detail/index.vue
index ec18ddf..6899eb0 100644
--- a/src/views/productionManagement/productStructure/Detail/index.vue
+++ b/src/views/productionManagement/productStructure/Detail/index.vue
@@ -22,16 +22,34 @@
               border
               :preserve-expanded-content="false"
               :default-expand-all="true"
-              style="width: 100%">
+              style="width: 100%"
+              @expand-change="handleOuterExpandChange">
       <el-table-column type="expand">
         <template #default="props">
           <el-form ref="form"
                    :model="dataValue">
-            <el-table :data="dataValue.dataList"
+            <div v-if="dataValue.isEdit"
+                 class="drag-tip">
+              <el-icon><Rank /></el-icon>
+              <span>鎷栧姩鎵嬫焺璋冩暣缁撴瀯:鍚屽眰绾х洿鎺ユ嫋鍔ㄦ帓搴�;鎷栧埌鏌愯姝d笅鏂规垚涓哄叾瀛愮骇;鎷栧埌鍒楄〃鏈�鍓嶆垚涓轰竴绾�</span>
+            </div>
+            <el-table ref="tableRef"
+                      :data="dataValue.dataList"
                       row-key="tempId"
                       default-expand-all
                       :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
+                      @expand-change="handleTreeExpandChange"
                       style="width: 100%">
+              <el-table-column v-if="dataValue.isEdit"
+                               width="44"
+                               align="center">
+                <template #default="{ row }">
+                  <div class="drag-handle"
+                       title="鎷栧姩璋冩暣椤哄簭:鎷栧埌鏌愯姝d笅鏂规垚涓哄叾瀛愮骇,鎷栧埌鍒楄〃鏈�鍓嶆垚涓轰竴绾�">
+                    <el-icon><Rank /></el-icon>
+                  </div>
+                </template>
+              </el-table-column>
               <el-table-column prop="productName"
                                label="浜у搧" />
               <el-table-column prop="model"
@@ -59,18 +77,20 @@
                   <el-form-item v-if="dataValue.isEdit"
                                 :rules="dataValue.dataList.some(item => (item as any).tempId === row.tempId) ? [] : [{ required: true, message: '璇烽�夋嫨娑堣�楀伐搴�', trigger: 'change' }]"
                                 style="margin: 0">
-                    <el-select v-model="row.processId"
-                               placeholder="璇烽�夋嫨"
-                               filterable
-                               clearable
-                               style="width: 100%"
-                               @change="value => handleProcessChange(row, value)"
-                               :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)">
-                      <el-option v-for="item in dataValue.processOptions"
-                                 :key="item.id"
-                                 :label="item.name"
-                                 :value="item.id" />
-                    </el-select>
+                    <el-input v-model="row.processName"
+                              readonly
+                              placeholder="璇烽�夋嫨娑堣�楀伐搴�"
+                              clearable
+                              style="width: 100%"
+                              :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)"
+                              @click="openProcessDialog(row, $event)"
+                              @clear="clearProcess(row)">
+                      <template #append>
+                        <el-button icon="Search"
+                                   :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)"
+                                   @click="openProcessDialog(row)" />
+                      </template>
+                    </el-input>
                   </el-form-item>
                 </template>
               </el-table-column>
@@ -150,6 +170,9 @@
                            v-model:model-value="dataValue.showProductDialog"
                            :single="true"
                            @confirm="handleProduct" />
+    <process-select-dialog v-if="dataValue.showProcessDialog"
+                           v-model:model-value="dataValue.showProcessDialog"
+                           @confirm="handleProcessConfirm" />
   </div>
 </template>
 
@@ -158,18 +181,23 @@
     computed,
     defineAsyncComponent,
     defineComponent,
+    nextTick,
+    onBeforeUnmount,
     onMounted,
     reactive,
     ref,
+    watch,
   } from "vue";
   import {
     queryList,
     addBomDetail,
   } from "@/api/productionManagement/productStructure.js";
   import { listProcessBom } from "@/api/productionManagement/productionOrder.js";
-  import { list } from "@/api/productionManagement/productionProcess";
+  import { listAll } from "@/api/productionManagement/productionProcess";
   import { ElMessage } from "element-plus";
   import { useRoute, useRouter } from "vue-router";
+  import Sortable from "sortablejs";
+  import { Rank } from "@element-plus/icons-vue";
 
   defineComponent({
     name: "StructureEdit",
@@ -178,8 +206,16 @@
   const ProductSelectDialog = defineAsyncComponent(
     () => import("@/views/basicData/product/ProductSelectDialog.vue")
   );
+  const ProcessSelectDialog = defineAsyncComponent(
+    () =>
+      import(
+        "@/views/productionManagement/productionProcess/ProcessSelectDialog.vue"
+      )
+  );
   const emit = defineEmits(["update:router"]);
   const form = ref();
+  const tableRef = ref();
+  let tableSortable = null;
 
   const route = useRoute();
   const router = useRouter();
@@ -210,8 +246,10 @@
     productOptions: [],
     processOptions: [],
     showProductDialog: false,
+    showProcessDialog: false,
     currentRowIndex: null,
     currentRowName: null,
+    currentProcessRow: null,
     loading: false,
     isEdit: false,
   });
@@ -303,6 +341,155 @@
     syncDemandedQuantityTree(dataValue.dataList);
   };
 
+  // ===== 鎷栨嫿鎺掑簭 =====
+  // 鎸夊綋鍓� DOM 琛岄『搴忓睍寮�鎴愭墎骞冲垪琛�(缂栬緫妯″紡涓嬪己鍒跺叏閮ㄥ睍寮�,淇濊瘉琛岄『搴忎笌鏁版嵁涓�鑷�)
+  const buildFlatTree = () => {
+    const flat = [];
+    const walk = (items, depth, parentNode) => {
+      items.forEach(node => {
+        flat.push({ node, depth, parentArray: items, parentNode });
+        if (Array.isArray(node.children) && node.children.length > 0) {
+          walk(node.children, depth + 1, node);
+        }
+      });
+    };
+    walk(dataValue.dataList, 0, null);
+    return flat;
+  };
+
+  const removeNodeByTempId = (items, tempId) => {
+    for (let i = 0; i < items.length; i++) {
+      if (String(items[i].tempId) === String(tempId)) {
+        items.splice(i, 1);
+        return true;
+      }
+      if (
+        Array.isArray(items[i].children) &&
+        removeNodeByTempId(items[i].children, tempId)
+      ) {
+        return true;
+      }
+    }
+    return false;
+  };
+
+  const updateParentRef = (node, parent) => {
+    node.parentTempId = parent?.tempId ?? "";
+    node.parentId = parent?.id ?? "";
+  };
+
+  const handleSortEnd = evt => {
+    const { oldIndex, newIndex } = evt;
+    if (
+      typeof oldIndex !== "number" ||
+      typeof newIndex !== "number" ||
+      oldIndex === newIndex
+    ) {
+      return;
+    }
+    const flat = buildFlatTree();
+    if (oldIndex < 0 || oldIndex >= flat.length) return;
+    const dragged = flat[oldIndex];
+
+    // 鎷栨斁鍚庝綅浜庤鎷栬涓婃柟涓�琛岀殑鏁版嵁(鎸夋渶缁� DOM 椤哄簭鎹㈢畻鍒板師鎵佸钩鍒楄〃)
+    let aEntry = null;
+    if (newIndex > oldIndex) {
+      aEntry = flat[newIndex];
+    } else if (newIndex > 0) {
+      aEntry = flat[newIndex - 1];
+    }
+
+    if (!aEntry) {
+      // 鎷栧埌鍒楄〃鏈�鍓� 鈫� 鎴愪负涓�绾�
+      removeNodeByTempId(dataValue.dataList, dragged.node.tempId);
+      dataValue.dataList.unshift(dragged.node);
+      updateParentRef(dragged.node, null);
+    } else if (aEntry.depth < dragged.depth) {
+      // 鎷栧埌鏌愯姝d笅鏂� 鈫� 鎴愪负璇ヨ鐨勫瓙绾�
+      removeNodeByTempId(dataValue.dataList, dragged.node.tempId);
+      if (!aEntry.node.children) {
+        aEntry.node.children = [];
+      }
+      aEntry.node.children.push(dragged.node);
+      updateParentRef(dragged.node, aEntry.node);
+    } else {
+      // 鍚岀骇鎻掑叆:鍚戜笂鎵剧涓�涓繁搴︿笉瓒呰繃琚嫋琛岀殑鑺傜偣
+      let i = flat.indexOf(aEntry);
+      while (i >= 0 && flat[i].depth > dragged.depth) {
+        i--;
+      }
+      const eEntry = flat[i];
+      // 鎷栧洖鑷繁鐨勫瓙鏍戝唴 鈫� 涓嶅鐞�(闃叉褰㈡垚寰幆)
+      if (!eEntry || eEntry.node.tempId === dragged.node.tempId) {
+        return;
+      }
+      removeNodeByTempId(dataValue.dataList, dragged.node.tempId);
+      const siblings = eEntry.parentArray;
+      const idx = siblings.indexOf(eEntry.node);
+      siblings.splice(idx + 1, 0, dragged.node);
+      updateParentRef(dragged.node, eEntry.parentNode);
+    }
+
+    recalculateDemandedQuantities();
+  };
+
+  const initSortable = () => {
+    if (!tableRef.value) return;
+    const tbody = tableRef.value.$el.querySelector(".el-table__body tbody");
+    if (!tbody) return;
+    tableSortable?.destroy();
+    tableSortable = new Sortable(tbody, {
+      animation: 150,
+      handle: ".drag-handle",
+      disabled: !dataValue.isEdit,
+      ghostClass: "sortable-ghost",
+      onEnd: handleSortEnd,
+    });
+  };
+
+  const expandAllTree = () => {
+    const table = tableRef.value;
+    if (!table) return;
+    const walk = items => {
+      items.forEach(item => {
+        if (Array.isArray(item.children) && item.children.length > 0) {
+          table.toggleRowExpansion(item, true);
+          walk(item.children);
+        }
+      });
+    };
+    walk(dataValue.dataList);
+  };
+
+  // 缂栬緫妯″紡涓嬩笉鍏佽鎶樺彔,淇濊瘉鎷栨嫿鏃� DOM 琛岄『搴忎笌 dataList 涓�鑷�
+  const handleTreeExpandChange = (row, expanded) => {
+    if (dataValue.isEdit && expanded === false) {
+      nextTick(() => {
+        tableRef.value?.toggleRowExpansion(row, true);
+      });
+    }
+  };
+
+  // 澶栧眰琛屾姌鍙犲悗鍐呭浼氶攢姣�,閲嶆柊灞曞紑鏃堕噸鏂板垵濮嬪寲鎷栨嫿
+  const handleOuterExpandChange = (row, expandedRows) => {
+    if (Array.isArray(expandedRows) && expandedRows.some(r => r === row)) {
+      nextTick(() => initSortable());
+    }
+  };
+
+  watch(
+    () => dataValue.isEdit,
+    val => {
+      tableSortable?.option("disabled", !val);
+      if (val) {
+        nextTick(() => {
+          initSortable();
+          expandAllTree();
+        });
+      }
+    }
+  );
+
   const buildSubmitTree = (items: any[]) => {
     return items.map((item: any) => {
       const current = { ...item };
@@ -346,6 +533,35 @@
     }
   };
 
+  const openProcessDialog = (row: any, event?: Event) => {
+    // 鐐瑰嚮娓呴櫎鎸夐挳鏃朵笉寮圭獥
+    const target = event?.target as HTMLElement | undefined;
+    if (target && target.closest(".el-input__clear")) {
+      return;
+    }
+    dataValue.currentProcessRow = row;
+    dataValue.showProcessDialog = true;
+  };
+
+  const clearProcess = (row: any) => {
+    row.processId = "";
+    row.operationId = "";
+    row.processName = "";
+    row.operationName = "";
+  };
+
+  const handleProcessConfirm = (processRow: any) => {
+    const row = dataValue.currentProcessRow;
+    if (!row) {
+      return;
+    }
+    row.processId = processRow.id;
+    row.operationId = processRow.id;
+    row.processName = processRow.name;
+    row.operationName = processRow.name;
+    handleProcessChange(row, processRow.id);
+  };
+
   const handleUnitQuantityChange = () => {
     recalculateDemandedQuantities();
   };
@@ -382,7 +598,7 @@
   };
 
   const fetchProcessOptions = async () => {
-    const { data } = await list({});
+    const { data } = await listAll();
     console.log(data, "dataValue.dataList");
     dataValue.processOptions = normalizeListData(data);
   };
@@ -693,5 +909,38 @@
     // 鍏堝姞杞藉伐搴忛�夐」锛屽啀鍔犺浇鏁版嵁锛岀‘淇漞l-select鑳藉姝g‘鍥炴樉
     await fetchProcessOptions();
     await fetchData();
+    await nextTick();
+    initSortable();
   });
-</script>
\ No newline at end of file
+
+  onBeforeUnmount(() => {
+    tableSortable?.destroy();
+    tableSortable = null;
+  });
+</script>
+
+<style scoped>
+  .drag-tip {
+    display: flex;
+    align-items: center;
+    gap: 4px;
+    margin-bottom: 8px;
+    font-size: 12px;
+    color: #909399;
+  }
+
+  .drag-handle {
+    cursor: move;
+    color: #909399;
+    text-align: center;
+  }
+
+  .drag-handle:hover {
+    color: var(--el-color-primary);
+  }
+
+  :deep(.sortable-ghost) {
+    opacity: 0.6;
+    background-color: #f5f7fa !important;
+  }
+</style>
\ No newline at end of file
diff --git a/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue b/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
index 6c175f9..9067dde 100644
--- a/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
+++ b/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
@@ -2,18 +2,25 @@
   <div>
     <el-dialog v-model="dialogVisible"
                title="棰嗘枡鍙拌处"
-               width="1350px"
+               width="1450px"
                @close="handleClose">
       <div class="material-toolbar">
+        <el-button type="primary"
+                   plain
+                   @click="handleAddPurchase">鏂板閲囪喘</el-button>
         <el-button type="primary"
                    @click="handleAddMaterialRow">鏂板</el-button>
       </div>
       <el-table v-loading="materialTableLoading"
                 :data="materialTableData"
                 border
+                @selection-change="handleMaterialSelectionChange"
                 row-key="tempId">
+        <el-table-column align="center"
+                         type="selection"
+                         width="55" />
         <el-table-column label="宸ュ簭鍚嶇О"
-                         min-width="80">
+                         min-width="100">
           <template #default="{ row }">
             <span v-if="row.bom === true">{{ row.operationName || "-" }}</span>
             <el-select v-else
@@ -43,13 +50,13 @@
           </template>
         </el-table-column>
         <el-table-column label="鍘熸枡鍨嬪彿"
-                         min-width="120">
+                         min-width="110">
           <template #default="{ row }">
             {{ row.materialModel || "-" }}
           </template>
         </el-table-column>
         <!-- 鎵瑰彿澶氶�� -->
-        <el-table-column min-width="200"
+        <el-table-column min-width="180"
                          label="鎵瑰彿">
           <template #default="{ row }">
             <el-select v-model="row.batchNo"
@@ -66,7 +73,7 @@
           </template>
         </el-table-column>
         <el-table-column label="闇�姹傛暟閲�"
-                         min-width="100">
+                         min-width="95">
           <template #default="{ row }">
             <span v-if="row.bom === true">{{ stripTrailingZeros(row.demandedQuantity) ?? "-" }}</span>
             <el-input-number v-else
@@ -78,6 +85,27 @@
                              :parser="value => parseFloat(value) || 0"
                              style="width: 100%;"
                              @change="val => handleRequiredQtyChange(row, val)" />
+          </template>
+        </el-table-column>
+        <el-table-column label="搴撳瓨浣欓噺"
+                         min-width="90">
+          <template #default="{ row }">
+            <span v-if="row.stockQty === null">-</span>
+            <span v-else>{{ stripTrailingZeros(row.stockQty) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="宸查鏁伴噺"
+                         min-width="90">
+          <template #default="{ row }">
+            {{ stripTrailingZeros(row.pickedQty) ?? 0 }}
+          </template>
+        </el-table-column>
+        <el-table-column label="鍓╀綑鍙"
+                         min-width="90">
+          <template #default="{ row }">
+            <span :class="{ 'remaining-empty': Number(row.remainingQty) <= 0 }">
+              {{ stripTrailingZeros(row.remainingQty) ?? 0 }}
+            </span>
           </template>
         </el-table-column>
         <el-table-column label="璁¢噺鍗曚綅"
@@ -102,7 +130,7 @@
                          width="90"
                          fixed="right">
           <template #default="{ $index, row }">
-            <el-button v-if="row.bom !== true"
+            <el-button v-if="row.bom !== true || Number(row.pickQty) > 0"
                        type="danger"
                        link
                        @click="handleDeleteMaterialRow($index)">鍒犻櫎</el-button>
@@ -122,6 +150,8 @@
     <ProductSelectDialog v-model="materialProductDialogVisible"
                          @confirm="handleMaterialProductConfirm"
                          single />
+    <PurchaseLedgerAddDialog ref="purchaseLedgerAddDialogRef"
+                             v-model="purchaseLedgerDialogVisible" />
     <!-- request-url="/stockInventory/rawMaterials" -->
   </div>
 </template>
@@ -130,18 +160,14 @@
   import { computed, ref, watch } from "vue";
   import { ElMessage } from "element-plus";
   import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
-  import {
-    findProductProcessRouteItemList,
-    listMain,
-  } from "@/api/productionManagement/productProcessRoute.js";
+  import PurchaseLedgerAddDialog from "./PurchaseLedgerAddDialog.vue";
+  import { findProductProcessRouteItemList } from "@/api/productionManagement/productProcessRoute.js";
+  import { getStockInventoryByModelId } from "@/api/inventoryManagement/stockInventory.js";
   import {
     listMaterialPickingDetail,
     listMaterialPickingBom,
-    listMaterialPickingLedger,
     saveMaterialPickingLedger,
-    updateMaterialPickingLedger,
   } from "@/api/productionManagement/productionOrder.js";
-  import { queryList2 } from "@/api/productionManagement/productStructure.js";
 
   const props = defineProps({
     modelValue: { type: Boolean, default: false },
@@ -158,64 +184,67 @@
   const materialTableLoading = ref(false);
   const materialSaving = ref(false);
   const materialTableData = ref([]);
+  const selectedMaterialRows = ref([]);
+  const purchaseLedgerDialogVisible = ref(false);
+  const purchaseLedgerAddDialogRef = ref(null);
 
   const isSaveDisabled = computed(() => {
+    if (materialTableLoading.value) return true;
     if (materialTableData.value.length === 0) return true;
-    return !materialTableData.value.some(row => {
-      // 妫�鏌ユ槸鍚︽湁浠讳綍鐢ㄦ埛杈撳叆鍐呭
-      const hasBatch = Array.isArray(row.batchNo) && row.batchNo.length > 0;
-      const hasPickQty =
-        row.pickQty !== null && row.pickQty !== undefined && row.pickQty !== 0;
-
-      if (row.bom) {
-        // 瀵逛簬鏉ヨ嚜BOM鐨勮锛岃緭鍏ユ鍙湁鈥滄壒鍙封�濆拰鈥滈鐢ㄦ暟閲忊��
-        return hasBatch || hasPickQty;
-      } else {
-        // 瀵逛簬鏂板琛岋紝杈撳叆妗嗗寘鎷�滃伐搴忊�濄�佲�滃師鏂欌�濄�佲�滈渶姹傛暟閲忊�濄�佲�滄壒鍙封�濆拰鈥滈鐢ㄦ暟閲忊��
-        const hasOperation = !!row.operationName;
-        const hasMaterial = !!row.materialName;
-        const hasDemanded =
-          row.demandedQuantity !== null &&
-          row.demandedQuantity !== undefined &&
-          row.demandedQuantity !== 0;
-        return (
-          hasBatch || hasPickQty || hasOperation || hasMaterial || hasDemanded
-        );
-      }
-    });
+    // 鍙湁瀛樺湪鏈棰嗙敤鏁伴噺澶т簬0鐨勮鎵嶅厑璁镐繚瀛�
+    return !materialTableData.value.some(item => Number(item.pickQty) > 0);
   });
 
   const processOptions = ref([]);
   const currentMaterialSelectRowIndex = ref(-1);
   let materialTempId = 0;
 
-  const getDefaultBatchNo = batchNoList => {
-    if (!Array.isArray(batchNoList) || batchNoList.length === 0) return [];
-    return [batchNoList[0]];
+  const toNumber = val => {
+    const n = Number(val);
+    return Number.isFinite(n) ? n : 0;
+  };
+
+  // 淇濈暀6浣嶅皬鏁帮紝閬垮厤娴偣璇樊
+  const roundQuantity = val => Math.round(toNumber(val) * 1e6) / 1e6;
+
+  // 闇�姹傛暟閲忓垎缁勯敭锛氫笌鍚庣淇濇寔涓�鑷达紙宸ュ簭ID浼樺厛锛岀己澶辨椂鐢ㄥ伐搴忓悕绉帮級
+  const buildDemandKey = (technologyOperationId, operationName, productModelId) =>
+    `${technologyOperationId != null && technologyOperationId !== undefined ? "T" + technologyOperationId : "N" + (operationName || "")}#${productModelId ?? ""}`;
+
+  const stripTrailingZeros = val => {
+    if (val === null || val === undefined || val === "") return "0";
+    const str = String(val);
+    if (str.includes(".")) {
+      return parseFloat(str).toString();
+    }
+    return str;
   };
 
   const createMaterialRow = (row = {}) => {
     const batchNoList = Array.isArray(row.batchNoList) ? row.batchNoList : [];
-    const batchNo = row.batchNo
-      ? typeof row.batchNo === "string"
-        ? row.batchNo.split(",")
-        : row.batchNo
-      : getDefaultBatchNo(batchNoList);
+    const pickedEntry = row.pickedEntry || null;
+    const pickedQty = pickedEntry ? roundQuantity(pickedEntry.pickedQty) : 0;
+    const returnedQty = pickedEntry ? roundQuantity(pickedEntry.returnedQty) : 0;
+    const demandedQuantity = roundQuantity(row.demandedQuantity ?? 0);
+    const remainingQty = Math.max(roundQuantity(demandedQuantity - pickedQty + returnedQty), 0);
 
     return {
-      tempId: row.id || `temp_${++materialTempId}`,
-      id: row.id,
-      processId: row.processId || row.technologyOperationId,
-      technologyOperationId: row.technologyOperationId || row.processId,
-      operationName: row.operationName || "",
+      tempId: `temp_${++materialTempId}`,
+      id: null,
       bom: row.bom === true,
-      materialModelId: row.materialModelId || row.productModelId,
-      materialName: row.materialName || row.productName || "",
-      materialModel: row.materialModel || row.model || "",
-      demandedQuantity: Number(row.requiredQty ?? row.demandedQuantity ?? 0),
+      technologyOperationId: row.technologyOperationId ?? null,
+      operationName: row.operationName || "",
+      materialModelId: row.materialModelId ?? null,
+      materialName: row.materialName || "",
+      materialModel: row.materialModel || "",
       unit: row.unit || "",
-      pickQty: Number(row.pickQty ?? row.pickQuantity ?? row.requiredQty ?? row.demandedQuantity ?? 0),
-      batchNo,
+      demandedQuantity,
+      pickedQty,
+      returnedQty,
+      remainingQty,
+      pickQty: 0,
+      stockQty: null,
+      batchNo: [],
       batchNoList,
     };
   };
@@ -237,37 +266,115 @@
       if (!processMap.has(key)) {
         processMap.set(key, {
           id: processId,
+          technologyOperationId: processId,
           name: operationName,
         });
       }
     });
     processOptions.value = Array.from(processMap.values());
   };
-  const isDetail = ref(true);
 
   const loadMaterialData = async () => {
     if (!props.orderRow?.id) return;
     materialTableLoading.value = true;
     materialTableData.value = [];
-    await getProcessOptions();
     try {
-      const detailRes = await listMaterialPickingDetail(props.orderRow.id);
+      await getProcessOptions();
+      // 鍚屾椂鑾峰彇BOM闇�姹傛暟鎹笌宸查鏂欐槑缁嗭紝鐢ㄤ簬璁$畻鈥滃凡棰嗘暟閲�/鍓╀綑鍙鈥�
+      const [bomRes, detailRes] = await Promise.all([
+        listMaterialPickingBom(props.orderRow.id),
+        listMaterialPickingDetail(props.orderRow.id),
+      ]);
+      const bomList = Array.isArray(bomRes?.data)
+        ? bomRes.data
+        : bomRes?.data?.records || [];
       const detailList = Array.isArray(detailRes?.data)
         ? detailRes.data
         : detailRes?.data?.records || [];
-      if (detailList.length > 0) {
-        isDetail.value = true;
-        materialTableData.value = detailList.map(item => createMaterialRow(item));
-        return;
-      } else {
-        isDetail.value = false;
-        const bomRes = await listMaterialPickingBom(props.orderRow.id);
-        const bomList = Array.isArray(bomRes?.data)
-          ? bomRes.data
-          : bomRes?.data?.records || [];
-        materialTableData.value = bomList.map(item => createMaterialRow(item));
-        return;
-      }
+
+      // 鎸夆�滃伐搴�+鍘熸枡鈥濇眹鎬诲巻鍙查鏂欙細宸查鏁伴噺銆侀��鏂欐暟閲�
+      const pickedMap = new Map();
+      detailList.forEach(item => {
+        if (!item) return;
+        const key = buildDemandKey(
+          item.technologyOperationId,
+          item.operationName,
+          item.productModelId
+        );
+        const entry = pickedMap.get(key) || {
+          pickedQty: 0,
+          returnedQty: 0,
+          manualDemand: 0,
+          technologyOperationId: item.technologyOperationId,
+          operationName: item.operationName || "",
+          materialModelId: item.productModelId,
+          materialName: item.productName || "",
+          materialModel: item.model || "",
+          unit: item.unit || "",
+          batchNoList: Array.isArray(item.batchNoList)
+            ? item.batchNoList
+            : [],
+        };
+        entry.pickedQty = roundQuantity(entry.pickedQty + toNumber(item.pickQuantity));
+        entry.returnedQty = roundQuantity(entry.returnedQty + toNumber(item.returnQty));
+        // 鎵嬪伐鏂板琛岋紙闈濨OM锛夌殑闇�姹傛暟閲忎互棰嗘枡蹇収涓哄噯
+        if (item.bom !== true && item.demandedQuantity != null) {
+          entry.manualDemand = Math.max(
+            toNumber(entry.manualDemand),
+            toNumber(item.demandedQuantity)
+          );
+        }
+        pickedMap.set(key, entry);
+      });
+
+      const rows = [];
+      const bomKeySet = new Set();
+      // BOM琛岋細闇�姹傛暟閲忓彇BOM锛屽凡棰嗘暟閲忓彇鍘嗗彶棰嗘枡姹囨��
+      bomList.forEach(item => {
+        const materialModelId = item.productModelId ?? item.materialModelId;
+        const key = buildDemandKey(
+          item.technologyOperationId,
+          item.operationName,
+          materialModelId
+        );
+        bomKeySet.add(key);
+        rows.push(
+          createMaterialRow({
+            bom: true,
+            technologyOperationId: item.technologyOperationId,
+            operationName: item.operationName || "",
+            materialModelId,
+            materialName: item.productName || item.materialName || "",
+            materialModel: item.model || item.materialModel || "",
+            unit: item.unit || "",
+            demandedQuantity: item.demandedQuantity ?? item.requiredQty ?? 0,
+            pickedEntry: pickedMap.get(key),
+            batchNoList: item.batchNoList || [],
+          })
+        );
+      });
+      // 涓嶅湪BOM涓殑鍘嗗彶棰嗘枡锛堟墜宸ユ柊澧炶锛夛紝涔熷睍绀哄嚭鏉ユ敮鎸佺户缁鏂�
+      pickedMap.forEach((entry, key) => {
+        if (bomKeySet.has(key)) return;
+        rows.push(
+          createMaterialRow({
+            bom: false,
+            technologyOperationId: entry.technologyOperationId,
+            operationName: entry.operationName,
+            materialModelId: entry.materialModelId,
+            materialName: entry.materialName,
+            materialModel: entry.materialModel,
+            unit: entry.unit,
+            demandedQuantity: entry.manualDemand,
+            pickedEntry: entry,
+            batchNoList: entry.batchNoList,
+          })
+        );
+      });
+      // 鍏堟煡鍑哄簱瀛樹綑閲忓啀鍏ヨ〃锛屼繚璇侀娆℃墦寮�鍗冲睍绀猴紙rows 灏氭湭杞负鍝嶅簲寮忎唬鐞嗭紝
+      // 鍏ヨ〃鍚庡啀鏀瑰師濮嬪璞′笉浼氳Е鍙戣鍥炬洿鏂帮級
+      await loadStockQtyForRows(rows);
+      materialTableData.value = rows;
     } finally {
       materialTableLoading.value = false;
     }
@@ -282,21 +389,74 @@
     }
   );
 
-  const stripTrailingZeros = val => {
-    const str = String(val);
-    if (str.includes(".")) {
-      return parseFloat(str).toString();
-    }
-    return str;
-  };
-
   const handleClose = () => {
     materialTableData.value = [];
+    selectedMaterialRows.value = [];
     currentMaterialSelectRowIndex.value = -1;
+  };
+
+  // 鎸夊瀷鍙锋眹鎬绘墍鏈夋壒娆$殑鍘熸枡鍙敤搴撳瓨浣欓噺
+  const loadStockQtyForRows = async rows => {
+    const modelIds = [
+      ...new Set(rows.map(row => row.materialModelId).filter(Boolean)),
+    ];
+    const stockMap = new Map();
+    await Promise.all(
+      modelIds.map(async modelId => {
+        try {
+          const res = await getStockInventoryByModelId(modelId);
+          const list = Array.isArray(res?.data)
+            ? res.data
+            : res?.data?.records || res?.data?.rows || [];
+          const total = list.reduce(
+            (sum, item) => sum + toNumber(item?.qualitity ?? item?.quantity),
+            0
+          );
+          stockMap.set(modelId, roundQuantity(total));
+        } catch (e) {
+          stockMap.set(modelId, 0);
+        }
+      })
+    );
+    rows.forEach(row => {
+      if (!row.materialModelId) {
+        row.stockQty = null;
+        return;
+      }
+      row.stockQty = stockMap.has(row.materialModelId)
+        ? stockMap.get(row.materialModelId)
+        : 0;
+    });
   };
 
   const handleAddMaterialRow = () => {
     materialTableData.value.push(createMaterialRow());
+  };
+
+  const handleMaterialSelectionChange = selection => {
+    selectedMaterialRows.value = selection;
+  };
+
+  const handleAddPurchase = () => {
+    if (selectedMaterialRows.value.length === 0) {
+      ElMessage.warning("璇烽�夋嫨闇�瑕佹柊澧為噰璐殑鍘熸枡琛�");
+      return;
+    }
+    const rowsWithModel = selectedMaterialRows.value.filter(
+      row => row.materialModelId
+    );
+    if (rowsWithModel.length === 0) {
+      ElMessage.warning("鎵�閫夎缂哄皯鍘熸枡鍨嬪彿锛岃鍏堥�夋嫨鍘熸枡鍚庡啀鏂板閲囪喘");
+      return;
+    }
+    const productRows = rowsWithModel.map(row => ({
+      productModelId: row.materialModelId,
+      productCategory: row.materialName,
+      specificationModel: row.materialModel,
+      unit: row.unit,
+      isChecked: false,
+    }));
+    purchaseLedgerAddDialogRef.value?.open(productRows);
   };
 
   const handleDeleteMaterialRow = index => {
@@ -311,11 +471,12 @@
   };
 
   const handleRequiredQtyChange = (row, val) => {
-    const required = Number(val ?? 0);
+    const required = roundQuantity(val);
     row.demandedQuantity = required;
-    if (!row.pickQty || Number(row.pickQty) === 0) {
-      row.pickQty = required;
-    }
+    row.remainingQty = Math.max(
+      roundQuantity(required - toNumber(row.pickedQty) + toNumber(row.returnedQty)),
+      0
+    );
   };
 
   const openMaterialProductSelect = row => {
@@ -326,8 +487,6 @@
   };
 
   const handleMaterialProductConfirm = products => {
-    console.log(products, "products");
-
     if (!products || products.length === 0) return;
     const index = currentMaterialSelectRowIndex.value;
     if (index < 0 || !materialTableData.value[index]) return;
@@ -340,42 +499,32 @@
     row.materialModel = product.materialModel || product.model || "";
     row.unit = product.unit || product.measureUnit || "";
     row.batchNoList = Array.isArray(product.batchNoList) ? product.batchNoList : [];
-    row.batchNo = getDefaultBatchNo(row.batchNoList);
+    row.batchNo = [];
     currentMaterialSelectRowIndex.value = -1;
     materialProductDialogVisible.value = false;
+    loadStockQtyForRows([row]);
   };
 
   const validateMaterialRows = () => {
     if (materialTableData.value.length === 0) {
       return { valid: false, message: "璇峰厛鏂板棰嗘枡鏁版嵁" };
     }
-    const invalidNewRow = materialTableData.value.find(
-      item => item.bom !== true && (!item.operationName || !item.materialName)
+    const pickRows = materialTableData.value.filter(
+      item => toNumber(item.pickQty) > 0
     );
-    if (invalidNewRow) {
-      return { valid: false, message: "鏂板琛岀殑宸ュ簭鍚嶇О鍜屽師鏂欏悕绉颁负蹇呭~椤�" };
+    if (pickRows.length === 0) {
+      return { valid: false, message: "璇疯嚦灏戝~鍐欎竴琛屽ぇ浜�0鐨勯鐢ㄦ暟閲�" };
     }
-    const invalidRow = materialTableData.value.find(
-      item =>
-        !item.operationName ||
-        !item.materialName ||
-        (Number(item.pickQty) > 0 &&
-          (!item.batchNo || item.batchNo.length === 0)) ||
-        (item.batchNo && item.batchNo.length > 0 && Number(item.pickQty) <= 0) ||
-        item.demandedQuantity === null ||
-        item.demandedQuantity === undefined ||
-        item.pickQty === null ||
-        item.pickQty === undefined
-    );
-    if (invalidRow) {
-      if (
-        invalidRow.batchNo &&
-        invalidRow.batchNo.length > 0 &&
-        Number(invalidRow.pickQty) <= 0
-      ) {
-        return { valid: false, message: "閫夋嫨浜嗘壒鍙锋椂锛岄鐢ㄦ暟閲忓繀椤诲ぇ浜庨浂" };
+    for (let i = 0; i < materialTableData.value.length; i++) {
+      const item = materialTableData.value[i];
+      const rowNo = i + 1;
+      if (toNumber(item.pickQty) <= 0) continue;
+      if (item.bom !== true && (!item.operationName || !item.materialName)) {
+        return { valid: false, message: `绗�${rowNo}琛岀殑宸ュ簭鍚嶇О鍜屽師鏂欏悕绉颁负蹇呭~椤筦 };
       }
-      return { valid: false, message: "璇峰畬鍠勫伐搴忋�佸師鏂欍�佹壒鍙峰拰鏁伴噺鍚庡啀淇濆瓨" };
+      if (!Array.isArray(item.batchNo) || item.batchNo.length === 0) {
+        return { valid: false, message: `绗�${rowNo}琛岃閫夋嫨鎵瑰彿` };
+      }
     }
     return { valid: true, message: "" };
   };
@@ -389,47 +538,26 @@
     }
     materialSaving.value = true;
     try {
-      if (isDetail.value) {
-        await updateMaterialPickingLedger({
-          productionOrderId: props.orderRow.id,
-          productionOrderPickDto: materialTableData.value.map(item => ({
-            id: item.id,
-            // processId: item.operationName,
-            technologyOperationId: item.technologyOperationId,
-            operationName: item.operationName,
-            bom: item.bom === true,
-            productModelId: item.materialModelId,
-            // materialName: item.materialName,
-            // materialModel: item.materialModel,
-            demandedQuantity: item.demandedQuantity,
-            unit: item.unit,
-            pickQuantity: item.pickQty,
-            batchNo: Array.isArray(item.batchNo)
-              ? item.batchNo.join(",")
-              : item.batchNo,
-          })),
-        });
-      } else {
-        await saveMaterialPickingLedger({
-          productionOrderId: props.orderRow.id,
-          productionOrderPickDto: materialTableData.value.map(item => ({
-            id: item.id,
-            // processId: item.operationName,
-            technologyOperationId: item.technologyOperationId,
-            operationName: item.operationName,
-            bom: item.bom === true,
-            productModelId: item.materialModelId,
-            // materialName: item.materialName,
-            // materialModel: item.materialModel,
-            demandedQuantity: item.demandedQuantity,
-            unit: item.unit,
-            pickQuantity: item.pickQty,
-            batchNo: Array.isArray(item.batchNo)
-              ? item.batchNo.join(",")
-              : item.batchNo,
-          })),
-        });
-      }
+      // 姣忔淇濆瓨閮芥槸涓�娆$嫭绔嬬殑棰嗘枡锛氬彧鎻愪氦鏈棰嗙敤鏁伴噺澶т簬0鐨勮锛�
+      // 鍑哄簱鍙拌处鎸夋湰娆¢鏂欐暟閲忚璐︼紝鍘嗗彶棰嗘枡璁板綍淇濇寔涓嶅彉銆�
+      const pickRows = materialTableData.value.filter(
+        item => toNumber(item.pickQty) > 0
+      );
+      await saveMaterialPickingLedger({
+        productionOrderId: props.orderRow.id,
+        productionOrderPickDto: pickRows.map(item => ({
+          technologyOperationId: item.technologyOperationId,
+          operationName: item.operationName,
+          bom: item.bom === true,
+          productModelId: item.materialModelId,
+          demandedQuantity: item.demandedQuantity,
+          unit: item.unit,
+          pickQuantity: toNumber(item.pickQty),
+          batchNo: Array.isArray(item.batchNo)
+            ? item.batchNo.join(",")
+            : item.batchNo,
+        })),
+      });
 
       ElMessage({ message: "棰嗘枡鎴愬姛", type: "success" });
       emit("saved");
@@ -445,4 +573,9 @@
     margin-bottom: 12px;
     text-align: right;
   }
+
+  .remaining-empty {
+    color: #f56c6c;
+    font-weight: 600;
+  }
 </style>
diff --git a/src/views/productionManagement/productionOrder/components/PrintMaterialRequisition.vue b/src/views/productionManagement/productionOrder/components/PrintMaterialRequisition.vue
index d1aa267..ac1f563 100644
--- a/src/views/productionManagement/productionOrder/components/PrintMaterialRequisition.vue
+++ b/src/views/productionManagement/productionOrder/components/PrintMaterialRequisition.vue
@@ -31,7 +31,7 @@
           </div>
           <div class="info-item"
                style="width: 25%;">
-            <span class="label">闇�姹傛棩鏈燂細</span>
+            <span class="label">浜よ揣鏃ユ湡锛�</span>
             <span class="value">{{ formatDate(orderRow?.planCompleteTime) }}</span>
           </div>
         </div>
diff --git a/src/views/productionManagement/productionOrder/components/PurchaseLedgerAddDialog.vue b/src/views/productionManagement/productionOrder/components/PurchaseLedgerAddDialog.vue
new file mode 100644
index 0000000..a0da9d4
--- /dev/null
+++ b/src/views/productionManagement/productionOrder/components/PurchaseLedgerAddDialog.vue
@@ -0,0 +1,856 @@
+<template>
+  <div>
+    <FormDialog v-model="dialogVisible"
+                title="鏂板閲囪喘鍙拌处椤甸潰"
+                :width="'70%'"
+                operation-type="add"
+                @close="closeDia"
+                @confirm="submitForm"
+                @cancel="closeDia">
+      <el-form :model="form"
+               label-width="140px"
+               label-position="top"
+               :rules="rules"
+               ref="formRef">
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="閲囪喘鍚堝悓鍙凤細" prop="purchaseContractNumber">
+              <el-input v-model="form.purchaseContractNumber" placeholder="璇疯緭鍏�" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col v-if="showSalesContractBinding" :span="12">
+            <el-form-item label="閿�鍞悎鍚屽彿锛�" prop="salesLedgerId">
+              <el-select v-model="form.salesLedgerId"
+                         placeholder="璇烽�夋嫨"
+                         filterable
+                         clearable
+                         @change="salesLedgerChange">
+                <el-option v-for="item in salesContractList"
+                           :key="item.id"
+                           :label="item.salesContractNo"
+                           :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="渚涘簲鍟嗗悕绉帮細" prop="supplierId">
+              <el-select v-model="form.supplierId" placeholder="璇烽�夋嫨" filterable clearable>
+                <el-option v-for="item in supplierList"
+                           :key="item.id"
+                           :label="item.supplierName"
+                           :value="item.id">
+                  {{ item.supplierName }}
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="椤圭洰鍚嶇О" prop="projectName">
+              <el-input v-model="form.projectName" placeholder="璇疯緭鍏�" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="浠樻鏂瑰紡">
+              <el-input v-model="form.paymentMethod" placeholder="璇疯緭鍏�" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="绛捐鏃ユ湡锛�" prop="executionDate">
+              <el-date-picker v-model="form.executionDate"
+                              style="width: 100%"
+                              value-format="YYYY-MM-DD"
+                              format="YYYY-MM-DD"
+                              type="date"
+                              placeholder="璇烽�夋嫨"
+                              clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="褰曞叆浜猴細" prop="recorderId">
+              <el-select v-model="form.recorderId"
+                         placeholder="璇烽�夋嫨"
+                         clearable
+                         filterable
+                         disabled>
+                <el-option v-for="item in userList"
+                           :key="item.userId"
+                           :label="item.nickName"
+                           :value="item.userId" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="褰曞叆鏃ユ湡锛�" prop="entryDate">
+              <el-date-picker v-model="form.entryDate"
+                              style="width: 100%"
+                              value-format="YYYY-MM-DD"
+                              format="YYYY-MM-DD"
+                              type="date"
+                              placeholder="璇烽�夋嫨"
+                              clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-form-item label="浜у搧淇℃伅锛�">
+            <el-button type="primary" @click="openProductForm('add')">娣诲姞</el-button>
+            <el-button plain type="danger" @click="deleteProduct">鍒犻櫎</el-button>
+          </el-form-item>
+          <div class="select-button-group">
+            <el-select v-model="templateName"
+                       filterable
+                       allow-create
+                       :reserve-keyword="true"
+                       :default-first-option="false"
+                       clearable
+                       :input-value="filterInputValue"
+                       @filter-change="onTemplateFilterChange"
+                       @change="onTemplateChange"
+                       @focus="getTemplateList"
+                       placeholder="璇烽�夋嫨妯$増鎴栬�呰緭鍏ユ柊鐨勬ā鐗堝悕绉板悗閫夋嫨"
+                       class="no-arrow-select template-select">
+              <el-option v-for="item in templateList"
+                         :key="item.id || item.value"
+                         :label="item.templateName"
+                         :value="item.templateName">
+                <div class="template-option">
+                  <span>{{ item.templateName }}</span>
+                  <el-icon v-if="item.id"
+                           class="delete-icon"
+                           @click.stop="handleDeleteTemplate(item)">
+                    <Delete />
+                  </el-icon>
+                </div>
+              </el-option>
+            </el-select>
+            <el-button size="small"
+                       class="template-save-button"
+                       :disabled="!templateName || templateName.trim() === '' || (!currentTemplateId && isTemplateNameDuplicate)"
+                       @click="handleButtonClick">
+              淇濆瓨
+            </el-button>
+          </div>
+        </el-row>
+        <el-table :data="productData"
+                  border
+                  show-summary
+                  :summary-method="summarizeProTable"
+                  @selection-change="productSelected">
+          <el-table-column align="center" type="selection" width="55" />
+          <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+          <el-table-column label="浜у搧澶х被" prop="productCategory" />
+          <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
+          <el-table-column label="鍗曚綅" prop="unit" width="70" />
+          <el-table-column label="鏁伴噺" prop="quantity" width="70" />
+          <el-table-column label="绋庣巼(%)" prop="taxRate" width="80" />
+          <el-table-column label="鍚◣鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" width="150" />
+          <el-table-column label="鍚◣鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" width="150" />
+          <el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" width="150" />
+          <el-table-column label="鏄惁璐ㄦ" prop="isChecked" width="150">
+            <template #default="{ row }">
+              <el-tag :type="row.isChecked ? 'success' : 'info'">
+                {{ row.isChecked ? '鏄�' : '鍚�' }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column fixed="right" label="鎿嶄綔" min-width="60" align="center">
+            <template #default="{ row, $index }">
+              <el-button link type="primary" @click="openProductForm('edit', row, $index)">缂栬緫</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <el-row :gutter="30">
+          <el-col :span="24">
+            <el-form-item label="澶囨敞路锛�" prop="remarks">
+              <el-input v-model="form.remarks" placeholder="璇疯緭鍏�" clearable type="textarea" :rows="2" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="24">
+            <el-form-item label="闄勪欢鏉愭枡锛�" prop="purchaseLedgerFiles">
+              <FileUpload v-model:file-list="fileList" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </FormDialog>
+
+    <FormDialog v-model="productFormVisible"
+                :title="productOperationType === 'add' ? '鏂板浜у搧' : '缂栬緫浜у搧'"
+                :width="'40%'"
+                :operation-type="productOperationType"
+                @close="closeProductDia"
+                @confirm="submitProduct"
+                @cancel="closeProductDia">
+      <el-form :model="productForm"
+               label-width="140px"
+               label-position="top"
+               :rules="productRules"
+               ref="productFormRef">
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="浜у搧澶х被锛�" prop="productCategory">
+              <el-input v-model="productForm.productCategory"
+                        placeholder="鐐瑰嚮閫夋嫨"
+                        readonly
+                        style="cursor:pointer"
+                        @click="showProductSelectDialog = true" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="瑙勬牸鍨嬪彿锛�" prop="productModelId">
+              <el-input v-model="productForm.specificationModel"
+                        placeholder="鐐瑰嚮閫夋嫨"
+                        readonly
+                        style="cursor:pointer"
+                        @click="showProductSelectDialog = true" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鍗曚綅锛�" prop="unit">
+              <el-input v-model="productForm.unit" placeholder="璇疯緭鍏�" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="绋庣巼(%)锛�" prop="taxRate">
+              <el-select v-model="productForm.taxRate" placeholder="璇烽�夋嫨" clearable @change="mathNum">
+                <el-option v-for="dict in tax_rate"
+                           :key="dict.value"
+                           :label="dict.label"
+                           :value="dict.value" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鍚◣鍗曚环(鍏�)锛�" prop="taxInclusiveUnitPrice">
+              <el-input-number v-model="productForm.taxInclusiveUnitPrice"
+                               :precision="6"
+                               :step="0.000001"
+                               :min="0"
+                               clearable
+                               style="width: 100%"
+                               @change="mathNum" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鏁伴噺锛�" prop="quantity">
+              <el-input-number v-model="productForm.quantity"
+                               :step="0.1"
+                               clearable
+                               :precision="2"
+                               :min="0"
+                               style="width: 100%"
+                               placeholder="璇疯緭鍏�"
+                               @change="mathNum" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鍚◣鎬讳环(鍏�)锛�" prop="taxInclusiveTotalPrice">
+              <el-input-number v-model="productForm.taxInclusiveTotalPrice"
+                               :precision="6"
+                               :step="0.000001"
+                               :min="0"
+                               clearable
+                               style="width: 100%"
+                               @change="reverseMathNum('taxInclusiveTotalPrice')" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="涓嶅惈绋庢�讳环(鍏�)锛�" prop="taxExclusiveTotalPrice">
+              <el-input-number v-model="productForm.taxExclusiveTotalPrice"
+                               :precision="6"
+                               :step="0.000001"
+                               :min="0"
+                               clearable
+                               style="width: 100%"
+                               @change="reverseMathNum('taxExclusiveTotalPrice')" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鍙戠エ绫诲瀷锛�" prop="invoiceType">
+              <el-select v-model="productForm.invoiceType" placeholder="璇烽�夋嫨" clearable>
+                <el-option label="澧炴櫘绁�" value="澧炴櫘绁�" />
+                <el-option label="澧炰笓绁�" value="澧炰笓绁�" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鏄惁璐ㄦ锛�" prop="isChecked">
+              <el-radio-group v-model="productForm.isChecked">
+                <el-radio label="鏄�" :value="true" />
+                <el-radio label="鍚�" :value="false" />
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </FormDialog>
+
+    <ProductSelectDialog v-model="showProductSelectDialog"
+                         @confirm="handleProductSelect"
+                         single />
+  </div>
+</template>
+
+<script setup>
+  import { computed, getCurrentInstance, nextTick, reactive, ref, toRefs } from "vue";
+  import { Delete } from "@element-plus/icons-vue";
+  import { ElMessage, ElMessageBox } from "element-plus";
+  import FormDialog from "@/components/Dialog/FormDialog.vue";
+  import FileUpload from "@/components/AttachmentUpload/file/index.vue";
+  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+  import useUserStore from "@/store/modules/user";
+  import { userListNoPage } from "@/api/system/user.js";
+  import { getProductInfoByContractNo } from "@/api/salesManagement/salesLedger.js";
+  import {
+    addOrEditPurchase,
+    addPurchaseTemplate,
+    createPurchaseNo,
+    delPurchaseTemplate,
+    getOptions,
+    getPurchaseTemplateList,
+    getSalesNo,
+    updatePurchaseTemplate,
+  } from "@/api/procurementManagement/procurementLedger.js";
+  import { tableAmountFormatter, formatDecimal, buildAmountSummaryFormat } from "@/utils/numberFormat";
+
+  const props = defineProps({
+    modelValue: { type: Boolean, default: false },
+    initialProducts: { type: Array, default: () => [] },
+  });
+  const emit = defineEmits(["update:modelValue", "saved"]);
+
+  const dialogVisible = computed({
+    get: () => props.modelValue,
+    set: val => emit("update:modelValue", val),
+  });
+
+  const { proxy } = getCurrentInstance();
+  const { tax_rate } = proxy.useDict("tax_rate");
+  const userStore = useUserStore();
+  const showSalesContractBinding = false;
+
+  const productData = ref([]);
+  const productSelectedRows = ref([]);
+  const userList = ref([]);
+  const salesContractList = ref([]);
+  const supplierList = ref([]);
+  const fileList = ref([]);
+  const productFormVisible = ref(false);
+  const showProductSelectDialog = ref(false);
+  const productOperationType = ref("");
+  const productOperationIndex = ref(-1);
+  const templateName = ref("");
+  const filterInputValue = ref("");
+  const templateList = ref([]);
+  const isTemplateNameDuplicate = ref(false);
+  const currentTemplateId = ref(null);
+  let duplicateCheckTimer = null;
+
+  const data = reactive({
+    form: {
+      purchaseContractNumber: "",
+      salesLedgerId: "",
+      projectName: "",
+      recorderId: "",
+      entryDate: "",
+      productData: [],
+      supplierName: "",
+      supplierId: "",
+      paymentMethod: "",
+      executionDate: "",
+      isChecked: false,
+    },
+    rules: {
+      purchaseContractNumber: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      projectName: [{ required: true, message: "璇疯緭鍏ラ」鐩悕绉�", trigger: "blur" }],
+      supplierId: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      entryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+      executionDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+    },
+  });
+  const { form, rules } = toRefs(data);
+
+  const productFormData = reactive({
+    productForm: {
+      productId: "",
+      productCategory: "",
+      productModelId: "",
+      specificationModel: "",
+      unit: "",
+      quantity: "",
+      taxInclusiveUnitPrice: "",
+      taxRate: "",
+      taxInclusiveTotalPrice: "",
+      taxExclusiveTotalPrice: "",
+      invoiceType: "",
+      warnNum: "",
+      isChecked: false,
+    },
+    productRules: {
+      productCategory: [{ required: true, message: "璇烽�夋嫨浜у搧澶х被", trigger: "change" }],
+      productModelId: [{ required: true, message: "璇烽�夋嫨瑙勬牸鍨嬪彿", trigger: "change" }],
+      unit: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      quantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      taxInclusiveUnitPrice: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      taxRate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+      taxInclusiveTotalPrice: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      taxExclusiveTotalPrice: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      invoiceType: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+      isChecked: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+    },
+  });
+  const { productForm, productRules } = toRefs(productFormData);
+
+  const formattedNumber = tableAmountFormatter;
+
+  const getCurrentDate = () => {
+    const today = new Date();
+    const year = today.getFullYear();
+    const month = String(today.getMonth() + 1).padStart(2, "0");
+    const day = String(today.getDate()).padStart(2, "0");
+    return `${year}-${month}-${day}`;
+  };
+
+  const normalizeProductRow = row => ({
+    productId: row.productId || "",
+    productCategory: row.productCategory || row.materialName || row.productName || "",
+    productModelId: row.productModelId || row.materialModelId || row.modelId || "",
+    specificationModel: row.specificationModel || row.materialModel || row.model || "",
+    unit: row.unit || "",
+    quantity: row.quantity || "",
+    taxInclusiveUnitPrice: row.taxInclusiveUnitPrice || "",
+    taxRate: row.taxRate || "",
+    taxInclusiveTotalPrice: row.taxInclusiveTotalPrice || "",
+    taxExclusiveTotalPrice: row.taxExclusiveTotalPrice || "",
+    invoiceType: row.invoiceType || "",
+    warnNum: row.warnNum || "",
+    isChecked: row.isChecked ?? false,
+  });
+
+  const resetDialogState = () => {
+    form.value = {
+      purchaseContractNumber: "",
+      salesLedgerId: "",
+      projectName: "",
+      recorderId: "",
+      entryDate: "",
+      productData: [],
+      supplierName: "",
+      supplierId: "",
+      paymentMethod: "",
+      executionDate: "",
+      isChecked: false,
+    };
+    productData.value = [];
+    productSelectedRows.value = [];
+    fileList.value = [];
+    templateName.value = "";
+    filterInputValue.value = "";
+    isTemplateNameDuplicate.value = false;
+    currentTemplateId.value = null;
+  };
+
+  const open = async products => {
+    resetDialogState();
+    await Promise.all([
+      getTemplateList(),
+      userListNoPage().then(res => {
+        userList.value = res.data || [];
+      }),
+      getSalesNo().then(res => {
+        salesContractList.value = res || [];
+      }),
+      getOptions().then(res => {
+        supplierList.value = (res.data || []).filter(item => item.isWhite === 0);
+      }),
+    ]);
+
+    form.value.entryDate = getCurrentDate();
+    form.value.recorderId = userStore.id;
+    try {
+      const purchaseNoRes = await createPurchaseNo();
+      if (purchaseNoRes?.data) {
+        form.value.purchaseContractNumber = purchaseNoRes.data;
+      }
+    } catch (error) {
+      console.error("鐢熸垚閲囪喘鍚堝悓鍙峰け璐�:", error);
+      proxy.$modal.msgWarning("鐢熸垚閲囪喘鍚堝悓鍙峰け璐�");
+    }
+
+    const sourceProducts = Array.isArray(products) ? products : props.initialProducts;
+    productData.value = sourceProducts.map(normalizeProductRow);
+    dialogVisible.value = true;
+    await nextTick();
+    if (productData.value.length > 0) {
+      ElMessage.warning("宸叉寜鎵�閫夐鏂欒鐢熸垚浜у搧淇℃伅锛岃缂栬緫琛ュ叏鏁伴噺銆佺◣鐜囥�佸崟浠枫�佸彂绁ㄧ被鍨嬬瓑蹇呭~椤瑰悗鍐嶆彁浜ら噰璐彴璐�");
+    }
+  };
+
+  defineExpose({ open });
+
+  const checkTemplateNameDuplicate = name => {
+    if (!name || name.trim() === "") {
+      isTemplateNameDuplicate.value = false;
+      return false;
+    }
+    const isDuplicate = templateList.value.some(item => item.templateName === name.trim());
+    isTemplateNameDuplicate.value = isDuplicate;
+    return isDuplicate;
+  };
+
+  const onTemplateFilterChange = val => {
+    filterInputValue.value = val ?? "";
+    if (duplicateCheckTimer) {
+      clearTimeout(duplicateCheckTimer);
+    }
+    if (val && val.trim()) {
+      duplicateCheckTimer = setTimeout(() => {
+        if (checkTemplateNameDuplicate(val)) {
+          ElMessage.warning("妯℃澘鍚嶇О宸插瓨鍦紝璇锋洿鎹㈡ā鏉垮悕绉�");
+        }
+      }, 300);
+    } else {
+      isTemplateNameDuplicate.value = false;
+    }
+  };
+
+  const onTemplateChange = async val => {
+    if (typeof val === "string") {
+      filterInputValue.value = val;
+      checkTemplateNameDuplicate(val);
+    }
+    const matchedTemplate = templateList.value.find(item => item.templateName === val);
+    if (matchedTemplate?.id) {
+      currentTemplateId.value = matchedTemplate.id;
+      isTemplateNameDuplicate.value = false;
+      form.value.supplierId = matchedTemplate.supplierId || form.value.supplierId;
+      form.value.supplierName = matchedTemplate.supplierName || form.value.supplierName;
+      form.value.projectName = matchedTemplate.projectName || form.value.projectName;
+      form.value.paymentMethod = matchedTemplate.paymentMethod || form.value.paymentMethod;
+      productData.value = matchedTemplate.productList || matchedTemplate.productData || [];
+    } else {
+      currentTemplateId.value = null;
+    }
+  };
+
+  const getTemplateList = async () => {
+    const res = await getPurchaseTemplateList();
+    if (res && res.code === 200 && Array.isArray(res.data)) {
+      templateList.value = res.data;
+    }
+  };
+
+  const handleButtonClick = async () => {
+    if (!templateName.value || templateName.value.trim() === "") {
+      ElMessage.warning("璇疯緭鍏ユā鏉垮悕绉�");
+      return;
+    }
+    if (!currentTemplateId.value && checkTemplateNameDuplicate(templateName.value)) {
+      ElMessage.warning("妯℃澘鍚嶇О宸插瓨鍦紝璇锋洿鎹㈡ā鏉垮悕绉�");
+      return;
+    }
+    if (!form.value.supplierId) {
+      ElMessage.warning("璇峰厛閫夋嫨渚涘簲鍟�");
+      return;
+    }
+    if (!productData.value || productData.value.length === 0) {
+      ElMessage.warning("璇峰厛娣诲姞浜у搧淇℃伅");
+      return;
+    }
+
+    const params = {
+      productData: proxy.HaveJson(productData.value),
+      supplierId: form.value.supplierId,
+      paymentMethod: form.value.paymentMethod,
+      recorderId: form.value.recorderId,
+      projectName: form.value.projectName,
+      templateName: templateName.value.trim(),
+    };
+    const res = currentTemplateId.value
+      ? await updatePurchaseTemplate({ id: currentTemplateId.value, ...params })
+      : await addPurchaseTemplate(params);
+    if (res && res.code === 200) {
+      ElMessage.success(currentTemplateId.value ? "妯℃澘鏇存柊鎴愬姛" : "妯℃澘淇濆瓨鎴愬姛");
+      await getTemplateList();
+      templateName.value = "";
+      filterInputValue.value = "";
+      isTemplateNameDuplicate.value = false;
+      currentTemplateId.value = null;
+    } else {
+      ElMessage.error(res?.msg || "妯℃澘淇濆瓨澶辫触");
+    }
+  };
+
+  const productSelected = selectedRows => {
+    productSelectedRows.value = selectedRows;
+  };
+
+  const openProductForm = (type, row, index) => {
+    productOperationType.value = type;
+    productOperationIndex.value = index ?? -1;
+    proxy.resetForm("productFormRef");
+    productForm.value = type === "edit" ? { ...row } : { isChecked: false };
+    productFormVisible.value = true;
+  };
+
+  const handleProductSelect = products => {
+    if (!products || !products.length) return;
+    const p = products[0];
+    productForm.value.productId = p.productId || "";
+    productForm.value.productCategory = p.productName || p.topProductName || "";
+    productForm.value.productModelId = p.id;
+    productForm.value.specificationModel = p.model || "";
+    productForm.value.unit = p.unit || "";
+    showProductSelectDialog.value = false;
+  };
+
+  const closeProductDia = () => {
+    proxy.resetForm("productFormRef");
+    productFormVisible.value = false;
+  };
+
+  const submitProduct = () => {
+    proxy.$refs["productFormRef"].validate(valid => {
+      if (!valid) return;
+      if (productOperationType.value === "add") {
+        productData.value.push({ ...productForm.value });
+      } else if (productOperationIndex.value > -1) {
+        productData.value[productOperationIndex.value] = { ...productForm.value };
+      }
+      closeProductDia();
+    });
+  };
+
+  const deleteProduct = () => {
+    if (productSelectedRows.value.length === 0) {
+      proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+      return;
+    }
+    productData.value = productData.value.filter(item => !productSelectedRows.value.includes(item));
+    productSelectedRows.value = [];
+  };
+
+  const summarizeProTable = param => proxy.summarizeTable(
+    param,
+    ["taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice"],
+    buildAmountSummaryFormat(["taxInclusiveUnitPrice", "taxInclusiveTotalPrice", "taxExclusiveTotalPrice"])
+  );
+
+  const mathNum = () => {
+    if (!productForm.value.taxRate) {
+      proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+      return;
+    }
+    if (!productForm.value.taxInclusiveUnitPrice || !productForm.value.quantity) return;
+    productForm.value.taxInclusiveTotalPrice = proxy.calculateTaxIncludeTotalPrice(
+      productForm.value.taxInclusiveUnitPrice,
+      productForm.value.quantity
+    );
+    productForm.value.taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(
+      productForm.value.taxInclusiveTotalPrice,
+      productForm.value.taxRate
+    );
+  };
+
+  const reverseMathNum = field => {
+    if (!productForm.value.taxRate) {
+      proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+      return;
+    }
+    const taxRate = Number(productForm.value.taxRate);
+    if (!taxRate) return;
+    if (field === "taxInclusiveTotalPrice") {
+      if (productForm.value.quantity) {
+        productForm.value.taxInclusiveUnitPrice = formatDecimal(
+          Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)
+        );
+      } else if (productForm.value.taxInclusiveUnitPrice) {
+        productForm.value.quantity = (
+          Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)
+        ).toFixed(2);
+      }
+      productForm.value.taxExclusiveTotalPrice = formatDecimal(
+        Number(productForm.value.taxInclusiveTotalPrice) / (1 + taxRate / 100)
+      );
+    } else if (field === "taxExclusiveTotalPrice") {
+      productForm.value.taxInclusiveTotalPrice = formatDecimal(
+        Number(productForm.value.taxExclusiveTotalPrice) * (1 + taxRate / 100)
+      );
+      if (productForm.value.quantity) {
+        productForm.value.taxInclusiveUnitPrice = formatDecimal(
+          Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)
+        );
+      } else if (productForm.value.taxInclusiveUnitPrice) {
+        productForm.value.quantity = (
+          Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)
+        ).toFixed(2);
+      }
+    }
+  };
+
+  const salesLedgerChange = async () => {
+    await querygProductInfoByContractNo();
+  };
+
+  const querygProductInfoByContractNo = async () => {
+    const { code, data } = await getProductInfoByContractNo({
+      contractNo: form.value.salesLedgerId,
+    });
+    if (code === 200) {
+      productData.value = data;
+    }
+  };
+
+  const validateProductRows = () => {
+    if (productData.value.length === 0) {
+      return "璇锋坊鍔犱骇鍝佷俊鎭�";
+    }
+    const requiredFields = [
+      ["productCategory", "浜у搧澶х被"],
+      ["productModelId", "瑙勬牸鍨嬪彿"],
+      ["unit", "鍗曚綅"],
+      ["quantity", "鏁伴噺"],
+      ["taxRate", "绋庣巼"],
+      ["taxInclusiveUnitPrice", "鍚◣鍗曚环"],
+      ["taxInclusiveTotalPrice", "鍚◣鎬讳环"],
+      ["taxExclusiveTotalPrice", "涓嶅惈绋庢�讳环"],
+      ["invoiceType", "鍙戠エ绫诲瀷"],
+    ];
+    for (let i = 0; i < productData.value.length; i++) {
+      const row = productData.value[i];
+      const missing = requiredFields.find(([field]) => row[field] === "" || row[field] === null || row[field] === undefined);
+      if (missing) {
+        return `绗�${i + 1}琛屼骇鍝佷俊鎭己灏�${missing[1]}锛岃缂栬緫瀹屽啀鎻愪氦`;
+      }
+    }
+    return "";
+  };
+
+  const submitForm = () => {
+    proxy.$refs["formRef"].validate(valid => {
+      if (!valid) return;
+      const productMessage = validateProductRows();
+      if (productMessage) {
+        proxy.$modal.msgWarning(productMessage);
+        return;
+      }
+
+      const processedProductData = productData.value.map(product => {
+        const { id, ...rest } = product;
+        return rest;
+      });
+      const submitData = {
+        ...form.value,
+        productData: proxy.HaveJson(processedProductData),
+        storageBlobDTOS: fileList.value,
+        type: 2,
+      };
+      if (!submitData.salesLedgerId) {
+        submitData.salesContractNo = "";
+      }
+      delete submitData.id;
+
+      addOrEditPurchase(submitData).then(() => {
+        proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+        closeDia();
+        emit("saved");
+      });
+    });
+  };
+
+  const closeDia = () => {
+    proxy.resetForm("formRef");
+    dialogVisible.value = false;
+  };
+
+  const handleDeleteTemplate = async item => {
+    if (!item.id) {
+      proxy.$modal.msgWarning("鏃犳硶鍒犻櫎璇ユā鏉�");
+      return;
+    }
+    try {
+      await ElMessageBox.confirm(`纭畾瑕佸垹闄ゆā鏉�"${item.templateName}"鍚楋紵`, "鍒犻櫎纭", {
+        confirmButtonText: "纭畾",
+        cancelButtonText: "鍙栨秷",
+        type: "warning",
+      });
+      const res = await delPurchaseTemplate([item.id]);
+      if (res && res.code === 200) {
+        ElMessage.success("鍒犻櫎鎴愬姛");
+        if (templateName.value === item.templateName) {
+          templateName.value = "";
+          filterInputValue.value = "";
+        }
+        await getTemplateList();
+      } else {
+        ElMessage.error(res?.msg || "鍒犻櫎澶辫触");
+      }
+    } catch (error) {
+      if (error !== "cancel") {
+        ElMessage.error("鍒犻櫎澶辫触锛岃绋嶅悗閲嶈瘯");
+      }
+    }
+  };
+</script>
+
+<style scoped lang="scss">
+  .el-row {
+    justify-content: space-between;
+    align-items: center;
+  }
+
+  .select-button-group {
+    display: flex;
+    align-items: center;
+    width: 500px;
+    margin: 20px 0;
+  }
+
+  .template-select {
+    width: 500px;
+  }
+
+  .template-save-button {
+    height: 32px;
+    margin-left: 8px;
+  }
+
+  .template-option {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+
+  .no-arrow-select {
+    --el-select-suffix-icon-color: transparent;
+  }
+
+  .delete-icon {
+    cursor: pointer;
+    color: #f56c6c;
+    font-size: 14px;
+    margin-left: 8px;
+    transition: all 0.3s;
+
+    &:hover {
+      color: #f56c6c !important;
+      transform: scale(1.2);
+    }
+  }
+</style>
diff --git a/src/views/productionManagement/productionOrder/index.vue b/src/views/productionManagement/productionOrder/index.vue
index 5c3d4cf..9ac0ffa 100644
--- a/src/views/productionManagement/productionOrder/index.vue
+++ b/src/views/productionManagement/productionOrder/index.vue
@@ -11,6 +11,14 @@
                     style="width: 160px;"
                     @change="handleQuery" />
         </el-form-item>
+        <el-form-item label="閿�鍞崟鍙�:">
+          <el-input v-model="searchForm.salesContractNo"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    prefix-icon="Search"
+                    style="width: 160px;"
+                    @change="handleQuery" />
+        </el-form-item>
         <el-form-item label="浜у搧鍚嶇О:">
           <el-input v-model="searchForm.productName"
                     placeholder="璇疯緭鍏�"
@@ -183,7 +191,7 @@
                   <div class="info-value">{{ item.unit || '-' }}</div>
                 </div>
                 <div class="info-item">
-                  <div class="info-label">闇�姹傛棩鏈�</div>
+                  <div class="info-label">浜よ揣鏃ユ湡</div>
                   <div class="info-value">{{ item.requiredDate ? dayjs(item.requiredDate).format('YYYY-MM-DD') : '-' }}</div>
                 </div>
               </div>
@@ -194,6 +202,7 @@
     </el-dialog>
     <MaterialLedgerDialog v-model="materialDialogVisible"
                           :order-row="currentMaterialOrder"
+                          @saved="getList"
                            />
     <MaterialDetailDialog v-model="materialDetailDialogVisible"
                           :order-row="currentMaterialDetailOrder"
@@ -277,6 +286,11 @@
       prop: "npsNo",
       width: "150px",
     },
+    {
+      label: "閿�鍞崟鍙�",
+      prop: "salesContractNo",
+      width: "150px",
+    },
     // 1.寰呭紑濮嬨��2.杩涜涓��3.宸插畬鎴愩��4.宸插彇娑堛��5.宸茬粨鏉�
     {
       label: "鐘舵��",
diff --git a/src/views/productionManagement/productionProcess/ProcessSelectDialog.vue b/src/views/productionManagement/productionProcess/ProcessSelectDialog.vue
new file mode 100644
index 0000000..9ece5b8
--- /dev/null
+++ b/src/views/productionManagement/productionProcess/ProcessSelectDialog.vue
@@ -0,0 +1,159 @@
+<template>
+  <el-dialog v-model="visible" title="閫夋嫨宸ュ簭" width="800px" destroy-on-close :close-on-click-modal="false">
+    <el-form :inline="true" :model="query" class="mb-2">
+      <el-form-item label="宸ュ簭鍚嶇О">
+        <el-input v-model="query.name" placeholder="杈撳叆宸ュ簭鍚嶇О" clearable @keyup.enter="onSearch" style="width: 180px" />
+      </el-form-item>
+      <el-form-item label="宸ュ簭缂栫爜">
+        <el-input v-model="query.no" placeholder="杈撳叆宸ュ簭缂栫爜" clearable @keyup.enter="onSearch" style="width: 180px" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="onSearch">鎼滅储</el-button>
+        <el-button @click="onReset">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table ref="tableRef" v-loading="loading" :data="tableData" height="380" highlight-current-row row-key="id"
+      @current-change="handleCurrentChange" @row-dblclick="onConfirm">
+      <el-table-column type="index" label="搴忓彿" width="60" />
+      <el-table-column prop="no" label="宸ュ簭缂栫爜" min-width="130" />
+      <el-table-column prop="name" label="宸ュ簭鍚嶇О" min-width="150" />
+      <el-table-column prop="isQuality" label="鏄惁璐ㄦ" min-width="90">
+        <template #default="{ row }">
+          <el-tag size="small" :type="row.isQuality ? 'warning' : 'info'">{{ row.isQuality ? '鏄�' : '鍚�' }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="isProduction" label="鏄惁鐢熶骇" min-width="90">
+        <template #default="{ row }">
+          <el-tag size="small" :type="row.isProduction ? 'success' : 'info'">{{ row.isProduction ? '鏄�' : '鍚�' }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="remark" label="澶囨敞" min-width="120" show-overflow-tooltip />
+    </el-table>
+
+    <div class="mt-3 flex justify-end">
+      <el-pagination background layout="total, sizes, prev, pager, next, jumper" :total="total"
+        v-model:page-size="page.pageSize" v-model:current-page="page.pageNum" :page-sizes="[10, 20, 50, 100]"
+        @size-change="onPageChange" @current-change="onPageChange" />
+    </div>
+
+    <template #footer>
+      <el-button type="primary" :disabled="!selectedRow" @click="onConfirm">纭畾</el-button>
+      <el-button @click="close()">鍙栨秷</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+  import { computed, reactive, ref, watch } from "vue";
+  import { ElMessage } from "element-plus";
+  import { listPage } from "@/api/productionManagement/productionProcess";
+
+  export type ProcessRow = {
+    id: number;
+    no?: string;
+    name: string;
+    isQuality?: boolean;
+    isProduction?: boolean;
+    remark?: string;
+  };
+
+  const props = defineProps<{
+    modelValue: boolean;
+  }>();
+
+  const emit = defineEmits(["update:modelValue", "confirm"]);
+
+  const visible = computed({
+    get: () => props.modelValue,
+    set: v => emit("update:modelValue", v),
+  });
+
+  const query = reactive({
+    name: "",
+    no: "",
+  });
+
+  const page = reactive({
+    pageNum: 1,
+    pageSize: 10,
+  });
+
+  const loading = ref(false);
+  const tableData = ref<ProcessRow[]>([]);
+  const total = ref(0);
+  const selectedRow = ref<ProcessRow | null>(null);
+  const tableRef = ref();
+
+  function close() {
+    visible.value = false;
+  }
+
+  function handleCurrentChange(row: ProcessRow) {
+    selectedRow.value = row;
+  }
+
+  function onSearch() {
+    page.pageNum = 1;
+    loadData();
+  }
+
+  function onReset() {
+    query.name = "";
+    query.no = "";
+    onSearch();
+  }
+
+  function onPageChange() {
+    loadData();
+  }
+
+  function onConfirm() {
+    if (!selectedRow.value) {
+      ElMessage.warning("璇烽�夋嫨涓�涓伐搴�");
+      return;
+    }
+    emit("confirm", selectedRow.value);
+    close();
+  }
+
+  async function loadData() {
+    loading.value = true;
+    try {
+      selectedRow.value = null;
+      if (tableRef.value) {
+        tableRef.value.setCurrentRow(null);
+      }
+      const { data } = await listPage({
+        current: page.pageNum,
+        size: page.pageSize,
+        name: query.name.trim(),
+        no: query.no.trim(),
+      });
+      const records = data?.records ?? (Array.isArray(data) ? data : []);
+      tableData.value = Array.isArray(records) ? records : [];
+      total.value = Number(data?.total ?? tableData.value.length);
+    } catch (e) {
+      console.error("鍔犺浇宸ュ簭鍒楄〃澶辫触", e);
+      tableData.value = [];
+      total.value = 0;
+    } finally {
+      loading.value = false;
+    }
+  }
+
+  // 鐩戝惉寮圭獥鎵撳紑锛岄噸缃煡璇㈡潯浠跺拰椤电爜锛坕mmediate锛氱粍浠跺垱寤烘椂 modelValue 宸蹭负 true锛屼篃绔嬪嵆鍔犺浇锛�
+  watch(
+    () => props.modelValue,
+    visible => {
+      if (visible) {
+        selectedRow.value = null;
+        query.name = "";
+        query.no = "";
+        page.pageNum = 1;
+        loadData();
+      }
+    },
+    { immediate: true }
+  );
+</script>
\ No newline at end of file
diff --git a/src/views/productionManagement/productionTraceability/index.vue b/src/views/productionManagement/productionTraceability/index.vue
index 19f7ed9..9312a44 100644
--- a/src/views/productionManagement/productionTraceability/index.vue
+++ b/src/views/productionManagement/productionTraceability/index.vue
@@ -157,7 +157,7 @@
             <el-table-column label="鍒涘缓鏃堕棿"
                              align="center">
               <template #default="{ row }">
-                {{ parseTime(row.createTime) }}
+                {{ parseTime(row.createTime, '{y}-{m}-{d}') }}
               </template>
             </el-table-column>
             <el-table-column label="宸ユ椂锛坔锛�"
diff --git a/src/views/productionPlan/productionPlan/index.vue b/src/views/productionPlan/productionPlan/index.vue
index fd56e89..44297e9 100644
--- a/src/views/productionPlan/productionPlan/index.vue
+++ b/src/views/productionPlan/productionPlan/index.vue
@@ -21,7 +21,7 @@
                     style="width: 160px;"
                     @keyup.enter="handleQuery" />
         </el-form-item>
-        <el-form-item label="闇�姹傛棩鏈熻寖鍥�:"
+        <el-form-item label="浜よ揣鏃ユ湡鑼冨洿:"
                       prop="dateRange">
           <el-date-picker v-model="searchForm.dateRange"
                           type="daterange"
@@ -212,13 +212,13 @@
           <el-input v-model="form.unit"
                     placeholder="璇疯緭鍏ュ崟浣�" />
         </el-form-item>
-        <el-form-item label="闇�姹傛棩鏈�"
+        <el-form-item label="鍙戣揣鏃ユ湡"
                       prop="requiredDate">
           <el-date-picker v-model="form.requiredDate"
                           type="date"
                           value-format="YYYY-MM-DD"
                           style="width: 100%"
-                          placeholder="璇烽�夋嫨闇�姹傛棩鏈�" />
+                          placeholder="璇烽�夋嫨鍙戣揣鏃ユ湡" />
         </el-form-item>
         <el-form-item label="鎵胯鏃ユ湡"
                       prop="promisedDeliveryDate">
@@ -371,7 +371,7 @@
       // formatData: (cell, row) => (cell ? `${cell}${row.unit || "鏂�"}` : 0),
     },
     {
-      label: "闇�姹傛棩鏈�",
+      label: "鍙戣揣鏃ユ湡",
       prop: "requiredDate",
       width: "160px",
       className: "date-cell",
@@ -513,7 +513,7 @@
     ],
     qtyRequired: [{ required: true, message: "璇疯緭鍏ユ暟閲�", trigger: "blur" }],
     requiredDate: [
-      { required: true, message: "璇烽�夋嫨闇�姹傛棩鏈�", trigger: "change" },
+      { required: true, message: "璇烽�夋嫨鍙戣揣鏃ユ湡", trigger: "change" },
     ],
   });
 
@@ -530,8 +530,7 @@
     });
   };
   const onBlur = value => {
-    // 闄愬埗鍥涗綅灏忔暟
-    mergeForm.totalAssignedQuantity = Number(value.toFixed(4));
+    mergeForm.totalAssignedQuantity = Number(Number(value).toFixed(10));
   };
 
   const fetchProductOptions = () => {
@@ -656,9 +655,9 @@
         };
       }
       summary[category].totalAssignedQuantity += Number(
-        (Number(row.qtyRequired || 0) - Number(row.quantityIssued || 0)).toFixed(
-          4
-        )
+        (
+          Number(row.qtyRequired || 0) - Number(row.quantityIssued || 0)
+        ).toFixed(10)
       );
     });
 
@@ -739,13 +738,14 @@
     // 璁$畻鎬诲埗閫犳暟閲� (榛樿qtyRequired鐨勫拰)
     const totalAssignedQuantity = selectedRows.value.reduce((sum, row) => {
       return sum + Number(row.qtyRequired || 0) - Number(row.quantityIssued || 0);
-    }, 0);
-    sumAssignedQuantity.value = totalAssignedQuantity;
-    console.log(totalAssignedQuantity);
+    }, 0);  // 閬垮厤娴偣鏁扮簿搴﹂棶棰橈紙濡� 37.800000000000004锛�
+    const fixedTotal = Number(totalAssignedQuantity.toFixed(10));
+    sumAssignedQuantity.value = fixedTotal;
+    console.log(fixedTotal);
     // 璁剧疆琛ㄥ崟鏁版嵁
     mergeForm.productName = productName;
     mergeForm.model = firstRow.model || "";
-    mergeForm.totalAssignedQuantity = totalAssignedQuantity;
+    mergeForm.totalAssignedQuantity = fixedTotal;
     mergeForm.planCompleteTime = firstRow.requiredDate || "";
     mergeForm.productId = firstRow.productId || "";
     mergeForm.ids = selectedRows.value.map(row => row.id);
diff --git a/src/views/qualityManagement/finalInspection/index.vue b/src/views/qualityManagement/finalInspection/index.vue
index 181f8bb..198adb2 100644
--- a/src/views/qualityManagement/finalInspection/index.vue
+++ b/src/views/qualityManagement/finalInspection/index.vue
@@ -2,7 +2,16 @@
   <div class="app-container">
     <div class="search_form mb20">
       <div>
-        <span class="search_title">浜у搧鍚嶇О锛�</span>
+        <span class="search_title">閿�鍞崟鍙凤細</span>
+        <el-input
+            v-model="searchForm.salesContractNo"
+            style="width: 240px"
+            placeholder="璇疯緭鍏ラ攢鍞崟鍙锋悳绱�"
+            @change="handleQuery"
+            clearable
+            :prefix-icon="Search"
+        />
+        <span style="margin-left: 10px" class="search_title">浜у搧鍚嶇О锛�</span>
         <el-input
             v-model="searchForm.productName"
             style="width: 240px"
@@ -80,6 +89,7 @@
 
 const data = reactive({
   searchForm: {
+    salesContractNo: "",
     productName: "",
     entryDate: undefined, // 褰曞叆鏃ユ湡
     entryDateStart: undefined,
diff --git a/src/views/qualityManagement/nonconformingManagement/components/detailModal.vue b/src/views/qualityManagement/nonconformingManagement/components/detailModal.vue
new file mode 100644
index 0000000..4622796
--- /dev/null
+++ b/src/views/qualityManagement/nonconformingManagement/components/detailModal.vue
@@ -0,0 +1,98 @@
+<template>
+  <el-dialog
+    v-model="visible"
+    title="涓嶅悎鏍煎鐞嗚鎯�"
+    width="70%"
+    @close="handleClose"
+  >
+    <el-descriptions border :column="2" class="detail-descriptions">
+      <el-descriptions-item label="绫诲埆">
+        {{ getInspectTypeName(form.inspectType) }}
+      </el-descriptions-item>
+      <el-descriptions-item label="浜у搧鍚嶇О">
+        {{ form.productName }}
+      </el-descriptions-item>
+      <el-descriptions-item label="瑙勬牸鍨嬪彿">
+        {{ form.model }}
+      </el-descriptions-item>
+      <el-descriptions-item label="鍗曚綅">
+        {{ form.unit }}
+      </el-descriptions-item>
+      <el-descriptions-item label="鏁伴噺">
+        {{ form.quantity }}
+      </el-descriptions-item>
+      <el-descriptions-item label="妫�娴嬫棩鏈�">
+        {{ form.checkTime }}
+      </el-descriptions-item>
+      <el-descriptions-item label="宸ュ簭">
+        {{ form.process }}
+      </el-descriptions-item>
+      <el-descriptions-item label="妫�楠屽憳">
+        {{ form.checkName }}
+      </el-descriptions-item>
+      <el-descriptions-item label="妫�娴嬪崟浣�">
+        {{ form.checkCompany }}
+      </el-descriptions-item>
+      <el-descriptions-item label="涓嶅悎鏍肩幇璞�" :span="2">
+        {{ form.defectivePhenomena }}
+      </el-descriptions-item>
+      <el-descriptions-item label="澶勭悊缁撴灉">
+        {{ form.dealResult }}
+      </el-descriptions-item>
+      <el-descriptions-item label="澶勭悊浜�">
+        {{ form.dealName }}
+      </el-descriptions-item>
+      <el-descriptions-item label="澶勭悊鏃ユ湡">
+        {{ form.dealTime }}
+      </el-descriptions-item>
+    </el-descriptions>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="handleClose">鍏抽棴</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import { getQualityUnqualifiedDetail } from "@/api/qualityManagement/nonconformingManagement.js";
+
+const visible = ref(false);
+const form = ref({});
+
+const getInspectTypeName = (type) => {
+  if (type === 0) return "鍘熸潗鏂欐楠�";
+  if (type === 1) return "杩囩▼妫�楠�";
+  if (type === 2) return "鍑哄巶妫�楠�";
+  return "";
+};
+
+const openDialog = (row) => {
+  visible.value = true;
+  form.value = { ...row };
+  if (row.id) {
+    getQualityUnqualifiedDetail(row.id).then((res) => {
+      if (res.data) {
+        form.value = { ...res.data };
+      }
+    });
+  }
+};
+
+const handleClose = () => {
+  visible.value = false;
+  form.value = {};
+};
+
+defineExpose({
+  openDialog,
+});
+</script>
+
+<style scoped>
+.detail-descriptions :deep(.el-descriptions__label) {
+  width: 120px;
+  justify-content: flex-end;
+}
+</style>
diff --git a/src/views/qualityManagement/nonconformingManagement/index.vue b/src/views/qualityManagement/nonconformingManagement/index.vue
index fc9d5d2..7f9feb6 100644
--- a/src/views/qualityManagement/nonconformingManagement/index.vue
+++ b/src/views/qualityManagement/nonconformingManagement/index.vue
@@ -55,6 +55,7 @@
     </div>
     <FormDia ref="formDia" @close="handleQuery"></FormDia>
     <InspectionFormDia ref="inspectionFormDia" @close="handleQuery"></InspectionFormDia>
+    <DetailModal ref="detailModal"></DetailModal>
   </div>
 </template>
 
@@ -65,6 +66,7 @@
 import {ElMessageBox} from "element-plus";
 import {qualityUnqualifiedDel, qualityUnqualifiedListPage} from "@/api/qualityManagement/nonconformingManagement.js";
 import InspectionFormDia from "@/views/qualityManagement/nonconformingManagement/components/inspectionFormDia.vue";
+import DetailModal from "@/views/qualityManagement/nonconformingManagement/components/detailModal.vue";
 import dayjs from "dayjs";
 
 const data = reactive({
@@ -177,7 +179,7 @@
     label: "鎿嶄綔",
     align: "center",
     fixed: "right",
-    width: 100,
+    width: 150,
     operation: [
       {
         name: "澶勭悊",
@@ -186,6 +188,13 @@
           openInspectionForm("edit", row);
         },
         disabled: (row) => row.inspectState === 1,
+      },
+      {
+        name: "璇︽儏",
+        type: "text",
+        clickFun: (row) => {
+          detailModal.value?.openDialog(row);
+        },
       },
     ],
   },
@@ -200,6 +209,7 @@
 });
 const formDia = ref()
 const inspectionFormDia = ref()
+const detailModal = ref()
 const { proxy } = getCurrentInstance()
 
 const changeDaterange = (value) => {
diff --git a/src/views/qualityManagement/processInspection/index.vue b/src/views/qualityManagement/processInspection/index.vue
index 1f2b26e..15ce00b 100644
--- a/src/views/qualityManagement/processInspection/index.vue
+++ b/src/views/qualityManagement/processInspection/index.vue
@@ -2,7 +2,16 @@
   <div class="app-container">
     <div class="search_form mb20">
       <div>
-        <span class="search_title">宸ュ簭锛�</span>
+        <span class="search_title">鐢熶骇宸ュ崟鍙凤細</span>
+        <el-input
+            v-model="searchForm.workOrderNo"
+            style="width: 240px"
+            placeholder="璇疯緭鍏ョ敓浜у伐鍗曞彿鎼滅储"
+            @change="handleQuery"
+            clearable
+            :prefix-icon="Search"
+        />
+        <span style="margin-left: 10px" class="search_title">宸ュ簭锛�</span>
         <el-input
             v-model="searchForm.process"
             style="width: 240px"
@@ -79,6 +88,7 @@
 
 const data = reactive({
   searchForm: {
+    workOrderNo: "",
     process: "",
     entryDate: undefined, // 褰曞叆鏃ユ湡
     entryDateStart: undefined,
diff --git a/src/views/qualityManagement/rawMaterialInspection/index.vue b/src/views/qualityManagement/rawMaterialInspection/index.vue
index 346ae9b..74d4766 100644
--- a/src/views/qualityManagement/rawMaterialInspection/index.vue
+++ b/src/views/qualityManagement/rawMaterialInspection/index.vue
@@ -2,7 +2,16 @@
   <div class="app-container">
     <div class="search_form mb20">
       <div>
-        <span class="search_title">渚涘簲鍟嗭細</span>
+        <span class="search_title">閲囪喘璁㈠崟鍙凤細</span>
+        <el-input
+            v-model="searchForm.purchaseContractNo"
+            style="width: 240px"
+            placeholder="璇疯緭鍏ラ噰璐鍗曞彿鎼滅储"
+            @change="handleQuery"
+            clearable
+            :prefix-icon="Search"
+        />
+        <span style="margin-left: 10px" class="search_title">渚涘簲鍟嗭細</span>
         <el-input
             v-model="searchForm.supplier"
             style="width: 240px"
@@ -81,6 +90,7 @@
 
 const data = reactive({
   searchForm: {
+    purchaseContractNo: "",
     supplier: "",
     entryDate: undefined, // 褰曞叆鏃ユ湡
     entryDateStart: undefined,
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index 9362a18..8dab3ca 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -24,6 +24,18 @@
                     prefix-icon="Search"
                     @change="handleQuery" />
         </el-form-item>
+        <el-form-item label="鍙戣揣鐘舵�侊細">
+          <el-select v-model="searchForm.shippingStatus"
+                     placeholder="璇烽�夋嫨"
+                     clearable
+                     style="width: 160px"
+                     @change="handleQuery">
+            <el-option label="鏈彂璐�"
+                       value="鏈彂璐�" />
+            <el-option label="宸插彂璐�"
+                       value="宸插彂璐�" />
+          </el-select>
+        </el-form-item>
         <el-form-item label="褰曞叆鏃ユ湡锛�">
           <el-date-picker v-model="searchForm.entryDate"
                           value-format="YYYY-MM-DD"
@@ -910,7 +922,7 @@
     <!-- 鍙戣揣寮规 -->
     <el-dialog v-model="deliveryFormVisible"
                title="鍙戣揣淇℃伅"
-               width="40%"
+               width="960px"
                @close="closeDeliveryDia">
       <el-form :model="deliveryForm"
                label-width="120px"
@@ -935,6 +947,12 @@
           <el-col :span="24">
             <el-form-item label="寰呭彂璐ф暟閲忥細">
               <el-input :model-value="currentDeliveryRow?.noQuantity"
+                        disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="宸查�夋嫨鍙戣揣鏁伴噺锛�">
+              <el-input :model-value="selectedDeliveryQuantity"
                         disabled />
             </el-form-item>
           </el-col>
@@ -990,6 +1008,13 @@
                 <el-table-column label="鎵瑰彿"
                                  prop="batchNo"
                                  min-width="180" />
+                <el-table-column label="閿�鍞鍗曞彿"
+                                 min-width="180"
+                                 show-overflow-tooltip>
+                  <template #default="scope">
+                    {{ formatDeliveryBatchSalesOrderNo(scope.row) }}
+                  </template>
+                </el-table-column>
                 <el-table-column label="浜у搧澶х被"
                                  prop="productName"
                                  min-width="100" />
@@ -1013,8 +1038,6 @@
                     <el-input-number v-model="scope.row.deliveryQuantity"
                                      :min="0"
                                      :max="getDeliveryBatchDeliveryMax(scope.row)"
-                                     :precision="2"
-                                     :step="0.01"
                                      controls-position="right"
                                      @change="handleDeliveryBatchQuantityChange(scope.row)"
                                      style="width: 100%;" />
@@ -1071,6 +1094,7 @@
     tableAmountFormatter,
     formatDecimal,
     buildAmountSummaryFormat,
+    roundAmount,
   } from "@/utils/numberFormat";
   import ImageUpload from "@/components/AttachmentUpload/image/index.vue";
   import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
@@ -1111,6 +1135,8 @@
     searchForm: {
       customerName: "", // 瀹㈡埛鍚嶇О
       salesContractNo: "", // 閿�鍞悎鍚岀紪鍙�
+      projectName: "", // 椤圭洰鍚嶇О
+      shippingStatus: "", // 鍙戣揣鐘舵�侊紙鏈彂璐�/宸插彂璐э級
       entryDate: null, // 褰曞叆鏃ユ湡
       entryDateStart: undefined,
       entryDateEnd: undefined,
@@ -1214,6 +1240,8 @@
       item?.stockQuantity;
     return quantity ?? 0;
   };
+  const formatDeliveryBatchSalesOrderNo = item =>
+    item?.salesContractNo || "--";
   const currentStock = ref(null);
   const fetchCurrentStock = async (productModelId) => {
     if (!productModelId) {
@@ -1221,7 +1249,7 @@
       return;
     }
     try {
-      const res = await getStockInventoryByModelId(productModelId);
+      const res = await getStockInventoryByModelId(productModelId, currentId.value);
       const rawList = Array.isArray(res?.data)
         ? res.data
         : res?.data?.records || res?.data?.rows || [];
@@ -1229,7 +1257,7 @@
       rawList.forEach(item => {
         total += Number(getDeliveryBatchQuantity(item) || 0);
       });
-      currentStock.value = total;
+      currentStock.value = roundAmount(total);
     } catch (e) {
       console.error(e);
       currentStock.value = 0;
@@ -1238,35 +1266,45 @@
   const getCurrentDeliveryRowQuantity = () => {
     return Number(currentDeliveryRow.value?.noQuantity || 0);
   };
+  const selectedDeliveryQuantity = computed(() => {
+    return roundAmount(
+      (deliveryForm.value.batchNoList || []).reduce(
+        (sum, item) => sum + Number(item?.deliveryQuantity || 0),
+        0
+      )
+    );
+  });
   const getDeliveryBatchDeliveryMax = row => {
     const productQuantity = getCurrentDeliveryRowQuantity();
     const batchQuantity = Number(getDeliveryBatchQuantity(row) || 0);
-    const otherBatchTotal = (deliveryForm.value.batchNoList || []).reduce(
-      (sum, item) => {
-        if (item?.id === row?.id) return sum;
-        return sum + Number(item?.deliveryQuantity || 0);
-      },
-      0
+    const otherBatchTotal = roundAmount(
+      (deliveryForm.value.batchNoList || []).reduce(
+        (sum, item) => {
+          if (item?.id === row?.id) return sum;
+          return sum + Number(item?.deliveryQuantity || 0);
+        },
+        0
+      )
     );
-    const remainingProductQuantity = Math.max(
-      0,
-      productQuantity - otherBatchTotal
+    const remainingProductQuantity = roundAmount(
+      Math.max(0, productQuantity - otherBatchTotal)
     );
     return Math.max(0, Math.min(batchQuantity, remainingProductQuantity));
   };
   const handleDeliveryBatchQuantityChange = row => {
     const productQuantity = getCurrentDeliveryRowQuantity();
     const batchQuantity = Number(getDeliveryBatchQuantity(row) || 0);
-    const otherBatchTotal = (deliveryForm.value.batchNoList || []).reduce(
-      (sum, item) => {
-        if (item?.id === row?.id) return sum;
-        return sum + Number(item?.deliveryQuantity || 0);
-      },
-      0
+    const otherBatchTotal = roundAmount(
+      (deliveryForm.value.batchNoList || []).reduce(
+        (sum, item) => {
+          if (item?.id === row?.id) return sum;
+          return sum + Number(item?.deliveryQuantity || 0);
+        },
+        0
+      )
     );
-    const remainingProductQuantity = Math.max(
-      0,
-      productQuantity - otherBatchTotal
+    const remainingProductQuantity = roundAmount(
+      Math.max(0, productQuantity - otherBatchTotal)
     );
     const currentValue = Number(row?.deliveryQuantity || 0);
 
@@ -1283,9 +1321,9 @@
       item => Number(item?.deliveryQuantity || 0) > 0
     );
   };
-  const getDeliveryBatchNoList = async productModelId => {
+  const getDeliveryBatchNoList = async (productModelId, salesLedgerId) => {
     if (!productModelId) return [];
-    const res = await getStockInventoryByModelId(productModelId);
+    const res = await getStockInventoryByModelId(productModelId, salesLedgerId);
     const rawList = Array.isArray(res?.data)
       ? res.data
       : res?.data?.records || res?.data?.rows || [];
@@ -2476,7 +2514,8 @@
 
     currentDeliveryRow.value = row;
     const batchNoList = await getDeliveryBatchNoList(
-      row.productModelId || row.modelId
+      row.productModelId || row.modelId,
+      row.salesLedgerId
     );
     deliveryForm.value = {
       shippingCarNumber: "",
@@ -2499,9 +2538,11 @@
           proxy.$modal.msgWarning("璇疯嚦灏戝~鍐欎竴涓壒鍙风殑鍙戣揣鏁伴噺");
           return;
         }
-        const totalDeliveryQuantity = selectedBatchRows.reduce(
-          (sum, item) => sum + Number(item.deliveryQuantity || 0),
-          0
+        const totalDeliveryQuantity = roundAmount(
+          selectedBatchRows.reduce(
+            (sum, item) => sum + Number(item.deliveryQuantity || 0),
+            0
+          )
         );
         const currentRowNoQuantity = Number(
           currentDeliveryRow.value?.noQuantity || 0

--
Gitblit v1.9.3