From 91eff4781fa4142d1f62d30a0fa1de5cae424eaf Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期三, 16 九月 2026 18:23:12 +0800
Subject: [PATCH] refactor: 单据审核改为审批配置状态机(前端统一审核弹窗)

---
 src/views/mes/pd/tech-confirm/index.vue                          |  124 +
 src/api/mes/wm/itemreceipt/index.ts                              |   32 
 src/views/mes/pd/project/index.vue                               |  100 
 src/views/wls/miscreceipt/modules/audit-modal.vue                |   95 +
 src/views/crm/invoice/index.vue                                  |   87 
 src/views/wls/stocktaking/task/modules/audit-modal.vue           |   99 +
 src/views/mes/pd/project/detail/index.vue                        |  102 
 src/views/wls/equipmentreceipt/modules/audit-modal.vue           |   99 +
 src/views/wls/stocktaking/task/index.vue                         |  130 +
 src/api/mes/pd/project/index.ts                                  |   50 
 src/views/crm/cancellation/modules/audit-modal.vue               |   99 +
 src/views/mes/pd/project/modules/audit-modal.vue                 |   96 +
 src/api/crm/cancellation/index.ts                                |   40 
 src/views/wls/itemreceipt/modules/audit-modal.vue                |   95 +
 src/api/hrm/resignation/index.ts                                 |   37 
 src/views/mes/pd/archive/modules/detail.vue                      |   63 
 src/views/crm/contract/index.vue                                 |  134 
 src/api/erp/finance/payment/index.ts                             |   41 
 src/views/hrm/performance-appraise/index.vue                     |  101 
 src/api/mes/pd/tech-confirm/index.ts                             |   70 
 src/api/hrm/attendance/exception/index.ts                        |   50 
 src/views/wls/miscissue/index.vue                                |  106 
 src/api/crm/refund/index.ts                                      |   29 
 src/views/wls/itemreceipt/index.vue                              |   64 
 src/api/hrm/performance-appraise/index.ts                        |   47 
 src/views/hrm/leave/index.vue                                    |   93 +
 src/views/erp/finance/payment/index.vue                          |  113 +
 src/views/erp/purchase/request/modules/audit-modal.vue           |   99 +
 src/api/mes/wm/miscreceipt/index.ts                              |   48 
 src/views/hrm/attendance/exception/modules/audit-modal.vue       |  102 +
 src/api/mes/wm/returnvendor/index.ts                             |   48 
 src/views/mes/pro/task/components/gantt-chart.vue                |   24 
 src/views/crm/invoice/modules/audit-modal.vue                    |   95 +
 src/api/mes/wm/equipmentreceipt/index.ts                         |   48 
 src/views/hrm/resignation/index.vue                              |   93 +
 src/views/mes/pd/tech-confirm/modules/audit-modal.vue            |   96 +
 src/views/erp/purchase/request/index.vue                         |  108 +
 src/views/hrm/transfer/modules/audit-modal.vue                   |   99 +
 src/views/wls/returnvendor/index.vue                             |  112 
 src/api/mes/wm/miscissue/index.ts                                |   48 
 src/views/mes/pd/tech-confirm/modules/form.vue                   |   29 
 src/api/erp/purchase/request/index.ts                            |   46 
 src/api/hrm/leave/index.ts                                       |   29 
 src/views/hrm/resignation/modules/audit-modal.vue                |   98 +
 src/views/hrm/performance-appraise/modules/audit-modal.vue       |   93 
 src/api/mes/wm/stocktaking/task/index.ts                         |   48 
 src/api/hrm/transfer/index.ts                                    |   29 
 src/views/hrm/transfer/index.vue                                 |   93 +
 src/api/crm/contract/index.ts                                    |   51 
 src/views/crm/contract/modules/audit-modal.vue                   |   95 +
 src/packages/constants/src/biz-mes-enum.ts                       |   15 
 src/views/wls/returnvendor/modules/audit-modal.vue               |   95 +
 src/views/hrm/leave/modules/audit-modal.vue                      |   98 +
 src/views/wls/miscissue/modules/audit-modal.vue                  |   95 +
 src/views/crm/refund/modules/audit-modal.vue                     |   95 +
 /dev/null                                                        |   83 -
 src/views/wls/miscreceipt/index.vue                              |  106 
 src/views/erp/finance/payment/modules/audit-modal.vue            |   99 +
 src/views/hrm/attendance/exception/index.vue                     |  134 
 src/api/crm/invoice/index.ts                                     |   31 
 src/views/mes/pd/tech-confirm/modules/customer-confirm-modal.vue |   74 
 src/views/wls/equipmentreceipt/index.vue                         |  111 
 src/views/mes/pd/tech-confirm/data.ts                            |   83 
 63 files changed, 4,059 insertions(+), 887 deletions(-)

diff --git a/src/api/crm/cancellation/index.ts b/src/api/crm/cancellation/index.ts
index 684850a..3b58856 100644
--- a/src/api/crm/cancellation/index.ts
+++ b/src/api/crm/cancellation/index.ts
@@ -19,7 +19,11 @@
     applyUserName?: string;
     applyTime?: string;
     auditStatus?: number;
-    processInstanceId?: string;
+    // 浠ヤ笅涓哄鏍告淳鐢熷瓧娈碉紝鐢卞悗绔湪瀹℃牳鏃跺啓鍏ワ紝鍓嶇鍙灞曠ず
+    reviewerId?: number; // 瀹℃牳浜篒D锛堝疄闄呮墽琛屽鏍哥殑鐢ㄦ埛锛�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: string; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堝鏍搁�氳繃鏃剁殑鎰忚 / 瀹℃牳涓嶉�氳繃鏃剁殑鍘熷洜锛�
     remark?: string;
     createTime?: string;
   }
@@ -58,12 +62,34 @@
   return requestClient.put('/crm/cancellation/update', data);
 }
 
-/** 瀹℃壒閿�鎴风敵璇� */
-export function auditCancellation(data: {
-  id: number;
-  auditStatus: number;
-}) {
-  return requestClient.put('/crm/cancellation/audit', data);
+/**
+ * 鎻愪氦瀹℃壒锛堣崏绋� / 瀹℃牳涓嶉�氳繃 鈫� 瀹℃壒涓級
+ * 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾銆�
+ */
+export function submitCancellation(id: number) {
+  return requestClient.put('/crm/cancellation/submit', null, { params: { id } });
+}
+
+/** 鑾峰緱閿�鎴风敵璇风殑瀹℃壒浜虹紪鍙烽泦鍚堬紝渚涘垪琛ㄩ〉鍒ゆ柇鏄惁灞曠ず瀹℃牳鎸夐挳 */
+export function getCancellationApproverIds() {
+  return requestClient.get<number[]>('/crm/cancellation/approver-ids');
+}
+
+/**
+ * 瀹℃牳閿�鎴风敵璇凤紙瀹℃壒涓� 鈫� 瀹℃牳閫氳繃 / 瀹℃牳涓嶉�氳繃锛�
+ *
+ * 浠呭鎵归厤缃腑鐨勫鎵逛汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditCancellation(
+  id: number,
+  pass: boolean,
+  reviewRemark?: string,
+) {
+  return requestClient.put('/crm/cancellation/audit', {
+    id,
+    pass,
+    reviewRemark,
+  });
 }
 
 /** 鍒犻櫎閿�鎴风敵璇� */
diff --git a/src/api/crm/contract/index.ts b/src/api/crm/contract/index.ts
index 35b4e05..d2af0de 100644
--- a/src/api/crm/contract/index.ts
+++ b/src/api/crm/contract/index.ts
@@ -18,8 +18,12 @@
     ownerUserId: number;
     ownerUserName?: string;
     ownerUserDeptName?: string;
-    processInstanceId: number;
     auditStatus: number;
+    // 浠ヤ笅涓哄鏍告淳鐢熷瓧娈碉紝鐢卞悗绔湪瀹℃牳鏃跺啓鍏ワ紝鍓嶇鍙灞曠ず
+    reviewerId?: number; // 瀹℃牳浜篒D锛堝疄闄呮墽琛屽鏍哥殑鐢ㄦ埛锛�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: Date; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堝鏍搁�氳繃鏃剁殑鎰忚 / 瀹℃牳涓嶉�氳繃鏃剁殑鍘熷洜锛�
     orderDate: Date;
     startTime: Date;
     endTime: Date;
@@ -70,11 +74,6 @@
     totalPrice: number;
   }
 
-  /** 瀹℃壒娴佺▼ */
-  export interface ApproveProcess {
-    key: string;
-    name: string;
-  }
 }
 
 /** 鏌ヨ鍚堝悓鍒楄〃 */
@@ -135,18 +134,42 @@
   return requestClient.download('/crm/contract/export-excel', { params });
 }
 
-/** 鎻愪氦瀹℃牳 */
-export function submitContract(id: number, processDefinitionKey: string) {
+/**
+ * 鎻愪氦鍚堝悓瀹℃壒锛堟湭鎻愪氦 / 瀹℃牳涓嶉�氳繃 鈫� 瀹℃壒涓級
+ *
+ * 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶆寜涓氬姟绫诲瀷缁熶竴閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾锛�
+ * 鏈惎鐢ㄥ鎵规垨鏈厤缃湁鏁堝鎵逛汉鏃跺悗绔細鎶涘嚭涓氬姟寮傚父銆�
+ */
+export function submitContract(id: number) {
   return requestClient.put('/crm/contract/submit', null, {
-    params: { id, processDefinitionKey },
+    params: { id },
   });
 }
 
-/** 鑾峰彇鍚堝悓瀹℃壒娴佺▼鍒楄〃 */
-export function getContractApproveProcessList() {
-  return requestClient.get<CrmContractApi.ApproveProcess[]>(
-    '/crm/contract/approve-process-list',
-  );
+/**
+ * 鑾峰緱鍚堝悓鐨勫鎵逛汉缂栧彿闆嗗悎
+ *
+ * 渚涘垪琛ㄩ〉鍒ゆ柇褰撳墠鐧诲綍鐢ㄦ埛鏄惁搴斿睍绀恒�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽紱鏈厤缃椂杩斿洖绌烘暟缁勩��
+ */
+export function getContractApproverIds() {
+  return requestClient.get<number[]>('/crm/contract/approver-ids');
+}
+
+/**
+ * 瀹℃牳鍚堝悓锛堝鎵逛腑 鈫� 瀹℃牳閫氳繃 / 瀹℃牳涓嶉�氳繃锛�
+ *
+ * 浠呭鎵归厤缃腑鐨勫鎵逛汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditContract(
+  id: number,
+  pass: boolean,
+  reviewRemark?: string,
+) {
+  return requestClient.put('/crm/contract/audit', {
+    id,
+    pass,
+    reviewRemark,
+  });
 }
 
 /** 鍚堝悓杞Щ */
diff --git a/src/api/crm/invoice/index.ts b/src/api/crm/invoice/index.ts
index 121f6ec..6f08289 100644
--- a/src/api/crm/invoice/index.ts
+++ b/src/api/crm/invoice/index.ts
@@ -31,11 +31,15 @@
     price?: number;
     invoiceTime?: string;
     auditStatus?: number;
+    // 浠ヤ笅涓哄鏍告淳鐢熷瓧娈碉紝鐢卞悗绔湪瀹℃牳鏃跺啓鍏ワ紝鍓嶇鍙灞曠ず
+    reviewerId?: number; // 瀹℃牳浜篒D锛堝疄闄呮墽琛屽鏍哥殑鐢ㄦ埛锛�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: string; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堝鏍搁�氳繃鏃剁殑鎰忚 / 瀹℃牳涓嶉�氳繃鏃剁殑鍘熷洜锛�
     remark?: string;
     ownerUserId?: number;
     ownerUserName?: string;
     ownerUserDeptName?: string;
-    processInstanceId?: string;
     attachmentList?: Attachment[];
     blobIds?: number[];
     hasAttachment?: boolean;
@@ -104,9 +108,30 @@
   return requestClient.download('/crm/invoice/export-excel', { params });
 }
 
-/** 鎻愪氦瀹℃牳 */
+/**
+ * 鎻愪氦瀹℃壒锛堣崏绋� / 瀹℃牳涓嶉�氳繃 鈫� 瀹℃壒涓級
+ * 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾銆�
+ */
 export function submitInvoice(id: number) {
-  return requestClient.put(`/crm/invoice/submit?id=${id}`);
+  return requestClient.put('/crm/invoice/submit', null, { params: { id } });
+}
+
+/** 鑾峰緱寮�绁ㄧ殑瀹℃壒浜虹紪鍙烽泦鍚堬紝渚涘垪琛ㄩ〉鍒ゆ柇鏄惁灞曠ず瀹℃牳鎸夐挳 */
+export function getInvoiceApproverIds() {
+  return requestClient.get<number[]>('/crm/invoice/approver-ids');
+}
+
+/**
+ * 瀹℃牳寮�绁紙瀹℃壒涓� 鈫� 瀹℃牳閫氳繃 / 瀹℃牳涓嶉�氳繃锛�
+ *
+ * 浠呭鎵归厤缃腑鐨勫鎵逛汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditInvoice(
+  id: number,
+  pass: boolean,
+  reviewRemark?: string,
+) {
+  return requestClient.put('/crm/invoice/audit', { id, pass, reviewRemark });
 }
 
 /** 鑾峰緱寰呭鏍稿紑绁ㄦ暟閲� */
diff --git a/src/api/crm/refund/index.ts b/src/api/crm/refund/index.ts
index 24bd37a..d479cfa 100644
--- a/src/api/crm/refund/index.ts
+++ b/src/api/crm/refund/index.ts
@@ -17,7 +17,11 @@
     applyUserName?: string;
     applyTime?: string;
     auditStatus?: number;
-    processInstanceId?: string;
+    // 浠ヤ笅涓哄鏍告淳鐢熷瓧娈碉紝鐢卞悗绔湪瀹℃牳鏃跺啓鍏ワ紝鍓嶇鍙灞曠ず
+    reviewerId?: number; // 瀹℃牳浜篒D锛堝疄闄呮墽琛屽鏍哥殑鐢ㄦ埛锛�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: string; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堝鏍搁�氳繃鏃剁殑鎰忚 / 瀹℃牳涓嶉�氳繃鏃剁殑鍘熷洜锛�
     remark?: string;
     createTime?: string;
   }
@@ -52,9 +56,26 @@
   return requestClient.put('/crm/refund/update', data);
 }
 
-/** 瀹℃壒閫�璐瑰崟 */
-export function auditRefund(data: { id: number; auditStatus: number }) {
-  return requestClient.put('/crm/refund/audit', data);
+/**
+ * 鎻愪氦瀹℃壒锛堣崏绋� / 瀹℃牳涓嶉�氳繃 鈫� 瀹℃壒涓級
+ * 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾銆�
+ */
+export function submitRefund(id: number) {
+  return requestClient.put('/crm/refund/submit', null, { params: { id } });
+}
+
+/** 鑾峰緱閫�璐瑰崟鐨勫鎵逛汉缂栧彿闆嗗悎锛屼緵鍒楄〃椤靛垽鏂槸鍚﹀睍绀哄鏍告寜閽� */
+export function getRefundApproverIds() {
+  return requestClient.get<number[]>('/crm/refund/approver-ids');
+}
+
+/**
+ * 瀹℃牳閫�璐瑰崟锛堝鎵逛腑 鈫� 瀹℃牳閫氳繃 / 瀹℃牳涓嶉�氳繃锛�
+ *
+ * 浠呭鎵归厤缃腑鐨勫鎵逛汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditRefund(id: number, pass: boolean, reviewRemark?: string) {
+  return requestClient.put('/crm/refund/audit', { id, pass, reviewRemark });
 }
 
 /** 鍒犻櫎閫�璐瑰崟 */
diff --git a/src/api/erp/finance/payment/index.ts b/src/api/erp/finance/payment/index.ts
index 276c047..6d4d8ee 100644
--- a/src/api/erp/finance/payment/index.ts
+++ b/src/api/erp/finance/payment/index.ts
@@ -18,7 +18,11 @@
     discountPrice: number; // 浼樻儬閲戦
     paymentPrice: number; // 瀹為檯浠樻閲戦
     status: number; // 鐘舵��
-    processInstanceId?: string; // 宸ヤ綔娴佺紪鍙�
+    // ========== 瀹℃壒淇℃伅锛堣交閲忕骇鐘舵�佹満瀹℃壒锛� ==========
+    reviewerId?: number; // 瀹℃牳浜虹紪鍙�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: Date | string; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堜笉閫氳繃鏃朵负椹冲洖鍘熷洜锛�
     remark: string; // 澶囨敞
     attachmentList?: { id: number; name?: string; url?: string }[]; // 闄勪欢鍒楄〃
     accountId?: number; // 浠樻璐︽埛
@@ -76,16 +80,37 @@
   return requestClient.put('/erp/finance-payment/update', data);
 }
 
-/** 鎻愪氦浠樻鍗曞鎵� */
-export function submitFinancePayment(id: number, processDefinitionKey: string) {
-  return requestClient.post('/erp/finance-payment/submit', null, {
-    params: { id, processDefinitionKey },
+/**
+ * 鎻愪氦瀹℃壒锛堟湭瀹℃牳 鈫� 瀹℃壒涓級
+ *
+ * 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾銆�
+ */
+export function submitFinancePayment(id: number) {
+  return requestClient.put('/erp/finance-payment/submit', null, {
+    params: { id },
   });
 }
 
-/** 鑾峰彇浠樻鍗曞鎵规祦绋嬪垪琛� */
-export function getFinancePaymentApproveProcessList() {
-  return requestClient.get('/erp/finance-payment/approve-process-list');
+/** 鑾峰緱瀹℃壒浜虹紪鍙烽泦鍚堬紝渚涘垪琛ㄩ〉鍒ゆ柇鏄惁灞曠ず瀹℃牳鎸夐挳 */
+export function getFinancePaymentApproverUserIds() {
+  return requestClient.get<number[]>('/erp/finance-payment/approver-ids');
+}
+
+/**
+ * 瀹℃牳锛堝鎵逛腑 鈫� 瀹℃牳閫氳繃/瀹℃牳涓嶉�氳繃锛�
+ *
+ * 浠呭鎵归厤缃腑鎸囧畾鐨勫鎵逛汉鏈汉鍙皟鐢紱pass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditFinancePayment(
+  id: number,
+  pass: boolean,
+  reviewRemark?: string,
+) {
+  return requestClient.put('/erp/finance-payment/audit', {
+    id,
+    pass,
+    reviewRemark,
+  });
 }
 
 /** 鍒犻櫎浠樻鍗� */
diff --git a/src/api/erp/purchase/request/index.ts b/src/api/erp/purchase/request/index.ts
index cdb5e08..4f803e7 100644
--- a/src/api/erp/purchase/request/index.ts
+++ b/src/api/erp/purchase/request/index.ts
@@ -8,7 +8,6 @@
     id?: number; // 缂栧彿
     no?: string; // 鐢宠缂栧彿
     status?: number; // 鐘舵�侊細0鏈彁浜� 10瀹℃壒涓� 20瀹℃牳閫氳繃 30瀹℃牳涓嶉�氳繃 40宸插彇娑�
-    processInstanceId?: string; // 宸ヤ綔娴佺紪鍙�
     requestUserId?: number; // 鐢宠浜虹紪鍙�
     requestUserName?: string; // 鐢宠浜哄悕绉�
     requestDeptId?: number; // 鐢宠閮ㄩ棬缂栧彿
@@ -25,7 +24,11 @@
     productNames?: string; // 浜у搧鍚嶇О鍒楄〃
     orderId?: number; // 鐢熸垚鐨勯噰璐鍗曠紪鍙�
     orderNo?: string; // 鐢熸垚鐨勯噰璐鍗曞彿
-    processInstanceId?: number; // 瀹℃壒娴佺▼瀹炰緥缂栧彿
+    // ========== 瀹℃壒淇℃伅锛堣交閲忕骇鐘舵�佹満瀹℃壒锛� ==========
+    reviewerId?: number; // 瀹℃牳浜虹紪鍙�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: Date | string; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堜笉閫氳繃鏃朵负椹冲洖鍘熷洜锛�
     items?: PurchaseRequestItem[]; // 鐢宠鏄庣粏
     createTime?: string; // 鍒涘缓鏃堕棿
   }
@@ -56,9 +59,37 @@
   return requestClient.put('/purchase/request/update', data);
 }
 
-/** 鎻愪氦閲囪喘鐢宠瀹℃壒 */
-export function submitPurchaseRequest(id: number, processDefinitionKey: string) {
-  return requestClient.put('/purchase/request/submit', null, { params: { id, processDefinitionKey } });
+/**
+ * 鎻愪氦瀹℃壒锛堟湭鎻愪氦/瀹℃牳涓嶉�氳繃 鈫� 瀹℃壒涓級
+ *
+ * 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾銆�
+ */
+export function submitPurchaseRequest(id: number) {
+  return requestClient.put('/purchase/request/submit', null, {
+    params: { id },
+  });
+}
+
+/** 鑾峰緱瀹℃壒浜虹紪鍙烽泦鍚堬紝渚涘垪琛ㄩ〉鍒ゆ柇鏄惁灞曠ず瀹℃牳鎸夐挳 */
+export function getPurchaseRequestApproverUserIds() {
+  return requestClient.get<number[]>('/purchase/request/approver-ids');
+}
+
+/**
+ * 瀹℃牳锛堝鎵逛腑 鈫� 瀹℃牳閫氳繃/瀹℃牳涓嶉�氳繃锛�
+ *
+ * 浠呭鎵归厤缃腑鎸囧畾鐨勫鎵逛汉鏈汉鍙皟鐢紱pass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditPurchaseRequest(
+  id: number,
+  pass: boolean,
+  reviewRemark?: string,
+) {
+  return requestClient.put('/purchase/request/audit', {
+    id,
+    pass,
+    reviewRemark,
+  });
 }
 
 /** 鐢熸垚閲囪喘璁㈠崟 */
@@ -89,9 +120,4 @@
 /** 瀵煎嚭閲囪喘鐢宠 Excel */
 export function exportPurchaseRequest(params: any) {
   return requestClient.download('/purchase/request/export-excel', { params });
-}
-
-/** 鑾峰彇瀹℃壒娴佺▼鍒楄〃 */
-export function getApproveProcessList(id: number) {
-  return requestClient.get(`/purchase/request/approve-process-list`, { params: { id } });
 }
diff --git a/src/api/hrm/attendance/exception/index.ts b/src/api/hrm/attendance/exception/index.ts
index 1adeb2a..1efb375 100644
--- a/src/api/hrm/attendance/exception/index.ts
+++ b/src/api/hrm/attendance/exception/index.ts
@@ -17,15 +17,14 @@
     blobIds?: number[];
     attachmentList?: SystemStorageApi.StorageBlob[];
     status?: number;
-    processInstanceId?: string;
+    exceptionDate?: string;
+    // 浠ヤ笅涓哄鏍告淳鐢熷瓧娈碉紝鐢卞悗绔湪瀹℃牳鏃跺啓鍏ワ紝鍓嶇鍙灞曠ず
+    reviewerId?: number; // 瀹℃牳浜篒D锛堝疄闄呮墽琛屽鏍哥殑鐢ㄦ埛锛�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: string; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堝鏍搁�氳繃鏃剁殑鎰忚 / 瀹℃牳涓嶉�氳繃鏃剁殑鍘熷洜锛�
     remark?: string;
     createTime?: string;
-  }
-
-  /** 瀹℃壒娴佺▼ */
-  export interface ApproveProcess {
-    key: string;
-    name: string;
   }
 }
 
@@ -51,16 +50,37 @@
   return requestClient.post('/hrm/attendance/exception/create', data);
 }
 
-/** 鎻愪氦鑰冨嫟寮傚父鐢宠 */
-export function submitAttendanceException(id: number, processDefinitionKey: string) {
-  return requestClient.post('/hrm/attendance/exception/submit', null, {
-    params: { id, processDefinitionKey },
+/**
+ * 鎻愪氦鑰冨嫟寮傚父鐢宠瀹℃壒锛堣崏绋� / 瀹℃牳涓嶉�氳繃 鈫� 瀹℃壒涓級
+ *
+ * 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶆寜涓氬姟绫诲瀷锛坔rm_attendance_approve锛夌粺涓�閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾銆�
+ */
+export function submitAttendanceException(id: number) {
+  return requestClient.put('/hrm/attendance/exception/submit', null, {
+    params: { id },
   });
 }
 
-/** 鑾峰彇鑰冨嫟寮傚父瀹℃壒娴佺▼鍒楄〃 */
-export function getAttendanceExceptionApproveProcessList() {
-  return requestClient.get<HrmAttendanceExceptionApi.ApproveProcess[]>(
-    '/hrm/attendance/exception/approve-process-list',
+/** 鑾峰緱鑰冨嫟寮傚父鐢宠鐨勫鎵逛汉缂栧彿闆嗗悎锛屼緵鍒楄〃椤靛垽鏂槸鍚﹀睍绀哄鏍告寜閽� */
+export function getAttendanceExceptionApproverIds() {
+  return requestClient.get<number[]>(
+    '/hrm/attendance/exception/approver-ids',
   );
+}
+
+/**
+ * 瀹℃牳鑰冨嫟寮傚父鐢宠锛堝鎵逛腑 鈫� 瀹℃牳閫氳繃 / 瀹℃牳涓嶉�氳繃锛�
+ *
+ * 浠呭鎵归厤缃腑鐨勫鎵逛汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditAttendanceException(
+  id: number,
+  pass: boolean,
+  reviewRemark?: string,
+) {
+  return requestClient.put('/hrm/attendance/exception/audit', {
+    id,
+    pass,
+    reviewRemark,
+  });
 }
\ No newline at end of file
diff --git a/src/api/hrm/leave/index.ts b/src/api/hrm/leave/index.ts
index e280286..03fbdcb 100644
--- a/src/api/hrm/leave/index.ts
+++ b/src/api/hrm/leave/index.ts
@@ -18,7 +18,11 @@
     blobIds?: number[];
     attachmentList?: SystemStorageApi.StorageBlob[];
     status?: number;
-    processInstanceId?: string;
+    // 浠ヤ笅涓哄鏍告淳鐢熷瓧娈碉紝鐢卞悗绔湪瀹℃牳鏃跺啓鍏ワ紝鍓嶇鍙灞曠ず
+    reviewerId?: number; // 瀹℃牳浜篒D锛堝疄闄呮墽琛屽鏍哥殑鐢ㄦ埛锛�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: string; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堝鏍搁�氳繃鏃剁殑鎰忚 / 瀹℃牳涓嶉�氳繃鏃剁殑鍘熷洜锛�
     remark?: string;
     createTime?: string;
   }
@@ -52,9 +56,13 @@
   return requestClient.delete(`/hrm/leave/delete?id=${id}`);
 }
 
-/** 鎻愪氦璇峰亣鐢宠锛堝彂璧峰鎵癸級 */
+/**
+ * 鎻愪氦璇峰亣鐢宠瀹℃壒锛堣崏绋� / 瀹℃牳涓嶉�氳繃 鈫� 瀹℃壒涓級
+ *
+ * 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶆寜涓氬姟绫诲瀷锛坔rm_leave_approve锛夌粺涓�閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾銆�
+ */
 export function submitLeave(id: number) {
-  return requestClient.post(`/hrm/leave/submit?id=${id}`);
+  return requestClient.put('/hrm/leave/submit', null, { params: { id } });
 }
 
 /** 鎾ら攢璇峰亣鐢宠 */
@@ -62,7 +70,16 @@
   return requestClient.post(`/hrm/leave/cancel?id=${id}`);
 }
 
-/** 鑾峰彇鍙敤娴佺▼瀹氫箟鍒楄〃 */
-export function getLeaveApproveProcessDefinitionList() {
-  return requestClient.get<any[]>('/hrm/leave/approve-process-definition-list');
+/** 鑾峰緱璇峰亣鐢宠鐨勫鎵逛汉缂栧彿闆嗗悎锛屼緵鍒楄〃椤靛垽鏂槸鍚﹀睍绀哄鏍告寜閽� */
+export function getLeaveApproverIds() {
+  return requestClient.get<number[]>('/hrm/leave/approver-ids');
+}
+
+/**
+ * 瀹℃牳璇峰亣鐢宠锛堝鎵逛腑 鈫� 瀹℃牳閫氳繃 / 瀹℃牳涓嶉�氳繃锛�
+ *
+ * 浠呭鎵归厤缃腑鐨勫鎵逛汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditLeave(id: number, pass: boolean, reviewRemark?: string) {
+  return requestClient.put('/hrm/leave/audit', { id, pass, reviewRemark });
 }
\ No newline at end of file
diff --git a/src/api/hrm/performance-appraise/index.ts b/src/api/hrm/performance-appraise/index.ts
index c9768cb..7ae7914 100644
--- a/src/api/hrm/performance-appraise/index.ts
+++ b/src/api/hrm/performance-appraise/index.ts
@@ -39,10 +39,11 @@
     statusName?: string;
     auditStatus?: number; // 瀹℃壒鐘舵�侊細0鏈彁浜� 10瀹℃壒涓� 20閫氳繃 30椹冲洖 40宸插彇娑�
     auditStatusName?: string;
-    processInstanceId?: string;
-    auditUserId?: number;
-    auditTime?: string;
-    auditRemark?: string;
+    // 浠ヤ笅涓哄鏍告淳鐢熷瓧娈碉紝鐢卞悗绔湪瀹℃牳鏃跺啓鍏ワ紝鍓嶇鍙灞曠ず
+    reviewerId?: number; // 瀹℃牳浜篒D锛堝疄闄呮墽琛屽鏍哥殑鐢ㄦ埛锛�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: string; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堝鏍搁�氳繃鏃剁殑鎰忚 / 瀹℃牳涓嶉�氳繃鏃剁殑鍘熷洜锛�
     remark?: string;
     itemCount?: number; // 鑰冩牳鎸囨爣鏁�
     resultCount?: number; // 宸茶瘎鍒嗕汉鏁�
@@ -111,23 +112,37 @@
   return requestClient.post<boolean>('/hrm/performance-appraise/item-save', data);
 }
 
-/** 鎻愪氦缁╂晥鑰冩牳鏂规瀹℃壒 */
-export function submitAppraise(id: number, processDefinitionKey: string) {
-  return requestClient.post<boolean>('/hrm/performance-appraise/submit', null, {
-    params: { id, processDefinitionKey },
+/**
+ * 鎻愪氦缁╂晥鑰冩牳鏂规瀹℃壒锛堣崏绋� / 瀹℃牳涓嶉�氳繃 鈫� 瀹℃壒涓級
+ *
+ * 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶆寜涓氬姟绫诲瀷锛坧erformance_appraise_approve锛夌粺涓�閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾銆�
+ */
+export function submitAppraise(id: number) {
+  return requestClient.put<boolean>('/hrm/performance-appraise/submit', null, {
+    params: { id },
   });
 }
 
-/** 瀹℃壒缁╂晥鑰冩牳鏂规 */
-export function auditAppraise(data: { id: number; auditStatus: number; remark?: string }) {
-  return requestClient.put<boolean>('/hrm/performance-appraise/audit', data);
+/** 鑾峰緱缁╂晥鑰冩牳鏂规鐨勫鎵逛汉缂栧彿闆嗗悎锛屼緵鍒楄〃椤靛垽鏂槸鍚﹀睍绀哄鏍告寜閽� */
+export function getAppraiseApproverIds() {
+  return requestClient.get<number[]>('/hrm/performance-appraise/approver-ids');
 }
 
-/** 鑾峰彇缁╂晥鑰冩牳鏂规瀹℃壒娴佺▼瀹氫箟鍒楄〃 */
-export function getAppraiseProcessList() {
-  return requestClient.get<{ id: string; name: string; version: number; key: string }[]>(
-    '/hrm/performance-appraise/approve-process-list',
-  );
+/**
+ * 瀹℃牳缁╂晥鑰冩牳鏂规锛堝鎵逛腑 鈫� 瀹℃牳閫氳繃 / 瀹℃牳涓嶉�氳繃锛�
+ *
+ * 浠呭鎵归厤缃腑鐨勫鎵逛汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditAppraise(
+  id: number,
+  pass: boolean,
+  reviewRemark?: string,
+) {
+  return requestClient.put<boolean>('/hrm/performance-appraise/audit', {
+    id,
+    pass,
+    reviewRemark,
+  });
 }
 
 /** 鑾峰彇鑰冩牳缁撴灉鍒嗛〉 */
diff --git a/src/api/hrm/resignation/index.ts b/src/api/hrm/resignation/index.ts
index 8b90cd2..c40839d 100644
--- a/src/api/hrm/resignation/index.ts
+++ b/src/api/hrm/resignation/index.ts
@@ -20,7 +20,11 @@
     blobIds?: number[];
     attachmentList?: SystemStorageApi.StorageBlob[];
     status?: number;
-    processInstanceId?: string;
+    // 浠ヤ笅涓哄鏍告淳鐢熷瓧娈碉紝鐢卞悗绔湪瀹℃牳鏃跺啓鍏ワ紝鍓嶇鍙灞曠ず
+    reviewerId?: number; // 瀹℃牳浜篒D锛堝疄闄呮墽琛屽鏍哥殑鐢ㄦ埛锛�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: string; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堝鏍搁�氳繃鏃剁殑鎰忚 / 瀹℃牳涓嶉�氳繃鏃剁殑鍘熷洜锛�
     remark?: string;
     createTime?: string;
   }
@@ -56,9 +60,13 @@
   return requestClient.delete(`/hrm/resignation/delete?id=${id}`);
 }
 
-/** 鎻愪氦绂昏亴鐢宠锛堝彂璧峰鎵癸級 */
+/**
+ * 鎻愪氦绂昏亴鐢宠瀹℃壒锛堣崏绋� / 瀹℃牳涓嶉�氳繃 鈫� 瀹℃壒涓級
+ *
+ * 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶆寜涓氬姟绫诲瀷锛坔rm_resignation_approve锛夌粺涓�閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾銆�
+ */
 export function submitResignation(id: number) {
-  return requestClient.post(`/hrm/resignation/submit?id=${id}`);
+  return requestClient.put('/hrm/resignation/submit', null, { params: { id } });
 }
 
 /** 鎾ら攢绂昏亴鐢宠 */
@@ -71,7 +79,24 @@
   return requestClient.put('/hrm/resignation/handover', { id, handoverStatus });
 }
 
-/** 鑾峰彇鍙敤娴佺▼瀹氫箟鍒楄〃 */
-export function getResignationApproveProcessDefinitionList() {
-  return requestClient.get<any[]>('/hrm/resignation/approve-process-definition-list');
+/** 鑾峰緱绂昏亴鐢宠鐨勫鎵逛汉缂栧彿闆嗗悎锛屼緵鍒楄〃椤靛垽鏂槸鍚﹀睍绀哄鏍告寜閽� */
+export function getResignationApproverIds() {
+  return requestClient.get<number[]>('/hrm/resignation/approver-ids');
+}
+
+/**
+ * 瀹℃牳绂昏亴鐢宠锛堝鎵逛腑 鈫� 瀹℃牳閫氳繃 / 瀹℃牳涓嶉�氳繃锛�
+ *
+ * 浠呭鎵归厤缃腑鐨勫鎵逛汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditResignation(
+  id: number,
+  pass: boolean,
+  reviewRemark?: string,
+) {
+  return requestClient.put('/hrm/resignation/audit', {
+    id,
+    pass,
+    reviewRemark,
+  });
 }
\ No newline at end of file
diff --git a/src/api/hrm/transfer/index.ts b/src/api/hrm/transfer/index.ts
index e992674..a328968 100644
--- a/src/api/hrm/transfer/index.ts
+++ b/src/api/hrm/transfer/index.ts
@@ -23,7 +23,11 @@
     blobIds?: number[];
     attachmentList?: SystemStorageApi.StorageBlob[];
     status?: number;
-    processInstanceId?: string;
+    // 浠ヤ笅涓哄鏍告淳鐢熷瓧娈碉紝鐢卞悗绔湪瀹℃牳鏃跺啓鍏ワ紝鍓嶇鍙灞曠ず
+    reviewerId?: number; // 瀹℃牳浜篒D锛堝疄闄呮墽琛屽鏍哥殑鐢ㄦ埛锛�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: string; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堝鏍搁�氳繃鏃剁殑鎰忚 / 瀹℃牳涓嶉�氳繃鏃剁殑鍘熷洜锛�
     remark?: string;
     createTime?: string;
   }
@@ -57,9 +61,13 @@
   return requestClient.delete(`/hrm/transfer/delete?id=${id}`);
 }
 
-/** 鎻愪氦璋冨矖鐢宠锛堝彂璧峰鎵癸級 */
+/**
+ * 鎻愪氦璋冨矖鐢宠瀹℃壒锛堣崏绋� / 瀹℃牳涓嶉�氳繃 鈫� 瀹℃壒涓級
+ *
+ * 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶆寜涓氬姟绫诲瀷锛坔rm_transfer_approve锛夌粺涓�閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾銆�
+ */
 export function submitTransfer(id: number) {
-  return requestClient.post(`/hrm/transfer/submit?id=${id}`);
+  return requestClient.put('/hrm/transfer/submit', null, { params: { id } });
 }
 
 /** 鎾ら攢璋冨矖鐢宠 */
@@ -67,7 +75,16 @@
   return requestClient.post(`/hrm/transfer/cancel?id=${id}`);
 }
 
-/** 鑾峰彇鍙敤娴佺▼瀹氫箟鍒楄〃 */
-export function getTransferApproveProcessDefinitionList() {
-  return requestClient.get<any[]>('/hrm/transfer/approve-process-definition-list');
+/** 鑾峰緱璋冨矖鐢宠鐨勫鎵逛汉缂栧彿闆嗗悎锛屼緵鍒楄〃椤靛垽鏂槸鍚﹀睍绀哄鏍告寜閽� */
+export function getTransferApproverIds() {
+  return requestClient.get<number[]>('/hrm/transfer/approver-ids');
+}
+
+/**
+ * 瀹℃牳璋冨矖鐢宠锛堝鎵逛腑 鈫� 瀹℃牳閫氳繃 / 瀹℃牳涓嶉�氳繃锛�
+ *
+ * 浠呭鎵归厤缃腑鐨勫鎵逛汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditTransfer(id: number, pass: boolean, reviewRemark?: string) {
+  return requestClient.put('/hrm/transfer/audit', { id, pass, reviewRemark });
 }
\ No newline at end of file
diff --git a/src/api/mes/pd/project/index.ts b/src/api/mes/pd/project/index.ts
index e6841ce..4954094 100644
--- a/src/api/mes/pd/project/index.ts
+++ b/src/api/mes/pd/project/index.ts
@@ -32,8 +32,14 @@
     remark?: string;
     /** 鍒涘缓鏃堕棿 */
     createTime?: number;
-    /** BPM 娴佺▼瀹炰緥缂栧彿 */
-    processInstanceId?: string;
+    /** 瀹℃牳浜虹敤鎴风紪鍙凤紙瀹℃牳閫氳繃鍚庡啓鍏ュ疄闄呭鏍镐汉锛� */
+    reviewerId?: number;
+    /** 瀹℃牳浜哄鍚� */
+    reviewerName?: string;
+    /** 瀹℃牳鏃堕棿 */
+    reviewTime?: number;
+    /** 瀹℃牳鎰忚锛堥�氳繃鏃剁殑鎰忚 / 涓嶉�氳繃鏃剁殑鍘熷洜锛� */
+    reviewRemark?: string;
   }
 
   export interface PageParams extends PageParam {
@@ -89,18 +95,42 @@
   });
 }
 
-/** 鎻愪氦瀹℃壒锛堝緟瀹℃牳鈫掑鎵逛腑锛� */
-export function submitPdProjectApproval(id: number, processDefinitionKey: string) {
+/**
+ * 鎻愪氦瀹℃壒锛堝緟瀹℃牳鈫掑鎵逛腑锛�
+ *
+ * 瀹℃牳浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶆寜涓氬姟绫诲瀷缁熶竴閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾锛�
+ * 鏈惎鐢ㄥ鎵规垨鏈厤缃湁鏁堝鏍镐汉鏃跺悗绔細鎶涘嚭涓氬姟寮傚父銆�
+ */
+export function submitPdProjectApproval(id: number) {
   return requestClient.put('/mes/pd/project/submit-approval', undefined, {
-    params: { id, processDefinitionKey },
+    params: { id },
   });
 }
 
-/** 鑾峰緱瀹℃壒娴佺▼鍒楄〃 */
-export function getPdProjectApproveProcessList() {
-  return requestClient.get<Array<{ id: string; name: string; key: string }>>(
-    '/mes/pd/project/approve-process-list',
-  );
+/**
+ * 鑾峰緱璁捐椤圭洰鐨勫鏍镐汉缂栧彿闆嗗悎
+ *
+ * 渚涘垪琛ㄩ〉鍒ゆ柇褰撳墠鐧诲綍鐢ㄦ埛鏄惁搴斿睍绀恒�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽紱鏈厤缃椂杩斿洖绌烘暟缁勩��
+ */
+export function getPdProjectApproverIds() {
+  return requestClient.get<number[]>('/mes/pd/project/approver-ids');
+}
+
+/**
+ * 瀹℃牳璁捐椤圭洰锛堝鎵逛腑鈫掑緟褰掓。/寰呭鏍革級
+ *
+ * 浠呭鎵归厤缃腑鐨勫鏍镐汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditPdProject(
+  id: number,
+  pass: boolean,
+  reviewRemark?: string,
+) {
+  return requestClient.put('/mes/pd/project/audit', {
+    id,
+    pass,
+    reviewRemark,
+  });
 }
 
 /** 褰掓。锛堝緟褰掓。鈫掑凡褰掓。锛夛紝鍚庣鑷姩鍒涘缓褰掓。鍙拌处璁板綍 */
diff --git a/src/api/mes/pd/tech-confirm/index.ts b/src/api/mes/pd/tech-confirm/index.ts
index be86723..70a01a7 100644
--- a/src/api/mes/pd/tech-confirm/index.ts
+++ b/src/api/mes/pd/tech-confirm/index.ts
@@ -13,15 +13,17 @@
     specification?: string; // 瑙勬牸鍨嬪彿
     techParams?: string; // 鎶�鏈弬鏁� JSON
     templateType?: string; // 妯℃澘绫诲瀷
+    // 浠ヤ笅涓烘祦绋嬫淳鐢熷瓧娈碉紝鐢卞悗绔嚜鍔ㄨ褰曪紝鍓嶇鍙灞曠ず
     drafterId?: number; // 缂栧埗浜篒D
     drafterName?: string; // 缂栧埗浜哄鍚�
     draftTime?: Date; // 缂栧埗鏃堕棿
-    reviewerId?: number; // 瀹℃牳浜篒D
-    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewerId?: number; // 瀹℃牳浜篒D锛堝疄闄呮墽琛屽鏍哥殑鐢ㄦ埛锛岀敱鍚庣鍦ㄥ鏍告椂鍐欏叆锛�
+    reviewerName?: string; // 瀹℃牳浜哄鍚嶏紙瀹℃牳鏃跺啑浣欒褰曪級
     reviewTime?: Date; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堝鏍搁�氳繃鏃剁殑鎰忚 / 瀹℃牳涓嶉�氳繃鏃剁殑鍘熷洜锛�
     customerConfirmUser?: string; // 瀹㈡埛纭浜�
     customerConfirmTime?: Date; // 瀹㈡埛纭鏃堕棿
-    status?: number; // 鐘舵��
+    status?: number; // 鐘舵�侊紙0=鑽夌锛�1=寰呭鏍革紝2=宸插鏍革紝3=瀹㈡埛宸茬‘璁わ紝4=瀹℃牳涓嶉�氳繃锛�
     remark?: string; // 澶囨敞
     createTime?: Date; // 鍒涘缓鏃堕棿
   }
@@ -42,21 +44,77 @@
   );
 }
 
-/** 鏂板浜у搧鎶�鏈‘璁ゅ嚱 */
+/**
+ * 鏂板浜у搧鎶�鏈‘璁ゅ嚱
+ *
+ * 鐘舵�佺敱鍚庣鍥哄畾涓鸿崏绋匡紝缂栧埗浜轰笌缂栧埗鏃堕棿鐢卞悗绔嚜鍔ㄨ褰曪紝鍧囨棤闇�鍓嶇浼犲弬銆�
+ */
 export function createTechConfirm(data: MesPdTechConfirmApi.TechConfirm) {
   return requestClient.post('/mes/pd/tech-confirm/create', data);
 }
 
-/** 淇敼浜у搧鎶�鏈‘璁ゅ嚱 */
+/**
+ * 淇敼浜у搧鎶�鏈‘璁ゅ嚱
+ *
+ * 浠呰崏绋跨姸鎬佸彲淇敼锛屼笖鐘舵�佷笌娴佺▼娲剧敓瀛楁涓嶅弬涓庢彁浜ゃ��
+ */
 export function updateTechConfirm(data: MesPdTechConfirmApi.TechConfirm) {
   return requestClient.put('/mes/pd/tech-confirm/update', data);
 }
 
-/** 鍒犻櫎浜у搧鎶�鏈‘璁ゅ嚱 */
+/** 鍒犻櫎浜у搧鎶�鏈‘璁ゅ嚱锛堜粎鑽夌 / 瀹℃牳涓嶉�氳繃鐘舵�佸彲鍒犻櫎锛� */
 export function deleteTechConfirm(id: number) {
   return requestClient.delete(`/mes/pd/tech-confirm/delete?id=${id}`);
 }
 
+/**
+ * 鎻愪氦鎶�鏈‘璁ゅ嚱瀹℃牳锛堣崏绋�/瀹℃牳涓嶉�氳繃鈫掑緟瀹℃牳锛�
+ *
+ * 瀹℃牳浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶆寜涓氬姟绫诲瀷缁熶竴閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾锛�
+ * 鏈惎鐢ㄥ鎵规垨鏈厤缃湁鏁堝鏍镐汉鏃跺悗绔細鎶涘嚭涓氬姟寮傚父銆�
+ */
+export function submitTechConfirmAudit(id: number) {
+  return requestClient.put('/mes/pd/tech-confirm/submit-approval', undefined, {
+    params: { id },
+  });
+}
+
+/**
+ * 鑾峰緱鎶�鏈‘璁ゅ嚱鐨勫鏍镐汉缂栧彿闆嗗悎
+ *
+ * 渚涘垪琛ㄩ〉鍒ゆ柇褰撳墠鐧诲綍鐢ㄦ埛鏄惁搴斿睍绀恒�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽紱鏈厤缃椂杩斿洖绌烘暟缁勩��
+ */
+export function getTechConfirmApproverIds() {
+  return requestClient.get<number[]>('/mes/pd/tech-confirm/approver-ids');
+}
+
+/**
+ * 瀹℃牳鎶�鏈‘璁ゅ嚱锛堝緟瀹℃牳鈫掑凡瀹℃牳/瀹℃牳涓嶉�氳繃锛�
+ *
+ * 浠呭鎵归厤缃腑鐨勫鏍镐汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditTechConfirm(
+  id: number,
+  pass: boolean,
+  reviewRemark?: string,
+) {
+  return requestClient.put('/mes/pd/tech-confirm/audit', {
+    id,
+    pass,
+    reviewRemark,
+  });
+}
+
+/** 瀹㈡埛纭锛堝凡瀹℃牳鈫掑鎴峰凡纭锛� */
+export function confirmTechConfirmByCustomer(
+  id: number,
+  customerConfirmUser: string,
+) {
+  return requestClient.put('/mes/pd/tech-confirm/customer-confirm', undefined, {
+    params: { id, customerConfirmUser },
+  });
+}
+
 /** 瀵煎嚭浜у搧鎶�鏈‘璁ゅ嚱 */
 export function exportTechConfirm(params: any) {
   return requestClient.download('/mes/pd/tech-confirm/export-excel', { params });
diff --git a/src/api/mes/wm/equipmentreceipt/index.ts b/src/api/mes/wm/equipmentreceipt/index.ts
index 782a8b1..2f7d603 100644
--- a/src/api/mes/wm/equipmentreceipt/index.ts
+++ b/src/api/mes/wm/equipmentreceipt/index.ts
@@ -13,7 +13,10 @@
     receiptDate?: number; // 鍏ュ簱鏃ユ湡
     source?: string; // 鍏ュ簱鏉ユ簮
     status?: number; // 鍗曟嵁鐘舵��
-    processInstanceId?: string; // 瀹℃壒娴佺▼瀹炰緥缂栧彿
+    reviewerId?: number; // 瀹℃牳浜虹敤鎴风紪鍙�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: number; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堥�氳繃鏃剁殑鎰忚 / 涓嶉�氳繃鏃剁殑鍘熷洜锛�
     remark?: string; // 澶囨敞
     createTime?: Date; // 鍒涘缓鏃堕棿
     lines?: MesWmEquipmentReceiptLineApi.EquipmentReceiptLine[]; // 鏄庣粏琛�
@@ -53,21 +56,42 @@
   return requestClient.delete(`/mes/wm/equipment-receipt/delete?id=${id}`);
 }
 
-/** 鏌ヨ鍙敤鐨勮澶囧叆搴撳崟瀹℃壒娴佺▼瀹氫箟 */
-export function getEquipmentReceiptApproveProcessList() {
-  return requestClient.get<{ id: string; name: string; key: string }[]>(
-    '/mes/wm/equipment-receipt/approval-process-list',
-  );
+/**
+ * 鎻愪氦璁惧鍏ュ簱鍗曞鎵癸紙鑽夌鈫掑鎵逛腑锛�
+ *
+ * 瀹℃牳浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶆寜涓氬姟绫诲瀷缁熶竴閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾锛�
+ * 鏈惎鐢ㄥ鎵规垨鏈厤缃湁鏁堝鏍镐汉鏃跺悗绔細鎶涘嚭涓氬姟寮傚父銆�
+ */
+export function submitEquipmentReceiptApproval(id: number) {
+  return requestClient.put('/mes/wm/equipment-receipt/submit-approval', undefined, {
+    params: { id },
+  });
 }
 
-/** 鎻愪氦璁惧鍏ュ簱鍗曞鎵� */
-export function submitEquipmentReceiptApproval(
+/**
+ * 鑾峰緱璁惧鍏ュ簱鍗曠殑瀹℃牳浜虹紪鍙烽泦鍚�
+ *
+ * 渚涘垪琛ㄩ〉鍒ゆ柇褰撳墠鐧诲綍鐢ㄦ埛鏄惁搴斿睍绀恒�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽紱鏈厤缃椂杩斿洖绌烘暟缁勩��
+ */
+export function getEquipmentReceiptApproverIds() {
+  return requestClient.get<number[]>('/mes/wm/equipment-receipt/approver-ids');
+}
+
+/**
+ * 瀹℃牳璁惧鍏ュ簱鍗曪紙瀹℃壒涓啋宸插鎵�/鑽夌锛�
+ *
+ * 浠呭鎵归厤缃腑鐨勫鏍镐汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditEquipmentReceipt(
   id: number,
-  processDefinitionKey: string,
+  pass: boolean,
+  reviewRemark?: string,
 ) {
-  return requestClient.put(
-    `/mes/wm/equipment-receipt/submit-approval?id=${id}&processDefinitionKey=${processDefinitionKey}`,
-  );
+  return requestClient.put('/mes/wm/equipment-receipt/audit', {
+    id,
+    pass,
+    reviewRemark,
+  });
 }
 
 /** 鎵ц璁惧鍏ュ簱 */
diff --git a/src/api/mes/wm/itemreceipt/index.ts b/src/api/mes/wm/itemreceipt/index.ts
index 63e50cc..00e93cd 100644
--- a/src/api/mes/wm/itemreceipt/index.ts
+++ b/src/api/mes/wm/itemreceipt/index.ts
@@ -23,6 +23,10 @@
     areaName?: string; // 搴撲綅鍚嶇О
     receiptDate?: number; // 鍏ュ簱鏃ユ湡
     status?: number; // 鐘舵��
+    reviewerId?: number; // 瀹℃牳浜虹敤鎴风紪鍙�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: number; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堥�氳繃鏃剁殑鎰忚 / 涓嶉�氳繃鏃剁殑鍘熷洜锛�
     remark?: string; // 澶囨敞
     createTime?: Date; // 鍒涘缓鏃堕棿
   }
@@ -92,12 +96,28 @@
   );
 }
 
-/** 鎻愪氦閲囪喘鍏ュ簱鍗曞鎵� */
-export function submitItemReceiptApproval(
+/**
+ * 鑾峰緱閲囪喘鍏ュ簱鍗曠殑瀹℃牳浜虹紪鍙烽泦鍚�
+ *
+ * 渚涘垪琛ㄩ〉鍒ゆ柇褰撳墠鐧诲綍鐢ㄦ埛鏄惁搴斿睍绀恒�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽紱鏈厤缃椂杩斿洖绌烘暟缁勩��
+ */
+export function getItemReceiptApproverIds() {
+  return requestClient.get<number[]>('/mes/wm/item-receipt/approver-ids');
+}
+
+/**
+ * 瀹℃牳閲囪喘鍏ュ簱鍗曪紙瀹℃壒涓啋宸插鎵�/鑽夌锛�
+ *
+ * 浠呭鎵归厤缃腑鐨勫鏍镐汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditItemReceipt(
   id: number,
-  processDefinitionKey: string,
+  pass: boolean,
+  reviewRemark?: string,
 ) {
-  return requestClient.put(
-    `/mes/wm/item-receipt/submit-approval?id=${id}&processDefinitionKey=${processDefinitionKey}`,
-  );
+  return requestClient.put('/mes/wm/item-receipt/audit', {
+    id,
+    pass,
+    reviewRemark,
+  });
 }
diff --git a/src/api/mes/wm/miscissue/index.ts b/src/api/mes/wm/miscissue/index.ts
index 7a7a8d2..39a68a6 100644
--- a/src/api/mes/wm/miscissue/index.ts
+++ b/src/api/mes/wm/miscissue/index.ts
@@ -14,7 +14,10 @@
     sourceDocCode?: string; // 鏉ユ簮鍗曟嵁缂栫爜
     issueDate?: number; // 鍑哄簱鏃ユ湡
     status?: number; // 鍗曟嵁鐘舵��
-    processInstanceId?: string; // 瀹℃壒娴佺▼瀹炰緥缂栧彿
+    reviewerId?: number; // 瀹℃牳浜虹敤鎴风紪鍙�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: number; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堥�氳繃鏃剁殑鎰忚 / 涓嶉�氳繃鏃剁殑鍘熷洜锛�
     remark?: string; // 澶囨敞
     createTime?: Date; // 鍒涘缓鏃堕棿
   }
@@ -50,21 +53,42 @@
   return requestClient.delete(`/mes/wm/misc-issue/delete?id=${id}`);
 }
 
-/** 鏌ヨ鍙敤鐨勬潅椤瑰嚭搴撳崟瀹℃壒娴佺▼瀹氫箟 */
-export function getMiscIssueApproveProcessList() {
-  return requestClient.get<{ id: string; name: string; key: string }[]>(
-    '/mes/wm/misc-issue/approval-process-list',
-  );
+/**
+ * 鎻愪氦鏉傞」鍑哄簱鍗曞鎵癸紙鑽夌 鈫� 瀹℃壒涓級
+ *
+ * 瀹℃牳浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶆寜涓氬姟绫诲瀷缁熶竴閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾锛�
+ * 鏈惎鐢ㄥ鎵规垨鏈厤缃湁鏁堝鏍镐汉鏃跺悗绔細鎶涘嚭涓氬姟寮傚父銆�
+ */
+export function submitMiscIssueApproval(id: number) {
+  return requestClient.put('/mes/wm/misc-issue/submit', null, {
+    params: { id },
+  });
 }
 
-/** 鎻愪氦鏉傞」鍑哄簱鍗曞鎵� */
-export function submitMiscIssueApproval(
+/**
+ * 鑾峰緱鏉傞」鍑哄簱鍗曠殑瀹℃牳浜虹紪鍙烽泦鍚�
+ *
+ * 渚涘垪琛ㄩ〉鍒ゆ柇褰撳墠鐧诲綍鐢ㄦ埛鏄惁搴斿睍绀恒�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽紱鏈厤缃椂杩斿洖绌烘暟缁勩��
+ */
+export function getMiscIssueApproverIds() {
+  return requestClient.get<number[]>('/mes/wm/misc-issue/approver-ids');
+}
+
+/**
+ * 瀹℃牳鏉傞」鍑哄簱鍗曪紙瀹℃壒涓� 鈫� 寰呮墽琛屽嚭搴� / 鑽夌锛�
+ *
+ * 浠呭鎵归厤缃腑鐨勫鏍镐汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditMiscIssue(
   id: number,
-  processDefinitionKey: string,
+  pass: boolean,
+  reviewRemark?: string,
 ) {
-  return requestClient.put(
-    `/mes/wm/misc-issue/submit?id=${id}&processDefinitionKey=${processDefinitionKey}`,
-  );
+  return requestClient.put('/mes/wm/misc-issue/audit', {
+    id,
+    pass,
+    reviewRemark,
+  });
 }
 
 /** 鎵ц鍑哄簱 */
diff --git a/src/api/mes/wm/miscreceipt/index.ts b/src/api/mes/wm/miscreceipt/index.ts
index ec3e2ee..c560ed1 100644
--- a/src/api/mes/wm/miscreceipt/index.ts
+++ b/src/api/mes/wm/miscreceipt/index.ts
@@ -14,7 +14,10 @@
     sourceDocCode?: string; // 鏉ユ簮鍗曟嵁缂栫爜
     receiptDate?: number; // 鍏ュ簱鏃ユ湡
     status?: number; // 鍗曟嵁鐘舵��
-    processInstanceId?: string; // 瀹℃壒娴佺▼瀹炰緥缂栧彿
+    reviewerId?: number; // 瀹℃牳浜虹敤鎴风紪鍙�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: number; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堥�氳繃鏃剁殑鎰忚 / 涓嶉�氳繃鏃剁殑鍘熷洜锛�
     remark?: string; // 澶囨敞
     createTime?: Date; // 鍒涘缓鏃堕棿
   }
@@ -50,21 +53,42 @@
   return requestClient.delete(`/mes/wm/misc-receipt/delete?id=${id}`);
 }
 
-/** 鏌ヨ鍙敤鐨勬潅椤瑰叆搴撳崟瀹℃壒娴佺▼瀹氫箟 */
-export function getMiscReceiptApproveProcessList() {
-  return requestClient.get<{ id: string; name: string; key: string }[]>(
-    '/mes/wm/misc-receipt/approval-process-list',
-  );
+/**
+ * 鎻愪氦鏉傞」鍏ュ簱鍗曞鎵癸紙鑽夌 鈫� 瀹℃壒涓級
+ *
+ * 瀹℃牳浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶆寜涓氬姟绫诲瀷缁熶竴閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾锛�
+ * 鏈惎鐢ㄥ鎵规垨鏈厤缃湁鏁堝鏍镐汉鏃跺悗绔細鎶涘嚭涓氬姟寮傚父銆�
+ */
+export function submitMiscReceiptApproval(id: number) {
+  return requestClient.put('/mes/wm/misc-receipt/submit', null, {
+    params: { id },
+  });
 }
 
-/** 鎻愪氦鏉傞」鍏ュ簱鍗曞鎵� */
-export function submitMiscReceiptApproval(
+/**
+ * 鑾峰緱鏉傞」鍏ュ簱鍗曠殑瀹℃牳浜虹紪鍙烽泦鍚�
+ *
+ * 渚涘垪琛ㄩ〉鍒ゆ柇褰撳墠鐧诲綍鐢ㄦ埛鏄惁搴斿睍绀恒�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽紱鏈厤缃椂杩斿洖绌烘暟缁勩��
+ */
+export function getMiscReceiptApproverIds() {
+  return requestClient.get<number[]>('/mes/wm/misc-receipt/approver-ids');
+}
+
+/**
+ * 瀹℃牳鏉傞」鍏ュ簱鍗曪紙瀹℃壒涓� 鈫� 宸插鎵� / 鑽夌锛�
+ *
+ * 浠呭鎵归厤缃腑鐨勫鏍镐汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditMiscReceipt(
   id: number,
-  processDefinitionKey: string,
+  pass: boolean,
+  reviewRemark?: string,
 ) {
-  return requestClient.put(
-    `/mes/wm/misc-receipt/submit?id=${id}&processDefinitionKey=${processDefinitionKey}`,
-  );
+  return requestClient.put('/mes/wm/misc-receipt/audit', {
+    id,
+    pass,
+    reviewRemark,
+  });
 }
 
 /** 鎵ц鍏ュ簱 */
diff --git a/src/api/mes/wm/returnvendor/index.ts b/src/api/mes/wm/returnvendor/index.ts
index 53d0b64..23b35c3 100644
--- a/src/api/mes/wm/returnvendor/index.ts
+++ b/src/api/mes/wm/returnvendor/index.ts
@@ -19,7 +19,10 @@
     transportCode?: string; // 杩愬崟鍙�
     transportTelephone?: string; // 鑱旂郴鐢佃瘽
     status?: number; // 鍗曟嵁鐘舵��
-    processInstanceId?: string; // 瀹℃壒娴佺▼瀹炰緥缂栧彿
+    reviewerId?: number; // 瀹℃牳浜虹敤鎴风紪鍙�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: number; // 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堥�氳繃鏃剁殑鎰忚 / 涓嶉�氳繃鏃剁殑鍘熷洜锛�
     remark?: string; // 澶囨敞
     createTime?: Date; // 鍒涘缓鏃堕棿
   }
@@ -55,21 +58,42 @@
   return requestClient.delete(`/mes/wm/return-vendor/delete?id=${id}`);
 }
 
-/** 鏌ヨ鍙敤鐨勪緵搴斿晢閫�璐у崟瀹℃壒娴佺▼瀹氫箟 */
-export function getReturnVendorApproveProcessList() {
-  return requestClient.get<{ id: string; name: string; key: string }[]>(
-    '/mes/wm/return-vendor/approval-process-list',
-  );
+/**
+ * 鎻愪氦渚涘簲鍟嗛��璐у崟瀹℃壒锛堣崏绋� 鈫� 寰呮嫞璐э級
+ *
+ * 瀹℃牳浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶆寜涓氬姟绫诲瀷缁熶竴閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾锛�
+ * 鏈惎鐢ㄥ鎵规垨鏈厤缃湁鏁堝鏍镐汉鏃跺悗绔細鎶涘嚭涓氬姟寮傚父銆�
+ */
+export function submitReturnVendorApproval(id: number) {
+  return requestClient.put('/mes/wm/return-vendor/submit', null, {
+    params: { id },
+  });
 }
 
-/** 鎻愪氦渚涘簲鍟嗛��璐у崟瀹℃壒 */
-export function submitReturnVendorApproval(
+/**
+ * 鑾峰緱渚涘簲鍟嗛��璐у崟鐨勫鏍镐汉缂栧彿闆嗗悎
+ *
+ * 渚涘垪琛ㄩ〉鍒ゆ柇褰撳墠鐧诲綍鐢ㄦ埛鏄惁搴斿睍绀恒�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽紱鏈厤缃椂杩斿洖绌烘暟缁勩��
+ */
+export function getReturnVendorApproverIds() {
+  return requestClient.get<number[]>('/mes/wm/return-vendor/approver-ids');
+}
+
+/**
+ * 瀹℃牳渚涘簲鍟嗛��璐у崟锛堝緟鎷h揣 鈫� 寰呮墽琛岄��璐� / 鑽夌锛�
+ *
+ * 浠呭鎵归厤缃腑鐨勫鏍镐汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditReturnVendor(
   id: number,
-  processDefinitionKey: string,
+  pass: boolean,
+  reviewRemark?: string,
 ) {
-  return requestClient.put(
-    `/mes/wm/return-vendor/submit?id=${id}&processDefinitionKey=${processDefinitionKey}`,
-  );
+  return requestClient.put('/mes/wm/return-vendor/audit', {
+    id,
+    pass,
+    reviewRemark,
+  });
 }
 
 /** 鎵ц鎷h揣 */
diff --git a/src/api/mes/wm/stocktaking/task/index.ts b/src/api/mes/wm/stocktaking/task/index.ts
index 985818a..a6da0b4 100644
--- a/src/api/mes/wm/stocktaking/task/index.ts
+++ b/src/api/mes/wm/stocktaking/task/index.ts
@@ -20,7 +20,10 @@
     startTime?: number; // 寮�濮嬫椂闂�
     endTime?: number; // 缁撴潫鏃堕棿
     status?: number; // 鍗曟嵁鐘舵��
-    processInstanceId?: string; // 瀹℃壒娴佺▼瀹炰緥缂栧彿
+    reviewerId?: number; // 瀹℃牳浜虹敤鎴风紪鍙�
+    reviewerName?: string; // 瀹℃牳浜哄鍚�
+    reviewTime?: number; // 缁撴灉瀹℃壒鎻愪氦鏃堕棿 / 瀹℃牳鏃堕棿
+    reviewRemark?: string; // 瀹℃牳鎰忚锛堥�氳繃鏃剁殑鎰忚 / 涓嶉�氳繃鏃剁殑鍘熷洜锛�
     remark?: string; // 澶囨敞
     createTime?: string; // 鍒涘缓鏃堕棿
   }
@@ -74,21 +77,42 @@
   });
 }
 
-/** 鏌ヨ鍙敤鐨勭洏鐐圭粨鏋滃鎵规祦绋嬪畾涔� */
-export function getStockTakingApproveProcessList() {
-  return requestClient.get<{ id: string; name: string; key: string }[]>(
-    '/mes/wm/stocktaking-task/approval-process-list',
-  );
+/**
+ * 鎻愪氦鐩樼偣缁撴灉瀹℃壒锛堢洏鐐逛腑 鈫� 鐩樼偣涓风粨鏋滃鎵逛腑锛�
+ *
+ * 瀹℃牳浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶆寜涓氬姟绫诲瀷缁熶竴閰嶇疆锛屾彁浜ゆ椂鏃犻渶鎸囧畾锛�
+ * 鏈惎鐢ㄥ鎵规垨鏈厤缃湁鏁堝鏍镐汉鏃跺悗绔細鎶涘嚭涓氬姟寮傚父銆�
+ */
+export function submitStockTakingApproval(id: number) {
+  return requestClient.put('/mes/wm/stocktaking-task/submit-approval', null, {
+    params: { id },
+  });
 }
 
-/** 鎻愪氦鐩樼偣缁撴灉瀹℃壒 */
-export function submitStockTakingApproval(
+/**
+ * 鑾峰緱鐩樼偣缁撴灉瀹℃壒鐨勫鏍镐汉缂栧彿闆嗗悎
+ *
+ * 渚涘垪琛ㄩ〉鍒ゆ柇褰撳墠鐧诲綍鐢ㄦ埛鏄惁搴斿睍绀恒�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽紱鏈厤缃椂杩斿洖绌烘暟缁勩��
+ */
+export function getStockTakingApproverIds() {
+  return requestClient.get<number[]>('/mes/wm/stocktaking-task/approver-ids');
+}
+
+/**
+ * 瀹℃牳鐩樼偣缁撴灉锛堢粨鏋滃鎵逛腑 鈫� 宸插畬鎴� / 鐩樼偣涓級
+ *
+ * 浠呭鎵归厤缃腑鐨勫鏍镐汉鍙皟鐢紙澶氫汉鏃舵垨绛撅級锛沺ass=false 鏃� reviewRemark锛堜笉閫氳繃鍘熷洜锛夊繀濉��
+ */
+export function auditStockTakingResult(
   id: number,
-  processDefinitionKey: string,
+  pass: boolean,
+  reviewRemark?: string,
 ) {
-  return requestClient.put(
-    `/mes/wm/stocktaking-task/submit-approval?id=${id}&processDefinitionKey=${processDefinitionKey}`,
-  );
+  return requestClient.put('/mes/wm/stocktaking-task/audit', {
+    id,
+    pass,
+    reviewRemark,
+  });
 }
 
 /** 瀵煎嚭鐩樼偣浠诲姟 */
diff --git a/src/packages/constants/src/biz-mes-enum.ts b/src/packages/constants/src/biz-mes-enum.ts
index f052e36..87bc8e9 100644
--- a/src/packages/constants/src/biz-mes-enum.ts
+++ b/src/packages/constants/src/biz-mes-enum.ts
@@ -649,3 +649,18 @@
   FINISHED: MesOrderStatusConstants.FINISHED,
   CANCELED: MesOrderStatusConstants.CANCELLED,
 } as const;
+
+/**
+ * MES 浜у搧鎶�鏈‘璁ゅ嚱鐘舵�佹灇涓�
+ *
+ * 鐘舵�佺敱涓氬姟娴佺▼椹卞姩锛屽墠绔笉鎻愪緵鎵嬪伐閫夋嫨锛�
+ * 鑽夌 --鎻愪氦瀹℃牳(鎸囧畾瀹℃牳浜�)--> 寰呭鏍� --瀹℃牳閫氳繃--> 宸插鏍� --瀹㈡埛纭--> 瀹㈡埛宸茬‘璁�
+ * 寰呭鏍� --瀹℃牳涓嶉�氳繃--> 瀹℃牳涓嶉�氳繃锛屽彲淇敼鍚庨噸鏂版彁浜ゃ��
+ */
+export const MesPdTechConfirmStatusEnum = {
+  DRAFT: 0, // 鑽夌锛屽彲淇敼銆佸垹闄ゃ�佹彁浜ゅ鏍�
+  PENDING_AUDIT: 1, // 寰呭鏍革紝鍗曟嵁閿佸畾锛屼粎鎸囧畾瀹℃牳浜哄彲鎿嶄綔
+  AUDITED: 2, // 宸插鏍革紝鍙鎴风‘璁�
+  CUSTOMER_CONFIRMED: 3, // 瀹㈡埛宸茬‘璁わ紝缁堟��
+  REJECTED: 4, // 瀹℃牳涓嶉�氳繃锛屽彲淇敼鍚庨噸鏂版彁浜�
+} as const;
diff --git a/src/views/crm/cancellation/modules/audit-modal.vue b/src/views/crm/cancellation/modules/audit-modal.vue
new file mode 100644
index 0000000..9ca1769
--- /dev/null
+++ b/src/views/crm/cancellation/modules/audit-modal.vue
@@ -0,0 +1,99 @@
+<script lang="ts" setup>
+  import type { VbenFormSchema } from '#/adapter/form';
+  import type { CrmCancellationApi } from '#/api/crm/cancellation';
+
+  import { computed, ref } from 'vue';
+
+  import { useVbenModal } from '@vben/common-ui';
+
+  import { message } from 'ant-design-vue';
+
+  import { useVbenForm } from '#/adapter/form';
+  import { auditCancellation } from '#/api/crm/cancellation';
+
+  const emit = defineEmits(['success']);
+
+  const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+  const rowData = ref<CrmCancellationApi.Cancellation>();
+  const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+  const [Form, formApi] = useVbenForm({
+    commonConfig: {
+      componentProps: {
+        class: 'w-full',
+      },
+      formItemClass: 'col-span-1',
+      labelWidth: 90,
+    },
+    wrapperClass: 'grid-cols-1',
+    layout: 'horizontal',
+    schema: [],
+    showDefaultActions: false,
+  });
+
+  /** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+  function useAuditSchema(): VbenFormSchema[] {
+    return [
+      {
+        fieldName: 'reviewRemark',
+        label: '瀹℃牳鎰忚',
+        component: 'Textarea',
+        formItemClass: 'col-span-1',
+        componentProps: {
+          rows: 3,
+          placeholder: pass.value
+            ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+            : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+        },
+        rules: pass.value ? '' : 'required',
+      },
+    ];
+  }
+
+  const [Modal, modalApi] = useVbenModal({
+    async onConfirm() {
+      const { valid } = await formApi.validate();
+      if (!valid) {
+        return;
+      }
+      modalApi.lock();
+      const values = await formApi.getValues();
+      try {
+        await auditCancellation(
+          rowData.value!.id!,
+          pass.value,
+          values.reviewRemark,
+        );
+        await modalApi.close();
+        emit('success');
+        message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+      } finally {
+        modalApi.unlock();
+      }
+    },
+    async onOpenChange(isOpen: boolean) {
+      if (!isOpen) {
+        rowData.value = undefined;
+        return;
+      }
+      const data = modalApi.getData<{
+        pass: boolean;
+        row: CrmCancellationApi.Cancellation;
+      }>();
+      rowData.value = data.row;
+      pass.value = data.pass;
+      formApi.setState({ schema: useAuditSchema() });
+      formApi.resetForm();
+    },
+  });
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>閿�鎴风紪鍙凤細{{ rowData.cancellationNo }}</div>
+      <div>瀹㈡埛鍚嶇О锛歿{ rowData.customerName }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/crm/contract/index.vue b/src/views/crm/contract/index.vue
index cd385ed..96704f9 100644
--- a/src/views/crm/contract/index.vue
+++ b/src/views/crm/contract/index.vue
@@ -6,35 +6,66 @@
 import { useRouter } from 'vue-router';
 
 import { Page, useVbenModal } from '@vben/common-ui';
+import { useUserStore } from '@vben/stores';
 import { downloadFileFromBlobPart } from '@vben/utils';
 
-import { Button, message, Modal, Select, Tabs, Tag, Tooltip } from 'ant-design-vue';
+import { Button, message, Tabs, Tag, Tooltip } from 'ant-design-vue';
 
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
 import {
   deleteContract,
   exportContract,
   generateSaleOrder,
-  getContractApproveProcessList,
+  getContractApproverIds,
   getContractPage,
   submitContract,
 } from '#/api/crm/contract';
 import { $t } from '#/locales';
 
 import { useGridColumns, useGridFormSchema } from './data';
+import AuditModal from './modules/audit-modal.vue';
 import Form from './modules/form.vue';
 
 const { push } = useRouter();
 const sceneType = ref('1');
+const userStore = useUserStore();
+const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鎵逛汉鏉冮檺鍒ゆ柇
+
+/** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鎵逛汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+const approverIds = ref<number[]>([]);
+
+/** 鎷夊彇瀹℃壒浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇鍒楄〃鍔犺浇 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getContractApproverIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鎵逛汉锛屼笖鍚堝悓澶勪簬瀹℃壒涓紙10锛� */
+function canAudit(row: CrmContractApi.Contract) {
+  return (
+    row.auditStatus === 10 &&
+    !!currentUserId &&
+    approverIds.value.includes(currentUserId)
+  );
+}
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
 });
 
-/** 鍒锋柊琛ㄦ牸 */
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
+  destroyOnClose: true,
+});
+
+/** 鍒锋柊琛ㄦ牸涓庡鎵逛汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
 }
 
 /** 澶勭悊鍦烘櫙绫诲瀷鐨勫垏鎹� */
@@ -78,35 +109,24 @@
   }
 }
 
-/** 鎻愪氦瀹℃壒寮圭獥 */
-const submitModalVisible = ref(false);
-const currentRow = ref<CrmContractApi.Contract>();
-const processList = ref<CrmContractApi.ApproveProcess[]>([]);
-const selectedProcessKey = ref<string>();
-const submitLoading = ref(false);
-
+/** 鎻愪氦瀹℃壒 - 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�鎸囧畾锛屾澶勬棤闇�閫夋嫨 */
 async function handleSubmit(row: CrmContractApi.Contract) {
-  const res = await getContractApproveProcessList();
-  processList.value = res;
-  currentRow.value = row;
-  selectedProcessKey.value = undefined;
-  submitModalVisible.value = true;
-}
-
-async function confirmSubmit() {
-  if (!selectedProcessKey.value) {
-    message.warning('璇烽�夋嫨瀹℃壒娴佺▼');
-    return;
-  }
-  submitLoading.value = true;
+  const hideLoading = message.loading({
+    content: '姝e湪鎻愪氦瀹℃壒...',
+    duration: 0,
+  });
   try {
-    await submitContract(currentRow.value!.id!, selectedProcessKey.value);
+    await submitContract(row.id!);
     message.success('鎻愪氦瀹℃壒鎴愬姛');
-    submitModalVisible.value = false;
     handleRefresh();
   } finally {
-    submitLoading.value = false;
+    hideLoading();
   }
+}
+
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(row: CrmContractApi.Contract, pass: boolean) {
+  auditModalApi.setData({ pass, row }).open();
 }
 
 /** 鏌ョ湅鍚堝悓璇︽儏 */
@@ -127,14 +147,6 @@
 /** 鏌ョ湅鍟嗘満璇︽儏 */
 function handleBusinessDetail(row: CrmContractApi.Contract) {
   push({ name: 'CrmBusinessDetail', params: { id: row.businessId } });
-}
-
-/** 鏌ョ湅瀹℃壒璇︽儏 */
-function handleProcessDetail(row: CrmContractApi.Contract) {
-  push({
-    name: 'BpmProcessInstanceDetail',
-    query: { id: row.processInstanceId },
-  });
 }
 
 /** 鐢熸垚閿�鍞鍗� */
@@ -200,11 +212,15 @@
     },
   } as VxeTableGridOptions<CrmContractApi.Contract>,
 });
+
+// 杩涘叆椤甸潰鍗虫媺鍙栧鎵逛汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+loadApproverIds();
 </script>
 
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
     <Grid>
       <template #toolbar-actions>
         <Tabs class="w-full" @change="handleChangeSceneType">
@@ -292,15 +308,31 @@
               label: '鎻愪氦瀹℃壒',
               type: 'link',
               auth: ['crm:contract:update'],
-              onClick: handleSubmit.bind(null, row),
-              ifShow: row.auditStatus === 0,
+              // 瀹℃壒浜虹敱瀹℃壒閰嶇疆缁熶竴鎸囧畾锛屾彁浜ゅ墠浠呬簩娆$‘璁わ紝涓嶅啀寮归�夋祦绋嬫
+              popConfirm: {
+                title: '纭鎻愪氦瀹℃壒锛熸彁浜ゅ悗鐢卞鎵归厤缃腑鎸囧畾鐨勫鎵逛汉澶勭悊銆�',
+                confirm: handleSubmit.bind(null, row),
+              },
+              // 鏈彁浜�(0) 涓� 瀹℃牳涓嶉�氳繃(30) 鍧囧彲鎻愪氦锛屽悗鑰呴渶淇敼鍚庨噸鏂版彁浜�
+              ifShow: row.auditStatus === 0 || row.auditStatus === 30,
             },
             {
-              label: '鏌ョ湅瀹℃壒',
+              label: '瀹℃牳',
               type: 'link',
-              auth: ['crm:contract:update'],
-              onClick: handleProcessDetail.bind(null, row),
-              ifShow: row.auditStatus !== 0,
+              icon: ACTION_ICON.AUDIT,
+              auth: ['crm:contract:audit'],
+              // 浠呫�屽鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃壒浜哄悕鍗曞唴銆嶆墠鏄剧ず
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              auth: ['crm:contract:audit'],
+              // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
             {
               label: '鐢熸垚閿�鍞鍗�',
@@ -332,27 +364,5 @@
       </template>
     </Grid>
 
-    <!-- 鎻愪氦瀹℃壒寮圭獥 -->
-    <Modal
-      v-model:open="submitModalVisible"
-      title="閫夋嫨瀹℃壒娴佺▼"
-      width="400px"
-      @ok="confirmSubmit"
-      :confirmLoading="submitLoading"
-    >
-      <Select
-        v-model:value="selectedProcessKey"
-        placeholder="璇烽�夋嫨瀹℃壒娴佺▼"
-        class="w-full"
-      >
-        <Select.Option
-          v-for="item in processList"
-          :key="item.key"
-          :value="item.key"
-        >
-          {{ item.name }}
-        </Select.Option>
-      </Select>
-    </Modal>
   </Page>
 </template>
diff --git a/src/views/crm/contract/modules/audit-modal.vue b/src/views/crm/contract/modules/audit-modal.vue
new file mode 100644
index 0000000..8ce56b6
--- /dev/null
+++ b/src/views/crm/contract/modules/audit-modal.vue
@@ -0,0 +1,95 @@
+<script lang="ts" setup>
+  import type { VbenFormSchema } from '#/adapter/form';
+  import type { CrmContractApi } from '#/api/crm/contract';
+
+  import { computed, ref } from 'vue';
+
+  import { useVbenModal } from '@vben/common-ui';
+
+  import { message } from 'ant-design-vue';
+
+  import { useVbenForm } from '#/adapter/form';
+  import { auditContract } from '#/api/crm/contract';
+
+  const emit = defineEmits(['success']);
+
+  const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+  const rowData = ref<CrmContractApi.Contract>();
+  const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+  const [Form, formApi] = useVbenForm({
+    commonConfig: {
+      componentProps: {
+        class: 'w-full',
+      },
+      formItemClass: 'col-span-1',
+      labelWidth: 90,
+    },
+    wrapperClass: 'grid-cols-1',
+    layout: 'horizontal',
+    schema: [],
+    showDefaultActions: false,
+  });
+
+  /** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+  function useAuditSchema(): VbenFormSchema[] {
+    return [
+      {
+        fieldName: 'reviewRemark',
+        label: '瀹℃牳鎰忚',
+        component: 'Textarea',
+        formItemClass: 'col-span-1',
+        componentProps: {
+          rows: 3,
+          placeholder: pass.value
+            ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+            : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+        },
+        rules: pass.value ? '' : 'required',
+      },
+    ];
+  }
+
+  const [Modal, modalApi] = useVbenModal({
+    async onConfirm() {
+      const { valid } = await formApi.validate();
+      if (!valid) {
+        return;
+      }
+      modalApi.lock();
+      const values = await formApi.getValues();
+      try {
+        await auditContract(rowData.value!.id!, pass.value, values.reviewRemark);
+        await modalApi.close();
+        emit('success');
+        message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+      } finally {
+        modalApi.unlock();
+      }
+    },
+    async onOpenChange(isOpen: boolean) {
+      if (!isOpen) {
+        rowData.value = undefined;
+        return;
+      }
+      const data = modalApi.getData<{
+        pass: boolean;
+        row: CrmContractApi.Contract;
+      }>();
+      rowData.value = data.row;
+      pass.value = data.pass;
+      formApi.setState({ schema: useAuditSchema() });
+      formApi.resetForm();
+    },
+  });
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>鍚堝悓鍚嶇О锛歿{ rowData.name }}</div>
+      <div>鍚堝悓缂栧彿锛歿{ rowData.no }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/crm/invoice/index.vue b/src/views/crm/invoice/index.vue
index 6adafea..6ac5964 100644
--- a/src/views/crm/invoice/index.vue
+++ b/src/views/crm/invoice/index.vue
@@ -6,6 +6,7 @@
 import { useRouter } from 'vue-router';
 
 import { Page, useVbenModal } from '@vben/common-ui';
+import { useUserStore } from '@vben/stores';
 import { downloadFileFromBlobPart } from '@vben/utils';
 
 import { Button, message, Tag, Tabs } from 'ant-design-vue';
@@ -14,25 +15,56 @@
 import {
   deleteInvoice,
   exportInvoice,
+  getInvoiceApproverIds,
   getInvoicePage,
   submitInvoice,
 } from '#/api/crm/invoice';
 import { $t } from '#/locales';
 
 import { useGridColumns, useGridFormSchema } from './data';
+import AuditModal from './modules/audit-modal.vue';
 import Form from './modules/form.vue';
 
 const { push } = useRouter();
 const sceneType = ref('1');
+const userStore = useUserStore();
+const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鎵逛汉鏉冮檺鍒ゆ柇
+
+/** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鎵逛汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+const approverIds = ref<number[]>([]);
+
+/** 鎷夊彇瀹℃壒浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇鍒楄〃鍔犺浇 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getInvoiceApproverIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鎵逛汉锛屼笖寮�绁ㄥ浜庡鎵逛腑锛�10锛� */
+function canAudit(row: CrmInvoiceApi.Invoice) {
+  return (
+    row.auditStatus === 10 &&
+    !!currentUserId &&
+    approverIds.value.includes(currentUserId)
+  );
+}
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
 });
 
-/** 鍒锋柊琛ㄦ牸 */
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
+  destroyOnClose: true,
+});
+
+/** 鍒锋柊琛ㄦ牸涓庡鎵逛汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
 }
 
 /** 澶勭悊鍦烘櫙绫诲瀷鐨勫垏鎹� */
@@ -76,19 +108,24 @@
   }
 }
 
-/** 鎻愪氦瀹℃牳 */
+/** 鎻愪氦瀹℃壒 - 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�鎸囧畾锛屾澶勬棤闇�閫夋嫨 */
 async function handleSubmit(row: CrmInvoiceApi.Invoice) {
   const hideLoading = message.loading({
-    content: '鎻愪氦瀹℃牳涓�...',
+    content: '姝e湪鎻愪氦瀹℃壒...',
     duration: 0,
   });
   try {
     await submitInvoice(row.id!);
-    message.success('鎻愪氦瀹℃牳鎴愬姛');
+    message.success('鎻愪氦瀹℃壒鎴愬姛');
     handleRefresh();
   } finally {
     hideLoading();
   }
+}
+
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(row: CrmInvoiceApi.Invoice, pass: boolean) {
+  auditModalApi.setData({ pass, row }).open();
 }
 
 /** 鏌ョ湅寮�绁ㄨ鎯� */
@@ -104,14 +141,6 @@
 /** 鏌ョ湅鍚堝悓璇︽儏 */
 function handleContractDetail(row: CrmInvoiceApi.Invoice) {
   push({ name: 'CrmContractDetail', params: { id: row.contractId } });
-}
-
-/** 鏌ョ湅瀹℃壒璇︽儏 */
-function handleProcessDetail(row: CrmInvoiceApi.Invoice) {
-  push({
-    name: 'BpmProcessInstanceDetail',
-    query: { id: row.processInstanceId },
-  });
 }
 
 const [Grid, gridApi] = useVbenVxeGrid({
@@ -144,11 +173,15 @@
     },
   } as VxeTableGridOptions<CrmInvoiceApi.Invoice>,
 });
+
+// 杩涘叆椤甸潰鍗虫媺鍙栧鎵逛汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+loadApproverIds();
 </script>
 
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
     <Grid>
       <template #toolbar-actions>
         <Tabs class="w-full" @change="handleChangeSceneType">
@@ -227,18 +260,34 @@
               onClick: handleEdit.bind(null, row),
             },
             {
-              label: '鎻愪氦瀹℃牳',
+              label: '鎻愪氦瀹℃壒',
               type: 'link',
               auth: ['crm:invoice:update'],
-              onClick: handleSubmit.bind(null, row),
-              ifShow: row.auditStatus === 0,
+              // 瀹℃壒浜虹敱瀹℃壒閰嶇疆缁熶竴鎸囧畾锛屾彁浜ゅ墠浠呬簩娆$‘璁わ紝涓嶅啀寮归�夋祦绋嬫
+              popConfirm: {
+                title: '纭鎻愪氦瀹℃壒锛熸彁浜ゅ悗鐢卞鎵归厤缃腑鎸囧畾鐨勫鎵逛汉澶勭悊銆�',
+                confirm: handleSubmit.bind(null, row),
+              },
+              // 鏈彁浜�(0) 涓� 瀹℃牳涓嶉�氳繃(30) 鍧囧彲鎻愪氦锛屽悗鑰呴渶淇敼鍚庨噸鏂版彁浜�
+              ifShow: row.auditStatus === 0 || row.auditStatus === 30,
             },
             {
-              label: '鏌ョ湅瀹℃壒',
+              label: '瀹℃牳',
               type: 'link',
-              auth: ['crm:invoice:update'],
-              onClick: handleProcessDetail.bind(null, row),
-              ifShow: row.auditStatus !== 0 && !!row.processInstanceId,
+              icon: ACTION_ICON.AUDIT,
+              auth: ['crm:invoice:audit'],
+              // 浠呫�屽鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃壒浜哄悕鍗曞唴銆嶆墠鏄剧ず
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              auth: ['crm:invoice:audit'],
+              // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
             {
               label: $t('common.delete'),
diff --git a/src/views/crm/invoice/modules/audit-modal.vue b/src/views/crm/invoice/modules/audit-modal.vue
new file mode 100644
index 0000000..afd7dfe
--- /dev/null
+++ b/src/views/crm/invoice/modules/audit-modal.vue
@@ -0,0 +1,95 @@
+<script lang="ts" setup>
+  import type { VbenFormSchema } from '#/adapter/form';
+  import type { CrmInvoiceApi } from '#/api/crm/invoice';
+
+  import { computed, ref } from 'vue';
+
+  import { useVbenModal } from '@vben/common-ui';
+
+  import { message } from 'ant-design-vue';
+
+  import { useVbenForm } from '#/adapter/form';
+  import { auditInvoice } from '#/api/crm/invoice';
+
+  const emit = defineEmits(['success']);
+
+  const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+  const rowData = ref<CrmInvoiceApi.Invoice>();
+  const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+  const [Form, formApi] = useVbenForm({
+    commonConfig: {
+      componentProps: {
+        class: 'w-full',
+      },
+      formItemClass: 'col-span-1',
+      labelWidth: 90,
+    },
+    wrapperClass: 'grid-cols-1',
+    layout: 'horizontal',
+    schema: [],
+    showDefaultActions: false,
+  });
+
+  /** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+  function useAuditSchema(): VbenFormSchema[] {
+    return [
+      {
+        fieldName: 'reviewRemark',
+        label: '瀹℃牳鎰忚',
+        component: 'Textarea',
+        formItemClass: 'col-span-1',
+        componentProps: {
+          rows: 3,
+          placeholder: pass.value
+            ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+            : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+        },
+        rules: pass.value ? '' : 'required',
+      },
+    ];
+  }
+
+  const [Modal, modalApi] = useVbenModal({
+    async onConfirm() {
+      const { valid } = await formApi.validate();
+      if (!valid) {
+        return;
+      }
+      modalApi.lock();
+      const values = await formApi.getValues();
+      try {
+        await auditInvoice(rowData.value!.id!, pass.value, values.reviewRemark);
+        await modalApi.close();
+        emit('success');
+        message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+      } finally {
+        modalApi.unlock();
+      }
+    },
+    async onOpenChange(isOpen: boolean) {
+      if (!isOpen) {
+        rowData.value = undefined;
+        return;
+      }
+      const data = modalApi.getData<{
+        pass: boolean;
+        row: CrmInvoiceApi.Invoice;
+      }>();
+      rowData.value = data.row;
+      pass.value = data.pass;
+      formApi.setState({ schema: useAuditSchema() });
+      formApi.resetForm();
+    },
+  });
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>寮�绁ㄧ紪鍙凤細{{ rowData.no }}</div>
+      <div>瀹㈡埛鍚嶇О锛歿{ rowData.customerName }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/crm/refund/modules/audit-modal.vue b/src/views/crm/refund/modules/audit-modal.vue
new file mode 100644
index 0000000..6ef0022
--- /dev/null
+++ b/src/views/crm/refund/modules/audit-modal.vue
@@ -0,0 +1,95 @@
+<script lang="ts" setup>
+  import type { VbenFormSchema } from '#/adapter/form';
+  import type { CrmRefundApi } from '#/api/crm/refund';
+
+  import { computed, ref } from 'vue';
+
+  import { useVbenModal } from '@vben/common-ui';
+
+  import { message } from 'ant-design-vue';
+
+  import { useVbenForm } from '#/adapter/form';
+  import { auditRefund } from '#/api/crm/refund';
+
+  const emit = defineEmits(['success']);
+
+  const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+  const rowData = ref<CrmRefundApi.Refund>();
+  const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+  const [Form, formApi] = useVbenForm({
+    commonConfig: {
+      componentProps: {
+        class: 'w-full',
+      },
+      formItemClass: 'col-span-1',
+      labelWidth: 90,
+    },
+    wrapperClass: 'grid-cols-1',
+    layout: 'horizontal',
+    schema: [],
+    showDefaultActions: false,
+  });
+
+  /** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+  function useAuditSchema(): VbenFormSchema[] {
+    return [
+      {
+        fieldName: 'reviewRemark',
+        label: '瀹℃牳鎰忚',
+        component: 'Textarea',
+        formItemClass: 'col-span-1',
+        componentProps: {
+          rows: 3,
+          placeholder: pass.value
+            ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+            : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+        },
+        rules: pass.value ? '' : 'required',
+      },
+    ];
+  }
+
+  const [Modal, modalApi] = useVbenModal({
+    async onConfirm() {
+      const { valid } = await formApi.validate();
+      if (!valid) {
+        return;
+      }
+      modalApi.lock();
+      const values = await formApi.getValues();
+      try {
+        await auditRefund(rowData.value!.id!, pass.value, values.reviewRemark);
+        await modalApi.close();
+        emit('success');
+        message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+      } finally {
+        modalApi.unlock();
+      }
+    },
+    async onOpenChange(isOpen: boolean) {
+      if (!isOpen) {
+        rowData.value = undefined;
+        return;
+      }
+      const data = modalApi.getData<{
+        pass: boolean;
+        row: CrmRefundApi.Refund;
+      }>();
+      rowData.value = data.row;
+      pass.value = data.pass;
+      formApi.setState({ schema: useAuditSchema() });
+      formApi.resetForm();
+    },
+  });
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>閫�璐瑰崟鍙凤細{{ rowData.refundNo }}</div>
+      <div>瀹㈡埛鍚嶇О锛歿{ rowData.customerName }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/erp/finance/payment/index.vue b/src/views/erp/finance/payment/index.vue
index 8a9658e..cf696df 100644
--- a/src/views/erp/finance/payment/index.vue
+++ b/src/views/erp/finance/payment/index.vue
@@ -2,10 +2,10 @@
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { ErpFinancePaymentApi } from '#/api/erp/finance/payment';
 
-import { ref } from 'vue';
-import { useRouter } from 'vue-router';
+import { onMounted, ref } from 'vue';
 
 import { Page, useVbenModal } from '@vben/common-ui';
+import { useUserStore } from '@vben/stores';
 import { downloadFileFromBlobPart, isEmpty } from '@vben/utils';
 
 import { message } from 'ant-design-vue';
@@ -14,33 +14,56 @@
 import {
   deleteFinancePayment,
   exportFinancePayment,
-  getFinancePaymentApproveProcessList,
+  getFinancePaymentApproverUserIds,
   getFinancePaymentPage,
+  submitFinancePayment,
 } from '#/api/erp/finance/payment';
 import { $t } from '#/locales';
 
 import { useGridColumns, useGridFormSchema } from './data';
+import AuditModal from './modules/audit-modal.vue';
 import Form from './modules/form.vue';
-import ProcessSelectModal from './modules/process-select-modal.vue';
 
 /** ERP 浠樻鍗曞垪琛� */
 defineOptions({ name: 'ErpFinancePayment' });
 
-const router = useRouter();
+const userStore = useUserStore();
+
+/** 褰撳墠涓氬姟绫诲瀷鐨勫鎵逛汉缂栧彿闆嗗悎锛岀敤浜庡垽鏂槸鍚﹀睍绀哄鏍告寜閽� */
+const approverIds = ref<number[]>([]);
+/** 褰撳墠鐧诲綍鐢ㄦ埛缂栧彿 */
+const currentUserId = userStore.userInfo?.id;
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
 });
 
-const [ProcessModal, processModalApi] = useVbenModal({
-  connectedComponent: ProcessSelectModal,
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
   destroyOnClose: true,
 });
 
-/** 鍒锋柊琛ㄦ牸 */
+/** 鍒锋柊琛ㄦ牸銆佸鎵逛汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
+}
+
+/** 鍔犺浇褰撳墠涓氬姟绫诲瀷鐨勫鎵逛汉缂栧彿闆嗗悎 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getFinancePaymentApproverUserIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐢ㄦ埛鏄惁涓鸿涓氬姟绫诲瀷鐨勫鎵逛汉锛屼笖鍗曟嵁澶勪簬瀹℃壒涓� */
+function canAudit(row: ErpFinancePaymentApi.FinancePayment) {
+  return (
+    row.status === 15 && approverIds.value.includes(currentUserId as number)
+  );
 }
 
 /** 瀵煎嚭琛ㄦ牸 */
@@ -74,36 +97,43 @@
   }
 }
 
-/** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
+/** 鎻愪氦瀹℃牳 - 鍚庣鎸夊鎵归厤缃牎楠屽鎵逛汉锛屾棤闇�鍓嶇閫夋嫨娴佺▼ */
 async function handleSubmit(row: ErpFinancePaymentApi.FinancePayment) {
   const hideLoading = message.loading({
-    content: '鑾峰彇瀹℃壒娴佺▼...',
+    content: '姝e湪鎻愪氦瀹℃牳...',
     duration: 0,
   });
   try {
-    const processList = await getFinancePaymentApproveProcessList();
-    hideLoading();
-    if (!processList || processList.length === 0) {
-      message.warning('鏆傛棤鍙敤瀹℃壒娴佺▼锛岃鍏堥厤缃� BPM 娴佺▼');
-      return;
-    }
-    // 鎵撳紑娴佺▼閫夋嫨寮圭獥
-    processModalApi.setData({ id: row.id, processList }).open();
-  } catch {
+    await submitFinancePayment(row.id!);
+    message.success('鎻愪氦鎴愬姛锛岀瓑寰呭鎵逛汉瀹℃牳');
+    handleRefresh();
+  } finally {
     hideLoading();
   }
 }
 
-/** 鏌ョ湅瀹℃壒璇︽儏 */
-function handleProcessDetail(row: ErpFinancePaymentApi.FinancePayment) {
-  if (!row.processInstanceId) {
-    message.warning('璇ヤ粯娆惧崟灏氭湭鎻愪氦瀹℃壒');
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(row: ErpFinancePaymentApi.FinancePayment, pass: boolean) {
+  auditModalApi.setData({ pass, row }).open();
+}
+
+/** 鏌ョ湅瀹℃壒璁板綍 - 杞婚噺绾у鎵逛笉鍐嶈烦杞� BPM 娴佺▼璇︽儏 */
+function handleViewProcess(row: ErpFinancePaymentApi.FinancePayment) {
+  const parts: string[] = [];
+  if (row.reviewerName) {
+    parts.push(`瀹℃牳浜猴細${row.reviewerName}`);
+  }
+  if (row.reviewTime) {
+    parts.push(`瀹℃牳鏃堕棿锛�${row.reviewTime}`);
+  }
+  if (row.reviewRemark) {
+    parts.push(`瀹℃牳鎰忚锛�${row.reviewRemark}`);
+  }
+  if (parts.length === 0) {
+    message.info('璇ヤ粯娆惧崟鏆傛棤瀹℃牳璁板綍');
     return;
   }
-  router.push({
-    name: 'BpmProcessInstanceDetail',
-    query: { id: row.processInstanceId },
-  });
+  message.info(parts.join('銆�'));
 }
 
 const checkedIds = ref<number[]>([]);
@@ -153,11 +183,15 @@
     checkboxChange: handleRowCheckboxChange,
   },
 });
+
+onMounted(() => {
+  loadApproverIds();
+});
 </script>
 
 <template>
   <Page auto-content-height><FormModal @success="handleRefresh" />
-    <ProcessModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
     <Grid table-title="浠樻鍗曞垪琛�">
       <template #toolbar-tools>
         <TableAction
@@ -221,16 +255,35 @@
               type: 'link',
               icon: ACTION_ICON.AUDIT,
               auth: ['erp:finance-payment:update'],
+              // 鏈鏍革紙鍚鏍镐笉閫氳繃閫�鍥烇級鍧囧彲鎻愪氦锛岄��鍥炲悗闇�淇敼鍐嶉噸鏂版彁浜�
               ifShow: () => row.status === 10,
               onClick: handleSubmit.bind(null, row),
+            },
+            {
+              label: '瀹℃牳',
+              type: 'link',
+              icon: ACTION_ICON.AUDIT,
+              auth: ['erp:finance-payment:audit'],
+              // 浠呫�屽鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鏄鎵逛汉銆嶆墠鏄剧ず锛屽叾浠栦汉鐪嬩笉鍒拌鎸夐挳
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              icon: ACTION_ICON.AUDIT,
+              auth: ['erp:finance-payment:audit'],
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
             {
               label: '鏌ョ湅瀹℃壒',
               type: 'link',
               icon: ACTION_ICON.VIEW,
               auth: ['erp:finance-payment:query'],
-              ifShow: () => row.status !== 10 && !!row.processInstanceId,
-              onClick: handleProcessDetail.bind(null, row),
+              ifShow: () => row.status === 20 || !!row.reviewRemark,
+              onClick: handleViewProcess.bind(null, row),
             },
             {
               label: $t('common.delete'),
diff --git a/src/views/erp/finance/payment/modules/audit-modal.vue b/src/views/erp/finance/payment/modules/audit-modal.vue
new file mode 100644
index 0000000..6edea09
--- /dev/null
+++ b/src/views/erp/finance/payment/modules/audit-modal.vue
@@ -0,0 +1,99 @@
+<script lang="ts" setup>
+  import type { VbenFormSchema } from '#/adapter/form';
+  import type { ErpFinancePaymentApi } from '#/api/erp/finance/payment';
+
+  import { computed, ref } from 'vue';
+
+  import { useVbenModal } from '@vben/common-ui';
+
+  import { message } from 'ant-design-vue';
+
+  import { useVbenForm } from '#/adapter/form';
+  import { auditFinancePayment } from '#/api/erp/finance/payment';
+
+  const emit = defineEmits(['success']);
+
+  const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+  const rowData = ref<ErpFinancePaymentApi.FinancePayment>();
+  const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+  const [Form, formApi] = useVbenForm({
+    commonConfig: {
+      componentProps: {
+        class: 'w-full',
+      },
+      formItemClass: 'col-span-1',
+      labelWidth: 130,
+    },
+    wrapperClass: 'grid-cols-1',
+    layout: 'horizontal',
+    schema: [],
+    showDefaultActions: false,
+  });
+
+  /** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+  function useAuditSchema(): VbenFormSchema[] {
+    return [
+      {
+        fieldName: 'reviewRemark',
+        label: '瀹℃牳鎰忚',
+        component: 'Textarea',
+        formItemClass: 'col-span-1',
+        componentProps: {
+          rows: 3,
+          placeholder: pass.value
+            ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+            : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+        },
+        rules: pass.value ? '' : 'required',
+      },
+    ];
+  }
+
+  const [Modal, modalApi] = useVbenModal({
+    async onConfirm() {
+      const { valid } = await formApi.validate();
+      if (!valid) {
+        return;
+      }
+      modalApi.lock();
+      const values = await formApi.getValues();
+      try {
+        await auditFinancePayment(
+          rowData.value!.id!,
+          pass.value,
+          values.reviewRemark,
+        );
+        await modalApi.close();
+        emit('success');
+        message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+      } finally {
+        modalApi.unlock();
+      }
+    },
+    async onOpenChange(isOpen: boolean) {
+      if (!isOpen) {
+        rowData.value = undefined;
+        return;
+      }
+      const data = modalApi.getData<{
+        pass: boolean;
+        row: ErpFinancePaymentApi.FinancePayment;
+      }>();
+      rowData.value = data.row;
+      pass.value = data.pass;
+      formApi.setState({ schema: useAuditSchema() });
+      formApi.resetForm();
+    },
+  });
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>浠樻鍗曞彿锛歿{ rowData.no }}</div>
+      <div>渚涘簲鍟嗭細{{ rowData.supplierName || '-' }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/erp/finance/payment/modules/process-select-modal.vue b/src/views/erp/finance/payment/modules/process-select-modal.vue
deleted file mode 100644
index 78ce174..0000000
--- a/src/views/erp/finance/payment/modules/process-select-modal.vue
+++ /dev/null
@@ -1,76 +0,0 @@
-<script lang="ts" setup>
-import { ref, computed } from 'vue';
-
-import { useVbenModal } from '@vben/common-ui';
-
-import { message, Select } from 'ant-design-vue';
-
-import { submitFinancePayment } from '#/api/erp/finance/payment';
-
-interface ProcessDefinition {
-  id: string;
-  name: string;
-  version: number;
-  key: string;
-}
-
-const emit = defineEmits(['success']);
-
-const paymentId = ref<number>();
-const processList = ref<ProcessDefinition[]>([]);
-const selectedProcessKey = ref<string>('');
-
-const processOptions = computed(() => {
-  return processList.value.map((p) => ({
-    label: `${p.name} (V${p.version})`,
-    value: p.key,
-  }));
-});
-
-const [Modal, modalApi] = useVbenModal({
-  async onConfirm() {
-    if (!selectedProcessKey.value) {
-      message.warning('璇烽�夋嫨瀹℃壒娴佺▼');
-      return;
-    }
-    modalApi.lock();
-    try {
-      await submitFinancePayment(paymentId.value!, selectedProcessKey.value);
-      await modalApi.close();
-      emit('success');
-      message.success('鎻愪氦鎴愬姛');
-    } finally {
-      modalApi.unlock();
-    }
-  },
-  async onOpenChange(isOpen: boolean) {
-    if (!isOpen) {
-      paymentId.value = undefined;
-      selectedProcessKey.value = '';
-      processList.value = [];
-      return;
-    }
-    const data = modalApi.getData<{ id: number; processList: ProcessDefinition[] }>();
-    paymentId.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: 200px"
-        placeholder="璇烽�夋嫨瀹℃壒娴佺▼"
-      />
-    </div>
-  </Modal>
-</template>
diff --git a/src/views/erp/purchase/request/index.vue b/src/views/erp/purchase/request/index.vue
index cd97ba8..ef3371b 100644
--- a/src/views/erp/purchase/request/index.vue
+++ b/src/views/erp/purchase/request/index.vue
@@ -2,10 +2,11 @@
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { ErpPurchaseRequestApi } from '#/api/erp/purchase/request';
 
-import { ref } from 'vue';
+import { onMounted, ref } from 'vue';
 import { useRouter } from 'vue-router';
 
-import { confirm, Page, useVbenModal } from '@vben/common-ui';
+import { Page, useVbenModal } from '@vben/common-ui';
+import { useUserStore } from '@vben/stores';
 import { downloadFileFromBlobPart, isEmpty } from '@vben/utils';
 
 import { message, Tag } from 'ant-design-vue';
@@ -14,21 +15,27 @@
 import {
   deletePurchaseRequest,
   exportPurchaseRequest,
-  getApproveProcessList,
+  getPurchaseRequestApproverUserIds,
   getPurchaseRequestPage,
   submitPurchaseRequest,
 } from '#/api/erp/purchase/request';
 import { $t } from '#/locales';
 
 import { useGridColumns, useGridFormSchema } from './data';
+import AuditModal from './modules/audit-modal.vue';
 import Form from './modules/form.vue';
 import OrderForm from './modules/order-form.vue';
-import ProcessSelectModal from './modules/process-select-modal.vue';
 
 /** ERP 閲囪喘鐢宠鍒楄〃 */
 defineOptions({ name: 'ErpPurchaseRequest' });
 
 const router = useRouter();
+const userStore = useUserStore();
+
+/** 褰撳墠涓氬姟绫诲瀷鐨勫鎵逛汉缂栧彿闆嗗悎锛岀敤浜庡垽鏂槸鍚﹀睍绀哄鏍告寜閽� */
+const approverIds = ref<number[]>([]);
+/** 褰撳墠鐧诲綍鐢ㄦ埛缂栧彿 */
+const currentUserId = userStore.userInfo?.id;
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
@@ -40,14 +47,31 @@
   destroyOnClose: true,
 });
 
-const [ProcessModal, processModalApi] = useVbenModal({
-  connectedComponent: ProcessSelectModal,
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
   destroyOnClose: true,
 });
 
-/** 鍒锋柊琛ㄦ牸 */
+/** 鍒锋柊琛ㄦ牸銆佸鎵逛汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
+}
+
+/** 鍔犺浇褰撳墠涓氬姟绫诲瀷鐨勫鎵逛汉缂栧彿闆嗗悎 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getPurchaseRequestApproverUserIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐢ㄦ埛鏄惁涓鸿涓氬姟绫诲瀷鐨勫鎵逛汉锛屼笖鍗曟嵁澶勪簬瀹℃壒涓� */
+function canAudit(row: ErpPurchaseRequestApi.PurchaseRequest) {
+  return (
+    row.status === 10 && approverIds.value.includes(currentUserId as number)
+  );
 }
 
 /** 瀵煎嚭琛ㄦ牸 */
@@ -81,24 +105,24 @@
   }
 }
 
-/** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
+/** 鎻愪氦瀹℃牳 - 鍚庣鎸夊鎵归厤缃牎楠屽鎵逛汉锛屾棤闇�鍓嶇閫夋嫨娴佺▼ */
 async function handleSubmit(row: ErpPurchaseRequestApi.PurchaseRequest) {
   const hideLoading = message.loading({
-    content: '鑾峰彇瀹℃壒娴佺▼...',
+    content: '姝e湪鎻愪氦瀹℃牳...',
     duration: 0,
   });
   try {
-    const processList = await getApproveProcessList(row.id!);
-    hideLoading();
-    if (!processList || processList.length === 0) {
-      message.warning('鏆傛棤鍙敤瀹℃壒娴佺▼锛岃鍏堥厤缃� BPM 娴佺▼');
-      return;
-    }
-    // 鎵撳紑娴佺▼閫夋嫨寮圭獥
-    processModalApi.setData({ id: row.id, processList }).open();
-  } catch {
+    await submitPurchaseRequest(row.id!);
+    message.success('鎻愪氦鎴愬姛锛岀瓑寰呭鎵逛汉瀹℃牳');
+    handleRefresh();
+  } finally {
     hideLoading();
   }
+}
+
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(row: ErpPurchaseRequestApi.PurchaseRequest, pass: boolean) {
+  auditModalApi.setData({ pass, row }).open();
 }
 
 /** 鐢熸垚閲囪喘璁㈠崟 */
@@ -120,13 +144,25 @@
   formModalApi.setData({ formType: 'detail', id: row.id }).open();
 }
 
-/** 鏌ョ湅瀹℃壒璇︽儏 */
+/** 鏌ョ湅瀹℃壒璁板綍 - 杞婚噺绾у鎵逛笉鍐嶈烦杞� BPM 娴佺▼璇︽儏 */
 function handleViewProcess(row: ErpPurchaseRequestApi.PurchaseRequest) {
-  if (!row.processInstanceId) {
-    message.warning('璇ョ敵璇峰皻鏈彁浜ゅ鎵�');
+  const parts: string[] = [];
+  if (row.reviewerName) {
+    parts.push(`瀹℃牳浜猴細${row.reviewerName}`);
+  }
+  if (row.reviewTime) {
+    parts.push(`瀹℃牳鏃堕棿锛�${row.reviewTime}`);
+  }
+  if (row.reviewRemark) {
+    parts.push(
+      `${row.status === 30 ? '涓嶉�氳繃鍘熷洜' : '瀹℃牳鎰忚'}锛�${row.reviewRemark}`,
+    );
+  }
+  if (parts.length === 0) {
+    message.info('璇ョ敵璇锋殏鏃犲鏍歌褰�');
     return;
   }
-  router.push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } });
+  message.info(parts.join('銆�'));
 }
 
 /** 鏌ョ湅閲囪喘璁㈠崟 */
@@ -167,13 +203,17 @@
     checkboxChange: handleRowCheckboxChange,
   },
 });
+
+onMounted(() => {
+  loadApproverIds();
+});
 </script>
 
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
     <OrderFormModal @success="handleRefresh" />
-    <ProcessModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
     <Grid table-title="閲囪喘鐢宠鍒楄〃">
       <template #toolbar-tools>
         <TableAction
@@ -240,8 +280,28 @@
             {
               label: '鎻愪氦',
               type: 'link',
+              auth: ['erp:purchase-request:submit'],
+              // 鏈彁浜や笌瀹℃牳涓嶉�氳繃鍧囧彲鎻愪氦锛屽鏍镐笉閫氳繃闇�淇敼鍚庨噸鏂版彁浜�
               ifShow: () => row.status === 0 || row.status === 30,
               onClick: handleSubmit.bind(null, row),
+            },
+            {
+              label: '瀹℃牳',
+              type: 'link',
+              icon: ACTION_ICON.AUDIT,
+              auth: ['erp:purchase-request:audit'],
+              // 浠呫�屽鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鏄鎵逛汉銆嶆墠鏄剧ず锛屽叾浠栦汉鐪嬩笉鍒拌鎸夐挳
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              icon: ACTION_ICON.AUDIT,
+              auth: ['erp:purchase-request:audit'],
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
             {
               label: '鐢熸垚璁㈠崟',
@@ -253,7 +313,7 @@
             {
               label: '鏌ョ湅瀹℃壒',
               type: 'link',
-              ifShow: () => row.status !== 0,
+              ifShow: () => row.status === 20 || row.status === 30,
               onClick: handleViewProcess.bind(null, row),
             },
             {
diff --git a/src/views/erp/purchase/request/modules/audit-modal.vue b/src/views/erp/purchase/request/modules/audit-modal.vue
new file mode 100644
index 0000000..82e3fa0
--- /dev/null
+++ b/src/views/erp/purchase/request/modules/audit-modal.vue
@@ -0,0 +1,99 @@
+<script lang="ts" setup>
+  import type { VbenFormSchema } from '#/adapter/form';
+  import type { ErpPurchaseRequestApi } from '#/api/erp/purchase/request';
+
+  import { computed, ref } from 'vue';
+
+  import { useVbenModal } from '@vben/common-ui';
+
+  import { message } from 'ant-design-vue';
+
+  import { useVbenForm } from '#/adapter/form';
+  import { auditPurchaseRequest } from '#/api/erp/purchase/request';
+
+  const emit = defineEmits(['success']);
+
+  const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+  const rowData = ref<ErpPurchaseRequestApi.PurchaseRequest>();
+  const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+  const [Form, formApi] = useVbenForm({
+    commonConfig: {
+      componentProps: {
+        class: 'w-full',
+      },
+      formItemClass: 'col-span-1',
+      labelWidth: 130,
+    },
+    wrapperClass: 'grid-cols-1',
+    layout: 'horizontal',
+    schema: [],
+    showDefaultActions: false,
+  });
+
+  /** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+  function useAuditSchema(): VbenFormSchema[] {
+    return [
+      {
+        fieldName: 'reviewRemark',
+        label: '瀹℃牳鎰忚',
+        component: 'Textarea',
+        formItemClass: 'col-span-1',
+        componentProps: {
+          rows: 3,
+          placeholder: pass.value
+            ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+            : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+        },
+        rules: pass.value ? '' : 'required',
+      },
+    ];
+  }
+
+  const [Modal, modalApi] = useVbenModal({
+    async onConfirm() {
+      const { valid } = await formApi.validate();
+      if (!valid) {
+        return;
+      }
+      modalApi.lock();
+      const values = await formApi.getValues();
+      try {
+        await auditPurchaseRequest(
+          rowData.value!.id!,
+          pass.value,
+          values.reviewRemark,
+        );
+        await modalApi.close();
+        emit('success');
+        message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+      } finally {
+        modalApi.unlock();
+      }
+    },
+    async onOpenChange(isOpen: boolean) {
+      if (!isOpen) {
+        rowData.value = undefined;
+        return;
+      }
+      const data = modalApi.getData<{
+        pass: boolean;
+        row: ErpPurchaseRequestApi.PurchaseRequest;
+      }>();
+      rowData.value = data.row;
+      pass.value = data.pass;
+      formApi.setState({ schema: useAuditSchema() });
+      formApi.resetForm();
+    },
+  });
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>鐢宠鍗曞彿锛歿{ rowData.no }}</div>
+      <div>鐢宠浜猴細{{ rowData.requestUserName || '-' }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/erp/purchase/request/modules/process-select-modal.vue b/src/views/erp/purchase/request/modules/process-select-modal.vue
deleted file mode 100644
index 2204441..0000000
--- a/src/views/erp/purchase/request/modules/process-select-modal.vue
+++ /dev/null
@@ -1,78 +0,0 @@
-<script lang="ts" setup>
-import type { ErpPurchaseRequestApi } from '#/api/erp/purchase/request';
-
-import { ref, computed } from 'vue';
-
-import { useVbenModal } from '@vben/common-ui';
-
-import { message, Select } from 'ant-design-vue';
-
-import { submitPurchaseRequest } from '#/api/erp/purchase/request';
-
-interface ProcessDefinition {
-  id: string;
-  name: string;
-  version: number;
-  key: string;
-}
-
-const emit = defineEmits(['success']);
-
-const requestId = ref<number>();
-const processList = ref<ProcessDefinition[]>([]);
-const selectedProcessKey = ref<string>('');
-
-const processOptions = computed(() => {
-  return processList.value.map((p) => ({
-    label: `${p.name} (V${p.version})`,
-    value: p.key,
-  }));
-});
-
-const [Modal, modalApi] = useVbenModal({
-  async onConfirm() {
-    if (!selectedProcessKey.value) {
-      message.warning('璇烽�夋嫨瀹℃壒娴佺▼');
-      return;
-    }
-    modalApi.lock();
-    try {
-      await submitPurchaseRequest(requestId.value!, selectedProcessKey.value);
-      await modalApi.close();
-      emit('success');
-      message.success('鎻愪氦鎴愬姛');
-    } finally {
-      modalApi.unlock();
-    }
-  },
-  async onOpenChange(isOpen: boolean) {
-    if (!isOpen) {
-      requestId.value = undefined;
-      selectedProcessKey.value = '';
-      processList.value = [];
-      return;
-    }
-    const data = modalApi.getData<{ id: number; processList: ProcessDefinition[] }>();
-    requestId.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: 200px"
-        placeholder="璇烽�夋嫨瀹℃壒娴佺▼"
-      />
-    </div>
-  </Modal>
-</template>
\ No newline at end of file
diff --git a/src/views/hrm/attendance/exception/index.vue b/src/views/hrm/attendance/exception/index.vue
index acd5d7a..bba52f3 100644
--- a/src/views/hrm/attendance/exception/index.vue
+++ b/src/views/hrm/attendance/exception/index.vue
@@ -5,28 +5,60 @@
 import { ref } from 'vue';
 
 import { Page, useVbenModal } from '#/packages/effects/common-ui/src';
+import { useUserStore } from '@vben/stores';
 
-import { message, Modal, Select } from 'ant-design-vue';
+import { message } from 'ant-design-vue';
 
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
 import {
-  getAttendanceExceptionApproveProcessList,
+  getAttendanceExceptionApproverIds,
   getAttendanceExceptionPage,
   submitAttendanceException,
 } from '#/api/hrm/attendance/exception';
 import { $t } from '#/locales';
 
 import { useGridColumns, useGridFormSchema } from './data';
+import AuditModal from './modules/audit-modal.vue';
 import Form from './modules/form.vue';
+
+const userStore = useUserStore();
+const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鎵逛汉鏉冮檺鍒ゆ柇
+
+/** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鎵逛汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+const approverIds = ref<number[]>([]);
+
+/** 鎷夊彇瀹℃壒浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇鍒楄〃鍔犺浇 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getAttendanceExceptionApproverIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鎵逛汉锛屼笖鐢宠澶勪簬瀹℃壒涓紙10锛� */
+function canAudit(row: HrmAttendanceExceptionApi.AttendanceException) {
+  return (
+    row.status === 10 &&
+    !!currentUserId &&
+    approverIds.value.includes(currentUserId)
+  );
+}
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
 });
 
-/** 鍒锋柊琛ㄦ牸 */
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
+  destroyOnClose: true,
+});
+
+/** 鍒锋柊琛ㄦ牸涓庡鎵逛汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
 }
 
 /** 鍒涘缓鑰冨嫟寮傚父鐢宠 */
@@ -39,37 +71,27 @@
   formModalApi.setData({ id: row.id, formType: 'detail' }).open();
 }
 
-/** 鎻愪氦瀹℃壒寮圭獥 */
-const submitModalVisible = ref(false);
-const currentRow = ref<HrmAttendanceExceptionApi.AttendanceException>();
-const processList = ref<HrmAttendanceExceptionApi.ApproveProcess[]>([]);
-const selectedProcessKey = ref<string>();
-const submitLoading = ref(false);
-
-/** 鎻愪氦鑰冨嫟寮傚父鐢宠 */
+/** 鎻愪氦瀹℃壒 - 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�鎸囧畾锛屾澶勬棤闇�閫夋嫨 */
 async function handleSubmit(row: HrmAttendanceExceptionApi.AttendanceException) {
-  const res = await getAttendanceExceptionApproveProcessList();
-  processList.value = res;
-  currentRow.value = row;
-  selectedProcessKey.value = undefined;
-  submitModalVisible.value = true;
-}
-
-/** 纭鎻愪氦 */
-async function confirmSubmit() {
-  if (!selectedProcessKey.value) {
-    message.warning('璇烽�夋嫨瀹℃壒娴佺▼');
-    return;
-  }
-  submitLoading.value = true;
+  const hideLoading = message.loading({
+    content: '姝e湪鎻愪氦瀹℃壒...',
+    duration: 0,
+  });
   try {
-    await submitAttendanceException(currentRow.value!.id!, selectedProcessKey.value);
+    await submitAttendanceException(row.id!);
     message.success('鎻愪氦瀹℃壒鎴愬姛');
-    submitModalVisible.value = false;
     handleRefresh();
   } finally {
-    submitLoading.value = false;
+    hideLoading();
   }
+}
+
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(
+  row: HrmAttendanceExceptionApi.AttendanceException,
+  pass: boolean,
+) {
+  auditModalApi.setData({ pass, row }).open();
 }
 
 const [Grid, gridApi] = useVbenVxeGrid({
@@ -100,11 +122,15 @@
     },
   } as VxeTableGridOptions<HrmAttendanceExceptionApi.AttendanceException>,
 });
+
+// 杩涘叆椤甸潰鍗虫媺鍙栧鎵逛汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+loadApproverIds();
 </script>
 
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
     <Grid table-title="鑰冨嫟寮傚父鐢宠鍒楄〃">
       <template #toolbar-tools>
         <TableAction
@@ -128,38 +154,38 @@
               onClick: handleDetail.bind(null, row),
             },
             {
-              label: '鎻愪氦',
+              label: '鎻愪氦瀹℃壒',
               type: 'link',
               auth: ['hrm:attendance-exception:submit'],
-              ifShow: row.status === 0,
-              onClick: handleSubmit.bind(null, row),
+              // 瀹℃壒浜虹敱瀹℃壒閰嶇疆缁熶竴鎸囧畾锛屾彁浜ゅ墠浠呬簩娆$‘璁わ紝涓嶅啀寮归�夋祦绋嬫
+              popConfirm: {
+                title: '纭鎻愪氦瀹℃壒锛熸彁浜ゅ悗鐢卞鎵归厤缃腑鎸囧畾鐨勫鎵逛汉澶勭悊銆�',
+                confirm: handleSubmit.bind(null, row),
+              },
+              // 鑽夌(0) 涓� 瀹℃牳涓嶉�氳繃(30) 鍧囧彲鎻愪氦锛屽悗鑰呴渶淇敼鍚庨噸鏂版彁浜�
+              ifShow: row.status === 0 || row.status === 30,
+            },
+            {
+              label: '瀹℃牳',
+              type: 'link',
+              icon: ACTION_ICON.AUDIT,
+              auth: ['hrm:attendance-exception:audit'],
+              // 浠呫�屽鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃壒浜哄悕鍗曞唴銆嶆墠鏄剧ず
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              auth: ['hrm:attendance-exception:audit'],
+              // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
           ]"
         />
       </template>
     </Grid>
-
-    <!-- 鎻愪氦瀹℃壒寮圭獥 -->
-    <Modal
-      v-model:open="submitModalVisible"
-      title="閫夋嫨瀹℃壒娴佺▼"
-      width="400px"
-      @ok="confirmSubmit"
-      :confirmLoading="submitLoading"
-    >
-      <Select
-        v-model:value="selectedProcessKey"
-        placeholder="璇烽�夋嫨瀹℃壒娴佺▼"
-        class="w-full"
-      >
-        <Select.Option
-          v-for="item in processList"
-          :key="item.key"
-          :value="item.key"
-        >
-          {{ item.name }}
-        </Select.Option>
-      </Select>
-    </Modal>
   </Page>
 </template>
\ No newline at end of file
diff --git a/src/views/hrm/attendance/exception/modules/audit-modal.vue b/src/views/hrm/attendance/exception/modules/audit-modal.vue
new file mode 100644
index 0000000..022ee7e
--- /dev/null
+++ b/src/views/hrm/attendance/exception/modules/audit-modal.vue
@@ -0,0 +1,102 @@
+<script lang="ts" setup>
+import type { VbenFormSchema } from '#/adapter/form';
+import type { HrmAttendanceExceptionApi } from '#/api/hrm/attendance/exception';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '#/packages/effects/common-ui/src';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import { auditAttendanceException } from '#/api/hrm/attendance/exception';
+
+const emit = defineEmits(['success']);
+
+const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+const rowData = ref<HrmAttendanceExceptionApi.AttendanceException>();
+const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: {
+      class: 'w-full',
+    },
+    formItemClass: 'col-span-1',
+    labelWidth: 90,
+  },
+  wrapperClass: 'grid-cols-1',
+  layout: 'horizontal',
+  schema: [],
+  showDefaultActions: false,
+});
+
+/** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+function useAuditSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'reviewRemark',
+      label: '瀹℃牳鎰忚',
+      component: 'Textarea',
+      formItemClass: 'col-span-1',
+      componentProps: {
+        rows: 3,
+        placeholder: pass.value
+          ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+          : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+      },
+      rules: pass.value ? '' : 'required',
+    },
+  ];
+}
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const values = await formApi.getValues();
+    try {
+      await auditAttendanceException(
+        rowData.value!.id!,
+        pass.value,
+        values.reviewRemark,
+      );
+      await modalApi.close();
+      emit('success');
+      message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      rowData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<{
+      pass: boolean;
+      row: HrmAttendanceExceptionApi.AttendanceException;
+    }>();
+    rowData.value = data.row;
+    pass.value = data.pass;
+    formApi.setState({ schema: useAuditSchema() });
+    formApi.resetForm();
+  },
+});
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>鐢宠浜猴細{{ rowData.userName }}</div>
+      <div>鎵�灞為儴闂細{{ rowData.deptName }}</div>
+      <div>寮傚父鏃ユ湡锛歿{ rowData.exceptionDate }}</div>
+      <div>寮傚父绫诲瀷锛歿{ rowData.exceptionTypeName }}</div>
+      <div>鐢宠鍘熷洜锛歿{ rowData.reason }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/hrm/leave/index.vue b/src/views/hrm/leave/index.vue
index e7ae028..89e6342 100644
--- a/src/views/hrm/leave/index.vue
+++ b/src/views/hrm/leave/index.vue
@@ -2,7 +2,10 @@
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { HrmLeaveApi } from '#/api/hrm/leave';
 
+import { ref } from 'vue';
+
 import { Page, useVbenModal } from '#/packages/effects/common-ui/src';
+import { useUserStore } from '@vben/stores';
 
 import { message, Tag } from 'ant-design-vue';
 
@@ -13,22 +16,54 @@
 import {
   cancelLeave,
   deleteLeave,
+  getLeaveApproverIds,
   getLeavePage,
   submitLeave,
 } from '#/api/hrm/leave';
 import { $t } from '#/locales';
 
 import { useGridColumns, useGridFormSchema } from './data';
+import AuditModal from './modules/audit-modal.vue';
 import Form from './modules/form.vue';
+
+const userStore = useUserStore();
+const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鎵逛汉鏉冮檺鍒ゆ柇
+
+/** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鎵逛汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+const approverIds = ref<number[]>([]);
+
+/** 鎷夊彇瀹℃壒浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇鍒楄〃鍔犺浇 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getLeaveApproverIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鎵逛汉锛屼笖鐢宠澶勪簬瀹℃壒涓紙10锛� */
+function canAudit(row: HrmLeaveApi.Leave) {
+  return (
+    row.status === 10 &&
+    !!currentUserId &&
+    approverIds.value.includes(currentUserId)
+  );
+}
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
 });
 
-/** 鍒锋柊琛ㄦ牸 */
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
+  destroyOnClose: true,
+});
+
+/** 鍒锋柊琛ㄦ牸涓庡鎵逛汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
 }
 
 /** 鍒涘缓璇峰亣鐢宠 */
@@ -61,11 +96,24 @@
   }
 }
 
-/** 鎻愪氦璇峰亣鐢宠 */
+/** 鎻愪氦瀹℃壒 - 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�鎸囧畾锛屾澶勬棤闇�閫夋嫨 */
 async function handleSubmit(row: HrmLeaveApi.Leave) {
-  await submitLeave(row.id!);
-  message.success('鎻愪氦鎴愬姛');
-  handleRefresh();
+  const hideLoading = message.loading({
+    content: '姝e湪鎻愪氦瀹℃壒...',
+    duration: 0,
+  });
+  try {
+    await submitLeave(row.id!);
+    message.success('鎻愪氦瀹℃壒鎴愬姛');
+    handleRefresh();
+  } finally {
+    hideLoading();
+  }
+}
+
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(row: HrmLeaveApi.Leave, pass: boolean) {
+  auditModalApi.setData({ pass, row }).open();
 }
 
 /** 鎾ら攢璇峰亣鐢宠 */
@@ -117,11 +165,15 @@
     },
   } as VxeTableGridOptions<HrmLeaveApi.Leave>,
 });
+
+// 杩涘叆椤甸潰鍗虫媺鍙栧鎵逛汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+loadApproverIds();
 </script>
 
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
     <Grid table-title="璇峰亣鐢宠鍒楄〃">
       <template #toolbar-tools>
         <TableAction
@@ -158,11 +210,34 @@
               onClick: handleEdit.bind(null, row),
             },
             {
-              label: '鎻愪氦',
+              label: '鎻愪氦瀹℃壒',
               type: 'link',
-              auth: ['hrm:leave:submit'],
-              ifShow: row.status === 0,
-              onClick: handleSubmit.bind(null, row),
+              auth: ['hrm:leave:update'],
+              // 瀹℃壒浜虹敱瀹℃壒閰嶇疆缁熶竴鎸囧畾锛屾彁浜ゅ墠浠呬簩娆$‘璁わ紝涓嶅啀寮归�夋祦绋嬫
+              popConfirm: {
+                title: '纭鎻愪氦瀹℃壒锛熸彁浜ゅ悗鐢卞鎵归厤缃腑鎸囧畾鐨勫鎵逛汉澶勭悊銆�',
+                confirm: handleSubmit.bind(null, row),
+              },
+              // 鑽夌(0) 涓� 瀹℃牳涓嶉�氳繃(30) 鍧囧彲鎻愪氦锛屽悗鑰呴渶淇敼鍚庨噸鏂版彁浜�
+              ifShow: row.status === 0 || row.status === 30,
+            },
+            {
+              label: '瀹℃牳',
+              type: 'link',
+              icon: ACTION_ICON.AUDIT,
+              auth: ['hrm:leave:audit'],
+              // 浠呫�屽鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃壒浜哄悕鍗曞唴銆嶆墠鏄剧ず
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              auth: ['hrm:leave:audit'],
+              // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
             {
               label: '鎾ら攢',
diff --git a/src/views/hrm/leave/modules/audit-modal.vue b/src/views/hrm/leave/modules/audit-modal.vue
new file mode 100644
index 0000000..5fca175
--- /dev/null
+++ b/src/views/hrm/leave/modules/audit-modal.vue
@@ -0,0 +1,98 @@
+<script lang="ts" setup>
+import type { VbenFormSchema } from '#/adapter/form';
+import type { HrmLeaveApi } from '#/api/hrm/leave';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '#/packages/effects/common-ui/src';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import { auditLeave } from '#/api/hrm/leave';
+
+const emit = defineEmits(['success']);
+
+const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+const rowData = ref<HrmLeaveApi.Leave>();
+const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: {
+      class: 'w-full',
+    },
+    formItemClass: 'col-span-1',
+    labelWidth: 90,
+  },
+  wrapperClass: 'grid-cols-1',
+  layout: 'horizontal',
+  schema: [],
+  showDefaultActions: false,
+});
+
+/** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+function useAuditSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'reviewRemark',
+      label: '瀹℃牳鎰忚',
+      component: 'Textarea',
+      formItemClass: 'col-span-1',
+      componentProps: {
+        rows: 3,
+        placeholder: pass.value
+          ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+          : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+      },
+      rules: pass.value ? '' : 'required',
+    },
+  ];
+}
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const values = await formApi.getValues();
+    try {
+      await auditLeave(rowData.value!.id!, pass.value, values.reviewRemark);
+      await modalApi.close();
+      emit('success');
+      message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      rowData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<{
+      pass: boolean;
+      row: HrmLeaveApi.Leave;
+    }>();
+    rowData.value = data.row;
+    pass.value = data.pass;
+    formApi.setState({ schema: useAuditSchema() });
+    formApi.resetForm();
+  },
+});
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>鐢宠浜猴細{{ rowData.employeeName }}</div>
+      <div>鎵�灞為儴闂細{{ rowData.deptName }}</div>
+      <div>寮�濮嬫椂闂达細{{ rowData.startTime }}</div>
+      <div>缁撴潫鏃堕棿锛歿{ rowData.endTime }}</div>
+      <div>璇峰亣鏃堕暱锛歿{ rowData.duration }} 澶�</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/hrm/performance-appraise/index.vue b/src/views/hrm/performance-appraise/index.vue
index c6f1597..ee13608 100644
--- a/src/views/hrm/performance-appraise/index.vue
+++ b/src/views/hrm/performance-appraise/index.vue
@@ -3,9 +3,9 @@
 import type { HrmPerformanceAppraiseApi } from '#/api/hrm/performance-appraise';
 
 import { ref } from 'vue';
-import { useRouter } from 'vue-router';
 
 import { Page, useVbenModal } from '@vben/common-ui';
+import { useUserStore } from '@vben/stores';
 import { isEmpty } from '@vben/utils';
 
 import { message, Tag } from 'ant-design-vue';
@@ -13,8 +13,9 @@
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
 import {
   deleteAppraise,
+  getAppraiseApproverIds,
   getAppraisePage,
-  getAppraiseProcessList,
+  submitAppraise,
 } from '#/api/hrm/performance-appraise';
 import { $t } from '#/locales';
 
@@ -22,14 +23,35 @@
 import AuditModal from './modules/audit-modal.vue';
 import Form from './modules/form.vue';
 import ItemModal from './modules/item.vue';
-import ProcessSelectModal from './modules/process-select-modal.vue';
 import ResultModal from './modules/result.vue';
 import ScoreModal from './modules/score.vue';
 
 /** HRM 缁╂晥鑰冩牳鏂规鍒楄〃 */
 defineOptions({ name: 'HrmPerformanceAppraise' });
 
-const router = useRouter();
+const userStore = useUserStore();
+const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鎵逛汉鏉冮檺鍒ゆ柇
+
+/** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鎵逛汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+const approverIds = ref<number[]>([]);
+
+/** 鎷夊彇瀹℃壒浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇鍒楄〃鍔犺浇 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getAppraiseApproverIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鎵逛汉锛屼笖鏂规澶勪簬瀹℃壒涓紙10锛� */
+function canAudit(row: HrmPerformanceAppraiseApi.Appraise) {
+  return (
+    row.auditStatus === AUDIT_STATUS.PROCESS &&
+    !!currentUserId &&
+    approverIds.value.includes(currentUserId)
+  );
+}
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
@@ -51,18 +73,15 @@
   destroyOnClose: true,
 });
 
-const [ProcessModal, processModalApi] = useVbenModal({
-  connectedComponent: ProcessSelectModal,
-  destroyOnClose: true,
-});
-
 const [AuditFormModal, auditModalApi] = useVbenModal({
   connectedComponent: AuditModal,
   destroyOnClose: true,
 });
 
+/** 鍒锋柊琛ㄦ牸涓庡鎵逛汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
 }
 
 function handleCreate() {
@@ -103,40 +122,24 @@
   }
 }
 
-/** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
+/** 鎻愪氦瀹℃壒 - 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�鎸囧畾锛屾澶勬棤闇�閫夋嫨 */
 async function handleSubmit(row: HrmPerformanceAppraiseApi.Appraise) {
   const hideLoading = message.loading({
-    content: '鑾峰彇瀹℃壒娴佺▼...',
+    content: '姝e湪鎻愪氦瀹℃壒...',
     duration: 0,
   });
   try {
-    const processList = await getAppraiseProcessList();
-    hideLoading();
-    if (!processList || processList.length === 0) {
-      message.warning('鏆傛棤鍙敤瀹℃壒娴佺▼锛岃鍏堥厤缃� BPM 娴佺▼');
-      return;
-    }
-    processModalApi.setData({ id: row.id, processList }).open();
-  } catch {
+    await submitAppraise(row.id!);
+    message.success('鎻愪氦瀹℃壒鎴愬姛');
+    handleRefresh();
+  } finally {
     hideLoading();
   }
 }
 
-/** 瀹℃壒锛堥�氳繃/椹冲洖锛� */
-function handleAudit(row: HrmPerformanceAppraiseApi.Appraise) {
-  auditModalApi.setData({ id: row.id }).open();
-}
-
-/** 鏌ョ湅瀹℃壒璇︽儏 */
-function handleViewProcess(row: HrmPerformanceAppraiseApi.Appraise) {
-  if (!row.processInstanceId) {
-    message.warning('璇ユ柟妗堝皻鏈彁浜ゅ鎵�');
-    return;
-  }
-  router.push({
-    name: 'BpmProcessInstanceDetail',
-    query: { id: row.processInstanceId },
-  });
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(row: HrmPerformanceAppraiseApi.Appraise, pass: boolean) {
+  auditModalApi.setData({ pass, row }).open();
 }
 
 const checkedIds = ref<number[]>([]);
@@ -181,6 +184,9 @@
     checkboxChange: handleRowCheckboxChange,
   },
 });
+
+// 杩涘叆椤甸潰鍗虫媺鍙栧鎵逛汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+loadApproverIds();
 </script>
 
 <template>
@@ -189,7 +195,6 @@
     <ItemFormModal @success="handleRefresh" />
     <ScoreFormModal @success="handleRefresh" />
     <ResultFormModal @success="handleRefresh" />
-    <ProcessModal @success="handleRefresh" />
     <AuditFormModal @success="handleRefresh" />
     <Grid table-title="缁╂晥鑰冩牳鏂规鍒楄〃">
       <template #toolbar-tools>
@@ -267,22 +272,32 @@
             {
               label: '鎻愪氦瀹℃壒',
               type: 'link',
-              auth: ['hrm:performance-appraise:submit'],
+              auth: ['hrm:performance-appraise:update'],
+              // 瀹℃壒浜虹敱瀹℃壒閰嶇疆缁熶竴鎸囧畾锛屾彁浜ゅ墠浠呬簩娆$‘璁わ紝涓嶅啀寮归�夋祦绋嬫
+              popConfirm: {
+                title: '纭鎻愪氦瀹℃壒锛熸彁浜ゅ悗鐢卞鎵归厤缃腑鎸囧畾鐨勫鎵逛汉澶勭悊銆�',
+                confirm: handleSubmit.bind(null, row),
+              },
+              // 鑽夌(0) 涓� 瀹℃牳涓嶉�氳繃(30) 鍧囧彲鎻愪氦锛屽悗鑰呴渶淇敼鍚庨噸鏂版彁浜�
               ifShow: row.auditStatus === AUDIT_STATUS.DRAFT || row.auditStatus === AUDIT_STATUS.REJECT,
-              onClick: handleSubmit.bind(null, row),
             },
             {
-              label: '瀹℃壒',
+              label: '瀹℃牳',
               type: 'link',
+              icon: ACTION_ICON.AUDIT,
               auth: ['hrm:performance-appraise:audit'],
-              ifShow: row.auditStatus === AUDIT_STATUS.PROCESS,
-              onClick: handleAudit.bind(null, row),
+              // 浠呫�屽鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃壒浜哄悕鍗曞唴銆嶆墠鏄剧ず
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
             },
             {
-              label: '鏌ョ湅瀹℃壒',
+              label: '瀹℃牳涓嶉�氳繃',
               type: 'link',
-              ifShow: row.auditStatus !== AUDIT_STATUS.DRAFT && !!row.processInstanceId,
-              onClick: handleViewProcess.bind(null, row),
+              danger: true,
+              auth: ['hrm:performance-appraise:audit'],
+              // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
             {
               label: $t('common.delete'),
diff --git a/src/views/hrm/performance-appraise/modules/audit-modal.vue b/src/views/hrm/performance-appraise/modules/audit-modal.vue
index 9bc18f5..30a2f0f 100644
--- a/src/views/hrm/performance-appraise/modules/audit-modal.vue
+++ b/src/views/hrm/performance-appraise/modules/audit-modal.vue
@@ -1,5 +1,8 @@
 <script lang="ts" setup>
-import { ref } from 'vue';
+import type { VbenFormSchema } from '#/adapter/form';
+import type { HrmPerformanceAppraiseApi } from '#/api/hrm/performance-appraise';
+
+import { computed, ref } from 'vue';
 
 import { useVbenModal } from '@vben/common-ui';
 
@@ -10,42 +13,42 @@
 
 const emit = defineEmits(['success']);
 
-const appraiseId = ref<number>();
+const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+const rowData = ref<HrmPerformanceAppraiseApi.Appraise>();
+const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
 
 const [Form, formApi] = useVbenForm({
   commonConfig: {
     componentProps: {
       class: 'w-full',
     },
-    labelWidth: 100,
+    formItemClass: 'col-span-1',
+    labelWidth: 90,
   },
   wrapperClass: 'grid-cols-1',
-  layout: 'vertical',
-  schema: [
-    {
-      fieldName: 'auditStatus',
-      label: '瀹℃壒缁撴灉',
-      component: 'RadioGroup',
-      componentProps: {
-        options: [
-          { label: '閫氳繃', value: 20 },
-          { label: '椹冲洖', value: 30 },
-        ],
-      },
-      rules: 'selectRequired',
-    },
-    {
-      fieldName: 'remark',
-      label: '瀹℃壒澶囨敞',
-      component: 'Textarea',
-      componentProps: {
-        placeholder: '璇疯緭鍏ュ鎵瑰娉�',
-        rows: 3,
-      },
-    },
-  ],
+  layout: 'horizontal',
+  schema: [],
   showDefaultActions: false,
 });
+
+/** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+function useAuditSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'reviewRemark',
+      label: '瀹℃牳鎰忚',
+      component: 'Textarea',
+      formItemClass: 'col-span-1',
+      componentProps: {
+        rows: 3,
+        placeholder: pass.value
+          ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+          : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+      },
+      rules: pass.value ? '' : 'required',
+    },
+  ];
+}
 
 const [Modal, modalApi] = useVbenModal({
   async onConfirm() {
@@ -54,38 +57,40 @@
       return;
     }
     modalApi.lock();
+    const values = await formApi.getValues();
     try {
-      const values = await formApi.getValues<{
-        auditStatus: number;
-        remark?: string;
-      }>();
-      await auditAppraise({
-        id: appraiseId.value!,
-        auditStatus: values.auditStatus,
-        remark: values.remark,
-      });
+      await auditAppraise(rowData.value!.id!, pass.value, values.reviewRemark);
       await modalApi.close();
       emit('success');
-      message.success('瀹℃壒瀹屾垚');
+      message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
     } finally {
       modalApi.unlock();
     }
   },
   async onOpenChange(isOpen: boolean) {
     if (!isOpen) {
-      appraiseId.value = undefined;
-      await formApi.resetForm();
+      rowData.value = undefined;
       return;
     }
-    const data = modalApi.getData<{ id: number }>();
-    appraiseId.value = data?.id;
-    await formApi.setValues({ auditStatus: 20 });
+    const data = modalApi.getData<{
+      pass: boolean;
+      row: HrmPerformanceAppraiseApi.Appraise;
+    }>();
+    rowData.value = data.row;
+    pass.value = data.pass;
+    formApi.setState({ schema: useAuditSchema() });
+    formApi.resetForm();
   },
 });
 </script>
 
 <template>
-  <Modal title="瀹℃壒缁╂晥鑰冩牳鏂规" class="w-1/3">
-    <Form class="mx-3" />
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>鏂规鍚嶇О锛歿{ rowData.name }}</div>
+      <div>鑰冩牳鍛ㄦ湡锛歿{ rowData.period }}</div>
+      <div>鑰冩牳鎸囨爣鏁帮細{{ rowData.itemCount }}</div>
+    </div>
+    <Form class="mx-4" />
   </Modal>
 </template>
diff --git a/src/views/hrm/performance-appraise/modules/process-select-modal.vue b/src/views/hrm/performance-appraise/modules/process-select-modal.vue
deleted file mode 100644
index c712ae8..0000000
--- a/src/views/hrm/performance-appraise/modules/process-select-modal.vue
+++ /dev/null
@@ -1,78 +0,0 @@
-<script lang="ts" setup>
-import { computed, ref } from 'vue';
-
-import { useVbenModal } from '@vben/common-ui';
-
-import { message, Select } from 'ant-design-vue';
-
-import { submitAppraise } from '#/api/hrm/performance-appraise';
-
-interface ProcessDefinition {
-  id: string;
-  name: string;
-  version: number;
-  key: string;
-}
-
-const emit = defineEmits(['success']);
-
-const appraiseId = ref<number>();
-const processList = ref<ProcessDefinition[]>([]);
-const selectedProcessKey = ref<string>('');
-
-const processOptions = computed(() => {
-  return processList.value.map((p) => ({
-    label: `${p.name} (V${p.version})`,
-    value: p.key,
-  }));
-});
-
-const [Modal, modalApi] = useVbenModal({
-  async onConfirm() {
-    if (!selectedProcessKey.value) {
-      message.warning('璇烽�夋嫨瀹℃壒娴佺▼');
-      return;
-    }
-    modalApi.lock();
-    try {
-      await submitAppraise(appraiseId.value!, selectedProcessKey.value);
-      await modalApi.close();
-      emit('success');
-      message.success('鎻愪氦鎴愬姛');
-    } finally {
-      modalApi.unlock();
-    }
-  },
-  async onOpenChange(isOpen: boolean) {
-    if (!isOpen) {
-      appraiseId.value = undefined;
-      selectedProcessKey.value = '';
-      processList.value = [];
-      return;
-    }
-    const data = modalApi.getData<{ id: number; processList: ProcessDefinition[] }>();
-    if (!data) {
-      return;
-    }
-    appraiseId.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: 200px"
-        placeholder="璇烽�夋嫨瀹℃壒娴佺▼"
-      />
-    </div>
-  </Modal>
-</template>
diff --git a/src/views/hrm/resignation/index.vue b/src/views/hrm/resignation/index.vue
index cdf72da..5b438bc 100644
--- a/src/views/hrm/resignation/index.vue
+++ b/src/views/hrm/resignation/index.vue
@@ -2,7 +2,10 @@
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { HrmResignationApi } from '#/api/hrm/resignation';
 
+import { ref } from 'vue';
+
 import { Page, useVbenModal } from '#/packages/effects/common-ui/src';
+import { useUserStore } from '@vben/stores';
 
 import { message } from 'ant-design-vue';
 
@@ -10,6 +13,7 @@
 import {
   cancelResignation,
   deleteResignation,
+  getResignationApproverIds,
   getResignationPage,
   submitResignation,
   updateHandoverStatus,
@@ -17,16 +21,47 @@
 import { $t } from '#/locales';
 
 import { useGridColumns, useGridFormSchema } from './data';
+import AuditModal from './modules/audit-modal.vue';
 import Form from './modules/form.vue';
+
+const userStore = useUserStore();
+const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鎵逛汉鏉冮檺鍒ゆ柇
+
+/** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鎵逛汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+const approverIds = ref<number[]>([]);
+
+/** 鎷夊彇瀹℃壒浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇鍒楄〃鍔犺浇 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getResignationApproverIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鎵逛汉锛屼笖鐢宠澶勪簬瀹℃壒涓紙10锛� */
+function canAudit(row: HrmResignationApi.Resignation) {
+  return (
+    row.status === 10 &&
+    !!currentUserId &&
+    approverIds.value.includes(currentUserId)
+  );
+}
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
 });
 
-/** 鍒锋柊琛ㄦ牸 */
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
+  destroyOnClose: true,
+});
+
+/** 鍒锋柊琛ㄦ牸涓庡鎵逛汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
 }
 
 /** 鍒涘缓绂昏亴鐢宠 */
@@ -59,11 +94,24 @@
   }
 }
 
-/** 鎻愪氦绂昏亴鐢宠 */
+/** 鎻愪氦瀹℃壒 - 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�鎸囧畾锛屾澶勬棤闇�閫夋嫨 */
 async function handleSubmit(row: HrmResignationApi.Resignation) {
-  await submitResignation(row.id!);
-  message.success('鎻愪氦鎴愬姛');
-  handleRefresh();
+  const hideLoading = message.loading({
+    content: '姝e湪鎻愪氦瀹℃壒...',
+    duration: 0,
+  });
+  try {
+    await submitResignation(row.id!);
+    message.success('鎻愪氦瀹℃壒鎴愬姛');
+    handleRefresh();
+  } finally {
+    hideLoading();
+  }
+}
+
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(row: HrmResignationApi.Resignation, pass: boolean) {
+  auditModalApi.setData({ pass, row }).open();
 }
 
 /** 鎾ら攢绂昏亴鐢宠 */
@@ -111,11 +159,15 @@
     },
   } as VxeTableGridOptions<HrmResignationApi.Resignation>,
 });
+
+// 杩涘叆椤甸潰鍗虫媺鍙栧鎵逛汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+loadApproverIds();
 </script>
 
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
     <Grid table-title="绂昏亴鐢宠鍒楄〃">
       <template #toolbar-tools>
         <TableAction
@@ -147,11 +199,34 @@
               onClick: handleEdit.bind(null, row),
             },
             {
-              label: '鎻愪氦',
+              label: '鎻愪氦瀹℃壒',
               type: 'link',
-              auth: ['hrm:resignation:submit'],
-              ifShow: row.status === 0,
-              onClick: handleSubmit.bind(null, row),
+              auth: ['hrm:resignation:update'],
+              // 瀹℃壒浜虹敱瀹℃壒閰嶇疆缁熶竴鎸囧畾锛屾彁浜ゅ墠浠呬簩娆$‘璁わ紝涓嶅啀寮归�夋祦绋嬫
+              popConfirm: {
+                title: '纭鎻愪氦瀹℃壒锛熸彁浜ゅ悗鐢卞鎵归厤缃腑鎸囧畾鐨勫鎵逛汉澶勭悊銆�',
+                confirm: handleSubmit.bind(null, row),
+              },
+              // 鑽夌(0) 涓� 瀹℃牳涓嶉�氳繃(30) 鍧囧彲鎻愪氦锛屽悗鑰呴渶淇敼鍚庨噸鏂版彁浜�
+              ifShow: row.status === 0 || row.status === 30,
+            },
+            {
+              label: '瀹℃牳',
+              type: 'link',
+              icon: ACTION_ICON.AUDIT,
+              auth: ['hrm:resignation:audit'],
+              // 浠呫�屽鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃壒浜哄悕鍗曞唴銆嶆墠鏄剧ず
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              auth: ['hrm:resignation:audit'],
+              // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
             {
               label: '鎾ら攢',
diff --git a/src/views/hrm/resignation/modules/audit-modal.vue b/src/views/hrm/resignation/modules/audit-modal.vue
new file mode 100644
index 0000000..b031e14
--- /dev/null
+++ b/src/views/hrm/resignation/modules/audit-modal.vue
@@ -0,0 +1,98 @@
+<script lang="ts" setup>
+import type { VbenFormSchema } from '#/adapter/form';
+import type { HrmResignationApi } from '#/api/hrm/resignation';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '#/packages/effects/common-ui/src';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import { auditResignation } from '#/api/hrm/resignation';
+
+const emit = defineEmits(['success']);
+
+const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+const rowData = ref<HrmResignationApi.Resignation>();
+const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: {
+      class: 'w-full',
+    },
+    formItemClass: 'col-span-1',
+    labelWidth: 90,
+  },
+  wrapperClass: 'grid-cols-1',
+  layout: 'horizontal',
+  schema: [],
+  showDefaultActions: false,
+});
+
+/** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+function useAuditSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'reviewRemark',
+      label: '瀹℃牳鎰忚',
+      component: 'Textarea',
+      formItemClass: 'col-span-1',
+      componentProps: {
+        rows: 3,
+        placeholder: pass.value
+          ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+          : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+      },
+      rules: pass.value ? '' : 'required',
+    },
+  ];
+}
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const values = await formApi.getValues();
+    try {
+      await auditResignation(rowData.value!.id!, pass.value, values.reviewRemark);
+      await modalApi.close();
+      emit('success');
+      message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      rowData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<{
+      pass: boolean;
+      row: HrmResignationApi.Resignation;
+    }>();
+    rowData.value = data.row;
+    pass.value = data.pass;
+    formApi.setState({ schema: useAuditSchema() });
+    formApi.resetForm();
+  },
+});
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>鐢宠浜猴細{{ rowData.employeeName }}</div>
+      <div>鎵�灞為儴闂細{{ rowData.deptName }}</div>
+      <div>棰勮绂昏亴鏃ユ湡锛歿{ rowData.expectedDate }}</div>
+      <div>浜ゆ帴浜猴細{{ rowData.handoverUserName }}</div>
+      <div>绂昏亴鍘熷洜锛歿{ rowData.reason }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/hrm/transfer/index.vue b/src/views/hrm/transfer/index.vue
index 36e331d..5b243c7 100644
--- a/src/views/hrm/transfer/index.vue
+++ b/src/views/hrm/transfer/index.vue
@@ -2,7 +2,10 @@
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { HrmTransferApi } from '#/api/hrm/transfer';
 
+import { ref } from 'vue';
+
 import { Page, useVbenModal } from '#/packages/effects/common-ui/src';
+import { useUserStore } from '@vben/stores';
 
 import { message } from 'ant-design-vue';
 
@@ -10,22 +13,54 @@
 import {
   cancelTransfer,
   deleteTransfer,
+  getTransferApproverIds,
   getTransferPage,
   submitTransfer,
 } from '#/api/hrm/transfer';
 import { $t } from '#/locales';
 
 import { useGridColumns, useGridFormSchema } from './data';
+import AuditModal from './modules/audit-modal.vue';
 import Form from './modules/form.vue';
+
+const userStore = useUserStore();
+const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鎵逛汉鏉冮檺鍒ゆ柇
+
+/** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鎵逛汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+const approverIds = ref<number[]>([]);
+
+/** 鎷夊彇瀹℃壒浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇鍒楄〃鍔犺浇 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getTransferApproverIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鎵逛汉锛屼笖鐢宠澶勪簬瀹℃壒涓紙10锛� */
+function canAudit(row: HrmTransferApi.Transfer) {
+  return (
+    row.status === 10 &&
+    !!currentUserId &&
+    approverIds.value.includes(currentUserId)
+  );
+}
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
 });
 
-/** 鍒锋柊琛ㄦ牸 */
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
+  destroyOnClose: true,
+});
+
+/** 鍒锋柊琛ㄦ牸涓庡鎵逛汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
 }
 
 /** 鍒涘缓璋冨矖鐢宠 */
@@ -58,11 +93,24 @@
   }
 }
 
-/** 鎻愪氦璋冨矖鐢宠 */
+/** 鎻愪氦瀹℃壒 - 瀹℃壒浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�鎸囧畾锛屾澶勬棤闇�閫夋嫨 */
 async function handleSubmit(row: HrmTransferApi.Transfer) {
-  await submitTransfer(row.id!);
-  message.success('鎻愪氦鎴愬姛');
-  handleRefresh();
+  const hideLoading = message.loading({
+    content: '姝e湪鎻愪氦瀹℃壒...',
+    duration: 0,
+  });
+  try {
+    await submitTransfer(row.id!);
+    message.success('鎻愪氦瀹℃壒鎴愬姛');
+    handleRefresh();
+  } finally {
+    hideLoading();
+  }
+}
+
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(row: HrmTransferApi.Transfer, pass: boolean) {
+  auditModalApi.setData({ pass, row }).open();
 }
 
 /** 鎾ら攢璋冨矖鐢宠 */
@@ -100,11 +148,15 @@
     },
   } as VxeTableGridOptions<HrmTransferApi.Transfer>,
 });
+
+// 杩涘叆椤甸潰鍗虫媺鍙栧鎵逛汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+loadApproverIds();
 </script>
 
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
     <Grid table-title="璋冨矖鐢宠鍒楄〃">
       <template #toolbar-tools>
         <TableAction
@@ -136,11 +188,34 @@
               onClick: handleEdit.bind(null, row),
             },
             {
-              label: '鎻愪氦',
+              label: '鎻愪氦瀹℃壒',
               type: 'link',
-              auth: ['hrm:transfer:submit'],
-              ifShow: row.status === 0,
-              onClick: handleSubmit.bind(null, row),
+              auth: ['hrm:transfer:update'],
+              // 瀹℃壒浜虹敱瀹℃壒閰嶇疆缁熶竴鎸囧畾锛屾彁浜ゅ墠浠呬簩娆$‘璁わ紝涓嶅啀寮归�夋祦绋嬫
+              popConfirm: {
+                title: '纭鎻愪氦瀹℃壒锛熸彁浜ゅ悗鐢卞鎵归厤缃腑鎸囧畾鐨勫鎵逛汉澶勭悊銆�',
+                confirm: handleSubmit.bind(null, row),
+              },
+              // 鑽夌(0) 涓� 瀹℃牳涓嶉�氳繃(30) 鍧囧彲鎻愪氦锛屽悗鑰呴渶淇敼鍚庨噸鏂版彁浜�
+              ifShow: row.status === 0 || row.status === 30,
+            },
+            {
+              label: '瀹℃牳',
+              type: 'link',
+              icon: ACTION_ICON.AUDIT,
+              auth: ['hrm:transfer:audit'],
+              // 浠呫�屽鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃壒浜哄悕鍗曞唴銆嶆墠鏄剧ず
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              auth: ['hrm:transfer:audit'],
+              // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
             {
               label: '鎾ら攢',
diff --git a/src/views/hrm/transfer/modules/audit-modal.vue b/src/views/hrm/transfer/modules/audit-modal.vue
new file mode 100644
index 0000000..e00905f
--- /dev/null
+++ b/src/views/hrm/transfer/modules/audit-modal.vue
@@ -0,0 +1,99 @@
+<script lang="ts" setup>
+import type { VbenFormSchema } from '#/adapter/form';
+import type { HrmTransferApi } from '#/api/hrm/transfer';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '#/packages/effects/common-ui/src';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import { auditTransfer } from '#/api/hrm/transfer';
+
+const emit = defineEmits(['success']);
+
+const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+const rowData = ref<HrmTransferApi.Transfer>();
+const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: {
+      class: 'w-full',
+    },
+    formItemClass: 'col-span-1',
+    labelWidth: 90,
+  },
+  wrapperClass: 'grid-cols-1',
+  layout: 'horizontal',
+  schema: [],
+  showDefaultActions: false,
+});
+
+/** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+function useAuditSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'reviewRemark',
+      label: '瀹℃牳鎰忚',
+      component: 'Textarea',
+      formItemClass: 'col-span-1',
+      componentProps: {
+        rows: 3,
+        placeholder: pass.value
+          ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+          : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+      },
+      rules: pass.value ? '' : 'required',
+    },
+  ];
+}
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const values = await formApi.getValues();
+    try {
+      await auditTransfer(rowData.value!.id!, pass.value, values.reviewRemark);
+      await modalApi.close();
+      emit('success');
+      message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      rowData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<{
+      pass: boolean;
+      row: HrmTransferApi.Transfer;
+    }>();
+    rowData.value = data.row;
+    pass.value = data.pass;
+    formApi.setState({ schema: useAuditSchema() });
+    formApi.resetForm();
+  },
+});
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>鐢宠浜猴細{{ rowData.employeeName }}</div>
+      <div>鍘熼儴闂細{{ rowData.currentDeptName }}</div>
+      <div>鐩爣閮ㄩ棬锛歿{ rowData.targetDeptName }}</div>
+      <div>鍘熷矖浣嶏細{{ rowData.currentPostName }}</div>
+      <div>鐩爣宀椾綅锛歿{ rowData.targetPostName }}</div>
+      <div>鐢熸晥鏃ユ湡锛歿{ rowData.effectiveDate }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/mes/pd/archive/modules/detail.vue b/src/views/mes/pd/archive/modules/detail.vue
index 5646b92..49a4498 100644
--- a/src/views/mes/pd/archive/modules/detail.vue
+++ b/src/views/mes/pd/archive/modules/detail.vue
@@ -9,11 +9,12 @@
 import { FilePreviewModal, useFilePreview } from '#/components/file-preview';
 
 interface Props {
-  visible: boolean;
+  visible?: boolean;
   productCode?: string;
 }
 
 const props = withDefaults(defineProps<Props>(), {
+  visible: false,
   productCode: '',
 });
 
@@ -27,6 +28,8 @@
 const loading = ref(false);
 const detailList = ref<MesPdArchiveApi.DetailItem[]>([]);
 const activeProject = ref('0');
+/** 褰撳墠鏌ョ湅鐨勪骇鍝佺紪鐮侊紙鍛戒护寮� open(code) 浼犲叆鎴栧彇鑷� prop锛� */
+const currentCode = ref('');
 
 const { openPreview, previewState } = useFilePreview();
 
@@ -52,28 +55,50 @@
   },
 ];
 
-watch(() => props.visible, async (newVal) => {
-  if (newVal && props.productCode) {
-    open.value = true;
-    loading.value = true;
-    detailList.value = [];
-    activeProject.value = '0';
-    try {
-      detailList.value = await getPdArchiveDetailByProduct(props.productCode);
-    } catch {
-      message.error('鑾峰彇褰掓。璇︽儏澶辫触');
-    } finally {
-      loading.value = false;
-    }
-  } else if (!newVal) {
-    open.value = false;
-    detailList.value = [];
+/** 鎵撳紑寮圭獥骞跺姞杞芥寚瀹氫骇鍝佺紪鐮佺殑褰掓。璇︽儏锛涚紪鐮佷负绌烘椂鐩存帴灞曠ず绌烘�� */
+async function showDetail(code: string) {
+  currentCode.value = code || '';
+  open.value = true;
+  detailList.value = [];
+  activeProject.value = '0';
+  if (!currentCode.value) {
+    return;
   }
-});
+  loading.value = true;
+  try {
+    detailList.value = await getPdArchiveDetailByProduct(currentCode.value);
+  } catch {
+    message.error('鑾峰彇褰掓。璇︽儏澶辫触');
+  } finally {
+    loading.value = false;
+  }
+}
+
+watch(
+  () => props.visible,
+  (newVal) => {
+    if (newVal) {
+      // 鍗充娇浜у搧缂栫爜涓虹┖涔熸墦寮�寮圭獥锛堝睍绀虹┖鎬侊級锛岄伩鍏嶇埗缁勪欢 visible 鍗″湪 true 鍚庡啀涔熸棤娉曡Е鍙�
+      showDetail(props.productCode || '');
+    } else {
+      open.value = false;
+      detailList.value = [];
+    }
+  },
+);
 
 function handleClose() {
+  open.value = false;
+  detailList.value = [];
   emit('update:visible', false);
 }
+
+/** 鍏煎鍛戒护寮忚皟鐢細archiveDetailRef.value?.open(productCode) */
+function handleOpen(code?: string) {
+  showDetail(code ?? props.productCode ?? '');
+}
+
+defineExpose({ open: handleOpen });
 </script>
 
 <template>
@@ -143,7 +168,7 @@
         </Tabs.TabPane>
       </Tabs>
       <div v-else class="py-8 text-center text-gray-400">
-        鏆傛棤褰掓。璁板綍
+        {{ currentCode ? '鏆傛棤褰掓。璁板綍' : '璇ュ伐鍗曟湭璁剧疆浜у搧缂栫爜锛屾殏鏃犲綊妗h褰�' }}
       </div>
     </template>
   </Modal>
diff --git a/src/views/mes/pd/project/detail/index.vue b/src/views/mes/pd/project/detail/index.vue
index 97f7f8b..a944c26 100644
--- a/src/views/mes/pd/project/detail/index.vue
+++ b/src/views/mes/pd/project/detail/index.vue
@@ -5,6 +5,8 @@
 import { computed, onMounted, ref } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 
+import { useUserStore } from '@vben/stores';
+
 import { Page, useVbenModal } from '#/packages/effects/common-ui/src';
 import { useTabs } from '#/packages/effects/hooks/src';
 import { MesPdProjectStatusEnum } from '#/packages/constants/src';
@@ -14,8 +16,9 @@
 import {
   archivePdProject,
   getPdProject,
-  getPdProjectApproveProcessList,
+  getPdProjectApproverIds,
   publishPdProject,
+  submitPdProjectApproval,
   uploadPdProject,
 } from '#/api/mes/pd/project';
 import { useDescription } from '#/components/description';
@@ -23,9 +26,9 @@
 import { $t } from '#/locales';
 
 import Form from '../modules/form.vue';
+import AuditModal from '../modules/audit-modal.vue';
 import DocumentAuditList from '../modules/document-audit-list.vue';
 import DocumentList from '../modules/document-list.vue';
-import ProcessSelectModal from '../modules/process-select-modal.vue';
 import WorkflowTimeline from '../modules/workflow-timeline.vue';
 import TaskDraftBanner from '../modules/task-draft-banner.vue';
 import { useDetailSchema } from './data';
@@ -77,10 +80,33 @@
   destroyOnClose: true,
 });
 
-const [ProcessModal, processModalApi] = useVbenModal({
-  connectedComponent: ProcessSelectModal,
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
   destroyOnClose: true,
 });
+
+const userStore = useUserStore();
+const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鏍镐汉鏉冮檺鍒ゆ柇
+
+/** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鏍镐汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+const approverIds = ref<number[]>([]);
+
+/** 鎷夊彇瀹℃牳浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇璇︽儏鍔犺浇 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getPdProjectApproverIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鏍镐汉锛屼笖鍗曟嵁澶勪簬瀹℃壒涓姸鎬� */
+const canAudit = computed(
+  () =>
+    isApproving.value &&
+    !!currentUserId &&
+    approverIds.value.includes(currentUserId),
+);
 
 async function loadProjectDetail() {
   loading.value = true;
@@ -128,38 +154,24 @@
   }
 }
 
-/** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
+/** 鎻愪氦瀹℃壒 - 瀹℃牳浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�鎸囧畾锛屾澶勬棤闇�閫夋嫨 */
 async function handleSubmit() {
-  const hideLoading = message.loading({ content: '鑾峰彇瀹℃壒娴佺▼...', duration: 0 });
-  try {
-    const processList = await getPdProjectApproveProcessList();
-    hideLoading();
-    if (!processList || processList.length === 0) {
-      message.warning('鏆傛棤鍙敤瀹℃壒娴佺▼锛岃鍏堥厤缃� BPM 娴佺▼');
-      return;
-    }
-    // 鎵撳紑娴佺▼閫夋嫨寮圭獥
-    processModalApi.setData({ id: projectId.value, processList }).open();
-  } catch {
-    hideLoading();
-  }
-}
-
-/** 鏌ョ湅瀹℃壒璇︽儏 */
-function handleProcessDetail() {
-  if (!project.value.processInstanceId) {
-    message.warning('璇ラ」鐩皻鏈彁浜ゅ鎵�');
-    return;
-  }
-  router.push({
-    name: 'BpmProcessInstanceDetail',
-    query: { id: project.value.processInstanceId },
+  const hideLoading = message.loading({
+    content: '姝e湪鎻愪氦瀹℃壒...',
+    duration: 0,
   });
+  try {
+    await submitPdProjectApproval(projectId.value);
+    message.success('鎻愪氦瀹℃壒鎴愬姛');
+    loadProjectDetail();
+  } finally {
+    hideLoading();
+  }
 }
 
-/** 鎻愪氦瀹℃壒鎴愬姛鍚庡埛鏂拌鎯� */
-function handleSubmitSuccess() {
-  loadProjectDetail();
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(pass: boolean) {
+  auditModalApi.setData({ pass, row: project.value }).open();
 }
 
 async function handleArchive() {
@@ -176,6 +188,8 @@
 onMounted(() => {
   projectId.value = Number(route.params.id);
   loadProjectDetail();
+  // 杩涘叆椤甸潰鍗虫媺鍙栧鏍镐汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+  loadApproverIds();
 });
 </script>
 
@@ -186,7 +200,7 @@
     :title="project.taskName || '璁捐浠诲姟璇︽儏'"
   >
     <FormModal @success="loadProjectDetail" />
-    <ProcessModal @success="handleSubmitSuccess" />
+    <AuditModalComp @success="loadProjectDetail" />
 
     <template #extra>
       <TableAction
@@ -220,13 +234,27 @@
             label: '鎻愪氦瀹℃壒',
             type: 'primary',
             ifShow: isPendingAudit,
-            onClick: handleSubmit,
+            // 瀹℃牳浜虹敱瀹℃壒閰嶇疆缁熶竴鎸囧畾锛屾彁浜ゅ墠浠呬簩娆$‘璁わ紝涓嶅啀寮归�夋祦绋嬫
+            popConfirm: {
+              title: '纭鎻愪氦瀹℃壒锛熸彁浜ゅ悗鐢卞鎵归厤缃腑鎸囧畾鐨勫鏍镐汉澶勭悊銆�',
+              confirm: handleSubmit,
+            },
           },
           {
-            label: '鏌ョ湅瀹℃壒',
+            label: '瀹℃牳',
             type: 'primary',
-            ifShow: isApproving,
-            onClick: handleProcessDetail,
+            icon: ACTION_ICON.AUDIT,
+            // 浠呫�屽鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃牳浜哄悕鍗曞唴銆嶆墠鏄剧ず
+            ifShow: canAudit,
+            onClick: () => handleAudit(true),
+          },
+          {
+            label: '瀹℃牳涓嶉�氳繃',
+            type: 'default',
+            danger: true,
+            // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+            ifShow: canAudit,
+            onClick: () => handleAudit(false),
           },
           {
             label: '褰掓。',
diff --git a/src/views/mes/pd/project/index.vue b/src/views/mes/pd/project/index.vue
index 6727d6d..6a694e9 100644
--- a/src/views/mes/pd/project/index.vue
+++ b/src/views/mes/pd/project/index.vue
@@ -2,8 +2,10 @@
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { MesPdProjectApi } from '#/api/mes/pd/project';
 
-import { onActivated } from 'vue';
+import { onActivated, ref } from 'vue';
 import { useRouter } from 'vue-router';
+
+import { useUserStore } from '@vben/stores';
 
 import { Page, useVbenModal } from '#/packages/effects/common-ui/src';
 import { MesPdProjectStatusEnum } from '#/packages/constants/src';
@@ -17,19 +19,44 @@
 
   deletePdProject,
   exportPdProject,
-  getPdProjectApproveProcessList,
+  getPdProjectApproverIds,
   getPdProjectPage,
   publishPdProject,
+  submitPdProjectApproval,
 
 } from '#/api/mes/pd/project';
 import { $t } from '#/locales';
 
 import { useGridColumns, useGridFormSchema } from './data';
+import AuditModal from './modules/audit-modal.vue';
 import CreateForm from './modules/create-form.vue';
 import Form from './modules/form.vue';
-import ProcessSelectModal from './modules/process-select-modal.vue';
 
 const router = useRouter();
+
+const userStore = useUserStore();
+const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鏍镐汉鏉冮檺鍒ゆ柇
+
+/** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鏍镐汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+const approverIds = ref<number[]>([]);
+
+/** 鎷夊彇瀹℃牳浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇鍒楄〃鍔犺浇 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getPdProjectApproverIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鏍镐汉锛屼笖鍗曟嵁澶勪簬瀹℃壒涓姸鎬� */
+function canAudit(row: MesPdProjectApi.Project) {
+  return (
+    row.status === MesPdProjectStatusEnum.APPROVING &&
+    !!currentUserId &&
+    approverIds.value.includes(currentUserId)
+  );
+}
 
 const [CreateFormModal, createFormModalApi] = useVbenModal({
   connectedComponent: CreateForm,
@@ -41,13 +68,15 @@
   destroyOnClose: true,
 });
 
-const [ProcessModal, processModalApi] = useVbenModal({
-  connectedComponent: ProcessSelectModal,
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
   destroyOnClose: true,
 });
 
+/** 鍒锋柊琛ㄦ牸涓庡鏍镐汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
 }
 
 function handleCreateSuccess() {
@@ -80,36 +109,24 @@
   }
 }
 
-/** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
+/** 鎻愪氦瀹℃壒 - 瀹℃牳浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�鎸囧畾锛屾澶勬棤闇�閫夋嫨 */
 async function handleSubmit(row: MesPdProjectApi.Project) {
   const hideLoading = message.loading({
-    content: '鑾峰彇瀹℃壒娴佺▼...',
+    content: '姝e湪鎻愪氦瀹℃壒...',
     duration: 0,
   });
   try {
-    const processList = await getPdProjectApproveProcessList();
-    hideLoading();
-    if (!processList || processList.length === 0) {
-      message.warning('鏆傛棤鍙敤瀹℃壒娴佺▼锛岃鍏堥厤缃� BPM 娴佺▼');
-      return;
-    }
-    // 鎵撳紑娴佺▼閫夋嫨寮圭獥
-    processModalApi.setData({ id: row.id, processList }).open();
-  } catch {
+    await submitPdProjectApproval(row.id!);
+    message.success('鎻愪氦瀹℃壒鎴愬姛');
+    handleRefresh();
+  } finally {
     hideLoading();
   }
 }
 
-/** 鏌ョ湅瀹℃壒璇︽儏 */
-function handleProcessDetail(row: MesPdProjectApi.Project) {
-  if (!row.processInstanceId) {
-    message.warning('璇ラ」鐩皻鏈彁浜ゅ鎵�');
-    return;
-  }
-  router.push({
-    name: 'BpmProcessInstanceDetail',
-    query: { id: row.processInstanceId },
-  });
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(row: MesPdProjectApi.Project, pass: boolean) {
+  auditModalApi.setData({ pass, row }).open();
 }
 
 async function handleArchive(row: MesPdProjectApi.Project) {
@@ -170,13 +187,16 @@
 onActivated(() => {
   handleRefresh();
 });
+
+// 杩涘叆椤甸潰鍗虫媺鍙栧鏍镐汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+loadApproverIds();
 </script>
 
 <template>
   <Page auto-content-height>
     <CreateFormModal @success="handleCreateSuccess" />
     <FormModal @success="handleRefresh" />
-    <ProcessModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
 
     <Grid table-title="璁捐浠诲姟">
       <template #toolbar-tools>
@@ -226,15 +246,31 @@
               label: '鎻愪氦瀹℃壒',
               type: 'link',
               auth: ['mes:pd-project:update'],
+              // 寰呭鏍稿彲鎻愪氦锛涘鏍镐笉閫氳繃浼氶��鍥炲緟瀹℃牳锛屽彲淇敼鍚庨噸鏂版彁浜�
               ifShow: row.status === MesPdProjectStatusEnum.PENDING_AUDIT,
-              onClick: handleSubmit.bind(null, row),
+              // 瀹℃牳浜虹敱瀹℃壒閰嶇疆缁熶竴鎸囧畾锛屾彁浜ゅ墠浠呬簩娆$‘璁わ紝涓嶅啀寮归�夋祦绋嬫
+              popConfirm: {
+                title: '纭鎻愪氦瀹℃壒锛熸彁浜ゅ悗鐢卞鎵归厤缃腑鎸囧畾鐨勫鏍镐汉澶勭悊銆�',
+                confirm: handleSubmit.bind(null, row),
+              },
             },
             {
-              label: '鏌ョ湅瀹℃壒',
+              label: '瀹℃牳',
               type: 'link',
-              auth: ['mes:pd-project:update'],
-              ifShow: row.status === MesPdProjectStatusEnum.APPROVING,
-              onClick: handleProcessDetail.bind(null, row),
+              icon: ACTION_ICON.AUDIT,
+              auth: ['mes:pd-project:audit'],
+              // 浠呫�屽鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃牳浜哄悕鍗曞唴銆嶆墠鏄剧ず
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              auth: ['mes:pd-project:audit'],
+              // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
             {
               label: '褰掓。',
diff --git a/src/views/mes/pd/project/modules/audit-modal.vue b/src/views/mes/pd/project/modules/audit-modal.vue
new file mode 100644
index 0000000..acb12ca
--- /dev/null
+++ b/src/views/mes/pd/project/modules/audit-modal.vue
@@ -0,0 +1,96 @@
+<script lang="ts" setup>
+import type { VbenFormSchema } from '#/adapter/form';
+import type { MesPdProjectApi } from '#/api/mes/pd/project';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '#/packages/effects/common-ui/src';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import { auditPdProject } from '#/api/mes/pd/project';
+
+const emit = defineEmits(['success']);
+
+const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+const rowData = ref<MesPdProjectApi.Project>();
+const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: {
+      class: 'w-full',
+    },
+    formItemClass: 'col-span-1',
+    labelWidth: 90,
+  },
+  wrapperClass: 'grid-cols-1',
+  layout: 'horizontal',
+  schema: [],
+  showDefaultActions: false,
+});
+
+/** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+function useAuditSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'reviewRemark',
+      label: '瀹℃牳鎰忚',
+      component: 'Textarea',
+      formItemClass: 'col-span-1',
+      componentProps: {
+        rows: 3,
+        placeholder: pass.value
+          ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+          : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+      },
+      rules: pass.value ? '' : 'required',
+    },
+  ];
+}
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const values = await formApi.getValues();
+    try {
+      await auditPdProject(rowData.value!.id!, pass.value, values.reviewRemark);
+      await modalApi.close();
+      emit('success');
+      message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      rowData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<{
+      pass: boolean;
+      row: MesPdProjectApi.Project;
+    }>();
+    rowData.value = data.row;
+    pass.value = data.pass;
+    formApi.setState({ schema: useAuditSchema() });
+    formApi.resetForm();
+  },
+});
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>浠诲姟缂栫爜锛歿{ rowData.taskCode }}</div>
+      <div>浠诲姟鍚嶇О锛歿{ rowData.taskName }}</div>
+      <div>浜у搧鍚嶇О锛歿{ rowData.productName }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/mes/pd/project/modules/process-select-modal.vue b/src/views/mes/pd/project/modules/process-select-modal.vue
deleted file mode 100644
index c0d099a..0000000
--- a/src/views/mes/pd/project/modules/process-select-modal.vue
+++ /dev/null
@@ -1,75 +0,0 @@
-<script lang="ts" setup>
-import { computed, ref } from 'vue';
-
-import { useVbenModal } from '@vben/common-ui';
-
-import { message, Select } from 'ant-design-vue';
-
-import { submitPdProjectApproval } from '#/api/mes/pd/project';
-
-interface ProcessDefinition {
-  id: string;
-  name: string;
-  key: string;
-}
-
-const emit = defineEmits(['success']);
-
-const projectId = ref<number>();
-const processList = ref<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 submitPdProjectApproval(projectId.value!, selectedProcessKey.value);
-      await modalApi.close();
-      emit('success');
-      message.success('鎻愪氦瀹℃壒鎴愬姛');
-    } finally {
-      modalApi.unlock();
-    }
-  },
-  async onOpenChange(isOpen: boolean) {
-    if (!isOpen) {
-      projectId.value = undefined;
-      selectedProcessKey.value = '';
-      processList.value = [];
-      return;
-    }
-    const data = modalApi.getData<{ id: number; processList: ProcessDefinition[] }>();
-    projectId.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: 200px"
-        placeholder="璇烽�夋嫨瀹℃壒娴佺▼"
-      />
-    </div>
-  </Modal>
-</template>
diff --git a/src/views/mes/pd/tech-confirm/data.ts b/src/views/mes/pd/tech-confirm/data.ts
index ea967b2..9e0871d 100644
--- a/src/views/mes/pd/tech-confirm/data.ts
+++ b/src/views/mes/pd/tech-confirm/data.ts
@@ -13,8 +13,18 @@
 /** 琛ㄥ崟绫诲瀷 */
 export type FormType = 'create' | 'detail' | 'update';
 
-/** 鏂板/淇敼浜у搧鎶�鏈‘璁ゅ嚱 */
+/**
+ * 鏂板/淇敼/鏌ョ湅浜у搧鎶�鏈‘璁ゅ嚱
+ *
+ * 鐘舵�佸強缂栧埗浜恒�佸鏍镐汉銆佸鎴风‘璁や汉绛夋祦绋嬫淳鐢熷瓧娈典竴寰嬪彧璇伙細
+ * 鐘舵�佺敱鍚庣鎸変笟鍔℃祦绋嬫帹杩涳紙鎻愪氦瀹℃牳 鈫� 瀹℃牳閫氳繃/涓嶉�氳繃 鈫� 瀹㈡埛纭锛夛紝
+ * 浜哄憳涓庢椂闂寸敱鍚庣鑷姩璁板綍锛屽潎涓嶅厑璁告墜宸ュ~鍐欍��
+ *
+ * @param formType 琛ㄥ崟绫诲瀷锛宑reate 鏃朵笉灞曠ず鐘舵�佸瓧娈碉紙鏂板缓鍗曟嵁鍥哄畾涓鸿崏绋匡級
+ * @param formApi 琛ㄥ崟 API
+ */
 export function useFormSchema(
+  formType: FormType,
   formApi?: VbenFormApi,
 ): VbenFormSchema[] {
   return [
@@ -97,9 +107,24 @@
       label: '鐘舵��',
       component: 'Select',
       componentProps: {
-        allowClear: true,
+        // 鐘舵�佺敱涓氬姟娴佺▼椹卞姩锛屼粎浣滃睍绀猴紝涓嶅彲鎵嬪伐閫夋嫨
+        disabled: true,
         options: getDictOptions(DICT_TYPE.PRODUCT_TECH_CONFIRM_STATUS, 'number'),
-        placeholder: '璇烽�夋嫨鐘舵��',
+      },
+      dependencies: {
+        triggerFields: [''],
+        // 鏂板缓鍗曟嵁鍥哄畾涓鸿崏绋匡紝涓嶅睍绀虹姸鎬�
+        show: () => formType !== 'create',
+      },
+    },
+    {
+      fieldName: 'flowDivider',
+      label: '娴佺▼淇℃伅锛堢郴缁熻嚜鍔ㄨ褰曪級',
+      component: 'Divider',
+      formItemClass: 'col-span-3',
+      dependencies: {
+        triggerFields: [''],
+        show: () => formType !== 'create',
       },
     },
     {
@@ -107,7 +132,12 @@
       label: '缂栧埗浜�',
       component: 'Input',
       componentProps: {
-        placeholder: '璇疯緭鍏ョ紪鍒朵汉濮撳悕',
+        disabled: true,
+        placeholder: '鐢辩郴缁熻嚜鍔ㄨ褰�',
+      },
+      dependencies: {
+        triggerFields: [''],
+        show: () => formType !== 'create',
       },
     },
     {
@@ -116,9 +146,14 @@
       component: 'DatePicker',
       componentProps: {
         class: '!w-full',
+        disabled: true,
         format: 'YYYY-MM-DD HH:mm:ss',
         showTime: true,
         valueFormat: 'YYYY-MM-DD HH:mm:ss',
+      },
+      dependencies: {
+        triggerFields: [''],
+        show: () => formType !== 'create',
       },
     },
     {
@@ -126,7 +161,12 @@
       label: '瀹℃牳浜�',
       component: 'Input',
       componentProps: {
-        placeholder: '璇疯緭鍏ュ鏍镐汉濮撳悕',
+        disabled: true,
+        placeholder: '鎻愪氦瀹℃牳鏃堕�夋嫨',
+      },
+      dependencies: {
+        triggerFields: [''],
+        show: () => formType !== 'create',
       },
     },
     {
@@ -135,9 +175,29 @@
       component: 'DatePicker',
       componentProps: {
         class: '!w-full',
+        disabled: true,
         format: 'YYYY-MM-DD HH:mm:ss',
         showTime: true,
         valueFormat: 'YYYY-MM-DD HH:mm:ss',
+      },
+      dependencies: {
+        triggerFields: [''],
+        show: () => formType !== 'create',
+      },
+    },
+    {
+      fieldName: 'reviewRemark',
+      label: '瀹℃牳鎰忚',
+      component: 'Textarea',
+      formItemClass: 'col-span-3',
+      componentProps: {
+        disabled: true,
+        placeholder: '瀹℃牳鏃跺~鍐�',
+        rows: 2,
+      },
+      dependencies: {
+        triggerFields: [''],
+        show: () => formType !== 'create',
       },
     },
     {
@@ -145,7 +205,12 @@
       label: '瀹㈡埛纭浜�',
       component: 'Input',
       componentProps: {
-        placeholder: '璇疯緭鍏ュ鎴风‘璁や汉',
+        disabled: true,
+        placeholder: '鐢辩郴缁熻嚜鍔ㄨ褰�',
+      },
+      dependencies: {
+        triggerFields: [''],
+        show: () => formType !== 'create',
       },
     },
     {
@@ -154,9 +219,14 @@
       component: 'DatePicker',
       componentProps: {
         class: '!w-full',
+        disabled: true,
         format: 'YYYY-MM-DD HH:mm:ss',
         showTime: true,
         valueFormat: 'YYYY-MM-DD HH:mm:ss',
+      },
+      dependencies: {
+        triggerFields: [''],
+        show: () => formType !== 'create',
       },
     },
     {
@@ -233,6 +303,7 @@
       },
     },
     { field: 'drafterName', title: '缂栧埗浜�', width: 90 },
+    { field: 'reviewerName', title: '瀹℃牳浜�', width: 90 },
     {
       field: 'status',
       title: '鐘舵��',
diff --git a/src/views/mes/pd/tech-confirm/index.vue b/src/views/mes/pd/tech-confirm/index.vue
index 88f2bf0..04798eb 100644
--- a/src/views/mes/pd/tech-confirm/index.vue
+++ b/src/views/mes/pd/tech-confirm/index.vue
@@ -2,11 +2,13 @@
   import type { VxeTableGridOptions } from '#/adapter/vxe-table';
   import type { MesPdTechConfirmApi } from '#/api/mes/pd/tech-confirm';
 
-  import { ref } from 'vue';
-
   import { Page, useVbenModal } from '@vben/common-ui';
   import { $t } from '@vben/locales';
   import { downloadFileFromBlobPart } from '@vben/utils';
+
+  import { ref } from 'vue';
+
+  import { MesPdTechConfirmStatusEnum } from '#/packages/constants/src';
 
   import { Button, message } from 'ant-design-vue';
 
@@ -14,21 +16,60 @@
   import {
     deleteTechConfirm,
     exportTechConfirm,
+    getTechConfirmApproverIds,
     getTechConfirmPage,
-    updateTechConfirm,
+    submitTechConfirmAudit,
   } from '#/api/mes/pd/tech-confirm';
+  import { useUserStore } from '@vben/stores';
 
   import { useGridColumns, useGridFormSchema } from './data';
+  import AuditModal from './modules/audit-modal.vue';
+  import CustomerConfirmModal from './modules/customer-confirm-modal.vue';
   import Form from './modules/form.vue';
+
+  const userStore = useUserStore();
+  const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鏍镐汉鏉冮檺鍒ゆ柇
+
+  /** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鏍镐汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+  const approverIds = ref<number[]>([]);
+
+  /** 鎷夊彇瀹℃牳浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇鍒楄〃鍔犺浇 */
+  async function loadApproverIds() {
+    try {
+      approverIds.value = (await getTechConfirmApproverIds()) ?? [];
+    } catch {
+      approverIds.value = [];
+    }
+  }
+
+  /** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鏍镐汉锛屼笖鍗曟嵁澶勪簬寰呭鏍哥姸鎬� */
+  function canAudit(row: MesPdTechConfirmApi.TechConfirm) {
+    return (
+      row.status === MesPdTechConfirmStatusEnum.PENDING_AUDIT &&
+      !!currentUserId &&
+      approverIds.value.includes(currentUserId)
+    );
+  }
 
   const [FormModal, formModalApi] = useVbenModal({
     connectedComponent: Form,
     destroyOnClose: true,
   });
 
-  /** 鍒锋柊琛ㄦ牸 */
+  const [AuditModalComp, auditModalApi] = useVbenModal({
+    connectedComponent: AuditModal,
+    destroyOnClose: true,
+  });
+
+  const [CustomerConfirm, customerConfirmApi] = useVbenModal({
+    connectedComponent: CustomerConfirmModal,
+    destroyOnClose: true,
+  });
+
+  /** 鍒锋柊琛ㄦ牸涓庡鏍镐汉闆嗗悎 */
   function handleRefresh() {
     gridApi.query();
+    loadApproverIds();
   }
 
   /** 鏂板 */
@@ -61,16 +102,35 @@
     }
   }
 
-  /** 鐘舵�佹祦杞� */
-  async function handleStatus(row: MesPdTechConfirmApi.TechConfirm, next: number) {
-    const hideLoading = message.loading({ content: '鐘舵�佹洿鏂颁腑...', duration: 0 });
+  /** 鎻愪氦瀹℃牳 - 瀹℃牳浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�鎸囧畾锛屾澶勬棤闇�閫夋嫨 */
+  async function handleSubmitAudit(row: MesPdTechConfirmApi.TechConfirm) {
+    const hideLoading = message.loading({
+      content: '姝e湪鎻愪氦瀹℃牳...',
+      duration: 0,
+    });
     try {
-      await updateTechConfirm({ ...row, status: next });
-      message.success($t('ui.actionMessage.operationSuccess'));
+      await submitTechConfirmAudit(row.id!);
+      message.success('鎻愪氦瀹℃牳鎴愬姛');
       handleRefresh();
     } finally {
       hideLoading();
     }
+  }
+
+  /** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+  function handleAudit(row: MesPdTechConfirmApi.TechConfirm, pass: boolean) {
+    auditModalApi.setData({ pass, row }).open();
+  }
+
+  /** 瀹㈡埛纭 - 璁板綍瀹㈡埛纭浜猴紝鐢卞悗绔啓鍏ョ‘璁ゆ椂闂� */
+  function handleCustomerConfirm(row: MesPdTechConfirmApi.TechConfirm) {
+    customerConfirmApi
+      .setData({
+        id: row.id,
+        confirmNo: row.confirmNo,
+        customerName: row.customerName,
+      })
+      .open();
   }
 
   /** 瀵煎嚭 */
@@ -107,11 +167,16 @@
       },
     } as VxeTableGridOptions<MesPdTechConfirmApi.TechConfirm>,
   });
+
+  // 杩涘叆椤甸潰鍗虫媺鍙栧鏍镐汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+  loadApproverIds();
 </script>
 
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
+    <CustomerConfirm @success="handleRefresh" />
     <Grid table-title="浜у搧鎶�鏈‘璁ゅ嚱鍒楄〃">
       <template #toolbar-tools>
         <TableAction
@@ -146,30 +211,51 @@
               type: 'link',
               icon: ACTION_ICON.AUDIT,
               auth: ['mes:pd-tech-confirm:update'],
-              ifShow: row.status === 0,
-              onClick: handleStatus.bind(null, row, 1),
+              // 鑽夌涓庡鏍镐笉閫氳繃鍧囧彲鎻愪氦锛屽鏍镐笉閫氳繃闇�淇敼鍚庨噸鏂版彁浜�
+              ifShow:
+                row.status === MesPdTechConfirmStatusEnum.DRAFT ||
+                row.status === MesPdTechConfirmStatusEnum.REJECTED,
+              // 瀹℃牳浜虹敱瀹℃壒閰嶇疆缁熶竴鎸囧畾锛屾彁浜ゅ墠浠呬簩娆$‘璁わ紝涓嶅啀寮归�変汉妗�
+              popConfirm: {
+                title: '纭鎻愪氦瀹℃牳锛熸彁浜ゅ悗鐢卞鎵归厤缃腑鎸囧畾鐨勫鏍镐汉澶勭悊銆�',
+                confirm: handleSubmitAudit.bind(null, row),
+              },
             },
             {
-              label: '瀹℃牳閫氳繃',
+              label: '瀹℃牳',
               type: 'link',
               icon: ACTION_ICON.AUDIT,
-              auth: ['mes:pd-tech-confirm:update'],
-              ifShow: row.status === 1,
-              onClick: handleStatus.bind(null, row, 2),
+              auth: ['mes:pd-tech-confirm:audit'],
+              // 浠呫�屽緟瀹℃牳 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃牳浜哄悕鍗曞唴銆嶆墠鏄剧ず
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              auth: ['mes:pd-tech-confirm:audit'],
+              // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
             {
               label: '瀹㈡埛纭',
               type: 'link',
               icon: ACTION_ICON.AUDIT,
               auth: ['mes:pd-tech-confirm:update'],
-              ifShow: row.status === 2,
-              onClick: handleStatus.bind(null, row, 3),
+              ifShow: row.status === MesPdTechConfirmStatusEnum.AUDITED,
+              onClick: handleCustomerConfirm.bind(null, row),
             },
             {
               label: $t('common.edit'),
               type: 'link',
               icon: ACTION_ICON.EDIT,
               auth: ['mes:pd-tech-confirm:update'],
+              // 浠呰崏绋夸笌瀹℃牳涓嶉�氳繃鍙慨鏀癸紝鍏朵綑鐘舵�佸崟鎹凡閿佸畾
+              ifShow:
+                row.status === MesPdTechConfirmStatusEnum.DRAFT ||
+                row.status === MesPdTechConfirmStatusEnum.REJECTED,
               onClick: handleEdit.bind(null, row),
             },
             {
@@ -178,6 +264,10 @@
               danger: true,
               icon: ACTION_ICON.DELETE,
               auth: ['mes:pd-tech-confirm:delete'],
+              // 浠呰崏绋夸笌瀹℃牳涓嶉�氳繃鍙垹闄�
+              ifShow:
+                row.status === MesPdTechConfirmStatusEnum.DRAFT ||
+                row.status === MesPdTechConfirmStatusEnum.REJECTED,
               popConfirm: {
                 title: $t('ui.actionMessage.deleteConfirm', [row.confirmNo ?? '']),
                 confirm: handleDelete.bind(null, row),
diff --git a/src/views/mes/pd/tech-confirm/modules/audit-modal.vue b/src/views/mes/pd/tech-confirm/modules/audit-modal.vue
new file mode 100644
index 0000000..6a2f3c4
--- /dev/null
+++ b/src/views/mes/pd/tech-confirm/modules/audit-modal.vue
@@ -0,0 +1,96 @@
+<script lang="ts" setup>
+  import type { VbenFormSchema } from '#/adapter/form';
+  import type { MesPdTechConfirmApi } from '#/api/mes/pd/tech-confirm';
+
+  import { computed, ref } from 'vue';
+
+  import { useVbenModal } from '@vben/common-ui';
+  import { $t } from '@vben/locales';
+
+  import { message } from 'ant-design-vue';
+
+  import { useVbenForm } from '#/adapter/form';
+  import { auditTechConfirm } from '#/api/mes/pd/tech-confirm';
+
+  const emit = defineEmits(['success']);
+
+  const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+  const rowData = ref<MesPdTechConfirmApi.TechConfirm>();
+  const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+  const [Form, formApi] = useVbenForm({
+    commonConfig: {
+      componentProps: {
+        class: 'w-full',
+      },
+      formItemClass: 'col-span-1',
+      labelWidth: 130,
+    },
+    wrapperClass: 'grid-cols-1',
+    layout: 'horizontal',
+    schema: [],
+    showDefaultActions: false,
+  });
+
+  /** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+  function useAuditSchema(): VbenFormSchema[] {
+    return [
+      {
+        fieldName: 'reviewRemark',
+        label: '瀹℃牳鎰忚',
+        component: 'Textarea',
+        formItemClass: 'col-span-1',
+        componentProps: {
+          rows: 3,
+          placeholder: pass.value
+            ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+            : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+        },
+        rules: pass.value ? '' : 'required',
+      },
+    ];
+  }
+
+  const [Modal, modalApi] = useVbenModal({
+    async onConfirm() {
+      const { valid } = await formApi.validate();
+      if (!valid) {
+        return;
+      }
+      modalApi.lock();
+      const values = await formApi.getValues();
+      try {
+        await auditTechConfirm(rowData.value!.id!, pass.value, values.reviewRemark);
+        await modalApi.close();
+        emit('success');
+        message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+      } finally {
+        modalApi.unlock();
+      }
+    },
+    async onOpenChange(isOpen: boolean) {
+      if (!isOpen) {
+        rowData.value = undefined;
+        return;
+      }
+      const data = modalApi.getData<{
+        pass: boolean;
+        row: MesPdTechConfirmApi.TechConfirm;
+      }>();
+      rowData.value = data.row;
+      pass.value = data.pass;
+      formApi.setState({ schema: useAuditSchema() });
+      formApi.resetForm();
+    },
+  });
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>纭鍑界紪鍙凤細{{ rowData.confirmNo }}</div>
+      <div>浜у搧鍚嶇О锛歿{ rowData.productName }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/mes/pd/tech-confirm/modules/customer-confirm-modal.vue b/src/views/mes/pd/tech-confirm/modules/customer-confirm-modal.vue
new file mode 100644
index 0000000..b5c829d
--- /dev/null
+++ b/src/views/mes/pd/tech-confirm/modules/customer-confirm-modal.vue
@@ -0,0 +1,74 @@
+<script lang="ts" setup>
+import { reactive } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { Form, FormItem, Input, message } from 'ant-design-vue';
+
+import { confirmTechConfirmByCustomer } from '#/api/mes/pd/tech-confirm';
+
+const emit = defineEmits(['success']);
+
+const formData = reactive({
+  id: 0,
+  confirmNo: '',
+  customerName: '',
+  customerConfirmUser: '',
+});
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    if (!formData.customerConfirmUser.trim()) {
+      message.warning('璇疯緭鍏ュ鎴风‘璁や汉');
+      return;
+    }
+    modalApi.lock();
+    try {
+      await confirmTechConfirmByCustomer(
+        formData.id,
+        formData.customerConfirmUser.trim(),
+      );
+      await modalApi.close();
+      emit('success');
+      message.success('瀹㈡埛纭鎴愬姛');
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      return;
+    }
+    const data = modalApi.getData<{
+      customerName?: string;
+      confirmNo?: string;
+      id: number;
+    }>();
+    formData.id = data.id;
+    formData.confirmNo = data.confirmNo ?? '';
+    formData.customerName = data.customerName ?? '';
+    // 榛樿甯﹀叆瀹㈡埛鍚嶇О浣滀负纭浜猴紝瀹㈡埛瀵规帴浜哄鍚嶅彲鐢变笟鍔″憳淇
+    formData.customerConfirmUser = data.customerName ?? '';
+  },
+});
+</script>
+
+<template>
+  <Modal class="w-1/3" title="瀹㈡埛纭">
+    <Form :label-col="{ style: { width: '100px' } }">
+      <FormItem label="纭鍑界紪鍙�">
+        {{ formData.confirmNo }}
+      </FormItem>
+      <FormItem label="瀹㈡埛鍚嶇О">
+        {{ formData.customerName }}
+      </FormItem>
+      <FormItem label="瀹㈡埛纭浜�" required>
+        <Input
+          v-model:value="formData.customerConfirmUser"
+          :maxlength="64"
+          placeholder="璇疯緭鍏ュ鎴风‘璁や汉濮撳悕"
+        />
+      </FormItem>
+    </Form>
+  </Modal>
+</template>
diff --git a/src/views/mes/pd/tech-confirm/modules/form.vue b/src/views/mes/pd/tech-confirm/modules/form.vue
index 5ae8653..85a8dfe 100644
--- a/src/views/mes/pd/tech-confirm/modules/form.vue
+++ b/src/views/mes/pd/tech-confirm/modules/form.vue
@@ -8,6 +8,8 @@
   import { useVbenModal } from '@vben/common-ui';
   import { $t } from '@vben/locales';
 
+  import { MesPdTechConfirmStatusEnum } from '#/packages/constants/src';
+
   import { message } from 'ant-design-vue';
 
   import { useVbenForm } from '#/adapter/form';
@@ -55,8 +57,21 @@
         return;
       }
       modalApi.lock();
-      // 鎻愪氦琛ㄥ崟
-      const data = (await formApi.getValues()) as MesPdTechConfirmApi.TechConfirm;
+      // 鎻愪氦琛ㄥ崟锛氬彧鎻愪氦鐢ㄦ埛鍙~鍐欑殑涓氬姟瀛楁锛岀姸鎬佷笌娴佺▼娲剧敓瀛楁鐢卞悗绔寜娴佺▼鎺ㄨ繘锛屼笉鍥炰紶
+      const {
+        status,
+        drafterId,
+        drafterName,
+        draftTime,
+        reviewerId,
+        reviewerName,
+        reviewTime,
+        reviewRemark,
+        customerConfirmUser,
+        customerConfirmTime,
+        ...values
+      } = await formApi.getValues();
+      const data = values as MesPdTechConfirmApi.TechConfirm;
       try {
         await (data.id ? updateTechConfirm(data) : createTechConfirm(data));
         // 鍏抽棴骞舵彁绀�
@@ -75,7 +90,7 @@
       // 鍔犺浇鏁版嵁
       const data = modalApi.getData<{ formType: FormType; id?: number }>();
       formType.value = data.formType;
-      formApi.setState({ schema: useFormSchema(formApi) });
+      formApi.setState({ schema: useFormSchema(formType.value, formApi) });
       formApi.setDisabled(formType.value === 'detail');
       modalApi.setState({ showConfirmButton: formType.value !== 'detail' });
       if (!data?.id) {
@@ -86,6 +101,14 @@
         formData.value = await getTechConfirm(data.id);
         // 璁剧疆鍒� values
         await formApi.setValues(formData.value);
+        // 浠呰崏绋夸笌瀹℃牳涓嶉�氳繃鍙慨鏀癸紱寰呭鏍� / 宸插鏍� / 瀹㈡埛宸茬‘璁や负閿佸畾鎴栫粓鎬�
+        const editable =
+          formData.value?.status === MesPdTechConfirmStatusEnum.DRAFT ||
+          formData.value?.status === MesPdTechConfirmStatusEnum.REJECTED;
+        if (!editable) {
+          formApi.setDisabled(true);
+          modalApi.setState({ showConfirmButton: false });
+        }
       } finally {
         modalApi.unlock();
       }
diff --git a/src/views/mes/pro/task/components/gantt-chart.vue b/src/views/mes/pro/task/components/gantt-chart.vue
index a6af9f1..8f67c32 100644
--- a/src/views/mes/pro/task/components/gantt-chart.vue
+++ b/src/views/mes/pro/task/components/gantt-chart.vue
@@ -168,15 +168,29 @@
     [BarcodeBizTypeEnum.WORKORDER]: 'project',
     [BarcodeBizTypeEnum.TASK]: 'task',
   };
-  gantt.parse({
-    data: tasks.map((item: any) => ({
+  const data = tasks
+    .map((item: any) => ({
       ...item,
       type: typeMap[item.type] || item.type,
       start_date: item.startDate ? new Date(item.startDate) : undefined,
       end_date: item.endDate ? new Date(item.endDate) : undefined,
-    })),
-    links: [],
-  });
+    }))
+    // 杩囨护缂哄け/闈炴硶鏃ユ湡鐨勪换鍔★紝閬垮厤 dhtmlx-gantt 璁$畻鏃堕棿杞存椂鎶� valueOf 寮傚父
+    .filter(
+      (task: any) =>
+        task.start_date instanceof Date &&
+        task.end_date instanceof Date &&
+        !Number.isNaN(task.start_date.getTime()) &&
+        !Number.isNaN(task.end_date.getTime()),
+    );
+  if (data.length === 0) {
+    return;
+  }
+  try {
+    gantt.parse({ data, links: [] });
+  } catch (error) {
+    console.error('鐢樼壒鍥炬覆鏌撳け璐ワ細', error);
+  }
 }
 
 watch(
diff --git a/src/views/wls/components/process-select-modal.vue b/src/views/wls/components/process-select-modal.vue
deleted file mode 100644
index d32d546..0000000
--- a/src/views/wls/components/process-select-modal.vue
+++ /dev/null
@@ -1,83 +0,0 @@
-<script lang="ts" setup>
-import { computed, ref } from 'vue';
-
-import { useVbenModal } from '@vben/common-ui';
-
-import { message, Select } from 'ant-design-vue';
-
-interface ProcessDefinition {
-  id: string;
-  name: string;
-  key: string;
-}
-
-type ProcessSubmitFn = (id: number, processDefinitionKey: string) => Promise<unknown>;
-
-const emit = defineEmits(['success']);
-
-const bizId = ref<number>();
-const processList = ref<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 modalApi.getData<{ submitFn: ProcessSubmitFn }>().submitFn(
-        bizId.value!,
-        selectedProcessKey.value,
-      );
-      await modalApi.close();
-      emit('success');
-      message.success('鎻愪氦瀹℃壒鎴愬姛');
-    } finally {
-      modalApi.unlock();
-    }
-  },
-  async onOpenChange(isOpen: boolean) {
-    if (!isOpen) {
-      bizId.value = undefined;
-      selectedProcessKey.value = '';
-      processList.value = [];
-      return;
-    }
-    const data = modalApi.getData<{
-      id: number;
-      processList: ProcessDefinition[];
-      submitFn: ProcessSubmitFn;
-    }>();
-    bizId.value = data.id;
-    processList.value = data.processList || [];
-    // 榛樿閫夋嫨绗竴涓祦绋�
-    const first = processList.value[0];
-    if (first) {
-      selectedProcessKey.value = first.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: 200px"
-        placeholder="璇烽�夋嫨瀹℃壒娴佺▼"
-      />
-    </div>
-  </Modal>
-</template>
\ No newline at end of file
diff --git a/src/views/wls/equipmentreceipt/index.vue b/src/views/wls/equipmentreceipt/index.vue
index fe1d8a0..f26e084 100644
--- a/src/views/wls/equipmentreceipt/index.vue
+++ b/src/views/wls/equipmentreceipt/index.vue
@@ -2,10 +2,11 @@
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { MesWmEquipmentReceiptApi } from '#/api/mes/wm/equipmentreceipt';
 
-import { useRouter } from 'vue-router';
+import { ref } from 'vue';
 
 import { Page, useVbenModal } from '@vben/common-ui';
 import { MesWmEquipmentReceiptStatusEnum } from '@vben/constants';
+import { useUserStore } from '@vben/stores';
 
 import { Button, message, Tag } from 'ant-design-vue';
 
@@ -13,31 +14,54 @@
 import {
   cancelEquipmentReceipt,
   deleteEquipmentReceipt,
-  getEquipmentReceiptApproveProcessList,
+  getEquipmentReceiptApproverIds,
   getEquipmentReceiptPage,
   submitEquipmentReceiptApproval,
 } from '#/api/mes/wm/equipmentreceipt';
 import { $t } from '#/locales';
 
-import ProcessSelectModal from '#/views/wls/components/process-select-modal.vue';
 import { EQUIPMENT_RECEIPT_STATUS_MAP, useGridColumns, useGridFormSchema } from './data';
+import AuditModal from './modules/audit-modal.vue';
 import Form from './modules/form.vue';
 
-const router = useRouter();
+const userStore = useUserStore();
+const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鏍镐汉鏉冮檺鍒ゆ柇
+
+/** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鏍镐汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+const approverIds = ref<number[]>([]);
+
+/** 鎷夊彇瀹℃牳浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇鍒楄〃鍔犺浇 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getEquipmentReceiptApproverIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鏍镐汉锛屼笖鍗曟嵁澶勪簬瀹℃壒涓姸鎬� */
+function canAudit(row: MesWmEquipmentReceiptApi.EquipmentReceipt) {
+  return (
+    row.status === MesWmEquipmentReceiptStatusEnum.APPROVING &&
+    !!currentUserId &&
+    approverIds.value.includes(currentUserId)
+  );
+}
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
 });
 
-const [ProcessModal, processModalApi] = useVbenModal({
-  connectedComponent: ProcessSelectModal,
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
   destroyOnClose: true,
 });
 
-/** 鍒锋柊琛ㄦ牸 */
+/** 鍒锋柊琛ㄦ牸涓庡鏍镐汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
 }
 
 /** 鍒涘缓璁惧鍏ュ簱鍗� */
@@ -60,41 +84,29 @@
   formModalApi.setData({ formType: 'finish', id: row.id }).open();
 }
 
-/** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
-async function handleSubmitApproval(row: MesWmEquipmentReceiptApi.EquipmentReceipt) {
+/** 鎻愪氦瀹℃壒 - 瀹℃牳浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�鎸囧畾锛屾澶勬棤闇�閫夋嫨 */
+async function handleSubmitApproval(
+  row: MesWmEquipmentReceiptApi.EquipmentReceipt,
+) {
   const hideLoading = message.loading({
-    content: '鑾峰彇瀹℃壒娴佺▼...',
+    content: '姝e湪鎻愪氦瀹℃壒...',
     duration: 0,
   });
   try {
-    const processList = await getEquipmentReceiptApproveProcessList();
-    hideLoading();
-    if (!processList || processList.length === 0) {
-      message.warning('鏆傛棤鍙敤瀹℃壒娴佺▼锛岃鍏堥厤缃� BPM 娴佺▼');
-      return;
-    }
-    processModalApi
-      .setData({
-        id: row.id,
-        processList,
-        submitFn: submitEquipmentReceiptApproval,
-      })
-      .open();
-  } catch {
+    await submitEquipmentReceiptApproval(row.id!);
+    message.success('鎻愪氦瀹℃壒鎴愬姛');
+    handleRefresh();
+  } finally {
     hideLoading();
   }
 }
 
-/** 鏌ョ湅瀹℃壒璇︽儏 */
-function handleProcessDetail(row: MesWmEquipmentReceiptApi.EquipmentReceipt) {
-  if (!row.processInstanceId) {
-    message.warning('璇ュ崟鎹皻鏈彁浜ゅ鎵�');
-    return;
-  }
-  router.push({
-    name: 'BpmProcessInstanceDetail',
-    query: { id: row.processInstanceId },
-  });
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(
+  row: MesWmEquipmentReceiptApi.EquipmentReceipt,
+  pass: boolean,
+) {
+  auditModalApi.setData({ pass, row }).open();
 }
 
 /** 鍒犻櫎璁惧鍏ュ簱鍗� */
@@ -150,11 +162,14 @@
 });
 
 const StatusEnum = MesWmEquipmentReceiptStatusEnum;
+
+// 杩涘叆椤甸潰鍗虫媺鍙栧鏍镐汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+loadApproverIds();
 </script>
 
 <template>
   <Page auto-content-height><FormModal @success="handleRefresh" />
-    <ProcessModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
 
     <Grid table-title="璁惧鍏ュ簱鍗曞垪琛�">
       <template #toolbar-tools>
@@ -207,15 +222,31 @@
               label: '鎻愪氦瀹℃壒',
               type: 'link',
               auth: ['mes:wm:equipment-receipt:submit'],
+              // 鑽夌鍙彁浜わ紱瀹℃牳涓嶉�氳繃浼氶��鍥炶崏绋匡紝鍙慨鏀瑰悗閲嶆柊鎻愪氦
               ifShow: row.status === StatusEnum.PREPARE,
-              onClick: handleSubmitApproval.bind(null, row),
+              // 瀹℃牳浜虹敱瀹℃壒閰嶇疆缁熶竴鎸囧畾锛屾彁浜ゅ墠浠呬簩娆$‘璁わ紝涓嶅啀寮归�夋祦绋嬫
+              popConfirm: {
+                title: '纭鎻愪氦瀹℃壒锛熸彁浜ゅ悗鐢卞鎵归厤缃腑鎸囧畾鐨勫鏍镐汉澶勭悊銆�',
+                confirm: handleSubmitApproval.bind(null, row),
+              },
             },
             {
-              label: '鏌ョ湅瀹℃壒',
+              label: '瀹℃牳',
               type: 'link',
-              auth: ['mes:wm:equipment-receipt:query'],
-              ifShow: row.status === StatusEnum.APPROVING,
-              onClick: handleProcessDetail.bind(null, row),
+              icon: ACTION_ICON.AUDIT,
+              auth: ['mes:wm:equipment-receipt:audit'],
+              // 浠呫�屽鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃牳浜哄悕鍗曞唴銆嶆墠鏄剧ず
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              auth: ['mes:wm:equipment-receipt:audit'],
+              // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
             {
               label: '鎵ц鍏ュ簱',
diff --git a/src/views/wls/equipmentreceipt/modules/audit-modal.vue b/src/views/wls/equipmentreceipt/modules/audit-modal.vue
new file mode 100644
index 0000000..1c45c88
--- /dev/null
+++ b/src/views/wls/equipmentreceipt/modules/audit-modal.vue
@@ -0,0 +1,99 @@
+<script lang="ts" setup>
+import type { VbenFormSchema } from '#/adapter/form';
+import type { MesWmEquipmentReceiptApi } from '#/api/mes/wm/equipmentreceipt';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import { auditEquipmentReceipt } from '#/api/mes/wm/equipmentreceipt';
+
+const emit = defineEmits(['success']);
+
+const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+const rowData = ref<MesWmEquipmentReceiptApi.EquipmentReceipt>();
+const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: {
+      class: 'w-full',
+    },
+    formItemClass: 'col-span-1',
+    labelWidth: 90,
+  },
+  wrapperClass: 'grid-cols-1',
+  layout: 'horizontal',
+  schema: [],
+  showDefaultActions: false,
+});
+
+/** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+function useAuditSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'reviewRemark',
+      label: '瀹℃牳鎰忚',
+      component: 'Textarea',
+      formItemClass: 'col-span-1',
+      componentProps: {
+        rows: 3,
+        placeholder: pass.value
+          ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+          : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+      },
+      rules: pass.value ? '' : 'required',
+    },
+  ];
+}
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const values = await formApi.getValues();
+    try {
+      await auditEquipmentReceipt(
+        rowData.value!.id!,
+        pass.value,
+        values.reviewRemark,
+      );
+      await modalApi.close();
+      emit('success');
+      message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      rowData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<{
+      pass: boolean;
+      row: MesWmEquipmentReceiptApi.EquipmentReceipt;
+    }>();
+    rowData.value = data.row;
+    pass.value = data.pass;
+    formApi.setState({ schema: useAuditSchema() });
+    formApi.resetForm();
+  },
+});
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>鍗曟嵁缂栫爜锛歿{ rowData.code }}</div>
+      <div>鍗曟嵁鍚嶇О锛歿{ rowData.name }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/wls/itemreceipt/index.vue b/src/views/wls/itemreceipt/index.vue
index 552e77a..407af51 100644
--- a/src/views/wls/itemreceipt/index.vue
+++ b/src/views/wls/itemreceipt/index.vue
@@ -2,8 +2,11 @@
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { MesWmItemReceiptApi } from '#/api/mes/wm/itemreceipt';
 
+import { ref } from 'vue';
+
 import { Page, useVbenModal } from '@vben/common-ui';
 import { MesWmItemReceiptStatusEnum } from '@vben/constants';
+import { useUserStore } from '@vben/stores';
 import { downloadFileFromBlobPart } from '@vben/utils';
 
 import { Button, message } from 'ant-design-vue';
@@ -14,16 +17,47 @@
   deleteItemReceipt,
   exportItemReceipt,
   generateItemReceiptFromArrivalNotice,
+  getItemReceiptApproverIds,
   getItemReceiptPage,
 } from '#/api/mes/wm/itemreceipt';
 import { $t } from '#/locales';
 
 import { useGridColumns, useGridFormSchema } from './data';
+import AuditModal from './modules/audit-modal.vue';
 import Form from './modules/form.vue';
 import AnSelectDialog from './modules/an-select-dialog.vue';
 
+const userStore = useUserStore();
+const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鏍镐汉鏉冮檺鍒ゆ柇
+
+/** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鏍镐汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+const approverIds = ref<number[]>([]);
+
+/** 鎷夊彇瀹℃牳浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇鍒楄〃鍔犺浇 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getItemReceiptApproverIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鏍镐汉锛屼笖鍗曟嵁澶勪簬瀹℃壒涓姸鎬� */
+function canAudit(row: MesWmItemReceiptApi.ItemReceipt) {
+  return (
+    row.status === MesWmItemReceiptStatusEnum.APPROVING &&
+    !!currentUserId &&
+    approverIds.value.includes(currentUserId)
+  );
+}
+
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
+  destroyOnClose: true,
+});
+
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
   destroyOnClose: true,
 });
 
@@ -32,9 +66,10 @@
   destroyOnClose: true,
 });
 
-/** 鍒锋柊琛ㄦ牸 */
+/** 鍒锋柊琛ㄦ牸涓庡鏍镐汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
 }
 
 /** 鍒涘缓閲囪喘鍏ュ簱鍗� */
@@ -106,6 +141,11 @@
   }
 }
 
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(row: MesWmItemReceiptApi.ItemReceipt, pass: boolean) {
+  auditModalApi.setData({ pass, row }).open();
+}
+
 /** 瀵煎嚭琛ㄦ牸 */
 async function handleExport() {
   const data = await exportItemReceipt(await gridApi.formApi.getValues());
@@ -141,11 +181,15 @@
     },
   } as VxeTableGridOptions<MesWmItemReceiptApi.ItemReceipt>,
 });
+
+// 杩涘叆椤甸潰鍗虫媺鍙栧鏍镐汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+loadApproverIds();
 </script>
 
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
     <AnSelectModal @selected="handleArrivalNoticeSelected" />
 
     <Grid table-title="閲囪喘鍏ュ簱鍗曞垪琛�">
@@ -212,6 +256,24 @@
               onClick: handleStock.bind(null, row),
             },
             {
+              label: '瀹℃牳',
+              type: 'link',
+              icon: ACTION_ICON.AUDIT,
+              auth: ['mes:wm-item-receipt:audit'],
+              // 浠呫�屽鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃牳浜哄悕鍗曞唴銆嶆墠鏄剧ず
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              auth: ['mes:wm-item-receipt:audit'],
+              // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
+            },
+            {
               label: '鎵ц鍏ュ簱',
               type: 'link',
               auth: ['mes:wm-item-receipt:finish'],
diff --git a/src/views/wls/itemreceipt/modules/audit-modal.vue b/src/views/wls/itemreceipt/modules/audit-modal.vue
new file mode 100644
index 0000000..eed6ed6
--- /dev/null
+++ b/src/views/wls/itemreceipt/modules/audit-modal.vue
@@ -0,0 +1,95 @@
+<script lang="ts" setup>
+import type { VbenFormSchema } from '#/adapter/form';
+import type { MesWmItemReceiptApi } from '#/api/mes/wm/itemreceipt';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import { auditItemReceipt } from '#/api/mes/wm/itemreceipt';
+
+const emit = defineEmits(['success']);
+
+const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+const rowData = ref<MesWmItemReceiptApi.ItemReceipt>();
+const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: {
+      class: 'w-full',
+    },
+    formItemClass: 'col-span-1',
+    labelWidth: 90,
+  },
+  wrapperClass: 'grid-cols-1',
+  layout: 'horizontal',
+  schema: [],
+  showDefaultActions: false,
+});
+
+/** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+function useAuditSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'reviewRemark',
+      label: '瀹℃牳鎰忚',
+      component: 'Textarea',
+      formItemClass: 'col-span-1',
+      componentProps: {
+        rows: 3,
+        placeholder: pass.value
+          ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+          : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+      },
+      rules: pass.value ? '' : 'required',
+    },
+  ];
+}
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const values = await formApi.getValues();
+    try {
+      await auditItemReceipt(rowData.value!.id!, pass.value, values.reviewRemark);
+      await modalApi.close();
+      emit('success');
+      message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      rowData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<{
+      pass: boolean;
+      row: MesWmItemReceiptApi.ItemReceipt;
+    }>();
+    rowData.value = data.row;
+    pass.value = data.pass;
+    formApi.setState({ schema: useAuditSchema() });
+    formApi.resetForm();
+  },
+});
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>鍏ュ簱鍗曠紪鐮侊細{{ rowData.code }}</div>
+      <div>鍏ュ簱鍗曞悕绉帮細{{ rowData.name }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/wls/miscissue/index.vue b/src/views/wls/miscissue/index.vue
index 382faf1..dab0a65 100644
--- a/src/views/wls/miscissue/index.vue
+++ b/src/views/wls/miscissue/index.vue
@@ -2,10 +2,11 @@
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { MesWmMiscIssueApi } from '#/api/mes/wm/miscissue';
 
-import { useRouter } from 'vue-router';
+import { ref } from 'vue';
 
 import { Page, useVbenModal } from '@vben/common-ui';
 import { MesWmMiscIssueStatusEnum } from '@vben/constants';
+import { useUserStore } from '@vben/stores';
 import { downloadFileFromBlobPart } from '@vben/utils';
 
 import { Button, message } from 'ant-design-vue';
@@ -15,31 +16,54 @@
   cancelMiscIssue,
   deleteMiscIssue,
   exportMiscIssue,
-  getMiscIssueApproveProcessList,
+  getMiscIssueApproverIds,
   getMiscIssuePage,
   submitMiscIssueApproval,
 } from '#/api/mes/wm/miscissue';
 import { $t } from '#/locales';
 
-import ProcessSelectModal from '#/views/wls/components/process-select-modal.vue';
 import { useGridColumns, useGridFormSchema } from './data';
+import AuditModal from './modules/audit-modal.vue';
 import Form from './modules/form.vue';
 
-const router = useRouter();
+const userStore = useUserStore();
+const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鏍镐汉鏉冮檺鍒ゆ柇
+
+/** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鏍镐汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+const approverIds = ref<number[]>([]);
+
+/** 鎷夊彇瀹℃牳浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇鍒楄〃鍔犺浇 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getMiscIssueApproverIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鏍镐汉锛屼笖鍗曟嵁澶勪簬瀹℃壒涓姸鎬� */
+function canAudit(row: MesWmMiscIssueApi.MiscIssue) {
+  return (
+    row.status === MesWmMiscIssueStatusEnum.APPROVING &&
+    !!currentUserId &&
+    approverIds.value.includes(currentUserId)
+  );
+}
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
 });
 
-const [ProcessModal, processModalApi] = useVbenModal({
-  connectedComponent: ProcessSelectModal,
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
   destroyOnClose: true,
 });
 
-/** 鍒锋柊琛ㄦ牸 */
+/** 鍒锋柊琛ㄦ牸涓庡鏍镐汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
 }
 
 /** 鍒涘缓鏉傞」鍑哄簱鍗� */
@@ -62,41 +86,24 @@
   formModalApi.setData({ formType: 'finish', id: row.id }).open();
 }
 
-/** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
+/** 鎻愪氦瀹℃壒 - 瀹℃牳浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�鎸囧畾锛屾澶勬棤闇�閫夋嫨 */
 async function handleSubmitApproval(row: MesWmMiscIssueApi.MiscIssue) {
   const hideLoading = message.loading({
-    content: '鑾峰彇瀹℃壒娴佺▼...',
+    content: '姝e湪鎻愪氦瀹℃壒...',
     duration: 0,
   });
   try {
-    const processList = await getMiscIssueApproveProcessList();
-    hideLoading();
-    if (!processList || processList.length === 0) {
-      message.warning('鏆傛棤鍙敤瀹℃壒娴佺▼锛岃鍏堥厤缃� BPM 娴佺▼');
-      return;
-    }
-    processModalApi
-      .setData({
-        id: row.id,
-        processList,
-        submitFn: submitMiscIssueApproval,
-      })
-      .open();
-  } catch {
+    await submitMiscIssueApproval(row.id!);
+    message.success('鎻愪氦瀹℃壒鎴愬姛');
+    handleRefresh();
+  } finally {
     hideLoading();
   }
 }
 
-/** 鏌ョ湅瀹℃壒璇︽儏 */
-function handleProcessDetail(row: MesWmMiscIssueApi.MiscIssue) {
-  if (!row.processInstanceId) {
-    message.warning('璇ュ崟鎹皻鏈彁浜ゅ鎵�');
-    return;
-  }
-  router.push({
-    name: 'BpmProcessInstanceDetail',
-    query: { id: row.processInstanceId },
-  });
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(row: MesWmMiscIssueApi.MiscIssue, pass: boolean) {
+  auditModalApi.setData({ pass, row }).open();
 }
 
 /** 鍒犻櫎鏉傞」鍑哄簱鍗� */
@@ -156,11 +163,15 @@
     },
   } as VxeTableGridOptions<MesWmMiscIssueApi.MiscIssue>,
 });
+
+// 杩涘叆椤甸潰鍗虫媺鍙栧鏍镐汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+loadApproverIds();
 </script>
 
 <template>
-  <Page auto-content-height><FormModal @success="handleRefresh" />
-    <ProcessModal @success="handleRefresh" />
+  <Page auto-content-height>
+    <FormModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
 
     <Grid table-title="鏉傞」鍑哄簱鍗曞垪琛�">
       <template #toolbar-tools>
@@ -216,14 +227,29 @@
               type: 'link',
               auth: ['mes:wm-misc-issue:update'],
               ifShow: row.status === MesWmMiscIssueStatusEnum.PREPARE,
-              onClick: handleSubmitApproval.bind(null, row),
+              // 瀹℃牳浜虹敱瀹℃壒閰嶇疆缁熶竴鎸囧畾锛屾彁浜ゅ墠浠呬簩娆$‘璁わ紝涓嶅啀寮归�夋祦绋嬫
+              popConfirm: {
+                title: '纭鎻愪氦瀹℃壒锛熸彁浜ゅ悗鐢卞鎵归厤缃腑鎸囧畾鐨勫鏍镐汉澶勭悊銆�',
+                confirm: handleSubmitApproval.bind(null, row),
+              },
             },
             {
-              label: '鏌ョ湅瀹℃壒',
+              label: '瀹℃牳',
               type: 'link',
-              auth: ['mes:wm-misc-issue:query'],
-              ifShow: row.status === MesWmMiscIssueStatusEnum.APPROVING,
-              onClick: handleProcessDetail.bind(null, row),
+              icon: ACTION_ICON.AUDIT,
+              auth: ['mes:wm-misc-issue:audit'],
+              // 浠呫�屽鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃牳浜哄悕鍗曞唴銆嶆墠鏄剧ず
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              auth: ['mes:wm-misc-issue:audit'],
+              // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
             {
               label: '鎵ц鍑哄簱',
diff --git a/src/views/wls/miscissue/modules/audit-modal.vue b/src/views/wls/miscissue/modules/audit-modal.vue
new file mode 100644
index 0000000..7c11678
--- /dev/null
+++ b/src/views/wls/miscissue/modules/audit-modal.vue
@@ -0,0 +1,95 @@
+<script lang="ts" setup>
+import type { VbenFormSchema } from '#/adapter/form';
+import type { MesWmMiscIssueApi } from '#/api/mes/wm/miscissue';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import { auditMiscIssue } from '#/api/mes/wm/miscissue';
+
+const emit = defineEmits(['success']);
+
+const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+const rowData = ref<MesWmMiscIssueApi.MiscIssue>();
+const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: {
+      class: 'w-full',
+    },
+    formItemClass: 'col-span-1',
+    labelWidth: 90,
+  },
+  wrapperClass: 'grid-cols-1',
+  layout: 'horizontal',
+  schema: [],
+  showDefaultActions: false,
+});
+
+/** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+function useAuditSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'reviewRemark',
+      label: '瀹℃牳鎰忚',
+      component: 'Textarea',
+      formItemClass: 'col-span-1',
+      componentProps: {
+        rows: 3,
+        placeholder: pass.value
+          ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+          : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+      },
+      rules: pass.value ? '' : 'required',
+    },
+  ];
+}
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const values = await formApi.getValues();
+    try {
+      await auditMiscIssue(rowData.value!.id!, pass.value, values.reviewRemark);
+      await modalApi.close();
+      emit('success');
+      message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      rowData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<{
+      pass: boolean;
+      row: MesWmMiscIssueApi.MiscIssue;
+    }>();
+    rowData.value = data.row;
+    pass.value = data.pass;
+    formApi.setState({ schema: useAuditSchema() });
+    formApi.resetForm();
+  },
+});
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>鍑哄簱鍗曠紪鍙凤細{{ rowData.code }}</div>
+      <div>鍑哄簱鍗曞悕绉帮細{{ rowData.name }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/wls/miscreceipt/index.vue b/src/views/wls/miscreceipt/index.vue
index 03a6f12..f701c8c 100644
--- a/src/views/wls/miscreceipt/index.vue
+++ b/src/views/wls/miscreceipt/index.vue
@@ -2,10 +2,11 @@
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { MesWmMiscReceiptApi } from '#/api/mes/wm/miscreceipt';
 
-import { useRouter } from 'vue-router';
+import { ref } from 'vue';
 
 import { Page, useVbenModal } from '@vben/common-ui';
 import { MesWmMiscReceiptStatusEnum } from '@vben/constants';
+import { useUserStore } from '@vben/stores';
 import { downloadFileFromBlobPart } from '@vben/utils';
 
 import { Button, message } from 'ant-design-vue';
@@ -15,31 +16,54 @@
   cancelMiscReceipt,
   deleteMiscReceipt,
   exportMiscReceipt,
-  getMiscReceiptApproveProcessList,
+  getMiscReceiptApproverIds,
   getMiscReceiptPage,
   submitMiscReceiptApproval,
 } from '#/api/mes/wm/miscreceipt';
 import { $t } from '#/locales';
 
-import ProcessSelectModal from '#/views/wls/components/process-select-modal.vue';
 import { useGridColumns, useGridFormSchema } from './data';
+import AuditModal from './modules/audit-modal.vue';
 import Form from './modules/form.vue';
 
-const router = useRouter();
+const userStore = useUserStore();
+const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鏍镐汉鏉冮檺鍒ゆ柇
+
+/** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鏍镐汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+const approverIds = ref<number[]>([]);
+
+/** 鎷夊彇瀹℃牳浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇鍒楄〃鍔犺浇 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getMiscReceiptApproverIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鏍镐汉锛屼笖鍗曟嵁澶勪簬瀹℃壒涓姸鎬� */
+function canAudit(row: MesWmMiscReceiptApi.MiscReceipt) {
+  return (
+    row.status === MesWmMiscReceiptStatusEnum.APPROVING &&
+    !!currentUserId &&
+    approverIds.value.includes(currentUserId)
+  );
+}
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
 });
 
-const [ProcessModal, processModalApi] = useVbenModal({
-  connectedComponent: ProcessSelectModal,
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
   destroyOnClose: true,
 });
 
-/** 鍒锋柊琛ㄦ牸 */
+/** 鍒锋柊琛ㄦ牸涓庡鏍镐汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
 }
 
 /** 鍒涘缓鏉傞」鍏ュ簱鍗� */
@@ -62,41 +86,24 @@
   formModalApi.setData({ formType: 'finish', id: row.id }).open();
 }
 
-/** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
+/** 鎻愪氦瀹℃壒 - 瀹℃牳浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�鎸囧畾锛屾澶勬棤闇�閫夋嫨 */
 async function handleSubmitApproval(row: MesWmMiscReceiptApi.MiscReceipt) {
   const hideLoading = message.loading({
-    content: '鑾峰彇瀹℃壒娴佺▼...',
+    content: '姝e湪鎻愪氦瀹℃壒...',
     duration: 0,
   });
   try {
-    const processList = await getMiscReceiptApproveProcessList();
-    hideLoading();
-    if (!processList || processList.length === 0) {
-      message.warning('鏆傛棤鍙敤瀹℃壒娴佺▼锛岃鍏堥厤缃� BPM 娴佺▼');
-      return;
-    }
-    processModalApi
-      .setData({
-        id: row.id,
-        processList,
-        submitFn: submitMiscReceiptApproval,
-      })
-      .open();
-  } catch {
+    await submitMiscReceiptApproval(row.id!);
+    message.success('鎻愪氦瀹℃壒鎴愬姛');
+    handleRefresh();
+  } finally {
     hideLoading();
   }
 }
 
-/** 鏌ョ湅瀹℃壒璇︽儏 */
-function handleProcessDetail(row: MesWmMiscReceiptApi.MiscReceipt) {
-  if (!row.processInstanceId) {
-    message.warning('璇ュ崟鎹皻鏈彁浜ゅ鎵�');
-    return;
-  }
-  router.push({
-    name: 'BpmProcessInstanceDetail',
-    query: { id: row.processInstanceId },
-  });
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(row: MesWmMiscReceiptApi.MiscReceipt, pass: boolean) {
+  auditModalApi.setData({ pass, row }).open();
 }
 
 /** 鍒犻櫎鏉傞」鍏ュ簱鍗� */
@@ -156,11 +163,15 @@
     },
   } as VxeTableGridOptions<MesWmMiscReceiptApi.MiscReceipt>,
 });
+
+// 杩涘叆椤甸潰鍗虫媺鍙栧鏍镐汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+loadApproverIds();
 </script>
 
 <template>
-  <Page auto-content-height><FormModal @success="handleRefresh" />
-    <ProcessModal @success="handleRefresh" />
+  <Page auto-content-height>
+    <FormModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
 
     <Grid table-title="鏉傞」鍏ュ簱鍗曞垪琛�">
       <template #toolbar-tools>
@@ -216,14 +227,29 @@
               type: 'link',
               auth: ['mes:wm:misc-receipt:submit'],
               ifShow: row.status === MesWmMiscReceiptStatusEnum.PREPARE,
-              onClick: handleSubmitApproval.bind(null, row),
+              // 瀹℃牳浜虹敱瀹℃壒閰嶇疆缁熶竴鎸囧畾锛屾彁浜ゅ墠浠呬簩娆$‘璁わ紝涓嶅啀寮归�夋祦绋嬫
+              popConfirm: {
+                title: '纭鎻愪氦瀹℃壒锛熸彁浜ゅ悗鐢卞鎵归厤缃腑鎸囧畾鐨勫鏍镐汉澶勭悊銆�',
+                confirm: handleSubmitApproval.bind(null, row),
+              },
             },
             {
-              label: '鏌ョ湅瀹℃壒',
+              label: '瀹℃牳',
               type: 'link',
-              auth: ['mes:wm:misc-receipt:query'],
-              ifShow: row.status === MesWmMiscReceiptStatusEnum.APPROVING,
-              onClick: handleProcessDetail.bind(null, row),
+              icon: ACTION_ICON.AUDIT,
+              auth: ['mes:wm:misc-receipt:audit'],
+              // 浠呫�屽鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃牳浜哄悕鍗曞唴銆嶆墠鏄剧ず
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              auth: ['mes:wm:misc-receipt:audit'],
+              // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
             {
               label: '鎵ц鍏ュ簱',
diff --git a/src/views/wls/miscreceipt/modules/audit-modal.vue b/src/views/wls/miscreceipt/modules/audit-modal.vue
new file mode 100644
index 0000000..0bd13a5
--- /dev/null
+++ b/src/views/wls/miscreceipt/modules/audit-modal.vue
@@ -0,0 +1,95 @@
+<script lang="ts" setup>
+import type { VbenFormSchema } from '#/adapter/form';
+import type { MesWmMiscReceiptApi } from '#/api/mes/wm/miscreceipt';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import { auditMiscReceipt } from '#/api/mes/wm/miscreceipt';
+
+const emit = defineEmits(['success']);
+
+const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+const rowData = ref<MesWmMiscReceiptApi.MiscReceipt>();
+const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: {
+      class: 'w-full',
+    },
+    formItemClass: 'col-span-1',
+    labelWidth: 90,
+  },
+  wrapperClass: 'grid-cols-1',
+  layout: 'horizontal',
+  schema: [],
+  showDefaultActions: false,
+});
+
+/** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+function useAuditSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'reviewRemark',
+      label: '瀹℃牳鎰忚',
+      component: 'Textarea',
+      formItemClass: 'col-span-1',
+      componentProps: {
+        rows: 3,
+        placeholder: pass.value
+          ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+          : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+      },
+      rules: pass.value ? '' : 'required',
+    },
+  ];
+}
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const values = await formApi.getValues();
+    try {
+      await auditMiscReceipt(rowData.value!.id!, pass.value, values.reviewRemark);
+      await modalApi.close();
+      emit('success');
+      message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      rowData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<{
+      pass: boolean;
+      row: MesWmMiscReceiptApi.MiscReceipt;
+    }>();
+    rowData.value = data.row;
+    pass.value = data.pass;
+    formApi.setState({ schema: useAuditSchema() });
+    formApi.resetForm();
+  },
+});
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>鍏ュ簱鍗曠紪鍙凤細{{ rowData.code }}</div>
+      <div>鍏ュ簱鍗曞悕绉帮細{{ rowData.name }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/wls/returnvendor/index.vue b/src/views/wls/returnvendor/index.vue
index 62d5716..8c38240 100644
--- a/src/views/wls/returnvendor/index.vue
+++ b/src/views/wls/returnvendor/index.vue
@@ -2,10 +2,11 @@
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { MesWmReturnVendorApi } from '#/api/mes/wm/returnvendor';
 
-import { useRouter } from 'vue-router';
+import { ref } from 'vue';
 
 import { Page, useVbenModal } from '@vben/common-ui';
 import { MesWmReturnVendorStatusEnum } from '@vben/constants';
+import { useUserStore } from '@vben/stores';
 import { downloadFileFromBlobPart } from '@vben/utils';
 
 import { Button, message } from 'ant-design-vue';
@@ -15,31 +16,54 @@
   cancelReturnVendor,
   deleteReturnVendor,
   exportReturnVendor,
-  getReturnVendorApproveProcessList,
+  getReturnVendorApproverIds,
   getReturnVendorPage,
   submitReturnVendorApproval,
 } from '#/api/mes/wm/returnvendor';
 import { $t } from '#/locales';
 
-import ProcessSelectModal from '#/views/wls/components/process-select-modal.vue';
 import { useGridColumns, useGridFormSchema } from './data';
+import AuditModal from './modules/audit-modal.vue';
 import Form from './modules/form.vue';
 
-const router = useRouter();
+const userStore = useUserStore();
+const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鏍镐汉鏉冮檺鍒ゆ柇
+
+/** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鏍镐汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+const approverIds = ref<number[]>([]);
+
+/** 鎷夊彇瀹℃牳浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇鍒楄〃鍔犺浇 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getReturnVendorApproverIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鏍镐汉锛屼笖鍗曟嵁澶勪簬寰呮嫞璐х姸鎬� */
+function canAudit(row: MesWmReturnVendorApi.ReturnVendor) {
+  return (
+    row.status === MesWmReturnVendorStatusEnum.APPROVING &&
+    !!currentUserId &&
+    approverIds.value.includes(currentUserId)
+  );
+}
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
 });
 
-const [ProcessModal, processModalApi] = useVbenModal({
-  connectedComponent: ProcessSelectModal,
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
   destroyOnClose: true,
 });
 
-/** 鍒锋柊琛ㄦ牸 */
+/** 鍒锋柊琛ㄦ牸涓庡鏍镐汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
 }
 
 /** 鍒涘缓渚涘簲鍟嗛��璐у崟 */
@@ -62,41 +86,24 @@
   formModalApi.setData({ formType: 'stock', id: row.id }).open();
 }
 
-/** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
+/** 鎻愪氦瀹℃壒 - 瀹℃牳浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�鎸囧畾锛屾澶勬棤闇�閫夋嫨 */
 async function handleSubmitApproval(row: MesWmReturnVendorApi.ReturnVendor) {
   const hideLoading = message.loading({
-    content: '鑾峰彇瀹℃壒娴佺▼...',
+    content: '姝e湪鎻愪氦瀹℃壒...',
     duration: 0,
   });
   try {
-    const processList = await getReturnVendorApproveProcessList();
-    hideLoading();
-    if (!processList || processList.length === 0) {
-      message.warning('鏆傛棤鍙敤瀹℃壒娴佺▼锛岃鍏堥厤缃� BPM 娴佺▼');
-      return;
-    }
-    processModalApi
-      .setData({
-        id: row.id,
-        processList,
-        submitFn: submitReturnVendorApproval,
-      })
-      .open();
-  } catch {
+    await submitReturnVendorApproval(row.id!);
+    message.success('鎻愪氦瀹℃壒鎴愬姛');
+    handleRefresh();
+  } finally {
     hideLoading();
   }
 }
 
-/** 鏌ョ湅瀹℃壒璇︽儏 */
-function handleProcessDetail(row: MesWmReturnVendorApi.ReturnVendor) {
-  if (!row.processInstanceId) {
-    message.warning('璇ュ崟鎹皻鏈彁浜ゅ鎵�');
-    return;
-  }
-  router.push({
-    name: 'BpmProcessInstanceDetail',
-    query: { id: row.processInstanceId },
-  });
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(row: MesWmReturnVendorApi.ReturnVendor, pass: boolean) {
+  auditModalApi.setData({ pass, row }).open();
 }
 
 /** 瀹屾垚閫�璐� */
@@ -161,11 +168,15 @@
     },
   } as VxeTableGridOptions<MesWmReturnVendorApi.ReturnVendor>,
 });
+
+// 杩涘叆椤甸潰鍗虫媺鍙栧鏍镐汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+loadApproverIds();
 </script>
 
 <template>
-  <Page auto-content-height><FormModal @success="handleRefresh" />
-    <ProcessModal @success="handleRefresh" />
+  <Page auto-content-height>
+    <FormModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
 
     <Grid table-title="渚涘簲鍟嗛��璐у崟鍒楄〃">
       <template #toolbar-tools>
@@ -201,7 +212,29 @@
               type: 'link',
               auth: ['mes:wm-return-vendor:update'],
               ifShow: row.status === MesWmReturnVendorStatusEnum.PREPARE,
-              onClick: handleSubmitApproval.bind(null, row),
+              // 瀹℃牳浜虹敱瀹℃壒閰嶇疆缁熶竴鎸囧畾锛屾彁浜ゅ墠浠呬簩娆$‘璁わ紝涓嶅啀寮归�夋祦绋嬫
+              popConfirm: {
+                title: '纭鎻愪氦瀹℃壒锛熸彁浜ゅ悗鐢卞鎵归厤缃腑鎸囧畾鐨勫鏍镐汉澶勭悊銆�',
+                confirm: handleSubmitApproval.bind(null, row),
+              },
+            },
+            {
+              label: '瀹℃牳',
+              type: 'link',
+              icon: ACTION_ICON.AUDIT,
+              auth: ['mes:wm-return-vendor:audit'],
+              // 浠呫�屽緟鎷h揣 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃牳浜哄悕鍗曞唴銆嶆墠鏄剧ず
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              auth: ['mes:wm-return-vendor:audit'],
+              // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
             {
               label: $t('common.edit'),
@@ -222,13 +255,6 @@
                 title: $t('ui.actionMessage.deleteConfirm', [row.code]),
                 confirm: handleDelete.bind(null, row),
               },
-            },
-            {
-              label: '鏌ョ湅瀹℃壒',
-              type: 'link',
-              auth: ['mes:wm-return-vendor:query'],
-              ifShow: row.status === MesWmReturnVendorStatusEnum.APPROVING,
-              onClick: handleProcessDetail.bind(null, row),
             },
             {
               label: '鎵ц鎷h揣',
diff --git a/src/views/wls/returnvendor/modules/audit-modal.vue b/src/views/wls/returnvendor/modules/audit-modal.vue
new file mode 100644
index 0000000..5d07111
--- /dev/null
+++ b/src/views/wls/returnvendor/modules/audit-modal.vue
@@ -0,0 +1,95 @@
+<script lang="ts" setup>
+import type { VbenFormSchema } from '#/adapter/form';
+import type { MesWmReturnVendorApi } from '#/api/mes/wm/returnvendor';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import { auditReturnVendor } from '#/api/mes/wm/returnvendor';
+
+const emit = defineEmits(['success']);
+
+const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+const rowData = ref<MesWmReturnVendorApi.ReturnVendor>();
+const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: {
+      class: 'w-full',
+    },
+    formItemClass: 'col-span-1',
+    labelWidth: 90,
+  },
+  wrapperClass: 'grid-cols-1',
+  layout: 'horizontal',
+  schema: [],
+  showDefaultActions: false,
+});
+
+/** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+function useAuditSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'reviewRemark',
+      label: '瀹℃牳鎰忚',
+      component: 'Textarea',
+      formItemClass: 'col-span-1',
+      componentProps: {
+        rows: 3,
+        placeholder: pass.value
+          ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+          : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+      },
+      rules: pass.value ? '' : 'required',
+    },
+  ];
+}
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const values = await formApi.getValues();
+    try {
+      await auditReturnVendor(rowData.value!.id!, pass.value, values.reviewRemark);
+      await modalApi.close();
+      emit('success');
+      message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      rowData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<{
+      pass: boolean;
+      row: MesWmReturnVendorApi.ReturnVendor;
+    }>();
+    rowData.value = data.row;
+    pass.value = data.pass;
+    formApi.setState({ schema: useAuditSchema() });
+    formApi.resetForm();
+  },
+});
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>閫�璐у崟缂栧彿锛歿{ rowData.code }}</div>
+      <div>渚涘簲鍟嗭細{{ rowData.vendorName }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/wls/stocktaking/task/index.vue b/src/views/wls/stocktaking/task/index.vue
index 30c46cc..d770142 100644
--- a/src/views/wls/stocktaking/task/index.vue
+++ b/src/views/wls/stocktaking/task/index.vue
@@ -3,10 +3,10 @@
 import type { MesWmStockTakingTaskApi } from '#/api/mes/wm/stocktaking/task';
 
 import { ref } from 'vue';
-import { useRouter } from 'vue-router';
 
 import { Page, useVbenModal } from '@vben/common-ui';
 import { MesWmStockTakingTaskStatusEnum } from '@vben/constants';
+import { useUserStore } from '@vben/stores';
 import { downloadFileFromBlobPart } from '@vben/utils';
 
 import { Button, message } from 'ant-design-vue';
@@ -16,7 +16,7 @@
   cancelStockTaking,
   deleteStockTaking,
   exportStockTaking,
-  getStockTakingApproveProcessList,
+  getStockTakingApproverIds,
   getStockTakingPage,
   submitStockTakingApproval,
 } from '#/api/mes/wm/stocktaking/task';
@@ -26,25 +26,70 @@
 } from '#/api/wls/stocktaking/task/line';
 import { $t } from '#/locales';
 
-import ProcessSelectModal from '#/views/wls/components/process-select-modal.vue';
 import { useGridColumns, useGridFormSchema } from './data';
+import AuditModal from './modules/audit-modal.vue';
 import Form from './modules/form.vue';
 
-const router = useRouter();
+const userStore = useUserStore();
+const currentUserId = userStore.userInfo?.id; // 褰撳墠鐧诲綍鐢ㄦ埛 ID锛岀敤浜庡鏍镐汉鏉冮檺鍒ゆ柇
+
+/** 瀹℃壒閰嶇疆涓涓氬姟鐨勫鏍镐汉缂栧彿闆嗗悎锛屽喅瀹氥�屽鏍� / 瀹℃牳涓嶉�氳繃銆嶆寜閽槸鍚﹀睍绀� */
+const approverIds = ref<number[]>([]);
+
+/** 鎷夊彇瀹℃牳浜洪泦鍚堬紱澶辫触鏃朵繚鎸佷负绌猴紙鎸夐挳涓嶅睍绀猴級锛屼笉闃绘柇鍒楄〃鍔犺浇 */
+async function loadApproverIds() {
+  try {
+    approverIds.value = (await getStockTakingApproverIds()) ?? [];
+  } catch {
+    approverIds.value = [];
+  }
+}
+
+/**
+ * 鏈疆鐩樼偣缁撴灉瀹℃壒鏄惁宸叉彁浜ゃ�佹鍦ㄧ瓑寰呭鏍�
+ *
+ * 浠诲姟鐘舵�併�岀洏鐐逛腑銆嶅悓鏃舵壙杞界洏鐐逛腑涓庣粨鏋滃鎵逛腑涓や釜闃舵锛�
+ * reviewTime 闈炵┖涓� reviewerId 涓虹┖琛ㄧず缁撴灉宸叉彁浜ゃ�佺瓑寰呭鏍革紱
+ * 瀹℃牳涓嶉�氳繃鍚� reviewerId 闈炵┖锛屼换鍔″洖鍒板彲閲嶆柊鎻愪氦缁撴灉瀹℃壒鐨勭姸鎬併��
+ */
+function isResultApprovalPending(row: MesWmStockTakingTaskApi.StockTakingTask) {
+  return !row.reviewerId && !!row.reviewTime;
+}
+
+/** 褰撳墠鐧诲綍鐢ㄦ埛鏄惁涓鸿涓氬姟鐨勫鏍镐汉锛屼笖缁撴灉瀹℃壒宸叉彁浜ょ瓑寰呭鏍� */
+function canAudit(row: MesWmStockTakingTaskApi.StockTakingTask) {
+  return (
+    row.status === MesWmStockTakingTaskStatusEnum.APPROVING &&
+    isResultApprovalPending(row) &&
+    !!currentUserId &&
+    approverIds.value.includes(currentUserId)
+  );
+}
+
+/** 鏄惁鍙彁浜ょ粨鏋滃鎵癸細鐩樼偣涓笖鏈疆缁撴灉瀹℃壒灏氭湭鎻愪氦 */
+function canSubmitApproval(
+  row: MesWmStockTakingTaskApi.StockTakingTask,
+) {
+  return (
+    row.status === MesWmStockTakingTaskStatusEnum.APPROVING &&
+    !isResultApprovalPending(row)
+  );
+}
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
   destroyOnClose: true,
 });
 
-const [ProcessModal, processModalApi] = useVbenModal({
-  connectedComponent: ProcessSelectModal,
+const [AuditModalComp, auditModalApi] = useVbenModal({
+  connectedComponent: AuditModal,
   destroyOnClose: true,
 });
 
-/** 鍒锋柊琛ㄦ牸 */
+/** 鍒锋柊琛ㄦ牸涓庡鏍镐汉闆嗗悎 */
 function handleRefresh() {
   gridApi.query();
+  loadApproverIds();
 }
 
 /** 鍒涘缓鐩樼偣浠诲姟 */
@@ -67,41 +112,24 @@
   formModalApi.setData({ formType: 'submit', id: row.id }).open();
 }
 
-/** 鎻愪氦缁撴灉瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
+/** 鎻愪氦缁撴灉瀹℃壒 - 瀹℃牳浜虹敱銆岀郴缁熺鐞� - 瀹℃壒閰嶇疆銆嶇粺涓�鎸囧畾锛屾澶勬棤闇�閫夋嫨 */
 async function handleSubmitApproval(row: MesWmStockTakingTaskApi.StockTakingTask) {
   const hideLoading = message.loading({
-    content: '鑾峰彇瀹℃壒娴佺▼...',
+    content: '姝e湪鎻愪氦缁撴灉瀹℃壒...',
     duration: 0,
   });
   try {
-    const processList = await getStockTakingApproveProcessList();
-    hideLoading();
-    if (!processList || processList.length === 0) {
-      message.warning('鏆傛棤鍙敤瀹℃壒娴佺▼锛岃鍏堥厤缃� BPM 娴佺▼');
-      return;
-    }
-    processModalApi
-      .setData({
-        id: row.id,
-        processList,
-        submitFn: submitStockTakingApproval,
-      })
-      .open();
-  } catch {
+    await submitStockTakingApproval(row.id!);
+    message.success('鎻愪氦缁撴灉瀹℃壒鎴愬姛');
+    handleRefresh();
+  } finally {
     hideLoading();
   }
 }
 
-/** 鏌ョ湅瀹℃壒璇︽儏 */
-function handleProcessDetail(row: MesWmStockTakingTaskApi.StockTakingTask) {
-  if (!row.processInstanceId) {
-    message.warning('璇ヤ换鍔″皻鏈彁浜ょ粨鏋滃鎵�');
-    return;
-  }
-  router.push({
-    name: 'BpmProcessInstanceDetail',
-    query: { id: row.processInstanceId },
-  });
+/** 瀹℃牳 - 閫氳繃 / 涓嶉�氳繃 */
+function handleAudit(row: MesWmStockTakingTaskApi.StockTakingTask, pass: boolean) {
+  auditModalApi.setData({ pass, row }).open();
 }
 
 /** 鍙栨秷鐩樼偣浠诲姟 */
@@ -198,11 +226,15 @@
 });
 
 const StatusEnum = MesWmStockTakingTaskStatusEnum;
+
+// 杩涘叆椤甸潰鍗虫媺鍙栧鏍镐汉闆嗗悎锛岀敤浜庢帶鍒跺鏍告寜閽樉闅�
+loadApproverIds();
 </script>
 
 <template>
-  <Page auto-content-height><FormModal @success="handleRefresh" />
-    <ProcessModal @success="handleRefresh" />
+  <Page auto-content-height>
+    <FormModal @success="handleRefresh" />
+    <AuditModalComp @success="handleRefresh" />
 
     <Grid table-title="鐩樼偣浠诲姟鍒楄〃">
       <template #toolbar-tools>
@@ -279,15 +311,31 @@
               label: '鎻愪氦缁撴灉瀹℃壒',
               type: 'link',
               auth: ['mes:wm-stock-taking-task:update'],
-              ifShow: row.status === StatusEnum.APPROVING,
-              onClick: handleSubmitApproval.bind(null, row),
+              // 鐩樼偣涓笖鏈疆缁撴灉瀹℃壒灏氭湭鎻愪氦鏃舵墠鍙彁浜�
+              ifShow: canSubmitApproval(row),
+              // 瀹℃牳浜虹敱瀹℃壒閰嶇疆缁熶竴鎸囧畾锛屾彁浜ゅ墠浠呬簩娆$‘璁わ紝涓嶅啀寮归�夋祦绋嬫
+              popConfirm: {
+                title: '纭鎻愪氦缁撴灉瀹℃壒锛熸彁浜ゅ悗鐢卞鎵归厤缃腑鎸囧畾鐨勫鏍镐汉澶勭悊銆�',
+                confirm: handleSubmitApproval.bind(null, row),
+              },
             },
             {
-              label: '鏌ョ湅瀹℃壒',
+              label: '瀹℃牳',
               type: 'link',
-              auth: ['mes:wm-stock-taking-task:query'],
-              ifShow: row.status === StatusEnum.APPROVING,
-              onClick: handleProcessDetail.bind(null, row),
+              icon: ACTION_ICON.AUDIT,
+              auth: ['mes:wm-stock-taking-task:audit'],
+              // 浠呫�岀粨鏋滃鎵逛腑 + 褰撳墠鐧诲綍鐢ㄦ埛鍦ㄥ鎵归厤缃殑瀹℃牳浜哄悕鍗曞唴銆嶆墠鏄剧ず
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, true),
+            },
+            {
+              label: '瀹℃牳涓嶉�氳繃',
+              type: 'link',
+              danger: true,
+              auth: ['mes:wm-stock-taking-task:audit'],
+              // 鍚屼笂锛屼笌銆屽鏍搞�嶆垚瀵瑰嚭鐜�
+              ifShow: canAudit(row),
+              onClick: handleAudit.bind(null, row, false),
             },
             {
               label: '鍙栨秷',
diff --git a/src/views/wls/stocktaking/task/modules/audit-modal.vue b/src/views/wls/stocktaking/task/modules/audit-modal.vue
new file mode 100644
index 0000000..4560211
--- /dev/null
+++ b/src/views/wls/stocktaking/task/modules/audit-modal.vue
@@ -0,0 +1,99 @@
+<script lang="ts" setup>
+import type { VbenFormSchema } from '#/adapter/form';
+import type { MesWmStockTakingTaskApi } from '#/api/mes/wm/stocktaking/task';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import { auditStockTakingResult } from '#/api/mes/wm/stocktaking/task';
+
+const emit = defineEmits(['success']);
+
+const pass = ref(true); // true=瀹℃牳閫氳繃 / false=瀹℃牳涓嶉�氳繃
+const rowData = ref<MesWmStockTakingTaskApi.StockTakingTask>();
+const getTitle = computed(() => (pass.value ? '瀹℃牳閫氳繃' : '瀹℃牳涓嶉�氳繃'));
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: {
+      class: 'w-full',
+    },
+    formItemClass: 'col-span-1',
+    labelWidth: 90,
+  },
+  wrapperClass: 'grid-cols-1',
+  layout: 'horizontal',
+  schema: [],
+  showDefaultActions: false,
+});
+
+/** 瀹℃牳琛ㄥ崟瀛楁锛氫笉閫氳繃鏃跺繀椤诲~鍐欏師鍥� */
+function useAuditSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'reviewRemark',
+      label: '瀹℃牳鎰忚',
+      component: 'Textarea',
+      formItemClass: 'col-span-1',
+      componentProps: {
+        rows: 3,
+        placeholder: pass.value
+          ? '璇疯緭鍏ュ鏍告剰瑙侊紙鍙�夛級'
+          : '璇疯緭鍏ヤ笉閫氳繃鍘熷洜锛堝繀濉級',
+      },
+      rules: pass.value ? '' : 'required',
+    },
+  ];
+}
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const values = await formApi.getValues();
+    try {
+      await auditStockTakingResult(
+        rowData.value!.id!,
+        pass.value,
+        values.reviewRemark,
+      );
+      await modalApi.close();
+      emit('success');
+      message.success(pass.value ? '瀹℃牳閫氳繃' : '宸查��鍥烇紝鍙慨鏀瑰悗閲嶆柊鎻愪氦');
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      rowData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<{
+      pass: boolean;
+      row: MesWmStockTakingTaskApi.StockTakingTask;
+    }>();
+    rowData.value = data.row;
+    pass.value = data.pass;
+    formApi.setState({ schema: useAuditSchema() });
+    formApi.resetForm();
+  },
+});
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-2/5">
+    <div v-if="rowData" class="mx-4 mb-4 text-sm text-gray-600">
+      <div>浠诲姟缂栫爜锛歿{ rowData.code }}</div>
+      <div>浠诲姟鍚嶇О锛歿{ rowData.name }}</div>
+    </div>
+    <Form class="mx-4" />
+  </Modal>
+</template>

--
Gitblit v1.9.3