From 31f8c28b502a3f963a2651194112584371448e37 Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期四, 27 八月 2026 16:39:47 +0800
Subject: [PATCH] feat(srm): 供应商准入审核接入协同办公(BPM)

---
 src/views/srm/supplierApply/modules/form.vue                 |   24 ++
 src/views/srm/supplierApply/detail/index.vue                 |   96 ++++++++++++
 src/views/srm/enums.ts                                       |   19 +-
 src/views/srm/supplierApply/modules/process-select-modal.vue |   74 +++++++++
 src/api/srm/supplierApply/index.ts                           |   53 +++---
 src/views/srm/supplierApply/index.vue                        |  188 +++++++----------------
 src/views/srm/supplierApply/data.ts                          |    3 
 7 files changed, 287 insertions(+), 170 deletions(-)

diff --git a/src/api/srm/supplierApply/index.ts b/src/api/srm/supplierApply/index.ts
index 35a48fa..ccfcf06 100644
--- a/src/api/srm/supplierApply/index.ts
+++ b/src/api/srm/supplierApply/index.ts
@@ -8,27 +8,32 @@
     applyNo?: string;
     supplierId?: number;
     supplierName: string;
-    contactName?: string;
-    contactMobile?: string;
-    contactEmail?: string;
+    supplierType?: string;
+    companyNature?: string;
+    registeredCapital?: number;
+    businessLicenseNo?: string;
+    taxNo?: string;
     address?: string;
     bankName?: string;
     bankAccount?: string;
-    taxNo?: string;
-    businessLicense?: string;
-    taxCertificate?: string;
-    orgCodeCertificate?: string;
+    contactName?: string;
+    contactMobile?: string;
+    contactEmail?: string;
+    applyUserName?: string;
+    applyTime?: string;
     applyStatus?: number;
     applyStatusLabel?: string;
     auditComment?: string;
+    processInstanceId?: string;
     remark?: string;
     createTime?: string;
   }
 
-  export interface ReviewVO {
-    id: number;
-    passed: boolean;
-    comment?: string;
+  /** 鍗忓悓鍔炲叕瀹℃壒娴佺▼瀹氫箟 */
+  export interface ProcessDefinition {
+    id: string;
+    key: string;
+    name: string;
   }
 }
 
@@ -57,22 +62,16 @@
   return requestClient.delete(`/srm/supplier-apply/delete?id=${id}`);
 }
 
-/** 鎻愪氦瀹℃牳 */
-export function submitApply(id: number) {
-  return requestClient.put(`/srm/supplier-apply/submit?id=${id}`);
+/** 鑾峰彇瀹℃壒娴佺▼鍒楄〃锛堝崗鍚屽姙鍏凡鍙戝竷娴佺▼锛� */
+export function getApproveProcessList() {
+  return requestClient.get<SrmSupplierApplyApi.ProcessDefinition[]>(
+    '/srm/supplier-apply/approve-process-list',
+  );
 }
 
-/** 閲囪喘瀹℃牳 */
-export function reviewPurchase(data: SrmSupplierApplyApi.ReviewVO) {
-  return requestClient.put('/srm/supplier-apply/review-purchase', data);
-}
-
-/** 璐ㄩ噺瀹℃牳 */
-export function reviewQuality(data: SrmSupplierApplyApi.ReviewVO) {
-  return requestClient.put('/srm/supplier-apply/review-quality', data);
-}
-
-/** 璐㈠姟瀹℃牳 */
-export function reviewFinance(data: SrmSupplierApplyApi.ReviewVO) {
-  return requestClient.put('/srm/supplier-apply/review-finance', data);
+/** 鎻愪氦鍑嗗叆鐢宠瀹℃壒锛堝惎鍔ㄥ崗鍚屽姙鍏祦绋嬶級 */
+export function submitApply(id: number, processDefinitionKey: string) {
+  return requestClient.put('/srm/supplier-apply/submit', null, {
+    params: { id, processDefinitionKey },
+  });
 }
diff --git a/src/views/srm/enums.ts b/src/views/srm/enums.ts
index 6077634..3c2cb0a 100644
--- a/src/views/srm/enums.ts
+++ b/src/views/srm/enums.ts
@@ -62,36 +62,35 @@
   labels: { '1': '鐢熶骇鍨�', '2': '璐告槗鍨�', '3': '鏈嶅姟鍨�', '4': '鍏朵粬' },
 };
 
-// ========== 鍑嗗叆鐢宠 鈥� 瀵归綈 SupplierApplyStatusEnum (0/10/20/30/40/50) ==========
+// ========== 鍑嗗叆鐢宠 鈥� 瀵归綈 SupplierApplyStatusEnum (0/10/30/40/50)
+// 鎺ュ叆鍗忓悓鍔炲叕鍚庣畝鍖栦负 5 鎬侊細寰呮彁浜� / 瀹℃壒涓� / 宸插彇娑� / 宸查�氳繃 / 宸查┏鍥�
+// 鍘熷緟閲囪喘瀹℃牳(10)/寰呰川閲忓鏍�(20)/寰呰储鍔″鏍�(30)宸插簾寮� ==========
 
 export const APPLY_STATUS = {
   DRAFT: 0,
-  PENDING_PURCHASE: 10,
-  PENDING_QUALITY: 20,
-  PENDING_FINANCE: 30,
+  PROCESS: 10,
+  CANCEL: 30,
   APPROVED: 40,
   REJECTED: 50,
 } as const;
 
 export const APPLY_STATUS_OPTIONS: EnumOption[] = [
   { label: '寰呮彁浜�', value: 0 },
-  { label: '寰呴噰璐鏍�', value: 10 },
-  { label: '寰呰川閲忓鏍�', value: 20 },
-  { label: '寰呰储鍔″鏍�', value: 30 },
+  { label: '瀹℃壒涓�', value: 10 },
+  { label: '宸插彇娑�', value: 30 },
   { label: '宸查�氳繃', value: 40 },
-  { label: '宸叉嫆缁�', value: 50 },
+  { label: '宸查┏鍥�', value: 50 },
 ];
 
 export const APPLY_STATUS_CELLTAG: { data: CellTagItem[]; labels: Record<number, string> } = {
   data: [
     { value: 0, color: 'gray' },
     { value: 10, color: 'blue' },
-    { value: 20, color: 'orange' },
     { value: 30, color: 'purple' },
     { value: 40, color: 'green' },
     { value: 50, color: 'red' },
   ],
-  labels: { 0: '寰呮彁浜�', 10: '寰呴噰璐鏍�', 20: '寰呰川閲忓鏍�', 30: '寰呰储鍔″鏍�', 40: '宸查�氳繃', 50: '宸叉嫆缁�' },
+  labels: { 0: '寰呮彁浜�', 10: '瀹℃壒涓�', 30: '宸插彇娑�', 40: '宸查�氳繃', 50: '宸查┏鍥�' },
 };
 
 // ========== 璧勮川璇佷功 鈥� 瀵归綈 CertificateTypeEnum (1=钀ヤ笟鎵х収 2=璁よ瘉璇佷功 3=妫�娴嬫姤鍛� 4=鍏朵粬) ==========
diff --git a/src/views/srm/supplierApply/data.ts b/src/views/srm/supplierApply/data.ts
index 6b99c4c..1d681b0 100644
--- a/src/views/srm/supplierApply/data.ts
+++ b/src/views/srm/supplierApply/data.ts
@@ -10,6 +10,9 @@
 
 import { APPLY_STATUS_CELLTAG, APPLY_STATUS_OPTIONS } from '../enums';
 
+/** 琛ㄥ崟绫诲瀷锛氭柊澧� / 缂栬緫 / 璇︽儏锛堝彧璇伙級 */
+export type FormType = 'create' | 'edit' | 'detail';
+
 /** 鏂板/淇敼鐨勮〃鍗� */
 export function useFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
   return [
diff --git a/src/views/srm/supplierApply/detail/index.vue b/src/views/srm/supplierApply/detail/index.vue
new file mode 100644
index 0000000..bdccd5c
--- /dev/null
+++ b/src/views/srm/supplierApply/detail/index.vue
@@ -0,0 +1,96 @@
+<script lang="ts" setup>
+import type { SrmSupplierApplyApi } from '#/api/srm/supplierApply';
+import type { DescriptionItemSchema } from '#/components/description';
+
+import { h, onMounted, ref } from 'vue';
+
+import { ContentWrap } from '@vben/common-ui';
+import { formatDateTime } from '@vben/utils';
+
+import { Spin, Tag } from 'ant-design-vue';
+
+import { getApply } from '#/api/srm/supplierApply';
+import { useDescription } from '#/components/description';
+
+import { APPLY_STATUS_CELLTAG } from '../../enums';
+
+defineOptions({ name: 'SrmSupplierApplyDetail' });
+
+const props = defineProps<{ id: string }>();
+
+const loading = ref(false);
+const formData = ref<SrmSupplierApplyApi.ApplyVO>();
+
+/** 鏂囨湰瀛楁绌哄�煎崰浣� */
+function textRender(val: unknown) {
+  return val === 0 || val ? String(val) : '-';
+}
+
+/** 鐢宠鐘舵�佹爣绛炬覆鏌� */
+function statusRender(val: unknown) {
+  const num = Number(val);
+  const item = APPLY_STATUS_CELLTAG.data.find((i) => Number(i.value) === num);
+  const label = APPLY_STATUS_CELLTAG.labels[num] || '-';
+  return h(Tag, { color: item?.color }, { default: () => label });
+}
+
+const [Descriptions] = useDescription({
+  bordered: true,
+  column: 2,
+  schema: [
+    { label: '鐢宠缂栧彿', field: 'applyNo', render: textRender },
+    { label: '鐢宠鐘舵��', field: 'applyStatus', render: statusRender },
+    { label: '鐢宠浜�', field: 'applyUserName', render: textRender },
+    {
+      label: '鐢宠鏃堕棿',
+      field: 'applyTime',
+      render: (val) => formatDateTime(val) as string,
+    },
+    { label: '渚涘簲鍟嗗悕绉�', field: 'supplierName', render: textRender },
+    { label: '渚涘簲鍟嗙被鍨�', field: 'supplierType', render: textRender },
+    { label: '浼佷笟鎬ц川', field: 'companyNature', render: textRender },
+    {
+      label: '娉ㄥ唽璧勯噾(涓囧厓)',
+      field: 'registeredCapital',
+      render: textRender,
+    },
+    { label: '钀ヤ笟鎵х収缂栧彿', field: 'businessLicenseNo', render: textRender },
+    { label: '绋庡彿', field: 'taxNo', render: textRender },
+    { label: '寮�鎴烽摱琛�', field: 'bankName', render: textRender },
+    { label: '閾惰璐﹀彿', field: 'bankAccount', render: textRender },
+    { label: '鑱旂郴浜�', field: 'contactName', render: textRender },
+    { label: '鑱旂郴鐢佃瘽', field: 'contactMobile', render: textRender },
+    { label: '閭', field: 'contactEmail', render: textRender },
+    { label: '鍏徃鍦板潃', field: 'address', render: textRender },
+    {
+      label: '澶囨敞',
+      field: 'remark',
+      render: textRender,
+      span: 2,
+    },
+  ] as DescriptionItemSchema[],
+});
+
+onMounted(async () => {
+  try {
+    loading.value = true;
+    formData.value = await getApply(Number(props.id));
+  } finally {
+    loading.value = false;
+  }
+});
+</script>
+
+<template>
+  <ContentWrap class="m-2">
+    <Spin :spinning="loading" tip="鍔犺浇涓�...">
+      <div class="rounded-lg border border-border bg-background p-4">
+        <div class="mb-4 flex items-center gap-2">
+          <span class="inline-block h-4 w-1 rounded bg-primary" />
+          <span class="text-base font-semibold">渚涘簲鍟嗗噯鍏ョ敵璇�</span>
+        </div>
+        <Descriptions :data="formData" />
+      </div>
+    </Spin>
+  </ContentWrap>
+</template>
diff --git a/src/views/srm/supplierApply/index.vue b/src/views/srm/supplierApply/index.vue
index 47b8a8f..34663a8 100644
--- a/src/views/srm/supplierApply/index.vue
+++ b/src/views/srm/supplierApply/index.vue
@@ -2,118 +2,38 @@
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { SrmSupplierApplyApi } from '#/api/srm/supplierApply';
 
-import { computed, ref } from 'vue';
+import { useRouter } from 'vue-router';
 
 import { Page, useVbenModal } from '@vben/common-ui';
 
 import { message } from 'ant-design-vue';
 
-import { useVbenForm } from '#/adapter/form';
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
 import {
   deleteApply,
+  getApproveProcessList,
   getApplyPage,
-  submitApply,
-  reviewPurchase,
-  reviewQuality,
-  reviewFinance,
 } from '#/api/srm/supplierApply';
 import { $t } from '#/locales';
 
 import { useGridColumns, useGridFormSchema } from './data';
 import ApplyForm from './modules/form.vue';
+import ProcessSelectModal from './modules/process-select-modal.vue';
 import { APPLY_STATUS } from '../enums';
 
 defineOptions({ name: 'SrmSupplierApply' });
 
-// ---------- reviewModal ----------
-const reviewFormData = ref({
-  applyId: 0,
-  reviewType: '' as 'purchase' | 'quality' | 'finance',
-  passed: true,
-  comment: '',
-});
-const reviewTitle = computed(() => {
-  const labels: Record<string, string> = {
-    purchase: '閲囪喘瀹℃牳',
-    quality: '璐ㄩ噺瀹℃牳',
-    finance: '璐㈠姟瀹℃牳',
-  };
-  return labels[reviewFormData.value.reviewType] || '瀹℃牳';
+const router = useRouter();
+
+const [FormModal, formModalApi] = useVbenModal({
+  connectedComponent: ApplyForm,
+  destroyOnClose: true,
 });
 
-const [ReviewForm, reviewFormApi] = useVbenForm({
-  commonConfig: {
-    componentProps: { class: 'w-full' },
-    labelWidth: 100,
-  },
-  wrapperClass: 'grid-cols-1',
-  layout: 'horizontal',
-  schema: [
-    {
-      fieldName: 'passed',
-      label: '瀹℃牳缁撴灉',
-      component: 'RadioGroup',
-      rules: 'required',
-      componentProps: {
-        options: [
-          { label: '閫氳繃', value: true },
-          { label: '鎷掔粷', value: false },
-        ],
-        buttonStyle: 'solid',
-        optionType: 'button',
-      },
-    },
-    {
-      fieldName: 'comment',
-      label: '瀹℃牳鎰忚',
-      component: 'Textarea',
-      componentProps: { placeholder: '璇疯緭鍏ュ鏍告剰瑙�', rows: 3 },
-    },
-  ],
-  showDefaultActions: false,
+const [ProcessModal, processModalApi] = useVbenModal({
+  connectedComponent: ProcessSelectModal,
+  destroyOnClose: true,
 });
-
-const [ReviewModal, reviewModalApi] = useVbenModal({
-  async onConfirm() {
-    const { valid } = await reviewFormApi.validate();
-    if (!valid) {
-      return;
-    }
-    reviewModalApi.lock();
-    const data = (await reviewFormApi.getValues()) as { passed: boolean; comment?: string };
-    try {
-      const payload: SrmSupplierApplyApi.ReviewVO = {
-        id: reviewFormData.value.applyId,
-        passed: data.passed,
-        comment: data.comment,
-      };
-      if (reviewFormData.value.reviewType === 'purchase') {
-        await reviewPurchase(payload);
-      } else if (reviewFormData.value.reviewType === 'quality') {
-        await reviewQuality(payload);
-      } else if (reviewFormData.value.reviewType === 'finance') {
-        await reviewFinance(payload);
-      }
-      await reviewModalApi.close();
-      handleRefresh();
-      message.success($t('ui.actionMessage.operationSuccess'));
-    } finally {
-      reviewModalApi.unlock();
-    }
-  },
-});
-
-function openReviewModal(row: SrmSupplierApplyApi.ApplyVO, reviewType: 'purchase' | 'quality' | 'finance') {
-  reviewFormData.value = {
-    applyId: row.id!,
-    reviewType,
-    passed: true,
-    comment: '',
-  };
-  reviewFormApi.resetForm();
-  reviewModalApi.open();
-}
 
 // ---------- grid ----------
 function handleRefresh() {
@@ -121,11 +41,16 @@
 }
 
 function handleCreate() {
-  formModalApi.setData(null).open();
+  formModalApi.setData({ formType: 'create' }).open();
 }
 
 function handleEdit(row: SrmSupplierApplyApi.ApplyVO) {
-  formModalApi.setData(row).open();
+  formModalApi.setData({ formType: 'edit', id: row.id }).open();
+}
+
+/** 鏌ョ湅璇︽儏锛堝彧璇伙級 */
+function handleDetail(row: SrmSupplierApplyApi.ApplyVO) {
+  formModalApi.setData({ formType: 'detail', id: row.id }).open();
 }
 
 async function handleDelete(row: SrmSupplierApplyApi.ApplyVO) {
@@ -142,21 +67,38 @@
   }
 }
 
+/** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栧崗鍚屽姙鍏鎵规祦绋嬪垪琛� */
 async function handleSubmit(row: SrmSupplierApplyApi.ApplyVO) {
-  const hideLoading = message.loading({ content: '鎻愪氦涓�...', duration: 0 });
+  const hideLoading = message.loading({
+    content: '鑾峰彇瀹℃壒娴佺▼...',
+    duration: 0,
+  });
   try {
-    await submitApply(row.id!);
-    message.success($t('ui.actionMessage.operationSuccess'));
-    handleRefresh();
-  } finally {
+    const processList = await getApproveProcessList();
+    hideLoading();
+    if (!processList || processList.length === 0) {
+      message.warning('鏆傛棤鍙敤瀹℃壒娴佺▼锛岃鍏堝湪鍗忓悓鍔炲叕閰嶇疆瀹℃壒娴佺▼');
+      return;
+    }
+    // 鎵撳紑娴佺▼閫夋嫨寮圭獥
+    processModalApi.setData({ id: row.id, processList }).open();
+  } catch {
     hideLoading();
   }
 }
 
-const [FormModal, formModalApi] = useVbenModal({
-  connectedComponent: ApplyForm,
-  destroyOnClose: true,
-});
+/** 鏌ョ湅瀹℃壒杩涘害锛堣烦杞崗鍚屽姙鍏祦绋嬪疄渚嬭鎯咃紝鍙鏌ョ湅锛� */
+function handleViewProcess(row: SrmSupplierApplyApi.ApplyVO) {
+  if (!row.processInstanceId) {
+    message.warning('璇ョ敵璇峰皻鏈彁浜ゅ鎵�');
+    return;
+  }
+  // viewOnly=1锛氶殣钘忓鎵规搷浣滄寜閽紝浠呮煡鐪嬪鎵硅繘搴�
+  router.push({
+    name: 'BpmProcessInstanceDetail',
+    query: { id: row.processInstanceId, viewOnly: '1' },
+  });
+}
 
 const [Grid, gridApi] = useVbenVxeGrid({
   formOptions: {
@@ -186,9 +128,7 @@
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
-    <ReviewModal :title="reviewTitle" class="w-1/3">
-      <ReviewForm class="mx-4" />
-    </ReviewModal>
+    <ProcessModal @success="handleRefresh" />
     <Grid table-title="鍑嗗叆鐢宠鍒楄〃">
       <template #toolbar-tools>
         <TableAction
@@ -207,43 +147,34 @@
         <TableAction
           :actions="[
             {
-              label: $t('common.edit'),
+              label: $t('common.detail'),
               type: 'link',
-              icon: ACTION_ICON.EDIT,
-              auth: ['srm:supplier-apply:update'],
-              onClick: handleEdit.bind(null, row),
+              icon: ACTION_ICON.VIEW,
+              auth: ['srm:supplier-apply:query'],
+              onClick: handleDetail.bind(null, row),
             },
             {
               label: '鎻愪氦',
               type: 'link',
-              icon: ACTION_ICON.CHECK,
+              icon: ACTION_ICON.AUDIT,
               auth: ['srm:supplier-apply:update'],
-              ifShow: row.applyStatus === APPLY_STATUS.DRAFT,
+              ifShow: () => row.applyStatus === APPLY_STATUS.DRAFT || row.applyStatus === APPLY_STATUS.REJECTED,
               onClick: handleSubmit.bind(null, row),
             },
             {
-              label: '閲囪喘瀹℃牳',
+              label: '鏌ョ湅瀹℃壒',
               type: 'link',
-              icon: ACTION_ICON.CHECK,
-              auth: ['srm:supplier-apply:update'],
-              ifShow: row.applyStatus === APPLY_STATUS.PENDING_PURCHASE,
-              onClick: openReviewModal.bind(null, row, 'purchase'),
+              icon: ACTION_ICON.VIEW,
+              ifShow: () => !!row.processInstanceId,
+              onClick: handleViewProcess.bind(null, row),
             },
             {
-              label: '璐ㄩ噺瀹℃牳',
+              label: $t('common.edit'),
               type: 'link',
-              icon: ACTION_ICON.CHECK,
+              icon: ACTION_ICON.EDIT,
               auth: ['srm:supplier-apply:update'],
-              ifShow: row.applyStatus === APPLY_STATUS.PENDING_QUALITY,
-              onClick: openReviewModal.bind(null, row, 'quality'),
-            },
-            {
-              label: '璐㈠姟瀹℃牳',
-              type: 'link',
-              icon: ACTION_ICON.CHECK,
-              auth: ['srm:supplier-apply:update'],
-              ifShow: row.applyStatus === APPLY_STATUS.PENDING_FINANCE,
-              onClick: openReviewModal.bind(null, row, 'finance'),
+              ifShow: () => row.applyStatus === APPLY_STATUS.DRAFT || row.applyStatus === APPLY_STATUS.REJECTED,
+              onClick: handleEdit.bind(null, row),
             },
             {
               label: $t('common.delete'),
@@ -251,6 +182,7 @@
               danger: true,
               icon: ACTION_ICON.DELETE,
               auth: ['srm:supplier-apply:delete'],
+              ifShow: () => row.applyStatus === APPLY_STATUS.DRAFT || row.applyStatus === APPLY_STATUS.REJECTED,
               popConfirm: {
                 title: $t('ui.actionMessage.deleteConfirm', [row.supplierName]),
                 confirm: handleDelete.bind(null, row),
diff --git a/src/views/srm/supplierApply/modules/form.vue b/src/views/srm/supplierApply/modules/form.vue
index 22305a4..cf382d4 100644
--- a/src/views/srm/supplierApply/modules/form.vue
+++ b/src/views/srm/supplierApply/modules/form.vue
@@ -1,5 +1,6 @@
 <script lang="ts" setup>
 import type { SrmSupplierApplyApi } from '#/api/srm/supplierApply';
+import type { FormType } from '../data';
 
 import { computed, ref } from 'vue';
 
@@ -19,10 +20,16 @@
 
 const emit = defineEmits(['success']);
 const formData = ref<SrmSupplierApplyApi.ApplyVO>();
+const formType = ref<FormType>('create');
+
 const getTitle = computed(() => {
-  return formData.value?.id
-    ? $t('ui.actionTitle.edit', ['鍑嗗叆鐢宠'])
-    : $t('ui.actionTitle.create', ['鍑嗗叆鐢宠']);
+  if (formType.value === 'create') {
+    return $t('ui.actionTitle.create', ['鍑嗗叆鐢宠']);
+  } else if (formType.value === 'edit') {
+    return $t('ui.actionTitle.edit', ['鍑嗗叆鐢宠']);
+  } else {
+    return '鍑嗗叆鐢宠璇︽儏';
+  }
 });
 
 const [Form, formApi] = useVbenForm({
@@ -60,7 +67,10 @@
       formData.value = undefined;
       return;
     }
-    const data = modalApi.getData<SrmSupplierApplyApi.ApplyVO>();
+    const data = modalApi.getData<{ formType: FormType; id?: number }>();
+    formType.value = data?.formType ?? 'create';
+    formApi.setDisabled(formType.value === 'detail');
+    formApi.updateSchema(useFormSchema(formApi));
     if (!data || !data.id) {
       return;
     }
@@ -78,7 +88,11 @@
 </script>
 
 <template>
-  <Modal :title="getTitle" class="w-1/2">
+  <Modal
+    :title="getTitle"
+    class="w-1/2"
+    :show-confirm-button="formType !== 'detail'"
+  >
     <Form class="mx-4" />
   </Modal>
 </template>
diff --git a/src/views/srm/supplierApply/modules/process-select-modal.vue b/src/views/srm/supplierApply/modules/process-select-modal.vue
new file mode 100644
index 0000000..e85cf5f
--- /dev/null
+++ b/src/views/srm/supplierApply/modules/process-select-modal.vue
@@ -0,0 +1,74 @@
+<script lang="ts" setup>
+import type { SrmSupplierApplyApi } from '#/api/srm/supplierApply';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { message, Select } from 'ant-design-vue';
+
+import { submitApply } from '#/api/srm/supplierApply';
+
+const emit = defineEmits(['success']);
+
+const applyId = ref<number>();
+const processList = ref<SrmSupplierApplyApi.ProcessDefinition[]>([]);
+const selectedProcessKey = ref<string>('');
+
+const processOptions = computed(() => {
+  return processList.value.map((p) => ({
+    label: p.name,
+    value: p.key,
+  }));
+});
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    if (!selectedProcessKey.value) {
+      message.warning('璇烽�夋嫨瀹℃壒娴佺▼');
+      return;
+    }
+    modalApi.lock();
+    try {
+      await submitApply(applyId.value!, selectedProcessKey.value);
+      await modalApi.close();
+      emit('success');
+      message.success('鎻愪氦鎴愬姛');
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      applyId.value = undefined;
+      selectedProcessKey.value = '';
+      processList.value = [];
+      return;
+    }
+    const data = modalApi.getData<{
+      id: number;
+      processList: SrmSupplierApplyApi.ProcessDefinition[];
+    }>();
+    applyId.value = data?.id;
+    processList.value = data?.processList || [];
+    // 榛樿閫夋嫨绗竴涓祦绋�
+    if (processList.value.length > 0) {
+      selectedProcessKey.value = processList.value[0]!.key;
+    }
+  },
+});
+</script>
+
+<template>
+  <Modal title="閫夋嫨瀹℃壒娴佺▼" class="w-1/3">
+    <div class="mb-4">
+      <span class="mr-2">瀹℃壒娴佺▼锛�</span>
+      <Select
+        v-model:value="selectedProcessKey"
+        :options="processOptions"
+        style="width: 220px"
+        placeholder="璇烽�夋嫨瀹℃壒娴佺▼"
+      />
+    </div>
+  </Modal>
+</template>

--
Gitblit v1.9.3