From ec8ff4f6259404460a0b4437e29a40986c2c4b9d Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 12 十月 2026 18:31:43 +0800
Subject: [PATCH] feat(workflow): 集成自动化审批功能并优化多个业务模块

---
 src/views/crm/backlog/index.vue                                |   17 
 src/views/mes/pd/project/index.vue                             |   12 
 src/views/crm/invoice/index.vue                                |    5 
 src/utils/audit-ui-policy.ts                                   |   12 
 src/views/mes/pd/project/detail/index.vue                      |   12 
 src/api/energy/index.ts                                        |   39 ++
 src/views/energy/analysis/index.vue                            |   58 ++++
 src/adapter/vxe-table.ts                                       |   17 +
 src/api/hrm/resignation/index.ts                               |    2 
 src/components/description/description.vue                     |    4 
 src/views/crm/contract/index.vue                               |   10 
 src/api/mes/dv/meteringcheck/index.ts                          |    1 
 src/views/crm/customer/detail/modules/distribute-form.vue      |   13 
 src/views/bpm/oa/leave/create.vue                              |   12 
 src/views/crm/saleQuotation/data.ts                            |    6 
 src/views/erp/sale/order/index.vue                             |   33 +
 src/components/table-action/table-action.vue                   |   14 
 src/store/auth.ts                                              |    3 
 src/views/crm/customer/pool/data.ts                            |    7 
 src/views/hrm/leave/index.vue                                  |    8 
 src/api/mes/qc/iqc/index.ts                                    |    1 
 src/views/energy/alert/index.vue                               |  145 ++++++++++
 src/views/erp/finance/payment/index.vue                        |   12 
 src/views/mes/md/item/components/select.vue                    |    4 
 src/views/hrm/overtime/index.vue                               |   10 
 dist.zip                                                       |    0 
 src/views/mes/qc/iqc/index.vue                                 |    2 
 src/views/bpm/model/index.vue                                  |    2 
 src/views/mes/qc/iqc/data.ts                                   |   35 -
 src/views/crm/customer/pool/index.vue                          |  107 +++++++
 src/views/erp/sale/order/detail/index.vue                      |    6 
 src/views/crm/contract/components/data.ts                      |    6 
 src/views/crm/contract/data.ts                                 |   12 
 src/views/mes/qc/pendinginspect/index.vue                      |    1 
 src/views/erp/purchase/request/index.vue                       |   11 
 src/adapter/form.ts                                            |   15 +
 src/store/audit-policy.ts                                      |   15 +
 src/views/erp/sale/order/modules/print.vue                     |    6 
 src/api/hrm/transfer/index.ts                                  |    2 
 src/views/energy/record/index.vue                              |   12 
 src/views/crm/contract/detail/data.ts                          |   10 
 src/views/erp/sale/order/data.ts                               |    6 
 src/views/mes/qc/oqc/data.ts                                   |    7 
 src/views/mes/workbench/components/tabs/FeedbackHistoryTab.vue |    3 
 src/api/bpm/autoApprove/index.ts                               |    5 
 src/views/crm/contract/detail/approve/index.vue                |   10 
 src/views/mes/md/item/data.ts                                  |   16 +
 src/views/hrm/attendance/exception/index.vue                   |   10 
 src/views/erp/sale/order/modules/form.vue                      |    6 
 src/views/crm/customer/data.ts                                 |    7 
 src/views/srm/supplierApply/index.vue                          |   12 
 src/views/mes/dv/meteringcheck/data.ts                         |   13 
 52 files changed, 665 insertions(+), 129 deletions(-)

diff --git a/dist.zip b/dist.zip
index ccc80a1..e256f88 100644
--- a/dist.zip
+++ b/dist.zip
Binary files differ
diff --git a/src/adapter/form.ts b/src/adapter/form.ts
index 0d5bb16..972af3a 100644
--- a/src/adapter/form.ts
+++ b/src/adapter/form.ts
@@ -8,6 +8,10 @@
 import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
 import { $t } from '@vben/locales';
 import { isMobile } from '@vben/utils';
+import { watch } from 'vue';
+
+import { automaticApproval } from '#/store/audit-policy';
+import { isAuditField } from '#/utils/audit-ui-policy';
 
 async function initSetupVbenForm() {
   setupVbenForm<ComponentType>({
@@ -61,7 +65,16 @@
   });
 }
 
-const useVbenForm = useForm<ComponentType, ComponentPropsMap>;
+const useVbenForm = (options: FormProps<ComponentType, ComponentPropsMap>) => {
+  const result = useForm<ComponentType, ComponentPropsMap>(options);
+  const schema = options.schema;
+  if (schema) {
+    watch(automaticApproval, () => {
+      result[1].setState({ schema: schema.filter((item) => !automaticApproval.value || !isAuditField(item)) });
+    }, { immediate: true });
+  }
+  return result;
+};
 
 export { initSetupVbenForm, useVbenForm, z };
 
diff --git a/src/adapter/vxe-table.ts b/src/adapter/vxe-table.ts
index 01f7374..dfcad08 100644
--- a/src/adapter/vxe-table.ts
+++ b/src/adapter/vxe-table.ts
@@ -3,7 +3,7 @@
 
 import type { ComponentPropsMap, ComponentType } from './component';
 
-import { h } from 'vue';
+import { h, watch } from 'vue';
 
 import { IconifyIcon } from '@vben/icons';
 import { $te } from '@vben/locales';
@@ -35,6 +35,8 @@
 
 import { DictTag } from '#/components/dict-tag';
 import { $t } from '#/locales';
+import { automaticApproval } from '#/store/audit-policy';
+import { isAuditField } from '#/utils/audit-ui-policy';
 
 import { useVbenForm } from './form';
 
@@ -403,6 +405,17 @@
 export * from '#/components/table-action';
 export const useVbenVxeGrid = <T extends Record<string, any>>(
   ...rest: Parameters<typeof useGrid<T, ComponentType, ComponentPropsMap>>
-) => useGrid<T, ComponentType, ComponentPropsMap>(...rest);
+) => {
+  const options = rest[0];
+  const columns = options.gridOptions?.columns;
+  const schema = options.formOptions?.schema;
+  const applyPolicy = () => ({
+    ...(columns ? { gridOptions: { columns: columns.filter((item) => !automaticApproval.value || !isAuditField(item)) } } : {}),
+    ...(schema ? { formOptions: { schema: schema.filter((item) => !automaticApproval.value || !isAuditField(item)) } } : {}),
+  });
+  const result = useGrid<T, ComponentType, ComponentPropsMap>(options);
+  watch(automaticApproval, () => result[1].setState(applyPolicy()), { immediate: true });
+  return result;
+};
 
 export type * from '@vben/plugins/vxe-table';
diff --git a/src/api/bpm/autoApprove/index.ts b/src/api/bpm/autoApprove/index.ts
index def749a..5a1d499 100644
--- a/src/api/bpm/autoApprove/index.ts
+++ b/src/api/bpm/autoApprove/index.ts
@@ -1,4 +1,5 @@
 import { requestClient } from '#/api/request';
+import { automaticApproval } from '#/store/audit-policy';
 
 export namespace BpmAutoApproveApi {
   /** 娴佺▼鍏嶅鎵归厤缃� */
@@ -35,9 +36,11 @@
   if (remark) {
     params.append('remark', remark);
   }
-  return requestClient.put<boolean>(
+  const result = await requestClient.put<boolean>(
     `/bpm/process-auto-approve/update-global?${params.toString()}`,
   );
+  automaticApproval.value = autoApprove;
+  return result;
 }
 
 /** 鎸夋祦绋嬪畾涔夌殑 Key 璁剧疆鍏嶅鎵瑰紑鍏筹紙涓嶅瓨鍦ㄥ垯鏂板銆佸瓨鍦ㄥ垯鏇存柊锛� */
diff --git a/src/api/energy/index.ts b/src/api/energy/index.ts
index c6bdb3e..ca09ced 100644
--- a/src/api/energy/index.ts
+++ b/src/api/energy/index.ts
@@ -55,3 +55,42 @@
 export function getEnergyAnalysis(params: EnergyFilters & { startDate: string; endDate: string; groupBy: 'DAY' | 'MONTH' }) {
   return requestClient.get<EnergyAnalysis>('/energy/record/analysis', { params });
 }
+
+export function exportEnergyRecords(params: EnergyFilters) {
+  return requestClient.download('/energy/record/export-excel', { params });
+}
+export function exportEnergyAnalysis(params: EnergyFilters & { startDate: string; endDate: string; groupBy: 'DAY' | 'MONTH' }) {
+  return requestClient.download('/energy/record/analysis-export', { params });
+}
+export type EnergyAlertMetric = 'AMOUNT' | 'QUANTITY' | 'MONTH_AMOUNT' | 'MONTH_QUANTITY';
+export interface EnergyAlertRule {
+  id?: number;
+  name: string;
+  energyType: EnergyType;
+  usageArea?: string;
+  productCategory?: string;
+  metric: EnergyAlertMetric;
+  threshold: number;
+  enabled: boolean;
+  recipientIds: number[];
+}
+export interface EnergyAlertLog {
+  id: number; ruleName: string; recordDate: string; energyType: EnergyType;
+  scope: string; metric: EnergyAlertMetric; periodType: 'DAY' | 'MONTH'; actualValue: number; threshold: number;
+  recipientName: string; status: 'FAILED' | 'SENT'; errorMessage: string; createTime: string;
+}
+export async function getEnergyAlertRules(params: { pageNo: number; pageSize: number }) {
+  const result = await requestClient.get<PageResult<Omit<EnergyAlertRule, 'recipientIds'> & { recipientIds: string }>>('/energy/alert/page', { params });
+  return { ...result, list: result.list.map(row => ({ ...row, recipientIds: row.recipientIds.split(',').filter(Boolean).map(Number) })) };
+}
+export function getEnergyAlertLogs(params: { pageNo: number; pageSize: number; date?: string; status?: string }) {
+  return requestClient.get<PageResult<EnergyAlertLog>>('/energy/alert/logs', { params });
+}
+export function getEnergyAlertRecipients() {
+  return requestClient.get<{ id: number; nickname: string }[]>('/energy/alert/recipients');
+}
+export function saveEnergyAlertRule(data: EnergyAlertRule) {
+  return data.id ? requestClient.put('/energy/alert/update', data) : requestClient.post('/energy/alert/create', data);
+}
+export function deleteEnergyAlertRule(id: number) { return requestClient.delete('/energy/alert/delete', { params: { id } }); }
+export function checkEnergyAlerts(date: string) { return requestClient.post<{ sent: number; failed: number }>('/energy/alert/check', undefined, { params: { date } }); }
diff --git a/src/api/hrm/resignation/index.ts b/src/api/hrm/resignation/index.ts
index 8b90cd2..88c8929 100644
--- a/src/api/hrm/resignation/index.ts
+++ b/src/api/hrm/resignation/index.ts
@@ -58,7 +58,7 @@
 
 /** 鎻愪氦绂昏亴鐢宠锛堝彂璧峰鎵癸級 */
 export function submitResignation(id: number) {
-  return requestClient.post(`/hrm/resignation/submit?id=${id}`);
+  return requestClient.post(`/hrm/resignation/submit?id=${id}&processDefinitionKey=hrm_resignation_approve`);
 }
 
 /** 鎾ら攢绂昏亴鐢宠 */
diff --git a/src/api/hrm/transfer/index.ts b/src/api/hrm/transfer/index.ts
index e992674..a18488d 100644
--- a/src/api/hrm/transfer/index.ts
+++ b/src/api/hrm/transfer/index.ts
@@ -59,7 +59,7 @@
 
 /** 鎻愪氦璋冨矖鐢宠锛堝彂璧峰鎵癸級 */
 export function submitTransfer(id: number) {
-  return requestClient.post(`/hrm/transfer/submit?id=${id}`);
+  return requestClient.post(`/hrm/transfer/submit?id=${id}&processDefinitionKey=hrm_transfer_approve`);
 }
 
 /** 鎾ら攢璋冨矖鐢宠 */
diff --git a/src/api/mes/dv/meteringcheck/index.ts b/src/api/mes/dv/meteringcheck/index.ts
index e8995ec..d657a38 100644
--- a/src/api/mes/dv/meteringcheck/index.ts
+++ b/src/api/mes/dv/meteringcheck/index.ts
@@ -15,6 +15,7 @@
     checkResult?: number; // 妫�娴嬬粨璁猴紙瀛楀吀锛�
     certNo?: string; // 璇佷功缂栧彿
     checkUserId?: number; // 妫�娴嬩汉缂栧彿
+    checkUserName?: string; // 鎵嬪伐濉啓鐨勬娴嬩汉濮撳悕
     checkUserNickname?: string; // 妫�娴嬩汉鏄电О
     nextCheckDate?: string; // 涓嬫妫�娴嬫棩鏈�
     remark?: string; // 澶囨敞
diff --git a/src/api/mes/qc/iqc/index.ts b/src/api/mes/qc/iqc/index.ts
index 865bb0f..1600d54 100644
--- a/src/api/mes/qc/iqc/index.ts
+++ b/src/api/mes/qc/iqc/index.ts
@@ -14,6 +14,7 @@
     sourceLineId?: number; // 鏉ユ簮鍗曟嵁琛� ID
     sourceDocCode?: string; // 鏉ユ簮鍗曟嵁缂栧彿锛堝叧鑱旀煡璇級
     vendorId?: number; // 渚涘簲鍟� ID
+    vendorName?: string; // 渚涘簲鍟嗗悕绉帮紙鎵嬪~锛�
     vendorNickname?: string; // 渚涘簲鍟嗙畝绉帮紙鍏宠仈鏌ヨ锛�
     vendorBatch?: string; // 渚涘簲鍟嗘壒娆″彿
     itemId?: number; // 浜у搧鐗╂枡 ID
diff --git a/src/components/description/description.vue b/src/components/description/description.vue
index d591e57..ca75a36 100644
--- a/src/components/description/description.vue
+++ b/src/components/description/description.vue
@@ -11,6 +11,9 @@
 
 import { Card, Descriptions } from 'ant-design-vue';
 
+import { automaticApproval } from '#/store/audit-policy';
+import { isAuditField } from '#/utils/audit-ui-policy';
+
 const props = {
   bordered: { default: true, type: Boolean },
   column: {
@@ -99,6 +102,7 @@
       const { data, schema } = unref(getProps);
       return unref(schema)
         .map((item) => {
+          if (automaticApproval.value && isAuditField(item)) return null;
           const { contentMinWidth, field, render, show, span } = item;
 
           if (show && isFunction(show) && !show(data)) {
diff --git a/src/components/table-action/table-action.vue b/src/components/table-action/table-action.vue
index 78220f5..e968d87 100644
--- a/src/components/table-action/table-action.vue
+++ b/src/components/table-action/table-action.vue
@@ -20,6 +20,9 @@
   Tooltip,
 } from 'ant-design-vue';
 
+import { automaticApproval } from '#/store/audit-policy';
+import { auditActionMode } from '#/utils/audit-ui-policy';
+
 const props = defineProps({
   actions: {
     type: Array as PropType<ActionItem[]>,
@@ -43,6 +46,7 @@
 
 /** 妫�鏌ユ槸鍚︽樉绀� */
 function isIfShow(action: ActionItem): boolean {
+  if (auditActionMode(action.label, automaticApproval.value) === 'hidden') return false;
   const ifShow = action.ifShow;
   let isIfShow = true;
   if (isBoolean(ifShow)) {
@@ -61,15 +65,21 @@
 /** 澶勭悊鎸夐挳 actions */
 const getActions = computed(() => {
   const actions = props.actions || [];
-  return actions.filter((action: ActionItem) => isIfShow(action));
+  return actions.filter((action: ActionItem) => isIfShow(action)).map(displayAction);
 });
 
 /** 澶勭悊涓嬫媺鑿滃崟 actions */
 const getDropdownList = computed(() => {
   const dropDownActions = props.dropDownActions || [];
-  return dropDownActions.filter((action: ActionItem) => isIfShow(action));
+  return dropDownActions.filter((action: ActionItem) => isIfShow(action)).map(displayAction);
 });
 
+function displayAction(action: ActionItem): ActionItem {
+  return auditActionMode(action.label, automaticApproval.value) === 'activate'
+    ? { ...action, label: '鐢熸晥', tooltip: '鎻愪氦鍚庤嚜鍔ㄧ敓鏁�' }
+    : action;
+}
+
 /** Space 缁勪欢鐨� size */
 const spaceSize = computed(() => {
   const actions = unref(getActions);
diff --git a/src/store/audit-policy.ts b/src/store/audit-policy.ts
new file mode 100644
index 0000000..bdf1e55
--- /dev/null
+++ b/src/store/audit-policy.ts
@@ -0,0 +1,15 @@
+import { ref } from 'vue';
+
+import { requestClient } from '#/api/request';
+
+export const automaticApproval = ref(false);
+
+/** Loaded with each authenticated session, before business pages are rendered. */
+export async function loadAuditPolicy() {
+  try {
+    automaticApproval.value = await requestClient.get<boolean>('/bpm/process-auto-approve/ui-policy');
+  } catch {
+    // A failed policy request must not remove the normal submission path.
+    automaticApproval.value = false;
+  }
+}
diff --git a/src/store/auth.ts b/src/store/auth.ts
index 3643cf0..15fd19e 100644
--- a/src/store/auth.ts
+++ b/src/store/auth.ts
@@ -21,6 +21,7 @@
   socialLogin,
 } from '#/api';
 import { $t } from '#/locales';
+import { automaticApproval, loadAuditPolicy } from '#/store/audit-policy';
 
 export const useAuthStore = defineStore('auth', () => {
   const accessStore = useAccessStore();
@@ -134,6 +135,7 @@
     // eslint-disable-next-line no-useless-assignment
     let authPermissionInfo: AuthPermissionInfo | null = null;
     authPermissionInfo = await getAuthPermissionInfoApi();
+    await loadAuditPolicy();
     // userStore
     userStore.setUserInfo(authPermissionInfo.user);
     userStore.setUserRoles(authPermissionInfo.roles);
@@ -145,6 +147,7 @@
 
   function $reset() {
     loginLoading.value = false;
+    automaticApproval.value = false;
   }
 
   return {
diff --git a/src/utils/audit-ui-policy.ts b/src/utils/audit-ui-policy.ts
new file mode 100644
index 0000000..f95170e
--- /dev/null
+++ b/src/utils/audit-ui-policy.ts
@@ -0,0 +1,12 @@
+/** Review-only UI disappears in automatic mode; document status and activation remain. */
+export function isAuditField(item: { field?: string; fieldName?: string; label?: unknown; title?: unknown }) {
+  return /^(auditStatus|auditUserId|auditTime|approvalStatus|approvalUserId|approvalTime|processInstanceId)$/.test(item.field ?? item.fieldName ?? '')
+    || /^(瀹℃壒鐘舵�亅瀹℃牳鐘舵�亅瀹℃壒浜簗瀹℃牳浜簗瀹℃壒鏃堕棿|瀹℃牳鏃堕棿|娴佺▼瀹炰緥)$/.test(String(item.title ?? item.label ?? ''));
+}
+
+export function auditActionMode(label: string | undefined, automatic: boolean) {
+  if (!automatic) return 'normal';
+  if (/^(鎻愪氦瀹℃壒|鎻愪氦瀹℃牳|瀹℃牳|瀹℃壒|閫氳繃|瀹℃牳閫氳繃)$/.test(label ?? '')) return 'activate';
+  if (/^(鏌ョ湅瀹℃壒|鏌ョ湅瀹℃牳|瀹℃壒杩涘害|鏌ョ湅瀹℃壒杩涘害|鍙嶅鏍竱鎾ら攢瀹℃壒|鍙栨秷瀹℃壒|椹冲洖|鎷掔粷)$/.test(label ?? '')) return 'hidden';
+  return 'normal';
+}
diff --git a/src/views/bpm/model/index.vue b/src/views/bpm/model/index.vue
index 2ed2171..4886395 100644
--- a/src/views/bpm/model/index.vue
+++ b/src/views/bpm/model/index.vue
@@ -1,4 +1,5 @@
 <script lang="ts" setup>
+import { automaticApproval } from '#/store/audit-policy';
 import type { ModelCategoryInfo } from '#/api/bpm/model';
 
 import {
@@ -196,6 +197,7 @@
   globalAutoApproveLoading.value = true;
   try {
     await updateGlobalAutoApprove(checked);
+    automaticApproval.value = checked;
     message.success(
       checked
         ? '宸插紑鍚叏灞�鍏嶅鎵癸細鎵�鏈夋祦绋嬫彁浜ゅ悗灏嗙洿鎺ラ�氳繃'
diff --git a/src/views/bpm/oa/leave/create.vue b/src/views/bpm/oa/leave/create.vue
index 8abc90c..b3772c2 100644
--- a/src/views/bpm/oa/leave/create.vue
+++ b/src/views/bpm/oa/leave/create.vue
@@ -18,6 +18,7 @@
 import { getApprovalDetail as getApprovalDetailApi } from '#/api/bpm/processInstance';
 import { $t } from '#/locales';
 import { router } from '#/router';
+import { automaticApproval } from '#/store/audit-policy';
 import ProcessInstanceTimeline from '#/views/bpm/processInstance/detail/modules/time-line.vue';
 
 import { useFormSchema } from './data';
@@ -67,7 +68,7 @@
     return;
   }
   // 1.2 瀹℃壒鐩稿叧锛氭牎楠屾寚瀹氬鎵逛汉
-  if (startUserSelectTasks.value?.length > 0) {
+  if (!automaticApproval.value && startUserSelectTasks.value?.length > 0) {
     for (const userTask of startUserSelectTasks.value) {
       const assignees = startUserSelectAssignees.value[userTask.id];
       if (Array.isArray(assignees) && assignees.length === 0) {
@@ -79,7 +80,7 @@
   // 鎻愪氦琛ㄥ崟
   const data = (await formApi.getValues()) as LeaveCreateData;
   // 瀹℃壒鐩稿叧锛氳缃寚瀹氬鎵逛汉
-  if (startUserSelectTasks.value?.length > 0) {
+  if (!automaticApproval.value && startUserSelectTasks.value?.length > 0) {
     data.startUserSelectAssignees = startUserSelectAssignees.value;
   }
   // 鏍煎紡鍖栧紑濮嬫椂闂村拰缁撴潫鏃堕棿鐨勫��
@@ -118,6 +119,7 @@
 
 /** 瀹℃壒鐩稿叧锛氳幏鍙栧鎵硅鎯� */
 async function getApprovalDetail() {
+  if (automaticApproval.value) return;
   processTimeLineLoading.value = true;
   try {
     const data = await getApprovalDetailApi({
@@ -144,7 +146,7 @@
         BpmCandidateStrategyEnum.START_USER_SELECT === node.candidateStrategy,
     );
     // 鎭㈠涔嬪墠鐨勯�夋嫨瀹℃壒浜�
-    if (startUserSelectTasks.value?.length > 0) {
+    if (!automaticApproval.value && startUserSelectTasks.value?.length > 0) {
       for (const node of startUserSelectTasks.value) {
         const tempAssignees = tempStartUserSelectAssignees.value[node.id];
         startUserSelectAssignees.value[node.id] = tempAssignees?.length
@@ -212,6 +214,10 @@
 
 /** 鍒濆鍖� */
 onMounted(async () => {
+  if (automaticApproval.value) {
+    if (query.id) await getDetail(Number(query.id));
+    return;
+  }
   const processDefinitionDetail: any = await getProcessDefinition(
     undefined,
     processDefineKey,
diff --git a/src/views/crm/backlog/index.vue b/src/views/crm/backlog/index.vue
index b461f18..a183a22 100644
--- a/src/views/crm/backlog/index.vue
+++ b/src/views/crm/backlog/index.vue
@@ -1,9 +1,10 @@
 <script lang="ts" setup>
-import { computed, onActivated, onMounted, ref } from 'vue';
+import { computed, onActivated, onMounted, ref, watch } from 'vue';
 
 import { Page } from '@vben/common-ui';
 
 import { Badge, Card, List } from 'ant-design-vue';
+import { automaticApproval } from '#/store/audit-policy';
 
 import { getFollowClueCount } from '#/api/crm/clue';
 import {
@@ -49,6 +50,14 @@
   receivableAuditCount,
   receivablePlanRemindCount,
 );
+const visibleLeftSides = computed(() => leftSides.filter((item) =>
+  !automaticApproval.value || !['contractAudit', 'receivableAudit'].includes(item.menu),
+));
+watch(automaticApproval, (automatic) => {
+  if (automatic && ['contractAudit', 'receivableAudit'].includes(leftMenu.value)) {
+    leftMenu.value = 'customerTodayContact';
+  }
+});
 
 const currentComponent = computed(() => {
   const components = {
@@ -75,9 +84,9 @@
   customerPutPoolRemindCount.value = await getPutPoolRemindCustomerCount();
   customerFollowCount.value = await getFollowCustomerCount();
   clueFollowCount.value = await getFollowClueCount();
-  contractAuditCount.value = await getAuditContractCount();
+  if (!automaticApproval.value) contractAuditCount.value = await getAuditContractCount();
   contractRemindCount.value = await getRemindContractCount();
-  receivableAuditCount.value = await getAuditReceivableCount();
+  if (!automaticApproval.value) receivableAuditCount.value = await getAuditReceivableCount();
   receivablePlanRemindCount.value = await getReceivablePlanRemindCount();
 }
 
@@ -95,7 +104,7 @@
   <Page auto-content-height>
     <div class="flex h-full w-full">
       <Card class="w-1/5">
-        <List item-layout="horizontal" :data-source="leftSides">
+        <List item-layout="horizontal" :data-source="visibleLeftSides">
           <template #renderItem="{ item }">
             <List.Item
               @click="sideClick(item)"
diff --git a/src/views/crm/contract/components/data.ts b/src/views/crm/contract/components/data.ts
index 9305a46..b3e1208 100644
--- a/src/views/crm/contract/components/data.ts
+++ b/src/views/crm/contract/components/data.ts
@@ -28,13 +28,13 @@
       title: '鍚堝悓寮�濮嬫椂闂�',
       field: 'startTime',
       minWidth: 150,
-      formatter: 'formatDateTime',
+      formatter: 'formatDate',
     },
     {
       title: '鍚堝悓缁撴潫鏃堕棿',
       field: 'endTime',
       minWidth: 150,
-      formatter: 'formatDateTime',
+      formatter: 'formatDate',
     },
     {
       title: '宸插洖娆鹃噾棰濓紙鍏冿級',
@@ -78,7 +78,7 @@
       title: '鍒涘缓鏃堕棿',
       field: 'createTime',
       minWidth: 150,
-      formatter: 'formatDateTime',
+      formatter: 'formatDate',
     },
     {
       title: '鍒涘缓浜�',
diff --git a/src/views/crm/contract/data.ts b/src/views/crm/contract/data.ts
index 7dabc09..a31f414 100644
--- a/src/views/crm/contract/data.ts
+++ b/src/views/crm/contract/data.ts
@@ -368,19 +368,19 @@
       title: '涓嬪崟鏃堕棿',
       field: 'orderDate',
       minWidth: 120,
-      formatter: 'formatDateTime',
+      formatter: 'formatDate',
     },
     {
       title: '鍚堝悓寮�濮嬫椂闂�',
       field: 'startTime',
       minWidth: 120,
-      formatter: 'formatDateTime',
+      formatter: 'formatDate',
     },
     {
       title: '鍚堝悓缁撴潫鏃堕棿',
       field: 'endTime',
       minWidth: 120,
-      formatter: 'formatDateTime',
+      formatter: 'formatDate',
     },
     {
       title: '瀹㈡埛绛剧害浜�',
@@ -430,7 +430,7 @@
       title: '鏈�鍚庤窡杩涙椂闂�',
       field: 'contactLastTime',
       minWidth: 180,
-      formatter: 'formatDateTime',
+      formatter: 'formatDate',
     },
     {
       title: '璐熻矗浜�',
@@ -446,13 +446,13 @@
       title: '鏇存柊鏃堕棿',
       field: 'updateTime',
       minWidth: 180,
-      formatter: 'formatDateTime',
+      formatter: 'formatDate',
     },
     {
       title: '鍒涘缓鏃堕棿',
       field: 'createTime',
       minWidth: 180,
-      formatter: 'formatDateTime',
+      formatter: 'formatDate',
     },
     {
       title: '鍒涘缓浜�',
diff --git a/src/views/crm/contract/detail/approve/index.vue b/src/views/crm/contract/detail/approve/index.vue
index adc3157..49cba53 100644
--- a/src/views/crm/contract/detail/approve/index.vue
+++ b/src/views/crm/contract/detail/approve/index.vue
@@ -10,7 +10,7 @@
 import {
   erpCountInputFormatter,
   erpPriceInputFormatter,
-  formatDateTime,
+  formatDate,
 } from '@vben/utils';
 
 import { Spin } from 'ant-design-vue';
@@ -116,17 +116,17 @@
     {
       label: '涓嬪崟鏃堕棿',
       field: 'orderDate',
-      render: (val) => formatDateTime(val) as string,
+      render: (val) => formatDate(val) as string,
     },
     {
       label: '鍚堝悓寮�濮嬫椂闂�',
       field: 'startTime',
-      render: (val) => formatDateTime(val) as string,
+      render: (val) => formatDate(val) as string,
     },
     {
       label: '鍚堝悓缁撴潫鏃堕棿',
       field: 'endTime',
-      render: (val) => formatDateTime(val) as string,
+      render: (val) => formatDate(val) as string,
     },
     { label: '瀹㈡埛绛剧害浜�', field: 'signContactName', render: textRender },
     { label: '鍏徃绛剧害浜�', field: 'signUserName', render: textRender },
@@ -135,7 +135,7 @@
     {
       label: '鍒涘缓鏃堕棿',
       field: 'createTime',
-      render: (val) => formatDateTime(val) as string,
+      render: (val) => formatDate(val) as string,
     },
     {
       label: '鍚堝悓鐘舵��',
diff --git a/src/views/crm/contract/detail/data.ts b/src/views/crm/contract/detail/data.ts
index ae82b00..30b51b1 100644
--- a/src/views/crm/contract/detail/data.ts
+++ b/src/views/crm/contract/detail/data.ts
@@ -3,7 +3,7 @@
 import { h } from 'vue';
 
 import { DICT_TYPE } from '@vben/constants';
-import { erpPriceInputFormatter, formatDateTime } from '@vben/utils';
+import { erpPriceInputFormatter, formatDate } from '@vben/utils';
 
 import { DictTag } from '#/components/dict-tag';
 
@@ -27,7 +27,7 @@
     {
       field: 'orderDate',
       label: '涓嬪崟鏃堕棿',
-      render: (val) => formatDateTime(val) as string,
+      render: (val) => formatDate(val) as string,
     },
     {
       field: 'totalReceivablePrice',
@@ -88,17 +88,17 @@
     {
       field: 'orderDate',
       label: '涓嬪崟鏃堕棿',
-      render: (val) => formatDateTime(val) as string,
+      render: (val) => formatDate(val) as string,
     },
     {
       field: 'startTime',
       label: '鍚堝悓寮�濮嬫椂闂�',
-      render: (val) => formatDateTime(val) as string,
+      render: (val) => formatDate(val) as string,
     },
     {
       field: 'endTime',
       label: '鍚堝悓缁撴潫鏃堕棿',
-      render: (val) => formatDateTime(val) as string,
+      render: (val) => formatDate(val) as string,
     },
     {
       field: 'signContactName',
diff --git a/src/views/crm/contract/index.vue b/src/views/crm/contract/index.vue
index b4f4f4b..66a35c4 100644
--- a/src/views/crm/contract/index.vue
+++ b/src/views/crm/contract/index.vue
@@ -20,6 +20,7 @@
   submitContract,
 } from '#/api/crm/contract';
 import { $t } from '#/locales';
+import { automaticApproval } from '#/store/audit-policy';
 
 import { useGridColumns, useGridFormSchema } from './data';
 import Form from './modules/form.vue';
@@ -97,10 +98,15 @@
 const submitLoading = ref(false);
 
 async function handleSubmit(row: CrmContractApi.Contract) {
-  const res = await getContractApproveProcessList();
+  const res = automaticApproval.value ? [] : await getContractApproveProcessList();
   processList.value = res;
   currentRow.value = row;
   selectedProcessKey.value = undefined;
+  if (automaticApproval.value) {
+    selectedProcessKey.value = 'contract_approve';
+    await confirmSubmit();
+    return;
+  }
   submitModalVisible.value = true;
 }
 
@@ -112,7 +118,7 @@
   submitLoading.value = true;
   try {
     await submitContract(currentRow.value!.id!, selectedProcessKey.value);
-    message.success('鎻愪氦瀹℃壒鎴愬姛');
+    message.success(automaticApproval.value ? '宸叉彁浜ょ敓鏁�' : '鎻愪氦瀹℃壒鎴愬姛');
     submitModalVisible.value = false;
     handleRefresh();
   } finally {
diff --git a/src/views/crm/customer/data.ts b/src/views/crm/customer/data.ts
index d3372ee..fc00aae 100644
--- a/src/views/crm/customer/data.ts
+++ b/src/views/crm/customer/data.ts
@@ -354,13 +354,6 @@
       minWidth: 180,
     },
     {
-      field: 'poolDay',
-      title: '璺濈杩涘叆鍏捣澶╂暟',
-      minWidth: 140,
-      formatter: ({ cellValue }) =>
-        cellValue === null ? '-' : `${cellValue} 澶ー,
-    },
-    {
       field: 'ownerUserName',
       title: '璐熻矗浜�',
       minWidth: 100,
diff --git a/src/views/crm/customer/detail/modules/distribute-form.vue b/src/views/crm/customer/detail/modules/distribute-form.vue
index 53787ca..28e3644 100644
--- a/src/views/crm/customer/detail/modules/distribute-form.vue
+++ b/src/views/crm/customer/detail/modules/distribute-form.vue
@@ -1,4 +1,6 @@
 <script lang="ts" setup>
+import { ref } from 'vue';
+
 import { useVbenModal } from '@vben/common-ui';
 
 import { message } from 'ant-design-vue';
@@ -10,6 +12,7 @@
 import { useDistributeFormSchema } from '../data';
 
 const emit = defineEmits(['success']);
+const customerIds = ref<number[]>([]);
 
 const [Form, formApi] = useVbenForm({
   commonConfig: {
@@ -34,7 +37,7 @@
     // 鎻愪氦琛ㄥ崟
     const data = await formApi.getValues();
     try {
-      await distributeCustomer([data.id], data.ownerUserId);
+      await distributeCustomer(customerIds.value, data.ownerUserId);
       // 鍏抽棴骞舵彁绀�
       await modalApi.close();
       emit('success');
@@ -48,13 +51,15 @@
       return;
     }
     // 鍔犺浇鏁版嵁
-    const data = modalApi.getData();
-    if (!data || !data.id) {
+    const data = modalApi.getData<{ id?: number; ids?: number[] }>();
+    customerIds.value = data?.ids ?? (data?.id ? [data.id] : []);
+    if (customerIds.value.length === 0) {
       return;
     }
     modalApi.lock();
     try {
-      await formApi.setValues(data);
+      await formApi.resetForm();
+      await formApi.setValues({ id: customerIds.value[0] });
     } finally {
       modalApi.unlock();
     }
diff --git a/src/views/crm/customer/pool/data.ts b/src/views/crm/customer/pool/data.ts
index d057b67..d48670e 100644
--- a/src/views/crm/customer/pool/data.ts
+++ b/src/views/crm/customer/pool/data.ts
@@ -60,6 +60,7 @@
 
 export function useGridColumns(): VxeTableGridOptions['columns'] {
   return [
+    { type: 'checkbox', width: 50, fixed: 'left' },
     {
       title: '瀹㈡埛鍚嶇О',
       field: 'name',
@@ -157,5 +158,11 @@
       field: 'creatorName',
       minWidth: 100,
     },
+    {
+      title: '鎿嶄綔',
+      width: 150,
+      fixed: 'right',
+      slots: { default: 'actions' },
+    },
   ];
 }
diff --git a/src/views/crm/customer/pool/index.vue b/src/views/crm/customer/pool/index.vue
index 6fa6410..1e68a0e 100644
--- a/src/views/crm/customer/pool/index.vue
+++ b/src/views/crm/customer/pool/index.vue
@@ -2,24 +2,86 @@
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { CrmCustomerApi } from '#/api/crm/customer';
 
+import { ref } from 'vue';
 import { useRouter } from 'vue-router';
 
-import { Page } from '@vben/common-ui';
+import { confirm, Page, useVbenModal } from '@vben/common-ui';
 import { downloadFileFromBlobPart } from '@vben/utils';
 
-import { Button } from 'ant-design-vue';
+import { Button, message } from 'ant-design-vue';
 
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
-import { exportCustomer, getCustomerPage } from '#/api/crm/customer';
+import {
+  exportCustomer,
+  getCustomerPage,
+  receiveCustomer,
+} from '#/api/crm/customer';
 import { $t } from '#/locales';
 
+import DistributeForm from '../detail/modules/distribute-form.vue';
 import { useGridColumns, useGridFormSchema } from './data';
 
 const { push } = useRouter();
+const receiving = ref(false);
+const [DistributeModal, distributeModalApi] = useVbenModal({
+  connectedComponent: DistributeForm,
+  destroyOnClose: true,
+});
+
+function handleRefresh() {
+  gridApi.grid.clearCheckboxRow();
+  gridApi.query();
+}
+
+function getCustomerIds(row?: CrmCustomerApi.Customer): number[] {
+  const records: CrmCustomerApi.Customer[] = row
+    ? [row]
+    : gridApi.grid.getCheckboxRecords();
+  const ids = records.map((item) => item.id!);
+  if (ids.length === 0) {
+    message.warning('璇峰厛鍕鹃�夊鎴�');
+  }
+  return ids;
+}
+
+/** 棰嗗彇鍗曟潯鎴栧嬀閫夌殑鍏捣瀹㈡埛 */
+async function handleReceive(row?: CrmCustomerApi.Customer) {
+  if (receiving.value) return;
+  const ids = getCustomerIds(row);
+  if (ids.length === 0) return;
+  receiving.value = true;
+  try {
+    try {
+      await confirm(
+        row
+          ? `纭畾棰嗗彇瀹㈡埛銆�${row.name}銆戝悧锛焋
+          : `纭畾棰嗗彇閫変腑鐨� ${ids.length} 涓鎴峰悧锛焋,
+      );
+    } catch {
+      return;
+    }
+    await receiveCustomer(ids);
+    message.success('棰嗗彇瀹㈡埛鎴愬姛');
+    handleRefresh();
+  } finally {
+    receiving.value = false;
+  }
+}
+
+/** 鍒嗛厤鍗曟潯鎴栧嬀閫夌殑鍏捣瀹㈡埛 */
+function handleDistribute(row?: CrmCustomerApi.Customer) {
+  const ids = getCustomerIds(row);
+  if (ids.length === 0) return;
+  distributeModalApi.setData({ ids }).open();
+}
 
 /** 瀵煎嚭琛ㄦ牸 */
 async function handleExport() {
-  const data = await exportCustomer(await gridApi.formApi.getValues());
+  const data = await exportCustomer({
+    ...(await gridApi.formApi.getValues()),
+    pool: true,
+    sceneType: 4,
+  });
   downloadFileFromBlobPart({ fileName: '瀹㈡埛鍏捣.xls', source: data });
 }
 
@@ -62,10 +124,25 @@
 </script>
 
 <template>
-  <Page auto-content-height><Grid>
+  <Page auto-content-height>
+    <DistributeModal @success="handleRefresh" />
+    <Grid>
       <template #toolbar-tools>
         <TableAction
           :actions="[
+            {
+              label: '棰嗗彇瀹㈡埛',
+              type: 'primary',
+              auth: ['crm:customer:receive'],
+              disabled: receiving,
+              onClick: () => handleReceive(),
+            },
+            {
+              label: '鍒嗛厤瀹㈡埛',
+              auth: ['crm:customer:distribute'],
+              disabled: receiving,
+              onClick: () => handleDistribute(),
+            },
             {
               label: $t('ui.actionTitle.export'),
               type: 'primary',
@@ -76,6 +153,26 @@
           ]"
         />
       </template>
+      <template #actions="{ row }">
+        <TableAction
+          :actions="[
+            {
+              label: '棰嗗彇',
+              type: 'link',
+              auth: ['crm:customer:receive'],
+              disabled: receiving,
+              onClick: () => handleReceive(row),
+            },
+            {
+              label: '鍒嗛厤',
+              type: 'link',
+              auth: ['crm:customer:distribute'],
+              disabled: receiving,
+              onClick: () => handleDistribute(row),
+            },
+          ]"
+        />
+      </template>
       <template #name="{ row }">
         <Button type="link" @click="handleDetail(row)">
           {{ row.name }}
diff --git a/src/views/crm/invoice/index.vue b/src/views/crm/invoice/index.vue
index bf65d18..ccc576d 100644
--- a/src/views/crm/invoice/index.vue
+++ b/src/views/crm/invoice/index.vue
@@ -18,6 +18,7 @@
   submitInvoice,
 } from '#/api/crm/invoice';
 import { $t } from '#/locales';
+import { automaticApproval } from '#/store/audit-policy';
 
 import { useGridColumns, useGridFormSchema } from './data';
 import Form from './modules/form.vue';
@@ -90,12 +91,12 @@
 /** 鎻愪氦瀹℃牳 */
 async function handleSubmit(row: CrmInvoiceApi.Invoice) {
   const hideLoading = message.loading({
-    content: '鎻愪氦瀹℃牳涓�...',
+    content: automaticApproval.value ? '姝e湪鎻愪氦鐢熸晥...' : '鎻愪氦瀹℃牳涓�...',
     duration: 0,
   });
   try {
     await submitInvoice(row.id!);
-    message.success('鎻愪氦瀹℃牳鎴愬姛');
+    message.success(automaticApproval.value ? '宸叉彁浜ょ敓鏁�' : '鎻愪氦瀹℃牳鎴愬姛');
     handleRefresh();
   } finally {
     hideLoading();
diff --git a/src/views/crm/saleQuotation/data.ts b/src/views/crm/saleQuotation/data.ts
index 71aee78..4658178 100644
--- a/src/views/crm/saleQuotation/data.ts
+++ b/src/views/crm/saleQuotation/data.ts
@@ -302,13 +302,13 @@
       field: 'quotationTime',
       title: '鎶ヤ环鏃ユ湡',
       minWidth: 120,
-      formatter: 'formatDateTime',
+      formatter: 'formatDate',
     },
     {
       field: 'validUntil',
       title: '鏈夋晥鏈熻嚦',
       minWidth: 120,
-      formatter: 'formatDateTime',
+      formatter: 'formatDate',
     },
     {
       field: 'status',
@@ -330,7 +330,7 @@
       field: 'createTime',
       title: '鍒涘缓鏃堕棿',
       minWidth: 180,
-      formatter: 'formatDateTime',
+      formatter: 'formatDate',
     },
     {
       title: '鎿嶄綔',
diff --git a/src/views/energy/alert/index.vue b/src/views/energy/alert/index.vue
new file mode 100644
index 0000000..78ebbc0
--- /dev/null
+++ b/src/views/energy/alert/index.vue
@@ -0,0 +1,145 @@
+<script setup lang="ts">
+import type { FormInstance, TablePaginationConfig } from 'ant-design-vue';
+import type { EnergyAlertRule, EnergyAlertLog, EnergyType } from '#/api/energy';
+import { computed, onMounted, reactive, ref } from 'vue';
+import { Page } from '@vben/common-ui';
+import { Alert, Button, Card, DatePicker, Form, FormItem, Input, InputNumber, Modal, Popconfirm, Select, Space, Switch, Table, Tabs, TabPane, Tag, message } from 'ant-design-vue';
+import dayjs from 'dayjs';
+import { checkEnergyAlerts, deleteEnergyAlertRule, getEnergyAlertLogs, getEnergyAlertRecipients, getEnergyAlertRules, saveEnergyAlertRule } from '#/api/energy';
+import { energyInfo, energyOptions, numberText, productOptions } from '../shared';
+
+defineOptions({ name: 'EnergyAlert' });
+const tab = ref('rules');
+const rules = ref<EnergyAlertRule[]>([]);
+const logs = ref<EnergyAlertLog[]>([]);
+const recipients = ref<{ label: string; value: number }[]>([]);
+const loading = ref(false), error = ref(false), saving = ref(false), checking = ref(false), open = ref(false);
+const rulePage = reactive({ current: 1, pageSize: 10, total: 0, showSizeChanger: true });
+const logPage = reactive({ current: 1, pageSize: 10, total: 0, showSizeChanger: true });
+const logFilters = reactive<{ date?: string; status?: string }>({});
+const checkDate = ref(dayjs().format('YYYY-MM-DD'));
+const initial = (): EnergyAlertRule => ({ name: '', energyType: 'WATER', metric: 'QUANTITY', threshold: 1, enabled: true, recipientIds: [], usageArea: '', productCategory: undefined });
+const model = reactive<EnergyAlertRule>(initial());
+const metricOptions = [
+  { label: '姣忔棩鐢ㄩ噺', value: 'QUANTITY' }, { label: '姣忔棩璐圭敤', value: 'AMOUNT' },
+  { label: '姣忔湀鐢ㄩ噺', value: 'MONTH_QUANTITY' }, { label: '姣忔湀璐圭敤', value: 'MONTH_AMOUNT' },
+];
+const metricLabel = (metric: string) => metricOptions.find(option => option.value === metric)?.label ?? metric;
+const isAmount = (metric: string) => metric.endsWith('AMOUNT');
+const formRef = ref<FormInstance>();
+const unit = computed(() => isAmount(model.metric) ? '鍏�' : energyInfo[model.energyType].unit);
+const validation = {
+  name: [{ required: true, whitespace: true, message: '璇疯緭鍏ヨ鍒欏悕绉�' }],
+  threshold: [{ required: true, type: 'number' as const, min: 0.001, message: '闃堝�煎繀椤诲ぇ浜庨浂' }],
+  recipientIds: [{ required: true, type: 'array' as const, min: 1, max: 50, message: '璇烽�夋嫨 1 鑷� 50 浣嶆帴鏀朵汉' }],
+};
+const ruleColumns = [
+  { title: '瑙勫垯鍚嶇О', dataIndex: 'name', width: 180 }, { title: '鑳芥簮', dataIndex: 'energyType', width: 80 },
+  { title: '缁熻鑼冨洿', key: 'scope', width: 200 }, { title: '缁熻鎸囨爣 / 闃堝��', key: 'threshold', width: 190 },
+  { title: '鎺ユ敹浜�', key: 'recipients', width: 180 }, { title: '鐘舵��', key: 'enabled', width: 80 },
+  { title: '鎿嶄綔', key: 'action', width: 140, fixed: 'right' as const },
+];
+const logColumns = [
+  { title: '缁熻鍛ㄦ湡', key: 'period', width: 120 }, { title: '瑙勫垯鍚嶇О', dataIndex: 'ruleName', width: 160 },
+  { title: '鑳芥簮 / 鑼冨洿', key: 'scope', width: 230 }, { title: '瀹為檯鍊� / 闃堝��', key: 'value', width: 210 },
+  { title: '鎺ユ敹浜�', dataIndex: 'recipientName', width: 120 }, { title: '缁撴灉', key: 'status', width: 100 },
+  { title: '澶辫触鍘熷洜', dataIndex: 'errorMessage', width: 300 },
+];
+let loadId = 0;
+async function load() {
+  const id = ++loadId; loading.value = true; error.value = false;
+  try {
+    if (tab.value === 'rules') {
+      const result = await getEnergyAlertRules({ pageNo: rulePage.current, pageSize: rulePage.pageSize });
+      if (id === loadId) { rules.value = result.list; rulePage.total = result.total; }
+    } else {
+      const result = await getEnergyAlertLogs({ ...logFilters, pageNo: logPage.current, pageSize: logPage.pageSize });
+      if (id === loadId) { logs.value = result.list; logPage.total = result.total; }
+    }
+  } catch { if (id === loadId) error.value = true; }
+  finally { if (id === loadId) loading.value = false; }
+}
+function changePage(page: TablePaginationConfig) {
+  Object.assign(tab.value === 'rules' ? rulePage : logPage, { current: page.current ?? 1, pageSize: page.pageSize ?? 10 }); void load();
+}
+async function showForm(row?: EnergyAlertRule) {
+  try { recipients.value = (await getEnergyAlertRecipients()).map(user => ({ label: user.nickname, value: user.id })); }
+  catch { message.error('鏃犳硶鍔犺浇鎺ユ敹浜猴紝璇烽噸璇�'); return; }
+  Object.keys(model).forEach(key => delete (model as unknown as Record<string, unknown>)[key]);
+  Object.assign(model, row ? { ...row, recipientIds: [...row.recipientIds] } : initial());
+  open.value = true; formRef.value?.clearValidate();
+}
+async function save() {
+  await formRef.value?.validate(); saving.value = true;
+  try { await saveEnergyAlertRule({ ...model }); open.value = false; message.success('瑙勫垯宸蹭繚瀛橈紝鍚庣画褰曞叆鑷姩妫�鏌ワ紱鍘嗗彶鏃ユ湡鍙墜鍔ㄦ鏌�'); await load(); }
+  finally { saving.value = false; }
+}
+async function remove(id: number) { await deleteEnergyAlertRule(id); message.success('宸插垹闄わ紝鍘嗗彶閫氱煡璁板綍淇濈暀'); rulePage.current = 1; await load(); }
+async function check() {
+  if (!checkDate.value || checkDate.value > dayjs().format('YYYY-MM-DD')) { message.warning('璇烽�夋嫨浠婂ぉ鎴栦箣鍓嶇殑鏃ユ湡'); return; }
+  checking.value = true;
+  try {
+    const result = await checkEnergyAlerts(checkDate.value);
+    message[result.failed ? 'warning' : 'success'](`妫�鏌ュ畬鎴愶細鏂板彂閫� ${result.sent} 鏉★紝澶辫触 ${result.failed} 鏉°�傚凡鍙戦�佺殑閫氱煡涓嶄細閲嶅鍙戦�併�俙);
+    tab.value = 'logs'; logFilters.date = checkDate.value; logPage.current = 1; await load();
+  } finally { checking.value = false; }
+}
+function recipientNames(ids: number[]) { return ids.map(id => recipients.value.find(user => user.value === id)?.label ?? `鐢ㄦ埛 ${id}`).join('銆�'); }
+onMounted(async () => {
+  await load();
+  try { recipients.value = (await getEnergyAlertRecipients()).map(user => ({ label: user.nickname, value: user.id })); } catch { /* Rules remain viewable when recipient lookup fails. */ }
+});
+</script>
+
+<template>
+  <Page title="鑳借�楅璀�" description="閰嶇疆姣忔棩鎴栨瘡鏈堢敤閲忋�佽垂鐢ㄩ槇鍊硷紝瓒呰繃闃堝�兼椂鍚戞寚瀹氫汉鍛樺彂閫佺珯鍐呴�氱煡銆�">
+    <Alert class="mb-4" type="info" show-icon message="姣忔棩鎸囨爣鎸夊ぉ姹囨�伙紝姣忔湀鎸囨爣鎸夎嚜鐒舵湀姹囨�绘湰鏈堝凡褰曞叆鏁版嵁锛岃秴闄愬嵆閫氱煡銆傚悓涓�瑙勫垯銆佺粺璁″懆鏈熷拰鎺ユ敹浜哄彧鎴愬姛閫氱煡涓�娆°�傛鏌ユ棩鏈熷悓鏃舵鏌ュ綋澶╁強鎵�灞炴湀浠姐�傚尯鍩熸寜鍚嶇О绮剧‘鍖归厤銆�" />
+    <Card>
+      <div class="mb-4 flex flex-wrap items-center justify-between gap-3">
+        <Button v-access:code="['energy:alert:create']" type="primary" @click="showForm()">鏂板棰勮瑙勫垯</Button>
+        <Space wrap><span>妫�鏌ユ棩鏈�</span><DatePicker v-model:value="checkDate" value-format="YYYY-MM-DD" :allow-clear="false" /><Button v-access:code="['energy:alert:check']" :loading="checking" @click="check">妫�鏌ラ璀� / 閲嶈瘯澶辫触閫氱煡</Button></Space>
+      </div>
+      <Alert v-if="error" class="mb-4" type="error" show-icon message="鍔犺浇澶辫触锛岃鐐瑰嚮鍒锋柊閲嶈瘯銆�" />
+      <Tabs v-model:active-key="tab" @change="load">
+        <TabPane key="rules" tab="棰勮瑙勫垯">
+          <Table :columns="ruleColumns" :data-source="rules" row-key="id" :loading="loading" :pagination="rulePage" :scroll="{ x: 1100 }" :locale="{ emptyText: '鏆傛棤瑙勫垯锛岃鍏堣缃槇鍊煎拰閫氱煡鎺ユ敹浜�' }" @change="changePage">
+            <template #bodyCell="{ column, record }">
+              <template v-if="column.dataIndex === 'energyType'">{{ energyInfo[record.energyType as EnergyType].name }}</template>
+              <template v-else-if="column.key === 'scope'">{{ record.usageArea || '鍏ㄩ儴鍖哄煙' }} / {{ record.productCategory || '鍏ㄩ儴浜у搧' }}</template>
+              <template v-else-if="column.key === 'threshold'">{{ metricLabel(record.metric) }} &gt; {{ numberText(record.threshold) }} {{ isAmount(record.metric) ? '鍏�' : energyInfo[record.energyType as EnergyType].unit }}</template>
+              <template v-else-if="column.key === 'recipients'">{{ recipientNames(record.recipientIds) }}</template>
+              <template v-else-if="column.key === 'enabled'"><Tag :color="record.enabled ? 'green' : 'default'">{{ record.enabled ? '鍚敤' : '鍋滅敤' }}</Tag></template>
+              <template v-else-if="column.key === 'action'"><Space :size="0"><Button v-access:code="['energy:alert:update']" type="link" @click="showForm(record as EnergyAlertRule)">缂栬緫</Button><Popconfirm title="鍒犻櫎姝よ鍒欙紵鍘嗗彶閫氱煡浠嶄細淇濈暀銆�" @confirm="remove(record.id)"><Button v-access:code="['energy:alert:delete']" type="link" danger>鍒犻櫎</Button></Popconfirm></Space></template>
+            </template>
+          </Table>
+        </TabPane>
+        <TabPane key="logs" tab="閫氱煡璁板綍">
+          <Space class="mb-4" wrap><DatePicker v-model:value="logFilters.date" value-format="YYYY-MM-DD" placeholder="缁熻鏃ユ湡" /><Select v-model:value="logFilters.status" :options="[{ label: '鍙戦�佹垚鍔�', value: 'SENT' }, { label: '鍙戦�佸け璐�', value: 'FAILED' }]" allow-clear placeholder="鍏ㄩ儴缁撴灉" style="width: 140px" /><Button @click="logPage.current = 1; load()">鏌ヨ</Button></Space>
+          <Table :columns="logColumns" :data-source="logs" row-key="id" :loading="loading" :pagination="logPage" :scroll="{ x: 1340 }" :locale="{ emptyText: '鏆傛棤棰勮閫氱煡璁板綍' }" @change="changePage">
+            <template #bodyCell="{ column, record }">
+              <template v-if="column.key === 'period'">{{ record.periodType === 'MONTH' ? record.recordDate.slice(0, 7) : record.recordDate }}<div class="text-xs text-muted-foreground">{{ metricLabel(record.metric) }}</div></template>
+              <template v-else-if="column.key === 'scope'">{{ energyInfo[record.energyType as EnergyType].name }} / {{ record.scope }}</template>
+              <template v-else-if="column.key === 'value'">{{ numberText(record.actualValue) }} / {{ numberText(record.threshold) }} {{ isAmount(record.metric) ? '鍏�' : energyInfo[record.energyType as EnergyType].unit }}</template>
+              <template v-else-if="column.key === 'status'"><Tag :color="record.status === 'SENT' ? 'green' : 'red'">{{ record.status === 'SENT' ? '鍙戦�佹垚鍔�' : '鍙戦�佸け璐�' }}</Tag></template>
+            </template>
+          </Table>
+        </TabPane>
+        <template #rightExtra><Button :loading="loading" @click="load">鍒锋柊</Button></template>
+      </Tabs>
+    </Card>
+    <Modal v-model:open="open" :title="model.id ? '缂栬緫棰勮瑙勫垯' : '鏂板棰勮瑙勫垯'" :confirm-loading="saving" :mask-closable="false" :width="640" @ok="save">
+      <Form ref="formRef" :model="model" :rules="validation" layout="vertical" class="mt-4">
+        <FormItem label="瑙勫垯鍚嶇О" name="name"><Input v-model:value="model.name" :maxlength="100" placeholder="渚嬪锛氱嚂绐濈敓浜у尯姣忔棩鐢ㄦ按棰勮" /></FormItem>
+        <div class="grid grid-cols-1 gap-x-4 sm:grid-cols-2">
+          <FormItem label="鑳芥簮"><Select v-model:value="model.energyType" :options="energyOptions" /></FormItem>
+          <FormItem label="缁熻鎸囨爣"><Select v-model:value="model.metric" :options="metricOptions" /></FormItem>
+          <FormItem label="浣跨敤鍖哄煙锛堢簿纭尮閰嶏紝鍙暀绌猴級"><Input v-model:value="model.usageArea" :maxlength="100" placeholder="鐣欑┖缁熻鍏ㄩ儴鍖哄煙" /></FormItem>
+          <FormItem label="浜у搧绫诲埆"><Select v-model:value="model.productCategory" :options="productOptions" allow-clear placeholder="鍏ㄩ儴浜у搧" /></FormItem>
+        </div>
+        <FormItem :label="`${model.metric.startsWith('MONTH_') ? '姣忔湀' : '姣忔棩'}闃堝�硷紙${unit}锛屽ぇ浜庢鍊兼椂閫氱煡锛塦" name="threshold"><InputNumber v-model:value="model.threshold" :min="0.001" :precision="3" style="width: 100%" /></FormItem>
+        <FormItem label="閫氱煡鎺ユ敹浜�" name="recipientIds"><Select v-model:value="model.recipientIds" mode="multiple" :options="recipients" option-filter-prop="label" placeholder="閫夋嫨鏈珯鍐呯敤鎴�" /></FormItem>
+        <FormItem label="鍚敤瑙勫垯"><Switch v-model:checked="model.enabled" /></FormItem>
+      </Form>
+    </Modal>
+  </Page>
+</template>
diff --git a/src/views/energy/analysis/index.vue b/src/views/energy/analysis/index.vue
index 5ddc53e..deb6e85 100644
--- a/src/views/energy/analysis/index.vue
+++ b/src/views/energy/analysis/index.vue
@@ -4,9 +4,10 @@
 import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue';
 import { Page } from '@vben/common-ui';
 import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
+import { downloadFileFromBlobPart } from '@vben/utils';
 import { Alert, Button, Card, DatePicker, Empty, Form, FormItem, Input, RadioGroup, Select, Space, Spin, Table, message } from 'ant-design-vue';
 import dayjs from 'dayjs';
-import { getEnergyAnalysis } from '#/api/energy';
+import { exportEnergyAnalysis, getEnergyAnalysis } from '#/api/energy';
 import { energyInfo, energyOptions, numberText, productOptions } from '../shared';
 
 defineOptions({ name: 'EnergyAnalysis' });
@@ -16,6 +17,7 @@
 const data = ref<EnergyAnalysis>();
 const loading = ref(false);
 const failed = ref(false);
+const exporting = ref(false);
 const selectedEnergy = ref<EnergyType>('WATER');
 const trendRef = ref<EchartsUIType>();
 const shareRef = ref<EchartsUIType>();
@@ -23,6 +25,18 @@
 const trendChart = useEcharts(trendRef);
 const shareChart = useEcharts(shareRef);
 const areaChart = useEcharts(areaRef);
+const costRef = ref<EchartsUIType>();
+const areaShareRef = ref<EchartsUIType>();
+const usageRef = ref<EchartsUIType>();
+const costChart = useEcharts(costRef);
+const areaShareChart = useEcharts(areaShareRef);
+const usageChart = useEcharts(usageRef);
+const toolbox = (name: string) => ({ right: 8, feature: { saveAsImage: { title: '涓嬭浇 PNG 鍥剧墖', name, pixelRatio: 2 } } });
+async function exportReport() {
+  exporting.value = true;
+  try { downloadFileFromBlobPart({ fileName: '鑳借�楀垎鏋�.xlsx', source: await exportEnergyAnalysis({ ...applied.value }) }); }
+  finally { exporting.value = false; }
+}
 const hasRecords = computed(() => (data.value?.recordCount ?? 0) > 0);
 const summaryColumns = [
   { title: '鑳芥簮', dataIndex: 'energyType' },
@@ -80,6 +94,7 @@
   const values = new Map(data.value.trend.filter(row => row.energyType === selectedEnergy.value).map(row => [row.label, Number(row.quantity)]));
   await trendChart.renderEcharts({
     animation: false,
+    toolbox: toolbox(`${info.name}鐢ㄩ噺瓒嬪娍`),
     tooltip: { trigger: 'axis', confine: true },
     grid: { left: 20, right: 24, top: 40, bottom: 70, containLabel: true },
     xAxis: { type: 'category', data: periods },
@@ -87,21 +102,56 @@
     dataZoom: [{ type: 'inside' }, { type: 'slider', height: 18, bottom: 12 }],
     series: [{ name: `${info.name}锛�${info.unit}锛塦, type: 'line', showSymbol: periods.length < 40, connectNulls: false, itemStyle: { color: info.color }, data: periods.map(period => values.get(period) ?? null) }],
   });
+  const usage = data.value.areas.filter(row => row.energyType === selectedEnergy.value).sort((a, b) => Number(b.quantity) - Number(a.quantity)).slice(0, 10).reverse();
+  await usageChart.renderEcharts({
+    animation: false, toolbox: toolbox(`${info.name}鍖哄煙鐢ㄩ噺`),
+    tooltip: { trigger: 'axis', confine: true, axisPointer: { type: 'shadow' } },
+    grid: { left: 16, right: 36, top: 40, bottom: 24, containLabel: true },
+    xAxis: { type: 'value', name: info.unit },
+    yAxis: { type: 'category', data: usage.map(row => row.label), axisLabel: { width: 100, overflow: 'truncate' } },
+    series: [{ name: `${info.name}锛�${info.unit}锛塦, type: 'bar', itemStyle: { color: info.color }, data: usage.map(row => Number(row.quantity)) }],
+  });
+  await costChart.renderEcharts({
+    animation: false, toolbox: toolbox('鑳芥簮璐圭敤瓒嬪娍'),
+    tooltip: { trigger: 'axis', confine: true }, legend: { bottom: 0 },
+    grid: { left: 16, right: 24, top: 40, bottom: 70, containLabel: true },
+    xAxis: { type: 'category', data: periods }, yAxis: { type: 'value', name: '璐圭敤锛堝厓锛�' },
+    dataZoom: [{ type: 'inside' }],
+    series: energyOptions.map(option => {
+      const costs = new Map(data.value!.trend.filter(row => row.energyType === option.value).map(row => [row.label, Number(row.amount)]));
+      return { name: option.label, type: 'line' as const, showSymbol: periods.length < 40, connectNulls: false,
+        itemStyle: { color: energyInfo[option.value as EnergyType].color }, data: periods.map(period => costs.get(period) ?? null) };
+    }),
+  });
 }
 async function renderCharts() {
   if (!hasRecords.value || !data.value) return;
   await renderTrend();
   if (Number(data.value.totalAmount) > 0) await shareChart.renderEcharts({
     animation: false,
+    toolbox: toolbox('鑳芥簮璐圭敤鍗犳瘮'),
     tooltip: { trigger: 'item', confine: true, formatter: '{b}锛歿c} 鍏冿紙{d}%锛�' },
     legend: { bottom: 0 },
     series: [{ type: 'pie', radius: '65%', center: ['50%', '45%'], label: { formatter: '{b}\n{d}%' }, data: data.value.summary.filter(row => Number(row.amount) > 0).map(row => ({ name: energyInfo[row.energyType].name, value: Number(row.amount), itemStyle: { color: energyInfo[row.energyType].color } })) }],
   });
   const totals = new Map<string, number>();
   for (const row of data.value.areas) totals.set(row.label, (totals.get(row.label) ?? 0) + Number(row.amount));
+  if (Number(data.value.totalAmount) > 0) {
+    const sorted = [...totals].filter(([, amount]) => amount > 0).sort((a, b) => b[1] - a[1]);
+    const shares = sorted.slice(0, 10).map(([name, value]) => ({ name, value }));
+    const others = sorted.slice(10).reduce((sum, [, amount]) => sum + amount, 0);
+    if (others > 0) shares.push({ name: '鍏朵粬鍖哄煙鍚堣', value: others });
+    await areaShareChart.renderEcharts({
+      animation: false, toolbox: toolbox('鍖哄煙璐圭敤鍗犳瘮'),
+      tooltip: { trigger: 'item', confine: true, formatter: '{b}锛歿c} 鍏冿紙{d}%锛�' },
+      legend: { type: 'scroll', bottom: 0 },
+      series: [{ type: 'pie', radius: ['35%', '60%'], center: ['50%', '44%'], label: { formatter: '{b}\n{d}%', width: 85, overflow: 'truncate' }, data: shares }],
+    });
+  }
   const areaLabels = [...totals].sort((a, b) => b[1] - a[1]).slice(0, 10).map(([label]) => label).reverse();
   await areaChart.renderEcharts({
     animation: false,
+    toolbox: toolbox('鍖哄煙璐圭敤鍒嗗竷'),
     tooltip: { trigger: 'axis', confine: true, axisPointer: { type: 'shadow' } },
     legend: { bottom: 0 },
     grid: { left: 16, right: 24, top: 30, bottom: 45, containLabel: true },
@@ -127,7 +177,7 @@
         <FormItem label="鑳芥簮"><Select v-model:value="filters.energyType" :options="energyOptions" allow-clear placeholder="鍏ㄩ儴鑳芥簮" style="width: 130px" /></FormItem>
         <FormItem label="鍖哄煙"><Input v-model:value="filters.usageArea" placeholder="鍖哄煙鍏抽敭璇�" allow-clear style="width: 150px" /></FormItem>
         <FormItem label="浜у搧"><Select v-model:value="filters.productCategory" :options="productOptions" allow-clear placeholder="鍏ㄩ儴绫诲埆" style="width: 150px" /></FormItem>
-        <FormItem><Space><Button type="primary" :loading="loading" @click="load">鍒嗘瀽</Button><Button @click="reset">閲嶇疆</Button></Space></FormItem>
+        <FormItem><Space wrap><Button type="primary" :loading="loading" @click="load">鍒嗘瀽</Button><Button @click="reset">閲嶇疆</Button><Button v-access:code="['energy:analysis:export']" :disabled="!data || loading" :loading="exporting" @click="exportReport">瀵煎嚭鍒嗘瀽鎶ヨ〃</Button></Space></FormItem>
       </Form>
     </Card>
     <Alert v-if="failed" type="error" show-icon message="鑳借�楀垎鏋愬姞杞藉け璐ワ紝璇风偣鍑诲垎鏋愰噸璇曘��" class="mb-4" />
@@ -157,7 +207,11 @@
           <div class="mb-4 grid grid-cols-1 gap-4 lg:grid-cols-2">
             <Card title="鑳芥簮璐圭敤鍗犳瘮"><EchartsUI v-if="Number(data.totalAmount) > 0" ref="shareRef" class="h-[330px] w-full" /><Empty v-else description="灏氭湭褰曞叆璐圭敤锛屽~鍐欏崟浠峰悗鏄剧ず璐圭敤鍗犳瘮銆�" /></Card>
             <Card title="鍖哄煙璐圭敤鍒嗗竷锛堝墠 10锛�"><EchartsUI ref="areaRef" class="h-[330px] w-full" /></Card>
+            <Card title="鑳芥簮璐圭敤瓒嬪娍"><EchartsUI ref="costRef" class="h-[330px] w-full" /></Card>
+            <Card title="鍖哄煙璐圭敤鍗犳瘮"><EchartsUI v-if="Number(data.totalAmount) > 0" ref="areaShareRef" class="h-[330px] w-full" /><Empty v-else description="灏氭棤璐圭敤鏁版嵁" /></Card>
+            <Card class="lg:col-span-2" :title="`${energyInfo[selectedEnergy].name} 路 鍖哄煙鐢ㄩ噺瀵规瘮锛堝墠 10锛塦"><EchartsUI ref="usageRef" class="h-[330px] w-full" /></Card>
           </div>
+          <p class="text-sm text-muted-foreground">鍥捐〃鍙充笂瑙掑彲涓嬭浇 PNG锛涘垎鏋愭姤琛ㄥ寘鍚粺璁″彛寰勩�佽兘婧愭眹鎬汇�佹棩鏈熻秼鍔裤�佸尯鍩熸眹鎬诲洓涓伐浣滆〃锛屼娇鐢ㄦ渶杩戜竴娆″垎鏋愮殑绛涢�夋潯浠躲��</p>
           <Card title="瓒嬪娍鏄庣粏">
             <Table :columns="detailColumns" :data-source="data.trend" :row-key="row => `${row.label}-${row.energyType}`" :scroll="{ x: 600 }" :pagination="{ pageSize: 10, showSizeChanger: true }" size="small">
               <template #bodyCell="{ column, record }">
diff --git a/src/views/energy/record/index.vue b/src/views/energy/record/index.vue
index 52ff946..b301653 100644
--- a/src/views/energy/record/index.vue
+++ b/src/views/energy/record/index.vue
@@ -3,15 +3,23 @@
 import type { EnergyFilters, EnergyRecord } from '#/api/energy';
 import { computed, onMounted, reactive, ref } from 'vue';
 import { Page } from '@vben/common-ui';
+import { downloadFileFromBlobPart } from '@vben/utils';
 import { Alert, AutoComplete, Button, Card, DatePicker, Form, FormItem, Input, InputNumber, Modal, Popconfirm, Select, Space, Table, Tag, message } from 'ant-design-vue';
 import dayjs from 'dayjs';
-import { createEnergyRecord, deleteEnergyRecord, getEnergyRecord, getEnergyRecordPage, updateEnergyRecord } from '#/api/energy';
+import { createEnergyRecord, deleteEnergyRecord, exportEnergyRecords, getEnergyRecord, getEnergyRecordPage, updateEnergyRecord } from '#/api/energy';
 import { areaOptions, energyInfo, energyOptions, numberText, productOptions, purposeOptions } from '../shared';
 
 defineOptions({ name: 'EnergyRecord' });
 const filters = reactive<EnergyFilters>({});
 const rows = ref<EnergyRecord[]>([]);
 const loading = ref(false);
+const exporting = ref(false);
+async function exportRecords() {
+  if (filters.startDate && filters.endDate && filters.startDate > filters.endDate) { message.warning('寮�濮嬫棩鏈熶笉鑳芥櫄浜庣粨鏉熸棩鏈�'); return; }
+  exporting.value = true;
+  try { downloadFileFromBlobPart({ fileName: '鑳借�楀彴璐�.xlsx', source: await exportEnergyRecords({ ...filters }) }); }
+  finally { exporting.value = false; }
+}
 const loadError = ref(false);
 const pagination = reactive({ current: 1, pageSize: 10, total: 0, showSizeChanger: true });
 const open = ref(false);
@@ -118,7 +126,7 @@
       </Form>
       <div class="mb-4 flex flex-wrap items-center justify-between gap-3">
         <span class="text-base font-semibold">鑳借�楀彴璐�</span>
-        <Button v-access:code="['energy:record:create']" type="primary" @click="showForm()">鏂板鑳借�楄褰�</Button>
+        <Space><Button v-access:code="['energy:record:export']" :loading="exporting" @click="exportRecords">瀵煎嚭鍙拌处</Button><Button v-access:code="['energy:record:create']" type="primary" @click="showForm()">鏂板鑳借�楄褰�</Button></Space>
       </div>
       <Alert v-if="loadError" type="error" message="鑳借�楄褰曞姞杞藉け璐ワ紝璇风偣鍑绘煡璇㈤噸璇曘��" show-icon class="mb-4" />
       <Table :columns="columns" :data-source="rows" row-key="id" :loading="loading" :pagination="pagination" :scroll="{ x: 1420 }" :locale="{ emptyText: '鏆傛棤鑳借�楄褰曪紝璇锋柊澧炴瘡鏃ョ敤鑳戒俊鎭�' }" @change="tableChange">
diff --git a/src/views/erp/finance/payment/index.vue b/src/views/erp/finance/payment/index.vue
index 8a9658e..2d5b675 100644
--- a/src/views/erp/finance/payment/index.vue
+++ b/src/views/erp/finance/payment/index.vue
@@ -1,4 +1,6 @@
 <script lang="ts" setup>
+import { automaticApproval } from '#/store/audit-policy';
+import { submitFinancePayment } from '#/api/erp/finance/payment';
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { ErpFinancePaymentApi } from '#/api/erp/finance/payment';
 
@@ -77,10 +79,17 @@
 /** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
 async function handleSubmit(row: ErpFinancePaymentApi.FinancePayment) {
   const hideLoading = message.loading({
-    content: '鑾峰彇瀹℃壒娴佺▼...',
+    content: automaticApproval.value ? '姝e湪鎻愪氦鐢熸晥...' : '鑾峰彇瀹℃壒娴佺▼...',
     duration: 0,
   });
   try {
+    if (automaticApproval.value) {
+      await submitFinancePayment(row.id!, 'finance_payment_approve');
+      hideLoading();
+      message.success('宸叉彁浜ょ敓鏁�');
+      handleRefresh();
+      return;
+    }
     const processList = await getFinancePaymentApproveProcessList();
     hideLoading();
     if (!processList || processList.length === 0) {
@@ -88,6 +97,7 @@
       return;
     }
     // 鎵撳紑娴佺▼閫夋嫨寮圭獥
+
     processModalApi.setData({ id: row.id, processList }).open();
   } catch {
     hideLoading();
diff --git a/src/views/erp/purchase/request/index.vue b/src/views/erp/purchase/request/index.vue
index 9957565..a0a8a00 100644
--- a/src/views/erp/purchase/request/index.vue
+++ b/src/views/erp/purchase/request/index.vue
@@ -1,4 +1,5 @@
 <script lang="ts" setup>
+import { automaticApproval } from '#/store/audit-policy';
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { ErpPurchaseRequestApi } from '#/api/erp/purchase/request';
 
@@ -96,10 +97,17 @@
 /** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
 async function handleSubmit(row: ErpPurchaseRequestApi.PurchaseRequest) {
   const hideLoading = message.loading({
-    content: '鑾峰彇瀹℃壒娴佺▼...',
+    content: automaticApproval.value ? '姝e湪鎻愪氦鐢熸晥...' : '鑾峰彇瀹℃壒娴佺▼...',
     duration: 0,
   });
   try {
+    if (automaticApproval.value) {
+      await submitPurchaseRequest(row.id!, 'purchase_request_approve');
+      hideLoading();
+      message.success('宸叉彁浜ょ敓鏁�');
+      handleRefresh();
+      return;
+    }
     const processList = await getApproveProcessList(row.id!);
     hideLoading();
     if (!processList || processList.length === 0) {
@@ -107,6 +115,7 @@
       return;
     }
     // 鎵撳紑娴佺▼閫夋嫨寮圭獥
+
     processModalApi.setData({ id: row.id, processList }).open();
   } catch {
     hideLoading();
diff --git a/src/views/erp/sale/order/data.ts b/src/views/erp/sale/order/data.ts
index d5c8233..6e8a843 100644
--- a/src/views/erp/sale/order/data.ts
+++ b/src/views/erp/sale/order/data.ts
@@ -70,8 +70,8 @@
       component: 'DatePicker',
       componentProps: {
         placeholder: '閫夋嫨璁㈠崟鏃堕棿',
-        showTime: true,
-        format: 'YYYY-MM-DD HH:mm:ss',
+        showTime: false,
+        format: 'YYYY-MM-DD',
         valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
@@ -341,6 +341,8 @@
       component: 'RangePicker',
       componentProps: {
         ...getRangePickerDefaultProps(),
+        showTime: false,
+        format: 'YYYY-MM-DD',
         allowClear: true,
       },
     },
diff --git a/src/views/erp/sale/order/detail/index.vue b/src/views/erp/sale/order/detail/index.vue
index 673f4a4..0f2ef2e 100644
--- a/src/views/erp/sale/order/detail/index.vue
+++ b/src/views/erp/sale/order/detail/index.vue
@@ -9,7 +9,7 @@
 import {
   erpCountInputFormatter,
   erpPriceInputFormatter,
-  formatDateTime,
+  formatDate,
 } from '@vben/utils';
 
 import { Spin, Tag } from 'ant-design-vue';
@@ -67,7 +67,7 @@
     {
       label: '璁㈠崟鏃堕棿',
       field: 'orderTime',
-      render: (val) => formatDateTime(val) as string,
+      render: (val) => formatDate(val) as string,
     },
     {
       label: '鍚堣鏁伴噺',
@@ -109,7 +109,7 @@
     {
       label: '鍒涘缓鏃堕棿',
       field: 'createTime',
-      render: (val) => formatDateTime(val) as string,
+      render: (val) => formatDate(val) as string,
     },
     { label: '澶囨敞', field: 'remark', render: textRender },
   ] as DescriptionItemSchema[],
diff --git a/src/views/erp/sale/order/index.vue b/src/views/erp/sale/order/index.vue
index 5d32a61..bec1872 100644
--- a/src/views/erp/sale/order/index.vue
+++ b/src/views/erp/sale/order/index.vue
@@ -9,6 +9,7 @@
 import { downloadFileFromBlobPart, isEmpty } from '@vben/utils';
 
 import { message, Tag, Tooltip } from 'ant-design-vue';
+import dayjs from 'dayjs';
 
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
 import {
@@ -22,6 +23,7 @@
 } from '#/api/erp/sale/order';
 import { generateFromSaleOrder } from '#/api/mes/wm/salesnotice';
 import { $t } from '#/locales';
+import { automaticApproval } from '#/store/audit-policy';
 
 import { useGridColumns, useGridFormSchema } from './data';
 import Form from './modules/form.vue';
@@ -57,8 +59,25 @@
 
 /** 瀵煎嚭琛ㄦ牸 */
 async function handleExport() {
-  const data = await exportSaleOrder(await gridApi.formApi.getValues());
+  const data = await exportSaleOrder(
+    normalizeDateRange(await gridApi.formApi.getValues()),
+  );
   downloadFileFromBlobPart({ fileName: '閿�鍞鍗�.xls', source: data });
+}
+
+/** 鏃ユ湡绛涢�夎鐩栨墍閫夎捣姝㈡棩鏈熺殑鍏ㄥぉ锛屽吋瀹瑰悗绔椂闂寸被鍨嬨�� */
+function normalizeDateRange(values: Record<string, any>) {
+  const range = values.orderTime;
+  if (!Array.isArray(range) || range.length !== 2 || !range.every(Boolean)) {
+    return values;
+  }
+  return {
+    ...values,
+    orderTime: [
+      dayjs(range[0]).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
+      dayjs(range[1]).endOf('day').format('YYYY-MM-DD HH:mm:ss'),
+    ],
+  };
 }
 
 /** 瀵煎叆閿�鍞鍗� */
@@ -94,18 +113,18 @@
 /** 鎻愪氦閿�鍞鍗曞鎵癸紙鍗忓悓鍔炲叕锛� */
 async function handleSubmit(row: ErpSaleOrderApi.SaleOrder) {
   // 鑾峰彇鍙敤鐨勫鎵规祦绋嬪畾涔�
-  const processList = await getSaleOrderApproveProcessList();
-  if (isEmpty(processList)) {
+  const processList = automaticApproval.value ? [] : await getSaleOrderApproveProcessList();
+  if (!automaticApproval.value && isEmpty(processList)) {
     message.error('閿�鍞鍗曞鎵规祦绋嬫湭閰嶇疆锛岃鍏堝湪銆屽崗鍚屽姙鍏啋娴佺▼绠$悊銆嶄腑鍒涘缓骞跺彂甯冮攢鍞鍗曞鎵规祦绋�');
     return;
   }
   const hideLoading = message.loading({
-    content: '姝e湪鎻愪氦瀹℃壒...',
+    content: automaticApproval.value ? '姝e湪鎻愪氦鐢熸晥...' : '姝e湪鎻愪氦瀹℃壒...',
     duration: 0,
   });
   try {
-    await submitSaleOrder(row.id!, processList[0].key);
-    message.success('鎻愪氦瀹℃壒鎴愬姛锛岃鍒板崗鍚屽姙鍏腑澶勭悊');
+    await submitSaleOrder(row.id!, automaticApproval.value ? 'sale_order_approve' : processList[0]!.key);
+    message.success(automaticApproval.value ? '宸叉彁浜ょ敓鏁�' : '鎻愪氦瀹℃壒鎴愬姛锛岃鍒板崗鍚屽姙鍏腑澶勭悊');
     handleRefresh();
   } finally {
     hideLoading();
@@ -208,7 +227,7 @@
       ajax: {
         query: async ({ page }, formValues) => {
           // 鐢熶骇鐘舵�佹悳绱㈠弬鏁拌浆鎹細-1(鏃犻渶鐢熶骇) 鈫� needProduction=0
-          const { productionStatus, ...rest } = formValues;
+          const { productionStatus, ...rest } = normalizeDateRange(formValues);
           const params: Record<string, any> = { ...rest };
           if (productionStatus === -1) {
             params.needProduction = 0;
diff --git a/src/views/erp/sale/order/modules/form.vue b/src/views/erp/sale/order/modules/form.vue
index 638f3ae..c7edfa7 100644
--- a/src/views/erp/sale/order/modules/form.vue
+++ b/src/views/erp/sale/order/modules/form.vue
@@ -6,6 +6,7 @@
 import { computed, ref } from 'vue';
 
 import { useVbenModal } from '@vben/common-ui';
+import { formatDate } from '@vben/utils';
 
 import { message, Tag } from 'ant-design-vue';
 
@@ -133,7 +134,10 @@
     try {
       formData.value = await getSaleOrder(data.id);
       // 璁剧疆鍒� values
-      await formApi.setValues(formData.value);
+      await formApi.setValues({
+        ...formData.value,
+        productionFinishTime: formatDate(formData.value.productionFinishTime),
+      });
       if (formData.value?.attachmentList?.length) {
         const blobIds = formData.value.attachmentList.map((item) => ({
           uid: String(item.id),
diff --git a/src/views/erp/sale/order/modules/print.vue b/src/views/erp/sale/order/modules/print.vue
index 12e3cc5..fb64923 100644
--- a/src/views/erp/sale/order/modules/print.vue
+++ b/src/views/erp/sale/order/modules/print.vue
@@ -7,7 +7,7 @@
 import { DICT_TYPE } from '@vben/constants';
 import { getDictLabel } from '@vben/hooks';
 import { useUserStore } from '@vben/stores';
-import { erpPriceMultiply, formatDateTime } from '@vben/utils';
+import { erpPriceMultiply, formatDate, formatDateTime } from '@vben/utils';
 
 import { getCustomerSimpleList } from '#/api/crm/customer';
 import { getAccountSimpleList } from '#/api/erp/finance/account';
@@ -216,7 +216,7 @@
         <div>璁㈠崟鍗曞彿锛歿{ printData.no || '-' }}</div>
         <div>
           璁㈠崟鏃堕棿锛歿{
-            formatDateTime(printData.orderTime) || '-'
+            formatDate(printData.orderTime) || '-'
           }}
         </div>
         <div>瀹㈡埛锛歿{ customerName }}</div>
@@ -229,7 +229,7 @@
         <div>鍒涘缓浜猴細{{ creatorName }}</div>
         <div>
           鍒涘缓鏃堕棿锛歿{
-            formatDateTime(printData.createTime) || '-'
+            formatDate(printData.createTime) || '-'
           }}
         </div>
         <div class="col-span-3">澶囨敞锛歿{ printData.remark || '-' }}</div>
diff --git a/src/views/hrm/attendance/exception/index.vue b/src/views/hrm/attendance/exception/index.vue
index acd5d7a..23522fb 100644
--- a/src/views/hrm/attendance/exception/index.vue
+++ b/src/views/hrm/attendance/exception/index.vue
@@ -1,4 +1,5 @@
 <script lang="ts" setup>
+import { automaticApproval } from '#/store/audit-policy';
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { HrmAttendanceExceptionApi } from '#/api/hrm/attendance/exception';
 
@@ -48,10 +49,15 @@
 
 /** 鎻愪氦鑰冨嫟寮傚父鐢宠 */
 async function handleSubmit(row: HrmAttendanceExceptionApi.AttendanceException) {
-  const res = await getAttendanceExceptionApproveProcessList();
+  const res = automaticApproval.value ? [] : await getAttendanceExceptionApproveProcessList();
   processList.value = res;
   currentRow.value = row;
   selectedProcessKey.value = undefined;
+  if (automaticApproval.value) {
+    selectedProcessKey.value = 'hrm_attendance_exception';
+    await confirmSubmit();
+    return;
+  }
   submitModalVisible.value = true;
 }
 
@@ -64,7 +70,7 @@
   submitLoading.value = true;
   try {
     await submitAttendanceException(currentRow.value!.id!, selectedProcessKey.value);
-    message.success('鎻愪氦瀹℃壒鎴愬姛');
+    message.success(automaticApproval.value ? '宸叉彁浜ょ敓鏁�' : '鎻愪氦瀹℃壒鎴愬姛');
     submitModalVisible.value = false;
     handleRefresh();
   } finally {
diff --git a/src/views/hrm/leave/index.vue b/src/views/hrm/leave/index.vue
index d088fc2..e1585a2 100644
--- a/src/views/hrm/leave/index.vue
+++ b/src/views/hrm/leave/index.vue
@@ -1,4 +1,5 @@
 <script lang="ts" setup>
+import { automaticApproval } from '#/store/audit-policy';
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { HrmLeaveApi } from '#/api/hrm/leave';
 
@@ -72,9 +73,14 @@
 
 /** 鎻愪氦璇峰亣鐢宠锛堝厛閫夋嫨瀹℃壒娴佺▼锛� */
 async function handleSubmit(row: HrmLeaveApi.Leave) {
-  processList.value = await getLeaveApproveProcessDefinitionList();
+  processList.value = automaticApproval.value ? [] : await getLeaveApproveProcessDefinitionList();
   currentRow.value = row;
   selectedProcessKey.value = undefined;
+  if (automaticApproval.value) {
+    selectedProcessKey.value = 'hrm_leave';
+    await confirmSubmit();
+    return;
+  }
   submitModalVisible.value = true;
 }
 
diff --git a/src/views/hrm/overtime/index.vue b/src/views/hrm/overtime/index.vue
index 9b58111..b4e116e 100644
--- a/src/views/hrm/overtime/index.vue
+++ b/src/views/hrm/overtime/index.vue
@@ -1,4 +1,5 @@
 <script lang="ts" setup>
+import { automaticApproval } from '#/store/audit-policy';
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { HrmOvertimeApi } from '#/api/hrm/overtime';
 
@@ -64,9 +65,14 @@
 const submitLoading = ref(false);
 
 async function handleSubmit(row: HrmOvertimeApi.Overtime) {
-  processList.value = await getOvertimeApproveProcessDefinitionList();
+  processList.value = automaticApproval.value ? [] : await getOvertimeApproveProcessDefinitionList();
   currentRow.value = row;
   selectedProcessKey.value = undefined;
+  if (automaticApproval.value) {
+    selectedProcessKey.value = 'hrm_overtime';
+    await confirmSubmit();
+    return;
+  }
   submitModalVisible.value = true;
 }
 
@@ -78,7 +84,7 @@
   submitLoading.value = true;
   try {
     await submitOvertime(currentRow.value!.id!, selectedProcessKey.value);
-    message.success('宸叉彁浜よ嚦鍗忓悓鍔炲叕瀹℃壒');
+    message.success(automaticApproval.value ? '宸叉彁浜ょ敓鏁�' : '宸叉彁浜よ嚦鍗忓悓鍔炲叕瀹℃壒');
     submitModalVisible.value = false;
     handleRefresh();
   } finally {
diff --git a/src/views/mes/dv/meteringcheck/data.ts b/src/views/mes/dv/meteringcheck/data.ts
index 4e1e4e9..5c88821 100644
--- a/src/views/mes/dv/meteringcheck/data.ts
+++ b/src/views/mes/dv/meteringcheck/data.ts
@@ -7,7 +7,6 @@
 import { DICT_TYPE } from '@vben/constants';
 import { getDictOptions } from '@vben/hooks';
 
-import { getSimpleUserList } from '#/api/system/user';
 import { DvMeteringSelect } from '#/views/mes/dv/metering/components';
 
 /** 琛ㄥ崟绫诲瀷 */
@@ -83,15 +82,13 @@
       },
     },
     {
-      fieldName: 'checkUserId',
+      fieldName: 'checkUserName',
       label: '妫�娴嬩汉',
-      component: 'ApiSelect',
+      component: 'Input',
       componentProps: {
         allowClear: true,
-        api: getSimpleUserList,
-        labelField: 'nickname',
-        placeholder: '璇烽�夋嫨妫�娴嬩汉',
-        valueField: 'id',
+        placeholder: '璇疯緭鍏ユ娴嬩汉濮撳悕',
+        maxlength: 100,
       },
     },
     {
@@ -193,7 +190,7 @@
       },
     },
     { field: 'certNo', title: '璇佷功缂栧彿', minWidth: 130 },
-    { field: 'checkUserNickname', title: '妫�娴嬩汉', minWidth: 100 },
+    { field: 'checkUserName', title: '妫�娴嬩汉', minWidth: 100 },
     {
       field: 'nextCheckDate',
       title: '涓嬫妫�娴嬫棩鏈�',
diff --git a/src/views/mes/md/item/components/select.vue b/src/views/mes/md/item/components/select.vue
index 6a45bdb..3671f6b 100644
--- a/src/views/mes/md/item/components/select.vue
+++ b/src/views/mes/md/item/components/select.vue
@@ -42,7 +42,9 @@
 
 const displayLabel = computed(() => {
   if (selectedItems.value.length === 0) return '';
-  return selectedItems.value.map(item => item.name ?? '').join('銆�');
+  return selectedItems.value.map(item =>
+    [item.name, item.specification, item.code ? `锛�${item.code}锛塦 : ''].filter(Boolean).join(' '),
+  ).join('銆�');
 });
 const showClear = computed(
   () =>
diff --git a/src/views/mes/md/item/data.ts b/src/views/mes/md/item/data.ts
index 5d74bb2..bf649b1 100644
--- a/src/views/mes/md/item/data.ts
+++ b/src/views/mes/md/item/data.ts
@@ -216,8 +216,14 @@
       component: 'Input',
       componentProps: {
         allowClear: true,
-        placeholder: '璇疯緭鍏ョ墿鏂欏悕绉�',
+        placeholder: '鏀寔鍚嶇О涓庤鏍肩粍鍚堟煡璇�',
       },
+    },
+    {
+      fieldName: 'specification',
+      label: '瑙勬牸鍨嬪彿',
+      component: 'Input',
+      componentProps: { allowClear: true, placeholder: '璇疯緭鍏ヨ鏍煎瀷鍙�' },
     },
     {
       fieldName: 'status',
@@ -350,9 +356,15 @@
       component: 'Input',
       componentProps: {
         allowClear: true,
-        placeholder: '璇疯緭鍏ョ墿鏂欏悕绉�',
+        placeholder: '鏀寔鍚嶇О涓庤鏍肩粍鍚堟煡璇�',
       },
     },
+    {
+      fieldName: 'specification',
+      label: '瑙勬牸鍨嬪彿',
+      component: 'Input',
+      componentProps: { allowClear: true, placeholder: '璇疯緭鍏ヨ鏍煎瀷鍙�' },
+    },
   ];
 }
 
diff --git a/src/views/mes/pd/project/detail/index.vue b/src/views/mes/pd/project/detail/index.vue
index a7f2c26..ac089ae 100644
--- a/src/views/mes/pd/project/detail/index.vue
+++ b/src/views/mes/pd/project/detail/index.vue
@@ -1,4 +1,6 @@
 <script lang="ts" setup>
+import { automaticApproval } from '#/store/audit-policy';
+import { submitPdProjectApproval } from '#/api/mes/pd/project';
 import type { FormType } from '../data';
 import type { MesPdProjectApi } from '#/api/mes/pd/project';
 
@@ -137,8 +139,15 @@
 
 /** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
 async function handleSubmit() {
-  const hideLoading = message.loading({ content: '鑾峰彇瀹℃壒娴佺▼...', duration: 0 });
+  const hideLoading = message.loading({ content: automaticApproval.value ? '姝e湪鎻愪氦鐢熸晥...' : '鑾峰彇瀹℃壒娴佺▼...', duration: 0 });
   try {
+    if (automaticApproval.value) {
+      await submitPdProjectApproval(projectId.value, 'pd_project_approve');
+      hideLoading();
+      message.success('宸叉彁浜ょ敓鏁�');
+      await loadProjectDetail();
+      return;
+    }
     const processList = await getPdProjectApproveProcessList();
     hideLoading();
     if (!processList || processList.length === 0) {
@@ -146,6 +155,7 @@
       return;
     }
     // 鎵撳紑娴佺▼閫夋嫨寮圭獥
+
     processModalApi.setData({ id: projectId.value, processList }).open();
   } catch {
     hideLoading();
diff --git a/src/views/mes/pd/project/index.vue b/src/views/mes/pd/project/index.vue
index a511ad9..a62fab5 100644
--- a/src/views/mes/pd/project/index.vue
+++ b/src/views/mes/pd/project/index.vue
@@ -1,4 +1,6 @@
 <script lang="ts" setup>
+import { automaticApproval } from '#/store/audit-policy';
+import { submitPdProjectApproval } from '#/api/mes/pd/project';
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { MesPdProjectApi } from '#/api/mes/pd/project';
 
@@ -83,10 +85,17 @@
 /** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
 async function handleSubmit(row: MesPdProjectApi.Project) {
   const hideLoading = message.loading({
-    content: '鑾峰彇瀹℃壒娴佺▼...',
+    content: automaticApproval.value ? '姝e湪鎻愪氦鐢熸晥...' : '鑾峰彇瀹℃壒娴佺▼...',
     duration: 0,
   });
   try {
+    if (automaticApproval.value) {
+      await submitPdProjectApproval(row.id!, 'pd_project_approve');
+      hideLoading();
+      message.success('宸叉彁浜ょ敓鏁�');
+      handleRefresh();
+      return;
+    }
     const processList = await getPdProjectApproveProcessList();
     hideLoading();
     if (!processList || processList.length === 0) {
@@ -94,6 +103,7 @@
       return;
     }
     // 鎵撳紑娴佺▼閫夋嫨寮圭獥
+
     processModalApi.setData({ id: row.id, processList }).open();
   } catch {
     hideLoading();
diff --git a/src/views/mes/qc/iqc/data.ts b/src/views/mes/qc/iqc/data.ts
index 73c0f84..af978f7 100644
--- a/src/views/mes/qc/iqc/data.ts
+++ b/src/views/mes/qc/iqc/data.ts
@@ -12,7 +12,6 @@
 
 import { generateAutoCode } from '#/api/mes/md/autocode/record';
 import { getTemplateItemOptionList } from '#/api/mes/qc/template/item';
-import { getSupplierSimpleList } from '#/api/srm/supplier';
 import { getSimpleUserList } from '#/api/system/user';
 import { getRangePickerDefaultProps } from '#/utils';
 import { MdmItemSelect } from '#/views/basicData/mdm/components';
@@ -163,26 +162,13 @@
       },
     },
     {
-      fieldName: 'vendorId',
+      fieldName: 'vendorName',
       label: '渚涘簲鍟�',
-      component: 'ApiSelect',
+      component: 'Input',
       componentProps: {
         allowClear: true,
-        api: getSupplierSimpleList,
-        labelField: 'name',
-        placeholder: '璇烽�夋嫨渚涘簲鍟�',
-        valueField: 'id',
-      },
-      dependencies: {
-        triggerFields: ['sourceDocId'],
-        componentProps: (values) => ({
-          disabled: !!values.sourceDocId,
-          allowClear: true,
-          api: getSupplierSimpleList,
-          labelField: 'name',
-          placeholder: '璇烽�夋嫨渚涘簲鍟�',
-          valueField: 'id',
-        }),
+        maxlength: 255,
+        placeholder: '璇疯緭鍏ヤ緵搴斿晢鍚嶇О锛堝彲鐣欑┖锛�',
       },
     },
     {
@@ -309,15 +295,12 @@
       },
     },
     {
-      fieldName: 'vendorId',
+      fieldName: 'vendorName',
       label: '渚涘簲鍟�',
-      component: 'ApiSelect',
+      component: 'Input',
       componentProps: {
         allowClear: true,
-        api: getSupplierSimpleList,
-        labelField: 'name',
-        placeholder: '璇烽�夋嫨渚涘簲鍟�',
-        valueField: 'id',
+        placeholder: '璇疯緭鍏ヤ緵搴斿晢鍚嶇О',
       },
     },
     {
@@ -393,8 +376,8 @@
       minWidth: 180,
     },
     {
-      field: 'vendorNickname',
-      title: '渚涘簲鍟嗙畝绉�',
+      field: 'vendorName',
+      title: '渚涘簲鍟�',
       width: 120,
     },
     {
diff --git a/src/views/mes/qc/iqc/index.vue b/src/views/mes/qc/iqc/index.vue
index 4b1f1c5..a2c7514 100644
--- a/src/views/mes/qc/iqc/index.vue
+++ b/src/views/mes/qc/iqc/index.vue
@@ -63,7 +63,7 @@
       fileName: '鏉ユ枡妫�楠屽崟瀵煎叆妯℃澘.xls',
       hints: [
         '浠呭厑璁稿鍏� xls銆亁lsx 鏍煎紡鏂囦欢锛涘繀濉垪锛氭楠屽崟鍚嶇О銆佷骇鍝佺墿鏂欑紪鐮併�佸悎鏍煎搧鏁伴噺銆佷笉鍚堟牸鍝佹暟閲忋�佹娴嬫棩鏈熴�佹潵鏂欐棩鏈熴��',
-        '銆屼骇鍝佺墿鏂欑紪鐮併�嶉渶涓庛�岀墿鏂欑鐞嗐�嶄腑鐨勭紪鐮佸畬鍏ㄤ竴鑷达紱銆屼緵搴斿晢鍚嶇О銆嶉渶涓庛�屼緵搴斿晢绠$悊銆嶄腑鐨勫悕绉颁竴鑷达紝鐣欑┖鍒欒鍗曟棤渚涘簲鍟嗐��',
+        '銆屼骇鍝佺墿鏂欑紪鐮併�嶉渶涓庛�岀墿鏂欑鐞嗐�嶄腑鐨勭紪鐮佸畬鍏ㄤ竴鑷达紱銆屼緵搴斿晢鍚嶇О銆嶉潪蹇呭~锛屽彲鐩存帴濉啓鍚嶇О锛屾棤闇�鍦ㄧ郴缁熶腑寤虹珛渚涘簲鍟嗘。妗堛��',
         '鍚堟牸鍝佹暟閲� + 涓嶅悎鏍煎搧鏁伴噺 蹇呴』绛変簬銆屾湰娆℃帴鏀舵暟閲忋�嶏紙鐣欑┖鏃惰嚜鍔ㄥ彇涓よ�呬箣鍜岋級锛涖�屾娴嬬粨鏋溿�嶆寜瀛楀吀鍚嶇О濉啓锛堟牎楠岄�氳繃 / 鏍¢獙涓嶉�氳繃锛夈��',
         '銆岃川妫�鏂规銆嶅~鏂规鍚嶇О锛岀暀绌哄垯鎸夌墿鏂欒嚜鍔ㄥ尮閰� IQC 鏂规锛涖�屾娴嬩汉鍛樸�嶇暀绌哄彇褰撳墠鐧诲綍鐢ㄦ埛锛涙棩鏈熸敮鎸� 2026-01-01 10:00:00 鎴� 2026-01-01銆�',
         '閫愯瀵煎叆锛屽け璐ヨ涓嶅奖鍝嶆垚鍔熻锛涘鍏ュ悗鐨勬楠屽崟涓恒�屽緟妫�楠屻�嶇姸鎬侊紝鍙湪鍒楄〃涓户缁紪杈戙�佸綍鍏ユ娴嬬粨鏋溿��',
diff --git a/src/views/mes/qc/oqc/data.ts b/src/views/mes/qc/oqc/data.ts
index 4aeca09..f27a382 100644
--- a/src/views/mes/qc/oqc/data.ts
+++ b/src/views/mes/qc/oqc/data.ts
@@ -166,14 +166,15 @@
       label: '瀹㈡埛',
       component: markRaw(CrmCustomerSelect),
       componentProps: {
-        placeholder: '璇烽�夋嫨瀹㈡埛',
+        allowClear: true,
+        placeholder: '璇烽�夋嫨瀹㈡埛锛堝彲涓嶅~锛�',
       },
-      rules: 'selectRequired',
       dependencies: {
         triggerFields: ['sourceDocId'],
         componentProps: (values) => ({
           disabled: !!values.sourceDocId,
-          placeholder: '璇烽�夋嫨瀹㈡埛',
+          allowClear: true,
+          placeholder: '璇烽�夋嫨瀹㈡埛锛堝彲涓嶅~锛�',
         }),
       },
     },
diff --git a/src/views/mes/qc/pendinginspect/index.vue b/src/views/mes/qc/pendinginspect/index.vue
index cc33a44..831df1f 100644
--- a/src/views/mes/qc/pendinginspect/index.vue
+++ b/src/views/mes/qc/pendinginspect/index.vue
@@ -52,6 +52,7 @@
     sourceDocType: row.sourceDocType,
     sourceLineId: row.sourceLineId,
     vendorId: row.vendorId,
+    vendorName: row.vendorName,
   };
   iqcFormModalApi.setData({ formType: 'create', prefill }).open();
 }
diff --git a/src/views/mes/workbench/components/tabs/FeedbackHistoryTab.vue b/src/views/mes/workbench/components/tabs/FeedbackHistoryTab.vue
index 2f7092e..40fe4f8 100644
--- a/src/views/mes/workbench/components/tabs/FeedbackHistoryTab.vue
+++ b/src/views/mes/workbench/components/tabs/FeedbackHistoryTab.vue
@@ -1,4 +1,5 @@
 <script lang="ts" setup>
+import { automaticApproval } from '#/store/audit-policy';
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { MesProFeedbackApi } from '#/api/mes/pro/feedback';
 
@@ -318,7 +319,7 @@
             </Popconfirm>
             <!-- 瀹℃壒涓細鏌ョ湅瀹℃壒 -->
             <Button
-              v-if="row.status === STATUS_APPROVING"
+              v-if="!automaticApproval && row.status === STATUS_APPROVING"
               type="link"
               size="small"
               @click="handleViewProcess(row)"
diff --git a/src/views/srm/supplierApply/index.vue b/src/views/srm/supplierApply/index.vue
index 34663a8..a99f15c 100644
--- a/src/views/srm/supplierApply/index.vue
+++ b/src/views/srm/supplierApply/index.vue
@@ -1,4 +1,6 @@
 <script lang="ts" setup>
+import { automaticApproval } from '#/store/audit-policy';
+import { submitApply } from '#/api/srm/supplierApply';
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
 import type { SrmSupplierApplyApi } from '#/api/srm/supplierApply';
 
@@ -70,10 +72,17 @@
 /** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栧崗鍚屽姙鍏鎵规祦绋嬪垪琛� */
 async function handleSubmit(row: SrmSupplierApplyApi.ApplyVO) {
   const hideLoading = message.loading({
-    content: '鑾峰彇瀹℃壒娴佺▼...',
+    content: automaticApproval.value ? '姝e湪鎻愪氦鐢熸晥...' : '鑾峰彇瀹℃壒娴佺▼...',
     duration: 0,
   });
   try {
+    if (automaticApproval.value) {
+      await submitApply(row.id!, 'supplier_apply_approve');
+      hideLoading();
+      message.success('宸叉彁浜ょ敓鏁�');
+      handleRefresh();
+      return;
+    }
     const processList = await getApproveProcessList();
     hideLoading();
     if (!processList || processList.length === 0) {
@@ -81,6 +90,7 @@
       return;
     }
     // 鎵撳紑娴佺▼閫夋嫨寮圭獥
+
     processModalApi.setData({ id: row.id, processList }).open();
   } catch {
     hideLoading();

--
Gitblit v1.9.3