From cb2381fd47d32b7a96dcd94ab5e5225e29958c7c Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期五, 24 四月 2026 18:01:56 +0800
Subject: [PATCH] 一些修改

---
 src/views/productionManagement/processRoute/processRouteItem/index.vue |  587 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 560 insertions(+), 27 deletions(-)

diff --git a/src/views/productionManagement/processRoute/processRouteItem/index.vue b/src/views/productionManagement/processRoute/processRouteItem/index.vue
index c310b45..734d34c 100644
--- a/src/views/productionManagement/processRoute/processRouteItem/index.vue
+++ b/src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -80,7 +80,7 @@
                        prop="technologyOperationId"
                        width="200">
         <template #default="scope">
-          {{ getProcessName(scope.row.technologyOperationId) || '-' }}
+          {{ scope.row.technologyOperationName || scope.row.operationName || '-' }}
         </template>
       </el-table-column>
       <el-table-column label="鍙傛暟鍒楄〃"
@@ -106,6 +106,13 @@
                        width="100">
         <template #default="scope">
           {{scope.row.isQuality ? "鏄�" : "鍚�"}}
+        </template>
+      </el-table-column>
+      <el-table-column label="鏄惁鐢熶骇"
+                       prop="isProduction"
+                       width="100">
+        <template #default="scope">
+          {{scope.row.isProduction ? "鏄�" : "鍚�"}}
         </template>
       </el-table-column>
       <el-table-column label="鎿嶄綔"
@@ -151,7 +158,7 @@
             <!-- 搴忓彿鍦嗗湀 -->
             <div class="card-header">
               <div class="card-number">{{ index + 1 }}</div>
-              <div class="card-process-name">{{ getProcessName(item.technologyOperationId) || '-' }}</div>
+              <div class="card-process-name">{{ item.technologyOperationName || item.operationName || '-' }}</div>
             </div>
             <!-- 浜у搧淇℃伅 -->
             <div class="card-content">
@@ -166,6 +173,10 @@
                 <el-tag type="primary"
                         class="product-tag"
                         v-if="item.isQuality">璐ㄦ</el-tag>
+                <el-tag type="primary"
+                        class="product-tag"
+                        :style="item.isQuality?'margin-left:8px':''"
+                        v-if="item.isProduction">鐢熶骇</el-tag>
               </div>
               <div v-else
                    class="product-info empty">鏆傛棤浜у搧淇℃伅</div>
@@ -191,6 +202,163 @@
         </div>
       </div>
     </template>
+    <!-- bom妯″潡 -->
+    <div class="section-header"
+         style="margin-top: 20px;">
+      <div class="section-title">BOM 缁撴瀯</div>
+      <div class="section-actions"
+           v-if="pageType === 'order'">
+        <el-button v-if="!bomDataValue.isEdit"
+                   type="primary"
+                   @click="bomDataValue.isEdit = true">
+          缂栬緫
+        </el-button>
+        <el-button v-if="bomDataValue.isEdit"
+                   @click="cancelEditBom">
+          鍙栨秷
+        </el-button>
+        <el-button v-if="bomDataValue.isEdit"
+                   type="primary"
+                   @click="handleSaveBom"
+                   :loading="bomDataValue.loading">
+          淇濆瓨BOM
+        </el-button>
+      </div>
+    </div>
+    <el-table :data="bomTableData"
+              border
+              :preserve-expanded-content="false"
+              :default-expand-all="true"
+              style="width: 100%">
+      <el-table-column type="expand">
+        <template #default>
+          <el-form ref="bomFormRef"
+                   :model="bomDataValue">
+            <el-table :data="bomDataValue.dataList"
+                      row-key="tempId"
+                      default-expand-all
+                      :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
+                      style="width: 100%">
+              <el-table-column prop="productName"
+                               label="浜у搧" />
+              <el-table-column prop="model"
+                               label="瑙勬牸">
+                <template #default="{ row }">
+                  <el-form-item v-if="pageType === 'order' && bomDataValue.isEdit"
+                                :rules="[{ required: true, message: '璇烽�夋嫨瑙勬牸', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-select v-model="row.model"
+                               placeholder="璇烽�夋嫨瑙勬牸"
+                               clearable
+                               :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)"
+                               style="width: 100%"
+                               @visible-change="(v) => { if (v) openBomDialog(row.tempId) }">
+                      <el-option v-if="row.model"
+                                 :label="row.model"
+                                 :value="row.model" />
+                    </el-select>
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="processName"
+                               label="娑堣�楀伐搴�">
+                <template #default="{ row }">
+                  <el-form-item v-if="pageType === 'order' && bomDataValue.isEdit"
+                                :rules="bomDataValue.dataList.some(item => (item).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 => handleBomProcessChange(row, value)"
+                               :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)">
+                      <el-option v-for="item in bomDataValue.processOptions"
+                                 :key="item.id"
+                                 :label="item.name"
+                                 :value="item.id" />
+                    </el-select>
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="unitQuantity"
+                               label="鍗曚綅浜у嚭鎵�闇�鏁伴噺">
+                <template #default="{ row }">
+                  <el-form-item v-if="pageType === 'order' && bomDataValue.isEdit"
+                                :rules="[{ required: true, message: '璇疯緭鍏ュ崟浣嶄骇鍑烘墍闇�鏁伴噺', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-input-number v-model="row.unitQuantity"
+                                     :min="0"
+                                     :precision="2"
+                                     :step="1"
+                                     controls-position="right"
+                                     style="width: 100%"
+                                     :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)" />
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column v-if="pageType === 'order'"
+                               prop="demandedQuantity"
+                               label="闇�姹傛�婚噺">
+                <template #default="{ row }">
+                  <el-form-item v-if="pageType === 'order' && bomDataValue.isEdit"
+                                :rules="[{ required: true, message: '璇疯緭鍏ラ渶姹傛�婚噺', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-input-number v-model="row.demandedQuantity"
+                                     :min="0"
+                                     :precision="2"
+                                     :step="1"
+                                     controls-position="right"
+                                     style="width: 100%"
+                                     :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)" />
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="unit"
+                               label="鍗曚綅">
+                <template #default="{ row }">
+                  <el-form-item v-if="pageType === 'order' && bomDataValue.isEdit"
+                                :rules="[{ required: true, message: '璇疯緭鍏ュ崟浣�', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-input v-model="row.unit"
+                              placeholder="璇疯緭鍏ュ崟浣�"
+                              clearable
+                              :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)" />
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column label="鎿嶄綔"
+                               fixed="right"
+                               width="200"
+                               v-if="pageType === 'order' && bomDataValue.isEdit">
+                <template #default="{ row }">
+                  <el-button v-if="bomDataValue.isEdit && !bomDataValue.dataList.some(item => (item).tempId === row.tempId)"
+                             type="danger"
+                             text
+                             @click="removeBomItem(row.tempId)">鍒犻櫎
+                  </el-button>
+                  <el-button v-if="bomDataValue.isEdit"
+                             type="primary"
+                             text
+                             @click="addBomItem(row.tempId)">娣诲姞
+                  </el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </el-form>
+        </template>
+      </el-table-column>
+      <el-table-column label="BOM缂栧彿"
+                       prop="bomNo" />
+      <el-table-column label="浜у搧鍚嶇О"
+                       prop="productName" />
+      <el-table-column label="瑙勬牸鍨嬪彿"
+                       prop="model" />
+    </el-table>
+    <ProductSelectDialog v-if="bomDataValue.showProductDialog"
+                         v-model="bomDataValue.showProductDialog"
+                         :single="true"
+                         @confirm="handleBomProduct" />
     <!-- 鏂板/缂栬緫寮圭獥 -->
     <el-dialog v-model="dialogVisible"
                :title="operationType === 'add' ? '鏂板宸ヨ壓璺嚎椤圭洰' : '缂栬緫宸ヨ壓璺嚎椤圭洰'"
@@ -205,6 +373,7 @@
           <el-select v-model="form.technologyOperationId"
                      placeholder="璇烽�夋嫨宸ュ簭"
                      clearable
+                     @change="processChange"
                      style="width: 100%">
             <el-option v-for="process in processOptions"
                        :key="process.id"
@@ -216,8 +385,8 @@
                       prop="productModelId">
           <el-button type="primary"
                      @click="showProductSelectDialog = true">
-            {{ form.productName && form.model 
-              ? `${form.productName} - ${form.model}` 
+            {{ form.productName
+              ? (form.model ? `${form.productName} - ${form.model}` : form.productName)
               : '閫夋嫨浜у搧' }}
           </el-button>
         </el-form-item>
@@ -232,7 +401,13 @@
                       prop="isQuality">
           <el-switch v-model="form.isQuality"
                      :active-value="true"
-                     inactive-value="false" />
+                     :inactive-value="false" />
+        </el-form-item>
+        <el-form-item label="鏄惁鐢熶骇"
+                      prop="isProduction">
+          <el-switch v-model="form.isProduction"
+                     :active-value="true"
+                     :inactive-value="false" />
         </el-form-item>
       </el-form>
       <template #footer>
@@ -249,7 +424,7 @@
     <!-- 鍙傛暟鍒楄〃瀵硅瘽妗� -->
     <!-- :editable="!routeInfo.status" -->
     <ProcessParamListDialog v-model="showParamListDialog"
-                            :title="`${currentProcess ? (currentProcess.processName || getProcessName(currentProcess.technologyOperationId)) : ''} - 鍙傛暟鍒楄〃`"
+                            :title="`${currentProcess ? (currentProcess.processName || currentProcess.technologyOperationName || currentProcess.operationName) : ''} - 鍙傛暟鍒楄〃`"
                             :route-id="routeId"
                             :order-id="orderId"
                             :process="currentProcess"
@@ -279,7 +454,10 @@
     batchDeleteProcessRouteItem,
     getProcessParamList,
   } from "@/api/productionManagement/processRouteItem.js";
-  import { syncProcessParamItem } from "@/api/productionManagement/processRouteItem.js";
+  import {
+    syncProcessParamItem,
+    syncProcessParamItemOrder,
+  } from "@/api/productionManagement/processRouteItem.js";
   import {
     findProductProcessRouteItemList,
     deleteRouteItem,
@@ -289,6 +467,13 @@
     sortRouteItem,
   } from "@/api/productionManagement/productProcessRoute.js";
   import { processList } from "@/api/productionManagement/productionProcess.js";
+  import { listProcessBom } from "@/api/productionManagement/productionOrder.js";
+  import {
+    queryList,
+    queryList2,
+    addBomDetail,
+  } from "@/api/productionManagement/productStructure.js";
+
   import { useRoute } from "vue-router";
   import { ElMessageBox, ElMessage } from "element-plus";
   import Sortable from "sortablejs";
@@ -305,6 +490,7 @@
   const dialogVisible = ref(false);
   const operationType = ref("add"); // add | edit
   const formRef = ref(null);
+  const bomFormRef = ref(null);
   const submitLoading = ref(false);
   const cardsContainer = ref(null);
   const tableRef = ref(null);
@@ -343,6 +529,7 @@
     model: "",
     unit: "",
     isQuality: false,
+    isProduction: false,
   });
 
   const rules = {
@@ -361,17 +548,31 @@
       type: "warning",
     })
       .then(() => {
-        syncProcessParamItem({
-          replaceExisting: true,
-          technologyRoutingOperationId: currentProcess.value.id,
-        }).then(res => {
-          if (res.code === 200) {
-            ElMessage.success("鍚屾鎴愬姛");
-            refreshParamList();
-          } else {
-            ElMessage.error(res.msg || "鍚屾澶辫触");
-          }
-        });
+        if (pageType.value === "order") {
+          syncProcessParamItemOrder({
+            replaceExisting: true,
+            technologyRoutingOperationId: currentProcess.value.id,
+          }).then(res => {
+            if (res.code === 200) {
+              ElMessage.success("鍚屾鎴愬姛");
+              refreshParamList();
+            } else {
+              ElMessage.error(res.msg || "鍚屾澶辫触");
+            }
+          });
+        } else {
+          syncProcessParamItem({
+            replaceExisting: true,
+            technologyRoutingOperationId: currentProcess.value.id,
+          }).then(res => {
+            if (res.code === 200) {
+              ElMessage.success("鍚屾鎴愬姛");
+              refreshParamList();
+            } else {
+              ElMessage.error(res.msg || "鍚屾澶辫触");
+            }
+          });
+        }
       })
       .catch(() => {});
   };
@@ -414,6 +615,7 @@
     processList({ size: -1, current: -1 })
       .then(res => {
         processOptions.value = res.data.records || [];
+        bomDataValue.value.processOptions = processOptions.value;
       })
       .catch(err => {
         console.error("鑾峰彇宸ュ簭澶辫触锛�", err);
@@ -427,9 +629,13 @@
       productName: route.query.productName || "",
       model: route.query.model || "",
       bomNo: route.query.bomNo || "",
+      bomId: route.query.bomId || "",
       description: route.query.description || "",
       status: !(route.query.status == 1 || route.query.status === "false"),
     };
+    bomTableData.value[0].productName = routeInfo.value.productName;
+    bomTableData.value[0].model = routeInfo.value.model;
+    bomTableData.value[0].bomNo = routeInfo.value.bomNo;
   };
 
   // 鏂板
@@ -451,6 +657,7 @@
       model: row.model || "",
       unit: row.unit || "",
       isQuality: row.isQuality,
+      isProduction: row.isProduction,
     };
     dialogVisible.value = true;
   };
@@ -483,15 +690,20 @@
 
   // 浜у搧閫夋嫨
   const handleProductSelect = products => {
+    console.log(products, "===products===");
     if (products && products.length > 0) {
       const product = products[0];
-      form.value.productModelId = product.id;
-      form.value.productName = product.productName;
-      form.value.model = product.model;
-      form.value.unit = product.unit || "";
+      console.log(product, "product");
+      form.value = {
+        ...form.value,
+        productModelId: product.id,
+        productName: product.productName,
+        model: product.model,
+        unit: product.unit || "",
+      };
       showProductSelectDialog.value = false;
       // 瑙﹀彂琛ㄥ崟楠岃瘉
-      formRef.value?.validateField("productModelId");
+      // formRef.value?.validateField("productModelId");
     }
   };
 
@@ -509,11 +721,12 @@
 
           const addPromise = isOrderPage
             ? addRouteItem({
-                productOrderId: orderId.value,
-                productRouteId: routeId.value,
+                productOrderId: Number(orderId.value),
+                productRouteId: Number(routeId.value),
                 technologyOperationId: form.value.technologyOperationId,
                 productModelId: form.value.productModelId,
                 isQuality: form.value.isQuality,
+                isProduction: form.value.isProduction,
                 dragSort,
               })
             : addOrUpdateProcessRouteItem({
@@ -521,6 +734,7 @@
                 technologyOperationId: form.value.technologyOperationId,
                 productModelId: form.value.productModelId,
                 isQuality: form.value.isQuality,
+                isProduction: form.value.isProduction,
                 dragSort,
               });
 
@@ -546,6 +760,7 @@
                 technologyOperationId: form.value.technologyOperationId,
                 productModelId: form.value.productModelId,
                 isQuality: form.value.isQuality,
+                isProduction: form.value.isProduction,
               })
             : addOrUpdateProcessRouteItem1({
                 technologyRoutingId: Number(routeId.value),
@@ -553,6 +768,7 @@
                 productModelId: form.value.productModelId,
                 id: form.value.id,
                 isQuality: form.value.isQuality,
+                isProduction: form.value.isProduction,
               });
 
           updatePromise
@@ -582,6 +798,8 @@
       productName: "",
       model: "",
       unit: "",
+      isQuality: false,
+      isProduction: false,
     };
     formRef.value?.resetFields();
   };
@@ -596,8 +814,8 @@
   const handleViewParams = row => {
     currentProcess.value = row;
     const query = {
-      routeItemId: row.id,
-      orderId: orderId.value,
+      technologyRoutingOperationId: row.id,
+      productionOrderId: orderId.value,
     };
 
     const apiPromise =
@@ -758,10 +976,325 @@
     }
   };
 
+  // BOM鐩稿叧鐘舵�佸拰鏂规硶
+  const bomTableData = ref([
+    {
+      productName: "",
+      model: "",
+      bomNo: "",
+    },
+  ]);
+
+  const bomDataValue = ref({
+    dataList: [],
+    processOptions: [],
+    showProductDialog: false,
+    currentRowName: null,
+    loading: false,
+    isEdit: false,
+  });
+
+  const syncProcessOperationFields = item => {
+    const processId = item.processId ?? item.operationId ?? "";
+    if (!processId) {
+      item.processId = "";
+      return;
+    }
+    const option = bomDataValue.value.processOptions.find(
+      p => p.id === processId
+    );
+    const processName =
+      option?.name || item.processName || item.operationName || "";
+
+    item.processId = processId;
+    item.operationId = processId;
+    item.processName = processName;
+    item.operationName = processName;
+  };
+
+  const normalizeTreeData = items => {
+    items.forEach(item => {
+      item.tempId = item.tempId || item.id || `${Date.now()}_${Math.random()}`;
+      syncProcessOperationFields(item);
+      if (Array.isArray(item.children) && item.children.length > 0) {
+        normalizeTreeData(item.children);
+      }
+    });
+  };
+  const processChange = value => {
+    processOptions.value.forEach(item => {
+      if (item.id == value) {
+        form.value.isQuality = item.isQuality;
+        form.value.isProduction = item.isProduction;
+      }
+    });
+  };
+
+  const handleBomProcessChange = (row, value) => {
+    row.processId = value || "";
+    syncProcessOperationFields(row);
+  };
+
+  const openBomDialog = tempId => {
+    bomDataValue.value.currentRowName = tempId;
+    bomDataValue.value.showProductDialog = true;
+  };
+
+  const fetchBomData = async () => {
+    try {
+      const isOrderPage = pageType.value === "order";
+      const { data } = await (isOrderPage ? queryList2 : queryList)(
+        routeInfo.value.bomId
+      );
+      bomDataValue.value.dataList = data || [];
+      normalizeTreeData(bomDataValue.value.dataList);
+    } catch (err) {
+      console.error("鑾峰彇BOM鏁版嵁澶辫触锛�", err);
+    }
+  };
+
+  const childItem = (item, tempId, productData) => {
+    if (item.tempId === tempId) {
+      item.productName = productData.productName;
+      item.model = productData.model;
+      item.productModelId = productData.id;
+      item.unit = productData.unit || "";
+      return true;
+    }
+    if (item.children && item.children.length > 0) {
+      for (let child of item.children) {
+        if (childItem(child, tempId, productData)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  };
+
+  const handleBomProduct = row => {
+    if (!Array.isArray(row) || row.length === 0) {
+      ElMessage.warning("璇烽�夋嫨涓�涓骇鍝�");
+      return;
+    }
+    const productData = row[row.length - 1];
+
+    const isTopLevel = bomDataValue.value.dataList.some(
+      item => item.tempId === bomDataValue.value.currentRowName
+    );
+    if (isTopLevel) {
+      if (
+        productData.productName === bomTableData.value[0].productName &&
+        productData.model === bomTableData.value[0].model
+      ) {
+        const hasOther = bomDataValue.value.dataList.some(
+          item =>
+            item.tempId !== bomDataValue.value.currentRowName &&
+            item.productName === bomTableData.value[0].productName &&
+            item.model === bomTableData.value[0].model
+        );
+        if (hasOther) {
+          ElMessage.warning("鏈�澶栧眰鍜屽綋鍓嶄骇鍝佷竴鏍风殑涓�绾у彧鑳芥湁涓�涓�");
+          return;
+        }
+      }
+    }
+    bomDataValue.value.dataList.forEach(item => {
+      if (item.tempId === bomDataValue.value.currentRowName) {
+        item.productName = productData.productName;
+        item.model = productData.model;
+        item.productModelId = productData.id;
+        item.unit = productData.unit || "";
+        return;
+      }
+      childItem(item, bomDataValue.value.currentRowName, productData);
+    });
+    bomDataValue.value.showProductDialog = false;
+  };
+
+  const removeBomItem = tempId => {
+    const topIndex = bomDataValue.value.dataList.findIndex(
+      item => item.tempId === tempId
+    );
+    if (topIndex !== -1) {
+      bomDataValue.value.dataList.splice(topIndex, 1);
+      return;
+    }
+
+    const delchildItem = (items, tempId) => {
+      for (let i = 0; i < items.length; i++) {
+        const item = items[i];
+        if (item.tempId === tempId) {
+          items.splice(i, 1);
+          return true;
+        }
+        if (item.children && item.children.length > 0) {
+          if (delchildItem(item.children, tempId)) {
+            return true;
+          }
+        }
+      }
+      return false;
+    };
+
+    bomDataValue.value.dataList.forEach(item => {
+      if (item.children && item.children.length > 0) {
+        delchildItem(item.children, tempId);
+      }
+    });
+  };
+
+  const addchildItem = (item, tempId) => {
+    if (item.tempId === tempId) {
+      if (!item.children) {
+        item.children = [];
+      }
+      item.children.push({
+        parentId: item.id || "",
+        parentTempId: item.tempId || "",
+        productName: "",
+        productId: "",
+        model: undefined,
+        productModelId: undefined,
+        processId: "",
+        processName: "",
+        operationId: "",
+        operationName: "",
+        unitQuantity: 1,
+        demandedQuantity: 0,
+        children: [],
+        unit: "",
+        tempId: new Date().getTime(),
+      });
+      return true;
+    }
+    if (item.children && item.children.length > 0) {
+      for (let child of item.children) {
+        if (addchildItem(child, tempId)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  };
+
+  const addBomItem = tempId => {
+    bomDataValue.value.dataList.forEach(item => {
+      if (item.tempId === tempId) {
+        if (!item.children) {
+          item.children = [];
+        }
+        item.children.push({
+          parentId: item.id || "",
+          parentTempId: item.tempId || "",
+          productName: "",
+          productId: "",
+          model: undefined,
+          productModelId: undefined,
+          processId: "",
+          processName: "",
+          operationId: "",
+          operationName: "",
+          unitQuantity: 1,
+          demandedQuantity: 0,
+          unit: "",
+          children: [],
+          tempId: new Date().getTime(),
+        });
+        return;
+      }
+      addchildItem(item, tempId);
+    });
+  };
+
+  const validateAllBom = () => {
+    let isValid = true;
+    const isOrderPage = pageType.value === "order";
+
+    const validateItem = (item, isTopLevel = false) => {
+      if (!item.model) {
+        ElMessage.error("璇烽�夋嫨瑙勬牸");
+        isValid = false;
+        return;
+      }
+      if (!isTopLevel && !item.processId) {
+        ElMessage.error("璇烽�夋嫨娑堣�楀伐搴�");
+        isValid = false;
+        return;
+      }
+      if (!item.unitQuantity) {
+        ElMessage.error("璇疯緭鍏ュ崟浣嶄骇鍑烘墍闇�鏁伴噺");
+        isValid = false;
+        return;
+      }
+      if (isOrderPage && !item.demandedQuantity) {
+        ElMessage.error("璇疯緭鍏ラ渶姹傛�婚噺");
+        isValid = false;
+        return;
+      }
+
+      if (item.children && item.children.length > 0) {
+        item.children.forEach(child => {
+          validateItem(child, false);
+        });
+      }
+    };
+
+    bomDataValue.value.dataList.forEach(item => {
+      validateItem(item, true);
+    });
+
+    return isValid;
+  };
+
+  const buildSubmitTree = items => {
+    return items.map(item => {
+      const current = { ...item };
+      syncProcessOperationFields(current);
+      current.children = Array.isArray(current.children)
+        ? buildSubmitTree(current.children)
+        : [];
+      return current;
+    });
+  };
+
+  const cancelEditBom = () => {
+    bomDataValue.value.isEdit = false;
+    fetchBomData();
+  };
+
+  const handleSaveBom = () => {
+    bomDataValue.value.loading = true;
+    console.log(bomDataValue.value.dataList, "bomDataValue.value.dataList");
+
+    normalizeTreeData(bomDataValue.value.dataList);
+
+    const valid = validateAllBom();
+    if (valid) {
+      addBomDetail({
+        bomId: Number(routeInfo.value.bomId),
+        children: buildSubmitTree(bomDataValue.value.dataList || []),
+      })
+        .then(() => {
+          ElMessage.success("BOM淇濆瓨鎴愬姛");
+          bomDataValue.value.isEdit = false;
+          fetchBomData();
+        })
+        .catch(() => {
+          ElMessage.error("BOM淇濆瓨澶辫触");
+        })
+        .finally(() => {
+          bomDataValue.value.loading = false;
+        });
+    } else {
+      bomDataValue.value.loading = false;
+    }
+  };
+
   onMounted(() => {
     getRouteInfo();
     getList();
     getProcessList();
+    fetchBomData();
   });
 
   onUnmounted(() => {

--
Gitblit v1.9.3