From c831574708e199a909b569b625fd92707bc660fd Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期五, 27 三月 2026 16:21:04 +0800
Subject: [PATCH] 军泰伟业 1.已发货数量等于总数量时,状态为已发货。已发货数量小于总数量时,状态建议新增发货中 2.产品清除工艺路线点击确定后,仍然回显绑定的工艺路线 3.新增退货单时,退一部分货后,继续对此退货单进行退货操作,展示数据有误

---
 src/views/productionManagement/productStructure/Detail/index.vue |  898 +++++++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 687 insertions(+), 211 deletions(-)

diff --git a/src/views/productionManagement/productStructure/Detail/index.vue b/src/views/productionManagement/productStructure/Detail/index.vue
index a131830..051d729 100644
--- a/src/views/productionManagement/productStructure/Detail/index.vue
+++ b/src/views/productionManagement/productStructure/Detail/index.vue
@@ -1,72 +1,75 @@
 <template>
   <div class="app-container">
-    <el-button v-if="dataValue.isEdit"
-               type="primary"
-               @click="addItem"
-               style="margin-bottom: 10px">娣诲姞
-    </el-button>
-    <el-button v-if="!dataValue.isEdit"
-               type="primary"
-               @click="dataValue.isEdit = true"
-               style="margin-bottom: 10px">缂栬緫
-    </el-button>
-    <el-button v-if="dataValue.isEdit"
-               type="primary"
-               @click="cancelEdit"
-               style="margin-bottom: 10px">鍙栨秷
-    </el-button>
-    <el-button type="primary"
-               :loading="dataValue.loading"
-               @click="submit"
-               :disabled="!dataValue.isEdit"
-               style="margin-bottom: 10px">纭
-    </el-button>
-    <el-table
-        :data="tableData"
-        border
-        :preserve-expanded-content="false"
-        style="width: 100%"
-    >
+    <PageHeader content="浜у搧缁撴瀯璇︽儏">
+      <template #right-button>
+        <el-button v-if="dataValue.isEdit && !isOrderPage"
+                   @click="cancelEdit">鍙栨秷
+        </el-button>
+        <el-button v-if="!dataValue.isEdit && !isOrderPage"
+                   type="primary"
+                   @click="dataValue.isEdit = true">鐐瑰嚮杩涜淇敼
+        </el-button>
+        <el-button v-if="!isOrderPage"
+                   type="primary"
+                   @click="openBomAddDialog"
+                   :disabled="!dataValue.isEdit">
+          <el-icon><Document /></el-icon> 鎸塀OM娣诲姞
+        </el-button>
+        <el-button v-if="!isOrderPage"
+                   type="success"
+                   :loading="dataValue.loading"
+                   @click="submit"
+                   :disabled="!dataValue.isEdit">纭
+        </el-button>
+      </template>
+    </PageHeader>
+    <el-table :data="tableData"
+              border
+              :preserve-expanded-content="false"
+              :default-expand-all="true"
+              style="width: 100%">
       <el-table-column type="expand">
         <template #default="props">
           <el-form ref="form"
                    :model="dataValue">
             <el-table :data="dataValue.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="瑙勬牸">
+                               label="浜у搧" />
+							<el-table-column prop="model"
+															 label="鍥剧焊缂栧彿">
+								<template #default="{ row, $index }">
+									<el-form-item v-if="dataValue.isEdit"
+															:rules="[{ required: true, message: '璇烽�夋嫨瑙勬牸', trigger: ['blur','change'] }]"
+															style="margin: 0">
+										<el-select v-model="row.model"
+															 placeholder="璇烽�夋嫨瑙勬牸"
+															 clearable
+															 :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)"
+															 style="width: 100%"
+															 @visible-change="(v) => { if (v) openDialog(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, $index }">
                   <el-form-item v-if="dataValue.isEdit"
-                                :prop="`dataList.${$index}.model`"
-                                :rules="[{ required: true, message: '璇烽�夋嫨瑙勬牸', trigger: ['blur','change'] }]"
-                                style="margin: 0">
-                    <el-select v-model="row.model"
-                               placeholder="璇烽�夋嫨瑙勬牸"
-                               clearable
-                               :disabled="!dataValue.isEdit"
-                               style="width: 100%"
-                               @visible-change="(v) => { if (v) openDialog($index) }">
-                      <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="processId"
-                               label="娑堣�楀伐搴�">
-                <template #default="{ row, $index }">
-                  <el-form-item :prop="`dataList.${$index}.processId`"
-                                :rules="[{ required: true, message: '璇烽�夋嫨娑堣�楀伐搴�', trigger: 'change' }]"
+                                :rules="dataValue.dataList.some(item => (item as any).tempId === row.tempId) ? [] : [{ required: true, message: '璇烽�夊伐搴�', trigger: 'change' }]"
                                 style="margin: 0">
                     <el-select v-model="row.processId"
                                placeholder="璇烽�夋嫨"
                                filterable
                                clearable
                                style="width: 100%"
-                               :disabled="!dataValue.isEdit">
+                               :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)">
                       <el-option v-for="item in dataValue.processOptions"
                                  :key="item.id"
                                  :label="item.name"
@@ -76,10 +79,10 @@
                 </template>
               </el-table-column>
               <el-table-column prop="unitQuantity"
-                               label="鍗曚綅浜у嚭鎵�闇�鏁伴噺">
+                               label="鍗曚綅鐢ㄩ噺">
                 <template #default="{ row, $index }">
-                  <el-form-item :prop="`dataList.${$index}.unitQuantity`"
-                                :rules="[{ required: true, message: '璇疯緭鍏ュ崟浣嶄骇鍑烘墍闇�鏁伴噺', trigger: ['blur','change'] }]"
+                  <el-form-item v-if="dataValue.isEdit"
+                                :rules="[{ required: true, message: '璇疯緭鍏ュ崟浣嶇敤閲�', trigger: ['blur','change'] }]"
                                 style="margin: 0">
                     <el-input-number v-model="row.unitQuantity"
                                      :min="0"
@@ -87,14 +90,15 @@
                                      :step="1"
                                      controls-position="right"
                                      style="width: 100%"
-                                     :disabled="!dataValue.isEdit" />
+                                     :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
                   </el-form-item>
                 </template>
               </el-table-column>
-              <el-table-column prop="demandedQuantity"
+              <el-table-column v-if="isOrderPage"
+                               prop="demandedQuantity"
                                label="闇�姹傛�婚噺">
                 <template #default="{ row, $index }">
-                  <el-form-item :prop="`dataList.${$index}.demandedQuantity`"
+                  <el-form-item v-if="dataValue.isEdit"
                                 :rules="[{ required: true, message: '璇疯緭鍏ラ渶姹傛�婚噺', trigger: ['blur','change'] }]"
                                 style="margin: 0">
                     <el-input-number v-model="row.demandedQuantity"
@@ -103,44 +107,36 @@
                                      :step="1"
                                      controls-position="right"
                                      style="width: 100%"
-                                     :disabled="!dataValue.isEdit" />
+                                     :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
                   </el-form-item>
                 </template>
               </el-table-column>
               <el-table-column prop="unit"
                                label="鍗曚綅">
                 <template #default="{ row, $index }">
-                  <el-form-item :prop="`dataList.${$index}.unit`"
+                  <el-form-item v-if="dataValue.isEdit"
                                 :rules="[{ required: true, message: '璇疯緭鍏ュ崟浣�', trigger: ['blur','change'] }]"
                                 style="margin: 0">
                     <el-input v-model="row.unit"
                               placeholder="璇疯緭鍏ュ崟浣�"
                               clearable
-                              :disabled="!dataValue.isEdit" />
+                              :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
                   </el-form-item>
                 </template>
               </el-table-column>
-              <el-table-column prop="diskQuantity"
-                               label="鐩樻暟锛堢洏锛�">
+              <el-table-column label="鎿嶄綔"
+                               fixed="right"
+                               width="200">
                 <template #default="{ row, $index }">
-                  <el-form-item :prop="`dataList.${$index}.diskQuantity`"
-                                :rules="[{ required: true, message: '璇疯緭鍏ョ洏鏁�', trigger: ['blur','change'] }]"
-                                style="margin: 0">
-                    <el-input-number v-model="row.diskQuantity"
-                                     :min="0"
-                                     :precision="0"
-                                     :step="1"
-                                     controls-position="right"
-                                     style="width: 100%"
-                                     :disabled="!dataValue.isEdit" />
-                  </el-form-item>
-                </template>
-              </el-table-column>
-              <el-table-column label="鎿嶄綔" fixed="right" width="100">
-                <template #default="{ row, $index }">
-                  <el-button type="danger"
+                  <el-button v-if="dataValue.isEdit && !dataValue.dataList.some(item => (item as any).tempId === row.tempId)"
+                             type="danger"
                              text
-                             @click="dataValue.dataList.splice($index, 1)">鍒犻櫎
+                             @click="removeItem(row.tempId)">鍒犻櫎
+                  </el-button>
+                  <el-button v-if="dataValue.isEdit"
+                             type="primary"
+                             text
+                             @click="addItem2(row.tempId)">娣诲姞
                   </el-button>
                 </template>
               </el-table-column>
@@ -148,146 +144,626 @@
           </el-form>
         </template>
       </el-table-column>
-      <el-table-column label="浜у搧缂栫爜" prop="productCode" />
-      <el-table-column label="浜у搧鍚嶇О" prop="productName" />
-      <el-table-column label="瑙勬牸鍨嬪彿" prop="model" />
-      <el-table-column label="鍗曚綅" prop="unit" />
+      <el-table-column v-if="!isOrderPage"
+                       label="BOM缂栧彿"
+                       prop="bomNo" />
+      <el-table-column label="浜у搧鍚嶇О"
+                       prop="productName" />
+			<el-table-column label="鍥剧焊缂栧彿"
+														 prop="model" />
     </el-table>
-
     <product-select-dialog v-if="dataValue.showProductDialog"
                            v-model:model-value="dataValue.showProductDialog"
                            @confirm="handleProduct" />
+
+    <!-- 鎸塀OM娣诲姞寮圭獥 -->
+    <el-dialog v-model="bomAddDialogVisible" title="鎸塀OM娣诲姞" width="500px" @close="closeBomAddDialog">
+      <el-form ref="bomAddFormRef" :model="bomAddForm" :rules="bomAddRules" label-width="100px">
+        <el-form-item label="鐖堕」浜у搧" prop="parentProductId">
+          <el-select v-model="bomAddForm.parentProductId" placeholder="璇烽�夋嫨" clearable filterable
+            style="width: 100%" @change="handleBomParentProductChange">
+            <el-option v-for="item in parentProductOptions" :key="item.id"
+              :label="`${item.model || item.productCode || item.id} | ${item.productName}`"
+              :value="item.id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鐢ㄩ噺绯绘暟" prop="coefficient" style="margin-top: 20px;">
+          <el-input-number v-model="bomAddForm.coefficient" :min="0.01" :precision="2" :step="1"
+            controls-position="right" style="width: 100%" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitBomAdd">纭畾</el-button>
+        <el-button @click="closeBomAddDialog">鍙栨秷</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 楂樼骇閫夋嫨浜у搧寮圭獥 -->
+    <product-select-dialog v-if="showAdvancedProductDialog" v-model:model-value="showAdvancedProductDialog"
+      @confirm="handleAdvancedProductSelect" />
   </div>
 </template>
 
 <script setup lang="ts">
-import {
-  computed,
-  defineAsyncComponent,
-  defineComponent,
-  onMounted,
-  reactive,
-  ref,
-} from "vue";
-import { queryList, add } from "@/api/productionManagement/productStructure.js";
-import { list } from "@/api/productionManagement/productionProcess";
-import { ElMessage } from "element-plus";
-import {useRoute, useRouter} from "vue-router";
+  import {
+    computed,
+    defineAsyncComponent,
+    defineComponent,
+    onMounted,
+    reactive,
+    ref,
+  } from "vue";
+  import { queryList, add, listByBomIdIsParent } from "@/api/productionManagement/productStructure.js";
+  import { listProcessBom } from "@/api/productionManagement/productionOrder.js";
+  import { list } from "@/api/productionManagement/productionProcess";
+  import { productListPage } from "@/api/basicData/product.js";
+  import { listPage as listBomPage } from "@/api/productionManagement/productBom.js";
+  import { ElMessage } from "element-plus";
+  import { useRoute, useRouter } from "vue-router";
+  import { Search, Document } from '@element-plus/icons-vue'
 
-defineComponent({
-  name: "StructureEdit",
-});
-
-const ProductSelectDialog = defineAsyncComponent(
-    () => import("@/views/basicData/product/ProductSelectDialog.vue")
-);
-const form = ref();
-
-const route = useRoute()
-const router = useRouter()
-const routeId = computed({
-  get() {
-    return route.query.id;
-  },
-
-  set(val) {
-    emit('update:router', val)
-  }
-});
-
-
-const dataValue = reactive({
-  dataList: [],
-  productOptions: [],
-  processOptions: [],
-  showProductDialog: false,
-  currentRowIndex: null,
-  loading: false,
-  isEdit: false,
-});
-
-const tableData = reactive([
-  {
-    productName: "",
-    model: "",
-    unit: "",
-    productCode: "",
-  }
-])
-
-const openDialog = index => {
-  dataValue.currentRowIndex = index;
-  dataValue.showProductDialog = true;
-};
-
-const fetchData = async () => {
-  const { data } = await queryList(routeId.value);
-  tableData[0].productName = data.productName;
-  tableData[0].model = data.model;
-  tableData[0].unit = data.unit;
-  tableData[0].productCode = data.productCode;
-  dataValue.dataList = data.productStructureList;
-};
-
-const fetchProcessOptions = async () => {
-  const { data } = await list(routeId.value);
-  dataValue.processOptions = data;
-};
-
-const handleProduct = row => {
-  if (row?.length > 1) {
-    ElMessage.error("鍙兘閫夋嫨涓�涓骇鍝�");
-  }
-  dataValue.dataList[dataValue.currentRowIndex].productName =
-      row[0].productName;
-  dataValue.dataList[dataValue.currentRowIndex].model = row[0].model;
-  dataValue.dataList[dataValue.currentRowIndex].productModelId = row[0].id;
-  dataValue.showProductDialog = false;
-};
-
-const submit = () => {
-  form.value
-      .validate(valid => {
-        dataValue.loading = true;
-        if (valid) {
-          add({
-            parentId: routeId.value,
-            productStructureList: dataValue.dataList || [],
-          }).then(res => {
-            router.push({
-              path: '/productionManagement/productionManagement/productStructure/index',
-            })
-            ElMessage.success("淇濆瓨鎴愬姛");
-            dataValue.loading = false;
-          });
-        }
-      })
-      .finally(() => {
-        dataValue.loading = false;
-      });
-};
-
-const addItem = () => {
-  dataValue.dataList.push({
-    productName: "",
-    productId: "",
-    model: undefined,
-    productModelId: undefined,
-    processId: "",
-    unitQuantity: 0,
-    demandedQuantity: 0,
-    unit: "",
-    diskQuantity: 0,
+  defineComponent({
+    name: "StructureEdit",
   });
-};
 
-const cancelEdit = () => {
-  dataValue.isEdit = false;
-  dataValue.dataList = dataValue.dataList.filter(item => item.id !== undefined);
-};
+  const ProductSelectDialog = defineAsyncComponent(
+    () => import("@/views/basicData/product/ProductSelectDialog.vue")
+  );
+  const emit = defineEmits(["update:router"]);
+  const form = ref();
 
-onMounted(() => {
-  fetchData();
-  fetchProcessOptions();
-});
-</script>
\ No newline at end of file
+  const route = useRoute();
+  const router = useRouter();
+  const routeId = computed({
+    get() {
+      return route.query.id;
+    },
+
+    set(val) {
+      emit("update:router", val);
+    },
+  });
+
+  // 浠庤矾鐢卞弬鏁拌幏鍙栦骇鍝佷俊鎭�
+  const routeBomNo = computed(() => route.query.bomNo || "");
+  const routeProductName = computed(() => route.query.productName || "");
+  const routeDrawingNumber = computed(() => route.query.drawingNumber || "");
+  const routeProductModelName = computed(
+    () => route.query.productModelName || ""
+  );
+  const routeOrderId = computed(() => route.query.orderId);
+  const pageType = computed(() => route.query.type);
+  const isOrderPage = computed(
+    () => pageType.value === "order" && routeOrderId.value
+  );
+
+  const dataValue = reactive({
+    dataList: [],
+    productOptions: [],
+    processOptions: [],
+    showProductDialog: false,
+    currentRowIndex: null,
+    currentRowName: null,
+    loading: false,
+    isEdit: false,
+  });
+
+  // 鎸塀OM娣诲姞鐩稿叧
+  const bomAddDialogVisible = ref(false);
+  const bomAddFormRef = ref();
+  const parentProductOptions = ref([]);
+  const selectedBomProduct = ref(null);
+  const selectedBomTreeData = ref([]); // 淇濆瓨閫変腑浜у搧鏃惰幏鍙栫殑BOM鏍戞暟鎹�
+  const showAdvancedProductDialog = ref(false);
+  const bomAddForm = reactive({
+    parentProductId: undefined,
+    coefficient:1
+  });
+  const bomAddRules = {
+    parentProductId: [{ required: true, message: "璇烽�夋嫨鐖堕」浜у搧", trigger: "change" }],
+    coefficient: [{ required: true, message: "璇疯緭鍏ョ敤閲忕郴鏁�", trigger: "blur" }]
+  };
+
+  const tableData = reactive([
+    {
+      productName: "",
+      drawingNumber: "",
+      model: "",
+      bomNo: "",
+    },
+  ]);
+
+  const openDialog = (tempId: any) => {
+    console.log(tempId, "tempId");
+    dataValue.currentRowName = tempId;
+    dataValue.showProductDialog = true;
+  };
+
+  const fetchData = async () => {
+    if (isOrderPage.value) {
+      // 璁㈠崟鎯呭喌锛氫娇鐢ㄨ鍗曠殑浜у搧缁撴瀯鎺ュ彛
+      const { data } = await listProcessBom({ orderId: routeOrderId.value });
+      dataValue.dataList = (data as any) || [];
+    } else {
+      // 闈炶鍗曟儏鍐碉細浣跨敤鍘熸潵鐨勬帴鍙�
+      const { data } = await queryList(routeId.value);
+      dataValue.dataList = (data as any) || [];
+      // 涓烘墍鏈夐」鍙婂叾瀛愰」璁剧疆name灞炴��
+      const setNameRecursively = (items: any[]) => {
+        items.forEach((item: any) => {
+          item.tempId = item.id;
+          item.processName =
+            dataValue.processOptions.find(option => option.id === item.processId)
+              ?.name || "";
+          if (item.children && item.children.length > 0) {
+            setNameRecursively(item.children);
+          }
+        });
+      };
+      setNameRecursively(dataValue.dataList);
+      console.log(dataValue.dataList, "dataValue.dataList");
+    }
+  };
+
+  const fetchProcessOptions = async () => {
+    const { data } = await list();
+    dataValue.processOptions = data as any;
+  };
+
+  const handleProduct = (row: any) => {
+    if (row?.length > 1) {
+      ElMessage.error("鍙兘閫夋嫨涓�涓骇鍝�");
+    }
+    const productData = row[0];
+
+    //  鏈�澶栧眰缁勪欢涓紝涓庡綋鍓嶄骇鍝佺浉鍚岀殑浜у搧鍙兘鏈変竴涓�
+    const isTopLevel = dataValue.dataList.some(
+      item => (item as any).tempId === dataValue.currentRowName
+    );
+    if (isTopLevel) {
+      if (
+        productData.productName === tableData[0].productName &&
+        productData.model === tableData[0].model
+      ) {
+        //  鏌ユ壘鏄惁宸茬粡鏈夊叾浠栭《灞傝宸茬粡鏄繖涓骇鍝�
+        const hasOther = dataValue.dataList.some(
+          item =>
+            (item as any).tempId !== dataValue.currentRowName &&
+            (item as any).productName === tableData[0].productName &&
+            (item as any).model === tableData[0].model
+        );
+        if (hasOther) {
+          ElMessage.warning("鏈�澶栧眰鍜屽綋鍓嶄骇鍝佷竴鏍风殑涓�绾у彧鑳芥湁涓�涓�");
+          return;
+        }
+      }
+    }
+    // dataValue.dataList[dataValue.currentRowIndex].productName =
+    //   row[0].productName;
+    // dataValue.dataList[dataValue.currentRowIndex].model = row[0].model;
+    // dataValue.dataList[dataValue.currentRowIndex].productModelId = row[0].id;
+    // dataValue.dataList[dataValue.currentRowIndex].unit = row[0].unit || "";
+    dataValue.dataList.map(item => {
+      if (item.tempId === dataValue.currentRowName) {
+        item.productName = productData.productName;
+        item.drawingNumber = productData.drawingNumber || "";
+        item.model = productData.model;
+        item.productModelId = productData.id;
+        item.unit = productData.unit || "";
+        return;
+      }
+      childItem(item, dataValue.currentRowName, productData);
+    });
+    dataValue.showProductDialog = false;
+  };
+  const childItem = (item: any, tempId: any, productData: any) => {
+    if (item.tempId === tempId) {
+      item.productName = productData.productName;
+      item.drawingNumber = productData.drawingNumber || "";
+      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 validateAll = () => {
+    let isValid = true;
+
+    // 鏍¢獙鍑芥暟
+    const validateItem = (item: any, isTopLevel = false) => {
+      // 鏍¢獙褰撳墠椤圭殑蹇呭~瀛楁
+      if (!item.model) {
+        ElMessage.error("璇烽�夋嫨瑙勬牸");
+        isValid = false;
+        return;
+      }
+      if (!item.unitQuantity) {
+        ElMessage.error("璇疯緭鍏ュ崟浣嶇敤閲�");
+        isValid = false;
+        return;
+      }
+      if (isOrderPage.value && !item.demandedQuantity) {
+        ElMessage.error("璇疯緭鍏ラ渶姹傛�婚噺");
+        isValid = false;
+        return;
+      }
+      // if (!item.unit) {
+      //   ElMessage.error("璇疯緭鍏ュ崟浣�");
+      //   isValid = false;
+      //   return;
+      // }
+
+      // 閫掑綊鏍¢獙瀛愰」
+      if (item.children && item.children.length > 0) {
+        item.children.forEach(child => {
+          validateItem(child, false);
+        });
+      }
+    };
+
+    // 閬嶅巻鎵�鏈夐《灞傞」
+    dataValue.dataList.forEach(item => {
+      validateItem(item, true);
+    });
+
+    return isValid;
+  };
+
+  const submit = () => {
+    dataValue.loading = true;
+
+    // 鍏堣繘琛岃〃鍗曟牎楠�
+    const valid = validateAll();
+    console.log(dataValue.dataList, "dataValue.dataList");
+    if (valid) {
+      add({
+        bomId: routeId.value,
+        children: dataValue.dataList || [],
+      })
+        .then(res => {
+          router.push({
+            path: "/productionManagement/productionManagement/productStructure/index",
+          });
+          ElMessage.success("淇濆瓨鎴愬姛");
+          dataValue.loading = false;
+        })
+        .catch(() => {
+          dataValue.loading = false;
+        });
+    } else {
+      dataValue.loading = false;
+    }
+  };
+
+  const removeItem = (tempId: string) => {
+    // 鍏堝皾璇曚粠椤跺眰鍒犻櫎
+    const topIndex = dataValue.dataList.findIndex(item => item.tempId === tempId);
+    if (topIndex !== -1) {
+      dataValue.dataList.splice(topIndex, 1);
+      return;
+    }
+
+    // 閫掑綊鍒犻櫎瀛愰」
+    const delchildItem = (items: any[], tempId: any) => {
+      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;
+    };
+
+    dataValue.dataList.forEach(item => {
+      if (item.children && item.children.length > 0) {
+        delchildItem(item.children, tempId);
+      }
+    });
+  };
+  const addItem2 = tempId => {
+    dataValue.dataList.map(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: "",
+          unitQuantity: 0,
+          demandedQuantity: 0,
+          unit: "",
+          children: [],
+
+          tempId: new Date().getTime(),
+        });
+        return;
+      }
+      addchildItem(item, tempId);
+    });
+  };
+  const addchildItem = (item: any, tempId: any) => {
+    if (item.tempId === tempId) {
+      console.log(item, "item");
+      if (!item.children) {
+        item.children = [];
+      }
+      item.children.push({
+        parentId: item.id || "",
+        parentTempId: item.tempId || "",
+        productName: "",
+        productId: "",
+        model: undefined,
+        productModelId: undefined,
+        processId: "",
+        unitQuantity: 0,
+        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 getPropPath = (row, field) => {
+    // 涓烘瘡涓猺ow鐢熸垚鍞竴鐨勮矾寰�
+    // 浣跨敤row.id鎴栫储寮曚綔涓哄敮涓�鏍囪瘑
+    let path = "dataList";
+
+    // 绠�鍗曞疄鐜帮細浣跨敤row鐨刬d鎴栦竴涓敮涓�鏍囪瘑
+    const uniqueId = row.id || Math.floor(Math.random() * 10000);
+    path += `.${uniqueId}`;
+
+    return path + `.${field}`;
+  };
+
+  const cancelEdit = () => {
+    dataValue.isEdit = false;
+    // dataValue.dataList = dataValue.dataList.filter(item => item.id !== undefined);
+    fetchData();
+  };
+
+  // 鑾峰彇鐖堕」浜у搧鍒楄〃
+  const getParentProductList = async () => {
+    try {
+      const { data } = await listBomPage({ current: 1, size: 1000 });
+      parentProductOptions.value = data?.records || [];
+    } catch (error) {
+      console.error("鑾峰彇鐖堕」浜у搧鍒楄〃澶辫触:", error);
+      parentProductOptions.value = [];
+    }
+  };
+
+  // 鎵撳紑鎸塀OM娣诲姞寮圭獥
+  const openBomAddDialog = () => {
+    bomAddDialogVisible.value = true;
+    bomAddForm.parentProductId = undefined;
+    bomAddForm.coefficient = 1;
+    selectedBomProduct.value = null;
+    getParentProductList();
+  };
+
+  // 鍏抽棴鎸塀OM娣诲姞寮圭獥
+  const closeBomAddDialog = () => {
+    bomAddDialogVisible.value = false;
+    bomAddFormRef.value?.resetFields();
+    selectedBomProduct.value = null;
+  };
+
+  // 鎵撳紑楂樼骇閫夋嫨
+  const openAdvancedSelect = () => {
+    showAdvancedProductDialog.value = true;
+  };
+
+  // 鐖堕」浜у搧鍙樻洿
+  const handleBomParentProductChange = async (val) => {
+    if (val) {
+      const product = parentProductOptions.value.find(item => item.id === val);
+      selectedBomProduct.value = product || null;
+      
+      // 鐩存帴鐢ㄩ�変腑鐨� BOM ID 璋冪敤 listByBomIdIsParent
+      const { data: treeData } = await listByBomIdIsParent(val);
+      selectedBomTreeData.value = treeData || [];
+      // 灏嗕簩绾ф爲涓殑浜у搧娣诲姞鍒扮埗椤逛骇鍝侀�夐」锛堜粠绗簩绾у紑濮嬶級
+      const addTreeToOptions = (items: any[]) => {
+        items.forEach((item: any) => {
+          // 璺宠繃绗竴绾э紝鍙坊鍔犵浜岀骇鍙婁互涓嬬殑浜у搧
+          if (item.children && item.children.length > 0) {
+            item.children.forEach((child: any) => {
+              const exists = parentProductOptions.value.some(opt => opt.id === child.id);
+              if (!exists) {
+                parentProductOptions.value.push({
+                  id: child.id,
+                  productName: child.productName,
+                  model: child.model,
+                  productCode: child.model,
+                  spec: child.drawingNumber,
+                  drawingNumber: child.drawingNumber,
+                  stockQuantity: child.stockQuantity || 0
+                });
+              }
+              // 閫掑綊娣诲姞瀛愰」
+              if (child.children && child.children.length > 0) {
+                addTreeToOptions([child]);
+              }
+            });
+          }
+        });
+      };
+      if (selectedBomTreeData.value.length > 0) {
+        addTreeToOptions(selectedBomTreeData.value);
+      }
+    } else {
+      selectedBomProduct.value = null;
+      selectedBomTreeData.value = [];
+    }
+  };
+
+  // 鎻愪氦鎸塀OM娣诲姞
+  const submitBomAdd = () => {
+    bomAddFormRef.value.validate(async (valid) => {
+      if (!valid) return;
+
+      const product = parentProductOptions.value.find(item => item.id === bomAddForm.parentProductId);
+      if (!product) {
+        ElMessage.error("鏈壘鍒伴�変腑鐨勪骇鍝�");
+        return;
+      }
+
+      try {
+        // 浣跨敤閫夋嫨浜у搧鏃朵繚瀛樼殑BOM鏍戞暟鎹�
+        const bomItems = selectedBomTreeData.value || [];
+
+        if (bomItems.length === 0) {
+          ElMessage.warning("璇ヤ骇鍝佹病鏈塀OM淇℃伅");
+          return;
+        }
+
+        // 鍒楄〃鐨勭涓�绾у凡缁忓瓨鍦紝鎶夿OM鏁版嵁浣滀负绗竴绾х殑瀛愰」锛堢浜岀骇锛夋坊鍔�
+        if (dataValue.dataList.length > 0) {
+          const firstLevelItem = dataValue.dataList[0];
+          
+          // 鎶夿OM鏁版嵁娣诲姞鍒扮涓�绾х殑children涓�
+          const addBomItemsRecursively = (items: any[], parentItem: any) => {
+            items.forEach((item: any) => {
+              const newItem: any = {
+                parentId: item.parentId || "",
+                parentTempId: parentItem.tempId || "",
+                productName: item.productName || "",
+                productId: item.productId || item.productModelId || "",
+                model: item.model || "",
+                productModelId: item.productModelId || "",
+                drawingNumber: item.drawingNumber || "",
+                processId: item.processId || "",
+                processName: item.processName || "",
+                unitQuantity: (item.unitQuantity || 0) * bomAddForm.coefficient,
+                demandedQuantity: (item.demandedQuantity || 0) * bomAddForm.coefficient,
+                unit: item.unit || "",
+                children: [],
+                tempId: new Date().getTime() + Math.random(),
+              };
+
+              // 娣诲姞鍒扮埗椤圭殑children
+              if (!parentItem.children) {
+                parentItem.children = [];
+              }
+              parentItem.children.push(newItem);
+
+              // 閫掑綊澶勭悊瀛愰」
+              if (item.children && item.children.length > 0) {
+                addBomItemsRecursively(item.children, newItem);
+              }
+            });
+          };
+
+          addBomItemsRecursively(bomItems, firstLevelItem);
+        }
+
+        ElMessage.success("娣诲姞鎴愬姛");
+        closeBomAddDialog();
+      } catch (error) {
+        console.error("鎸塀OM娣诲姞澶辫触:", error);
+        ElMessage.error("娣诲姞澶辫触");
+      }
+    });
+  };
+
+  onMounted(async () => {
+    // 浠庤矾鐢卞弬鏁板洖鏄炬暟鎹�
+    tableData[0].productName = routeProductName.value as string;
+    tableData[0].drawingNumber = routeDrawingNumber.value as string;
+    tableData[0].model = routeProductModelName.value as string;
+    tableData[0].bomNo = routeBomNo.value as string;
+
+    // 璁㈠崟鎯呭喌涓嬬鐢ㄧ紪杈�
+    if (isOrderPage.value) {
+      dataValue.isEdit = false;
+    }
+
+    // 鍏堝姞杞藉伐搴忛�夐」锛屽啀鍔犺浇鏁版嵁锛岀‘淇漞l-select鑳藉姝g‘鍥炴樉
+    await fetchProcessOptions();
+    await fetchData();
+  });
+</script>
+
+<style scoped>
+.selected-product-info {
+  background-color: #f5f7fa;
+  border-radius: 4px;
+  padding: 16px;
+  margin: 10px 0;
+  position: relative;
+}
+
+.product-info-row {
+  display: flex;
+  margin-bottom: 8px;
+  font-size: 14px;
+}
+
+.product-info-row:last-child {
+  margin-bottom: 0;
+}
+
+.info-label {
+  color: #909399;
+  width: 70px;
+  flex-shrink: 0;
+}
+
+.info-value {
+  color: #303133;
+  flex: 1;
+}
+
+.stock-info-box {
+  position: absolute;
+  right: 16px;
+  top: 50%;
+  transform: translateY(-50%);
+  text-align: center;
+}
+
+.stock-number {
+  font-size: 28px;
+  font-weight: bold;
+  color: #303133;
+  line-height: 1;
+  margin-bottom: 4px;
+}
+
+.stock-label {
+  font-size: 12px;
+  color: #909399;
+}
+</style>

--
Gitblit v1.9.3