From 6738e8f5a78431e41fed28e6b795e70c2396070a Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期四, 03 九月 2026 17:27:23 +0800
Subject: [PATCH] refactor(mes): 优化工艺路线配置与工单表单功能

---
 src/views/mes/process-design/route/modules/form.vue |  146 +++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 124 insertions(+), 22 deletions(-)

diff --git a/src/views/mes/process-design/route/modules/form.vue b/src/views/mes/process-design/route/modules/form.vue
index 8c0c230..cf73e0e 100644
--- a/src/views/mes/process-design/route/modules/form.vue
+++ b/src/views/mes/process-design/route/modules/form.vue
@@ -2,24 +2,28 @@
 import type { FormType } from '../data';
 
 import type { MesProRouteApi } from '#/api/mes/pro/route';
+import type { MesProRouteProductApi } from '#/api/mes/pro/route/product';
 
 import { computed, ref } from 'vue';
 
-import { useVbenModal } from '@vben/common-ui';
+import { confirm, useVbenModal } from '@vben/common-ui';
 
-import { message, TabPane, Tabs } from 'ant-design-vue';
+import { Button, Divider, message, Select } from 'ant-design-vue';
 
 import { useVbenForm } from '#/adapter/form';
 import { createRoute, getRoute, updateRoute } from '#/api/mes/pro/route';
+import {
+  deleteRouteProduct,
+  getRouteProductListByRoute,
+} from '#/api/mes/pro/route/product';
 import { $t } from '#/locales';
 
 import { useFormSchema } from '../data';
-import ProcessList from './process-list.vue';
-import ProductList from './product-list.vue';
+import ProductForm from './product-form.vue';
+import Workbench from './workbench.vue';
 
 const emit = defineEmits(['success']);
 const formType = ref<FormType>('create'); // 琛ㄥ崟妯″紡
-const subTab = ref('process'); // 褰撳墠婵�娲荤殑瀛愯〃 Tab
 const formData = ref<MesProRouteApi.Route>();
 
 const isDetail = computed(() => formType.value === 'detail'); // 鏄惁鏌ョ湅妯″紡
@@ -32,6 +36,72 @@
     : $t('ui.actionTitle.create', ['宸ヨ壓璺嚎']);
 });
 
+// ==================== 浜у搧缁戝畾 ====================
+const products = ref<MesProRouteProductApi.RouteProduct[]>([]); // 鍏宠仈浜у搧鍒楄〃
+const selectedProductItemId = ref<number>(); // 褰撳墠閫変腑鐨勪骇鍝佺墿鏂欑紪鍙�
+const selectedProduct = computed(() =>
+  products.value.find((item) => item.itemId === selectedProductItemId.value),
+);
+const productOptions = computed(() =>
+  products.value.map((item) => ({ label: item.itemName, value: item.itemId })),
+);
+
+async function loadProducts() {
+  const routeId = formData.value?.id;
+  if (!routeId) {
+    products.value = [];
+    return;
+  }
+  products.value = (await getRouteProductListByRoute(routeId)) || [];
+  if (
+    !products.value.some((item) => item.itemId === selectedProductItemId.value)
+  ) {
+    selectedProductItemId.value = products.value[0]?.itemId;
+  }
+}
+
+const [ProductFormModal, productFormModalApi] = useVbenModal({
+  connectedComponent: ProductForm,
+  destroyOnClose: true,
+});
+
+/** 鎵撳紑浜у搧寮圭獥锛堢粦瀹� / 缂栬緫锛� */
+function openProductForm(row?: MesProRouteProductApi.RouteProduct) {
+  if (!formData.value?.id) {
+    return;
+  }
+  productFormModalApi
+    .setData(
+      row
+        ? { id: row.id, routeId: formData.value.id }
+        : {
+            routeId: formData.value.id,
+            boundItemIds: products.value
+              .map((item) => item.itemId)
+              .filter((id): id is number => id != null),
+          },
+    )
+    .open();
+}
+
+/** 绉婚櫎浜у搧锛堜細绾ц仈鍒犻櫎璇ヤ骇鍝佺殑鎶曞叆鐗╂枡閰嶇疆锛� */
+async function handleRemoveProduct() {
+  if (!selectedProduct.value?.id) {
+    return;
+  }
+  try {
+    await confirm(
+      `纭灏嗕骇鍝併��${selectedProduct.value.itemName}銆嶄粠璇ュ伐鑹鸿矾绾跨Щ闄わ紵鍏跺叏閮ㄦ姇鍏ョ墿鏂欓厤缃皢涓�骞跺垹闄ゃ�俙,
+    );
+  } catch {
+    return;
+  }
+  await deleteRouteProduct(selectedProduct.value.id);
+  message.success($t('ui.actionMessage.operationSuccess'));
+  await loadProducts();
+}
+
+// ==================== 鍩烘湰淇℃伅琛ㄥ崟 ====================
 const [Form, formApi] = useVbenForm({
   commonConfig: {
     componentProps: { class: 'w-full' },
@@ -59,11 +129,12 @@
     const data = (await formApi.getValues()) as MesProRouteApi.Route;
     try {
       if (formType.value === 'create') {
-        // 鏂板鎴愬姛鍚庡垏鍒扮紪杈戞ā寮忥紝鏂逛究缁х画缁存姢缁勬垚宸ュ簭銆佸叧鑱斾骇鍝�
+        // 鏂板鎴愬姛鍚庡垏鍒扮紪杈戞ā寮忥紝杩涘叆銆屼骇鍝佷笌宸ュ簭閰嶇疆銆嶅伐浣滃彴
         const id = await createRoute(data);
         formData.value = { ...data, id };
         await formApi.setFieldValue('id', id);
         formType.value = 'update';
+        await loadProducts();
       } else {
         await updateRoute(data);
         formData.value = { ...formData.value, ...data };
@@ -82,10 +153,11 @@
   async onOpenChange(isOpen: boolean) {
     if (!isOpen) {
       formData.value = undefined;
+      products.value = [];
+      selectedProductItemId.value = undefined;
       return;
     }
     formApi.setState({ schema: useFormSchema(formApi) });
-    subTab.value = 'process';
     // 鍔犺浇鏁版嵁
     const data = modalApi.getData<{ formType: FormType; id?: number }>();
     formType.value = data.formType;
@@ -99,6 +171,7 @@
       formData.value = await getRoute(data.id);
       // 璁剧疆鍒� values
       await formApi.setValues(formData.value);
+      await loadProducts();
     } finally {
       modalApi.unlock();
     }
@@ -107,20 +180,49 @@
 </script>
 
 <template>
-  <Modal :title="getTitle" class="w-4/5">
+  <Modal :title="getTitle" class="w-11/12">
     <Form class="mx-4" />
-    <!-- 缂栬緫/璇︽儏妯″紡涓嬪睍绀哄瓙琛� Tab锛屾柊澧炴ā寮忎笅闅愯棌 -->
-    <Tabs
-      v-if="formType !== 'create' && formData?.id"
-      v-model:active-key="subTab"
-      class="mx-4 mt-4"
-    >
-      <TabPane key="process" tab="缁勬垚宸ュ簭">
-        <ProcessList :form-type="formType" :route-id="formData.id" />
-      </TabPane>
-      <TabPane key="product" tab="鍏宠仈浜у搧">
-        <ProductList :form-type="formType" :route-id="formData.id" />
-      </TabPane>
-    </Tabs>
+    <!-- 缂栬緫/璇︽儏妯″紡涓嬪睍绀恒�屼骇鍝佷笌宸ュ簭閰嶇疆銆嶅伐浣滃彴锛屾柊澧炴ā寮忎笅闅愯棌 -->
+    <template v-if="formData?.id">
+      <div class="mx-4 mt-4">
+        <Divider class="!my-2" orientation="left">
+          <span class="text-sm">浜у搧涓庡伐搴忛厤缃�</span>
+        </Divider>
+        <div class="flex flex-wrap items-center gap-3">
+          <span class="shrink-0 text-sm font-medium">鐢熶骇浜у搧</span>
+          <Select
+            v-model:value="selectedProductItemId"
+            class="!w-64"
+            allow-clear
+            :disabled="isDetail"
+            placeholder="璇烽�夋嫨鎴栫粦瀹氫骇鍝�"
+            :options="productOptions"
+          />
+          <template v-if="!isDetail">
+            <Button type="primary" @click="openProductForm()"> 缁戝畾浜у搧 </Button>
+            <Button
+              :disabled="!selectedProduct"
+              @click="openProductForm(selectedProduct)"
+            >
+              缂栬緫浜у搧
+            </Button>
+            <Button danger :disabled="!selectedProduct" @click="handleRemoveProduct">
+              绉婚櫎浜у搧
+            </Button>
+          </template>
+        </div>
+        <p v-if="products.length === 0" class="mt-2 text-xs text-gray-400">
+          鍏堢粦瀹氳鐢熶骇鐨勭墿鏂欙紝鍐嶄负姣忛亾宸ュ簭閰嶇疆銆屾姇鍏� + 浜у嚭銆�
+        </p>
+        <div class="mt-3">
+          <Workbench
+            :form-type="formType"
+            :product="selectedProduct"
+            :route-id="formData.id"
+          />
+        </div>
+      </div>
+    </template>
   </Modal>
-</template>
+  <ProductFormModal @success="loadProducts" />
+</template>
\ No newline at end of file

--
Gitblit v1.9.3