From 5960d731989c58e887b6cca103edfe23dec5e06b Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期四, 23 七月 2026 09:03:34 +0800
Subject: [PATCH] feat(workorder): 添加工单工序管理功能并优化API服务

---
 src/views/mes/pro/workorder/modules/form.vue |  103 +++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 87 insertions(+), 16 deletions(-)

diff --git a/src/views/mes/pro/workorder/modules/form.vue b/src/views/mes/pro/workorder/modules/form.vue
index 676159e..bc00c84 100644
--- a/src/views/mes/pro/workorder/modules/form.vue
+++ b/src/views/mes/pro/workorder/modules/form.vue
@@ -3,6 +3,7 @@
 
   import type { MesProWorkOrderApi } from "#/api/mes/pro/workorder";
   import type { MesProWorkOrderBomApi } from "#/api/mes/pro/workorder/bom";
+  import type { MesProWorkOrderProcessApi } from "#/api/mes/pro/workorder/process";
 
   import { computed, ref } from "vue";
 
@@ -22,29 +23,33 @@
     getWorkOrder,
     updateWorkOrder,
   } from "#/api/mes/pro/workorder";
+  import { batchSaveWorkOrderProcess } from "#/api/mes/pro/workorder/process";
   import { $t } from "#/locales";
   import { BarcodeDetail } from "#/views/wls/barcode/components";
 
   import { useFormSchema } from "../data";
   import BomList from "./bom-list.vue";
   import ItemList from "./item-list.vue";
+  import ProcessList from "./process-list.vue";
 
   const emit = defineEmits(["success"]);
   const formType = ref<FormType>("create");
   const formData = ref<MesProWorkOrderApi.WorkOrder>();
-  const originalSnapshot = ref(""); // 琛ㄥ崟鍘熷鏁版嵁蹇収锛岀敤浜庣‘璁ゆ椂璺宠繃鏈彉鏇寸殑淇濆瓨璇锋眰
-  const subTabsName = ref("bom"); // 褰撳墠閫変腑鐨勫瓙琛� Tab
-  const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>(); // 鏉$爜璇︽儏寮圭獥
+  const originalSnapshot = ref("");
+  const subTabsName = ref("bom");
+  const barcodeDetailRef = ref<InstanceType<typeof BarcodeDetail>>();
+  const processListRef = ref<InstanceType<typeof ProcessList>>();
+  const currentProductId = ref<number>(); // 褰撳墠閫変腑鐨勪骇鍝� ID锛堢敤浜庡伐搴忓姞杞斤級
+  const tempProcessList = ref<MesProWorkOrderProcessApi.WorkOrderProcess[]>([]); // 涓存椂宸ュ簭鍒楄〃
 
   const isEditable = computed(() =>
-    // 鏄惁涓虹紪杈戞ā寮忥紙鍙繚瀛橈級
     ["create", "update"].includes(formType.value)
   );
-  const isConfirm = computed(() => formType.value === "confirm"); // 鏄惁涓虹‘璁ゆā寮�
-  const isFinish = computed(() => formType.value === "finish"); // 鏄惁涓哄畬鎴愭ā寮�
+  const isConfirm = computed(() => formType.value === "confirm");
+  const isFinish = computed(() => formType.value === "finish");
+  const isCreateMode = computed(() => formType.value === "create");
   const canConfirm = computed(
     () =>
-      // 缂栬緫鎬佽崏绋垮彲纭
       formType.value === "update" &&
       formData.value?.status === MesProWorkOrderStatusEnum.PREPARE
   );
@@ -69,6 +74,9 @@
     }
   });
 
+  // 鍒ゆ柇鏄惁鏄剧ず Tab锛堟柊澧炴ā寮忔垨宸叉湁 ID锛�
+  const showTabs = computed(() => isCreateMode.value || formData.value?.id);
+
   const [Form, formApi] = useVbenForm({
     commonConfig: {
       componentProps: {
@@ -81,6 +89,15 @@
     schema: [],
     showDefaultActions: false,
     wrapperClass: "grid-cols-3",
+    handleValuesChange: (values, fieldsChanged) => {
+      // 浜у搧鍙樻洿鏃讹紝瑙﹀彂宸ュ簭鍔犺浇
+      if (isCreateMode.value && fieldsChanged.includes('productId')) {
+        const newProductId = values.productId;
+        if (newProductId && newProductId !== currentProductId.value) {
+          currentProductId.value = newProductId;
+        }
+      }
+    },
   });
 
   /** 閲嶆柊鎸傝浇 schema 骞舵寜琛ㄥ崟鎬佹帶鍒跺簳閮ㄦ寜閽� */
@@ -110,7 +127,7 @@
     );
   }
 
-  /** 浠� BOM 鐗╂枡琛岄濉瓙璁㈠崟锛氬鐢ㄥ綋鍓嶈鍗曚笂涓嬫枃锛岃繘鍏ユ柊澧炴�� */
+  /** 浠� BOM 鐗╂枡琛岄濉瓙璁㈠崟 */
   async function handleGenerateWorkOrder(
     bomRow: MesProWorkOrderBomApi.WorkOrderBom
   ) {
@@ -137,8 +154,15 @@
       type: current.type,
       vendorId: current.vendorId,
     });
+    // 璁剧疆褰撳墠浜у搧 ID锛岃Е鍙戝伐搴忓姞杞�
+    currentProductId.value = bomRow.itemId;
     originalSnapshot.value = JSON.stringify(await formApi.getValues());
     message.info("宸蹭粠 BOM 鐗╂枡棰勫~瀛愯鍗曪紝璇疯ˉ鍏呰鍗曠紪鐮佺瓑淇℃伅鍚庝繚瀛�");
+  }
+
+  /** 宸ュ簭鍒楄〃鍙樺寲鍥炶皟 */
+  function handleProcessListChange(list: MesProWorkOrderProcessApi.WorkOrderProcess[]) {
+    tempProcessList.value = list;
   }
 
   const [Modal, modalApi] = useVbenModal({
@@ -154,7 +178,7 @@
         }
         modalApi.lock();
         try {
-          // 缂栬緫鎬佹湁淇敼鏃跺厛淇濆瓨
+          // 1. 淇濆瓨宸ュ崟淇℃伅锛堝鏋滄湁鍙樻洿锛�
           if (isEditable.value) {
             const current = JSON.stringify(await formApi.getValues());
             if (current !== originalSnapshot.value) {
@@ -163,6 +187,15 @@
               originalSnapshot.value = current;
             }
           }
+          // 2. 鎵归噺淇濆瓨宸ュ簭锛堝鏋滄湁锛�
+          if (tempProcessList.value.length > 0) {
+            const processList = tempProcessList.value.map(item => ({
+              ...item,
+              workOrderId: formData.value!.id,
+            }));
+            await batchSaveWorkOrderProcess(processList);
+          }
+          // 3. 纭宸ュ崟
           await confirmWorkOrder(formData.value.id);
           message.success('璁㈠崟宸茬‘璁�');
           await modalApi.close();
@@ -202,13 +235,31 @@
       const data = (await formApi.getValues()) as MesProWorkOrderApi.WorkOrder;
       try {
         if (formData.value?.id) {
+          // 缂栬緫妯″紡
           await updateWorkOrder({ ...formData.value, ...data });
+          // 鎵归噺淇濆瓨宸ュ簭
+          if (tempProcessList.value.length > 0) {
+            const processList = tempProcessList.value.map(item => ({
+              ...item,
+              workOrderId: formData.value!.id,
+            }));
+            await batchSaveWorkOrderProcess(processList);
+          }
           formData.value = { ...formData.value, ...data };
           emit("success");
           message.success($t("ui.actionMessage.operationSuccess"));
           await modalApi.close();
         } else {
+          // 鏂板妯″紡
           const id = await createWorkOrder(data);
+          // 鎵归噺淇濆瓨宸ュ簭
+          if (tempProcessList.value.length > 0) {
+            const processList = tempProcessList.value.map(item => ({
+              ...item,
+              workOrderId: id,
+            }));
+            await batchSaveWorkOrderProcess(processList);
+          }
           formData.value = {
             ...data,
             id,
@@ -228,9 +279,10 @@
       if (!isOpen) {
         formData.value = undefined;
         originalSnapshot.value = "";
+        currentProductId.value = undefined;
+        tempProcessList.value = [];
         return;
       }
-      // 鍔犺浇鏁版嵁
       const data = modalApi.getData<{
         formType: FormType;
         id?: number;
@@ -238,18 +290,18 @@
       }>();
       formType.value = data.formType;
       subTabsName.value = "bom";
+      tempProcessList.value = [];
       applySchema();
       if (data?.id) {
         modalApi.lock();
         try {
           formData.value = await getWorkOrder(data.id);
-          // 璁剧疆鍒� values
           await formApi.setValues(formData.value);
+          currentProductId.value = formData.value.productId;
         } finally {
           modalApi.unlock();
         }
       } else if (data?.parentRow) {
-        // 鏂板瀛愯鍗曟椂锛岄濉埗璁㈠崟淇℃伅
         const parent = data.parentRow;
         await formApi.setValues({
           clientId: parent.clientId,
@@ -270,20 +322,39 @@
   <Modal :title="getTitle"
          class="w-3/5">
     <Form class="mx-4" />
-    <!-- BOM / 鐗╂枡闇�姹� Tab锛氶潪鏂板缓鎬佸睍绀� -->
-    <template v-if="formData?.id">
+    <!-- BOM / 宸ュ簭 / 鐗╂枡闇�姹� Tab -->
+    <template v-if="showTabs">
       <Tabs v-model:active-key="subTabsName"
             class="mx-4">
         <Tabs.TabPane key="bom"
                       tab="璁㈠崟 BOM">
-          <BomList :form-type="formType"
+          <BomList v-if="formData?.id"
+                   :form-type="formType"
                    :work-order="formData"
                    :work-order-id="formData.id"
                    @generate-work-order="handleGenerateWorkOrder" />
+          <div v-else class="text-gray-400 text-center py-8">
+            璇峰厛淇濆瓨宸ュ崟鍚庡啀閰嶇疆 BOM
+          </div>
+        </Tabs.TabPane>
+        <Tabs.TabPane key="process"
+                      tab="宸ュ簭">
+          <ProcessList
+            ref="processListRef"
+            :form-type="formType"
+            :work-order="formData || {}"
+            :work-order-id="formData?.id"
+            :product-id="currentProductId"
+            @process-list-change="handleProcessListChange"
+          />
         </Tabs.TabPane>
         <Tabs.TabPane key="item"
                       tab="鐗╂枡闇�姹�">
-          <ItemList :work-order-id="formData.id" />
+          <ItemList v-if="formData?.id"
+                    :work-order-id="formData.id" />
+          <div v-else class="text-gray-400 text-center py-8">
+            璇峰厛淇濆瓨宸ュ崟鍚庡啀鏌ョ湅鐗╂枡闇�姹�
+          </div>
         </Tabs.TabPane>
       </Tabs>
     </template>

--
Gitblit v1.9.3