From 908fae82bbf0476df66c9213b49a8199571e8333 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 29 七月 2026 11:05:38 +0800
Subject: [PATCH] fix(datepicker): 修复日期选择器时间格式化问题

---
 src/views/mes/pro/mps/data.ts                                     |    2 
 src/views/erp/finance/payment/data.ts                             |    4 
 src/views/srm/statistics/index.vue                                |  128 +
 src/views/system/tenant/data.ts                                   |    2 
 src/views/wms/order/shipment/data.ts                              |    2 
 src/views/infra/demo/demo01/data.ts                               |    2 
 src/adapter/vxe-table.ts                                          |   33 
 src/views/srm/tender/index.vue                                    |  223 +++
 src/views/srm/tender/modules/form.vue                             |   84 +
 src/views/wms/order/movement/data.ts                              |    2 
 src/views/erp/stock/move/data.ts                                  |    2 
 src/views/srm/enums.ts                                            |  263 +++
 src/api/srm/supplierApply/index.ts                                |   78 +
 src/views/infra/demo/demo03/inner/data.ts                         |    2 
 src/views/mes/pro/workorder/data.ts                               |    2 
 src/views/erp/stock/check/data.ts                                 |    2 
 src/views/wls/outsourceissue/data.ts                              |    2 
 src/views/crm/saleQuotation/data.ts                               |    4 
 src/views/infra/demo/demo03/erp/data.ts                           |    2 
 src/views/srm/supplier/modules/form.vue                           |   82 +
 src/views/wls/stocktaking/task/data.ts                            |    4 
 src/views/mes/pro/feedback/data.ts                                |    2 
 src/views/crm/clue/data.ts                                        |    2 
 src/views/wls/productreceipt/data.ts                              |    2 
 src/views/erp/purchase/in/data.ts                                 |    4 
 src/views/mes/workbench/components/tabs/ReturnIssueTab.vue        |    2 
 src/views/mes/pd/project/data.ts                                  |    2 
 src/views/mes/qc/iqc/data.ts                                      |    4 
 src/views/mes/pro/card/data.ts                                    |    4 
 src/views/crm/contract/data.ts                                    |    6 
 src/views/wms/order/check/data.ts                                 |    2 
 src/views/srm/supplier/components/index.ts                        |    2 
 src/views/srm/supplier/data.ts                                    |  178 ++
 src/views/srm/tender/detail.vue                                   |  811 ++++++++++
 src/views/wls/arrivalnotice/data.ts                               |    2 
 src/views/erp/purchase/order/data.ts                              |    4 
 src/views/mes/workbench/components/tabs/ProductIssueFormModal.vue |    2 
 src/views/mes/dv/maintenrecord/data.ts                            |    2 
 src/views/erp/purchase/request/data.ts                            |    4 
 src/views/erp/sale/return/data.ts                                 |    2 
 src/views/srm/tender/data.ts                                      |  188 ++
 src/api/mdm/item/index.ts                                         |   11 
 src/api/srm/tender/index.ts                                       |  232 +++
 src/views/bpm/oa/leave/data.ts                                    |    4 
 src/views/srm/supplierCertificate/modules/form.vue                |   82 +
 src/views/erp/purchase/statistics/supplier.vue                    |    2 
 src/views/wls/salesnotice/data.ts                                 |    2 
 src/views/mes/dv/checkplan/data.ts                                |    4 
 src/views/mes/dv/machinery/data.ts                                |    4 
 src/views/srm/supplierEvaluation/data.ts                          |  132 +
 src/views/crm/receivable/plan/data.ts                             |    2 
 src/views/wls/returnsales/data.ts                                 |    2 
 src/views/mes/qc/oqc/data.ts                                      |    4 
 src/views/crm/customer/data.ts                                    |    2 
 src/views/erp/stock/in/data.ts                                    |    4 
 src/views/wls/productsales/data.ts                                |    2 
 src/views/mes/qc/rqc/data.ts                                      |    2 
 src/views/crm/business/data.ts                                    |    2 
 src/views/mes/dv/checkrecord/data.ts                              |    2 
 src/views/mes/qc/ipqc/data.ts                                     |    2 
 src/views/srm/supplierCertificate/data.ts                         |  153 ++
 src/views/srm/supplierApply/modules/form.vue                      |   84 +
 src/api/srm/supplierCertificate/index.ts                          |   55 
 src/views/srm/supplier/components/select-dialog.vue               |  259 +++
 src/views/wls/outsourcereceipt/data.ts                            |    6 
 src/views/erp/purchase/supplier/data.ts                           |    2 
 src/views/infra/demo/demo03/normal/data.ts                        |    2 
 src/views/erp/purchase/request/modules/order-form.vue             |    2 
 src/views/srm/supplierEvaluation/index.vue                        |  120 +
 src/router/routes/modules/srm.ts                                  |   27 
 src/api/srm/supplierEvaluation/index.ts                           |   47 
 src/views/erp/finance/receipt/data.ts                             |    2 
 src/views/mes/pd/project/modules/task/data.ts                     |    4 
 src/views/mes/workbench/components/tabs/ProductIssueTab.vue       |    2 
 src/views/erp/purchase/statistics/uncompleted.vue                 |    2 
 src/views/wls/productissue/data.ts                                |    2 
 src/views/erp/stock/out/data.ts                                   |    2 
 src/api/srm/statistics/index.ts                                   |   18 
 src/views/wls/miscissue/data.ts                                   |    2 
 src/views/wls/packages/data.ts                                    |    4 
 src/views/wls/stocktaking/plan/data.ts                            |    4 
 src/views/mes/pro/task/data.ts                                    |    6 
 src/views/mes/cal/plan/data.ts                                    |    4 
 src/views/crm/receivable/data.ts                                  |    2 
 src/views/wls/miscreceipt/data.ts                                 |    2 
 src/views/mes/dv/repair/data.ts                                   |    6 
 src/views/mes/workbench/components/tabs/ReturnIssueFormModal.vue  |    2 
 src/views/wls/returnissue/data.ts                                 |    2 
 src/views/srm/supplierApply/data.ts                               |  160 ++
 src/views/mes/pro/andon/record/data.ts                            |    2 
 src/views/crm/followup/data.ts                                    |    2 
 src/views/erp/purchase/return/data.ts                             |    4 
 src/views/erp/sale/order/data.ts                                  |    2 
 src/views/crm/contact/data.ts                                     |    2 
 src/views/wls/itemreceipt/data.ts                                 |    6 
 src/views/srm/supplierEvaluation/modules/form.vue                 |   82 +
 src/views/srm/supplierCertificate/index.vue                       |  141 +
 src/api/srm/supplier/index.ts                                     |   52 
 src/views/srm/supplier/components/select.vue                      |  117 +
 src/views/srm/supplier/index.vue                                  |  120 +
 src/views/erp/sale/out/data.ts                                    |    2 
 src/views/wls/returnvendor/data.ts                                |    2 
 src/views/srm/supplierApply/index.vue                             |  264 +++
 src/views/wms/order/receipt/data.ts                               |    2 
 src/views/mes/tm/tool/data.ts                                     |    2 
 105 files changed, 4,325 insertions(+), 107 deletions(-)

diff --git a/src/adapter/vxe-table.ts b/src/adapter/vxe-table.ts
index 554ae3a..01f7374 100644
--- a/src/adapter/vxe-table.ts
+++ b/src/adapter/vxe-table.ts
@@ -120,11 +120,42 @@
     });
 
     // 琛ㄦ牸閰嶇疆椤瑰彲浠ョ敤 cellRender: { name: 'CellTag' },
+    // props: { data: [{value, color}], labels?: {[value]: label} }
+    // 鏀寔涓ょ鍖归厤妯″紡:
+    //   1. 绮剧‘鍖归厤: data 鍜� labels 涓兘鏄簿纭�� (鐘舵��/绫诲瀷鏋氫妇)
+    //   2. 闃堝�煎尮閰�: data 涓洪檷搴忛槇鍊�, label 鏄剧ず鍘熷鏁板�� (璇勫垎)
     vxeUI.renderer.add('CellTag', {
       renderTableDefault(renderOpts, params) {
         const { props } = renderOpts;
         const { column, row } = params;
-        return h(Tag, { color: props?.color }, () => row[column.field]);
+        const rawValue = row[column.field];
+
+        // 鏌ユ壘鏄剧ず鏂囨湰
+        let label = rawValue;
+        if (props?.labels && rawValue !== undefined && rawValue !== null) {
+          label = props.labels[rawValue] ?? rawValue;
+        }
+
+        // 鏌ユ壘棰滆壊: 浼樺厛绮剧‘鍖归厤锛屽惁鍒欓槇鍊煎尮閰�
+        let color: string | undefined;
+        if (props?.data) {
+          const exact = props.data.find(
+            (item: any) => item.value === rawValue,
+          );
+          if (exact) {
+            color = exact.color;
+          } else {
+            const sorted = [...props.data]
+              .filter((item: any) => typeof item.value === 'number')
+              .sort((a: any, b: any) => b.value - a.value);
+            const threshold = sorted.find(
+              (item: any) => Number(rawValue) >= item.value,
+            );
+            if (threshold) color = threshold.color;
+          }
+        }
+
+        return h(Tag, { color }, () => label);
       },
     });
 
diff --git a/src/api/mdm/item/index.ts b/src/api/mdm/item/index.ts
index 2f40e36..f169a1f 100644
--- a/src/api/mdm/item/index.ts
+++ b/src/api/mdm/item/index.ts
@@ -67,9 +67,14 @@
   });
 }
 
-/** 鏌ヨ鐗╂枡鍒楄〃 */
-export function getItemList(params?: Partial<MdmItemApi.Item>) {
-  return requestClient.get<MdmItemApi.Item[]>('/mdm/item/list', { params });
+/** 鏌ヨ鐗╂枡鍒楄〃锛堟寜ID鍒楄〃锛� */
+export function getItemList(ids: number[]) {
+  return requestClient.get<MdmItemApi.Item[]>('/mdm/item/list-by-ids', { params: { ids: ids.join(',') } });
+}
+
+/** 鑾峰彇鐗╂枡绮剧畝鍒楄〃锛堝惎鐢ㄧ姸鎬侊紝鐢ㄤ簬涓嬫媺閫夋嫨锛� */
+export function getItemSimpleList() {
+  return requestClient.get<MdmItemApi.Item[]>('/mdm/item/simple-list');
 }
 
 /** 鏌ヨ鐗╂枡璇︽儏 */
diff --git a/src/api/srm/statistics/index.ts b/src/api/srm/statistics/index.ts
new file mode 100644
index 0000000..36525c0
--- /dev/null
+++ b/src/api/srm/statistics/index.ts
@@ -0,0 +1,18 @@
+import { requestClient } from '#/api/request';
+
+export namespace SrmStatisticsApi {
+  export interface TenderStatisticsVO {
+    totalTenderCount?: number;
+    totalTenderAmount?: number;
+    totalAwardAmount?: number;
+    savedAmount?: number;
+    winRate?: number;
+    supplierCount?: number;
+    period?: string;
+  }
+}
+
+/** 鑾峰彇鎷涙爣缁熻 */
+export function getTenderStatistics(year: string) {
+  return requestClient.get<SrmStatisticsApi.TenderStatisticsVO>(`/srm/statistics/tender?year=${year}`);
+}
diff --git a/src/api/srm/supplier/index.ts b/src/api/srm/supplier/index.ts
new file mode 100644
index 0000000..27da1c2
--- /dev/null
+++ b/src/api/srm/supplier/index.ts
@@ -0,0 +1,52 @@
+import type { PageParam, PageResult } from '@vben/request';
+
+import { requestClient } from '#/api/request';
+
+export namespace SrmSupplierApi {
+  export interface SupplierVO {
+    id?: number;
+    code: string;
+    name: string;
+    type: number;
+    status: number;
+    contactName?: string;
+    contactMobile?: string;
+    contactEmail?: string;
+    address?: string;
+    bankName?: string;
+    bankAccount?: string;
+    taxNo?: string;
+    remark?: string;
+    createTime?: string;
+  }
+}
+
+/** 鍒嗛〉鏌ヨ渚涘簲鍟� */
+export function getSupplierPage(params: PageParam) {
+  return requestClient.get<PageResult<SrmSupplierApi.SupplierVO>>('/srm/supplier/page', { params });
+}
+
+/** 鑾峰彇渚涘簲鍟嗙簿绠�鍒楄〃锛堜粎杩斿洖宸查�氳繃鍑嗗叆瀹℃壒鐨勪緵搴斿晢锛� */
+export function getSupplierSimpleList() {
+  return requestClient.get<SrmSupplierApi.SupplierVO[]>('/srm/supplier/simple-list');
+}
+
+/** 鑾峰彇渚涘簲鍟嗚鎯� */
+export function getSupplier(id: number) {
+  return requestClient.get<SrmSupplierApi.SupplierVO>(`/srm/supplier/get?id=${id}`);
+}
+
+/** 鍒涘缓渚涘簲鍟� */
+export function createSupplier(data: SrmSupplierApi.SupplierVO) {
+  return requestClient.post('/srm/supplier/create', data);
+}
+
+/** 鏇存柊渚涘簲鍟� */
+export function updateSupplier(data: SrmSupplierApi.SupplierVO) {
+  return requestClient.put('/srm/supplier/update', data);
+}
+
+/** 鍒犻櫎渚涘簲鍟� */
+export function deleteSupplier(id: number) {
+  return requestClient.delete(`/srm/supplier/delete?id=${id}`);
+}
diff --git a/src/api/srm/supplierApply/index.ts b/src/api/srm/supplierApply/index.ts
new file mode 100644
index 0000000..35a48fa
--- /dev/null
+++ b/src/api/srm/supplierApply/index.ts
@@ -0,0 +1,78 @@
+import type { PageParam, PageResult } from '@vben/request';
+
+import { requestClient } from '#/api/request';
+
+export namespace SrmSupplierApplyApi {
+  export interface ApplyVO {
+    id?: number;
+    applyNo?: string;
+    supplierId?: number;
+    supplierName: string;
+    contactName?: string;
+    contactMobile?: string;
+    contactEmail?: string;
+    address?: string;
+    bankName?: string;
+    bankAccount?: string;
+    taxNo?: string;
+    businessLicense?: string;
+    taxCertificate?: string;
+    orgCodeCertificate?: string;
+    applyStatus?: number;
+    applyStatusLabel?: string;
+    auditComment?: string;
+    remark?: string;
+    createTime?: string;
+  }
+
+  export interface ReviewVO {
+    id: number;
+    passed: boolean;
+    comment?: string;
+  }
+}
+
+/** 鍒嗛〉鏌ヨ鍑嗗叆鐢宠 */
+export function getApplyPage(params: PageParam) {
+  return requestClient.get<PageResult<SrmSupplierApplyApi.ApplyVO>>('/srm/supplier-apply/page', { params });
+}
+
+/** 鑾峰彇鍑嗗叆鐢宠璇︽儏 */
+export function getApply(id: number) {
+  return requestClient.get<SrmSupplierApplyApi.ApplyVO>(`/srm/supplier-apply/get?id=${id}`);
+}
+
+/** 鍒涘缓鍑嗗叆鐢宠 */
+export function createApply(data: SrmSupplierApplyApi.ApplyVO) {
+  return requestClient.post('/srm/supplier-apply/create', data);
+}
+
+/** 鏇存柊鍑嗗叆鐢宠 */
+export function updateApply(data: SrmSupplierApplyApi.ApplyVO) {
+  return requestClient.put('/srm/supplier-apply/update', data);
+}
+
+/** 鍒犻櫎鍑嗗叆鐢宠 */
+export function deleteApply(id: number) {
+  return requestClient.delete(`/srm/supplier-apply/delete?id=${id}`);
+}
+
+/** 鎻愪氦瀹℃牳 */
+export function submitApply(id: number) {
+  return requestClient.put(`/srm/supplier-apply/submit?id=${id}`);
+}
+
+/** 閲囪喘瀹℃牳 */
+export function reviewPurchase(data: SrmSupplierApplyApi.ReviewVO) {
+  return requestClient.put('/srm/supplier-apply/review-purchase', data);
+}
+
+/** 璐ㄩ噺瀹℃牳 */
+export function reviewQuality(data: SrmSupplierApplyApi.ReviewVO) {
+  return requestClient.put('/srm/supplier-apply/review-quality', data);
+}
+
+/** 璐㈠姟瀹℃牳 */
+export function reviewFinance(data: SrmSupplierApplyApi.ReviewVO) {
+  return requestClient.put('/srm/supplier-apply/review-finance', data);
+}
diff --git a/src/api/srm/supplierCertificate/index.ts b/src/api/srm/supplierCertificate/index.ts
new file mode 100644
index 0000000..3b775cf
--- /dev/null
+++ b/src/api/srm/supplierCertificate/index.ts
@@ -0,0 +1,55 @@
+import type { PageParam, PageResult } from '@vben/request';
+
+import { requestClient } from '#/api/request';
+
+export namespace SrmSupplierCertificateApi {
+  export interface CertificateVO {
+    id?: number;
+    supplierId: number;
+    supplierName?: string;
+    certificateName: string;
+    certificateNo?: string;
+    certificateType: number;
+    issuingAuthority?: string;
+    issueDate: string;
+    expireDate: string;
+    expireStatus?: number;
+    remark?: string;
+    createTime?: string;
+  }
+}
+
+/** 鍒嗛〉鏌ヨ璧勮川 */
+export function getCertificatePage(params: PageParam) {
+  return requestClient.get<PageResult<SrmSupplierCertificateApi.CertificateVO>>('/srm/supplier-certificate/page', { params });
+}
+
+/** 鑾峰彇璧勮川璇︽儏 */
+export function getCertificate(id: number) {
+  return requestClient.get<SrmSupplierCertificateApi.CertificateVO>(`/srm/supplier-certificate/get?id=${id}`);
+}
+
+/** 鍒涘缓璧勮川 */
+export function createCertificate(data: SrmSupplierCertificateApi.CertificateVO) {
+  return requestClient.post('/srm/supplier-certificate/create', data);
+}
+
+/** 鏇存柊璧勮川 */
+export function updateCertificate(data: SrmSupplierCertificateApi.CertificateVO) {
+  return requestClient.put('/srm/supplier-certificate/update', data);
+}
+
+/** 鍒犻櫎璧勮川 */
+export function deleteCertificate(id: number) {
+  return requestClient.delete(`/srm/supplier-certificate/delete?id=${id}`);
+}
+
+/** 鎸変緵搴斿晢鑾峰彇璧勮川鍒楄〃 */
+export function getCertificateListBySupplier(supplierId: number) {
+  return requestClient.get<SrmSupplierCertificateApi.CertificateVO[]>(`/srm/supplier-certificate/list-by-supplier?supplierId=${supplierId}`);
+}
+
+/** 鑾峰彇鍗冲皢鍒版湡鐨勮祫璐ㄥ垪琛� */
+export function getExpiringCertificates() {
+  return requestClient.get<SrmSupplierCertificateApi.CertificateVO[]>('/srm/supplier-certificate/expiring-list');
+}
diff --git a/src/api/srm/supplierEvaluation/index.ts b/src/api/srm/supplierEvaluation/index.ts
new file mode 100644
index 0000000..c7ed017
--- /dev/null
+++ b/src/api/srm/supplierEvaluation/index.ts
@@ -0,0 +1,47 @@
+import type { PageParam, PageResult } from '@vben/request';
+
+import { requestClient } from '#/api/request';
+
+export namespace SrmSupplierEvaluationApi {
+  export interface EvaluationVO {
+    id?: number;
+    supplierId: number;
+    supplierName?: string;
+    evaluationPeriod: string;
+    qualityScore: number;
+    deliveryScore: number;
+    priceScore: number;
+    serviceScore: number;
+    compositeScore?: number;
+    grade?: string;
+    evaluatorId?: number;
+    evaluatorName?: string;
+    remark?: string;
+    createTime?: string;
+  }
+}
+
+/** 鍒嗛〉鏌ヨ缁╂晥璇勪环 */
+export function getEvaluationPage(params: PageParam) {
+  return requestClient.get<PageResult<SrmSupplierEvaluationApi.EvaluationVO>>('/srm/supplier-evaluation/page', { params });
+}
+
+/** 鑾峰彇缁╂晥璇勪环璇︽儏 */
+export function getEvaluation(id: number) {
+  return requestClient.get<SrmSupplierEvaluationApi.EvaluationVO>(`/srm/supplier-evaluation/get?id=${id}`);
+}
+
+/** 鍒涘缓缁╂晥璇勪环 */
+export function createEvaluation(data: SrmSupplierEvaluationApi.EvaluationVO) {
+  return requestClient.post('/srm/supplier-evaluation/create', data);
+}
+
+/** 鏇存柊缁╂晥璇勪环 */
+export function updateEvaluation(data: SrmSupplierEvaluationApi.EvaluationVO) {
+  return requestClient.put('/srm/supplier-evaluation/update', data);
+}
+
+/** 鍒犻櫎缁╂晥璇勪环 */
+export function deleteEvaluation(id: number) {
+  return requestClient.delete(`/srm/supplier-evaluation/delete?id=${id}`);
+}
diff --git a/src/api/srm/tender/index.ts b/src/api/srm/tender/index.ts
new file mode 100644
index 0000000..965eba4
--- /dev/null
+++ b/src/api/srm/tender/index.ts
@@ -0,0 +1,232 @@
+import type { PageParam, PageResult } from '@vben/request';
+
+import { requestClient } from '#/api/request';
+
+export namespace SrmTenderApi {
+  export interface TenderProjectVO {
+    id?: number;
+    tenderNo?: string;
+    tenderName: string;
+    tenderType: number;
+    purchaseOrg?: string;
+    projectLeaderId?: number;
+    projectLeaderName?: string;
+    tenderStatus?: number;
+    budgetAmount?: number;
+    startTime?: string;
+    endTime?: string;
+    bidOpenTime?: string;
+    tenderDesc?: string;
+    qualificationRequirements?: string;
+    purchaseRequestId?: number;
+    awardId?: number;
+    purchaseOrderNo?: string;
+    createTime?: string;
+  }
+
+  export interface TenderMaterialVO {
+    id?: number;
+    tenderProjectId: number;
+    productId?: number;
+    productCode?: string;
+    productName: string;
+    productSpec?: string;
+    unit?: string;
+    quantity?: number;
+    techRequirement?: string;
+    estimatedPrice?: number;
+    sort?: number;
+  }
+
+  export interface TenderBidVO {
+    id?: number;
+    tenderProjectId: number;
+    supplierId: number;
+    supplierName?: string;
+    bidNo: string;
+    bidStatus?: number;
+    bidTime?: string;
+    bidTotalAmount?: number;
+    bidFileName?: string;
+    bidFileUrl?: string;
+    createTime?: string;
+  }
+
+  export interface QuoteVO {
+    id?: number;
+    tenderProjectId?: number;
+    supplierId?: number;
+    bidId: number;
+    tenderMaterialId: number;
+    materialName?: string;
+    quotePrice?: number;
+    taxRate?: number;
+    deliveryCycle?: number;
+    paymentTerms?: string;
+    warrantyPeriod?: number;
+    remark?: string;
+  }
+
+  export interface BidEvaluationVO {
+    id?: number;
+    tenderProjectId: number;
+    supplierId: number;
+    supplierName?: string;
+    bidId: number;
+    priceScore?: number;
+    techScore?: number;
+    deliveryScore?: number;
+    serviceScore?: number;
+    compositeScore?: number;
+    rank?: number;
+    evaluatorId?: number;
+    evaluatorName?: string;
+    evaluationOpinion?: string;
+  }
+
+  export interface BidAwardVO {
+    id?: number;
+    tenderProjectId: number;
+    supplierId: number;
+    supplierName?: string;
+    bidId: number;
+    awardNo: string;
+    awardStatus?: number;
+    awardAmount?: number;
+    awardTime?: string;
+    noticeFileName?: string;
+    noticeFileUrl?: string;
+    purchaseOrderId?: number;
+    remark?: string;
+  }
+}
+
+// ========== 鎷涙爣椤圭洰 ==========
+
+export function getTenderProjectPage(params: PageParam) {
+  return requestClient.get<PageResult<SrmTenderApi.TenderProjectVO>>('/srm/tender-project/page', { params });
+}
+
+export function getTenderProject(id: number) {
+  return requestClient.get<SrmTenderApi.TenderProjectVO>(`/srm/tender-project/get?id=${id}`);
+}
+
+export function createTenderProject(data: SrmTenderApi.TenderProjectVO) {
+  return requestClient.post('/srm/tender-project/create', data);
+}
+
+export function updateTenderProject(data: SrmTenderApi.TenderProjectVO) {
+  return requestClient.put('/srm/tender-project/update', data);
+}
+
+export function deleteTenderProject(id: number) {
+  return requestClient.delete(`/srm/tender-project/delete?id=${id}`);
+}
+
+export function publishTender(id: number) {
+  return requestClient.put(`/srm/tender-project/publish?id=${id}`);
+}
+
+export function closeTender(id: number) {
+  return requestClient.put(`/srm/tender-project/close?id=${id}`);
+}
+
+export function startBidding(id: number) {
+  return requestClient.put(`/srm/tender-project/start-bidding?id=${id}`);
+}
+
+export function startEvaluation(id: number) {
+  return requestClient.put(`/srm/tender-project/start-evaluation?id=${id}`);
+}
+
+// ========== 鐗╂枡瀛愯〃 ==========
+
+export function getMaterialList(tenderProjectId: number) {
+  return requestClient.get<SrmTenderApi.TenderMaterialVO[]>(`/srm/tender-project/material/list?tenderProjectId=${tenderProjectId}`);
+}
+
+export function createMaterial(data: SrmTenderApi.TenderMaterialVO) {
+  return requestClient.post('/srm/tender-project/material/create', data);
+}
+
+export function updateMaterial(data: SrmTenderApi.TenderMaterialVO) {
+  return requestClient.put('/srm/tender-project/material/update', data);
+}
+
+export function deleteMaterial(id: number) {
+  return requestClient.delete(`/srm/tender-project/material/delete?id=${id}`);
+}
+
+// ========== 鎶曟爣绠$悊 ==========
+
+export function getBidListByProject(tenderProjectId: number) {
+  return requestClient.get<SrmTenderApi.TenderBidVO[]>(`/srm/tender-bid/list-by-project?tenderProjectId=${tenderProjectId}`);
+}
+
+export function getBid(id: number) {
+  return requestClient.get<SrmTenderApi.TenderBidVO>(`/srm/tender-bid/get?id=${id}`);
+}
+
+export function createBid(data: SrmTenderApi.TenderBidVO) {
+  return requestClient.post('/srm/tender-bid/create', data);
+}
+
+export function withdrawBid(id: number) {
+  return requestClient.put(`/srm/tender-bid/withdraw?id=${id}`);
+}
+
+// ========== 鎶ヤ环绠$悊 ==========
+
+export function getQuoteList(bidId: number) {
+  return requestClient.get<SrmTenderApi.QuoteVO[]>(`/srm/tender-bid/quote/list-by-bid?bidId=${bidId}`);
+}
+
+export function createQuote(data: SrmTenderApi.QuoteVO) {
+  return requestClient.post('/srm/tender-bid/quote/create', data);
+}
+
+export function updateQuote(data: SrmTenderApi.QuoteVO) {
+  return requestClient.put('/srm/tender-bid/quote/update', data);
+}
+
+// ========== 璇勬爣绠$悊 ==========
+
+export function getEvaluationList(tenderProjectId: number) {
+  return requestClient.get<SrmTenderApi.BidEvaluationVO[]>(`/srm/bid-evaluation/list-by-project?tenderProjectId=${tenderProjectId}`);
+}
+
+export function createBidEvaluation(data: SrmTenderApi.BidEvaluationVO) {
+  return requestClient.post('/srm/bid-evaluation/create', data);
+}
+
+export function calculateRanking(tenderProjectId: number) {
+  return requestClient.put(`/srm/bid-evaluation/calculate-ranking?tenderProjectId=${tenderProjectId}`);
+}
+
+// ========== 寮�鏍� ==========
+
+export function createBidOpen(tenderProjectId: number) {
+  return requestClient.post(`/srm/bid-evaluation/bid-open/create?tenderProjectId=${tenderProjectId}`);
+}
+
+export function getBidOpen(id: number) {
+  return requestClient.get(`/srm/bid-evaluation/bid-open/get?id=${id}`);
+}
+
+// ========== 瀹氭爣绠$悊 ==========
+
+export function createAward(data: SrmTenderApi.BidAwardVO) {
+  return requestClient.post('/srm/bid-award/create', data);
+}
+
+export function getAward(id: number) {
+  return requestClient.get<SrmTenderApi.BidAwardVO>(`/srm/bid-award/get?id=${id}`);
+}
+
+export function approveAward(id: number) {
+  return requestClient.put(`/srm/bid-award/approve?id=${id}`);
+}
+
+export function generatePurchaseOrder(id: number) {
+  return requestClient.put(`/srm/bid-award/generate-purchase-order/${id}`);
+}
diff --git a/src/router/routes/modules/srm.ts b/src/router/routes/modules/srm.ts
new file mode 100644
index 0000000..113b30d
--- /dev/null
+++ b/src/router/routes/modules/srm.ts
@@ -0,0 +1,27 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+const routes: RouteRecordRaw[] = [
+  {
+    path: '/srm',
+    name: 'SrmCenter',
+    meta: {
+      title: 'SRM 渚涘簲鍟嗙鐞�',
+      icon: 'lucide:package-search',
+      keepAlive: true,
+      hideInMenu: true,
+    },
+    children: [
+      {
+        path: 'tender/detail',
+        name: 'SrmTenderDetail',
+        meta: {
+          title: '鎷涙爣璇︽儏',
+          activePath: '/srm/tender',
+        },
+        component: () => import('#/views/srm/tender/detail.vue'),
+      },
+    ],
+  },
+];
+
+export default routes;
diff --git a/src/views/bpm/oa/leave/data.ts b/src/views/bpm/oa/leave/data.ts
index 9570f42..bff2476 100644
--- a/src/views/bpm/oa/leave/data.ts
+++ b/src/views/bpm/oa/leave/data.ts
@@ -41,7 +41,7 @@
       componentProps: {
         placeholder: '璇烽�夋嫨寮�濮嬫椂闂�',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
         format: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
@@ -53,7 +53,7 @@
       componentProps: {
         placeholder: '璇烽�夋嫨缁撴潫鏃堕棿',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
         format: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
diff --git a/src/views/crm/business/data.ts b/src/views/crm/business/data.ts
index 2b07d09..d1c9a3e 100644
--- a/src/views/crm/business/data.ts
+++ b/src/views/crm/business/data.ts
@@ -98,7 +98,7 @@
       componentProps: {
         showTime: false,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨棰勮鎴愪氦鏃ユ湡',
       },
     },
diff --git a/src/views/crm/clue/data.ts b/src/views/crm/clue/data.ts
index 018c079..9973372 100644
--- a/src/views/crm/clue/data.ts
+++ b/src/views/crm/clue/data.ts
@@ -144,7 +144,7 @@
       componentProps: {
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨涓嬫鑱旂郴鏃堕棿',
       },
     },
diff --git a/src/views/crm/contact/data.ts b/src/views/crm/contact/data.ts
index a95a655..331135d 100644
--- a/src/views/crm/contact/data.ts
+++ b/src/views/crm/contact/data.ts
@@ -165,7 +165,7 @@
       componentProps: {
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨涓嬫鑱旂郴鏃堕棿',
       },
     },
diff --git a/src/views/crm/contract/data.ts b/src/views/crm/contract/data.ts
index 5ea024f..2903856 100644
--- a/src/views/crm/contract/data.ts
+++ b/src/views/crm/contract/data.ts
@@ -79,7 +79,7 @@
       componentProps: {
         showTime: false,
         format: 'YYYY-MM-DD',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨涓嬪崟鏃ユ湡',
       },
     },
@@ -128,7 +128,7 @@
       componentProps: {
         showTime: false,
         format: 'YYYY-MM-DD',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍚堝悓寮�濮嬫椂闂�',
       },
     },
@@ -139,7 +139,7 @@
       componentProps: {
         showTime: false,
         format: 'YYYY-MM-DD',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍚堝悓缁撴潫鏃堕棿',
       },
     },
diff --git a/src/views/crm/customer/data.ts b/src/views/crm/customer/data.ts
index 199eac0..d3372ee 100644
--- a/src/views/crm/customer/data.ts
+++ b/src/views/crm/customer/data.ts
@@ -154,7 +154,7 @@
       componentProps: {
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨涓嬫鑱旂郴鏃堕棿',
         allowClear: true,
       },
diff --git a/src/views/crm/followup/data.ts b/src/views/crm/followup/data.ts
index 9ff3914..7132d4b 100644
--- a/src/views/crm/followup/data.ts
+++ b/src/views/crm/followup/data.ts
@@ -49,7 +49,7 @@
       componentProps: {
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨涓嬫鑱旂郴鏃堕棿',
       },
       rules: 'required',
diff --git a/src/views/crm/receivable/data.ts b/src/views/crm/receivable/data.ts
index e1e0255..300d02b 100644
--- a/src/views/crm/receivable/data.ts
+++ b/src/views/crm/receivable/data.ts
@@ -183,7 +183,7 @@
       componentProps: {
         placeholder: '璇烽�夋嫨鍥炴鏃ユ湡',
         showTime: false,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
         format: 'YYYY-MM-DD',
       },
     },
diff --git a/src/views/crm/receivable/plan/data.ts b/src/views/crm/receivable/plan/data.ts
index 6527ce2..cfd71c4 100644
--- a/src/views/crm/receivable/plan/data.ts
+++ b/src/views/crm/receivable/plan/data.ts
@@ -109,7 +109,7 @@
       componentProps: {
         placeholder: '璇烽�夋嫨璁″垝鍥炴鏃ユ湡',
         showTime: false,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
         format: 'YYYY-MM-DD',
       },
       defaultValue: new Date(),
diff --git a/src/views/crm/saleQuotation/data.ts b/src/views/crm/saleQuotation/data.ts
index ce713e1..ab372f0 100644
--- a/src/views/crm/saleQuotation/data.ts
+++ b/src/views/crm/saleQuotation/data.ts
@@ -128,7 +128,7 @@
       componentProps: {
         showTime: false,
         format: 'YYYY-MM-DD',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鎶ヤ环鏃ユ湡',
       },
     },
@@ -139,7 +139,7 @@
       componentProps: {
         showTime: false,
         format: 'YYYY-MM-DD',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鏈夋晥鏈�',
       },
     },
diff --git a/src/views/erp/finance/payment/data.ts b/src/views/erp/finance/payment/data.ts
index 3d04084..22aa220 100644
--- a/src/views/erp/finance/payment/data.ts
+++ b/src/views/erp/finance/payment/data.ts
@@ -6,7 +6,7 @@
 import { erpPriceInputFormatter } from '@vben/utils';
 
 import { getAccountSimpleList } from '#/api/erp/finance/account';
-import { getSupplierSimpleList } from '#/api/erp/purchase/supplier';
+import { getSupplierSimpleList } from '#/api/srm/supplier';
 import { getSimpleUserList } from '#/api/system/user';
 import { getRangePickerDefaultProps } from '#/utils';
 
@@ -42,7 +42,7 @@
         placeholder: '閫夋嫨浠樻鏃堕棿',
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/erp/finance/receipt/data.ts b/src/views/erp/finance/receipt/data.ts
index 87e7ea4..a81b7c0 100644
--- a/src/views/erp/finance/receipt/data.ts
+++ b/src/views/erp/finance/receipt/data.ts
@@ -42,7 +42,7 @@
         placeholder: '閫夋嫨鏀舵鏃堕棿',
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/erp/purchase/in/data.ts b/src/views/erp/purchase/in/data.ts
index b379d63..ef9d96b 100644
--- a/src/views/erp/purchase/in/data.ts
+++ b/src/views/erp/purchase/in/data.ts
@@ -8,7 +8,7 @@
 import { z } from '#/adapter/form';
 import { getAccountSimpleList } from '#/api/erp/finance/account';
 import { getProductSimpleList } from '#/api/erp/product/product';
-import { getSupplierSimpleList } from '#/api/erp/purchase/supplier';
+import { getSupplierSimpleList } from '#/api/srm/supplier';
 import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse';
 import { getSimpleUserList } from '#/api/system/user';
 import { getRangePickerDefaultProps } from '#/utils';
@@ -45,7 +45,7 @@
         placeholder: '閫夋嫨鍏ュ簱鏃堕棿',
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/erp/purchase/order/data.ts b/src/views/erp/purchase/order/data.ts
index 0aa5868..19ccc26 100644
--- a/src/views/erp/purchase/order/data.ts
+++ b/src/views/erp/purchase/order/data.ts
@@ -9,7 +9,7 @@
 
 import { z } from '#/adapter/form';
 import { getAccountSimpleList } from '#/api/erp/finance/account';
-import { getSupplierSimpleList } from '#/api/erp/purchase/supplier';
+import { getSupplierSimpleList } from '#/api/srm/supplier';
 import { getSimpleUserList } from '#/api/system/user';
 import { MdmItemSelect } from '#/views/basicData/mdm/components';
 import { getRangePickerDefaultProps } from '#/utils';
@@ -45,7 +45,7 @@
         placeholder: '閫夋嫨璁㈠崟鏃堕棿',
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/erp/purchase/request/data.ts b/src/views/erp/purchase/request/data.ts
index 58f9035..59cc3c5 100644
--- a/src/views/erp/purchase/request/data.ts
+++ b/src/views/erp/purchase/request/data.ts
@@ -4,7 +4,7 @@
 import { DICT_TYPE } from '@vben/constants';
 import { getDictOptions } from '@vben/hooks';
 
-import { getSupplierSimpleList } from '#/api/erp/purchase/supplier';
+import { getSupplierSimpleList } from '#/api/srm/supplier';
 import { getSimpleUserList } from '#/api/system/user';
 import { getRangePickerDefaultProps } from '#/utils';
 
@@ -48,7 +48,7 @@
         placeholder: '閫夋嫨鐢宠鏃堕棿',
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
         disabled: formType === 'detail',
       },
       rules: 'required',
diff --git a/src/views/erp/purchase/request/modules/order-form.vue b/src/views/erp/purchase/request/modules/order-form.vue
index 3878b6e..4fd325c 100644
--- a/src/views/erp/purchase/request/modules/order-form.vue
+++ b/src/views/erp/purchase/request/modules/order-form.vue
@@ -8,7 +8,7 @@
 import { message, Select } from 'ant-design-vue';
 
 import { generatePurchaseOrder } from '#/api/erp/purchase/request';
-import { getSupplierSimpleList } from '#/api/erp/purchase/supplier';
+import { getSupplierSimpleList } from '#/api/srm/supplier';
 
 const emit = defineEmits(['success']);
 
diff --git a/src/views/erp/purchase/return/data.ts b/src/views/erp/purchase/return/data.ts
index fdbcf2e..5bb8b24 100644
--- a/src/views/erp/purchase/return/data.ts
+++ b/src/views/erp/purchase/return/data.ts
@@ -8,7 +8,7 @@
 import { z } from '#/adapter/form';
 import { getAccountSimpleList } from '#/api/erp/finance/account';
 import { getProductSimpleList } from '#/api/erp/product/product';
-import { getSupplierSimpleList } from '#/api/erp/purchase/supplier';
+import { getSupplierSimpleList } from '#/api/srm/supplier';
 import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse';
 import { getSimpleUserList } from '#/api/system/user';
 import { getRangePickerDefaultProps } from '#/utils';
@@ -45,7 +45,7 @@
         placeholder: '閫夋嫨閫�璐ф椂闂�',
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/erp/purchase/statistics/supplier.vue b/src/views/erp/purchase/statistics/supplier.vue
index e6ffc4d..bb8e75d 100644
--- a/src/views/erp/purchase/statistics/supplier.vue
+++ b/src/views/erp/purchase/statistics/supplier.vue
@@ -6,7 +6,7 @@
 
 import { useVbenVxeGrid } from '#/adapter/vxe-table';
 import { getSupplierStatistics } from '#/api/erp/purchase/statistics';
-import { getSupplierSimpleList } from '#/api/erp/purchase/supplier';
+import { getSupplierSimpleList } from '#/api/srm/supplier';
 import { getRangePickerDefaultProps } from '#/utils';
 
 import type { VbenFormSchema } from '#/adapter/form';
diff --git a/src/views/erp/purchase/statistics/uncompleted.vue b/src/views/erp/purchase/statistics/uncompleted.vue
index 8634cf4..07e6d1c 100644
--- a/src/views/erp/purchase/statistics/uncompleted.vue
+++ b/src/views/erp/purchase/statistics/uncompleted.vue
@@ -8,7 +8,7 @@
 
 import { useVbenVxeGrid } from '#/adapter/vxe-table';
 import { getUncompletedOrders } from '#/api/erp/purchase/statistics';
-import { getSupplierSimpleList } from '#/api/erp/purchase/supplier';
+import { getSupplierSimpleList } from '#/api/srm/supplier';
 import { getRangePickerDefaultProps } from '#/utils';
 
 import type { VbenFormSchema } from '#/adapter/form';
diff --git a/src/views/erp/purchase/supplier/data.ts b/src/views/erp/purchase/supplier/data.ts
index 490415c..ea96133 100644
--- a/src/views/erp/purchase/supplier/data.ts
+++ b/src/views/erp/purchase/supplier/data.ts
@@ -89,7 +89,7 @@
       componentProps: {
         placeholder: '璇烽�夋嫨鍚堜綔寮�濮嬫椂闂�',
         class: '!w-full',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
     },
     {
diff --git a/src/views/erp/sale/order/data.ts b/src/views/erp/sale/order/data.ts
index 254f22a..7886d14 100644
--- a/src/views/erp/sale/order/data.ts
+++ b/src/views/erp/sale/order/data.ts
@@ -72,7 +72,7 @@
         placeholder: '閫夋嫨璁㈠崟鏃堕棿',
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/erp/sale/out/data.ts b/src/views/erp/sale/out/data.ts
index 15f309e..298eed3 100644
--- a/src/views/erp/sale/out/data.ts
+++ b/src/views/erp/sale/out/data.ts
@@ -45,7 +45,7 @@
         placeholder: '閫夋嫨鍑哄簱鏃堕棿',
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/erp/sale/return/data.ts b/src/views/erp/sale/return/data.ts
index bb92f50..68e9e22 100644
--- a/src/views/erp/sale/return/data.ts
+++ b/src/views/erp/sale/return/data.ts
@@ -45,7 +45,7 @@
         placeholder: '閫夋嫨閫�璐ф椂闂�',
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/erp/stock/check/data.ts b/src/views/erp/stock/check/data.ts
index d58c9f5..e65fce8 100644
--- a/src/views/erp/stock/check/data.ts
+++ b/src/views/erp/stock/check/data.ts
@@ -40,7 +40,7 @@
         placeholder: '閫夋嫨鐩樼偣鏃堕棿',
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/erp/stock/in/data.ts b/src/views/erp/stock/in/data.ts
index 85e8e81..353ec6c 100644
--- a/src/views/erp/stock/in/data.ts
+++ b/src/views/erp/stock/in/data.ts
@@ -5,7 +5,7 @@
 import { getDictOptions } from '@vben/hooks';
 
 import { getProductSimpleList } from '#/api/erp/product/product';
-import { getSupplierSimpleList } from '#/api/erp/purchase/supplier';
+import { getSupplierSimpleList } from '#/api/srm/supplier';
 import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse';
 import { getSimpleUserList } from '#/api/system/user';
 import { getRangePickerDefaultProps } from '#/utils';
@@ -41,7 +41,7 @@
         placeholder: '閫夋嫨鍏ュ簱鏃堕棿',
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/erp/stock/move/data.ts b/src/views/erp/stock/move/data.ts
index 0baf242..4a0bac9 100644
--- a/src/views/erp/stock/move/data.ts
+++ b/src/views/erp/stock/move/data.ts
@@ -40,7 +40,7 @@
         placeholder: '閫夋嫨璋冨害鏃堕棿',
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/erp/stock/out/data.ts b/src/views/erp/stock/out/data.ts
index c764bff..524e3a6 100644
--- a/src/views/erp/stock/out/data.ts
+++ b/src/views/erp/stock/out/data.ts
@@ -41,7 +41,7 @@
         placeholder: '閫夋嫨鍑哄簱鏃堕棿',
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/infra/demo/demo01/data.ts b/src/views/infra/demo/demo01/data.ts
index 3656a96..5c22e14 100644
--- a/src/views/infra/demo/demo01/data.ts
+++ b/src/views/infra/demo/demo01/data.ts
@@ -46,7 +46,7 @@
       componentProps: {
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨鍑虹敓骞�',
       },
     },
diff --git a/src/views/infra/demo/demo03/erp/data.ts b/src/views/infra/demo/demo03/erp/data.ts
index f33d20c..eb17117 100644
--- a/src/views/infra/demo/demo03/erp/data.ts
+++ b/src/views/infra/demo/demo03/erp/data.ts
@@ -46,7 +46,7 @@
       componentProps: {
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨鍑虹敓鏃ユ湡',
       },
     },
diff --git a/src/views/infra/demo/demo03/inner/data.ts b/src/views/infra/demo/demo03/inner/data.ts
index 0166ba1..1c183ab 100644
--- a/src/views/infra/demo/demo03/inner/data.ts
+++ b/src/views/infra/demo/demo03/inner/data.ts
@@ -46,7 +46,7 @@
       componentProps: {
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
     },
     {
diff --git a/src/views/infra/demo/demo03/normal/data.ts b/src/views/infra/demo/demo03/normal/data.ts
index aaf2e53..7995289 100644
--- a/src/views/infra/demo/demo03/normal/data.ts
+++ b/src/views/infra/demo/demo03/normal/data.ts
@@ -46,7 +46,7 @@
       componentProps: {
         showTime: true,
         format: 'YYYY-MM-DD HH:mm:ss',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨鍑虹敓鏃ユ湡',
       },
     },
diff --git a/src/views/mes/cal/plan/data.ts b/src/views/mes/cal/plan/data.ts
index 7127744..1d98d19 100644
--- a/src/views/mes/cal/plan/data.ts
+++ b/src/views/mes/cal/plan/data.ts
@@ -97,7 +97,7 @@
       componentProps: {
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨寮�濮嬫棩鏈�',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
@@ -108,7 +108,7 @@
       componentProps: {
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨缁撴潫鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
diff --git a/src/views/mes/dv/checkplan/data.ts b/src/views/mes/dv/checkplan/data.ts
index 5eee160..40b6629 100644
--- a/src/views/mes/dv/checkplan/data.ts
+++ b/src/views/mes/dv/checkplan/data.ts
@@ -97,7 +97,7 @@
       componentProps: {
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨寮�濮嬫棩鏈�',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
@@ -108,7 +108,7 @@
       componentProps: {
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨缁撴潫鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
diff --git a/src/views/mes/dv/checkrecord/data.ts b/src/views/mes/dv/checkrecord/data.ts
index 7152a5e..dc71a89 100644
--- a/src/views/mes/dv/checkrecord/data.ts
+++ b/src/views/mes/dv/checkrecord/data.ts
@@ -79,7 +79,7 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨鐐规鏃堕棿',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/mes/dv/machinery/data.ts b/src/views/mes/dv/machinery/data.ts
index 4dbb6a0..ef002c5 100644
--- a/src/views/mes/dv/machinery/data.ts
+++ b/src/views/mes/dv/machinery/data.ts
@@ -129,7 +129,7 @@
         disabled: true,
         format: 'YYYY-MM-DD HH:mm:ss',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       dependencies: {
         triggerFields: ['id'],
@@ -145,7 +145,7 @@
         disabled: true,
         format: 'YYYY-MM-DD HH:mm:ss',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       dependencies: {
         triggerFields: ['id'],
diff --git a/src/views/mes/dv/maintenrecord/data.ts b/src/views/mes/dv/maintenrecord/data.ts
index 8e16b83..d7359d6 100644
--- a/src/views/mes/dv/maintenrecord/data.ts
+++ b/src/views/mes/dv/maintenrecord/data.ts
@@ -79,7 +79,7 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨淇濆吇鏃堕棿',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/mes/dv/repair/data.ts b/src/views/mes/dv/repair/data.ts
index 9506cd2..6d9e47b 100644
--- a/src/views/mes/dv/repair/data.ts
+++ b/src/views/mes/dv/repair/data.ts
@@ -109,7 +109,7 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨鎶ヤ慨鏃ユ湡',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
@@ -141,7 +141,7 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨瀹屾垚鏃ユ湡',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       // 缁翠慨涓�(鈮ONFIRMED)鎬佸睍绀猴紱浠�"瀹屾垚缁翠慨"寮圭獥鍙紪杈戝苟蹇呭~锛屽叾浣欐�佸彧璇诲洖鏄�
       dependencies: {
@@ -182,7 +182,7 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨楠屾敹鏃ユ湡',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       // 楠屾敹淇℃伅涓哄凡纭(鈮INISHED)鎬佽嚜鍔ㄤ骇鐢熺殑鍙鍥炴樉瀛楁
       dependencies: {
diff --git a/src/views/mes/pd/project/data.ts b/src/views/mes/pd/project/data.ts
index 9c1d3e1..3ac91aa 100644
--- a/src/views/mes/pd/project/data.ts
+++ b/src/views/mes/pd/project/data.ts
@@ -36,7 +36,7 @@
       document.body,
     inputReadOnly: true,
     placeholder: options?.placeholder ?? "璇烽�夋嫨鏃ユ湡",
-    valueFormat: "x",
+    valueFormat: 'YYYY-MM-DD',
   };
 }
 
diff --git a/src/views/mes/pd/project/modules/task/data.ts b/src/views/mes/pd/project/modules/task/data.ts
index f7d551a..b319a71 100644
--- a/src/views/mes/pd/project/modules/task/data.ts
+++ b/src/views/mes/pd/project/modules/task/data.ts
@@ -106,7 +106,7 @@
       fieldName: 'planFinishDate',
       label: '璁″垝瀹屾垚',
       component: 'DatePicker',
-      componentProps: { disabled: readonly, class: 'w-full', valueFormat: 'x' },
+      componentProps: { disabled: readonly, class: 'w-full', valueFormat: 'YYYY-MM-DD' },
     },
     {
       fieldName: 'remark',
@@ -135,7 +135,7 @@
       fieldName: 'planFinishDate',
       label: '璁″垝瀹屾垚',
       component: 'DatePicker',
-      componentProps: { class: 'w-full', valueFormat: 'x' },
+      componentProps: { class: 'w-full', valueFormat: 'YYYY-MM-DD' },
     },
     {
       fieldName: 'remark',
diff --git a/src/views/mes/pro/andon/record/data.ts b/src/views/mes/pro/andon/record/data.ts
index 6c8496b..e8c8a4a 100644
--- a/src/views/mes/pro/andon/record/data.ts
+++ b/src/views/mes/pro/andon/record/data.ts
@@ -275,7 +275,7 @@
               format: 'YYYY-MM-DD HH:mm:ss',
               placeholder: '璇烽�夋嫨澶勭疆鏃堕棿',
               showTime: true,
-              valueFormat: 'x',
+              valueFormat: 'YYYY-MM-DD HH:mm:ss',
             },
           },
           isUpdate
diff --git a/src/views/mes/pro/card/data.ts b/src/views/mes/pro/card/data.ts
index c7785cd..4e3b4a8 100644
--- a/src/views/mes/pro/card/data.ts
+++ b/src/views/mes/pro/card/data.ts
@@ -347,7 +347,7 @@
         class: '!w-full',
         placeholder: '璇烽�夋嫨杩涘叆宸ュ簭鏃堕棿',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
     },
     {
@@ -358,7 +358,7 @@
         class: '!w-full',
         placeholder: '璇烽�夋嫨鍑哄伐搴忔椂闂�',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
     },
     {
diff --git a/src/views/mes/pro/feedback/data.ts b/src/views/mes/pro/feedback/data.ts
index e2728f1..affe7dd 100644
--- a/src/views/mes/pro/feedback/data.ts
+++ b/src/views/mes/pro/feedback/data.ts
@@ -554,7 +554,7 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨鎶ュ伐鏃堕棿',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/mes/pro/mps/data.ts b/src/views/mes/pro/mps/data.ts
index 7e0a1a9..8afc6da 100644
--- a/src/views/mes/pro/mps/data.ts
+++ b/src/views/mes/pro/mps/data.ts
@@ -108,7 +108,7 @@
         class: '!w-full',
         placeholder: '璇烽�夋嫨闇�姹傛棩鏈�',
         format: 'YYYY-MM-DD',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
         disabled: !isCreate && !isEdit,
       },
       rules: 'required',
diff --git a/src/views/mes/pro/task/data.ts b/src/views/mes/pro/task/data.ts
index 30470f0..bba413d 100644
--- a/src/views/mes/pro/task/data.ts
+++ b/src/views/mes/pro/task/data.ts
@@ -281,7 +281,7 @@
         class: '!w-full',
         disabled: true,
         format: 'YYYY-MM-DD',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
     },
     {
@@ -415,7 +415,7 @@
         class: '!w-full',
         placeholder: '璇烽�夋嫨寮�濮嬫椂闂�',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
         // 寮�濮嬫椂闂村彉鏇达細閲嶆柊璁$畻缁撴潫鏃堕棿
         onChange: () => recalcEndTime(formApi),
       },
@@ -443,7 +443,7 @@
         class: '!w-full',
         disabled: true,
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
     },
     {
diff --git a/src/views/mes/pro/workorder/data.ts b/src/views/mes/pro/workorder/data.ts
index add9af7..d01a104 100644
--- a/src/views/mes/pro/workorder/data.ts
+++ b/src/views/mes/pro/workorder/data.ts
@@ -232,7 +232,7 @@
         disabled: headerReadonly,
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨闇�姹傛棩鏈�',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
diff --git a/src/views/mes/qc/ipqc/data.ts b/src/views/mes/qc/ipqc/data.ts
index d035a14..eeeff54 100644
--- a/src/views/mes/qc/ipqc/data.ts
+++ b/src/views/mes/qc/ipqc/data.ts
@@ -328,7 +328,7 @@
       componentProps: {
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨妫�娴嬫棩鏈�',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
diff --git a/src/views/mes/qc/iqc/data.ts b/src/views/mes/qc/iqc/data.ts
index 0a77b97..da23bdb 100644
--- a/src/views/mes/qc/iqc/data.ts
+++ b/src/views/mes/qc/iqc/data.ts
@@ -208,7 +208,7 @@
       componentProps: {
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鏉ユ枡鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
@@ -232,7 +232,7 @@
       componentProps: {
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨妫�娴嬫棩鏈�',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
diff --git a/src/views/mes/qc/oqc/data.ts b/src/views/mes/qc/oqc/data.ts
index 6706cca..62ef238 100644
--- a/src/views/mes/qc/oqc/data.ts
+++ b/src/views/mes/qc/oqc/data.ts
@@ -254,7 +254,7 @@
       componentProps: {
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍑鸿揣鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
@@ -265,7 +265,7 @@
       componentProps: {
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨妫�娴嬫棩鏈�',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
diff --git a/src/views/mes/qc/rqc/data.ts b/src/views/mes/qc/rqc/data.ts
index 4d08cbf..4ea4739 100644
--- a/src/views/mes/qc/rqc/data.ts
+++ b/src/views/mes/qc/rqc/data.ts
@@ -219,7 +219,7 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨妫�娴嬫棩鏈�',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/mes/tm/tool/data.ts b/src/views/mes/tm/tool/data.ts
index b5252fd..af2c051 100644
--- a/src/views/mes/tm/tool/data.ts
+++ b/src/views/mes/tm/tool/data.ts
@@ -183,7 +183,7 @@
       componentProps: {
         format: 'YYYY-MM-DD HH:mm:ss',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       dependencies: {
         triggerFields: ['maintenType'],
diff --git a/src/views/mes/workbench/components/tabs/ProductIssueFormModal.vue b/src/views/mes/workbench/components/tabs/ProductIssueFormModal.vue
index 97235e4..1e0a056 100644
--- a/src/views/mes/workbench/components/tabs/ProductIssueFormModal.vue
+++ b/src/views/mes/workbench/components/tabs/ProductIssueFormModal.vue
@@ -98,7 +98,7 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨闇�姹傛椂闂�',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
     },
     {
diff --git a/src/views/mes/workbench/components/tabs/ProductIssueTab.vue b/src/views/mes/workbench/components/tabs/ProductIssueTab.vue
index 9731a1b..ae52420 100644
--- a/src/views/mes/workbench/components/tabs/ProductIssueTab.vue
+++ b/src/views/mes/workbench/components/tabs/ProductIssueTab.vue
@@ -119,7 +119,7 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨闇�姹傛椂闂�',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
     },
     {
diff --git a/src/views/mes/workbench/components/tabs/ReturnIssueFormModal.vue b/src/views/mes/workbench/components/tabs/ReturnIssueFormModal.vue
index 89453de..3a0e29e 100644
--- a/src/views/mes/workbench/components/tabs/ReturnIssueFormModal.vue
+++ b/src/views/mes/workbench/components/tabs/ReturnIssueFormModal.vue
@@ -117,7 +117,7 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨閫�鏂欐棩鏈�',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
     },
     {
diff --git a/src/views/mes/workbench/components/tabs/ReturnIssueTab.vue b/src/views/mes/workbench/components/tabs/ReturnIssueTab.vue
index 236020c..5ab1037 100644
--- a/src/views/mes/workbench/components/tabs/ReturnIssueTab.vue
+++ b/src/views/mes/workbench/components/tabs/ReturnIssueTab.vue
@@ -131,7 +131,7 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨閫�鏂欐棩鏈�',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
     },
     {
diff --git a/src/views/srm/enums.ts b/src/views/srm/enums.ts
new file mode 100644
index 0000000..71712b5
--- /dev/null
+++ b/src/views/srm/enums.ts
@@ -0,0 +1,263 @@
+// ============================================================
+// SRM 缁熶竴鏋氫妇甯搁噺
+// 鎵�鏈夊�间弗鏍煎榻愬悗绔灇涓惧畾涔� (yudao-module-srm/enums/)
+// ============================================================
+
+/** 閫夐」绫诲瀷 (Select/RadioGroup閫氱敤) */
+export interface EnumOption {
+  label: string;
+  value: number | string;
+}
+
+/** CellTag 鏁版嵁绫诲瀷 */
+export interface CellTagItem {
+  value: number | string;
+  color: string;
+}
+
+// ========== 渚涘簲鍟� (瀛楁鍊煎榻� CommonStatusEnum + SupplierTypeEnum) ==========
+
+/** 渚涘簲鍟嗙姸鎬� 鈥� 瀵归綈 CommonStatusEnum (0=寮�鍚� 1=鍏抽棴) */
+export const SUPPLIER_STATUS = {
+  ENABLE: 0,
+  DISABLE: 1,
+} as const;
+
+export const SUPPLIER_STATUS_OPTIONS: EnumOption[] = [
+  { label: '寮�鍚�', value: 0 },
+  { label: '鍏抽棴', value: 1 },
+];
+
+export const SUPPLIER_STATUS_CELLTAG: { data: CellTagItem[]; labels: Record<number, string> } = {
+  data: [
+    { value: 0, color: 'green' },
+    { value: 1, color: 'red' },
+  ],
+  labels: { 0: '寮�鍚�', 1: '鍏抽棴' },
+};
+
+/** 渚涘簲鍟嗙被鍨� 鈥� 瀵归綈 SupplierTypeEnum (1=鐢熶骇鍨� 2=璐告槗鍨� 3=鏈嶅姟鍨� 4=鍏朵粬) */
+export const SUPPLIER_TYPE = {
+  PRODUCTION: 1,
+  TRADING: 2,
+  SERVICE: 3,
+  OTHER: 4,
+} as const;
+
+export const SUPPLIER_TYPE_OPTIONS: EnumOption[] = [
+  { label: '鐢熶骇鍨�', value: 1 },
+  { label: '璐告槗鍨�', value: 2 },
+  { label: '鏈嶅姟鍨�', value: 3 },
+  { label: '鍏朵粬', value: 4 },
+];
+
+export const SUPPLIER_TYPE_CELLTAG: { data: CellTagItem[]; labels: Record<number, string> } = {
+  data: [
+    { value: 1, color: 'blue' },
+    { value: 2, color: 'green' },
+    { value: 3, color: 'orange' },
+    { value: 4, color: 'default' },
+  ],
+  labels: { 1: '鐢熶骇鍨�', 2: '璐告槗鍨�', 3: '鏈嶅姟鍨�', 4: '鍏朵粬' },
+};
+
+// ========== 鍑嗗叆鐢宠 鈥� 瀵归綈 SupplierApplyStatusEnum (0/10/20/30/40/50) ==========
+
+export const APPLY_STATUS = {
+  DRAFT: 0,
+  PENDING_PURCHASE: 10,
+  PENDING_QUALITY: 20,
+  PENDING_FINANCE: 30,
+  APPROVED: 40,
+  REJECTED: 50,
+} as const;
+
+export const APPLY_STATUS_OPTIONS: EnumOption[] = [
+  { label: '寰呮彁浜�', value: 0 },
+  { label: '寰呴噰璐鏍�', value: 10 },
+  { label: '寰呰川閲忓鏍�', value: 20 },
+  { label: '寰呰储鍔″鏍�', value: 30 },
+  { label: '宸查�氳繃', value: 40 },
+  { label: '宸叉嫆缁�', value: 50 },
+];
+
+export const APPLY_STATUS_CELLTAG: { data: CellTagItem[]; labels: Record<number, string> } = {
+  data: [
+    { value: 0, color: 'gray' },
+    { value: 10, color: 'blue' },
+    { value: 20, color: 'orange' },
+    { value: 30, color: 'purple' },
+    { value: 40, color: 'green' },
+    { value: 50, color: 'red' },
+  ],
+  labels: { 0: '寰呮彁浜�', 10: '寰呴噰璐鏍�', 20: '寰呰川閲忓鏍�', 30: '寰呰储鍔″鏍�', 40: '宸查�氳繃', 50: '宸叉嫆缁�' },
+};
+
+// ========== 璧勮川璇佷功 鈥� 瀵归綈 CertificateTypeEnum (1=钀ヤ笟鎵х収 2=璁よ瘉璇佷功 3=妫�娴嬫姤鍛� 4=鍏朵粬) ==========
+
+export const CERTIFICATE_TYPE = {
+  BUSINESS_LICENSE: 1,
+  CERTIFICATION: 2,
+  INSPECTION_REPORT: 3,
+  OTHER: 4,
+} as const;
+
+export const CERTIFICATE_TYPE_OPTIONS: EnumOption[] = [
+  { label: '钀ヤ笟鎵х収', value: 1 },
+  { label: '璁よ瘉璇佷功', value: 2 },
+  { label: '妫�娴嬫姤鍛�', value: 3 },
+  { label: '鍏朵粬', value: 4 },
+];
+
+export const CERTIFICATE_TYPE_CELLTAG: { data: CellTagItem[]; labels: Record<number, string> } = {
+  data: [
+    { value: 1, color: 'blue' },
+    { value: 2, color: 'green' },
+    { value: 3, color: 'orange' },
+    { value: 4, color: 'default' },
+  ],
+  labels: { 1: '钀ヤ笟鎵х収', 2: '璁よ瘉璇佷功', 3: '妫�娴嬫姤鍛�', 4: '鍏朵粬' },
+};
+
+// ========== 缁╂晥璇勪环 鈥� 鍚庣 evaluationLevel 涓� String 绫诲瀷 (A/B/C/D) ==========
+
+export const EVALUATION_GRADE = {
+  A: 'A',
+  B: 'B',
+  C: 'C',
+  D: 'D',
+} as const;
+
+export const EVALUATION_GRADE_CELLTAG: { data: CellTagItem[] } = {
+  data: [
+    { value: 'A', color: 'green' },
+    { value: 'B', color: 'blue' },
+    { value: 'C', color: 'orange' },
+    { value: 'D', color: 'red' },
+  ],
+};
+
+/** 缁煎悎璇勫垎鍖洪棿 鈥� 鍚庣璁$畻: >=90鈫扐, >=80鈫払, >=70鈫扖, <70鈫扗 */
+export const COMPOSITE_SCORE_CELLTAG: { data: CellTagItem[] } = {
+  data: [
+    { value: 90, color: 'green' },
+    { value: 80, color: 'blue' },
+    { value: 70, color: 'orange' },
+    { value: 0, color: 'red' },
+  ],
+};
+
+// ========== 鎷涙爣椤圭洰 鈥� 瀵归綈 TenderStatusEnum (0/1/2/3/4/5) + TenderTypeEnum (1/2/3) ==========
+
+/** 鎷涙爣绫诲瀷 鈥� 瀵归綈 TenderTypeEnum (1=鍏紑鎷涙爣 2=閭�璇锋嫑鏍� 3=绔炰簤鎬ц皥鍒�) */
+export const TENDER_TYPE = {
+  PUBLIC: 1,
+  INVITATION: 2,
+  COMPETITIVE_NEGOTIATION: 3,
+} as const;
+
+export const TENDER_TYPE_OPTIONS: EnumOption[] = [
+  { label: '鍏紑鎷涙爣', value: 1 },
+  { label: '閭�璇锋嫑鏍�', value: 2 },
+  { label: '绔炰簤鎬ц皥鍒�', value: 3 },
+];
+
+export const TENDER_TYPE_CELLTAG: { data: CellTagItem[]; labels: Record<number, string> } = {
+  data: [
+    { value: 1, color: 'blue' },
+    { value: 2, color: 'blue' },
+    { value: 3, color: 'green' },
+  ],
+  labels: { 1: '鍏紑鎷涙爣', 2: '閭�璇锋嫑鏍�', 3: '绔炰簤鎬ц皥鍒�' },
+};
+
+/** 鎷涙爣椤圭洰鐘舵�� 鈥� 瀵归綈 TenderStatusEnum (0=鑽夌 1=鍙戝竷涓� 2=鎶曟爣涓� 3=璇勬爣涓� 4=宸插畾鏍� 5=宸插叧闂�) */
+export const TENDER_STATUS = {
+  DRAFT: 0,
+  PUBLISHED: 1,
+  BIDDING: 2,
+  EVALUATING: 3,
+  AWARDED: 4,
+  CLOSED: 5,
+} as const;
+
+export const TENDER_STATUS_OPTIONS: EnumOption[] = [
+  { label: '鑽夌', value: 0 },
+  { label: '鍙戝竷涓�', value: 1 },
+  { label: '鎶曟爣涓�', value: 2 },
+  { label: '璇勬爣涓�', value: 3 },
+  { label: '宸插畾鏍�', value: 4 },
+  { label: '宸插叧闂�', value: 5 },
+];
+
+export const TENDER_STATUS_CELLTAG: { data: CellTagItem[]; labels: Record<number, string> } = {
+  data: [
+    { value: 0, color: 'gray' },
+    { value: 1, color: 'blue' },
+    { value: 2, color: 'cyan' },
+    { value: 3, color: 'orange' },
+    { value: 4, color: 'green' },
+    { value: 5, color: 'red' },
+  ],
+  labels: { 0: '鑽夌', 1: '鍙戝竷涓�', 2: '鎶曟爣涓�', 3: '璇勬爣涓�', 4: '宸插畾鏍�', 5: '宸插叧闂�' },
+};
+
+/** 鎷涙爣鐘舵�� Ant Design Tag 棰滆壊鏄犲皠 */
+export const TENDER_STATUS_COLORS: Record<number, string> = {
+  [TENDER_STATUS.DRAFT]: 'default',
+  [TENDER_STATUS.PUBLISHED]: 'blue',
+  [TENDER_STATUS.BIDDING]: 'cyan',
+  [TENDER_STATUS.EVALUATING]: 'orange',
+  [TENDER_STATUS.AWARDED]: 'green',
+  [TENDER_STATUS.CLOSED]: 'red',
+};
+
+// ========== 鎶曟爣 鈥� 瀵归綈 BidStatusEnum (0=鎶ュ悕 10=宸叉姇鏍� 20=鎾ゆ爣 30=涓爣 40=鏈腑鏍�) ==========
+
+export const BID_STATUS = {
+  REGISTERED: 0,
+  BID: 10,
+  WITHDRAWN: 20,
+  WIN: 30,
+  LOSE: 40,
+} as const;
+
+export const BID_STATUS_MAP: Record<number, string> = {
+  0: '鎶ュ悕',
+  10: '宸叉姇鏍�',
+  20: '鎾ゆ爣',
+  30: '涓爣',
+  40: '鏈腑鏍�',
+};
+
+// ========== 瀹氭爣 鈥� 瀵归綈 AwardStatusEnum (0=寰呭鎵� 10=宸插畾鏍� 20=宸茬敓鎴愯鍗�) ==========
+
+export const AWARD_STATUS = {
+  PENDING_APPROVAL: 0,
+  AWARDED: 10,
+  ORDER_GENERATED: 20,
+} as const;
+
+export const AWARD_STATUS_MAP: Record<number, string> = {
+  0: '寰呭鎵�',
+  10: '宸插畾鏍�',
+  20: '宸茬敓鎴愯鍗�',
+};
+
+// ========== 宸ュ叿鍑芥暟 ==========
+
+/** 鏍规嵁鍊艰幏鍙栨爣绛� */
+export function getLabel(map: Record<number, string>, value?: number): string {
+  return value !== undefined ? map[value] || String(value) : '';
+}
+
+/** 灏� options 鏁扮粍杞负 labels 鏄犲皠 */
+export function optionsToLabels(options: EnumOption[]): Record<number, string> {
+  const labels: Record<number, string> = {};
+  for (const opt of options) {
+    if (typeof opt.value === 'number') {
+      labels[opt.value] = opt.label;
+    }
+  }
+  return labels;
+}
diff --git a/src/views/srm/statistics/index.vue b/src/views/srm/statistics/index.vue
new file mode 100644
index 0000000..439ae78
--- /dev/null
+++ b/src/views/srm/statistics/index.vue
@@ -0,0 +1,128 @@
+<script lang="ts" setup>
+import type { EchartsUIType } from '@vben/plugins/echarts';
+import type { SrmStatisticsApi } from '#/api/srm/statistics';
+
+import { onMounted, ref, watch } from 'vue';
+
+import { Page } from '@vben/common-ui';
+
+import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
+
+import { getTenderStatistics } from '#/api/srm/statistics';
+import { getSupplierSimpleList } from '#/api/srm/supplier';
+
+defineOptions({ name: 'SrmStatistics' });
+
+const year = ref(new Date().getFullYear().toString());
+const stats = ref<SrmStatisticsApi.TenderStatisticsVO>({});
+const supplierCount = ref(0);
+
+async function loadStats() {
+  stats.value = await getTenderStatistics(year.value);
+}
+async function loadSupplierCount() {
+  const list = await getSupplierSimpleList();
+  supplierCount.value = list?.length ?? 0;
+}
+
+// 鍥捐〃
+const pieChartRef = ref<EchartsUIType>();
+const barChartRef = ref<EchartsUIType>();
+const { renderEcharts: renderPie } = useEcharts(pieChartRef);
+const { renderEcharts: renderBar } = useEcharts(barChartRef);
+
+async function renderCharts() {
+  // 楗煎浘锛氫緵搴斿晢鐘舵�佸垎甯�
+  await renderPie({
+    title: { text: '渚涘簲鍟嗙姸鎬佸垎甯�', left: 'center' },
+    tooltip: { trigger: 'item' },
+    legend: { bottom: 0 },
+    series: [{
+      type: 'pie',
+      radius: ['40%', '70%'],
+      data: [
+        { value: supplierCount.value, name: '宸插惎鐢�' },
+        { value: 0, name: '宸茬鐢�' },
+      ],
+    }],
+  });
+
+  // 鏌辩姸鍥撅細鎷涙爣缁熻姒傝
+  await renderBar({
+    title: { text: `${year.value} 骞存嫑鏍囩粺璁℃瑙坄, left: 'center' },
+    tooltip: { trigger: 'axis' },
+    xAxis: { type: 'category', data: ['鎷涙爣鎬绘暟', '鍙備笌渚涘簲鍟�', '瀹氭爣閲戦(涓�)', '鑺傜害閲戦(涓�)'] },
+    yAxis: { type: 'value' },
+    series: [{
+      type: 'bar',
+      data: [
+        stats.value.totalTenderCount ?? 0,
+        stats.value.supplierCount ?? 0,
+        +(((stats.value.totalAwardAmount ?? 0) / 10000).toFixed(2)),
+        +(((stats.value.savedAmount ?? 0) / 10000).toFixed(2)),
+      ],
+      itemStyle: {
+        color: (params: any) => ['#3b82f6', '#ec4899', '#f97316', '#8b5cf6'][params.dataIndex],
+      },
+    }],
+  });
+}
+
+watch([stats, supplierCount], () => renderCharts());
+onMounted(() => { loadStats(); loadSupplierCount(); });
+</script>
+
+<template>
+  <Page auto-content-height>
+    <div class="p-4">
+      <div class="mb-6 flex items-center gap-4">
+        <span class="text-lg font-medium">SRM 缁熻鐪嬫澘</span>
+        <a-input v-model:value="year" placeholder="骞翠唤" style="width: 120px" @change="loadStats" />
+      </div>
+
+      <!-- 6 涓粺璁″崱鐗� -->
+      <div class="mb-6 grid grid-cols-3 gap-4">
+        <div class="rounded-lg bg-blue-50 p-6 text-center shadow-sm">
+          <div class="text-3xl font-bold text-blue-600">{{ stats.totalTenderCount ?? 0 }}</div>
+          <div class="mt-2 text-gray-500">鎷涙爣鎬绘暟</div>
+        </div>
+        <div class="rounded-lg bg-green-50 p-6 text-center shadow-sm">
+          <div class="text-3xl font-bold text-green-600">{{ stats.totalTenderAmount?.toLocaleString() ?? 0 }}</div>
+          <div class="mt-2 text-gray-500">鎷涙爣鎬婚噾棰�(鍏�)</div>
+        </div>
+        <div class="rounded-lg bg-orange-50 p-6 text-center shadow-sm">
+          <div class="text-3xl font-bold text-orange-600">{{ stats.totalAwardAmount?.toLocaleString() ?? 0 }}</div>
+          <div class="mt-2 text-gray-500">瀹氭爣鎬婚噾棰�(鍏�)</div>
+        </div>
+        <div class="rounded-lg bg-purple-50 p-6 text-center shadow-sm">
+          <div class="text-3xl font-bold text-purple-600">{{ stats.savedAmount?.toLocaleString() ?? 0 }}</div>
+          <div class="mt-2 text-gray-500">鑺傜害閲戦(鍏�)</div>
+        </div>
+        <div class="rounded-lg bg-teal-50 p-6 text-center shadow-sm">
+          <div class="text-3xl font-bold text-teal-600">{{ stats.winRate ?? 0 }}%</div>
+          <div class="mt-2 text-gray-500">涓爣鐜�</div>
+        </div>
+        <div class="rounded-lg bg-rose-50 p-6 text-center shadow-sm">
+          <div class="text-3xl font-bold text-rose-600">{{ stats.supplierCount ?? 0 }}</div>
+          <div class="mt-2 text-gray-500">鍙備笌鎷涙爣渚涘簲鍟�</div>
+        </div>
+      </div>
+
+      <!-- 鍥捐〃鍖哄煙 -->
+      <div class="mb-6 grid grid-cols-2 gap-4">
+        <div class="rounded-lg bg-white p-4 shadow-sm">
+          <EchartsUI ref="pieChartRef" class="h-[350px] w-full" />
+        </div>
+        <div class="rounded-lg bg-white p-4 shadow-sm">
+          <EchartsUI ref="barChartRef" class="h-[350px] w-full" />
+        </div>
+      </div>
+
+      <!-- 搴曢儴淇℃伅 -->
+      <div class="rounded-lg bg-white p-4 shadow-sm">
+        <h3 class="mb-3 text-base font-medium">渚涘簲鍟嗘�绘暟: {{ supplierCount }} 瀹�</h3>
+        <a-empty v-if="supplierCount === 0" description="鏆傛棤渚涘簲鍟嗘暟鎹�" />
+      </div>
+    </div>
+  </Page>
+</template>
diff --git a/src/views/srm/supplier/components/index.ts b/src/views/srm/supplier/components/index.ts
new file mode 100644
index 0000000..0033d6f
--- /dev/null
+++ b/src/views/srm/supplier/components/index.ts
@@ -0,0 +1,2 @@
+export { default as SrmSupplierSelect } from './select.vue';
+export { default as SrmSupplierSelectDialog } from './select-dialog.vue';
diff --git a/src/views/srm/supplier/components/select-dialog.vue b/src/views/srm/supplier/components/select-dialog.vue
new file mode 100644
index 0000000..1f3c614
--- /dev/null
+++ b/src/views/srm/supplier/components/select-dialog.vue
@@ -0,0 +1,259 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { SrmSupplierApi } from '#/api/srm/supplier';
+
+import { nextTick, ref } from 'vue';
+
+import { CommonStatusEnum } from '@vben/constants';
+
+import { message, Modal } from 'ant-design-vue';
+
+import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import { getSupplierPage } from '#/api/srm/supplier';
+
+defineOptions({ name: 'SrmSupplierSelectDialog' });
+
+const emit = defineEmits<{
+  selected: [rows: SrmSupplierApi.SupplierVO[]];
+}>();
+
+const open = ref(false);
+const multiple = ref(true);
+const approvalFilter = ref(true);
+const selectedRows = ref<SrmSupplierApi.SupplierVO[]>([]);
+const preSelectedIds = ref<number[]>([]);
+
+function getTableRows() {
+  return gridApi.grid.getTableData().fullData as SrmSupplierApi.SupplierVO[];
+}
+
+function getMultipleSelectedRows() {
+  const selectedMap = new Map<number, SrmSupplierApi.SupplierVO>();
+  const records = [
+    ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
+    ...(gridApi.grid.getCheckboxRecords?.() ?? []),
+  ] as SrmSupplierApi.SupplierVO[];
+  records.forEach((row) => {
+    const rowId = row.id;
+    if (rowId) {
+      selectedMap.set(rowId, row);
+    }
+  });
+  return [...selectedMap.values()];
+}
+
+function handleCheckboxSelectChange() {
+  selectedRows.value = getMultipleSelectedRows();
+}
+
+function handleRadioChange(row: SrmSupplierApi.SupplierVO) {
+  selectedRows.value = [row];
+}
+
+async function toggleMultipleRow(row: SrmSupplierApi.SupplierVO) {
+  const selected = gridApi.grid.isCheckedByCheckboxRow(row);
+  await gridApi.grid.setCheckboxRow(row, !selected);
+  selectedRows.value = getMultipleSelectedRows();
+}
+
+async function handleCellDblclick({ row }: { row: SrmSupplierApi.SupplierVO }) {
+  if (multiple.value) {
+    await toggleMultipleRow(row);
+    return;
+  }
+  selectedRows.value = [row];
+  await gridApi.grid.setRadioRow(row);
+  handleConfirm();
+}
+
+async function applyPreSelection() {
+  if (preSelectedIds.value.length === 0) {
+    return;
+  }
+  const rows = getTableRows();
+  for (const row of rows) {
+    if (
+      row.id === undefined ||
+      row.id === null ||
+      !preSelectedIds.value.includes(row.id)
+    ) {
+      continue;
+    }
+    if (multiple.value) {
+      await gridApi.grid.setCheckboxRow(row, true);
+    } else {
+      await gridApi.grid.setRadioRow(row);
+      selectedRows.value = [row];
+      return;
+    }
+  }
+  if (multiple.value) {
+    selectedRows.value = getMultipleSelectedRows();
+  }
+}
+
+function useSelectGridFormSchema() {
+  return [
+    {
+      fieldName: 'name',
+      label: '渚涘簲鍟嗗悕绉�',
+      component: 'Input',
+      componentProps: {
+        placeholder: '璇疯緭鍏ヤ緵搴斿晢鍚嶇О',
+        allowClear: true,
+      },
+    },
+    {
+      fieldName: 'code',
+      label: '渚涘簲鍟嗙紪鐮�',
+      component: 'Input',
+      componentProps: {
+        placeholder: '璇疯緭鍏ヤ緵搴斿晢缂栫爜',
+        allowClear: true,
+      },
+    },
+  ];
+}
+
+function useSelectGridColumns(multiple = false): VxeTableGridOptions<SrmSupplierApi.SupplierVO>['columns'] {
+  return [
+    {
+      type: multiple ? 'checkbox' : 'radio',
+      width: 50,
+    },
+    {
+      field: 'code',
+      title: '缂栫爜',
+      minWidth: 100,
+    },
+    {
+      field: 'name',
+      title: '渚涘簲鍟嗗悕绉�',
+      minWidth: 150,
+    },
+    {
+      field: 'contactName',
+      title: '鑱旂郴浜�',
+      minWidth: 100,
+    },
+    {
+      field: 'contactMobile',
+      title: '鑱旂郴鐢佃瘽',
+      minWidth: 120,
+    },
+    {
+      field: 'contactEmail',
+      title: '閭',
+      minWidth: 150,
+    },
+  ];
+}
+
+const [Grid, gridApi] = useVbenVxeGrid({
+  formOptions: {
+    schema: useSelectGridFormSchema(),
+  },
+  gridOptions: {
+    columns: useSelectGridColumns(true),
+    height: 520,
+    keepSource: true,
+    checkboxConfig: {
+      highlight: true,
+      range: true,
+      reserve: true,
+    },
+    radioConfig: {
+      highlight: true,
+      trigger: 'row',
+    },
+    proxyConfig: {
+      ajax: {
+        query: async ({ page }, formValues) => {
+          const params: Record<string, unknown> = {
+            pageNo: page.currentPage,
+            pageSize: page.pageSize,
+            ...formValues,
+            status: CommonStatusEnum.ENABLE,
+          };
+          if (approvalFilter.value) {
+            params.approved = true;
+          }
+          return await getSupplierPage(params);
+        },
+      },
+    },
+    rowConfig: {
+      keyField: 'id',
+      isHover: true,
+    },
+    toolbarConfig: {
+      refresh: true,
+      search: true,
+    },
+  } as VxeTableGridOptions<SrmSupplierApi.SupplierVO>,
+  gridEvents: {
+    cellDblclick: handleCellDblclick,
+    checkboxAll: handleCheckboxSelectChange,
+    checkboxChange: handleCheckboxSelectChange,
+    radioChange: ({ row }: { row: SrmSupplierApi.SupplierVO }) => {
+      handleRadioChange(row);
+    },
+  },
+});
+
+async function resetQueryState() {
+  selectedRows.value = [];
+  await gridApi.grid.clearCheckboxRow();
+  await gridApi.grid.clearCheckboxReserve();
+  await gridApi.grid.clearRadioRow();
+  await gridApi.formApi.resetForm();
+}
+
+async function openModal(
+  selectedIds?: number[],
+  options?: { multiple?: boolean; approvalFilter?: boolean },
+) {
+  open.value = true;
+  multiple.value = options?.multiple ?? true;
+  approvalFilter.value = options?.approvalFilter ?? true;
+  preSelectedIds.value = selectedIds || [];
+  await nextTick();
+  gridApi.setGridOptions({
+    columns: useSelectGridColumns(multiple.value),
+  });
+  await resetQueryState();
+  await gridApi.query();
+  await nextTick();
+  await applyPreSelection();
+}
+
+async function closeModal() {
+  open.value = false;
+  await resetQueryState();
+}
+
+function handleConfirm() {
+  const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value;
+  if (rows.length === 0) {
+    message.warning(multiple.value ? '璇疯嚦灏戦�夋嫨涓�鏉℃暟鎹�' : '璇烽�夋嫨涓�鏉℃暟鎹�');
+    return;
+  }
+  emit('selected', multiple.value ? rows : [rows[0]!]);
+  open.value = false;
+}
+
+defineExpose({ open: openModal });
+</script>
+
+<template>
+  <Modal
+    v-model:open="open"
+    title="SRM 渚涘簲鍟嗛�夋嫨"
+    width="70%"
+    :destroy-on-close="true"
+    @ok="handleConfirm"
+    @cancel="closeModal"
+  >
+    <Grid table-title="渚涘簲鍟嗗垪琛�" />
+  </Modal>
+</template>
diff --git a/src/views/srm/supplier/components/select.vue b/src/views/srm/supplier/components/select.vue
new file mode 100644
index 0000000..0da35fd
--- /dev/null
+++ b/src/views/srm/supplier/components/select.vue
@@ -0,0 +1,117 @@
+<script lang="ts" setup>
+import type { SrmSupplierApi } from '#/api/srm/supplier';
+
+import { computed, ref, useAttrs } from 'vue';
+
+import { IconifyIcon } from '@vben/icons';
+
+import { Input, Tooltip } from 'ant-design-vue';
+
+import SrmSupplierSelectDialog from './select-dialog.vue';
+
+defineOptions({ name: 'SrmSupplierSelect', inheritAttrs: false });
+
+const props = withDefaults(
+  defineProps<{
+    allowClear?: boolean;
+    approvalFilter?: boolean;
+    disabled?: boolean;
+    modelValue?: number;
+    placeholder?: string;
+    supplierName?: string;
+  }>(),
+  {
+    allowClear: true,
+    approvalFilter: true,
+    disabled: false,
+    modelValue: undefined,
+    placeholder: '璇烽�夋嫨渚涘簲鍟�',
+    supplierName: undefined,
+  },
+);
+const emit = defineEmits<{
+  change: [item: SrmSupplierApi.SupplierVO | undefined];
+  'update:modelValue': [value: number | undefined];
+}>();
+const attrs = useAttrs();
+const dialogRef = ref<InstanceType<typeof SrmSupplierSelectDialog>>();
+const hovering = ref(false);
+const selectedItem = ref<SrmSupplierApi.SupplierVO>();
+
+const displayLabel = computed(() => props.supplierName || selectedItem.value?.name || '');
+const showClear = computed(
+  () =>
+    props.allowClear &&
+    !props.disabled &&
+    hovering.value &&
+    props.modelValue !== null,
+);
+
+function clearSelected() {
+  selectedItem.value = undefined;
+  emit('update:modelValue', undefined);
+  emit('change', undefined);
+}
+
+function handleClick(event: MouseEvent) {
+  if (props.disabled) {
+    return;
+  }
+  const target = event.target as HTMLElement;
+  if (showClear.value && target.closest('.ant-input-suffix')) {
+    event.stopPropagation();
+    clearSelected();
+    return;
+  }
+  const selectedIds = (
+    props.modelValue === null ? [] : [props.modelValue]
+  ) as number[];
+  dialogRef.value?.open(selectedIds, { multiple: false, approvalFilter: props.approvalFilter });
+}
+
+function handleSelected(rows: SrmSupplierApi.SupplierVO[]) {
+  const item = rows[0];
+  if (!item) {
+    return;
+  }
+  selectedItem.value = item;
+  emit('update:modelValue', item.id);
+  emit('change', item);
+}
+</script>
+
+<template>
+  <div
+    v-bind="attrs"
+    class="w-full"
+    :class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
+    @click="handleClick"
+    @mouseenter="hovering = true"
+    @mouseleave="hovering = false"
+  >
+    <Tooltip :mouse-enter-delay="0.5" :open="selectedItem ? undefined : false">
+      <template #title>
+        <div v-if="selectedItem" class="leading-6">
+          <div>缂栫爜锛歿{ selectedItem.code || '-' }}</div>
+          <div>鍚嶇О锛歿{ selectedItem.name || '-' }}</div>
+          <div>鑱旂郴浜猴細{{ selectedItem.contactName || '-' }}</div>
+          <div>鐢佃瘽锛歿{ selectedItem.contactMobile || '-' }}</div>
+        </div>
+      </template>
+      <Input
+        :disabled="disabled"
+        :placeholder="placeholder"
+        :value="displayLabel"
+        readonly
+      >
+        <template #suffix>
+          <IconifyIcon
+            class="size-4"
+            :icon="showClear ? 'lucide:circle-x' : 'lucide:search'"
+          />
+        </template>
+      </Input>
+    </Tooltip>
+  </div>
+  <SrmSupplierSelectDialog ref="dialogRef" @selected="handleSelected" />
+</template>
diff --git a/src/views/srm/supplier/data.ts b/src/views/srm/supplier/data.ts
new file mode 100644
index 0000000..58dc484
--- /dev/null
+++ b/src/views/srm/supplier/data.ts
@@ -0,0 +1,178 @@
+import type { VbenFormSchema } from '#/adapter/form';
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+
+import { z } from '#/adapter/form';
+
+import {
+  SUPPLIER_STATUS_OPTIONS,
+  SUPPLIER_STATUS_CELLTAG,
+  SUPPLIER_TYPE_OPTIONS,
+  SUPPLIER_TYPE_CELLTAG,
+} from '../enums';
+
+/** 鏂板/淇敼鐨勮〃鍗� */
+export function useFormSchema(): VbenFormSchema[] {
+  return [
+    {
+      component: 'Input',
+      fieldName: 'id',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'code',
+      label: '渚涘簲鍟嗙紪鐮�',
+      component: 'Input',
+      rules: 'required',
+      componentProps: { placeholder: '璇疯緭鍏ヤ緵搴斿晢缂栫爜' },
+    },
+    {
+      fieldName: 'name',
+      label: '渚涘簲鍟嗗悕绉�',
+      component: 'Input',
+      rules: 'required',
+      componentProps: { placeholder: '璇疯緭鍏ヤ緵搴斿晢鍚嶇О' },
+    },
+    {
+      fieldName: 'type',
+      label: '渚涘簲鍟嗙被鍨�',
+      component: 'Select',
+      rules: 'required',
+      componentProps: {
+        placeholder: '璇烽�夋嫨渚涘簲鍟嗙被鍨�',
+        options: SUPPLIER_TYPE_OPTIONS,
+      },
+    },
+    {
+      fieldName: 'status',
+      label: '鐘舵��',
+      component: 'RadioGroup',
+      componentProps: {
+        options: SUPPLIER_STATUS_OPTIONS,
+        buttonStyle: 'solid',
+        optionType: 'button',
+      },
+      rules: z.number().default(1),
+    },
+    {
+      fieldName: 'contactName',
+      label: '鑱旂郴浜�',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヨ仈绯讳汉' },
+    },
+    {
+      fieldName: 'contactMobile',
+      label: '鑱旂郴鐢佃瘽',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヨ仈绯荤數璇�' },
+    },
+    {
+      fieldName: 'contactEmail',
+      label: '閭',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ラ偖绠�' },
+    },
+    {
+      fieldName: 'address',
+      label: '鍦板潃',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ュ湴鍧�' },
+      formItemClass: 'col-span-2',
+    },
+    {
+      fieldName: 'bankName',
+      label: '寮�鎴烽摱琛�',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ュ紑鎴烽摱琛�' },
+    },
+    {
+      fieldName: 'bankAccount',
+      label: '閾惰璐﹀彿',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ラ摱琛岃处鍙�' },
+    },
+    {
+      fieldName: 'taxNo',
+      label: '绋庡彿',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ョ◣鍙�' },
+    },
+    {
+      fieldName: 'remark',
+      label: '澶囨敞',
+      component: 'Textarea',
+      componentProps: { placeholder: '璇疯緭鍏ュ娉�', rows: 3 },
+      formItemClass: 'col-span-2',
+    },
+  ];
+}
+
+/** 鎼滅储琛ㄥ崟 */
+export function useGridFormSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'code',
+      label: '缂栫爜',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ョ紪鐮�', allowClear: true },
+    },
+    {
+      fieldName: 'name',
+      label: '渚涘簲鍟嗗悕绉�',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヤ緵搴斿晢鍚嶇О', allowClear: true },
+    },
+    {
+      fieldName: 'type',
+      label: '绫诲瀷',
+      component: 'Select',
+      componentProps: {
+        placeholder: '璇烽�夋嫨绫诲瀷',
+        allowClear: true,
+        options: SUPPLIER_TYPE_OPTIONS,
+      },
+    },
+    {
+      fieldName: 'status',
+      label: '鐘舵��',
+      component: 'Select',
+      componentProps: {
+        placeholder: '璇烽�夋嫨鐘舵��',
+        allowClear: true,
+        options: SUPPLIER_STATUS_OPTIONS,
+      },
+    },
+  ];
+}
+
+/** 琛ㄦ牸鍒� */
+export function useGridColumns(): VxeTableGridOptions['columns'] {
+  return [
+    { field: 'code', title: '缂栫爜', minWidth: 120 },
+    { field: 'name', title: '渚涘簲鍟嗗悕绉�', minWidth: 150 },
+    {
+      field: 'type',
+      title: '绫诲瀷',
+      minWidth: 100,
+      cellRender: { name: 'CellTag', props: SUPPLIER_TYPE_CELLTAG },
+    },
+    {
+      field: 'status',
+      title: '鐘舵��',
+      minWidth: 80,
+      cellRender: { name: 'CellTag', props: SUPPLIER_STATUS_CELLTAG },
+    },
+    { field: 'contactName', title: '鑱旂郴浜�', minWidth: 100 },
+    { field: 'contactMobile', title: '鑱旂郴鐢佃瘽', minWidth: 120 },
+    { field: 'contactEmail', title: '閭', minWidth: 150 },
+    { field: 'createTime', title: '鍒涘缓鏃堕棿', minWidth: 160 },
+    {
+      title: '鎿嶄綔',
+      width: 130,
+      fixed: 'right',
+      slots: { default: 'actions' },
+    },
+  ];
+}
diff --git a/src/views/srm/supplier/index.vue b/src/views/srm/supplier/index.vue
new file mode 100644
index 0000000..ff73e1a
--- /dev/null
+++ b/src/views/srm/supplier/index.vue
@@ -0,0 +1,120 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { SrmSupplierApi } from '#/api/srm/supplier';
+
+import { Page, useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
+import {
+  deleteSupplier,
+  getSupplierPage,
+} from '#/api/srm/supplier';
+import { $t } from '#/locales';
+
+import { useGridColumns, useGridFormSchema } from './data';
+import SupplierForm from './modules/form.vue';
+
+defineOptions({ name: 'SrmSupplier' });
+
+function handleRefresh() {
+  gridApi.query();
+}
+
+function handleCreate() {
+  formModalApi.setData(null).open();
+}
+
+function handleEdit(row: SrmSupplierApi.SupplierVO) {
+  formModalApi.setData(row).open();
+}
+
+async function handleDelete(row: SrmSupplierApi.SupplierVO) {
+  const hideLoading = message.loading({
+    content: $t('ui.actionMessage.deleting', [row.name]),
+    duration: 0,
+  });
+  try {
+    await deleteSupplier(row.id!);
+    message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
+    handleRefresh();
+  } finally {
+    hideLoading();
+  }
+}
+
+const [FormModal, formModalApi] = useVbenModal({
+  connectedComponent: SupplierForm,
+  destroyOnClose: true,
+});
+
+const [Grid, gridApi] = useVbenVxeGrid({
+  formOptions: {
+    schema: useGridFormSchema(),
+  },
+  gridOptions: {
+    columns: useGridColumns(),
+    height: 'auto',
+    keepSource: true,
+    proxyConfig: {
+      ajax: {
+        query: async ({ page }, formValues) => {
+          return await getSupplierPage({
+            pageNo: page.currentPage,
+            pageSize: page.pageSize,
+            ...formValues,
+          });
+        },
+      },
+    },
+    rowConfig: { keyField: 'id', isHover: true },
+    toolbarConfig: { refresh: true, search: true },
+  } as VxeTableGridOptions<SrmSupplierApi.SupplierVO>,
+});
+</script>
+
+<template>
+  <Page auto-content-height>
+    <FormModal @success="handleRefresh" />
+    <Grid table-title="渚涘簲鍟嗗垪琛�">
+      <template #toolbar-tools>
+        <TableAction
+          :actions="[
+            {
+              label: $t('ui.actionTitle.create', ['渚涘簲鍟�']),
+              type: 'primary',
+              icon: ACTION_ICON.ADD,
+              auth: ['srm:supplier:create'],
+              onClick: handleCreate,
+            },
+          ]"
+        />
+      </template>
+      <template #actions="{ row }">
+        <TableAction
+          :actions="[
+            {
+              label: $t('common.edit'),
+              type: 'link',
+              icon: ACTION_ICON.EDIT,
+              auth: ['srm:supplier:update'],
+              onClick: handleEdit.bind(null, row),
+            },
+            {
+              label: $t('common.delete'),
+              type: 'link',
+              danger: true,
+              icon: ACTION_ICON.DELETE,
+              auth: ['srm:supplier:delete'],
+              popConfirm: {
+                title: $t('ui.actionMessage.deleteConfirm', [row.name]),
+                confirm: handleDelete.bind(null, row),
+              },
+            },
+          ]"
+        />
+      </template>
+    </Grid>
+  </Page>
+</template>
diff --git a/src/views/srm/supplier/modules/form.vue b/src/views/srm/supplier/modules/form.vue
new file mode 100644
index 0000000..4542a25
--- /dev/null
+++ b/src/views/srm/supplier/modules/form.vue
@@ -0,0 +1,82 @@
+<script lang="ts" setup>
+import type { SrmSupplierApi } from '#/api/srm/supplier';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import {
+  createSupplier,
+  getSupplier,
+  updateSupplier,
+} from '#/api/srm/supplier';
+import { $t } from '#/locales';
+
+import { useFormSchema } from '../data';
+
+const emit = defineEmits(['success']);
+const formData = ref<SrmSupplierApi.SupplierVO>();
+const getTitle = computed(() => {
+  return formData.value?.id
+    ? $t('ui.actionTitle.edit', ['渚涘簲鍟�'])
+    : $t('ui.actionTitle.create', ['渚涘簲鍟�']);
+});
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: { class: 'w-full' },
+    labelWidth: 120,
+  },
+  wrapperClass: 'grid-cols-2',
+  layout: 'horizontal',
+  schema: useFormSchema(),
+  showDefaultActions: false,
+});
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const data = (await formApi.getValues()) as SrmSupplierApi.SupplierVO;
+    try {
+      await (formData.value?.id ? updateSupplier(data) : createSupplier(data));
+      await modalApi.close();
+      emit('success');
+      message.success($t('ui.actionMessage.operationSuccess'));
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      formData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<SrmSupplierApi.SupplierVO>();
+    if (!data || !data.id) {
+      return;
+    }
+    modalApi.lock();
+    try {
+      formData.value = await getSupplier(data.id);
+      await formApi.setValues(formData.value);
+    } finally {
+      modalApi.unlock();
+    }
+  },
+});
+
+defineExpose({ modalApi });
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-1/2">
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/srm/supplierApply/data.ts b/src/views/srm/supplierApply/data.ts
new file mode 100644
index 0000000..0e172a0
--- /dev/null
+++ b/src/views/srm/supplierApply/data.ts
@@ -0,0 +1,160 @@
+import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { SrmSupplierApi } from '#/api/srm/supplier';
+
+import { markRaw } from 'vue';
+
+import { z } from '#/adapter/form';
+
+import { SrmSupplierSelect } from '#/views/srm/supplier/components';
+
+import { APPLY_STATUS_CELLTAG, APPLY_STATUS_OPTIONS } from '../enums';
+
+/** 鏂板/淇敼鐨勮〃鍗� */
+export function useFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
+  return [
+    {
+      component: 'Input',
+      fieldName: 'id',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'supplierId',
+      label: '閫夋嫨渚涘簲鍟�',
+      component: markRaw(SrmSupplierSelect),
+      componentProps: {
+        approvalFilter: false,
+        placeholder: '璇烽�夋嫨SRM渚涘簲鍟嗭紙鎴栫洿鎺ュ~鍐欎緵搴斿晢鍚嶇О锛�',
+        onChange: async (supplier?: SrmSupplierApi.SupplierVO) => {
+          if (!supplier || !formApi) return;
+          await formApi.setValues({
+            supplierName: supplier.name || '',
+            contactName: supplier.contactName || '',
+            contactMobile: supplier.contactMobile || '',
+            contactEmail: supplier.contactEmail || '',
+            taxNo: supplier.taxNo || '',
+            address: supplier.address || '',
+          });
+        },
+      },
+      dependencies: {
+        triggerFields: ['supplierName'],
+        componentProps: (values) => ({
+          approvalFilter: false,
+          placeholder: '璇烽�夋嫨SRM渚涘簲鍟嗭紙鎴栫洿鎺ュ~鍐欎緵搴斿晢鍚嶇О锛�',
+          supplierName: values.supplierName as string,
+        }),
+      },
+    },
+    {
+      fieldName: 'supplierName',
+      label: '渚涘簲鍟嗗悕绉�',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヤ緵搴斿晢鍚嶇О' },
+    },
+    {
+      fieldName: 'contactName',
+      label: '鑱旂郴浜�',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヨ仈绯讳汉' },
+    },
+    {
+      fieldName: 'contactMobile',
+      label: '鑱旂郴鐢佃瘽',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヨ仈绯荤數璇�' },
+    },
+    {
+      fieldName: 'contactEmail',
+      label: '閭',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ラ偖绠�' },
+    },
+    {
+      fieldName: 'address',
+      label: '鍦板潃',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ュ湴鍧�' },
+      formItemClass: 'col-span-2',
+    },
+    {
+      fieldName: 'bankName',
+      label: '寮�鎴烽摱琛�',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ュ紑鎴烽摱琛�' },
+    },
+    {
+      fieldName: 'bankAccount',
+      label: '閾惰璐﹀彿',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ラ摱琛岃处鍙�' },
+    },
+    {
+      fieldName: 'taxNo',
+      label: '绋庡彿',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ョ◣鍙�' },
+    },
+    {
+      fieldName: 'remark',
+      label: '澶囨敞',
+      component: 'Textarea',
+      componentProps: { placeholder: '璇疯緭鍏ュ娉�', rows: 3 },
+      formItemClass: 'col-span-2',
+    },
+  ];
+}
+
+/** 鎼滅储琛ㄥ崟 */
+export function useGridFormSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'applyNo',
+      label: '鐢宠缂栧彿',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ョ敵璇风紪鍙�', allowClear: true },
+    },
+    {
+      fieldName: 'supplierName',
+      label: '渚涘簲鍟嗗悕绉�',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヤ緵搴斿晢鍚嶇О', allowClear: true },
+    },
+    {
+      fieldName: 'applyStatus',
+      label: '鐘舵��',
+      component: 'Select',
+      componentProps: {
+        placeholder: '璇烽�夋嫨鐘舵��',
+        allowClear: true,
+        options: APPLY_STATUS_OPTIONS,
+      },
+    },
+  ];
+}
+
+/** 琛ㄦ牸鍒� */
+export function useGridColumns(): VxeTableGridOptions['columns'] {
+  return [
+    { field: 'applyNo', title: '鐢宠缂栧彿', minWidth: 140 },
+    { field: 'supplierName', title: '渚涘簲鍟嗗悕绉�', minWidth: 150 },
+    {
+      field: 'applyStatus',
+      title: '鐘舵��',
+      minWidth: 100,
+      cellRender: { name: 'CellTag', props: APPLY_STATUS_CELLTAG },
+    },
+    { field: 'contactName', title: '鑱旂郴浜�', minWidth: 100 },
+    { field: 'contactMobile', title: '鑱旂郴鐢佃瘽', minWidth: 120 },
+    { field: 'createTime', title: '鍒涘缓鏃堕棿', minWidth: 160 },
+    {
+      title: '鎿嶄綔',
+      width: 200,
+      fixed: 'right',
+      slots: { default: 'actions' },
+    },
+  ];
+}
diff --git a/src/views/srm/supplierApply/index.vue b/src/views/srm/supplierApply/index.vue
new file mode 100644
index 0000000..47b8a8f
--- /dev/null
+++ b/src/views/srm/supplierApply/index.vue
@@ -0,0 +1,264 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { SrmSupplierApplyApi } from '#/api/srm/supplierApply';
+
+import { computed, ref } from 'vue';
+
+import { Page, useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
+import {
+  deleteApply,
+  getApplyPage,
+  submitApply,
+  reviewPurchase,
+  reviewQuality,
+  reviewFinance,
+} from '#/api/srm/supplierApply';
+import { $t } from '#/locales';
+
+import { useGridColumns, useGridFormSchema } from './data';
+import ApplyForm from './modules/form.vue';
+import { APPLY_STATUS } from '../enums';
+
+defineOptions({ name: 'SrmSupplierApply' });
+
+// ---------- reviewModal ----------
+const reviewFormData = ref({
+  applyId: 0,
+  reviewType: '' as 'purchase' | 'quality' | 'finance',
+  passed: true,
+  comment: '',
+});
+const reviewTitle = computed(() => {
+  const labels: Record<string, string> = {
+    purchase: '閲囪喘瀹℃牳',
+    quality: '璐ㄩ噺瀹℃牳',
+    finance: '璐㈠姟瀹℃牳',
+  };
+  return labels[reviewFormData.value.reviewType] || '瀹℃牳';
+});
+
+const [ReviewForm, reviewFormApi] = useVbenForm({
+  commonConfig: {
+    componentProps: { class: 'w-full' },
+    labelWidth: 100,
+  },
+  wrapperClass: 'grid-cols-1',
+  layout: 'horizontal',
+  schema: [
+    {
+      fieldName: 'passed',
+      label: '瀹℃牳缁撴灉',
+      component: 'RadioGroup',
+      rules: 'required',
+      componentProps: {
+        options: [
+          { label: '閫氳繃', value: true },
+          { label: '鎷掔粷', value: false },
+        ],
+        buttonStyle: 'solid',
+        optionType: 'button',
+      },
+    },
+    {
+      fieldName: 'comment',
+      label: '瀹℃牳鎰忚',
+      component: 'Textarea',
+      componentProps: { placeholder: '璇疯緭鍏ュ鏍告剰瑙�', rows: 3 },
+    },
+  ],
+  showDefaultActions: false,
+});
+
+const [ReviewModal, reviewModalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await reviewFormApi.validate();
+    if (!valid) {
+      return;
+    }
+    reviewModalApi.lock();
+    const data = (await reviewFormApi.getValues()) as { passed: boolean; comment?: string };
+    try {
+      const payload: SrmSupplierApplyApi.ReviewVO = {
+        id: reviewFormData.value.applyId,
+        passed: data.passed,
+        comment: data.comment,
+      };
+      if (reviewFormData.value.reviewType === 'purchase') {
+        await reviewPurchase(payload);
+      } else if (reviewFormData.value.reviewType === 'quality') {
+        await reviewQuality(payload);
+      } else if (reviewFormData.value.reviewType === 'finance') {
+        await reviewFinance(payload);
+      }
+      await reviewModalApi.close();
+      handleRefresh();
+      message.success($t('ui.actionMessage.operationSuccess'));
+    } finally {
+      reviewModalApi.unlock();
+    }
+  },
+});
+
+function openReviewModal(row: SrmSupplierApplyApi.ApplyVO, reviewType: 'purchase' | 'quality' | 'finance') {
+  reviewFormData.value = {
+    applyId: row.id!,
+    reviewType,
+    passed: true,
+    comment: '',
+  };
+  reviewFormApi.resetForm();
+  reviewModalApi.open();
+}
+
+// ---------- grid ----------
+function handleRefresh() {
+  gridApi.query();
+}
+
+function handleCreate() {
+  formModalApi.setData(null).open();
+}
+
+function handleEdit(row: SrmSupplierApplyApi.ApplyVO) {
+  formModalApi.setData(row).open();
+}
+
+async function handleDelete(row: SrmSupplierApplyApi.ApplyVO) {
+  const hideLoading = message.loading({
+    content: $t('ui.actionMessage.deleting', [row.supplierName]),
+    duration: 0,
+  });
+  try {
+    await deleteApply(row.id!);
+    message.success($t('ui.actionMessage.deleteSuccess', [row.supplierName]));
+    handleRefresh();
+  } finally {
+    hideLoading();
+  }
+}
+
+async function handleSubmit(row: SrmSupplierApplyApi.ApplyVO) {
+  const hideLoading = message.loading({ content: '鎻愪氦涓�...', duration: 0 });
+  try {
+    await submitApply(row.id!);
+    message.success($t('ui.actionMessage.operationSuccess'));
+    handleRefresh();
+  } finally {
+    hideLoading();
+  }
+}
+
+const [FormModal, formModalApi] = useVbenModal({
+  connectedComponent: ApplyForm,
+  destroyOnClose: true,
+});
+
+const [Grid, gridApi] = useVbenVxeGrid({
+  formOptions: {
+    schema: useGridFormSchema(),
+  },
+  gridOptions: {
+    columns: useGridColumns(),
+    height: 'auto',
+    keepSource: true,
+    proxyConfig: {
+      ajax: {
+        query: async ({ page }, formValues) => {
+          return await getApplyPage({
+            pageNo: page.currentPage,
+            pageSize: page.pageSize,
+            ...formValues,
+          });
+        },
+      },
+    },
+    rowConfig: { keyField: 'id', isHover: true },
+    toolbarConfig: { refresh: true, search: true },
+  } as VxeTableGridOptions<SrmSupplierApplyApi.ApplyVO>,
+});
+</script>
+
+<template>
+  <Page auto-content-height>
+    <FormModal @success="handleRefresh" />
+    <ReviewModal :title="reviewTitle" class="w-1/3">
+      <ReviewForm class="mx-4" />
+    </ReviewModal>
+    <Grid table-title="鍑嗗叆鐢宠鍒楄〃">
+      <template #toolbar-tools>
+        <TableAction
+          :actions="[
+            {
+              label: $t('ui.actionTitle.create', ['鍑嗗叆鐢宠']),
+              type: 'primary',
+              icon: ACTION_ICON.ADD,
+              auth: ['srm:supplier-apply:create'],
+              onClick: handleCreate,
+            },
+          ]"
+        />
+      </template>
+      <template #actions="{ row }">
+        <TableAction
+          :actions="[
+            {
+              label: $t('common.edit'),
+              type: 'link',
+              icon: ACTION_ICON.EDIT,
+              auth: ['srm:supplier-apply:update'],
+              onClick: handleEdit.bind(null, row),
+            },
+            {
+              label: '鎻愪氦',
+              type: 'link',
+              icon: ACTION_ICON.CHECK,
+              auth: ['srm:supplier-apply:update'],
+              ifShow: row.applyStatus === APPLY_STATUS.DRAFT,
+              onClick: handleSubmit.bind(null, row),
+            },
+            {
+              label: '閲囪喘瀹℃牳',
+              type: 'link',
+              icon: ACTION_ICON.CHECK,
+              auth: ['srm:supplier-apply:update'],
+              ifShow: row.applyStatus === APPLY_STATUS.PENDING_PURCHASE,
+              onClick: openReviewModal.bind(null, row, 'purchase'),
+            },
+            {
+              label: '璐ㄩ噺瀹℃牳',
+              type: 'link',
+              icon: ACTION_ICON.CHECK,
+              auth: ['srm:supplier-apply:update'],
+              ifShow: row.applyStatus === APPLY_STATUS.PENDING_QUALITY,
+              onClick: openReviewModal.bind(null, row, 'quality'),
+            },
+            {
+              label: '璐㈠姟瀹℃牳',
+              type: 'link',
+              icon: ACTION_ICON.CHECK,
+              auth: ['srm:supplier-apply:update'],
+              ifShow: row.applyStatus === APPLY_STATUS.PENDING_FINANCE,
+              onClick: openReviewModal.bind(null, row, 'finance'),
+            },
+            {
+              label: $t('common.delete'),
+              type: 'link',
+              danger: true,
+              icon: ACTION_ICON.DELETE,
+              auth: ['srm:supplier-apply:delete'],
+              popConfirm: {
+                title: $t('ui.actionMessage.deleteConfirm', [row.supplierName]),
+                confirm: handleDelete.bind(null, row),
+              },
+            },
+          ]"
+        />
+      </template>
+    </Grid>
+  </Page>
+</template>
diff --git a/src/views/srm/supplierApply/modules/form.vue b/src/views/srm/supplierApply/modules/form.vue
new file mode 100644
index 0000000..22305a4
--- /dev/null
+++ b/src/views/srm/supplierApply/modules/form.vue
@@ -0,0 +1,84 @@
+<script lang="ts" setup>
+import type { SrmSupplierApplyApi } from '#/api/srm/supplierApply';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import {
+  createApply,
+  getApply,
+  updateApply,
+} from '#/api/srm/supplierApply';
+import { $t } from '#/locales';
+
+import { useFormSchema } from '../data';
+
+const emit = defineEmits(['success']);
+const formData = ref<SrmSupplierApplyApi.ApplyVO>();
+const getTitle = computed(() => {
+  return formData.value?.id
+    ? $t('ui.actionTitle.edit', ['鍑嗗叆鐢宠'])
+    : $t('ui.actionTitle.create', ['鍑嗗叆鐢宠']);
+});
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: { class: 'w-full' },
+    labelWidth: 120,
+  },
+  wrapperClass: 'grid-cols-2',
+  layout: 'horizontal',
+  schema: useFormSchema(),
+  showDefaultActions: false,
+});
+// 娉ㄥ叆 formApi 浠ユ敮鎸侀�夋嫨 SRM 渚涘簲鍟嗗悗鑷姩濉厖琛ㄥ崟瀛楁
+formApi.setState({ schema: useFormSchema(formApi) });
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const data = (await formApi.getValues()) as SrmSupplierApplyApi.ApplyVO;
+    try {
+      await (formData.value?.id ? updateApply(data) : createApply(data));
+      await modalApi.close();
+      emit('success');
+      message.success($t('ui.actionMessage.operationSuccess'));
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      formData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<SrmSupplierApplyApi.ApplyVO>();
+    if (!data || !data.id) {
+      return;
+    }
+    modalApi.lock();
+    try {
+      formData.value = await getApply(data.id);
+      await formApi.setValues(formData.value);
+    } finally {
+      modalApi.unlock();
+    }
+  },
+});
+
+defineExpose({ modalApi });
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-1/2">
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/srm/supplierCertificate/data.ts b/src/views/srm/supplierCertificate/data.ts
new file mode 100644
index 0000000..34a7cde
--- /dev/null
+++ b/src/views/srm/supplierCertificate/data.ts
@@ -0,0 +1,153 @@
+import type { VbenFormSchema } from '#/adapter/form';
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+
+import { getSupplierSimpleList } from '#/api/srm/supplier';
+
+import { CERTIFICATE_TYPE_CELLTAG, CERTIFICATE_TYPE_OPTIONS } from '../enums';
+
+/** 鏂板/淇敼鐨勮〃鍗� */
+export function useFormSchema(): VbenFormSchema[] {
+  return [
+    {
+      component: 'Input',
+      fieldName: 'id',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'supplierId',
+      label: '渚涘簲鍟�',
+      component: 'ApiSelect',
+      componentProps: {
+        placeholder: '璇烽�夋嫨渚涘簲鍟�',
+        allowClear: true,
+        showSearch: true,
+        api: getSupplierSimpleList,
+        labelField: 'name',
+        valueField: 'id',
+      },
+      rules: 'required',
+    },
+    {
+      fieldName: 'certificateName',
+      label: '璧勮川璇佷功鍚嶇О',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヨ祫璐ㄨ瘉涔﹀悕绉�' },
+      rules: 'required',
+    },
+    {
+      fieldName: 'certificateNo',
+      label: '璇佷功缂栧彿',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヨ瘉涔︾紪鍙�' },
+    },
+    {
+      fieldName: 'certificateType',
+      label: '璇佷功绫诲瀷',
+      component: 'Select',
+      componentProps: {
+        placeholder: '璇烽�夋嫨璇佷功绫诲瀷',
+        options: CERTIFICATE_TYPE_OPTIONS,
+      },
+      rules: 'required',
+    },
+    {
+      fieldName: 'issuingAuthority',
+      label: '棰佸彂鏈烘瀯',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ラ鍙戞満鏋�' },
+    },
+    {
+      fieldName: 'issueDate',
+      label: '棰佸彂鏃ユ湡',
+      component: 'DatePicker',
+      componentProps: {
+        placeholder: '璇烽�夋嫨棰佸彂鏃ユ湡',
+        allowClear: true,
+        format: 'YYYY-MM-DD',
+        valueFormat: 'YYYY-MM-DD',
+      },
+    },
+    {
+      fieldName: 'expireDate',
+      label: '鍒版湡鏃ユ湡',
+      component: 'DatePicker',
+      componentProps: {
+        placeholder: '璇烽�夋嫨鍒版湡鏃ユ湡',
+        allowClear: true,
+        format: 'YYYY-MM-DD',
+        valueFormat: 'YYYY-MM-DD',
+      },
+      rules: 'required',
+    },
+    {
+      fieldName: 'remark',
+      label: '澶囨敞',
+      component: 'Textarea',
+      componentProps: { placeholder: '璇疯緭鍏ュ娉�', rows: 3 },
+      formItemClass: 'col-span-2',
+    },
+  ];
+}
+
+/** 鎼滅储琛ㄥ崟 */
+export function useGridFormSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'certificateName',
+      label: '璧勮川璇佷功鍚嶇О',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヨ祫璐ㄨ瘉涔﹀悕绉�', allowClear: true },
+    },
+    {
+      fieldName: 'certificateType',
+      label: '璇佷功绫诲瀷',
+      component: 'Select',
+      componentProps: {
+        placeholder: '璇烽�夋嫨璇佷功绫诲瀷',
+        allowClear: true,
+        options: CERTIFICATE_TYPE_OPTIONS,
+      },
+    },
+    {
+      fieldName: 'supplierId',
+      label: '渚涘簲鍟�',
+      component: 'ApiSelect',
+      componentProps: {
+        placeholder: '璇烽�夋嫨渚涘簲鍟�',
+        allowClear: true,
+        showSearch: true,
+        api: getSupplierSimpleList,
+        labelField: 'name',
+        valueField: 'id',
+      },
+    },
+  ];
+}
+
+/** 琛ㄦ牸鍒� */
+export function useGridColumns(): VxeTableGridOptions['columns'] {
+  return [
+    { field: 'supplierName', title: '渚涘簲鍟�', minWidth: 120 },
+    { field: 'certificateName', title: '璧勮川璇佷功鍚嶇О', minWidth: 150 },
+    { field: 'certificateNo', title: '璇佷功缂栧彿', minWidth: 120 },
+    {
+      field: 'certificateType',
+      title: '璇佷功绫诲瀷',
+      minWidth: 120,
+      cellRender: { name: 'CellTag', props: CERTIFICATE_TYPE_CELLTAG },
+    },
+    { field: 'issuingAuthority', title: '棰佸彂鏈烘瀯', minWidth: 120 },
+    { field: 'issueDate', title: '棰佸彂鏃ユ湡', minWidth: 120 },
+    { field: 'expireDate', title: '鍒版湡鏃ユ湡', minWidth: 120 },
+    { field: 'createTime', title: '鍒涘缓鏃堕棿', minWidth: 160 },
+    {
+      title: '鎿嶄綔',
+      width: 130,
+      fixed: 'right',
+      slots: { default: 'actions' },
+    },
+  ];
+}
diff --git a/src/views/srm/supplierCertificate/index.vue b/src/views/srm/supplierCertificate/index.vue
new file mode 100644
index 0000000..29ea03f
--- /dev/null
+++ b/src/views/srm/supplierCertificate/index.vue
@@ -0,0 +1,141 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { SrmSupplierCertificateApi } from '#/api/srm/supplierCertificate';
+
+import { Page, useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
+import {
+  deleteCertificate,
+  getCertificatePage,
+  getExpiringCertificates,
+} from '#/api/srm/supplierCertificate';
+import { $t } from '#/locales';
+
+import { useGridColumns, useGridFormSchema } from './data';
+import CertificateForm from './modules/form.vue';
+
+defineOptions({ name: 'SrmSupplierCertificate' });
+
+function handleRefresh() {
+  gridApi.query();
+}
+
+function handleCreate() {
+  formModalApi.setData(null).open();
+}
+
+function handleEdit(row: SrmSupplierCertificateApi.CertificateVO) {
+  formModalApi.setData(row).open();
+}
+
+async function handleDelete(row: SrmSupplierCertificateApi.CertificateVO) {
+  const hideLoading = message.loading({
+    content: $t('ui.actionMessage.deleting', [row.certificateName]),
+    duration: 0,
+  });
+  try {
+    await deleteCertificate(row.id!);
+    message.success($t('ui.actionMessage.deleteSuccess', [row.certificateName]));
+    handleRefresh();
+  } finally {
+    hideLoading();
+  }
+}
+
+async function handleExpiringCertificates() {
+  try {
+    const data = await getExpiringCertificates();
+    if (data && data.length > 0) {
+      message.warning(`鏈� ${data.length} 涓祫璐ㄨ瘉涔﹀嵆灏嗗埌鏈焋);
+    } else {
+      message.success('娌℃湁鍗冲皢鍒版湡鐨勮祫璐ㄨ瘉涔�');
+    }
+  } catch {
+    message.error('鑾峰彇鍗冲皢鍒版湡鐨勮祫璐ㄨ瘉涔﹀け璐�');
+  }
+}
+
+const [FormModal, formModalApi] = useVbenModal({
+  connectedComponent: CertificateForm,
+  destroyOnClose: true,
+});
+
+const [Grid, gridApi] = useVbenVxeGrid({
+  formOptions: {
+    schema: useGridFormSchema(),
+  },
+  gridOptions: {
+    columns: useGridColumns(),
+    height: 'auto',
+    keepSource: true,
+    proxyConfig: {
+      ajax: {
+        query: async ({ page }, formValues) => {
+          return await getCertificatePage({
+            pageNo: page.currentPage,
+            pageSize: page.pageSize,
+            ...formValues,
+          });
+        },
+      },
+    },
+    rowConfig: { keyField: 'id', isHover: true },
+    toolbarConfig: { refresh: true, search: true },
+  } as VxeTableGridOptions<SrmSupplierCertificateApi.CertificateVO>,
+});
+</script>
+
+<template>
+  <Page auto-content-height>
+    <FormModal @success="handleRefresh" />
+    <Grid table-title="璧勮川璇佷功鍒楄〃">
+      <template #toolbar-tools>
+        <TableAction
+          :actions="[
+            {
+              label: $t('ui.actionTitle.create', ['璧勮川璇佷功']),
+              type: 'primary',
+              icon: ACTION_ICON.ADD,
+              auth: ['srm:supplier-certificate:create'],
+              onClick: handleCreate,
+            },
+            {
+              label: '鍒版湡鎻愰啋',
+              type: 'warning',
+              icon: 'ant-design:bell-outlined',
+              auth: ['srm:supplier-certificate:query'],
+              onClick: handleExpiringCertificates,
+            },
+          ]"
+        />
+      </template>
+      <template #actions="{ row }">
+        <TableAction
+          :actions="[
+            {
+              label: $t('common.edit'),
+              type: 'link',
+              icon: ACTION_ICON.EDIT,
+              auth: ['srm:supplier-certificate:update'],
+              onClick: handleEdit.bind(null, row),
+            },
+            {
+              label: $t('common.delete'),
+              type: 'link',
+              danger: true,
+              icon: ACTION_ICON.DELETE,
+              auth: ['srm:supplier-certificate:delete'],
+              popConfirm: {
+                title: $t('ui.actionMessage.deleteConfirm', [row.certificateName]),
+                confirm: handleDelete.bind(null, row),
+              },
+            },
+          ]"
+        />
+      </template>
+    </Grid>
+  </Page>
+</template>
diff --git a/src/views/srm/supplierCertificate/modules/form.vue b/src/views/srm/supplierCertificate/modules/form.vue
new file mode 100644
index 0000000..d0bc05b
--- /dev/null
+++ b/src/views/srm/supplierCertificate/modules/form.vue
@@ -0,0 +1,82 @@
+<script lang="ts" setup>
+import type { SrmSupplierCertificateApi } from '#/api/srm/supplierCertificate';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import {
+  createCertificate,
+  getCertificate,
+  updateCertificate,
+} from '#/api/srm/supplierCertificate';
+import { $t } from '#/locales';
+
+import { useFormSchema } from '../data';
+
+const emit = defineEmits(['success']);
+const formData = ref<SrmSupplierCertificateApi.CertificateVO>();
+const getTitle = computed(() => {
+  return formData.value?.id
+    ? $t('ui.actionTitle.edit', ['璧勮川璇佷功'])
+    : $t('ui.actionTitle.create', ['璧勮川璇佷功']);
+});
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: { class: 'w-full' },
+    labelWidth: 120,
+  },
+  wrapperClass: 'grid-cols-2',
+  layout: 'horizontal',
+  schema: useFormSchema(),
+  showDefaultActions: false,
+});
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const data = (await formApi.getValues()) as SrmSupplierCertificateApi.CertificateVO;
+    try {
+      await (formData.value?.id ? updateCertificate(data) : createCertificate(data));
+      await modalApi.close();
+      emit('success');
+      message.success($t('ui.actionMessage.operationSuccess'));
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      formData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<SrmSupplierCertificateApi.CertificateVO>();
+    if (!data || !data.id) {
+      return;
+    }
+    modalApi.lock();
+    try {
+      formData.value = await getCertificate(data.id);
+      await formApi.setValues(formData.value);
+    } finally {
+      modalApi.unlock();
+    }
+  },
+});
+
+defineExpose({ modalApi });
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-1/2">
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/srm/supplierEvaluation/data.ts b/src/views/srm/supplierEvaluation/data.ts
new file mode 100644
index 0000000..4a20888
--- /dev/null
+++ b/src/views/srm/supplierEvaluation/data.ts
@@ -0,0 +1,132 @@
+import type { VbenFormSchema } from '#/adapter/form';
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+
+import { getSupplierSimpleList } from '#/api/srm/supplier';
+
+import { COMPOSITE_SCORE_CELLTAG, EVALUATION_GRADE_CELLTAG } from '../enums';
+
+/** 鏂板/淇敼鐨勮〃鍗� */
+export function useFormSchema(): VbenFormSchema[] {
+  return [
+    {
+      component: 'Input',
+      fieldName: 'id',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'supplierId',
+      label: '渚涘簲鍟�',
+      component: 'ApiSelect',
+      rules: 'required',
+      componentProps: {
+        placeholder: '璇烽�夋嫨渚涘簲鍟�',
+        allowClear: true,
+        showSearch: true,
+        api: getSupplierSimpleList,
+        labelField: 'name',
+        valueField: 'id',
+      },
+    },
+    {
+      fieldName: 'evaluationPeriod',
+      label: '璇勪环鍛ㄦ湡',
+      component: 'Input',
+      rules: 'required',
+      componentProps: { placeholder: '濡� 2024-Q1' },
+    },
+    {
+      fieldName: 'qualityScore',
+      label: '璐ㄩ噺璇勫垎',
+      component: 'InputNumber',
+      rules: 'required',
+      componentProps: { min: 0, max: 100, placeholder: '璇疯緭鍏ヨ川閲忚瘎鍒�' },
+    },
+    {
+      fieldName: 'deliveryScore',
+      label: '浜や粯璇勫垎',
+      component: 'InputNumber',
+      rules: 'required',
+      componentProps: { min: 0, max: 100, placeholder: '璇疯緭鍏ヤ氦浠樿瘎鍒�' },
+    },
+    {
+      fieldName: 'priceScore',
+      label: '浠锋牸璇勫垎',
+      component: 'InputNumber',
+      rules: 'required',
+      componentProps: { min: 0, max: 100, placeholder: '璇疯緭鍏ヤ环鏍艰瘎鍒�' },
+    },
+    {
+      fieldName: 'serviceScore',
+      label: '鏈嶅姟璇勫垎',
+      component: 'InputNumber',
+      rules: 'required',
+      componentProps: { min: 0, max: 100, placeholder: '璇疯緭鍏ユ湇鍔¤瘎鍒�' },
+    },
+    {
+      fieldName: 'remark',
+      label: '璇勪环璇存槑',
+      component: 'Textarea',
+      componentProps: { placeholder: '璇疯緭鍏ヨ瘎浠疯鏄�', rows: 3 },
+      formItemClass: 'col-span-2',
+    },
+  ];
+}
+
+/** 鎼滅储琛ㄥ崟 */
+export function useGridFormSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'supplierId',
+      label: '渚涘簲鍟�',
+      component: 'ApiSelect',
+      componentProps: {
+        placeholder: '璇烽�夋嫨渚涘簲鍟�',
+        allowClear: true,
+        showSearch: true,
+        api: getSupplierSimpleList,
+        labelField: 'name',
+        valueField: 'id',
+      },
+    },
+    {
+      fieldName: 'evaluationPeriod',
+      label: '璇勪环鍛ㄦ湡',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヨ瘎浠峰懆鏈�', allowClear: true },
+    },
+  ];
+}
+
+/** 琛ㄦ牸鍒� */
+export function useGridColumns(): VxeTableGridOptions['columns'] {
+  return [
+    { field: 'supplierName', title: '渚涘簲鍟�', minWidth: 150 },
+    { field: 'evaluationPeriod', title: '璇勪环鍛ㄦ湡', minWidth: 120 },
+    { field: 'qualityScore', title: '璐ㄩ噺璇勫垎', minWidth: 100 },
+    { field: 'deliveryScore', title: '浜や粯璇勫垎', minWidth: 100 },
+    { field: 'priceScore', title: '浠锋牸璇勫垎', minWidth: 100 },
+    { field: 'serviceScore', title: '鏈嶅姟璇勫垎', minWidth: 100 },
+    {
+      field: 'compositeScore',
+      title: '缁煎悎璇勫垎',
+      minWidth: 120,
+      cellRender: { name: 'CellTag', props: COMPOSITE_SCORE_CELLTAG },
+    },
+    {
+      field: 'grade',
+      title: '绛夌骇',
+      minWidth: 80,
+      cellRender: { name: 'CellTag', props: EVALUATION_GRADE_CELLTAG },
+    },
+    { field: 'createTime', title: '鍒涘缓鏃堕棿', minWidth: 160 },
+    {
+      title: '鎿嶄綔',
+      width: 130,
+      fixed: 'right',
+      slots: { default: 'actions' },
+    },
+  ];
+}
diff --git a/src/views/srm/supplierEvaluation/index.vue b/src/views/srm/supplierEvaluation/index.vue
new file mode 100644
index 0000000..0bc2675
--- /dev/null
+++ b/src/views/srm/supplierEvaluation/index.vue
@@ -0,0 +1,120 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { SrmSupplierEvaluationApi } from '#/api/srm/supplierEvaluation';
+
+import { Page, useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
+import {
+  deleteEvaluation,
+  getEvaluationPage,
+} from '#/api/srm/supplierEvaluation';
+import { $t } from '#/locales';
+
+import { useGridColumns, useGridFormSchema } from './data';
+import EvaluationForm from './modules/form.vue';
+
+defineOptions({ name: 'SrmSupplierEvaluation' });
+
+function handleRefresh() {
+  gridApi.query();
+}
+
+function handleCreate() {
+  formModalApi.setData(null).open();
+}
+
+function handleEdit(row: SrmSupplierEvaluationApi.EvaluationVO) {
+  formModalApi.setData(row).open();
+}
+
+async function handleDelete(row: SrmSupplierEvaluationApi.EvaluationVO) {
+  const hideLoading = message.loading({
+    content: $t('ui.actionMessage.deleting', [row.evaluationPeriod]),
+    duration: 0,
+  });
+  try {
+    await deleteEvaluation(row.id!);
+    message.success($t('ui.actionMessage.deleteSuccess', [row.evaluationPeriod]));
+    handleRefresh();
+  } finally {
+    hideLoading();
+  }
+}
+
+const [FormModal, formModalApi] = useVbenModal({
+  connectedComponent: EvaluationForm,
+  destroyOnClose: true,
+});
+
+const [Grid, gridApi] = useVbenVxeGrid({
+  formOptions: {
+    schema: useGridFormSchema(),
+  },
+  gridOptions: {
+    columns: useGridColumns(),
+    height: 'auto',
+    keepSource: true,
+    proxyConfig: {
+      ajax: {
+        query: async ({ page }, formValues) => {
+          return await getEvaluationPage({
+            pageNo: page.currentPage,
+            pageSize: page.pageSize,
+            ...formValues,
+          });
+        },
+      },
+    },
+    rowConfig: { keyField: 'id', isHover: true },
+    toolbarConfig: { refresh: true, search: true },
+  } as VxeTableGridOptions<SrmSupplierEvaluationApi.EvaluationVO>,
+});
+</script>
+
+<template>
+  <Page auto-content-height>
+    <FormModal @success="handleRefresh" />
+    <Grid table-title="缁╂晥璇勪环鍒楄〃">
+      <template #toolbar-tools>
+        <TableAction
+          :actions="[
+            {
+              label: $t('ui.actionTitle.create', ['缁╂晥璇勪环']),
+              type: 'primary',
+              icon: ACTION_ICON.ADD,
+              auth: ['srm:supplier-evaluation:create'],
+              onClick: handleCreate,
+            },
+          ]"
+        />
+      </template>
+      <template #actions="{ row }">
+        <TableAction
+          :actions="[
+            {
+              label: $t('common.edit'),
+              type: 'link',
+              icon: ACTION_ICON.EDIT,
+              auth: ['srm:supplier-evaluation:update'],
+              onClick: handleEdit.bind(null, row),
+            },
+            {
+              label: $t('common.delete'),
+              type: 'link',
+              danger: true,
+              icon: ACTION_ICON.DELETE,
+              auth: ['srm:supplier-evaluation:delete'],
+              popConfirm: {
+                title: $t('ui.actionMessage.deleteConfirm', [row.evaluationPeriod]),
+                confirm: handleDelete.bind(null, row),
+              },
+            },
+          ]"
+        />
+      </template>
+    </Grid>
+  </Page>
+</template>
diff --git a/src/views/srm/supplierEvaluation/modules/form.vue b/src/views/srm/supplierEvaluation/modules/form.vue
new file mode 100644
index 0000000..d29aa1d
--- /dev/null
+++ b/src/views/srm/supplierEvaluation/modules/form.vue
@@ -0,0 +1,82 @@
+<script lang="ts" setup>
+import type { SrmSupplierEvaluationApi } from '#/api/srm/supplierEvaluation';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import {
+  createEvaluation,
+  getEvaluation,
+  updateEvaluation,
+} from '#/api/srm/supplierEvaluation';
+import { $t } from '#/locales';
+
+import { useFormSchema } from '../data';
+
+const emit = defineEmits(['success']);
+const formData = ref<SrmSupplierEvaluationApi.EvaluationVO>();
+const getTitle = computed(() => {
+  return formData.value?.id
+    ? $t('ui.actionTitle.edit', ['缁╂晥璇勪环'])
+    : $t('ui.actionTitle.create', ['缁╂晥璇勪环']);
+});
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: { class: 'w-full' },
+    labelWidth: 120,
+  },
+  wrapperClass: 'grid-cols-2',
+  layout: 'horizontal',
+  schema: useFormSchema(),
+  showDefaultActions: false,
+});
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const data = (await formApi.getValues()) as SrmSupplierEvaluationApi.EvaluationVO;
+    try {
+      await (formData.value?.id ? updateEvaluation(data) : createEvaluation(data));
+      await modalApi.close();
+      emit('success');
+      message.success($t('ui.actionMessage.operationSuccess'));
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      formData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<SrmSupplierEvaluationApi.EvaluationVO>();
+    if (!data || !data.id) {
+      return;
+    }
+    modalApi.lock();
+    try {
+      formData.value = await getEvaluation(data.id);
+      await formApi.setValues(formData.value);
+    } finally {
+      modalApi.unlock();
+    }
+  },
+});
+
+defineExpose({ modalApi });
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-1/2">
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/srm/tender/data.ts b/src/views/srm/tender/data.ts
new file mode 100644
index 0000000..d0bb157
--- /dev/null
+++ b/src/views/srm/tender/data.ts
@@ -0,0 +1,188 @@
+import type { VbenFormSchema } from '#/adapter/form';
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+
+import {
+  TENDER_STATUS_CELLTAG,
+  TENDER_STATUS_OPTIONS,
+  TENDER_TYPE_CELLTAG,
+  TENDER_TYPE_OPTIONS,
+} from '../enums';
+
+/** 鏂板/淇敼鐨勮〃鍗� */
+export function useFormSchema(): VbenFormSchema[] {
+  return [
+    {
+      component: 'Input',
+      fieldName: 'id',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'tenderNo',
+      label: '鎷涙爣缂栧彿',
+      component: 'Input',
+      rules: 'required',
+      componentProps: { placeholder: '璇疯緭鍏ユ嫑鏍囩紪鍙�' },
+    },
+    {
+      fieldName: 'tenderName',
+      label: '鎷涙爣椤圭洰鍚嶇О',
+      component: 'Input',
+      rules: 'required',
+      componentProps: { placeholder: '璇疯緭鍏ユ嫑鏍囬」鐩悕绉�' },
+    },
+    {
+      fieldName: 'tenderType',
+      label: '鎷涙爣绫诲瀷',
+      component: 'Select',
+      rules: 'required',
+      componentProps: {
+        placeholder: '璇烽�夋嫨鎷涙爣绫诲瀷',
+        options: TENDER_TYPE_OPTIONS,
+      },
+    },
+    {
+      fieldName: 'purchaseOrg',
+      label: '閲囪喘缁勭粐',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ラ噰璐粍缁�' },
+    },
+    {
+      fieldName: 'projectLeaderName',
+      label: '椤圭洰璐熻矗浜�',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ラ」鐩礋璐d汉' },
+    },
+    {
+      fieldName: 'budgetAmount',
+      label: '棰勭畻閲戦',
+      component: 'InputNumber',
+      componentProps: {
+        class: '!w-full',
+        min: 0,
+        placeholder: '璇疯緭鍏ラ绠楅噾棰�',
+      },
+    },
+    {
+      fieldName: 'startTime',
+      label: '寮�濮嬫椂闂�',
+      component: 'DatePicker',
+      componentProps: {
+        class: '!w-full',
+        placeholder: '璇烽�夋嫨寮�濮嬫椂闂�',
+        showTime: true,
+        format: 'YYYY-MM-DD HH:mm:ss',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
+      },
+    },
+    {
+      fieldName: 'endTime',
+      label: '鎴鏃堕棿',
+      component: 'DatePicker',
+      componentProps: {
+        class: '!w-full',
+        placeholder: '璇烽�夋嫨鎴鏃堕棿',
+        showTime: true,
+        format: 'YYYY-MM-DD HH:mm:ss',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
+      },
+    },
+    {
+      fieldName: 'bidOpenTime',
+      label: '寮�鏍囨椂闂�',
+      component: 'DatePicker',
+      componentProps: {
+        class: '!w-full',
+        placeholder: '璇烽�夋嫨寮�鏍囨椂闂�',
+        showTime: true,
+        format: 'YYYY-MM-DD HH:mm:ss',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
+      },
+    },
+    {
+      fieldName: 'tenderDesc',
+      label: '鎷涙爣璇存槑',
+      component: 'Textarea',
+      componentProps: { placeholder: '璇疯緭鍏ユ嫑鏍囪鏄�', rows: 3 },
+      formItemClass: 'col-span-2',
+    },
+    {
+      fieldName: 'qualificationRequirements',
+      label: '璧勮川瑕佹眰',
+      component: 'Textarea',
+      componentProps: { placeholder: '璇疯緭鍏ヨ祫璐ㄨ姹�', rows: 3 },
+      formItemClass: 'col-span-2',
+    },
+  ];
+}
+
+/** 鎼滅储琛ㄥ崟 */
+export function useGridFormSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'tenderNo',
+      label: '鎷涙爣缂栧彿',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ユ嫑鏍囩紪鍙�', allowClear: true },
+    },
+    {
+      fieldName: 'tenderName',
+      label: '鎷涙爣椤圭洰鍚嶇О',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ユ嫑鏍囬」鐩悕绉�', allowClear: true },
+    },
+    {
+      fieldName: 'tenderType',
+      label: '鎷涙爣绫诲瀷',
+      component: 'Select',
+      componentProps: {
+        placeholder: '璇烽�夋嫨鎷涙爣绫诲瀷',
+        allowClear: true,
+        options: TENDER_TYPE_OPTIONS,
+      },
+    },
+    {
+      fieldName: 'tenderStatus',
+      label: '鐘舵��',
+      component: 'Select',
+      componentProps: {
+        placeholder: '璇烽�夋嫨鐘舵��',
+        allowClear: true,
+        options: TENDER_STATUS_OPTIONS,
+      },
+    },
+  ];
+}
+
+/** 琛ㄦ牸鍒� */
+export function useGridColumns(): VxeTableGridOptions['columns'] {
+  return [
+    { field: 'tenderNo', title: '鎷涙爣缂栧彿', minWidth: 140 },
+    { field: 'tenderName', title: '鎷涙爣椤圭洰鍚嶇О', minWidth: 180 },
+    {
+      field: 'tenderType',
+      title: '鎷涙爣绫诲瀷',
+      minWidth: 120,
+      cellRender: { name: 'CellTag', props: TENDER_TYPE_CELLTAG },
+    },
+    {
+      field: 'tenderStatus',
+      title: '鐘舵��',
+      minWidth: 100,
+      cellRender: { name: 'CellTag', props: TENDER_STATUS_CELLTAG },
+    },
+    { field: 'budgetAmount', title: '棰勭畻閲戦', minWidth: 120 },
+    { field: 'purchaseOrg', title: '閲囪喘缁勭粐', minWidth: 100 },
+    { field: 'startTime', title: '寮�濮嬫椂闂�', minWidth: 160 },
+    { field: 'endTime', title: '鎴鏃堕棿', minWidth: 160 },
+    { field: 'purchaseOrderNo', title: '閲囪喘璁㈠崟鍙�', minWidth: 140 },
+    {
+      title: '鎿嶄綔',
+      width: 250,
+      fixed: 'right',
+      slots: { default: 'actions' },
+    },
+  ];
+}
diff --git a/src/views/srm/tender/detail.vue b/src/views/srm/tender/detail.vue
new file mode 100644
index 0000000..4b34190
--- /dev/null
+++ b/src/views/srm/tender/detail.vue
@@ -0,0 +1,811 @@
+<script lang="ts" setup>
+import type { SrmTenderApi } from '#/api/srm/tender';
+
+import { computed, onMounted, ref } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+
+import { Page } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+import { IconifyIcon } from '@vben/icons';
+
+import {
+  getTenderProject,
+  getMaterialList, createMaterial, updateMaterial, deleteMaterial,
+  getBidListByProject, createBid, withdrawBid,
+  getQuoteList, createQuote, updateQuote,
+  createBidOpen, getBidOpen,
+  getEvaluationList, createBidEvaluation, calculateRanking,
+  createAward, getAward, approveAward, generatePurchaseOrder,
+} from '#/api/srm/tender';
+import { getSupplierSimpleList } from '#/api/srm/supplier';
+import { getItemSimpleList } from '#/api/mdm/item';
+import type { MdmItemApi } from '#/api/mdm/item';
+import {
+  TENDER_STATUS,
+  TENDER_STATUS_CELLTAG,
+  TENDER_STATUS_COLORS,
+  TENDER_TYPE_CELLTAG,
+  BID_STATUS,
+  BID_STATUS_MAP,
+  AWARD_STATUS,
+  AWARD_STATUS_MAP,
+  getLabel,
+} from '../enums';
+
+defineOptions({ name: 'SrmTenderDetail' });
+
+const route = useRoute();
+const router = useRouter();
+const projectId = Number(route.query.id);
+
+const project = ref<SrmTenderApi.TenderProjectVO>();
+const activeTab = ref('info');
+
+// ========== 涓嬫媺閫夐」鏁版嵁 ==========
+const mdmItemList = ref<MdmItemApi.Item[]>([]);
+const supplierList = ref<{ id: number; name: string }[]>([]);
+
+onMounted(async () => {
+  const [items, suppliers] = await Promise.all([
+    getItemSimpleList(),
+    getSupplierSimpleList(),
+  ]);
+  mdmItemList.value = items;
+  supplierList.value = suppliers;
+});
+
+const filterOption = (input: string, option: any) =>
+  option.label?.toLowerCase().indexOf(input.toLowerCase()) >= 0;
+
+// ========== 鍩烘湰淇℃伅 ==========
+const materialList = ref<SrmTenderApi.TenderMaterialVO[]>([]);
+const materialLoading = ref(false);
+const materialFormVisible = ref(false);
+const materialForm = ref<SrmTenderApi.TenderMaterialVO>({ tenderProjectId: projectId });
+
+async function loadProject() {
+  project.value = await getTenderProject(projectId);
+}
+async function loadMaterials() {
+  materialLoading.value = true;
+  try { materialList.value = await getMaterialList(projectId); } finally { materialLoading.value = false; }
+}
+async function handleSaveMaterial() {
+  if (materialForm.value.id) {
+    await updateMaterial(materialForm.value);
+  } else {
+    await createMaterial(materialForm.value);
+  }
+  materialFormVisible.value = false;
+  loadMaterials();
+}
+function onMaterialSelect(itemId: number) {
+  const item = mdmItemList.value.find((i) => i.id === itemId);
+  if (item) {
+    materialForm.value.productCode = item.code;
+    materialForm.value.productName = item.name;
+    materialForm.value.productSpec = item.specification;
+    materialForm.value.unit = item.unitMeasureName;
+  }
+}
+
+function handleAddMaterial() {
+  materialForm.value = { tenderProjectId: projectId };
+  materialFormVisible.value = true;
+}
+function handleEditMaterial(row: SrmTenderApi.TenderMaterialVO) {
+  materialForm.value = { ...row };
+  materialFormVisible.value = true;
+}
+async function handleDeleteMaterial(id: number) {
+  await deleteMaterial(id);
+  loadMaterials();
+}
+
+// ========== 鎶曟爣绠$悊 ==========
+const bidList = ref<SrmTenderApi.TenderBidVO[]>([]);
+const bidLoading = ref(false);
+const bidFormVisible = ref(false);
+const bidForm = ref<SrmTenderApi.TenderBidVO>({ tenderProjectId: projectId, supplierId: 0, bidNo: '' });
+
+async function loadBids() {
+  bidLoading.value = true;
+  try { bidList.value = await getBidListByProject(projectId); } finally { bidLoading.value = false; }
+}
+async function handleCreateBid() {
+  await createBid(bidForm.value);
+  bidFormVisible.value = false;
+  loadBids();
+}
+async function handleWithdrawBid(id: number) {
+  await withdrawBid(id);
+  loadBids();
+}
+
+// ========== 鎶ヤ环绠$悊 ==========
+const quoteList = ref<SrmTenderApi.QuoteVO[]>([]);
+const quoteLoading = ref(false);
+const quoteFormVisible = ref(false);
+const quoteForm = ref<SrmTenderApi.QuoteVO>({ bidId: 0, tenderMaterialId: 0 });
+const selectedBidId = ref<number>();
+
+const availableMaterials = computed(() => {
+  const quotedIds = new Set(
+    quoteList.value
+      .filter((q) => q.tenderMaterialId !== undefined)
+      .map((q) => q.tenderMaterialId),
+  );
+  // 缂栬緫鏃跺厑璁镐繚鐣欏綋鍓嶇墿鏂�
+  if (quoteForm.value.id && quoteForm.value.tenderMaterialId) {
+    quotedIds.delete(quoteForm.value.tenderMaterialId);
+  }
+  return materialList.value.filter((m) => !quotedIds.has(m.id));
+});
+
+async function loadQuotes(bidId?: number) {
+  if (!bidId) return;
+  selectedBidId.value = bidId;
+  quoteLoading.value = true;
+  try { quoteList.value = await getQuoteList(bidId); } finally { quoteLoading.value = false; }
+}
+async function handleSaveQuote() {
+  if (quoteForm.value.id) {
+    await updateQuote(quoteForm.value);
+  } else {
+    await createQuote(quoteForm.value);
+  }
+  quoteFormVisible.value = false;
+  loadQuotes(selectedBidId.value);
+}
+function handleAddQuote() {
+  quoteForm.value = { bidId: selectedBidId.value!, tenderMaterialId: 0 };
+  quoteFormVisible.value = true;
+}
+function handleEditQuote(row: SrmTenderApi.QuoteVO) {
+  quoteForm.value = { ...row };
+  quoteFormVisible.value = true;
+}
+
+// ========== 寮�鏍� ==========
+const bidOpen = ref<any>(null);
+
+async function loadBidOpen() {
+  try {
+    bidOpen.value = null;
+  } catch { /* 鏆傛棤寮�鏍囪褰� */ }
+}
+async function handleCreateBidOpen() {
+  await createBidOpen(projectId);
+  message.success('寮�鏍囨垚鍔�');
+  loadBidOpen();
+  loadProject();
+}
+
+// ========== 璇勬爣绠$悊 ==========
+const evaluationList = ref<SrmTenderApi.BidEvaluationVO[]>([]);
+const evalLoading = ref(false);
+const evalFormVisible = ref(false);
+const evalForm = ref<SrmTenderApi.BidEvaluationVO>({ tenderProjectId: projectId, supplierId: 0, bidId: 0 });
+
+async function loadEvaluations() {
+  evalLoading.value = true;
+  try { evaluationList.value = await getEvaluationList(projectId); } finally { evalLoading.value = false; }
+}
+async function handleCreateEvaluation() {
+  await createBidEvaluation(evalForm.value);
+  evalFormVisible.value = false;
+  loadEvaluations();
+  loadProject();
+}
+async function handleCalculateRanking() {
+  await calculateRanking(projectId);
+  message.success('鎺掑悕璁$畻瀹屾垚');
+  loadEvaluations();
+}
+
+// ========== 瀹氭爣 ==========
+const award = ref<SrmTenderApi.BidAwardVO>();
+const awardFormVisible = ref(false);
+const awardForm = ref<SrmTenderApi.BidAwardVO>({ tenderProjectId: projectId, supplierId: 0, bidId: 0, awardNo: '' });
+
+async function loadAward() {
+  try { award.value = await getAward(projectId); } catch { award.value = undefined; }
+}
+async function handleCreateAward() {
+  await createAward(awardForm.value);
+  awardFormVisible.value = false;
+  loadAward();
+  loadProject();
+}
+async function handleApproveAward() {
+  if (!award.value?.id) return;
+  await approveAward(award.value.id);
+  message.success('瀹氭爣瀹℃壒閫氳繃');
+  loadAward();
+  loadProject();
+}
+async function handleGeneratePurchaseOrder() {
+  if (!award.value?.id) return;
+  await generatePurchaseOrder(award.value.id);
+  message.success('閲囪喘璁㈠崟宸茬敓鎴�');
+  loadAward();
+  loadProject();
+}
+
+// ========== 鍒濆鍖� ==========
+loadProject();
+loadMaterials();
+loadBids();
+loadEvaluations();
+loadAward();
+
+// ========== 鐘舵�佸垽鏂� ==========
+const statusLabel = computed(() => getLabel(TENDER_STATUS_CELLTAG.labels, project.value?.tenderStatus));
+const statusColor = computed(() => TENDER_STATUS_COLORS[project.value?.tenderStatus!] || 'default');
+const typeLabel = computed(() => getLabel(TENDER_TYPE_CELLTAG.labels, project.value?.tenderType));
+
+const stepItems = [
+  { title: '鑽夌', status: TENDER_STATUS.DRAFT },
+  { title: '鍙戝竷', status: TENDER_STATUS.PUBLISHED },
+  { title: '鎶曟爣涓�', status: TENDER_STATUS.BIDDING },
+  { title: '璇勬爣涓�', status: TENDER_STATUS.EVALUATING },
+  { title: '宸插畾鏍�', status: TENDER_STATUS.AWARDED },
+];
+const currentStep = computed(() => {
+  const s = project.value?.tenderStatus ?? 0;
+  if (s === TENDER_STATUS.CLOSED) return 4;
+  const idx = stepItems.findIndex((item) => item.status >= s);
+  return idx === -1 ? 0 : idx;
+});
+const stepStatus = computed(() => {
+  const s = project.value?.tenderStatus;
+  if (s === TENDER_STATUS.CLOSED) return 'error';
+  if (s === TENDER_STATUS.AWARDED) return 'finish';
+  return 'process';
+});
+
+const materialTotalAmount = computed(() =>
+  materialList.value.reduce((sum, m) => sum + (m.estimatedPrice ?? 0) * (m.quantity ?? 0), 0),
+);
+const bidCount = computed(() => bidList.value.length);
+const bidTotalAmount = computed(() =>
+  bidList.value.reduce((sum, b) => sum + (b.bidTotalAmount ?? 0), 0),
+);
+const evalCount = computed(() => evaluationList.value.length);
+const rankedCount = computed(() => evaluationList.value.filter((e) => e.rank != null).length);
+
+const selectedBid = computed(() =>
+  bidList.value.find((b) => b.id === selectedBidId.value),
+);
+
+const materialColumns = [
+  { title: '鐗╂枡缂栫爜', dataIndex: 'productCode', width: 120 },
+  { title: '鐗╂枡鍚嶇О', dataIndex: 'productName', width: 150 },
+  { title: '瑙勬牸鍨嬪彿', dataIndex: 'productSpec', width: 120 },
+  { title: '鍗曚綅', dataIndex: 'unit', width: 80 },
+  { title: '鏁伴噺', dataIndex: 'quantity', width: 80 },
+  { title: '鎶�鏈姹�', dataIndex: 'techRequirement', width: 150 },
+  { title: '棰勪及鍗曚环', dataIndex: 'estimatedPrice', width: 100 },
+  { title: '鎿嶄綔', key: 'actions', width: 150 },
+];
+
+const bidColumns = [
+  { title: '鎶曟爣缂栧彿', dataIndex: 'bidNo', width: 140 },
+  { title: '渚涘簲鍟�', dataIndex: 'supplierName', width: 200 },
+  { title: '鎶曟爣閲戦', dataIndex: 'bidTotalAmount', width: 120 },
+  { title: '鎶曟爣鏃堕棿', dataIndex: 'bidTime', width: 160 },
+  { title: '鐘舵��', dataIndex: 'bidStatus', width: 100, customRender: ({ text }: any) => BID_STATUS_MAP[text] || text },
+  { title: '鎿嶄綔', key: 'actions', width: 180 },
+];
+
+const quoteColumns = [
+  { title: '鐗╂枡', dataIndex: 'materialName', width: 120 },
+  { title: '鎶ヤ环浠锋牸', dataIndex: 'quotePrice', width: 100 },
+  { title: '绋庣巼(%)', dataIndex: 'taxRate', width: 80 },
+  { title: '浜ゆ湡(澶�)', dataIndex: 'deliveryCycle', width: 80 },
+  { title: '浠樻鏉′欢', dataIndex: 'paymentTerms', width: 120 },
+  { title: '璐ㄤ繚鏈�(鏈�)', dataIndex: 'warrantyPeriod', width: 80 },
+  { title: '澶囨敞', dataIndex: 'remark', width: 150 },
+  { title: '鎿嶄綔', key: 'actions', width: 120 },
+];
+
+const evalColumns = [
+  { title: '鎶曟爣ID', dataIndex: 'bidId', width: 80 },
+  { title: '渚涘簲鍟�', dataIndex: 'supplierName', width: 150 },
+  { title: '浠锋牸璇勫垎', dataIndex: 'priceScore', width: 100 },
+  { title: '鎶�鏈瘎鍒�', dataIndex: 'techScore', width: 100 },
+  { title: '浜や粯璇勫垎', dataIndex: 'deliveryScore', width: 100 },
+  { title: '鏈嶅姟璇勫垎', dataIndex: 'serviceScore', width: 100 },
+  { title: '缁煎悎璇勫垎', dataIndex: 'compositeScore', width: 100 },
+  { title: '鎺掑悕', dataIndex: 'rank', width: 80 },
+  { title: '璇勫', dataIndex: 'evaluatorName', width: 100 },
+  { title: '璇勮', dataIndex: 'evaluationOpinion', width: 150 },
+  { title: '鎿嶄綔', key: 'actions', width: 120 },
+];
+</script>
+
+<template>
+  <Page auto-content-height>
+    <!-- 鍔犺浇涓� -->
+    <a-skeleton v-if="!project" active class="p-4" />
+    <template v-else>
+      <!-- 椤堕儴鏍囬鏍� -->
+      <div class="flex items-center gap-2 px-4 pt-3 pb-2">
+        <a-button type="text" size="small" @click="router.back()">
+          <template #icon><IconifyIcon icon="ant-design:arrow-left-outlined" /></template>
+        </a-button>
+        <span class="text-lg font-bold truncate max-w-md">{{ project.tenderName }}</span>
+        <span class="text-gray-400 text-xs">{{ project.tenderNo }}</span>
+        <a-tag :color="statusColor">{{ statusLabel }}</a-tag>
+        <a-tag color="blue">{{ typeLabel }}</a-tag>
+        <div class="flex-1" />
+        <a-button size="small" @click="loadProject(); loadMaterials(); loadBids(); loadEvaluations(); loadAward();">
+          <template #icon><IconifyIcon icon="ant-design:reload-outlined" /></template>
+          鍒锋柊
+        </a-button>
+      </div>
+
+      <!-- 宸ヤ綔娴佽繘搴� + 鍏抽敭淇℃伅 -->
+      <div class="px-4 mb-3">
+        <a-card size="small">
+          <!-- 娴佺▼姝ラ -->
+          <a-steps :current="currentStep" :status="stepStatus" size="small" class="mb-3">
+              <a-step v-for="item in stepItems" :key="item.status" :title="item.title" />
+            </a-steps>
+
+          <!-- 鍏抽敭瀛楁 -->
+          <div class="grid grid-cols-6 gap-3 text-xs">
+            <div>
+              <div class="text-gray-400 mb-0.5">棰勭畻閲戦</div>
+              <div class="font-semibold text-blue-500">{{ project.budgetAmount?.toLocaleString() ?? '-' }} 鍏�</div>
+            </div>
+            <div>
+              <div class="text-gray-400 mb-0.5">璐熻矗浜�</div>
+              <div class="font-semibold">{{ project.projectLeaderName || '-' }}</div>
+            </div>
+            <div>
+              <div class="text-gray-400 mb-0.5">閲囪喘缁勭粐</div>
+              <div class="font-semibold">{{ project.purchaseOrg || '-' }}</div>
+            </div>
+            <div>
+              <div class="text-gray-400 mb-0.5">寮�鏍囨椂闂�</div>
+              <div class="font-semibold">{{ project.bidOpenTime || '寰呭畾' }}</div>
+            </div>
+            <div>
+              <div class="text-gray-400 mb-0.5">寮�濮嬫椂闂�</div>
+              <div class="font-semibold">{{ project.startTime || '-' }}</div>
+            </div>
+            <div>
+              <div class="text-gray-400 mb-0.5">鎴鏃堕棿</div>
+              <div class="font-semibold">{{ project.endTime || '-' }}</div>
+            </div>
+          </div>
+
+          <!-- 鎷涙爣璇存槑 / 璧勮川瑕佹眰 -->
+          <template v-if="project.tenderDesc || project.qualificationRequirements">
+            <a-divider class="!my-2" />
+            <a-collapse :bordered="false" ghost expand-icon-position="end">
+              <a-collapse-panel v-if="project.tenderDesc" key="desc" header="鎷涙爣璇存槑">
+                <p class="text-gray-500 text-xs whitespace-pre-wrap">{{ project.tenderDesc }}</p>
+              </a-collapse-panel>
+              <a-collapse-panel v-if="project.qualificationRequirements" key="req" header="璧勮川瑕佹眰">
+                <p class="text-gray-500 text-xs whitespace-pre-wrap">{{ project.qualificationRequirements }}</p>
+              </a-collapse-panel>
+            </a-collapse>
+          </template>
+        </a-card>
+      </div>
+
+      <!-- Tab 椤电 -->
+      <div class="px-4 pb-4">
+        <a-card size="small">
+          <a-tabs v-model:activeKey="activeTab" size="small">
+            <!-- Tab1: 鐗╂枡娓呭崟 -->
+            <a-tab-pane key="info">
+              <template #tab>
+                <a-space :size="4"><IconifyIcon icon="ant-design:unordered-list-outlined" />鐗╂枡娓呭崟</a-space>
+              </template>
+              <div class="flex items-center justify-between mb-3">
+                <a-space>
+                  <span class="text-xs text-gray-400">鍏� <b class="text-gray-600">{{ materialList.length }}</b> 椤癸紝棰勪及鎬婚噾棰� <b class="text-blue-500">{{ materialTotalAmount.toLocaleString() }}</b> 鍏�</span>
+                </a-space>
+                <a-button type="primary" size="small" @click="handleAddMaterial">
+                  <template #icon><IconifyIcon icon="ant-design:plus-outlined" /></template>
+                  娣诲姞鐗╂枡
+                </a-button>
+              </div>
+              <a-table :columns="materialColumns" :data-source="materialList" :loading="materialLoading" :pagination="false" row-key="id" size="small">
+                <template #bodyCell="{ column, record }">
+                  <template v-if="column.dataIndex === 'estimatedPrice'">
+                    {{ record.estimatedPrice ? record.estimatedPrice.toLocaleString() : '-' }}
+                  </template>
+                  <template v-if="column.key === 'actions'">
+                    <a-space>
+                      <a-button size="small" type="link" @click="handleEditMaterial(record)"><IconifyIcon icon="ant-design:edit-outlined" />缂栬緫</a-button>
+                      <a-popconfirm title="纭鍒犻櫎璇ョ墿鏂欙紵" @confirm="handleDeleteMaterial(record.id)">
+                        <a-button size="small" type="link" danger><IconifyIcon icon="ant-design:delete-outlined" />鍒犻櫎</a-button>
+                      </a-popconfirm>
+                    </a-space>
+                  </template>
+                </template>
+              </a-table>
+              <a-empty v-if="!materialLoading && materialList.length === 0" description="鏆傛棤鐗╂枡" class="mt-8" />
+            </a-tab-pane>
+
+            <!-- Tab2: 鎶曟爣绠$悊 -->
+            <a-tab-pane key="bid">
+              <template #tab>
+                <a-space :size="4"><IconifyIcon icon="ant-design:team-outlined" />鎶曟爣绠$悊</a-space>
+              </template>
+              <div class="flex items-center justify-between mb-3">
+                <span class="text-xs text-gray-400">鍏� <b class="text-gray-600">{{ bidCount }}</b> 涓姇鏍�</span>
+                <a-button type="primary" size="small" @click="bidForm = { tenderProjectId: projectId, supplierId: 0, bidNo: '' }; bidFormVisible = true">
+                  <template #icon><IconifyIcon icon="ant-design:plus-outlined" /></template>
+                  鏂板鎶曟爣
+                </a-button>
+              </div>
+              <a-table :columns="bidColumns" :data-source="bidList" :loading="bidLoading" :pagination="false" row-key="id" size="small">
+                <template #bodyCell="{ column, record }">
+                  <template v-if="column.dataIndex === 'bidTotalAmount'">
+                    <span class="font-medium">{{ record.bidTotalAmount?.toLocaleString() }}</span>
+                  </template>
+                  <template v-if="column.dataIndex === 'bidStatus'">
+                    <a-tag :color="record.bidStatus === BID_STATUS.WIN ? 'green' : record.bidStatus === BID_STATUS.LOSE ? 'red' : 'default'">
+                      {{ BID_STATUS_MAP[record.bidStatus] }}
+                    </a-tag>
+                  </template>
+                  <template v-if="column.key === 'actions'">
+                    <a-space>
+                      <a-button size="small" type="link" @click="loadQuotes(record.id); activeTab = 'quote'"><IconifyIcon icon="ant-design:dollar-outlined" />鎶ヤ环</a-button>
+                      <a-popconfirm v-if="record.bidStatus === BID_STATUS.REGISTERED" title="纭鎾ゆ爣锛�" @confirm="handleWithdrawBid(record.id)">
+                        <a-button size="small" type="link" danger><IconifyIcon icon="ant-design:close-circle-outlined" />鎾ゆ爣</a-button>
+                      </a-popconfirm>
+                    </a-space>
+                  </template>
+                </template>
+              </a-table>
+              <a-empty v-if="!bidLoading && bidList.length === 0" description="鏆傛棤鎶曟爣璁板綍" class="mt-8" />
+            </a-tab-pane>
+
+            <!-- Tab3: 鎶ヤ环绠$悊 -->
+            <a-tab-pane key="quote">
+              <template #tab>
+                <a-space :size="4"><IconifyIcon icon="ant-design:dollar-outlined" />鎶ヤ环绠$悊</a-space>
+              </template>
+              <template v-if="selectedBidId && selectedBid">
+                <div class="flex items-center gap-3 mb-3 px-3 py-2 bg-gray-50 rounded text-xs">
+                  <span class="text-gray-400">鎶曟爣锛�<b class="text-gray-700">{{ selectedBid.bidNo }}</b></span>
+                  <a-divider type="vertical" />
+                  <span class="text-gray-400">渚涘簲鍟嗭細<b class="text-gray-700">{{ selectedBid.supplierName }}</b></span>
+                  <a-button size="small" type="link" class="ml-auto" @click="activeTab = 'bid'">鍒囨崲鎶曟爣</a-button>
+                </div>
+                <div class="text-right mb-3">
+                  <a-button v-if="selectedBid.bidStatus !== BID_STATUS.WITHDRAWN" type="primary" size="small" @click="handleAddQuote">
+                    <template #icon><IconifyIcon icon="ant-design:plus-outlined" /></template>
+                    鏂板鎶ヤ环
+                  </a-button>
+                </div>
+                <a-table :columns="quoteColumns" :data-source="quoteList" :loading="quoteLoading" :pagination="false" row-key="id" size="small">
+                  <template #bodyCell="{ column, record }">
+                    <template v-if="column.dataIndex === 'quotePrice'">
+                      <span class="font-medium text-blue-600">{{ record.quotePrice?.toLocaleString() }}</span>
+                    </template>
+                    <template v-if="column.dataIndex === 'taxRate'">
+                      {{ record.taxRate != null ? `${record.taxRate}%` : '-' }}
+                    </template>
+                    <template v-if="column.dataIndex === 'deliveryCycle'">
+                      {{ record.deliveryCycle != null ? `${record.deliveryCycle} 澶ー : '-' }}
+                    </template>
+                    <template v-if="column.dataIndex === 'warrantyPeriod'">
+                      {{ record.warrantyPeriod != null ? `${record.warrantyPeriod} 涓湀` : '-' }}
+                    </template>
+                    <template v-if="column.key === 'actions'">
+                      <a-button v-if="selectedBid?.bidStatus !== BID_STATUS.WITHDRAWN" size="small" type="link" @click="handleEditQuote(record)"><IconifyIcon icon="ant-design:edit-outlined" />缂栬緫</a-button>
+                    </template>
+                  </template>
+                </a-table>
+                <a-empty v-if="!quoteLoading && quoteList.length === 0" description="鏆傛棤鎶ヤ环鏄庣粏" class="mt-8" />
+              </template>
+              <a-empty v-else description="璇峰厛鍦ㄣ�屾姇鏍囩鐞嗐�嶄腑閫夋嫨涓�涓姇鏍囨煡鐪嬫姤浠�">
+                <a-button type="primary" @click="activeTab = 'bid'">鍓嶅線鎶曟爣绠$悊</a-button>
+              </a-empty>
+            </a-tab-pane>
+
+            <!-- Tab4: 寮�鏍� -->
+            <a-tab-pane key="bidOpen">
+              <template #tab>
+                <a-space :size="4"><IconifyIcon icon="ant-design:unlock-outlined" />寮�鏍�</a-space>
+              </template>
+              <template v-if="bidOpen">
+                <a-result status="success" title="宸插紑鏍�">
+                  <template #sub-title>
+                    <a-descriptions :column="2" size="small" class="mt-4">
+                      <a-descriptions-item label="寮�鏍囩紪鍙�">{{ bidOpen.id }}</a-descriptions-item>
+                      <a-descriptions-item label="寮�鏍囨椂闂�">{{ bidOpen.openTime }}</a-descriptions-item>
+                    </a-descriptions>
+                  </template>
+                </a-result>
+              </template>
+              <template v-else>
+                <div class="text-center py-8">
+                  <a-result status="info" title="灏氭湭寮�鏍�" sub-title="鎵ц寮�鏍囧悗椤圭洰杩涘叆鎶曟爣闃舵锛屼緵搴斿晢鍙寮忔姇鏍�">
+                    <template #extra>
+                      <a-button type="primary" @click="handleCreateBidOpen">
+                        <template #icon><IconifyIcon icon="ant-design:thunderbolt-outlined" /></template>
+                        鎵ц寮�鏍�
+                      </a-button>
+                    </template>
+                  </a-result>
+                </div>
+              </template>
+            </a-tab-pane>
+
+            <!-- Tab5: 璇勬爣 -->
+            <a-tab-pane key="evaluation">
+              <template #tab>
+                <a-space :size="4"><IconifyIcon icon="ant-design:trophy-outlined" />璇勬爣</a-space>
+              </template>
+              <div class="flex items-center justify-between mb-3">
+                <span class="text-xs text-gray-400">鍏� <b class="text-gray-600">{{ evalCount }}</b> 鏉★紝宸叉帓鍚� <b class="text-green-500">{{ rankedCount }}</b> 鏉�</span>
+                <a-space>
+                  <a-button size="small" @click="handleCalculateRanking">
+                    <template #icon><IconifyIcon icon="ant-design:bar-chart-outlined" /></template>
+                    璁$畻鎺掑悕
+                  </a-button>
+                  <a-button type="primary" size="small" @click="evalForm = { tenderProjectId: projectId, supplierId: 0, bidId: 0 }; evalFormVisible = true">
+                    <template #icon><IconifyIcon icon="ant-design:plus-outlined" /></template>
+                    鏂板璇勬爣
+                  </a-button>
+                </a-space>
+              </div>
+              <a-table :columns="evalColumns" :data-source="evaluationList" :loading="evalLoading" :pagination="false" row-key="id" size="small">
+                <template #bodyCell="{ column, record }">
+                  <template v-if="['priceScore', 'techScore', 'deliveryScore', 'serviceScore'].includes(column.dataIndex)">
+                    <div class="flex items-center gap-2">
+                      <a-progress :percent="record[column.dataIndex] || 0" :show-info="false" size="small"
+                        :stroke-color="record[column.dataIndex] >= 80 ? '#52c41a' : record[column.dataIndex] >= 60 ? '#faad14' : '#ff4d4f'"
+                        class="flex-1" style="min-width: 50px;" />
+                      <span class="text-xs w-8 text-right">{{ record[column.dataIndex] ?? '-' }}</span>
+                    </div>
+                  </template>
+                  <template v-if="column.dataIndex === 'compositeScore'">
+                    <span class="font-bold" :class="record.compositeScore >= 80 ? 'text-green-500' : record.compositeScore >= 60 ? 'text-orange-500' : 'text-red-500'">
+                      {{ record.compositeScore ?? '-' }}
+                    </span>
+                  </template>
+                  <template v-if="column.dataIndex === 'rank'">
+                    <a-tag v-if="record.rank === 1" color="gold"><IconifyIcon icon="ant-design:trophy-outlined" /> 绗�1鍚�</a-tag>
+                    <a-tag v-else-if="record.rank" color="default">绗瑊{ record.rank }}鍚�</a-tag>
+                    <span v-else class="text-gray-300">-</span>
+                  </template>
+                  <template v-if="column.key === 'actions'">
+                    <a-button size="small" type="link" @click="evalForm = { ...record }; evalFormVisible = true"><IconifyIcon icon="ant-design:edit-outlined" />缂栬緫</a-button>
+                  </template>
+                </template>
+              </a-table>
+              <a-empty v-if="!evalLoading && evaluationList.length === 0" description="鏆傛棤璇勬爣璁板綍" class="mt-8" />
+            </a-tab-pane>
+
+            <!-- Tab6: 瀹氭爣 -->
+            <a-tab-pane key="award">
+              <template #tab>
+                <a-space :size="4"><IconifyIcon icon="ant-design:check-circle-outlined" />瀹氭爣</a-space>
+              </template>
+              <template v-if="award">
+                <a-row :gutter="16">
+                  <a-col :span="16">
+                    <a-descriptions :column="2" bordered size="small">
+                      <a-descriptions-item label="瀹氭爣缂栧彿">
+                        <span class="font-mono">{{ award.awardNo }}</span>
+                      </a-descriptions-item>
+                      <a-descriptions-item label="涓爣渚涘簲鍟�">
+                        <a-tag color="blue">{{ award.supplierName || award.supplierId }}</a-tag>
+                      </a-descriptions-item>
+                      <a-descriptions-item label="瀹氭爣閲戦">
+                        <span class="font-bold text-blue-600">{{ award.awardAmount?.toLocaleString() }}</span> 鍏�
+                      </a-descriptions-item>
+                      <a-descriptions-item label="瀹氭爣鏃堕棿">{{ award.awardTime || '-' }}</a-descriptions-item>
+                      <a-descriptions-item label="鐘舵��">
+                        <a-tag :color="award.awardStatus === AWARD_STATUS.AWARDED ? 'green' : 'orange'">{{ AWARD_STATUS_MAP[award.awardStatus] }}</a-tag>
+                      </a-descriptions-item>
+                      <a-descriptions-item label="閲囪喘璁㈠崟">{{ award.purchaseOrderId || '寰呯敓鎴�' }}</a-descriptions-item>
+                      <a-descriptions-item label="澶囨敞" :span="2">{{ award.remark || '-' }}</a-descriptions-item>
+                    </a-descriptions>
+                  </a-col>
+                  <a-col :span="8" class="flex items-center justify-center">
+                    <template v-if="award.awardStatus === AWARD_STATUS.ORDER_GENERATED">
+                      <a-result status="success" title="璁㈠崟宸茬敓鎴�" class="p-0">
+                        <template #sub-title>
+                          <span class="text-sm">閲囪喘璁㈠崟 ID: {{ award.purchaseOrderId }}</span>
+                        </template>
+                      </a-result>
+                    </template>
+                    <template v-else-if="award.awardStatus === AWARD_STATUS.AWARDED">
+                      <div class="text-center">
+                        <a-result status="success" title="瀹氭爣瀹屾垚" class="p-0 mb-3" />
+                        <a-button type="primary" block @click="handleGeneratePurchaseOrder">
+                          <template #icon><IconifyIcon icon="ant-design:shopping-cart-outlined" /></template>
+                          鐢熸垚閲囪喘璁㈠崟
+                        </a-button>
+                      </div>
+                    </template>
+                    <template v-else>
+                      <div class="text-center">
+                        <div class="text-gray-500 text-sm mb-3">瀹氭爣淇℃伅宸插垱寤猴紝璇峰鎵�</div>
+                        <a-button type="primary" block @click="handleApproveAward" style="background: #52c41a; border-color: #52c41a;">
+                          <template #icon><IconifyIcon icon="ant-design:check-outlined" /></template>
+                          瀹℃壒閫氳繃
+                        </a-button>
+                      </div>
+                    </template>
+                  </a-col>
+                </a-row>
+              </template>
+              <template v-else>
+                <div class="text-center py-8">
+                  <a-result status="info" title="灏氭湭瀹氭爣" sub-title="鍩轰簬璇勬爣鎺掑悕閫夋嫨涓爣渚涘簲鍟嗗苟鍒涘缓瀹氭爣">
+                    <template #extra>
+                      <a-button type="primary" @click="awardForm = { tenderProjectId: projectId, supplierId: 0, bidId: 0, awardNo: '' }; awardFormVisible = true">
+                        <template #icon><IconifyIcon icon="ant-design:plus-outlined" /></template>
+                        鍒涘缓瀹氭爣
+                      </a-button>
+                    </template>
+                  </a-result>
+                </div>
+              </template>
+            </a-tab-pane>
+          </a-tabs>
+        </a-card>
+      </div>
+
+      <!-- ========== 寮圭獥 ========== -->
+
+      <!-- 鐗╂枡琛ㄥ崟 -->
+      <a-modal v-model:open="materialFormVisible" :title="materialForm.id ? '缂栬緫鐗╂枡' : '娣诲姞鐗╂枡'" :width="560" @ok="handleSaveMaterial">
+        <a-form layout="vertical" class="mt-4">
+          <a-form-item label="MDM鐗╂枡" required>
+            <a-select v-model:value="materialForm.productId" show-search placeholder="鎼滅储骞堕�夋嫨MDM鐗╂枡" :filter-option="filterOption" option-label-prop="label" @change="onMaterialSelect">
+              <a-select-option v-for="item in mdmItemList" :key="item.id" :value="item.id" :label="`${item.code} ${item.name}`">
+                <div class="flex flex-col">
+                  <span class="font-medium">{{ item.code }}</span>
+                  <span class="text-xs text-gray-400">{{ item.name }}{{ item.specification ? ` 路 ${item.specification}` : '' }}</span>
+                </div>
+              </a-select-option>
+            </a-select>
+          </a-form-item>
+          <a-row :gutter="12">
+            <a-col :span="12">
+              <a-form-item label="鐗╂枡鍚嶇О" required><a-input v-model:value="materialForm.productName" placeholder="鑷姩甯﹀叆" /></a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item label="瑙勬牸鍨嬪彿"><a-input v-model:value="materialForm.productSpec" placeholder="鑷姩甯﹀叆" /></a-form-item>
+            </a-col>
+          </a-row>
+          <a-row :gutter="12">
+            <a-col :span="8">
+              <a-form-item label="鍗曚綅"><a-input v-model:value="materialForm.unit" placeholder="鑷姩甯﹀叆" /></a-form-item>
+            </a-col>
+            <a-col :span="8">
+              <a-form-item label="鏁伴噺" required><a-input-number v-model:value="materialForm.quantity" :min="0" class="!w-full" placeholder="鏁伴噺" /></a-form-item>
+            </a-col>
+            <a-col :span="8">
+              <a-form-item label="棰勪及鍗曚环"><a-input-number v-model:value="materialForm.estimatedPrice" :min="0" class="!w-full" placeholder="鍗曚环" /></a-form-item>
+            </a-col>
+          </a-row>
+          <a-form-item label="鎶�鏈姹�">
+            <a-textarea v-model:value="materialForm.techRequirement" placeholder="濡傦細绗﹀悎GB/T鏍囧噯" :rows="2" />
+          </a-form-item>
+        </a-form>
+      </a-modal>
+
+      <!-- 鎶曟爣琛ㄥ崟 -->
+      <a-modal v-model:open="bidFormVisible" title="鏂板鎶曟爣" :width="460" @ok="handleCreateBid">
+        <a-form layout="vertical" class="mt-4">
+          <a-form-item label="鎶曟爣缂栧彿" required><a-input v-model:value="bidForm.bidNo" placeholder="濡� BID2024010001" /></a-form-item>
+          <a-form-item label="渚涘簲鍟�" required>
+            <a-select v-model:value="bidForm.supplierId" show-search placeholder="鎼滅储骞堕�夋嫨渚涘簲鍟�" :filter-option="filterOption">
+              <a-select-option v-for="s in supplierList" :key="s.id" :value="s.id" :label="s.name">{{ s.name }}</a-select-option>
+            </a-select>
+          </a-form-item>
+          <a-form-item label="鎶曟爣鎬婚噾棰�">
+            <a-input-number v-model:value="bidForm.bidTotalAmount" :min="0" class="!w-full" placeholder="鎶曟爣鎬婚噾棰�"><template #addonAfter>鍏�</template></a-input-number>
+          </a-form-item>
+        </a-form>
+      </a-modal>
+
+      <!-- 鎶ヤ环琛ㄥ崟 -->
+      <a-modal v-model:open="quoteFormVisible" :title="quoteForm.id ? '缂栬緫鎶ヤ环' : '鏂板鎶ヤ环'" :width="560" @ok="handleSaveQuote">
+        <a-form layout="vertical" class="mt-4">
+          <a-form-item label="鎷涙爣鐗╂枡" required>
+            <a-select v-model:value="quoteForm.tenderMaterialId" show-search placeholder="鎼滅储骞堕�夋嫨鎷涙爣鐗╂枡" :filter-option="filterOption">
+              <a-select-option v-for="m in availableMaterials" :key="m.id" :value="m.id" :label="m.productName">{{ m.productCode }} - {{ m.productName }}</a-select-option>
+            </a-select>
+          </a-form-item>
+          <a-row :gutter="12">
+            <a-col :span="8">
+              <a-form-item label="鎶ヤ环浠锋牸" required><a-input-number v-model:value="quoteForm.quotePrice" :min="0" class="!w-full" placeholder="鎶ヤ环"><template #addonAfter>鍏�</template></a-input-number></a-form-item>
+            </a-col>
+            <a-col :span="8">
+              <a-form-item label="绋庣巼(%)"><a-input-number v-model:value="quoteForm.taxRate" :min="0" :max="100" class="!w-full" placeholder="濡� 13" /></a-form-item>
+            </a-col>
+            <a-col :span="8">
+              <a-form-item label="浜ゆ湡(澶�)"><a-input-number v-model:value="quoteForm.deliveryCycle" :min="0" class="!w-full" placeholder="澶╂暟" /></a-form-item>
+            </a-col>
+          </a-row>
+          <a-row :gutter="12">
+            <a-col :span="12">
+              <a-form-item label="浠樻鏉′欢"><a-input v-model:value="quoteForm.paymentTerms" placeholder="濡傦細璐у埌30澶╀粯娆�" /></a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item label="璐ㄤ繚鏈�(鏈�)"><a-input-number v-model:value="quoteForm.warrantyPeriod" :min="0" class="!w-full" placeholder="鏈堟暟" /></a-form-item>
+            </a-col>
+          </a-row>
+          <a-form-item label="澶囨敞"><a-textarea v-model:value="quoteForm.remark" placeholder="濡傚惈杩愯垂" :rows="2" /></a-form-item>
+        </a-form>
+      </a-modal>
+
+      <!-- 璇勬爣琛ㄥ崟 -->
+      <a-modal v-model:open="evalFormVisible" :title="evalForm.id ? '缂栬緫璇勬爣' : '鏂板璇勬爣'" :width="600" @ok="handleCreateEvaluation">
+        <a-form layout="vertical" class="mt-4">
+          <a-row :gutter="12">
+            <a-col :span="12">
+              <a-form-item label="鎶曟爣" required>
+                <a-select v-model:value="evalForm.bidId" show-search placeholder="閫夋嫨鎶曟爣" :filter-option="filterOption"
+                  @change="(val: number) => { const b = bidList.find(i => i.id === val); if (b) { evalForm.supplierId = b.supplierId; evalForm.supplierName = b.supplierName; } }">
+                  <a-select-option v-for="b in bidList" :key="b.id" :value="b.id" :label="`${b.bidNo} - ${b.supplierName}`">{{ b.bidNo }} - {{ b.supplierName }}</a-select-option>
+                </a-select>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item label="渚涘簲鍟�">
+                <a-select v-model:value="evalForm.supplierId" show-search placeholder="閫夋嫨渚涘簲鍟�" :filter-option="filterOption">
+                  <a-select-option v-for="s in supplierList" :key="s.id" :value="s.id" :label="s.name">{{ s.name }}</a-select-option>
+                </a-select>
+              </a-form-item>
+            </a-col>
+          </a-row>
+          <a-divider class="!my-2">璇勫垎椤癸紙鐧惧垎鍒讹級</a-divider>
+          <a-row :gutter="12">
+            <a-col :span="12"><a-form-item label="浠锋牸璇勫垎"><a-input-number v-model:value="evalForm.priceScore" :min="0" :max="100" class="!w-full" placeholder="0-100" /></a-form-item></a-col>
+            <a-col :span="12"><a-form-item label="鎶�鏈瘎鍒�"><a-input-number v-model:value="evalForm.techScore" :min="0" :max="100" class="!w-full" placeholder="0-100" /></a-form-item></a-col>
+            <a-col :span="12"><a-form-item label="浜や粯璇勫垎"><a-input-number v-model:value="evalForm.deliveryScore" :min="0" :max="100" class="!w-full" placeholder="0-100" /></a-form-item></a-col>
+            <a-col :span="12"><a-form-item label="鏈嶅姟璇勫垎"><a-input-number v-model:value="evalForm.serviceScore" :min="0" :max="100" class="!w-full" placeholder="0-100" /></a-form-item></a-col>
+          </a-row>
+          <a-divider class="!my-2" />
+          <a-row :gutter="12">
+            <a-col :span="12"><a-form-item label="璇勫"><a-input v-model:value="evalForm.evaluatorName" placeholder="璇勫濮撳悕" /></a-form-item></a-col>
+          </a-row>
+          <a-form-item label="璇勬爣鎰忚"><a-textarea v-model:value="evalForm.evaluationOpinion" placeholder="渚涘簲鍟嗙殑鎶�鏈柟妗堛�佷氦浠樿兘鍔涚瓑缁煎悎璇勪环" :rows="3" /></a-form-item>
+        </a-form>
+      </a-modal>
+
+      <!-- 瀹氭爣琛ㄥ崟 -->
+      <a-modal v-model:open="awardFormVisible" title="鍒涘缓瀹氭爣" :width="500" @ok="handleCreateAward">
+        <a-form layout="vertical" class="mt-4">
+          <a-form-item label="瀹氭爣缂栧彿" required><a-input v-model:value="awardForm.awardNo" placeholder="濡� AWARD2024010001" /></a-form-item>
+          <a-row :gutter="12">
+            <a-col :span="12">
+              <a-form-item label="渚涘簲鍟�" required>
+                <a-select v-model:value="awardForm.supplierId" show-search placeholder="閫夋嫨渚涘簲鍟�" :filter-option="filterOption">
+                  <a-select-option v-for="s in supplierList" :key="s.id" :value="s.id" :label="s.name">{{ s.name }}</a-select-option>
+                </a-select>
+              </a-form-item>
+            </a-col>
+            <a-col :span="12">
+              <a-form-item label="鎶曟爣" required>
+                <a-select v-model:value="awardForm.bidId" show-search placeholder="閫夋嫨鎶曟爣" :filter-option="filterOption">
+                  <a-select-option v-for="b in bidList" :key="b.id" :value="b.id" :label="`${b.bidNo} - ${b.supplierName}`">{{ b.bidNo }} - {{ b.supplierName }}</a-select-option>
+                </a-select>
+              </a-form-item>
+            </a-col>
+          </a-row>
+          <a-form-item label="瀹氭爣閲戦" required>
+            <a-input-number v-model:value="awardForm.awardAmount" :min="0" class="!w-full" placeholder="瀹氭爣閲戦"><template #addonAfter>鍏�</template></a-input-number>
+          </a-form-item>
+          <a-form-item label="澶囨敞"><a-textarea v-model:value="awardForm.remark" placeholder="濡傦細缁煎悎璇勬爣绗竴鍚�" :rows="2" /></a-form-item>
+        </a-form>
+      </a-modal>
+    </template>
+  </Page>
+</template>
diff --git a/src/views/srm/tender/index.vue b/src/views/srm/tender/index.vue
new file mode 100644
index 0000000..f966d8f
--- /dev/null
+++ b/src/views/srm/tender/index.vue
@@ -0,0 +1,223 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { SrmTenderApi } from '#/api/srm/tender';
+
+import { Page, useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+import { useRouter } from 'vue-router';
+
+import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
+import {
+  closeTender,
+  deleteTenderProject,
+  generatePurchaseOrder,
+  getTenderProjectPage,
+  publishTender,
+  startBidding,
+  startEvaluation,
+} from '#/api/srm/tender';
+import { $t } from '#/locales';
+
+import { useGridColumns, useGridFormSchema } from './data';
+import TenderForm from './modules/form.vue';
+import { TENDER_STATUS } from '../enums';
+
+defineOptions({ name: 'SrmTender' });
+
+const router = useRouter();
+
+function handleDetail(row: SrmTenderApi.TenderProjectVO) {
+  router.push({ path: '/srm/tender/detail', query: { id: row.id } });
+}
+
+function handleRefresh() {
+  gridApi.query();
+}
+
+function handleCreate() {
+  formModalApi.setData(null).open();
+}
+
+function handleEdit(row: SrmTenderApi.TenderProjectVO) {
+  formModalApi.setData(row).open();
+}
+
+async function handleDelete(row: SrmTenderApi.TenderProjectVO) {
+  const hideLoading = message.loading({
+    content: $t('ui.actionMessage.deleting', [row.tenderName]),
+    duration: 0,
+  });
+  try {
+    await deleteTenderProject(row.id!);
+    message.success($t('ui.actionMessage.deleteSuccess', [row.tenderName]));
+    handleRefresh();
+  } finally {
+    hideLoading();
+  }
+}
+
+async function handlePublish(row: SrmTenderApi.TenderProjectVO) {
+  await publishTender(row.id!);
+  message.success('鍙戝竷鎴愬姛');
+  handleRefresh();
+}
+
+async function handleStartBidding(row: SrmTenderApi.TenderProjectVO) {
+  await startBidding(row.id!);
+  message.success('宸插紑濮嬫姇鏍�');
+  handleRefresh();
+}
+
+async function handleStartEvaluation(row: SrmTenderApi.TenderProjectVO) {
+  await startEvaluation(row.id!);
+  message.success('宸插紑濮嬭瘎鏍�');
+  handleRefresh();
+}
+
+async function handleClose(row: SrmTenderApi.TenderProjectVO) {
+  await closeTender(row.id!);
+  message.success('宸插叧闂�');
+  handleRefresh();
+}
+
+async function handleGeneratePurchaseOrder(row: SrmTenderApi.TenderProjectVO) {
+  if (!row.awardId) return;
+  await generatePurchaseOrder(row.awardId);
+  message.success('閲囪喘璁㈠崟宸茬敓鎴�');
+  handleRefresh();
+}
+
+const [FormModal, formModalApi] = useVbenModal({
+  connectedComponent: TenderForm,
+  destroyOnClose: true,
+});
+
+const [Grid, gridApi] = useVbenVxeGrid({
+  formOptions: {
+    schema: useGridFormSchema(),
+  },
+  gridOptions: {
+    columns: useGridColumns(),
+    height: 'auto',
+    keepSource: true,
+    proxyConfig: {
+      ajax: {
+        query: async ({ page }, formValues) => {
+          return await getTenderProjectPage({
+            pageNo: page.currentPage,
+            pageSize: page.pageSize,
+            ...formValues,
+          });
+        },
+      },
+    },
+    rowConfig: { keyField: 'id', isHover: true },
+    toolbarConfig: { refresh: true, search: true },
+  } as VxeTableGridOptions<SrmTenderApi.TenderProjectVO>,
+});
+</script>
+
+<template>
+  <Page auto-content-height>
+    <FormModal @success="handleRefresh" />
+    <Grid table-title="鎷涙爣椤圭洰鍒楄〃">
+      <template #toolbar-tools>
+        <TableAction
+          :actions="[
+            {
+              label: $t('ui.actionTitle.create', ['鎷涙爣椤圭洰']),
+              type: 'primary',
+              icon: ACTION_ICON.ADD,
+              auth: ['srm:tender-project:create'],
+              onClick: handleCreate,
+            },
+          ]"
+        />
+      </template>
+      <template #actions="{ row }">
+        <TableAction
+          :actions="[
+            {
+              label: '璇︽儏',
+              type: 'link',
+              icon: ACTION_ICON.EDIT,
+              onClick: handleDetail.bind(null, row),
+            },
+            {
+              label: '鍙戝竷',
+              type: 'link',
+              auth: ['srm:tender-project:update'],
+              ifShow: row.tenderStatus === TENDER_STATUS.DRAFT,
+              popConfirm: {
+                title: '纭瑕佸彂甯冭椤圭洰鍚楋紵鍙戝竷鍚庢棤娉曚慨鏀广��',
+                confirm: handlePublish.bind(null, row),
+              },
+            },
+            {
+              label: $t('common.edit'),
+              type: 'link',
+              icon: ACTION_ICON.EDIT,
+              auth: ['srm:tender-project:update'],
+              ifShow: row.tenderStatus === TENDER_STATUS.DRAFT,
+              onClick: handleEdit.bind(null, row),
+            },
+            {
+              label: $t('common.delete'),
+              type: 'link',
+              danger: true,
+              icon: ACTION_ICON.DELETE,
+              auth: ['srm:tender-project:delete'],
+              ifShow: row.tenderStatus === TENDER_STATUS.DRAFT,
+              popConfirm: {
+                title: $t('ui.actionMessage.deleteConfirm', [row.tenderName]),
+                confirm: handleDelete.bind(null, row),
+              },
+            },
+            {
+              label: '寮�濮嬫姇鏍�',
+              type: 'link',
+              auth: ['srm:tender-project:update'],
+              ifShow: row.tenderStatus === TENDER_STATUS.PUBLISHED,
+              popConfirm: {
+                title: '纭瑕佸紑濮嬫姇鏍囧悧锛�',
+                confirm: handleStartBidding.bind(null, row),
+              },
+            },
+            {
+              label: '鍏抽棴',
+              type: 'link',
+              danger: true,
+              auth: ['srm:tender-project:update'],
+              ifShow: row.tenderStatus === TENDER_STATUS.PUBLISHED || row.tenderStatus === TENDER_STATUS.BIDDING,
+              popConfirm: {
+                title: '纭瑕佸叧闂椤圭洰鍚楋紵',
+                confirm: handleClose.bind(null, row),
+              },
+            },
+            {
+              label: '寮�濮嬭瘎鏍�',
+              type: 'link',
+              auth: ['srm:tender-project:update'],
+              ifShow: row.tenderStatus === TENDER_STATUS.BIDDING,
+              popConfirm: {
+                title: '纭瑕佸紑濮嬭瘎鏍囧悧锛�',
+                confirm: handleStartEvaluation.bind(null, row),
+              },
+            },
+            {
+              label: '鐢熸垚閲囪喘璁㈠崟',
+              type: 'link',
+              auth: ['srm:bid-award:update'],
+              ifShow: !!row.awardId && !row.purchaseOrderNo,
+              popConfirm: {
+                title: '纭瑕佺敓鎴愰噰璐鍗曞悧锛�',
+                confirm: handleGeneratePurchaseOrder.bind(null, row),
+              },
+            },
+          ]"
+        />
+      </template>
+    </Grid>
+  </Page>
+</template>
diff --git a/src/views/srm/tender/modules/form.vue b/src/views/srm/tender/modules/form.vue
new file mode 100644
index 0000000..49520dd
--- /dev/null
+++ b/src/views/srm/tender/modules/form.vue
@@ -0,0 +1,84 @@
+<script lang="ts" setup>
+import type { SrmTenderApi } from '#/api/srm/tender';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { useVbenForm } from '#/adapter/form';
+import {
+  createTenderProject,
+  getTenderProject,
+  updateTenderProject,
+} from '#/api/srm/tender';
+import { $t } from '#/locales';
+
+import { useFormSchema } from '../data';
+
+const emit = defineEmits(['success']);
+const formData = ref<SrmTenderApi.TenderProjectVO>();
+const getTitle = computed(() => {
+  return formData.value?.id
+    ? $t('ui.actionTitle.edit', ['鎷涙爣椤圭洰'])
+    : $t('ui.actionTitle.create', ['鎷涙爣椤圭洰']);
+});
+
+const [Form, formApi] = useVbenForm({
+  commonConfig: {
+    componentProps: { class: 'w-full' },
+    labelWidth: 120,
+  },
+  wrapperClass: 'grid-cols-2',
+  layout: 'horizontal',
+  schema: useFormSchema(),
+  showDefaultActions: false,
+});
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const { valid } = await formApi.validate();
+    if (!valid) {
+      return;
+    }
+    modalApi.lock();
+    const data = (await formApi.getValues()) as SrmTenderApi.TenderProjectVO;
+    try {
+      await (formData.value?.id
+        ? updateTenderProject(data)
+        : createTenderProject(data));
+      await modalApi.close();
+      emit('success');
+      message.success($t('ui.actionMessage.operationSuccess'));
+    } finally {
+      modalApi.unlock();
+    }
+  },
+  async onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      formData.value = undefined;
+      return;
+    }
+    const data = modalApi.getData<SrmTenderApi.TenderProjectVO>();
+    if (!data || !data.id) {
+      return;
+    }
+    modalApi.lock();
+    try {
+      formData.value = await getTenderProject(data.id);
+      await formApi.setValues(formData.value);
+    } finally {
+      modalApi.unlock();
+    }
+  },
+});
+
+defineExpose({ modalApi });
+</script>
+
+<template>
+  <Modal :title="getTitle" class="w-1/2">
+    <Form class="mx-4" />
+  </Modal>
+</template>
diff --git a/src/views/system/tenant/data.ts b/src/views/system/tenant/data.ts
index b05e638..7cb9049 100644
--- a/src/views/system/tenant/data.ts
+++ b/src/views/system/tenant/data.ts
@@ -102,7 +102,7 @@
       component: 'DatePicker',
       componentProps: {
         format: 'YYYY-MM-DD',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨杩囨湡鏃堕棿',
       },
       rules: 'required',
diff --git a/src/views/wls/arrivalnotice/data.ts b/src/views/wls/arrivalnotice/data.ts
index 812b6dd..4fd3a0b 100644
--- a/src/views/wls/arrivalnotice/data.ts
+++ b/src/views/wls/arrivalnotice/data.ts
@@ -125,7 +125,7 @@
         class: '!w-full',
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍒拌揣鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
diff --git a/src/views/wls/itemreceipt/data.ts b/src/views/wls/itemreceipt/data.ts
index 4f72501..291714f 100644
--- a/src/views/wls/itemreceipt/data.ts
+++ b/src/views/wls/itemreceipt/data.ts
@@ -101,7 +101,7 @@
         class: '!w-full',
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍏ュ簱鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
@@ -385,7 +385,7 @@
         class: '!w-full',
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鐢熶骇鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
     },
     {
@@ -396,7 +396,7 @@
         class: '!w-full',
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鏈夋晥鏈�',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
     },
     {
diff --git a/src/views/wls/miscissue/data.ts b/src/views/wls/miscissue/data.ts
index a61e01b..5018e10 100644
--- a/src/views/wls/miscissue/data.ts
+++ b/src/views/wls/miscissue/data.ts
@@ -101,7 +101,7 @@
       componentProps: {
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍑哄簱鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
diff --git a/src/views/wls/miscreceipt/data.ts b/src/views/wls/miscreceipt/data.ts
index 25bc48e..6614440 100644
--- a/src/views/wls/miscreceipt/data.ts
+++ b/src/views/wls/miscreceipt/data.ts
@@ -116,7 +116,7 @@
       componentProps: {
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍏ュ簱鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
diff --git a/src/views/wls/outsourceissue/data.ts b/src/views/wls/outsourceissue/data.ts
index ddd8661..dd70166 100644
--- a/src/views/wls/outsourceissue/data.ts
+++ b/src/views/wls/outsourceissue/data.ts
@@ -100,7 +100,7 @@
       componentProps: {
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍙戞枡鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
     },
     {
diff --git a/src/views/wls/outsourcereceipt/data.ts b/src/views/wls/outsourcereceipt/data.ts
index bc03596..4d336b4 100644
--- a/src/views/wls/outsourcereceipt/data.ts
+++ b/src/views/wls/outsourcereceipt/data.ts
@@ -122,7 +122,7 @@
       componentProps: {
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍏ュ簱鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
@@ -363,7 +363,7 @@
         class: '!w-full',
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鐢熶骇鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
     },
     {
@@ -374,7 +374,7 @@
         class: '!w-full',
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鏈夋晥鏈�',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
     },
     {
diff --git a/src/views/wls/packages/data.ts b/src/views/wls/packages/data.ts
index 3c0b502..14358b2 100644
--- a/src/views/wls/packages/data.ts
+++ b/src/views/wls/packages/data.ts
@@ -78,7 +78,7 @@
       componentProps: {
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨瑁呯鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
@@ -455,7 +455,7 @@
       componentProps: {
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鏈夋晥鏈�',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
     },
     {
diff --git a/src/views/wls/productissue/data.ts b/src/views/wls/productissue/data.ts
index 28f37ed..2ee6dcc 100644
--- a/src/views/wls/productissue/data.ts
+++ b/src/views/wls/productissue/data.ts
@@ -101,7 +101,7 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨闇�姹傛椂闂�',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/wls/productreceipt/data.ts b/src/views/wls/productreceipt/data.ts
index 431138e..07cc1bf 100644
--- a/src/views/wls/productreceipt/data.ts
+++ b/src/views/wls/productreceipt/data.ts
@@ -98,7 +98,7 @@
         class: '!w-full',
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍏ュ簱鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
diff --git a/src/views/wls/productsales/data.ts b/src/views/wls/productsales/data.ts
index 31a2dec..9522d75 100644
--- a/src/views/wls/productsales/data.ts
+++ b/src/views/wls/productsales/data.ts
@@ -182,7 +182,7 @@
         disabled: isHeaderReadonly(formType),
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍑哄簱鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
diff --git a/src/views/wls/returnissue/data.ts b/src/views/wls/returnissue/data.ts
index d95b2c4..4ca6336 100644
--- a/src/views/wls/returnissue/data.ts
+++ b/src/views/wls/returnissue/data.ts
@@ -128,7 +128,7 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨閫�鏂欐棩鏈�',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
     },
     {
diff --git a/src/views/wls/returnsales/data.ts b/src/views/wls/returnsales/data.ts
index 4c983da..fadbd4a 100644
--- a/src/views/wls/returnsales/data.ts
+++ b/src/views/wls/returnsales/data.ts
@@ -129,7 +129,7 @@
         class: '!w-full',
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨閫�璐ф棩鏈�',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
diff --git a/src/views/wls/returnvendor/data.ts b/src/views/wls/returnvendor/data.ts
index 5d2b324..1903c81 100644
--- a/src/views/wls/returnvendor/data.ts
+++ b/src/views/wls/returnvendor/data.ts
@@ -137,7 +137,7 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         placeholder: '璇烽�夋嫨閫�璐ф棩鏈�',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       rules: 'required',
     },
diff --git a/src/views/wls/salesnotice/data.ts b/src/views/wls/salesnotice/data.ts
index e295707..6818446 100644
--- a/src/views/wls/salesnotice/data.ts
+++ b/src/views/wls/salesnotice/data.ts
@@ -114,7 +114,7 @@
         class: '!w-full',
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍙戣揣鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       rules: 'required',
     },
diff --git a/src/views/wls/stocktaking/plan/data.ts b/src/views/wls/stocktaking/plan/data.ts
index cd3059b..a8b2d91 100644
--- a/src/views/wls/stocktaking/plan/data.ts
+++ b/src/views/wls/stocktaking/plan/data.ts
@@ -91,7 +91,7 @@
       componentProps: {
         placeholder: '璇烽�夋嫨寮�濮嬫椂闂�',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       dependencies: {
         triggerFields: ['type'],
@@ -105,7 +105,7 @@
       componentProps: {
         placeholder: '璇烽�夋嫨缁撴潫鏃堕棿',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       dependencies: {
         triggerFields: ['type'],
diff --git a/src/views/wls/stocktaking/task/data.ts b/src/views/wls/stocktaking/task/data.ts
index 762de99..ed62e0c 100644
--- a/src/views/wls/stocktaking/task/data.ts
+++ b/src/views/wls/stocktaking/task/data.ts
@@ -134,7 +134,7 @@
       componentProps: {
         placeholder: '璇烽�夋嫨寮�濮嬫椂闂�',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       dependencies: {
         triggerFields: ['type'],
@@ -148,7 +148,7 @@
       componentProps: {
         placeholder: '璇烽�夋嫨缁撴潫鏃堕棿',
         showTime: true,
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
       dependencies: {
         triggerFields: ['type'],
diff --git a/src/views/wms/order/check/data.ts b/src/views/wms/order/check/data.ts
index 0efaec3..3e3ba5b 100644
--- a/src/views/wms/order/check/data.ts
+++ b/src/views/wms/order/check/data.ts
@@ -396,7 +396,7 @@
         class: 'w-full',
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍗曟嵁鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       fieldName: 'orderTime',
       label: '鍗曟嵁鏃ユ湡',
diff --git a/src/views/wms/order/movement/data.ts b/src/views/wms/order/movement/data.ts
index 2ae4ac6..4fcb2c2 100644
--- a/src/views/wms/order/movement/data.ts
+++ b/src/views/wms/order/movement/data.ts
@@ -319,7 +319,7 @@
         class: 'w-full',
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍗曟嵁鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       fieldName: 'orderTime',
       label: '鍗曟嵁鏃ユ湡',
diff --git a/src/views/wms/order/receipt/data.ts b/src/views/wms/order/receipt/data.ts
index eebdadb..e31e002 100644
--- a/src/views/wms/order/receipt/data.ts
+++ b/src/views/wms/order/receipt/data.ts
@@ -359,7 +359,7 @@
         class: 'w-full',
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍗曟嵁鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       fieldName: 'orderTime',
       label: '鍗曟嵁鏃ユ湡',
diff --git a/src/views/wms/order/shipment/data.ts b/src/views/wms/order/shipment/data.ts
index afcc3c9..7d41840 100644
--- a/src/views/wms/order/shipment/data.ts
+++ b/src/views/wms/order/shipment/data.ts
@@ -369,7 +369,7 @@
         class: 'w-full',
         format: 'YYYY-MM-DD',
         placeholder: '璇烽�夋嫨鍗曟嵁鏃ユ湡',
-        valueFormat: 'x',
+        valueFormat: 'YYYY-MM-DD',
       },
       fieldName: 'orderTime',
       label: '鍗曟嵁鏃ユ湡',

--
Gitblit v1.9.3