From fb6e4a0041575dfacc4da9f8fc66d9a3d1f8a6b8 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 19 五月 2026 15:50:40 +0800
Subject: [PATCH] feat(multiple): 为构建过程添加环境变量管理功能

---
 src/views/productionManagement/productStructure/Detail/index.vue |  267 ++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 212 insertions(+), 55 deletions(-)

diff --git a/src/views/productionManagement/productStructure/Detail/index.vue b/src/views/productionManagement/productStructure/Detail/index.vue
index 9f8ad51..0c326cc 100644
--- a/src/views/productionManagement/productStructure/Detail/index.vue
+++ b/src/views/productionManagement/productStructure/Detail/index.vue
@@ -1,6 +1,6 @@
 <template>
-  <div :class="embedded ? 'embedded-container' : 'app-container'">
-    <PageHeader v-if="!embedded" content="浜у搧缁撴瀯璇︽儏">
+  <div class="app-container">
+    <PageHeader content="浜у搧缁撴瀯璇︽儏">
       <template #right-button>
         <el-button v-if="!dataValue.isEdit && !isOrderPage"
                    type="primary"
@@ -64,6 +64,7 @@
                                filterable
                                clearable
                                style="width: 100%"
+                               @change="value => handleProcessChange(row, value)"
                                :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)">
                       <el-option v-for="item in dataValue.processOptions"
                                  :key="item.id"
@@ -85,6 +86,7 @@
                                      :step="1"
                                      controls-position="right"
                                      style="width: 100%"
+                                     @change="handleUnitQuantityChange"
                                      :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
                   </el-form-item>
                 </template>
@@ -102,7 +104,7 @@
                                      :step="1"
                                      controls-position="right"
                                      style="width: 100%"
-                                     :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
+                                     :disabled="true" />
                   </el-form-item>
                 </template>
               </el-table-column>
@@ -148,6 +150,7 @@
     </el-table>
     <product-select-dialog v-if="dataValue.showProductDialog"
                            v-model:model-value="dataValue.showProductDialog"
+                           :single="true"
                            @confirm="handleProduct" />
   </div>
 </template>
@@ -161,7 +164,10 @@
     reactive,
     ref,
   } from "vue";
-  import { queryList, add } from "@/api/productionManagement/productStructure.js";
+  import {
+    queryList,
+    addBomDetail,
+  } from "@/api/productionManagement/productStructure.js";
   import { listProcessBom } from "@/api/productionManagement/productionOrder.js";
   import { list } from "@/api/productionManagement/productionProcess";
   import { ElMessage } from "element-plus";
@@ -174,18 +180,6 @@
   const ProductSelectDialog = defineAsyncComponent(
     () => import("@/views/basicData/product/ProductSelectDialog.vue")
   );
-  const props = defineProps({
-    embedded: {
-      type: Boolean,
-      default: false,
-    },
-    // 鏄惧紡鎸囧畾BOM涓婚敭锛堢敤浜庡祵鍏ュ埌鈥滃伐鑹鸿矾绾块」鐩�濈瓑椤甸潰鏃讹紝璺敱 query.id 涓嶆槸 bomId 鐨勬儏鍐碉級
-    bomId: {
-      type: [String, Number],
-      default: undefined,
-    },
-  });
-  const embedded = computed(() => props.embedded);
   const emit = defineEmits(["update:router"]);
   const form = ref();
 
@@ -193,8 +187,7 @@
   const router = useRouter();
   const routeId = computed({
     get() {
-      // 浼樺厛浣跨敤澶栭儴浼犲叆鐨� bomId锛屽叾娆′娇鐢ㄨ矾鐢辩殑 bomId锛屾渶鍚庡洖閫�鍒拌矾鐢辩殑 id锛堝吋瀹瑰師椤甸潰锛�
-      return props.bomId ?? route.query.bomId ?? route.query.id;
+      return route.query.id;
     },
 
     set(val) {
@@ -225,6 +218,145 @@
     isEdit: false,
   });
 
+  const normalizeListData = (source: any) => {
+    if (Array.isArray(source)) {
+      return source;
+    }
+    if (Array.isArray(source?.records)) {
+      return source.records;
+    }
+    return [];
+  };
+
+  const getProcessOptionById = (id: any) => {
+    if (id === undefined || id === null || id === "") {
+      return null;
+    }
+    return (
+      normalizeListData(dataValue.processOptions).find(
+        option => String(option.id) === String(id)
+      ) || null
+    );
+  };
+
+  const syncProcessOperationFields = (item: any) => {
+    const processId = item.processId ?? item.operationId ?? "";
+    if (!processId) {
+      item.processId = "";
+      item.operationId = "";
+      item.processName = "";
+      item.operationName = "";
+      return;
+    }
+
+    const option = getProcessOptionById(processId);
+    const processName =
+      option?.name || item.processName || item.operationName || "";
+
+    item.processId = processId;
+    item.operationId = processId;
+    item.processName = processName;
+    item.operationName = processName;
+  };
+
+  const normalizeTreeData = (items: any[]) => {
+    items.forEach((item: any) => {
+      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 toQuantityNumber = (value: any) => {
+    const numberValue = Number(value);
+    if (!Number.isFinite(numberValue)) {
+      return 0;
+    }
+    return Number(numberValue.toFixed(2));
+  };
+
+  const syncDemandedQuantityTree = (
+    items: any[],
+    parentDemandedQuantity: number | null = null
+  ) => {
+    items.forEach((item: any) => {
+      if (parentDemandedQuantity !== null) {
+        item.demandedQuantity = toQuantityNumber(
+          parentDemandedQuantity * toQuantityNumber(item.unitQuantity)
+        );
+      }
+
+      if (Array.isArray(item.children) && item.children.length > 0) {
+        syncDemandedQuantityTree(
+          item.children,
+          toQuantityNumber(item.demandedQuantity)
+        );
+      }
+    });
+  };
+
+  const recalculateDemandedQuantities = () => {
+    if (!isOrderPage.value) {
+      return;
+    }
+
+    syncDemandedQuantityTree(dataValue.dataList);
+  };
+
+  const buildSubmitTree = (items: any[]) => {
+    return items.map((item: any) => {
+      const current = { ...item };
+      syncProcessOperationFields(current);
+      current.children = Array.isArray(current.children)
+        ? buildSubmitTree(current.children)
+        : [];
+      return current;
+    });
+  };
+
+  const findSiblings = (items: any[], tempId: string): any[] | null => {
+    if (!items || items.length === 0) return null;
+    // 妫�鏌ュ綋鍓嶅眰绾�
+    if (items.some(item => item.tempId === tempId)) {
+      return items;
+    }
+    // 閫掑綊鏌ユ壘瀛愮骇
+    for (const item of items) {
+      if (item.children && item.children.length > 0) {
+        const result = findSiblings(item.children, tempId);
+        if (result) return result;
+      }
+    }
+    return null;
+  };
+
+  const handleProcessChange = (row: any, value: any) => {
+    if (value) {
+      const siblings = findSiblings(dataValue.dataList, row.tempId);
+      if (siblings) {
+        const isDuplicate = siblings.some(
+          s => s.tempId !== row.tempId && s.processId === value
+        );
+        if (isDuplicate) {
+          const option = getProcessOptionById(value);
+          const processName = option?.name || "璇ュ伐搴�";
+          ElMessage.warning(`鍚屼竴灞傜骇涓嬩笉鑳介�夋嫨閲嶅鐨勬秷鑰楀伐搴忥細${processName}`);
+          row.processId = "";
+          syncProcessOperationFields(row);
+          return;
+        }
+      }
+    }
+    row.processId = value || "";
+    syncProcessOperationFields(row);
+  };
+
+  const handleUnitQuantityChange = () => {
+    recalculateDemandedQuantities();
+  };
+
   const tableData = reactive([
     {
       productName: "",
@@ -240,43 +372,35 @@
   };
 
   const fetchData = async () => {
-    const setNameRecursively = (items: any[]) => {
-      items.forEach((item: any) => {
-        item.tempId = item.tempId || item.id || new Date().getTime() + Math.random();
-        item.processName =
-          dataValue.processOptions.find(option => option.id === item.processId)?.name || item.processName || "";
-        if (item.children && item.children.length > 0) {
-          setNameRecursively(item.children);
-        }
-      });
-    };
-
     if (isOrderPage.value) {
       // 璁㈠崟鎯呭喌锛氫娇鐢ㄨ鍗曠殑浜у搧缁撴瀯鎺ュ彛
       const { data } = await listProcessBom({ orderId: routeOrderId.value });
-      const list = Array.isArray(data) ? data : (data as any)?.records || [];
-      dataValue.dataList = list;
-      setNameRecursively(dataValue.dataList);
+      dataValue.dataList = (data as any) || [];
+      normalizeTreeData(dataValue.dataList);
+      recalculateDemandedQuantities();
     } else {
       // 闈炶鍗曟儏鍐碉細浣跨敤鍘熸潵鐨勬帴鍙�
       const { data } = await queryList(routeId.value);
       dataValue.dataList = (data as any) || [];
-      // 涓烘墍鏈夐」鍙婂叾瀛愰」璁剧疆name灞炴��
-      setNameRecursively(dataValue.dataList);
+      console.log(dataValue);
+      normalizeTreeData(dataValue.dataList);
       console.log(dataValue.dataList, "dataValue.dataList");
     }
   };
 
   const fetchProcessOptions = async () => {
-    const { data } = await list();
-    dataValue.processOptions = data as any;
+    const { data } = await list({});
+    console.log(data, "dataValue.dataList");
+    dataValue.processOptions = normalizeListData(data);
   };
 
   const handleProduct = (row: any) => {
-    if (row?.length > 1) {
-      ElMessage.error("鍙兘閫夋嫨涓�涓骇鍝�");
+    if (!Array.isArray(row) || row.length === 0) {
+      ElMessage.warning("璇烽�夋嫨涓�涓骇鍝�");
+      return;
     }
-    const productData = row[0];
+    // 鍙厑璁镐竴涓細濡傛灉涓婃父杩斿洖浜嗗涓紝榛樿浣跨敤鏈�鍚庝竴娆¢�夋嫨骞惰鐩栧綋鍓嶅��
+    const productData = row[row.length - 1];
 
     //  鏈�澶栧眰缁勪欢涓紝涓庡綋鍓嶄骇鍝佺浉鍚岀殑浜у搧鍙兘鏈変竴涓�
     const isTopLevel = dataValue.dataList.some(
@@ -339,8 +463,37 @@
   const validateAll = () => {
     let isValid = true;
 
+    // 鏍¢獙涓�缁勫厔寮熻妭鐐圭殑宸ュ簭鏄惁鍞竴
+    const checkProcessUniqueness = (items: any[]) => {
+      if (!items || items.length === 0 || !isValid) return;
+
+      const processIds = new Set();
+      for (const item of items) {
+        if (item.processId) {
+          if (processIds.has(item.processId)) {
+            const option = getProcessOptionById(item.processId);
+            const processName = option?.name || item.processName || "鏈煡宸ュ簭";
+            ElMessage.error(
+              `浜у搧銆�${item.productName}銆嶇殑娑堣�楀伐搴忋��${processName}銆嶅湪褰撳墠灞傜骇宸插瓨鍦紝璇峰嬁閲嶅璁剧疆`
+            );
+            isValid = false;
+            return;
+          }
+          processIds.add(item.processId);
+        }
+      }
+
+      // 閫掑綊鏍¢獙瀛愮骇鐨勫厔寮熻妭鐐�
+      for (const item of items) {
+        if (item.children && item.children.length > 0) {
+          checkProcessUniqueness(item.children);
+        }
+      }
+    };
+
     // 鏍¢獙鍑芥暟
     const validateItem = (item: any, isTopLevel = false) => {
+      if (!isValid) return;
       // 鏍¢獙褰撳墠椤圭殑蹇呭~瀛楁
       if (!item.model) {
         ElMessage.error("璇烽�夋嫨瑙勬牸");
@@ -368,7 +521,7 @@
       //   return;
       // }
 
-      // 閫掑綊鏍¢獙瀛愰」
+      // 閫掑綊鏍¢獙瀛愰」瀛楁
       if (item.children && item.children.length > 0) {
         item.children.forEach(child => {
           validateItem(child, false);
@@ -376,7 +529,11 @@
       }
     };
 
-    // 閬嶅巻鎵�鏈夐《灞傞」
+    // 1. 棣栧厛鏍¢獙鍚屼竴鐖剁骇涓嬬殑鍚屽眰娑堣�楀伐搴忔槸鍚﹀敮涓�
+    checkProcessUniqueness(dataValue.dataList);
+    if (!isValid) return false;
+
+    // 2. 鐒跺悗閬嶅巻鏍¢獙鎵�鏈夐《灞傞」鐨勫瓧娈靛繀濉儏鍐�
     dataValue.dataList.forEach(item => {
       validateItem(item, true);
     });
@@ -386,19 +543,19 @@
 
   const submit = () => {
     dataValue.loading = true;
+    normalizeTreeData(dataValue.dataList);
+    recalculateDemandedQuantities();
 
     // 鍏堣繘琛岃〃鍗曟牎楠�
     const valid = validateAll();
     console.log(dataValue.dataList, "dataValue.dataList");
     if (valid) {
-      add({
+      addBomDetail({
         bomId: routeId.value,
-        children: dataValue.dataList || [],
+        children: buildSubmitTree(dataValue.dataList || []),
       })
         .then(res => {
-          router.push({
-            path: "/productionManagement/productionManagement/productStructure/index",
-          });
+          router.go(-1);
           ElMessage.success("淇濆瓨鎴愬姛");
           dataValue.loading = false;
         })
@@ -456,13 +613,16 @@
           productModelId: undefined,
           processId: "",
           processName: "",
-          unitQuantity: 0,
+          operationId: "",
+          operationName: "",
+          unitQuantity: 1,
           demandedQuantity: 0,
           unit: "",
           children: [],
 
           tempId: new Date().getTime(),
         });
+        recalculateDemandedQuantities();
         return;
       }
       addchildItem(item, tempId);
@@ -482,12 +642,16 @@
         model: undefined,
         productModelId: undefined,
         processId: "",
-        unitQuantity: 0,
+        processName: "",
+        operationId: "",
+        operationName: "",
+        unitQuantity: 1,
         demandedQuantity: 0,
         children: [],
         unit: "",
         tempId: new Date().getTime(),
       });
+      recalculateDemandedQuantities();
       return true;
     }
     if (item.children && item.children.length > 0) {
@@ -534,10 +698,3 @@
     await fetchData();
   });
 </script>
-
-<style scoped>
-.embedded-container {
-  padding: 0;
-  margin: 0;
-}
-</style>
\ No newline at end of file

--
Gitblit v1.9.3