From b7e3bc6bbe2f6464f4f92e457212fac7ea61758d Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期二, 28 四月 2026 11:04:33 +0800
Subject: [PATCH] 领料功能

---
 src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue     |  338 +++++++-----
 src/views/inventoryManagement/stockManagement/New.vue                                  |  350 ++++++------
 src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue     |  183 ++++--
 src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue       |  502 ++++++++++--------
 src/views/productionManagement/productionOrder/index.vue                               |   37 
 src/views/productionManagement/productionOrder/components/MaterialSupplementDialog.vue |  159 +++++
 src/api/productionManagement/productionOrder.js                                        |   40 +
 7 files changed, 978 insertions(+), 631 deletions(-)

diff --git a/src/api/productionManagement/productionOrder.js b/src/api/productionManagement/productionOrder.js
index 9f7ce58..b87adbb 100644
--- a/src/api/productionManagement/productionOrder.js
+++ b/src/api/productionManagement/productionOrder.js
@@ -72,23 +72,47 @@
 }
 
 // 鐢熶骇璁㈠崟-淇濆瓨棰嗘枡鍙拌处
+// export function saveMaterialPickingLedger(data) {
+//   return request({
+//     url: "/productOrderMaterial/save",
+//     method: "post",
+//     data,
+//   });
+// }
 export function saveMaterialPickingLedger(data) {
   return request({
-    url: "/productOrderMaterial/save",
+    url: "/productionOrderPick/savePick",
     method: "post",
     data,
   });
 }
-
-// 鐢熶骇璁㈠崟-棰嗘枡璇︽儏鍒楄〃
-export function listMaterialPickingDetail(query) {
+export function updateMaterialPickingLedger(data) {
   return request({
-    url: "/productOrderMaterial/detailList",
-    method: "get",
-    params: query,
+    url: "/productionOrderPick/updatePick",
+    method: "post",
+    data,
   });
 }
-
+// 鐢熶骇璁㈠崟-棰嗘枡璇︽儏鍒楄〃
+// export function listMaterialPickingDetail(query) {
+//   return request({
+//     url: "/productOrderMaterial/detailList",
+//     method: "get",
+//     params: query,
+//   });
+// }
+export function listMaterialPickingBom(productionOrderId) {
+  return request({
+    url: "/productionOrder/pick/" + productionOrderId,
+    method: "get",
+  });
+}
+export function listMaterialPickingDetail(productionOrderId) {
+  return request({
+    url: "/productionOrderPick/detail/" + productionOrderId,
+    method: "get",
+  });
+}
 // 鐢熶骇璁㈠崟-琛ユ枡璁板綍鍒楄〃
 export function listMaterialSupplementRecord(query) {
   return request({
diff --git a/src/views/inventoryManagement/stockManagement/New.vue b/src/views/inventoryManagement/stockManagement/New.vue
index eebe313..1c9b333 100644
--- a/src/views/inventoryManagement/stockManagement/New.vue
+++ b/src/views/inventoryManagement/stockManagement/New.vue
@@ -1,96 +1,97 @@
 <template>
   <div>
-    <el-dialog
-        v-model="isShow"
-        title="鏂板搴撳瓨"
-        width="800"
-        @close="closeModal"
-    >
-      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
-        <el-form-item
-            label="浜у搧鍚嶇О"
-            prop="productModelId"
-            :rules="[
+    <el-dialog v-model="isShow"
+               title="鏂板搴撳瓨"
+               width="800"
+               @close="closeModal">
+      <el-form label-width="140px"
+               :model="formState"
+               label-position="top"
+               ref="formRef">
+        <el-form-item label="浜у搧鍚嶇О"
+                      prop="productModelId"
+                      :rules="[
                 {
                 required: true,
                 message: '璇烽�夋嫨浜у搧',
                 trigger: 'change',
               }
-            ]"
-        >
-          <el-button type="primary" @click="showProductSelectDialog = true">
+            ]">
+          <el-button type="primary"
+                     @click="showProductSelectDialog = true">
             {{ formState.productName ? formState.productName : '閫夋嫨浜у搧' }}
           </el-button>
         </el-form-item>
-
-        <el-form-item
-            label="瑙勬牸"
-            prop="productModelName"
-        >
-          <el-input v-model="formState.productModelName"  disabled />
+        <el-form-item label="瑙勬牸"
+                      prop="productModelName">
+          <el-input v-model="formState.productModelName"
+                    disabled />
         </el-form-item>
-
-        <el-form-item
-            label="鍗曚綅"
-            prop="unit"
-        >
-          <el-input v-model="formState.unit"  disabled />
+        <el-form-item label="鍗曚綅"
+                      prop="unit">
+          <el-input v-model="formState.unit"
+                    disabled />
         </el-form-item>
-
-        <el-form-item
-            label="搴撳瓨绫诲瀷"
-            prop="type"
-            :rules="[
+        <el-form-item label="搴撳瓨绫诲瀷"
+                      prop="type"
+                      :rules="[
                 {
                 required: true,
                 message: '璇烽�夋嫨搴撳瓨绫诲瀷',
                 trigger: 'change',
               }
-            ]"
-        >
-          <el-select v-model="formState.type" placeholder="璇烽�夋嫨搴撳瓨绫诲瀷">
-            <el-option label="鍚堟牸搴撳瓨" value="qualified" />
-            <el-option label="涓嶅悎鏍煎簱瀛�" value="unqualified" />
+            ]">
+          <el-select v-model="formState.type"
+                     placeholder="璇烽�夋嫨搴撳瓨绫诲瀷">
+            <el-option label="鍚堟牸搴撳瓨"
+                       value="qualified" />
+            <el-option label="涓嶅悎鏍煎簱瀛�"
+                       value="unqualified" />
           </el-select>
         </el-form-item>
-
-        <el-form-item
-            label="搴撳瓨鏁伴噺"
-            prop="qualitity"
-        >
-          <el-input-number v-model="formState.qualitity" :step="1" :min="1" style="width: 100%" />
+        <el-form-item label="搴撳瓨鏁伴噺"
+                      prop="qualitity">
+          <el-input-number v-model="formState.qualitity"
+                           :step="1"
+                           :min="1"
+                           style="width: 100%" />
         </el-form-item>
-
-        <el-form-item
-            label="鎵瑰彿"
-            prop="batchNo"
-        >
-          <el-input v-model="formState.batchNo" placeholder="璇疯緭鍏ユ壒鍙�" />
+        <el-form-item label="鎵瑰彿"
+                      prop="batchNo"
+                      :rules="[
+                {
+                required: true,
+                message: '璇疯緭鍏ユ壒鍙�',
+                trigger: 'blur',
+              }
+            ]">
+          <el-input v-model="formState.batchNo"
+                    placeholder="璇疯緭鍏ユ壒鍙�" />
         </el-form-item>
-
-        <el-form-item
-            v-if="formState.type === 'qualified'"
-            label="搴撳瓨棰勮鏁伴噺"
-            prop="warnNum"
-        >
-          <el-input-number v-model="formState.warnNum" :step="1" :min="0" :max="formState.qualitity" style="width: 100%" />
+        <el-form-item v-if="formState.type === 'qualified'"
+                      label="搴撳瓨棰勮鏁伴噺"
+                      prop="warnNum">
+          <el-input-number v-model="formState.warnNum"
+                           :step="1"
+                           :min="0"
+                           :max="formState.qualitity"
+                           style="width: 100%" />
         </el-form-item>
-
-        <el-form-item label="澶囨敞" prop="remark">
-          <el-input v-model="formState.remark" type="textarea" />
+        <el-form-item label="澶囨敞"
+                      prop="remark">
+          <el-input v-model="formState.remark"
+                    type="textarea" />
         </el-form-item>
       </el-form>
-
       <!-- 浜у搧閫夋嫨寮圭獥 -->
-      <ProductSelectDialog
-          v-model="showProductSelectDialog"
-          @confirm="handleProductSelect"
-          :top-product-parent-id="props.topProductParentId"
-          single
-        />
+      <ProductSelectDialog v-model="showProductSelectDialog"
+                           @confirm="handleProductSelect"
+                           :top-product-parent-id="props.topProductParentId"
+                           single />
       <template #footer>
         <div class="dialog-footer">
-          <el-button type="primary" @click="handleSubmit">纭</el-button>
+          <el-button type="primary"
+                     @click="handleSubmit">纭</el-button>
           <el-button @click="closeModal">鍙栨秷</el-button>
         </div>
       </template>
@@ -99,62 +100,27 @@
 </template>
 
 <script setup>
-import {ref, computed, watch, getCurrentInstance} from "vue";
-import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
-import {addStockInRecordOnly} from "@/api/inventoryManagement/stockInventory.js";
-import {createStockUnInventory} from "@/api/inventoryManagement/stockUninventory.js";
+  import { ref, computed, watch, getCurrentInstance } from "vue";
+  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+  import { addStockInRecordOnly } from "@/api/inventoryManagement/stockInventory.js";
+  import { createStockUnInventory } from "@/api/inventoryManagement/stockUninventory.js";
 
-const props = defineProps({
-  visible: {
-    type: Boolean,
-    required: true,
-  },
-  topProductParentId: {
-    type: Number,
-    default: undefined,
-    required: false,
-  }
-});
+  const props = defineProps({
+    visible: {
+      type: Boolean,
+      required: true,
+    },
+    topProductParentId: {
+      type: Number,
+      default: undefined,
+      required: false,
+    },
+  });
 
-const emit = defineEmits(['update:visible', 'completed']);
+  const emit = defineEmits(["update:visible", "completed"]);
 
-// 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
-const formState = ref({
-  productId: undefined,
-  productModelId: undefined,
-  productName: "",
-  productModelName: "",
-  unit: "",
-  type: undefined,
-  qualitity: 0,
-  batchNo: null,
-  warnNum: 0,
-  remark: '',
-});
-
-const isShow = computed({
-  get() {
-    return props.visible;
-  },
-  set(val) {
-    emit('update:visible', val);
-  },
-});
-
-const showProductSelectDialog = ref(false);
-
-// 鎵瑰彿涓虹┖鏃惰浆涓� null
-watch(() => formState.value.batchNo, (val) => {
-  if (val === '') {
-    formState.value.batchNo = null;
-  }
-});
-
-let { proxy } = getCurrentInstance()
-
-const closeModal = () => {
-  // 閲嶇疆琛ㄥ崟鏁版嵁
-  formState.value = {
+  // 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
+  const formState = ref({
     productId: undefined,
     productModelId: undefined,
     productName: "",
@@ -164,65 +130,101 @@
     qualitity: 0,
     batchNo: null,
     warnNum: 0,
-    remark: '',
-  };
-  isShow.value = false;
-};
+    remark: "",
+  });
 
-// 浜у搧閫夋嫨澶勭悊
-const handleProductSelect = async (products) => {
-  if (products && products.length > 0) {
-    const product = products[0];
-    formState.value.productId = product.productId;
-    formState.value.productName = product.productName;
-    formState.value.productModelName = product.model;
-    formState.value.productModelId = product.id;
-    formState.value.unit = product.unit;
-    showProductSelectDialog.value = false;
-    // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
-    proxy.$refs["formRef"]?.validateField('productModelId');
-  }
-};
+  const isShow = computed({
+    get() {
+      return props.visible;
+    },
+    set(val) {
+      emit("update:visible", val);
+    },
+  });
 
-const handleSubmit = () => {
-  proxy.$refs["formRef"].validate(valid => {
-    if (valid) {
-      // 楠岃瘉鏄惁閫夋嫨浜嗕骇鍝佸拰瑙勬牸
-      if (!formState.value.productModelId) {
-        proxy.$modal.msgError("璇烽�夋嫨浜у搧");
-        return;
-      }
-      if (!formState.value.productModelId) {
-        proxy.$modal.msgError("璇烽�夋嫨瑙勬牸");
-        return;
-      }
-      if (formState.value.type === 'qualified') {
-        addStockInRecordOnly(formState.value).then(res => {
-          // 鍏抽棴妯℃�佹
-          isShow.value = false;
-          // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
-          emit('completed');
-          proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-        })
-      } else {
-        formState.value.warnNum = 0;
-        createStockUnInventory(formState.value).then(res => {
-          // 鍏抽棴妯℃�佹
-          isShow.value = false;
-          // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
-          emit('completed');
-          proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-        })
-      }
+  const showProductSelectDialog = ref(false);
 
+  // 鎵瑰彿涓虹┖鏃惰浆涓� null
+  watch(
+    () => formState.value.batchNo,
+    val => {
+      if (val === "") {
+        formState.value.batchNo = null;
+      }
     }
-  })
-};
+  );
 
+  let { proxy } = getCurrentInstance();
 
-defineExpose({
-  closeModal,
-  handleSubmit,
-  isShow,
-});
+  const closeModal = () => {
+    // 閲嶇疆琛ㄥ崟鏁版嵁
+    formState.value = {
+      productId: undefined,
+      productModelId: undefined,
+      productName: "",
+      productModelName: "",
+      unit: "",
+      type: undefined,
+      qualitity: 0,
+      batchNo: null,
+      warnNum: 0,
+      remark: "",
+    };
+    isShow.value = false;
+  };
+
+  // 浜у搧閫夋嫨澶勭悊
+  const handleProductSelect = async products => {
+    if (products && products.length > 0) {
+      const product = products[0];
+      formState.value.productId = product.productId;
+      formState.value.productName = product.productName;
+      formState.value.productModelName = product.model;
+      formState.value.productModelId = product.id;
+      formState.value.unit = product.unit;
+      showProductSelectDialog.value = false;
+      // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
+      proxy.$refs["formRef"]?.validateField("productModelId");
+    }
+  };
+
+  const handleSubmit = () => {
+    proxy.$refs["formRef"].validate(valid => {
+      if (valid) {
+        // 楠岃瘉鏄惁閫夋嫨浜嗕骇鍝佸拰瑙勬牸
+        if (!formState.value.productModelId) {
+          proxy.$modal.msgError("璇烽�夋嫨浜у搧");
+          return;
+        }
+        if (!formState.value.productModelId) {
+          proxy.$modal.msgError("璇烽�夋嫨瑙勬牸");
+          return;
+        }
+        if (formState.value.type === "qualified") {
+          addStockInRecordOnly(formState.value).then(res => {
+            // 鍏抽棴妯℃�佹
+            isShow.value = false;
+            // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
+            emit("completed");
+            proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+          });
+        } else {
+          formState.value.warnNum = 0;
+          createStockUnInventory(formState.value).then(res => {
+            // 鍏抽棴妯℃�佹
+            isShow.value = false;
+            // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
+            emit("completed");
+            proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+          });
+        }
+      }
+    });
+  };
+
+  defineExpose({
+    closeModal,
+    handleSubmit,
+    isShow,
+  });
 </script>
diff --git a/src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue b/src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue
index 9c50fc8..a83ff6a 100644
--- a/src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue
+++ b/src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue
@@ -1,44 +1,82 @@
 <template>
   <div>
-    <el-dialog v-model="dialogVisible" title="棰嗘枡璇︽儏" width="1400px" @close="handleClose">
-      <el-table v-loading="materialDetailLoading" :data="materialDetailTableData" border row-key="id">
-        <el-table-column label="宸ュ簭鍚嶇О" prop="processName" min-width="180" />
-        <el-table-column label="鍘熸枡鍚嶇О" prop="materialName" min-width="160" />
-        <el-table-column label="鍘熸枡鍨嬪彿" prop="materialModel" min-width="180" />
-        <el-table-column label="闇�姹傛暟閲�" prop="requiredQty" min-width="110" />
-        <el-table-column label="璁¢噺鍗曚綅" prop="unit" width="100" />
-        <el-table-column label="棰嗙敤鏁伴噺" prop="pickQty" min-width="110" />
-        <el-table-column label="琛ユ枡鏁伴噺" min-width="120">
+    <el-dialog v-model="dialogVisible"
+               title="棰嗘枡璇︽儏"
+               width="1400px"
+               @close="handleClose">
+      <el-table v-loading="materialDetailLoading"
+                :data="materialDetailTableData"
+                border
+                row-key="id">
+        <el-table-column label="宸ュ簭鍚嶇О"
+                         prop="operationName"
+                         min-width="180" />
+        <el-table-column label="鍘熸枡鍚嶇О"
+                         prop="productName"
+                         min-width="160" />
+        <el-table-column label="鍘熸枡鍨嬪彿"
+                         prop="model"
+                         min-width="180" />
+        <el-table-column label="鎵瑰彿"
+                         prop="batchNo"
+                         min-width="150" />
+        <el-table-column label="闇�姹傛暟閲�"
+                         prop="demandedQuantity"
+                         min-width="110" />
+        <el-table-column label="璁¢噺鍗曚綅"
+                         prop="unit"
+                         width="100" />
+        <el-table-column label="棰嗙敤鏁伴噺"
+                         prop="pickQuantity"
+                         min-width="110" />
+        <el-table-column label="琛ユ枡鏁伴噺"
+                         min-width="120">
           <template #default="{ row }">
-            <el-button type="primary" link @click="handleViewSupplementRecord(row)">
+            <el-button type="primary"
+                       link
+                       @click="handleViewSupplementRecord(row)">
               {{ row.supplementQty ?? 0 }}
             </el-button>
           </template>
         </el-table-column>
-        <el-table-column label="閫�鏂欐暟閲�" prop="returnQty" min-width="110" />
-        <el-table-column label="瀹為檯鏁伴噺" prop="actualQty" min-width="110" />
+        <el-table-column label="閫�鏂欐暟閲�"
+                         prop="returnQty"
+                         min-width="110" />
+        <el-table-column label="瀹為檯鏁伴噺"
+                         prop="actualQty"
+                         min-width="110" />
       </el-table>
       <template #footer>
         <span class="dialog-footer">
-          <el-button
-            type="warning"
-            :loading="materialReturnConfirming"
-            :disabled="!canOpenReturnSummary"
-            @click="openReturnSummaryDialog"
-          >
+          <el-button type="warning"
+                     :loading="materialReturnConfirming"
+                     :disabled="!canOpenReturnSummary"
+                     @click="openReturnSummaryDialog">
             閫�鏂欑‘璁�
           </el-button>
           <el-button @click="dialogVisible = false">鍙栨秷</el-button>
         </span>
       </template>
     </el-dialog>
-
-    <el-dialog v-model="supplementRecordDialogVisible" title="琛ユ枡璁板綍" width="800px">
-      <el-table v-loading="supplementRecordLoading" :data="supplementRecordTableData" border row-key="id">
-        <el-table-column label="琛ユ枡鏁伴噺" prop="supplementQty" min-width="120" />
-        <el-table-column label="琛ユ枡浜�" prop="supplementUserName" min-width="120" />
-        <el-table-column label="琛ユ枡鏃ユ湡" prop="supplementTime" min-width="160" />
-        <el-table-column label="琛ユ枡鍘熷洜" prop="supplementReason" min-width="200" />
+    <el-dialog v-model="supplementRecordDialogVisible"
+               title="琛ユ枡璁板綍"
+               width="800px">
+      <el-table v-loading="supplementRecordLoading"
+                :data="supplementRecordTableData"
+                border
+                row-key="id">
+        <el-table-column label="琛ユ枡鏁伴噺"
+                         prop="supplementQty"
+                         min-width="120" />
+        <el-table-column label="琛ユ枡浜�"
+                         prop="supplementUserName"
+                         min-width="120" />
+        <el-table-column label="琛ユ枡鏃ユ湡"
+                         prop="supplementTime"
+                         min-width="160" />
+        <el-table-column label="琛ユ枡鍘熷洜"
+                         prop="supplementReason"
+                         min-width="200" />
       </el-table>
       <template #footer>
         <span class="dialog-footer">
@@ -46,18 +84,30 @@
         </span>
       </template>
     </el-dialog>
-
-    <el-dialog v-model="returnSummaryDialogVisible" title="閫�鏂欐眹鎬荤‘璁�" width="900px">
-      <el-table :data="returnSummaryList" border row-key="summaryKey">
-        <el-table-column label="鍘熸枡鍚嶇О" prop="materialName" min-width="180" />
-        <el-table-column label="鍘熸枡鍨嬪彿" prop="materialModel" min-width="180" />
-        <el-table-column label="璁¢噺鍗曚綅" prop="unit" min-width="100" />
-        <el-table-column label="閫�鏂欐眹鎬绘暟閲�" prop="returnQtyTotal" min-width="140" />
+    <el-dialog v-model="returnSummaryDialogVisible"
+               title="閫�鏂欐眹鎬荤‘璁�"
+               width="900px">
+      <el-table :data="returnSummaryList"
+                border
+                row-key="summaryKey">
+        <el-table-column label="鍘熸枡鍚嶇О"
+                         prop="materialName"
+                         min-width="180" />
+        <el-table-column label="鍘熸枡鍨嬪彿"
+                         prop="materialModel"
+                         min-width="180" />
+        <el-table-column label="璁¢噺鍗曚綅"
+                         prop="unit"
+                         min-width="100" />
+        <el-table-column label="閫�鏂欐眹鎬绘暟閲�"
+                         prop="returnQtyTotal"
+                         min-width="140" />
       </el-table>
-
       <template #footer>
         <span class="dialog-footer">
-          <el-button type="primary" :loading="materialReturnConfirming" @click="handleReturnConfirm">纭鎻愪氦</el-button>
+          <el-button type="primary"
+                     :loading="materialReturnConfirming"
+                     @click="handleReturnConfirm">纭鎻愪氦</el-button>
           <el-button @click="returnSummaryDialogVisible = false">鍙栨秷</el-button>
         </span>
       </template>
@@ -66,116 +116,126 @@
 </template>
 
 <script setup>
-import { computed, ref, watch } from "vue";
-import { ElMessage } from "element-plus";
-import { listMaterialPickingDetail, listMaterialSupplementRecord, confirmMaterialReturn } from "@/api/productionManagement/productionOrder.js";
+  import { computed, ref, watch } from "vue";
+  import { ElMessage } from "element-plus";
+  import {
+    listMaterialPickingDetail,
+    listMaterialSupplementRecord,
+    confirmMaterialReturn,
+  } from "@/api/productionManagement/productionOrder.js";
 
-const props = defineProps({
-  modelValue: { type: Boolean, default: false },
-  orderRow: { type: Object, default: null },
-});
-const emit = defineEmits(["update:modelValue", "confirmed"]);
-
-const dialogVisible = computed({
-  get: () => props.modelValue,
-  set: val => emit("update:modelValue", val),
-});
-
-const materialDetailLoading = ref(false);
-const materialDetailTableData = ref([]);
-const materialReturnConfirming = ref(false);
-const supplementRecordDialogVisible = ref(false);
-const supplementRecordLoading = ref(false);
-const supplementRecordTableData = ref([]);
-const returnSummaryDialogVisible = ref(false);
-const returnSummaryList = ref([]);
-const calcReturnQty = item =>
-  Number(item.pickQty || 0) + Number(item.supplementQty || 0) - Number(item.actualQty || 0);
-const canOpenReturnSummary = computed(() =>
-  materialDetailTableData.value.some(item => calcReturnQty(item) > 0)
-);
-
-const loadDetailList = async () => {
-  if (!props.orderRow?.id) return;
-  materialDetailLoading.value = true;
-  materialDetailTableData.value = [];
-  try {
-    const res = await listMaterialPickingDetail({ orderId: props.orderRow.id });
-    materialDetailTableData.value = res.data || [];
-  } finally {
-    materialDetailLoading.value = false;
-  }
-};
-
-watch(
-  () => dialogVisible.value,
-  visible => {
-    if (visible) {
-      loadDetailList();
-    }
-  }
-);
-
-const handleClose = () => {
-  materialDetailTableData.value = [];
-};
-
-const handleViewSupplementRecord = async row => {
-  if (!row?.id) return;
-  supplementRecordDialogVisible.value = true;
-  supplementRecordLoading.value = true;
-  supplementRecordTableData.value = [];
-  try {
-    const res = await listMaterialSupplementRecord({ materialDetailId: row.id });
-    supplementRecordTableData.value = res.data || [];
-  } finally {
-    supplementRecordLoading.value = false;
-  }
-};
-
-const buildReturnSummary = () => {
-  const map = new Map();
-  materialDetailTableData.value.forEach(item => {
-    const returnQty = calcReturnQty(item);
-    if (returnQty <= 0) return;
-    const key = `${item.materialModelId || ""}_${item.materialName || ""}_${item.materialModel || ""}_${item.unit || ""}`;
-    const old = map.get(key) || {
-      summaryKey: key,
-      materialName: item.materialName || "",
-      materialModel: item.materialModel || "",
-      unit: item.unit || "",
-      returnQtyTotal: 0,
-    };
-    old.returnQtyTotal += returnQty;
-    map.set(key, old);
+  const props = defineProps({
+    modelValue: { type: Boolean, default: false },
+    orderRow: { type: Object, default: null },
   });
-  return Array.from(map.values());
-};
+  const emit = defineEmits(["update:modelValue", "confirmed"]);
 
-const openReturnSummaryDialog = async () => {
-  if (!canOpenReturnSummary.value) {
-    ElMessage.warning("閫�鏂欐暟閲�=棰嗙敤鏁伴噺+琛ユ枡鏁伴噺-瀹為檯鏁伴噺锛屼笖闇�澶т簬0");
-    return;
-  }
-  returnSummaryList.value = buildReturnSummary();
-  returnSummaryDialogVisible.value = true;
-};
+  const dialogVisible = computed({
+    get: () => props.modelValue,
+    set: val => emit("update:modelValue", val),
+  });
 
-const handleReturnConfirm = async () => {
-  if (!props.orderRow?.id) return;
-  materialReturnConfirming.value = true;
-  try {
-    await confirmMaterialReturn({
-      orderId: props.orderRow.id,
-      returnSummaryList: returnSummaryList.value,
+  const materialDetailLoading = ref(false);
+  const materialDetailTableData = ref([]);
+  const materialReturnConfirming = ref(false);
+  const supplementRecordDialogVisible = ref(false);
+  const supplementRecordLoading = ref(false);
+  const supplementRecordTableData = ref([]);
+  const returnSummaryDialogVisible = ref(false);
+  const returnSummaryList = ref([]);
+  const calcReturnQty = item =>
+    Number(item.pickQuantity || 0) +
+    Number(item.supplementQty || 0) -
+    Number(item.actualQty || 0);
+  const canOpenReturnSummary = computed(() =>
+    materialDetailTableData.value.some(item => calcReturnQty(item) > 0)
+  );
+
+  const loadDetailList = async () => {
+    if (!props.orderRow?.id) return;
+    materialDetailLoading.value = true;
+    materialDetailTableData.value = [];
+    try {
+      const res = await listMaterialPickingDetail(props.orderRow.id);
+      materialDetailTableData.value = res.data || [];
+    } finally {
+      materialDetailLoading.value = false;
+    }
+  };
+
+  watch(
+    () => dialogVisible.value,
+    visible => {
+      if (visible) {
+        loadDetailList();
+      }
+    }
+  );
+
+  const handleClose = () => {
+    materialDetailTableData.value = [];
+  };
+
+  const handleViewSupplementRecord = async row => {
+    if (!row?.id) return;
+    supplementRecordDialogVisible.value = true;
+    supplementRecordLoading.value = true;
+    supplementRecordTableData.value = [];
+    try {
+      const res = await listMaterialSupplementRecord({
+        materialDetailId: row.id,
+      });
+      supplementRecordTableData.value = res.data || [];
+    } finally {
+      supplementRecordLoading.value = false;
+    }
+  };
+
+  const buildReturnSummary = () => {
+    const map = new Map();
+    materialDetailTableData.value.forEach(item => {
+      const returnQty = calcReturnQty(item);
+      if (returnQty <= 0) return;
+      const key = `${item.productModelId || ""}_${item.productName || ""}_${
+        item.model || ""
+      }_${item.unit || ""}`;
+      const old = map.get(key) || {
+        summaryKey: key,
+        materialName: item.productName || "",
+        materialModel: item.model || "",
+        unit: item.unit || "",
+        returnQtyTotal: 0,
+      };
+      old.returnQtyTotal += returnQty;
+      map.set(key, old);
     });
-    returnSummaryDialogVisible.value = false;
-    dialogVisible.value = false;
-    emit("confirmed");
-  } finally {
-    materialReturnConfirming.value = false;
-  }
-};
+    return Array.from(map.values());
+  };
+
+  const openReturnSummaryDialog = async () => {
+    if (!canOpenReturnSummary.value) {
+      ElMessage.warning("閫�鏂欐暟閲�=棰嗙敤鏁伴噺+琛ユ枡鏁伴噺-瀹為檯鏁伴噺锛屼笖闇�澶т簬0");
+      return;
+    }
+    returnSummaryList.value = buildReturnSummary();
+    returnSummaryDialogVisible.value = true;
+  };
+
+  const handleReturnConfirm = async () => {
+    if (!props.orderRow?.id) return;
+    materialReturnConfirming.value = true;
+    try {
+      await confirmMaterialReturn({
+        orderId: props.orderRow.id,
+        returnSummaryList: returnSummaryList.value,
+      });
+      returnSummaryDialogVisible.value = false;
+      dialogVisible.value = false;
+      emit("confirmed");
+    } finally {
+      materialReturnConfirming.value = false;
+    }
+  };
 </script>
 
 <style scoped lang="scss"></style>
diff --git a/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue b/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
index 6b040d6..991f2a1 100644
--- a/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
+++ b/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
@@ -13,25 +13,25 @@
                 border
                 row-key="tempId">
         <el-table-column label="宸ュ簭鍚嶇О"
-                         min-width="180">
+                         min-width="140">
           <template #default="{ row }">
-            <span v-if="row.bom === true">{{ row.processName || "-" }}</span>
+            <span v-if="row.bom === true">{{ row.operationName || "-" }}</span>
             <el-select v-else
-                       v-model="row.processName"
+                       v-model="row.operationName"
                        placeholder="璇烽�夋嫨宸ュ簭"
                        clearable
                        filterable
                        style="width: 100%;"
                        @change="val => handleProcessNameChange(row, val)">
               <el-option v-for="item in processOptions"
-                         :key="item.id"
+                         :key="item.technologyOperationId"
                          :label="item.name"
                          :value="item.name" />
             </el-select>
           </template>
         </el-table-column>
         <el-table-column label="鍘熸枡鍚嶇О"
-                         min-width="160">
+                         min-width="140">
           <template #default="{ row }">
             <span v-if="row.bom === true">{{ row.materialName || "-" }}</span>
             <el-button v-else
@@ -43,17 +43,37 @@
           </template>
         </el-table-column>
         <el-table-column label="鍘熸枡鍨嬪彿"
-                         min-width="180">
+                         min-width="140">
           <template #default="{ row }">
             {{ row.materialModel || "-" }}
+          </template>
+        </el-table-column>
+        <!-- 鎵瑰彿澶氶�� -->
+        <el-table-column min-width="200">
+          <template #header>
+            <span style="color: #f56c6c; margin-right: 4px;">*</span>
+            <span>鎵瑰彿</span>
+          </template>
+          <template #default="{ row }">
+            <el-select v-model="row.batchNo"
+                       multiple
+                       collapse-tags
+                       collapse-tags-indicator
+                       placeholder="璇烽�夋嫨鎵瑰彿"
+                       style="width: 100%;">
+              <el-option v-for="item in row.batchNoList"
+                         :key="item"
+                         :label="item"
+                         :value="item" />
+            </el-select>
           </template>
         </el-table-column>
         <el-table-column label="闇�姹傛暟閲�"
                          min-width="120">
           <template #default="{ row }">
-            <span v-if="row.bom === true">{{ row.requiredQty ?? "-" }}</span>
+            <span v-if="row.bom === true">{{ row.demandedQuantity ?? "-" }}</span>
             <el-input-number v-else
-                             v-model="row.requiredQty"
+                             v-model="row.demandedQuantity"
                              :min="0"
                              :precision="3"
                              :step="1"
@@ -63,7 +83,7 @@
           </template>
         </el-table-column>
         <el-table-column label="璁¢噺鍗曚綅"
-                         width="120">
+                         width="100">
           <template #default="{ row }">
             {{ row.unit || "-" }}
           </template>
@@ -110,12 +130,18 @@
   import { computed, ref, watch } from "vue";
   import { ElMessage } from "element-plus";
   import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
-  import { findProductProcessRouteItemList } from "@/api/productionManagement/productProcessRoute.js";
+  import {
+    findProductProcessRouteItemList,
+    listMain,
+  } from "@/api/productionManagement/productProcessRoute.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 },
@@ -139,16 +165,22 @@
   const createMaterialRow = (row = {}) => ({
     tempId: row.id || `temp_${++materialTempId}`,
     id: row.id,
-    processId: row.processId,
-    productProcessId: row.productProcessId || row.processId,
-    processName: row.processName || "",
+    processId: row.processId || row.technologyOperationId,
+    technologyOperationId: row.technologyOperationId || row.processId,
+    operationName: row.operationName || "",
     bom: row.bom === true,
-    materialModelId: row.materialModelId,
-    materialName: row.materialName || "",
-    materialModel: row.materialModel || "",
-    requiredQty: Number(row.requiredQty ?? 0),
+    materialModelId: row.materialModelId || row.productModelId,
+    materialName: row.materialName || row.productName || "",
+    materialModel: row.materialModel || row.model || "",
+    demandedQuantity: Number(row.requiredQty ?? row.demandedQuantity ?? 0),
     unit: row.unit || "",
-    pickQty: Number(row.pickQty ?? row.requiredQty ?? 0),
+    pickQty: Number(row.pickQty ?? row.pickQuantity ?? 0),
+    batchNo: row.batchNo
+      ? typeof row.batchNo === "string"
+        ? row.batchNo.split(",")
+        : row.batchNo
+      : [],
+    batchNoList: row.batchNoList || [],
   });
 
   const getProcessOptions = async () => {
@@ -161,19 +193,20 @@
       : res?.data?.records || [];
     const processMap = new Map();
     routeList.forEach(item => {
-      const processId = item.processId;
-      const processName = item.processName;
-      if (!processId || !processName) return;
-      const key = `${processId}_${processName}`;
+      const processId = item.technologyOperationId;
+      const operationName = item.operationName;
+      if (!processId || !operationName) return;
+      const key = `${processId}_${operationName}`;
       if (!processMap.has(key)) {
         processMap.set(key, {
           id: processId,
-          name: processName,
+          name: operationName,
         });
       }
     });
     processOptions.value = Array.from(processMap.values());
   };
+  const isDetail = ref(true);
 
   const loadMaterialData = async () => {
     if (!props.orderRow?.id) return;
@@ -181,23 +214,23 @@
     materialTableData.value = [];
     await getProcessOptions();
     try {
-      const detailRes = await listMaterialPickingDetail({
-        orderId: props.orderRow.id,
-      });
+      const detailRes = await listMaterialPickingDetail(props.orderRow.id);
       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 ledgerRes = await listMaterialPickingLedger({
-        orderId: props.orderRow.id,
-      });
-      const ledgerList = Array.isArray(ledgerRes?.data)
-        ? ledgerRes.data
-        : ledgerRes?.data?.records || [];
-      materialTableData.value = ledgerList.map(item => createMaterialRow(item));
     } finally {
       materialTableLoading.value = false;
     }
@@ -225,14 +258,16 @@
     materialTableData.value.splice(index, 1);
   };
 
-  const handleProcessNameChange = (row, processName) => {
-    const process = processOptions.value.find(item => item.name === processName);
-    row.productProcessId = process?.id;
+  const handleProcessNameChange = (row, operationName) => {
+    const process = processOptions.value.find(
+      item => item.name === operationName
+    );
+    row.technologyOperationId = process?.technologyOperationId;
   };
 
   const handleRequiredQtyChange = (row, val) => {
     const required = Number(val ?? 0);
-    row.requiredQty = required;
+    row.demandedQuantity = required;
     if (!row.pickQty || Number(row.pickQty) === 0) {
       row.pickQty = required;
     }
@@ -246,6 +281,8 @@
   };
 
   const handleMaterialProductConfirm = products => {
+    console.log(products, "products");
+
     if (!products || products.length === 0) return;
     const index = currentMaterialSelectRowIndex.value;
     if (index < 0 || !materialTableData.value[index]) return;
@@ -257,6 +294,7 @@
       product.materialName || product.productName || product.name || "";
     row.materialModel = product.materialModel || product.model || "";
     row.unit = product.unit || product.measureUnit || "";
+    row.batchNoList = product.batchNoList;
     currentMaterialSelectRowIndex.value = -1;
     materialProductDialogVisible.value = false;
   };
@@ -266,22 +304,24 @@
       return { valid: false, message: "璇峰厛鏂板棰嗘枡鏁版嵁" };
     }
     const invalidNewRow = materialTableData.value.find(
-      item => item.bom !== true && (!item.processName || !item.materialName)
+      item => item.bom !== true && (!item.operationName || !item.materialName)
     );
     if (invalidNewRow) {
       return { valid: false, message: "鏂板琛岀殑宸ュ簭鍚嶇О鍜屽師鏂欏悕绉颁负蹇呭~椤�" };
     }
     const invalidRow = materialTableData.value.find(
       item =>
-        !item.processName ||
+        !item.operationName ||
         !item.materialName ||
-        item.requiredQty === null ||
-        item.requiredQty === undefined ||
+        !item.batchNo ||
+        item.batchNo.length === 0 ||
+        item.demandedQuantity === null ||
+        item.demandedQuantity === undefined ||
         item.pickQty === null ||
         item.pickQty === undefined
     );
     if (invalidRow) {
-      return { valid: false, message: "璇峰畬鍠勫伐搴忋�佸師鏂欏拰鏁伴噺鍚庡啀淇濆瓨" };
+      return { valid: false, message: "璇峰畬鍠勫伐搴忋�佸師鏂欍�佹壒鍙峰拰鏁伴噺鍚庡啀淇濆瓨" };
     }
     return { valid: true, message: "" };
   };
@@ -295,22 +335,49 @@
     }
     materialSaving.value = true;
     try {
-      await saveMaterialPickingLedger({
-        orderId: props.orderRow.id,
-        items: materialTableData.value.map(item => ({
-          id: item.id,
-          processId: item.processName,
-          productProcessId: item.productProcessId,
-          processName: item.processName,
-          bom: item.bom === true,
-          materialModelId: item.materialModelId,
-          materialName: item.materialName,
-          materialModel: item.materialModel,
-          requiredQty: item.requiredQty,
-          unit: item.unit,
-          pickQty: item.pickQty,
-        })),
-      });
+      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,
+          })),
+        });
+      }
+
+      ElMessage({ message: "棰嗘枡鎴愬姛", type: "success" });
       emit("saved");
       dialogVisible.value = false;
     } finally {
diff --git a/src/views/productionManagement/productionOrder/components/MaterialSupplementDialog.vue b/src/views/productionManagement/productionOrder/components/MaterialSupplementDialog.vue
new file mode 100644
index 0000000..67e44f1
--- /dev/null
+++ b/src/views/productionManagement/productionOrder/components/MaterialSupplementDialog.vue
@@ -0,0 +1,159 @@
+<template>
+  <el-dialog v-model="dialogVisible"
+             title="琛ユ枡"
+             width="1200px"
+             @close="handleClose">
+    <el-table v-loading="loading"
+              :data="tableData"
+              border
+              row-key="id">
+      <el-table-column label="宸ュ簭鍚嶇О"
+                       prop="operationName"
+                       min-width="140" />
+      <el-table-column label="鍘熸枡鍚嶇О"
+                       prop="productName"
+                       min-width="140" />
+      <el-table-column label="鍘熸枡鍨嬪彿"
+                       prop="model"
+                       min-width="140" />
+      <el-table-column label="璁¢噺鍗曚綅"
+                       prop="unit"
+                       width="100" />
+      <el-table-column label="棰嗙敤鏁伴噺"
+                       prop="pickQuantity"
+                       width="100" />
+      <el-table-column label="琛ユ枡鏁伴噺"
+                       min-width="150">
+        <template #default="{ row }">
+          <el-input-number v-model="row.newSupplementQty"
+                           :min="0"
+                           :precision="3"
+                           :step="1"
+                           controls-position="right"
+                           placeholder="杈撳叆琛ユ枡鏁伴噺"
+                           style="width: 100%;" />
+        </template>
+      </el-table-column>
+      <el-table-column label="琛ユ枡鍘熷洜"
+                       min-width="200">
+        <template #default="{ row }">
+          <el-input v-model="row.newSupplementReason"
+                    placeholder="杈撳叆琛ユ枡鍘熷洜"
+                    maxlength="200"
+                    show-word-limit />
+        </template>
+      </el-table-column>
+    </el-table>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button type="primary"
+                   :loading="submitting"
+                   @click="handleSubmit">纭� 瀹�</el-button>
+        <el-button @click="dialogVisible = false">鍙� 娑�</el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+  import { computed, ref, watch } from "vue";
+  import { ElMessage } from "element-plus";
+  import {
+    listMaterialPickingDetail,
+    updateMaterialPickingLedger,
+  } from "@/api/productionManagement/productionOrder.js";
+
+  const props = defineProps({
+    modelValue: { type: Boolean, default: false },
+    orderRow: { type: Object, default: null },
+  });
+  const emit = defineEmits(["update:modelValue", "saved"]);
+
+  const dialogVisible = computed({
+    get: () => props.modelValue,
+    set: val => emit("update:modelValue", val),
+  });
+
+  const loading = ref(false);
+  const submitting = ref(false);
+  const tableData = ref([]);
+
+  const loadData = async () => {
+    if (!props.orderRow?.id) return;
+    loading.value = true;
+    try {
+      const res = await listMaterialPickingDetail(props.orderRow.id);
+      tableData.value = (res.data || []).map(item => ({
+        ...item,
+        newSupplementQty: 0,
+        newSupplementReason: "",
+      }));
+    } catch (e) {
+      console.error("鑾峰彇鐗╂枡鏄庣粏澶辫触锛�", e);
+      ElMessage.error("鑾峰彇鐗╂枡鏄庣粏澶辫触");
+    } finally {
+      loading.value = false;
+    }
+  };
+
+  watch(
+    () => dialogVisible.value,
+    visible => {
+      if (visible) {
+        loadData();
+      }
+    }
+  );
+
+  const handleClose = () => {
+    tableData.value = [];
+  };
+
+  const handleSubmit = async () => {
+    const supplementList = tableData.value.filter(
+      item => item.newSupplementQty > 0
+    );
+    if (supplementList.length === 0) {
+      ElMessage.warning("璇疯嚦灏戣緭鍏ヤ竴鏉¤ˉ鏂欐暟閲�");
+      return;
+    }
+
+    const invalidRow = supplementList.find(item => !item.newSupplementReason);
+    if (invalidRow) {
+      ElMessage.warning("璇疯緭鍏ヨˉ鏂欏師鍥�");
+      return;
+    }
+
+    submitting.value = true;
+    try {
+      await updateMaterialPickingLedger({
+        productionOrderId: props.orderRow.id,
+        productionOrderPickDto: tableData.value.map(item => ({
+          id: item.id,
+          technologyOperationId: item.technologyOperationId,
+          operationName: item.operationName,
+          bom: item.bom === true,
+          productModelId: item.productModelId,
+          demandedQuantity: item.demandedQuantity,
+          unit: item.unit,
+          pickQuantity: item.pickQuantity,
+          batchNo: item.batchNo,
+          feedingQuantity: item.newSupplementQty || 0,
+          feedingReason: item.newSupplementReason || "",
+          pickType: 2,
+        })),
+      });
+      ElMessage.success("琛ユ枡鎴愬姛");
+      dialogVisible.value = false;
+      emit("saved");
+    } catch (e) {
+      console.error("琛ユ枡澶辫触锛�", e);
+      ElMessage.error("琛ユ枡澶辫触");
+    } finally {
+      submitting.value = false;
+    }
+  };
+</script>
+
+<style scoped lang="scss">
+</style>
diff --git a/src/views/productionManagement/productionOrder/index.vue b/src/views/productionManagement/productionOrder/index.vue
index 4ecd0c9..f348aae 100644
--- a/src/views/productionManagement/productionOrder/index.vue
+++ b/src/views/productionManagement/productionOrder/index.vue
@@ -304,7 +304,7 @@
       label: "鎿嶄綔",
       align: "center",
       fixed: "right",
-      width: 340,
+      width: 360,
       operation: [
         {
           name: "宸ヨ壓璺嚎",
@@ -337,27 +337,20 @@
             showSourceData(row);
           },
         },
-        // {
-        //   name: "浜у搧缁撴瀯",
-        //   type: "text",
-        //   clickFun: row => {
-        //     showProductStructure(row);
-        //   },
-        // },
-        // {
-        //   name: "棰嗘枡",
-        //   type: "text",
-        //   clickFun: row => {
-        //     openMaterialDialog(row);
-        //   },
-        // },
-        // {
-        //   name: "棰嗘枡璇︽儏",
-        //   type: "text",
-        //   clickFun: row => {
-        //     openMaterialDetailDialog(row);
-        //   },
-        // },
+        {
+          name: "棰嗘枡",
+          type: "text",
+          clickFun: row => {
+            openMaterialDialog(row);
+          },
+        },
+        {
+          name: "棰嗘枡璇︽儏",
+          type: "text",
+          clickFun: row => {
+            openMaterialDetailDialog(row);
+          },
+        },
       ],
     },
   ]);
diff --git a/src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue b/src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue
index 575d888..45944b5 100644
--- a/src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue
+++ b/src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue
@@ -1,86 +1,119 @@
 <template>
   <div>
-    <el-dialog
-      v-model="dialogVisible"
-      title="鐗╂枡"
-      width="1200px"
-      @close="handleCloseMaterialDialog"
-    >
-      <el-table v-loading="materialTableLoading" :data="materialTableData" border row-key="id">
-        <el-table-column label="宸ュ簭鍚嶇О" prop="processName" min-width="140" />
-        <el-table-column label="鍘熸枡鍚嶇О" prop="materialName" min-width="140" />
-        <el-table-column label="鍘熸枡鍨嬪彿" prop="materialModel" min-width="140" />
-        <el-table-column label="璁¢噺鍗曚綅" prop="unit" min-width="100" />
-        <el-table-column label="绾胯竟浠撴暟閲�" prop="pickQty" min-width="100" />
-        <el-table-column label="琛ユ枡鏁伴噺" prop="supplementQty" min-width="100" />
-        <el-table-column label="瀹為檯鏁伴噺" min-width="140">
+    <el-dialog v-model="dialogVisible"
+               title="鐗╂枡"
+               width="1200px"
+               @close="handleCloseMaterialDialog">
+      <el-table v-loading="materialTableLoading"
+                :data="materialTableData"
+                border
+                row-key="id">
+        <el-table-column label="宸ュ簭鍚嶇О"
+                         prop="processName"
+                         min-width="140" />
+        <el-table-column label="鍘熸枡鍚嶇О"
+                         prop="materialName"
+                         min-width="140" />
+        <el-table-column label="鍘熸枡鍨嬪彿"
+                         prop="materialModel"
+                         min-width="140" />
+        <el-table-column label="璁¢噺鍗曚綅"
+                         prop="unit"
+                         min-width="100" />
+        <el-table-column label="绾胯竟浠撴暟閲�"
+                         prop="pickQty"
+                         min-width="100" />
+        <el-table-column label="琛ユ枡鏁伴噺"
+                         prop="supplementQty"
+                         min-width="100" />
+        <el-table-column label="瀹為檯鏁伴噺"
+                         min-width="140">
           <template #default="{ row }">
-            <el-input-number
-              v-model="row.actualQty"
-              :min="0"
-              :precision="3"
-              :step="1"
-              controls-position="right"
-              style="width: 100%;"
-            />
+            <el-input-number v-model="row.actualQty"
+                             :min="0"
+                             :precision="3"
+                             :step="1"
+                             controls-position="right"
+                             style="width: 100%;" />
           </template>
         </el-table-column>
-        <el-table-column label="鎿嶄綔" align="center" fixed="right" width="180">
+        <el-table-column label="鎿嶄綔"
+                         align="center"
+                         fixed="right"
+                         width="180">
           <template #default="{ row }">
-            <el-button type="primary" link @click="openSupplementDialog(row)">琛ユ枡</el-button>
-            <el-button type="info" link @click="openSupplementRecordDialog(row)">琛ユ枡璁板綍</el-button>
+            <el-button type="primary"
+                       link
+                       @click="openSupplementDialog(row)">琛ユ枡</el-button>
+            <el-button type="info"
+                       link
+                       @click="openSupplementRecordDialog(row)">琛ユ枡璁板綍</el-button>
           </template>
         </el-table-column>
       </el-table>
       <template #footer>
         <span class="dialog-footer">
-          <el-button type="primary" :loading="pickSubmitting" @click="handleSubmitPick">棰嗙敤</el-button>
+          <el-button type="primary"
+                     :loading="pickSubmitting"
+                     @click="handleSubmitPick">棰嗙敤</el-button>
           <el-button @click="dialogVisible = false">鍙栨秷</el-button>
         </span>
       </template>
     </el-dialog>
-
-    <FormDialog
-      v-model="supplementDialogVisible"
-      title="琛ユ枡"
-      width="500px"
-      @confirm="handleSubmitSupplement"
-    >
-      <el-form ref="supplementFormRef" :model="supplementForm" :rules="supplementRules" label-width="100px">
-        <el-form-item label="琛ユ枡鏁伴噺" prop="supplementQty">
-          <el-input-number
-            v-model="supplementForm.supplementQty"
-            :min="0.001"
-            :precision="3"
-            :step="1"
-            style="width: 100%;"
-          />
+    <FormDialog v-model="supplementDialogVisible"
+                title="琛ユ枡"
+                width="500px"
+                @confirm="handleSubmitSupplement">
+      <el-form ref="supplementFormRef"
+               :model="supplementForm"
+               :rules="supplementRules"
+               label-width="100px">
+        <el-form-item label="琛ユ枡鏁伴噺"
+                      prop="supplementQty">
+          <el-input-number v-model="supplementForm.supplementQty"
+                           :min="0.001"
+                           :precision="3"
+                           :step="1"
+                           style="width: 100%;" />
         </el-form-item>
-        <el-form-item label="琛ユ枡鍘熷洜" prop="supplementReason">
-          <el-input
-            v-model="supplementForm.supplementReason"
-            type="textarea"
-            :rows="3"
-            maxlength="200"
-            show-word-limit
-            placeholder="璇疯緭鍏ヨˉ鏂欏師鍥�"
-          />
+        <el-form-item label="琛ユ枡鍘熷洜"
+                      prop="supplementReason">
+          <el-input v-model="supplementForm.supplementReason"
+                    type="textarea"
+                    :rows="3"
+                    maxlength="200"
+                    show-word-limit
+                    placeholder="璇疯緭鍏ヨˉ鏂欏師鍥�" />
         </el-form-item>
       </el-form>
       <template #footer>
         <span class="dialog-footer">
-          <el-button type="primary" :loading="supplementSubmitting" @click="handleSubmitSupplement">纭畾</el-button>
+          <el-button type="primary"
+                     :loading="supplementSubmitting"
+                     @click="handleSubmitSupplement">纭畾</el-button>
           <el-button @click="supplementDialogVisible = false">鍙栨秷</el-button>
         </span>
       </template>
     </FormDialog>
-
-    <el-dialog v-model="supplementRecordDialogVisible" title="琛ユ枡璁板綍" width="900px">
-      <el-table v-loading="supplementRecordLoading" :data="supplementRecordTableData" border row-key="id">
-        <el-table-column label="琛ユ枡鏁伴噺" prop="supplementQty" min-width="100" />
-        <el-table-column label="琛ユ枡鍘熷洜" prop="supplementReason" min-width="200" />
-        <el-table-column label="琛ユ枡浜�" prop="supplementUserName" min-width="120" />
-        <el-table-column label="琛ユ枡鏃ユ湡" prop="supplementTime" min-width="160" />
+    <el-dialog v-model="supplementRecordDialogVisible"
+               title="琛ユ枡璁板綍"
+               width="900px">
+      <el-table v-loading="supplementRecordLoading"
+                :data="supplementRecordTableData"
+                border
+                row-key="id">
+        <el-table-column label="琛ユ枡鏁伴噺"
+                         prop="supplementQty"
+                         min-width="100" />
+        <el-table-column label="琛ユ枡鍘熷洜"
+                         prop="supplementReason"
+                         min-width="200" />
+        <el-table-column label="琛ユ枡浜�"
+                         prop="supplementUserName"
+                         min-width="120" />
+        <el-table-column label="琛ユ枡鏃ユ湡"
+                         prop="supplementTime"
+                         min-width="160" />
       </el-table>
       <template #footer>
         <span class="dialog-footer">
@@ -92,187 +125,196 @@
 </template>
 
 <script setup>
-import { computed, nextTick, reactive, ref, watch } from "vue";
-import { ElMessage } from "element-plus";
-import FormDialog from "@/components/Dialog/FormDialog.vue";
-import {
-  listWorkOrderMaterialLedger,
-  addWorkOrderMaterialSupplement,
-  listWorkOrderMaterialSupplementRecord,
-  pickWorkOrderMaterial,
-} from "@/api/productionManagement/workOrder.js";
+  import { computed, nextTick, reactive, ref, watch } from "vue";
+  import { ElMessage } from "element-plus";
+  import FormDialog from "@/components/Dialog/FormDialog.vue";
+  import {
+    listWorkOrderMaterialLedger,
+    addWorkOrderMaterialSupplement,
+    listWorkOrderMaterialSupplementRecord,
+    pickWorkOrderMaterial,
+  } from "@/api/productionManagement/workOrder.js";
 
-const props = defineProps({
-  modelValue: {
-    type: Boolean,
-    default: false,
-  },
-  rowData: {
-    type: Object,
-    default: () => null,
-  },
-});
-
-const emit = defineEmits(["update:modelValue", "refresh"]);
-
-const dialogVisible = computed({
-  get: () => props.modelValue,
-  set: val => emit("update:modelValue", val),
-});
-
-const materialTableLoading = ref(false);
-const materialTableData = ref([]);
-const currentMaterialRow = ref(null);
-const currentMaterialOrderRow = ref(null);
-const pickSubmitting = ref(false);
-
-const supplementDialogVisible = ref(false);
-const supplementSubmitting = ref(false);
-const supplementFormRef = ref(null);
-const supplementForm = reactive({
-  supplementQty: null,
-  supplementReason: "",
-});
-
-const supplementRecordDialogVisible = ref(false);
-const supplementRecordLoading = ref(false);
-const supplementRecordTableData = ref([]);
-
-const supplementRules = {
-  supplementQty: [{ required: true, message: "璇疯緭鍏ヨˉ鏂欐暟閲�", trigger: "blur" }],
-  supplementReason: [{ required: true, message: "璇疯緭鍏ヨˉ鏂欏師鍥�", trigger: "blur" }],
-};
-const loadMaterialTable = async row => {
-  if (!row?.id) return;
-  currentMaterialOrderRow.value = row;
-  materialTableLoading.value = true;
-  materialTableData.value = [];
-  try {
-    const res = await listWorkOrderMaterialLedger({
-      workOrderId: row.id,
-      processId: row.processId,
-      productProcessRouteItemId: row.productProcessRouteItemId,
-    });
-    materialTableData.value = res.data || [];
-  } catch (e) {
-    console.error("鑾峰彇鐗╂枡鍙拌处澶辫触", e);
-    ElMessage.error("鑾峰彇鐗╂枡鍙拌处澶辫触");
-  } finally {
-    materialTableLoading.value = false;
-  }
-};
-
-watch(
-  () => props.modelValue,
-  visible => {
-    if (visible && props.rowData) {
-      loadMaterialTable(props.rowData);
-    }
-  }
-);
-
-const handleCloseMaterialDialog = () => {
-  materialTableData.value = [];
-  currentMaterialRow.value = null;
-  currentMaterialOrderRow.value = null;
-};
-
-const openSupplementDialog = row => {
-  currentMaterialRow.value = row;
-  supplementForm.supplementQty = null;
-  supplementForm.supplementReason = "";
-  supplementDialogVisible.value = true;
-  nextTick(() => {
-    supplementFormRef.value?.clearValidate();
+  const props = defineProps({
+    modelValue: {
+      type: Boolean,
+      default: false,
+    },
+    rowData: {
+      type: Object,
+      default: () => null,
+    },
   });
-};
 
-const handleSubmitSupplement = () => {
-  supplementFormRef.value?.validate(async valid => {
-    if (!valid || !currentMaterialRow.value?.id) {
-      ElMessage.warning("缂哄皯鐗╂枡鏄庣粏ID");
+  const emit = defineEmits(["update:modelValue", "refresh"]);
+
+  const dialogVisible = computed({
+    get: () => props.modelValue,
+    set: val => emit("update:modelValue", val),
+  });
+
+  const materialTableLoading = ref(false);
+  const materialTableData = ref([]);
+  const currentMaterialRow = ref(null);
+  const currentMaterialOrderRow = ref(null);
+  const pickSubmitting = ref(false);
+
+  const supplementDialogVisible = ref(false);
+  const supplementSubmitting = ref(false);
+  const supplementFormRef = ref(null);
+  const supplementForm = reactive({
+    supplementQty: null,
+    supplementReason: "",
+  });
+
+  const supplementRecordDialogVisible = ref(false);
+  const supplementRecordLoading = ref(false);
+  const supplementRecordTableData = ref([]);
+
+  const supplementRules = {
+    supplementQty: [
+      { required: true, message: "璇疯緭鍏ヨˉ鏂欐暟閲�", trigger: "blur" },
+    ],
+    supplementReason: [
+      { required: true, message: "璇疯緭鍏ヨˉ鏂欏師鍥�", trigger: "blur" },
+    ],
+  };
+  const loadMaterialTable = async row => {
+    if (!row?.id) return;
+    currentMaterialOrderRow.value = row;
+    materialTableLoading.value = true;
+    materialTableData.value = [];
+    try {
+      const res = await listWorkOrderMaterialLedger({
+        workOrderId: row.id,
+        processId: row.processId,
+        productProcessRouteItemId: row.productProcessRouteItemId,
+      });
+      materialTableData.value = res.data || [];
+    } catch (e) {
+      console.error("鑾峰彇鐗╂枡鍙拌处澶辫触", e);
+      ElMessage.error("鑾峰彇鐗╂枡鍙拌处澶辫触");
+    } finally {
+      materialTableLoading.value = false;
+    }
+  };
+
+  watch(
+    () => props.modelValue,
+    visible => {
+      if (visible && props.rowData) {
+        loadMaterialTable(props.rowData);
+      }
+    }
+  );
+
+  const handleCloseMaterialDialog = () => {
+    materialTableData.value = [];
+    currentMaterialRow.value = null;
+    currentMaterialOrderRow.value = null;
+  };
+
+  const openSupplementDialog = row => {
+    currentMaterialRow.value = row;
+    supplementForm.supplementQty = null;
+    supplementForm.supplementReason = "";
+    supplementDialogVisible.value = true;
+    nextTick(() => {
+      supplementFormRef.value?.clearValidate();
+    });
+  };
+
+  const handleSubmitSupplement = () => {
+    supplementFormRef.value?.validate(async valid => {
+      if (!valid || !currentMaterialRow.value?.id) {
+        ElMessage.warning("缂哄皯鐗╂枡鏄庣粏ID");
+        return;
+      }
+      supplementSubmitting.value = true;
+      try {
+        await addWorkOrderMaterialSupplement({
+          materialLedgerId: currentMaterialRow.value.id,
+          supplementQty: Number(supplementForm.supplementQty),
+          supplementReason: supplementForm.supplementReason,
+          workOrderId: currentMaterialOrderRow.value?.id,
+        });
+        supplementDialogVisible.value = false;
+        await loadMaterialTable(currentMaterialOrderRow.value);
+        ElMessage.success("琛ユ枡鎴愬姛");
+        emit("refresh");
+      } catch (e) {
+        console.error("琛ユ枡澶辫触", e);
+        ElMessage.error("琛ユ枡澶辫触");
+      } finally {
+        supplementSubmitting.value = false;
+      }
+    });
+  };
+
+  const openSupplementRecordDialog = async row => {
+    supplementRecordDialogVisible.value = true;
+    supplementRecordLoading.value = true;
+    supplementRecordTableData.value = [];
+    try {
+      const res = await listWorkOrderMaterialSupplementRecord({
+        materialLedgerId: row.id,
+      });
+      supplementRecordTableData.value = res.data || [];
+    } catch (e) {
+      console.error("鑾峰彇琛ユ枡璁板綍澶辫触", e);
+      ElMessage.error("鑾峰彇琛ユ枡璁板綍澶辫触");
+    } finally {
+      supplementRecordLoading.value = false;
+    }
+  };
+
+  const validatePickRows = () => {
+    if (materialTableData.value.length === 0) {
+      return { valid: false, message: "鏆傛棤鍙鐢ㄧ墿鏂�" };
+    }
+    const invalidRow = materialTableData.value.find(
+      item =>
+        item.actualQty === null ||
+        item.actualQty === undefined ||
+        item.actualQty === ""
+    );
+    if (invalidRow) {
+      return { valid: false, message: "璇峰~鍐欏疄闄呮暟閲忓悗鍐嶉鐢�" };
+    }
+    const exceedRow = materialTableData.value.find(item => {
+      const maxQty = Number(item.pickQty || 0) + Number(item.supplementQty || 0);
+      return Number(item.actualQty || 0) > maxQty;
+    });
+    if (exceedRow) {
+      return { valid: false, message: "瀹為檯鏁伴噺涓嶈兘澶т簬棰嗙敤鏁伴噺+琛ユ枡鏁伴噺" };
+    }
+    return { valid: true, message: "" };
+  };
+
+  const handleSubmitPick = async () => {
+    if (!currentMaterialOrderRow.value?.id) return;
+    const validateResult = validatePickRows();
+    if (!validateResult.valid) {
+      ElMessage.warning(validateResult.message);
       return;
     }
-    supplementSubmitting.value = true;
+    pickSubmitting.value = true;
     try {
-      await addWorkOrderMaterialSupplement({
-        materialLedgerId: currentMaterialRow.value.id,
-        supplementQty: Number(supplementForm.supplementQty),
-        supplementReason: supplementForm.supplementReason,
-        workOrderId: currentMaterialOrderRow.value?.id,
+      await pickWorkOrderMaterial({
+        workOrderId: currentMaterialOrderRow.value.id,
+        items: materialTableData.value.map(item => ({
+          materialLedgerId: item.id,
+          actualQty: Number(item.actualQty || 0),
+        })),
       });
-      supplementDialogVisible.value = false;
+      ElMessage.success("棰嗙敤鎴愬姛");
       await loadMaterialTable(currentMaterialOrderRow.value);
-      ElMessage.success("琛ユ枡鎴愬姛");
       emit("refresh");
     } catch (e) {
-      console.error("琛ユ枡澶辫触", e);
-      ElMessage.error("琛ユ枡澶辫触");
+      console.error("棰嗙敤澶辫触", e);
+      ElMessage.error("棰嗙敤澶辫触");
     } finally {
-      supplementSubmitting.value = false;
+      pickSubmitting.value = false;
     }
-  });
-};
-
-const openSupplementRecordDialog = async row => {
-  supplementRecordDialogVisible.value = true;
-  supplementRecordLoading.value = true;
-  supplementRecordTableData.value = [];
-  try {
-    const res = await listWorkOrderMaterialSupplementRecord({
-      materialLedgerId: row.id,
-    });
-    supplementRecordTableData.value = res.data || [];
-  } catch (e) {
-    console.error("鑾峰彇琛ユ枡璁板綍澶辫触", e);
-    ElMessage.error("鑾峰彇琛ユ枡璁板綍澶辫触");
-  } finally {
-    supplementRecordLoading.value = false;
-  }
-};
-
-const validatePickRows = () => {
-  if (materialTableData.value.length === 0) {
-    return { valid: false, message: "鏆傛棤鍙鐢ㄧ墿鏂�" };
-  }
-  const invalidRow = materialTableData.value.find(item => item.actualQty === null || item.actualQty === undefined || item.actualQty === "");
-  if (invalidRow) {
-    return { valid: false, message: "璇峰~鍐欏疄闄呮暟閲忓悗鍐嶉鐢�" };
-  }
-  const exceedRow = materialTableData.value.find(item => {
-    const maxQty = Number(item.pickQty || 0) + Number(item.supplementQty || 0);
-    return Number(item.actualQty || 0) > maxQty;
-  });
-  if (exceedRow) {
-    return { valid: false, message: "瀹為檯鏁伴噺涓嶈兘澶т簬棰嗙敤鏁伴噺+琛ユ枡鏁伴噺" };
-  }
-  return { valid: true, message: "" };
-};
-
-const handleSubmitPick = async () => {
-  if (!currentMaterialOrderRow.value?.id) return;
-  const validateResult = validatePickRows();
-  if (!validateResult.valid) {
-    ElMessage.warning(validateResult.message);
-    return;
-  }
-  pickSubmitting.value = true;
-  try {
-    await pickWorkOrderMaterial({
-      workOrderId: currentMaterialOrderRow.value.id,
-      items: materialTableData.value.map(item => ({
-        materialLedgerId: item.id,
-        actualQty: Number(item.actualQty || 0),
-      })),
-    });
-    ElMessage.success("棰嗙敤鎴愬姛");
-    await loadMaterialTable(currentMaterialOrderRow.value);
-    emit("refresh");
-  } catch (e) {
-    console.error("棰嗙敤澶辫触", e);
-    ElMessage.error("棰嗙敤澶辫触");
-  } finally {
-    pickSubmitting.value = false;
-  }
-};
+  };
 </script>

--
Gitblit v1.9.3