From f11bd0d8d9c1190340a445a67df9e43cdbde0a3e Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期四, 27 八月 2026 11:21:26 +0800
Subject: [PATCH] feat(mes): 基础参数与工序绑定前端 + 报工填写参数字段

---
 src/views/mes/process-design/process/index.vue                         |   21 ++
 src/views/mes/pro/feedback/detail/index.vue                            |   39 ++++
 src/views/mes/process-design/parameter/modules/process-link-dialog.vue |   70 +++++++
 src/views/mes/process-design/process/modules/param-manage.vue          |  193 +++++++++++++++++++++
 src/views/mes/pro/feedback/data.ts                                     |    5 
 src/api/mes/pro/feedback/index.ts                                      |    7 
 src/views/mes/pro/feedback/modules/form.vue                            |   37 ++-
 src/views/mes/process-design/parameter/index.vue                       |   21 ++
 src/api/mes/pro/workorderparamrecord/index.ts                          |    1 
 src/views/mes/pro/feedback/param-state.ts                              |   47 +++++
 src/api/mes/pro/processdesign/processparam/index.ts                    |   52 +++++
 11 files changed, 473 insertions(+), 20 deletions(-)

diff --git a/src/api/mes/pro/feedback/index.ts b/src/api/mes/pro/feedback/index.ts
index 4ac3351..1ce18a2 100644
--- a/src/api/mes/pro/feedback/index.ts
+++ b/src/api/mes/pro/feedback/index.ts
@@ -70,6 +70,13 @@
   export interface ParamValue {
     detailId: number;
     value?: string;
+    // ---- 鍥炴樉瀛楁锛堟姤宸ヨ鎯�/鍒楄〃杩斿洖锛� ----
+    paramCode?: string;
+    paramName?: string;
+    paramType?: number;
+    unitMeasureName?: string;
+    paramValue?: string;
+    required?: boolean;
   }
 
   /** MES 鐢熶骇鎶ュ伐鍒嗛〉鏌ヨ鍙傛暟 */
diff --git a/src/api/mes/pro/processdesign/processparam/index.ts b/src/api/mes/pro/processdesign/processparam/index.ts
new file mode 100644
index 0000000..7ddf303
--- /dev/null
+++ b/src/api/mes/pro/processdesign/processparam/index.ts
@@ -0,0 +1,52 @@
+import { requestClient } from '#/api/request';
+
+export namespace MesProProcessParamApi {
+  /** 宸ュ簭-宸ヨ壓鍙傛暟缁戝畾 */
+  export interface ProcessParam {
+    id?: number;
+    processId?: number;
+    processParamId?: number;
+    paramCode?: string;
+    paramName?: string;
+    paramType?: number;
+    unitMeasureId?: number;
+    unitMeasureName?: string;
+    required?: boolean;
+    sort?: number;
+    remark?: string;
+  }
+
+  /** 淇濆瓨缁戝畾椤� */
+  export interface SaveItem {
+    processParamId: number;
+    required?: boolean;
+    sort?: number;
+    remark?: string;
+  }
+}
+
+/** 鏌ヨ宸ュ簭缁戝畾鐨勫伐鑹哄弬鏁板垪琛� */
+export function getProcessParamListByProcess(processId: number) {
+  return requestClient.get<MesProProcessParamApi.ProcessParam[]>(
+    '/mes/pro/process-param/list-by-process',
+    { params: { processId } },
+  );
+}
+
+/** 鍏ㄩ噺瑕嗙洊淇濆瓨宸ュ簭缁戝畾鐨勫伐鑹哄弬鏁� */
+export function updateProcessParams(
+  processId: number,
+  items: MesProProcessParamApi.SaveItem[],
+) {
+  return requestClient.post('/mes/pro/process-param/update-bindings', {
+    processId,
+    items,
+  });
+}
+
+/** 鏌ヨ鏌愬伐鑹哄弬鏁拌鍝簺宸ュ簭缁戝畾 */
+export function getProcessListByParam(paramId: number) {
+  return requestClient.get<
+    { id: number; code?: string; name?: string; attention?: string }[]
+  >('/mes/pro/process-param/list-by-param', { params: { paramId } });
+}
diff --git a/src/api/mes/pro/workorderparamrecord/index.ts b/src/api/mes/pro/workorderparamrecord/index.ts
index babaafd..90ea727 100644
--- a/src/api/mes/pro/workorderparamrecord/index.ts
+++ b/src/api/mes/pro/workorderparamrecord/index.ts
@@ -13,6 +13,7 @@
     unitMeasureId?: number;
     unitMeasureName?: string;
     paramValue?: string;
+    required?: boolean;
     remark?: string;
   }
 
diff --git a/src/views/mes/pro/feedback/data.ts b/src/views/mes/pro/feedback/data.ts
index 036cae3..573f29c 100644
--- a/src/views/mes/pro/feedback/data.ts
+++ b/src/views/mes/pro/feedback/data.ts
@@ -18,8 +18,8 @@
 
 import { generateAutoCode } from '#/api/mes/md/autocode/record';
 import { getRouteProcessByRouteAndProcess } from '#/api/mes/pro/route/process';
-import { getWorkOrderParamRecordPage } from '#/api/mes/pro/workorderparamrecord';
 import { getSimpleUserList } from '#/api/system/user';
+import { loadParamDetails } from './param-state';
 import { getRangePickerDefaultProps } from '#/utils';
 import { MdItemSelect } from '#/views/mes/md/item/components';
 import { MdWorkstationSelect } from '#/views/mes/md/workstation/components';
@@ -287,9 +287,10 @@
           workstationId: values.workstationId,
         }),
       },
-      // 浠诲姟鍙樻洿锛氳嚜鍔ㄥ~鍏呭叧鑱斿瓧娈点�佷骇鍝佷俊鎭�乧heckFlag
+      // 浠诲姟鍙樻洿锛氳嚜鍔ㄥ~鍏呭叧鑱斿瓧娈点�佷骇鍝佷俊鎭�乧heckFlag锛屽苟鍔犺浇浠诲姟缁戝畾鐨勫伐鑹哄弬鏁�
       componentProps: {
         onChange: async (task?: MesProTaskApi.Task) => {
+          await loadParamDetails(task?.id);
           if (!task) {
             return;
           }
diff --git a/src/views/mes/pro/feedback/detail/index.vue b/src/views/mes/pro/feedback/detail/index.vue
index e3ecf9c..01acd2e 100644
--- a/src/views/mes/pro/feedback/detail/index.vue
+++ b/src/views/mes/pro/feedback/detail/index.vue
@@ -7,15 +7,27 @@
 import { Page } from '@vben/common-ui';
 import { useTabs } from '@vben/hooks';
 
-import { Card, Tabs } from 'ant-design-vue';
+import { DICT_TYPE } from '@vben/constants';
+
+import { Card, Table, Tabs } from 'ant-design-vue';
 
 import { TableAction } from '#/adapter/vxe-table';
 import { getFeedback } from '#/api/mes/pro/feedback';
+import { DictTag } from '#/components/dict-tag';
 import { useDescription } from '#/components/description';
 
 import { useDetailSchema } from '../data';
 import ItemConsumeList from '../modules/item-consume-list.vue';
 import ProductProduceList from '../modules/product-produce-list.vue';
+
+const paramColumns = [
+  { title: '鍙傛暟鍚嶇О', dataIndex: 'paramName', key: 'paramName' },
+  { title: '鍙傛暟缂栫爜', dataIndex: 'paramCode', key: 'paramCode' },
+  { title: '鍙傛暟绫诲瀷', dataIndex: 'paramType', key: 'paramType' },
+  { title: '鍗曚綅', dataIndex: 'unitMeasureName', key: 'unitMeasureName' },
+  { title: '鍙傛暟鍊�', dataIndex: 'paramValue', key: 'paramValue' },
+  { title: '鏄惁蹇呭~', dataIndex: 'required', key: 'required' },
+];
 
 const props = defineProps<{ id: string }>();
 
@@ -62,6 +74,31 @@
     <Card class="min-h-[10%]">
       <Descriptions :data="feedback" />
     </Card>
+    <Card v-if="feedback.paramValues && feedback.paramValues.length > 0" class="mt-3" title="宸ヨ壓鍙傛暟">
+      <Table
+        :columns="paramColumns"
+        :data-source="feedback.paramValues"
+        :pagination="false"
+        row-key="detailId"
+        size="small"
+      >
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'paramType'">
+            <DictTag
+              v-if="record.paramType !== undefined && record.paramType !== null"
+              :type="DICT_TYPE.MES_PD_PARAM_TYPE"
+              :value="record.paramType"
+            />
+          </template>
+          <template v-else-if="column.key === 'required'">
+            <DictTag
+              :type="DICT_TYPE.INFRA_BOOLEAN_STRING"
+              :value="record.required ? 'true' : 'false'"
+            />
+          </template>
+        </template>
+      </Table>
+    </Card>
 
   </Page>
 </template>
diff --git a/src/views/mes/pro/feedback/modules/form.vue b/src/views/mes/pro/feedback/modules/form.vue
index 6857a99..a295a40 100644
--- a/src/views/mes/pro/feedback/modules/form.vue
+++ b/src/views/mes/pro/feedback/modules/form.vue
@@ -26,10 +26,10 @@
 } from '#/api/mes/pro/feedback';
 import { getRouteProcessByRouteAndProcess } from '#/api/mes/pro/route/process';
 import { getWorkOrder } from '#/api/mes/pro/workorder';
-import { getWorkOrderParamRecordPage } from '#/api/mes/pro/workorderparamrecord';
 import { $t } from '#/locales';
 
 import { useFormSchema } from '../data';
+import { findMissingRequiredParams, loadParamDetails, paramDetails } from '../param-state';
 import BatchInputDialog from './batch-input-dialog.vue';
 import ItemConsumeList from './item-consume-list.vue';
 import ProductProduceList from './product-produce-list.vue';
@@ -41,7 +41,6 @@
 const userStore = useUserStore();
 const subTabsName = ref('itemConsume');
 const originalSnapshot = ref(''); // 琛ㄥ崟鍘熷鏁版嵁蹇収锛岀敤浜� submit 鏃惰烦杩囨湭鍙樻洿鐨勪繚瀛樿姹�
-const paramDetails = ref<Array<{ id: number; paramName?: string; paramCode?: string; unitMeasureName?: string; paramValue?: string }>>([]);
 
 const isEditable = computed(() =>
   ['create', 'submit', 'update'].includes(formType.value),
@@ -118,6 +117,9 @@
   if (!valid) {
     return;
   }
+  if (!checkRequiredParams()) {
+    return;
+  }
   modalApi.lock();
   try {
     const data = await getAlignedData();
@@ -149,25 +151,21 @@
 const batchDialogRef = ref<InstanceType<typeof BatchInputDialog>>();
 const batchActionType = ref<'submit' | 'approve'>('');
 
-async function loadParamDetails(taskId?: number) {
-  paramDetails.value = [];
-  if (!taskId) return;
-  const page = await getWorkOrderParamRecordPage({ taskId, pageNo: 1, pageSize: 1 });
-  const record = page.list?.[0];
-  paramDetails.value = (record?.details ?? []).map((detail) => ({
-    id: detail.id!,
-    paramCode: detail.paramCode,
-    paramName: detail.paramName,
-    unitMeasureName: detail.unitMeasureName,
-    paramValue: detail.paramValue,
-  }));
-}
-
 function syncParamValues(data: MesProFeedbackApi.Feedback) {
   data.paramValues = paramDetails.value.map((detail) => ({
     detailId: detail.id,
     value: detail.paramValue,
   }));
+}
+
+/** 鏍¢獙蹇呭~鍙傛暟鏄惁濉啓瀹屾暣 */
+function checkRequiredParams(): boolean {
+  const missing = findMissingRequiredParams();
+  if (missing.length > 0) {
+    message.warning(`蹇呭~鍙傛暟鏈~鍐欙細${missing.join('銆�')}`);
+    return false;
+  }
+  return true;
 }
 
 /** 鎵ц鎻愪氦锛堝惈鎵规灞炴�э級 */
@@ -188,6 +186,9 @@
 async function handleSubmit() {
   const { valid } = await formApi.validate();
   if (!valid) {
+    return;
+  }
+  if (!checkRequiredParams()) {
     return;
   }
   modalApi.lock();
@@ -326,6 +327,7 @@
       formData.value = undefined;
       subTabsName.value = 'itemConsume';
       originalSnapshot.value = '';
+      paramDetails.value = [];
       return;
     }
     // 鍔犺浇鏁版嵁
@@ -383,7 +385,8 @@
       <div class="mb-3 font-medium">宸ヨ壓鍙傛暟</div>
       <div class="grid grid-cols-2 gap-3">
         <div v-for="detail in paramDetails" :key="detail.id" class="flex items-center gap-2">
-          <span class="w-40 text-right text-sm text-gray-600">
+          <span class="w-44 text-right text-sm text-gray-600">
+            <span v-if="detail.required" class="text-red-500">*&nbsp;</span>
             {{ detail.paramName || detail.paramCode }}{{ detail.unitMeasureName ? `锛�${detail.unitMeasureName}锛塦 : '' }}
           </span>
           <Input v-model:value="detail.paramValue" :disabled="!isEditable" placeholder="璇疯緭鍏ュ弬鏁板��" />
diff --git a/src/views/mes/pro/feedback/param-state.ts b/src/views/mes/pro/feedback/param-state.ts
new file mode 100644
index 0000000..2b3599a
--- /dev/null
+++ b/src/views/mes/pro/feedback/param-state.ts
@@ -0,0 +1,47 @@
+import { ref } from 'vue';
+
+import { getWorkOrderParamRecordPage } from '#/api/mes/pro/workorderparamrecord';
+
+/** 鎶ュ伐琛ㄥ崟瑕佸~鍐欑殑鍙傛暟椤癸紙鏉ヨ嚜浠诲姟鍙傛暟璁板綍蹇収锛� */
+export interface ParamDetailItem {
+  id: number;
+  paramCode?: string;
+  paramName?: string;
+  paramType?: number;
+  unitMeasureName?: string;
+  paramValue?: string;
+  required?: boolean;
+}
+
+/** 褰撳墠鎶ュ伐琛ㄥ崟鐨勫弬鏁版槑缁嗭紙鍏变韩缁欒〃鍗曚笌浠诲姟閫夋嫨鑱斿姩锛� */
+export const paramDetails = ref<ParamDetailItem[]>([]);
+
+/** 鎸変换鍔″姞杞藉弬鏁版槑缁嗭紙浠诲姟鏃犲弬鏁拌褰曟椂涓虹┖鏁扮粍锛� */
+export async function loadParamDetails(taskId?: number) {
+  paramDetails.value = [];
+  if (!taskId) {
+    return;
+  }
+  const page = await getWorkOrderParamRecordPage({
+    taskId,
+    pageNo: 1,
+    pageSize: 1,
+  });
+  const record = page.list?.[0];
+  paramDetails.value = (record?.details ?? []).map((detail) => ({
+    id: detail.id!,
+    paramCode: detail.paramCode,
+    paramName: detail.paramName,
+    paramType: detail.paramType,
+    unitMeasureName: detail.unitMeasureName,
+    paramValue: detail.paramValue,
+    required: detail.required,
+  }));
+}
+
+/** 鏍¢獙蹇呭~鍙傛暟鏄惁閮藉凡濉啓锛涜繑鍥炴湭濉啓鐨勫弬鏁板悕 */
+export function findMissingRequiredParams(): string[] {
+  return paramDetails.value
+    .filter((detail) => detail.required && !detail.paramValue?.trim())
+    .map((detail) => detail.paramName || detail.paramCode || '');
+}
diff --git a/src/views/mes/process-design/parameter/index.vue b/src/views/mes/process-design/parameter/index.vue
index d022a88..f8ff530 100644
--- a/src/views/mes/process-design/parameter/index.vue
+++ b/src/views/mes/process-design/parameter/index.vue
@@ -17,15 +17,28 @@
 
 import { useGridColumns, useGridFormSchema } from './data';
 import Form from './modules/form.vue';
+import ProcessLinkDialog from './modules/process-link-dialog.vue';
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
 });
 
+const [ProcessLinkModal, processLinkModalApi] = useVbenModal({
+  connectedComponent: ProcessLinkDialog,
+  destroyOnClose: true,
+});
+
 /** 鍒锋柊琛ㄦ牸 */
 function handleRefresh() {
   gridApi.query();
+}
+
+/** 鏌ョ湅璇ュ弬鏁拌鍝簺宸ュ簭缁戝畾 */
+function handleViewProcessLink(row: MesPdParameterApi.Parameter) {
+  processLinkModalApi
+    .setData({ paramId: row.id, paramName: row.paramName })
+    .open();
 }
 
 /** 鍒涘缓宸ヨ壓鍙傛暟 */
@@ -98,6 +111,7 @@
 
 <template>
   <Page auto-content-height><FormModal @success="handleRefresh" />
+    <ProcessLinkModal />
     <Grid table-title="宸ヨ壓鍙傛暟鍒楄〃">
       <template #toolbar-tools>
         <TableAction
@@ -126,6 +140,13 @@
         <TableAction
           :actions="[
             {
+              label: '鍏宠仈宸ュ簭',
+              type: 'link',
+              icon: ACTION_ICON.LINK,
+              auth: ['mes:pd:parameter:update'],
+              onClick: handleViewProcessLink.bind(null, row),
+            },
+            {
               label: $t('common.edit'),
               type: 'link',
               icon: ACTION_ICON.EDIT,
diff --git a/src/views/mes/process-design/parameter/modules/process-link-dialog.vue b/src/views/mes/process-design/parameter/modules/process-link-dialog.vue
new file mode 100644
index 0000000..1a7b09e
--- /dev/null
+++ b/src/views/mes/process-design/parameter/modules/process-link-dialog.vue
@@ -0,0 +1,70 @@
+<script lang="ts" setup>
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { Table } from 'ant-design-vue';
+
+import { getProcessListByParam } from '#/api/mes/pro/processdesign/processparam';
+
+const emit = defineEmits(['success']);
+
+const paramId = ref<number>();
+const paramName = ref<string>('');
+const loading = ref(false);
+const processList = ref<{ id: number; code?: string; name?: string; attention?: string }[]>([]);
+
+const getTitle = computed(() => `鍏宠仈宸ュ簭 - ${paramName.value || ''}`);
+
+async function loadData() {
+  if (!paramId.value) {
+    return;
+  }
+  loading.value = true;
+  try {
+    processList.value = await getProcessListByParam(paramId.value);
+  } finally {
+    loading.value = false;
+  }
+}
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    await modalApi.close();
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      paramId.value = undefined;
+      processList.value = [];
+      return;
+    }
+    const data = modalApi.getData<{ paramId: number; paramName?: string }>();
+    paramId.value = data.paramId;
+    paramName.value = data.paramName || '';
+    await loadData();
+  },
+});
+
+const columns = [
+  { title: '宸ュ簭缂栫爜', dataIndex: 'code', key: 'code' },
+  { title: '宸ュ簭鍚嶇О', dataIndex: 'name', key: 'name' },
+  { title: '宸ヨ壓瑕佹眰', dataIndex: 'attention', key: 'attention' },
+];
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <Table
+      :columns="columns"
+      :data-source="processList"
+      :loading="loading"
+      :pagination="false"
+      row-key="id"
+      size="small"
+    >
+      <template #emptyText>
+        {{ loading ? '鍔犺浇涓��' : '璇ュ弬鏁版殏鏈浠讳綍宸ュ簭缁戝畾' }}
+      </template>
+    </Table>
+  </Modal>
+</template>
diff --git a/src/views/mes/process-design/process/index.vue b/src/views/mes/process-design/process/index.vue
index f1ee23f..f24e48f 100644
--- a/src/views/mes/process-design/process/index.vue
+++ b/src/views/mes/process-design/process/index.vue
@@ -17,15 +17,28 @@
 
 import { useGridColumns, useGridFormSchema } from './data';
 import Form from './modules/form.vue';
+import ParamManage from './modules/param-manage.vue';
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
 });
 
+const [ParamManageModal, paramManageModalApi] = useVbenModal({
+  connectedComponent: ParamManage,
+  destroyOnClose: true,
+});
+
 /** 鍒锋柊琛ㄦ牸 */
 function handleRefresh() {
   gridApi.query();
+}
+
+/** 绠$悊宸ュ簭缁戝畾鐨勫伐鑹哄弬鏁� */
+function handleParamManage(row: MesProProcessApi.Process) {
+  paramManageModalApi
+    .setData({ processId: row.id, processName: row.name })
+    .open();
 }
 
 /** 鍒涘缓鐢熶骇宸ュ簭 */
@@ -97,6 +110,7 @@
 
 <template>
   <Page auto-content-height><FormModal @success="handleRefresh" />
+    <ParamManageModal @success="handleRefresh" />
     <Grid table-title="鐢熶骇宸ュ簭鍒楄〃">
       <template #toolbar-tools>
         <TableAction
@@ -125,6 +139,13 @@
         <TableAction
           :actions="[
             {
+              label: '宸ヨ壓鍙傛暟',
+              type: 'link',
+              icon: ACTION_ICON.EDIT,
+              auth: ['mes:pro-process:update'],
+              onClick: handleParamManage.bind(null, row),
+            },
+            {
               label: $t('common.edit'),
               type: 'link',
               icon: ACTION_ICON.EDIT,
diff --git a/src/views/mes/process-design/process/modules/param-manage.vue b/src/views/mes/process-design/process/modules/param-manage.vue
new file mode 100644
index 0000000..e0b5157
--- /dev/null
+++ b/src/views/mes/process-design/process/modules/param-manage.vue
@@ -0,0 +1,193 @@
+<script lang="ts" setup>
+import type { MesPdParameterApi } from '#/api/mes/pro/processdesign/parameter';
+import type { MesProProcessParamApi } from '#/api/mes/pro/processdesign/processparam';
+
+import { computed, ref } from 'vue';
+
+import { DICT_TYPE } from '@vben/constants';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { Button, Input, InputNumber, message, Switch } from 'ant-design-vue';
+
+import {
+  getProcessParamListByProcess,
+  updateProcessParams,
+} from '#/api/mes/pro/processdesign/processparam';
+import { $t } from '#/locales';
+import { DictTag } from '#/components/dict-tag';
+
+import ParameterSelectDialog from '../../template/modules/parameter-select-dialog.vue';
+
+const emit = defineEmits(['success']);
+
+const processId = ref<number>();
+const processName = ref<string>('');
+const loading = ref(false);
+const boundList = ref<MesProProcessParamApi.ProcessParam[]>([]);
+const parameterSelectDialogRef =
+  ref<InstanceType<typeof ParameterSelectDialog>>();
+
+const getTitle = computed(() => `宸ヨ壓鍙傛暟缁戝畾 - ${processName.value || ''}`);
+
+/** 鍔犺浇宸ュ簭宸茬粦瀹氱殑鍙傛暟 */
+async function loadData() {
+  if (!processId.value) {
+    return;
+  }
+  loading.value = true;
+  try {
+    boundList.value = await getProcessParamListByProcess(processId.value);
+  } finally {
+    loading.value = false;
+  }
+}
+
+/** 鎵撳紑宸ヨ壓鍙傛暟閫夋嫨寮圭獥 */
+function handleSelectParameters() {
+  parameterSelectDialogRef.value?.open(
+    boundList.value
+      .map((item) => item.processParamId)
+      .filter((id): id is number => id !== undefined),
+  );
+}
+
+/** 鎺ユ敹閫変腑鐨勫弬鏁帮紝杩藉姞鍒扮粦瀹氬垪琛紙蹇呭~/鎺掑簭璧伴粯璁ゅ�硷級 */
+function onParametersSelected(rows: MesPdParameterApi.Parameter[]) {
+  const existingIds = new Set(
+    boundList.value
+      .map((item) => item.processParamId)
+      .filter((id): id is number => id !== undefined),
+  );
+  const newRows = rows
+    .filter((row) => row.id !== undefined && !existingIds.has(row.id!))
+    .map((row, index) => ({
+      processParamId: row.id,
+      paramCode: row.paramCode,
+      paramName: row.paramName,
+      paramType: row.paramType,
+      unitMeasureName: row.unitMeasureName,
+      required: row.required,
+      sort: boundList.value.length + index + 1,
+      remark: row.remark,
+    }));
+  boundList.value = [...boundList.value, ...newRows];
+}
+
+/** 绉婚櫎缁戝畾 */
+function removeItem(index: number) {
+  boundList.value.splice(index, 1);
+}
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    if (processId.value === undefined) {
+      return;
+    }
+    modalApi.lock();
+    try {
+      await updateProcessParams(
+        processId.value,
+        boundList.value.map((item, index) => ({
+          processParamId: item.processParamId!,
+          required: item.required,
+          sort: item.sort ?? index + 1,
+          remark: item.remark,
+        })),
+      );
+      message.success($t('ui.actionMessage.operationSuccess'));
+      await modalApi.close();
+      emit('success');
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      processId.value = undefined;
+      boundList.value = [];
+      return;
+    }
+    const data = modalApi.getData<{ processId: number; processName?: string }>();
+    processId.value = data.processId;
+    processName.value = data.processName || '';
+    await loadData();
+  },
+});
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-4/5">
+    <div class="mx-4">
+      <div class="mb-2 flex items-center justify-between">
+        <span class="text-sm font-medium">宸茬粦瀹氬伐鑹哄弬鏁�</span>
+        <Button type="primary" size="small" @click="handleSelectParameters">
+          娣诲姞鍙傛暟
+        </Button>
+      </div>
+      <div v-if="boundList.length > 0" class="rounded border border-gray-200">
+        <div
+          class="grid grid-cols-12 gap-2 border-b border-gray-200 bg-gray-50 p-2 text-xs font-medium"
+        >
+          <div class="col-span-3">鍙傛暟鍚嶇О</div>
+          <div class="col-span-2">鍙傛暟缂栫爜</div>
+          <div class="col-span-2">鍙傛暟绫诲瀷</div>
+          <div class="col-span-1">鍗曚綅</div>
+          <div class="col-span-1 text-center">鏄惁蹇呭~</div>
+          <div class="col-span-1">鎺掑簭</div>
+          <div class="col-span-1">澶囨敞</div>
+          <div class="col-span-1 text-center">鎿嶄綔</div>
+        </div>
+        <div
+          v-for="(item, index) in boundList"
+          :key="item.processParamId || index"
+          class="grid grid-cols-12 items-center gap-2 border-b border-gray-100 p-2"
+        >
+          <div class="col-span-3 text-sm">{{ item.paramName }}</div>
+          <div class="col-span-2 text-sm text-gray-500">{{ item.paramCode }}</div>
+          <div class="col-span-2">
+            <DictTag
+              v-if="item.paramType !== undefined"
+              :type="DICT_TYPE.MES_PD_PARAM_TYPE"
+              :value="item.paramType"
+            />
+          </div>
+          <div class="col-span-1 text-sm">{{ item.unitMeasureName }}</div>
+          <div class="col-span-1 flex items-center justify-center">
+            <Switch v-model:checked="item.required" size="small" />
+          </div>
+          <div class="col-span-1">
+            <InputNumber
+              v-model:value="item.sort"
+              :min="1"
+              size="small"
+              class="w-full"
+            />
+          </div>
+          <div class="col-span-1">
+            <Input
+              v-model:value="item.remark"
+              placeholder="澶囨敞"
+              size="small"
+            />
+          </div>
+          <div class="col-span-1 flex items-center justify-center">
+            <Button type="text" danger size="small" @click="removeItem(index)">
+              鍒犻櫎
+            </Button>
+          </div>
+        </div>
+      </div>
+      <div
+        v-else
+        class="rounded border border-dashed border-gray-300 p-4 text-center text-gray-400"
+      >
+        {{ loading ? '鍔犺浇涓��' : '鏆傛湭缁戝畾鍙傛暟' }}
+      </div>
+    </div>
+    <ParameterSelectDialog
+      ref="parameterSelectDialogRef"
+      @selected="onParametersSelected"
+    />
+  </Modal>
+</template>

--
Gitblit v1.9.3