From 07f9f8657d057a38792c3822acc9b08d83478967 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 07 五月 2026 14:23:10 +0800
Subject: [PATCH] 合并代码

---
 src/views/financialManagement/accounting/index.vue                                     |    1 
 src/assets/styles/index.scss                                                           |   49 
 src/api/inventoryManagement/stockInRecord.js                                           |   17 
 src/layout/index.vue                                                                   |  250 
 src/api/inventoryManagement/stockInventory.js                                          |   35 
 src/views/collaborativeApproval/approvalManagement/index.vue                           |  881 +
 src/views/collaborativeApproval/sealManagement/index.vue                               |    4 
 src/views/productionManagement/productStructure/index.vue                              |  865 
 src/views/tool/build/CodeTypeDialog.vue                                                |    2 
 src/views/salesManagement/invoiceLedger/index.vue                                      |   29 
 src/views/inventoryManagement/receiptManagement/index.vue                              |   59 
 src/views/equipmentManagement/calibration/index.vue                                    |    2 
 src/views/financialManagement/voucher/generalLedger.vue                                |  230 
 src/views/personnelManagement/analytics/index.vue                                      |    1 
 src/views/collaborativeApproval/meetingBoard/index.vue                                 |    2 
 src/api/basicData/parameterMaintenance.js                                              |   81 
 src/utils/generator/html.js                                                            |    4 
 src/api/basicData/customer.js                                                          |   75 
 src/views/productionManagement/workOrderEdit/index.vue                                 |  266 
 src/components/Breadcrumb/index.vue                                                    |   43 
 src/views/financialManagement/receivable/salesReturn.vue                               |  305 
 src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue     |  284 
 src/views/reportAnalysis/reportManagement/index.vue                                    |    1 
 src/api/system/appVersion.js                                                           |   19 
 src/views/customerService/feedbackRegistration/index.vue                               |   18 
 src/views/personnelManagement/employeeRecord/components/JobInfoSection.vue             |   30 
 src/views/procurementManagement/purchaseReturnOrder/New.vue                            |  278 
 src/views/salesManagement/orderManagement/index.vue                                    |    2 
 src/views/reportAnalysis/dataDashboard/index0.vue                                      |    2 
 src/views/collaborativeApproval/noticeManagement/index.vue                             |    6 
 src/views/system/role/index.vue                                                        |    2 
 src/views/equipmentManagement/upkeep/Form/PlanModal.vue                                |   26 
 jsconfig.json                                                                          |   10 
 src/views/lavorissue/statistics/index.vue                                              |    2 
 src/views/procurementManagement/paymentEntry/index.vue                                 |   32 
 src/api/productionManagement/productionCosting.js                                      |    4 
 src/assets/styles/variables.module.scss                                                |  446 
 src/views/qualityManagement/metricBinding/index.vue                                    |    4 
 src/views/safeProduction/accidentReportingRecord/index.vue                             |    2 
 src/components/AttachmentPreview/image/index.vue                                       |   76 
 src/views/financialManagement/receivable/reconciliation.vue                            |  469 
 src/views/customerService/afterSalesHandling/index.vue                                 |  167 
 src/views/collaborativeApproval/knowledgeBase/index.vue                                |    4 
 src/views/financialManagement/voucher/detailLedger.vue                                 |  289 
 src/views/collaborativeApproval/attendanceManagement/index.vue                         |    2 
 src/views/fileManagement/document/index.vue                                            |   10 
 src/views/procurementManagement/procurementReport/index.vue                            |   33 
 src/api/procurementManagement/procurementInvoiceLedger.js                              |    8 
 src/views/equipmentManagement/brand/index.vue                                          |    2 
 src/views/collaborativeApproval/customerVisit/index.vue                                |    2 
 src/api/inventoryManagement/stockOut.js                                                |   18 
 src/api/basicData/productModel.js                                                      |    8 
 src/views/inventoryManagement/stockManagement/Subtract.vue                             |   48 
 src/views/personnelManagement/monthlyStatistics/index.vue                              |    2 
 src/views/productionManagement/productionProcess/index.vue                             | 1306 +
 src/views/basicData/product/ProductSelectDialog.vue                                    |   29 
 src/views/financialManagement/receivable/salesOut.vue                                  |  271 
 src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue       |  320 
 src/views/collaborativeApproval/rulesRegulationsManagement/index.vue                   |   89 
 src/api/procurementManagement/purchase_return_order.js                                 |   18 
 src/api/productionManagement/productionOrder.js                                        |  106 
 src/api/salesManagement/deliveryLedger.js                                              |    7 
 src/views/productionManagement/productionProcess/Edit.vue                              |   12 
 src/views/salesManagement/returnOrder/index.vue                                        |   57 
 src/views/personnelManagement/attendanceCheckin/checkinRules/components/form.vue       |    6 
 src/layout/components/Sidebar/Logo.vue                                                 |  271 
 src/views/safeProduction/safeWorkApproval/index.vue                                    |    2 
 src/views/productionManagement/processStatistics/index.vue                             |  268 
 src/views/collaborativeApproval/planTemplate/index.vue                                 |    2 
 src/views/inventoryManagement/receiptManagement/Record.vue                             |   91 
 src/views/equipmentManagement/upkeep/index.vue                                         | 1108 
 src/views/salesManagement/deliveryLedger/index.vue                                     |  468 
 src/views/equipmentManagement/repair/Modal/MaintainModal.vue                           |  115 
 src/views/personnelManagement/employeeRecord/index.vue                                 |  141 
 src/views/productionPlan/productionPlan/components/PIMTable.vue                        |  470 
 src/views/safeProduction/hazardSourceLedger/index.vue                                  |    2 
 src/router/index.js                                                                    |  127 
 src/api/productionManagement/productProcessRoute.js                                    |   43 
 src/views/inventoryManagement/transportTaskManagement/index.vue                        |    4 
 src/components/PurchaseAIChatSidebar/index.vue                                         |   26 
 src/views/projectManagement/roles/index.vue                                            |   79 
 src/views/productionManagement/productionOrder/components/PrintMaterialRequisition.vue |  225 
 src/api/productionPlan/productionPlan.js                                               |   79 
 src/views/customerService/feedbackRegistration/components/formDia.vue                  |   45 
 src/components/AttachmentUpload/image/index.vue                                        |  335 
 src/views/productionManagement/workOrder/index.vue                                     |    2 
 src/views/inventoryManagement/dispatchLog/Record.vue                                   |   94 
 src/components/ProjectManagement/ProgressReportDialog.vue                              |   50 
 src/api/productionManagement/productStructure.js                                       |   35 
 src/views/procurementManagement/purchaseReturnOrder/index.vue                          |  289 
 src/api/productionManagement/productBom.js                                             |   24 
 src/components/Dialog/FileListDialog.vue                                               |    1 
 src/views/basicData/supplierManage/components/HomeTab.vue                              |    6 
 src/views/collaborativeApproval/enterpriseBook/index.vue                               |    2 
 src/views/productionManagement/productionCosting/index.vue                             |  698 
 src/components/SvgIcon/index.vue                                                       |    2 
 src/views/financialManagement/receivable/invoiceApply.vue                              |  363 
 src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue       |   33 
 src/views/productionManagement/productStructure/Detail/index.vue                       |  121 
 src/views/login.vue                                                                    |  633 
 src/views/oaSystem/projectManagement/components/milestoneList.vue                      |    2 
 src/components/AttachmentUpload/file/index.vue                                         |  309 
 src/views/personnelManagement/dimission/index.vue                                      |    2 
 src/views/safeProduction/hazardousMaterialsControl/index.vue                           |    2 
 src/views/collaborativeApproval/notificationManagement/summary/index.vue               |    6 
 src/api/productionManagement/workOrder.js                                              |   68 
 src/api/collaborativeApproval/approvalManagement.js                                    |   20 
 src/api/inventoryManagement/stockUninventory.js                                        |   18 
 src/views/basicData/product/index.vue                                                  | 1016 
 src/views/financialManagement/payable/payment.vue                                      |  377 
 src/views/financialManagement/salesRefund/components/ReceiptandRefundPopupWindow.vue   |    5 
 src/views/inventoryManagement/stockManagement/FrozenAndThaw.vue                        |   60 
 src/views/financialManagement/generalLedger/index.vue                                  |  291 
 src/views/qualityManagement/rawMaterialInspection/index.vue                            |    2 
 src/views/inventoryManagement/stockWarning/index.vue                                   |    4 
 src/views/qualityManagement/finalInspection/index.vue                                  |  262 
 src/views/qualityManagement/processInspection/index.vue                                |    2 
 src/views/financialManagement/payable/input-invoice.vue                                |  409 
 src/views/productionManagement/workOrderManagement/index.vue                           |  309 
 src/api/personnelManagement/class.js                                                   |    3 
 src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue     |  391 
 src/views/productionManagement/processRoute/New.vue                                    |  285 
 src/views/productionManagement/productionTraceability/index.vue                        |  706 
 src/views/inventoryManagement/stockManagement/index.vue                                |   57 
 src/components/Dialog/FileList.vue                                                     |  245 
 src/views/equipmentManagement/ledger/index.vue                                         |   88 
 src/views/safeProduction/safetyTrainingAssessment/index.vue                            |   51 
 src/views/inventoryManagement/dispatchLog/index.vue                                    |   61 
 src/store/modules/user.js                                                              |   53 
 src/api/basicData/customerFile.js                                                      |   72 
 src/views/financialManagement/receivable/receipt.vue                                   |  356 
 src/views/financialManagement/revenueManagement/index.vue                              |  445 
 src/views/financialManagement/salesRefund/index.vue                                    |    4 
 src/views/financialManagement/assets/fixedAssets.vue                                   |  462 
 src/api/basicData/common.js                                                            |   25 
 src/views/collaborativeApproval/approvalProcess/fileList.vue                           |    6 
 src/views/qualityManagement/nonconformingManagement/index.vue                          |   36 
 src/views/salesManagement/receiptPaymentHistory/index.vue                              |    1 
 src/views/procurementManagement/procurementInvoiceLedger/index.vue                     |  159 
 src/views/productionManagement/processRoute/index.vue                                  |  424 
 src/views/productionManagement/processRoute/processRouteItem/index.vue                 | 2162 +
 src/views/collaborativeApproval/notificationManagement/meetSetting/index.vue           |    6 
 src/views/salesManagement/receiptPaymentLedger/index.vue                               |    2 
 src/views/collaborativeApproval/approvalProcess/index.vue                              |  474 
 src/views/safeProduction/emergencyPlanReview/index.vue                                 |    2 
 src/views/financialManagement/voucher/index.vue                                        |  836 +
 src/views/salesManagement/salesLedger/index.vue                                        | 4440 ++--
 src/components/Editor/index.vue                                                        |   54 
 src/api/productionManagement/processRouteItem.js                                       |   62 
 src/views/fileManagement/return/index.vue                                              |   35 
 src/api/productionManagement/processRoute.js                                           |   29 
 src/layout/components/Sidebar/index.vue                                                |  236 
 src/views/financialManagement/assets/intangibleAssets.vue                              |  458 
 src/views/inventoryManagement/stockManagement/Record.vue                               |  223 
 src/views/inventoryManagement/stockManagement/Unqualified.vue                          |    8 
 src/views/salesManagement/salesLedger/fileList.vue                                     |    4 
 src/views/safeProduction/safeQualifications/index.vue                                  |   46 
 src/views/example/DynamicTableExample.vue                                              |    2 
 src/views/personnelManagement/socialSecuritySet/index.vue                              |    2 
 src/views/equipmentManagement/spareParts/index.vue                                     |  291 
 src/views/fileManagement/bookshelf/index.vue                                           |    6 
 src/views/salesManagement/receiptPayment/index.vue                                     |    5 
 src/assets/styles/sidebar.scss                                                         |  271 
 src/api/productionManagement/processRouteFile.js                                       |   28 
 src/assets/styles/element-ui.scss                                                      |  193 
 src/views/equipmentManagement/defectManagement/index.vue                               |    2 
 src/components/PIMTable/PIMTable.vue                                                   |  785 
 src/views/basicData/customerFileOpenSea/index.vue                                      | 1803 ++
 src/layout/components/TagsView/index.vue                                               |  178 
 src/views/inventoryManagement/stockManagement/Qualified.vue                            |    9 
 src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue             |  202 
 src/views/salesManagement/invoiceRegistration/index.vue                                |    2 
 src/views/collaborativeApproval/notificationManagement/index.vue                       |    2 
 src/views/customerService/expiryAfterSales/index.vue                                   |   11 
 src/views/personnelManagement/classsSheduling/index.vue                                |   21 
 src/views/personnelManagement/attendanceCheckin/checkinRules/index.vue                 |    2 
 src/api/basicData/storageAttachment.js                                                 |   29 
 src/layout/components/AppMain.vue                                                      |   11 
 src/api/basicData/productProcess.js                                                    |   10 
 src/views/financialManagement/receivable/outputInvoice.vue                             |  373 
 src/views/collaborativeApproval/rpaManagement/index.vue                                |    2 
 src/views/oaSystem/projectManagement/projectDetail.vue                                 |    4 
 src/views/safeProduction/dangerInvestigation/index.vue                                 |  178 
 src/views/index.vue                                                                    |    3 
 src/views/personnelManagement/attendanceCheckin/index.vue                              |    5 
 src/views/basicData/parameterMaintenance/index.vue                                     |  793 
 src/views/reportAnalysis/productionAnalysis/components/left-top.vue                    |    2 
 src/components/PageHeader/index.vue                                                    |   10 
 src/views/personnelManagement/contractManagement/index.vue                             |    2 
 src/views/equipmentManagement/ledger/Form.vue                                          |   21 
 src/views/collaborativeApproval/purchaseApproval/index.vue                             | 1960 +-
 src/views/inventoryManagement/vehicleFuelManagement/index.vue                          |    2 
 src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue                 |    2 
 src/views/basicData/customerFile/index.vue                                             |  125 
 src/views/fileManagement/borrow/index.vue                                              |   35 
 src/views/qualityManagement/metricMaintenance/index.vue                                |   23 
 src/views/projectManagement/projectType/components/ProjectTypeDialog.vue               |   58 
 vite.config.js                                                                         |    4 
 src/views/inventoryManagement/stockManagement/New.vue                                  |  330 
 src/views/equipmentManagement/repair/Modal/RepairModal.vue                             |   19 
 src/views/salesManagement/strategyControl/index.vue                                    |    4 
 src/views/inventoryManagement/issueManagement/index.vue                                |    2 
 src/views/salesManagement/salesQuotation/index.vue                                     |  274 
 src/views/financialManagement/payable/paymentApply.vue                                 |  360 
 src/views/financialManagement/expenseManagement/index.vue                              |  176 
 src/views/oaSystem/projectManagement/components/taskTree.vue                           |    2 
 src/views/procurementManagement/procurementLedger/index.vue                            |  416 
 src/views/equipmentManagement/measurementEquipment/index.vue                           |    2 
 src/views/financialManagement/payable/reconciliation.vue                               |  469 
 src/views/collaborativeApproval/processTracking/index.vue                              |    2 
 src/views/productionManagement/productionReporting/index.vue                           |   61 
 src/views/financialManagement/payable/purchaseIn.vue                                   |  331 
 src/main.js                                                                            |    9 
 src/layout/components/TagsView/ScrollPane.vue                                          |    2 
 src/views/productionManagement/productionOrder/index.vue                               |  743 
 FILE_UPLOAD_README.md                                                                  |  480 
 src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue                         |  115 
 src/views/productionManagement/productionProcess/New.vue                               |    8 
 src/views/procurementManagement/paymentHistory/index.vue                               |    1 
 src/views/safeProduction/safeWorkApproval/components/infoFormDia.vue                   |   76 
 src/views/projectManagement/projectType/index.vue                                      |    2 
 src/views/system/appVersion/index.vue                                                  |  270 
 src/layout/components/Navbar.vue                                                       |  116 
 src/views/equipmentManagement/inspectionManagement/components/formDia.vue              |    2 
 src/components/ProcessParamListDialog.vue                                              |  642 
 src/views/productManagement/productIdentifier/index.vue                                |    2 
 src/api/equipmentManagement/sparePartsUsage.js                                         |   36 
 src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue             |   40 
 src/views/productionManagement/productionOrder/New.vue                                 |  300 
 src/views/salesManagement/returnOrder/components/formDia.vue                           |   21 
 src/views/projectManagement/Management/components/formDia.vue                          |   11 
 src/views/oaSystem/projectManagement/index.vue                                         |    2 
 src/api/productionManagement/productionProcess.js                                      |   75 
 src/views/equipmentManagement/repair/index.vue                                         |   25 
 src/views/productionManagement/productionOrder/components/MaterialSupplementDialog.vue |  165 
 src/store/modules/permission.js                                                        |   49 
 src/components/AIChatSidebar/index.vue                                                 | 4423 +++++
 src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue            |   63 
 src/views/procurementManagement/paymentLedger/index.vue                                |    2 
 src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue                |    2 
 /dev/null                                                                              |  256 
 src/views/personnelManagement/dimission/components/formDia.vue                         |    1 
 src/views/salesManagement/indicatorStats/index.vue                                     |    1 
 src/views/systemArchitecture/index.vue                                                 |  436 
 src/views/productionPlan/productionPlan/index.vue                                      | 1332 +
 src/views/projectManagement/Management/projectDetail.vue                               |    2 
 src/components/PIMTable/Pagination.vue                                                 |    1 
 src/views/projectManagement/Management/index.vue                                       |   10 
 src/views/energyManagement/dynamicEnergySaving/index.vue                               |    6 
 249 files changed, 38,268 insertions(+), 11,527 deletions(-)

diff --git a/FILE_UPLOAD_README.md b/FILE_UPLOAD_README.md
new file mode 100644
index 0000000..ad5b42a
--- /dev/null
+++ b/FILE_UPLOAD_README.md
@@ -0,0 +1,480 @@
+# 鏈湴鏂囦欢涓婁紶 README
+
+鏈枃妗e熀浜庝互涓嬪疄鐜版暣鐞嗭細
+
+- `src/components/AttachmentUpload/file/index.vue`
+- `src/components/AttachmentUpload/image/index.vue`
+- `src/components/AttachmentPreview/image/index.vue`
+- `src/components/Dialog/FileList.vue`
+- `src/api/basicData/common.js`
+- `src/api/publicApi/commonFile.js`
+
+鐩稿叧缁勪欢宸插湪 `src/main.js` 涓敞鍐屼负鍏ㄥ眬缁勪欢锛屽彲鐩存帴鍦ㄩ〉闈腑浣跨敤锛�
+
+- `FileUpload`
+- `ImageUpload`
+- `ImagePreview`
+- `FileListDialog`
+
+## 1. 鍔熻兘姒傝
+
+褰撳墠杩欏涓婁紶鑳藉姏涓昏鍒嗕负 4 閮ㄥ垎锛�
+
+1. `FileUpload`锛氭櫘閫氭枃浠朵笂浼狅紝鏀寔鎷栨嫿銆佹壒閲忎笂浼犮�侀瑙堛�佸垹闄�
+2. `ImageUpload`锛氬浘鐗囦笂浼狅紝鏀寔鍥剧墖澧欏睍绀恒�侀瑙堛�佸垹闄�
+3. `ImagePreview`锛氬浘鐗囧垪琛ㄩ瑙堝睍绀�
+4. `FileListDialog`锛氫笟鍔¢檮浠跺脊绐楋紝鏀寔鏌ヨ銆佷笂浼犮�佸垹闄ゃ�佷笅杞�
+
+涓婁紶搴曞眰缁熶竴璧版帴鍙o細
+
+- `POST /common/upload`
+
+瀵瑰簲鏂规硶鍦� `src/api/basicData/common.js`锛�
+
+```js
+uploadFile(data)
+```
+
+## 2. 涓婁紶鎺ュ彛璇存槑
+
+### 2.1 閫氱敤涓婁紶鎺ュ彛
+
+鏂囦欢涓婁紶缁勪欢鍜屽浘鐗囦笂浼犵粍浠堕兘璋冪敤浜嗭細
+
+```js
+import { uploadFile } from '@/api/basicData/common'
+```
+
+鎺ュ彛鐗瑰緛锛�
+
+- 璇锋眰鏂瑰紡锛歚POST`
+- 鍦板潃锛歚/common/upload`
+- 璇锋眰绫诲瀷锛歚multipart/form-data`
+- 鏀寔 `FormData` 鎵归噺涓婁紶
+- 榛樿瀛楁鍚嶏細`files`
+
+缁勪欢鍐呴儴浼氳繖鏍风粍瑁呭弬鏁帮細
+
+```js
+const formData = new FormData()
+validFiles.forEach((file) => {
+  formData.append(props.uploadFieldName, file.raw)
+})
+```
+
+### 2.2 涓婁紶杩斿洖鍊艰姹�
+
+涓婁紶鎴愬姛鍚庯紝缁勪欢浼氬皾璇曚粠浠ヤ笅缁撴瀯涓彁鍙栨暟缁勶細
+
+- `response`
+- `response.data`
+- `response.data.data`
+- `response.payload`
+- `response.payload.data`
+- `response.rows`
+- `response.result`
+
+鍥犳鍚庣杩斿洖鏁扮粍鏃讹紝涓婇潰浠绘剰涓�绉嶇粨鏋勯兘鍙互琚瘑鍒��
+
+缁勪欢灞曠ず鏃跺父鐢ㄥ埌鐨勫瓧娈垫湁锛�
+
+- 鏂囦欢鍚嶏細`name` / `originalFilename` / `fileName` / `uidFilename`
+- 鏂囦欢鍦板潃锛歚url` / `downloadURL`
+- 鍥剧墖鍦板潃锛歚url` / `previewURL` / `previewUrl`
+- 涓婚敭锛歚id`
+
+寤鸿涓婁紶鎺ュ彛杩斿洖鐨勫崟椤瑰璞″敖閲忓寘鍚細
+
+```js
+{
+  id: 1,
+  originalFilename: 'demo.pdf',
+  downloadURL: 'https://xxx/demo.pdf',
+  previewURL: 'https://xxx/demo.png'
+}
+```
+
+## 3. FileUpload 鏂囦欢涓婁紶缁勪欢
+
+缁勪欢璺緞锛�
+
+`src/components/AttachmentUpload/file/index.vue`
+
+### 3.1 鍩虹鐢ㄦ硶
+
+```vue
+<template>
+  <FileUpload v-model:file-list="fileList" />
+</template>
+
+<script setup>
+import { ref } from 'vue'
+
+const fileList = ref([])
+</script>
+```
+
+### 3.2 甯哥敤灞炴��
+
+| 灞炴�� | 璇存槑 | 绫诲瀷 | 榛樿鍊� |
+| --- | --- | --- | --- |
+| `fileList` | 缁戝畾鏂囦欢鍒楄〃 | `Array` | `[]` |
+| `limit` | 鏈�澶т笂浼犳暟閲� | `Number` | `10` |
+| `fileSize` | 鍗曚釜鏂囦欢澶у皬闄愬埗锛屽崟浣� MB | `Number` | `50` |
+| `fileType` | 鍏佽涓婁紶鐨勬枃浠剁被鍨嬶紝濡� `['pdf', 'docx']` | `Array` | `[]` |
+| `buttonText` | 涓婁紶鎻愮ず鏂囨 | `String` | `鍗曞嚮閫夋嫨鏂囦欢` |
+| `disabled` | 鏄惁绂佺敤 | `Boolean` | `false` |
+| `uploadFieldName` | `FormData` 瀛楁鍚� | `String` | `files` |
+| `index` | 琛ㄦ牸/鍒楄〃琛屾ā寮忎笅鐨勫綋鍓嶈绱㈠紩 | `Number` | `-1` |
+| `childrenKey` | 琛屽唴鎸傝浇瀛楁鍚� | `String` | `files` |
+
+### 3.3 浜嬩欢
+
+| 浜嬩欢 | 璇存槑 |
+| --- | --- |
+| `update:fileList` | 鏂囦欢鍒楄〃鍙樺寲鏃惰Е鍙� |
+| `change` | 鏂囦欢鍒楄〃鍙樺寲鏃惰Е鍙戯紝杩斿洖鏈�鏂板垪琛� |
+
+### 3.4 闄愬埗瑙勫垯
+
+缁勪欢鍐呭凡瀹炵幇锛�
+
+- 鏂囦欢鏁伴噺闄愬埗
+- 鏂囦欢澶у皬闄愬埗
+- 鏂囦欢绫诲瀷鏍¢獙
+- 涓婁紶涓姸鎬侀攣瀹�
+- 澶辫触鍚庤嚜鍔ㄦ竻绌哄綋鍓嶉�夋嫨闃熷垪
+
+渚嬪闄愬埗 PDF/Word锛�
+
+```vue
+<FileUpload
+  v-model:file-list="fileList"
+  :limit="5"
+  :file-size="20"
+  :file-type="['pdf', 'doc', 'docx']"
+/>
+```
+
+### 3.5 杩斿洖鏁版嵁鏍煎紡寤鸿
+
+`FileUpload` 鏇撮�傚悎鎺ユ敹杩欐牱鐨勫垪琛細
+
+```js
+[
+  {
+    id: 1,
+    originalFilename: '鍚堝悓.pdf',
+    downloadURL: 'https://xxx/contract.pdf'
+  }
+]
+```
+
+鍥犱负缁勪欢鎵撳紑鏂囦欢鏃朵細浼樺厛璇诲彇锛�
+
+```js
+url || downloadURL || previewURL || previewUrl
+```
+
+### 3.6 琛屽唴宓屽妯″紡
+
+濡傛灉涓婁紶缁勪欢鏀惧湪琛ㄦ牸鏌愪竴琛屼腑锛屽彲閰嶅悎 `index` 鍜� `childrenKey` 浣跨敤锛�
+
+```vue
+<FileUpload
+  v-model:file-list="tableData"
+  :index="scope.$index"
+  children-key="files"
+/>
+```
+
+姝ゆ椂缁勪欢浼氳嚜鍔ㄨ鍐欙細
+
+```js
+tableData[scope.$index].files
+```
+
+## 4. ImageUpload 鍥剧墖涓婁紶缁勪欢
+
+缁勪欢璺緞锛�
+
+`src/components/AttachmentUpload/image/index.vue`
+
+### 4.1 鍩虹鐢ㄦ硶
+
+```vue
+<template>
+  <ImageUpload v-model:file-list="imageList" />
+</template>
+
+<script setup>
+import { ref } from 'vue'
+
+const imageList = ref([])
+</script>
+```
+
+### 4.2 榛樿琛屼负
+
+鍥剧墖涓婁紶缁勪欢榛樿锛�
+
+- 鏈�澶氫笂浼� `10` 寮�
+- 鍗曞紶涓嶈秴杩� `10MB`
+- 榛樿鏀寔鏍煎紡锛歚png / jpg / jpeg / webp`
+- 浣跨敤 `picture-card` 椋庢牸灞曠ず
+- 鐐瑰嚮缂╃暐鍥惧彲棰勮澶у浘
+
+### 4.3 甯哥敤绀轰緥
+
+```vue
+<ImageUpload
+  v-model:file-list="imageList"
+  :limit="9"
+  :file-size="5"
+  :file-type="['png', 'jpg', 'jpeg']"
+  button-text="涓婁紶鍥剧墖"
+/>
+```
+
+### 4.4 杩斿洖鏁版嵁鏍煎紡寤鸿
+
+`ImageUpload` 灞曠ず鍥剧墖鏃朵紭鍏堣鍙栵細
+
+```js
+url || previewURL || previewUrl
+```
+
+寤鸿鍚庣杩斿洖锛�
+
+```js
+[
+  {
+    id: 1,
+    originalFilename: '鐜板満鍥剧墖.jpg',
+    previewURL: 'https://xxx/image.jpg'
+  }
+]
+```
+
+### 4.5 琛屽唴宓屽妯″紡
+
+鍥剧墖缁勪欢鍚屾牱鏀寔琛屽唴瀛楁鍐欏洖锛�
+
+```vue
+<ImageUpload
+  v-model:file-list="tableData"
+  :index="scope.$index"
+  children-key="images"
+/>
+```
+
+榛樿鍐欏洖瀛楁涓� `images`銆�
+
+## 5. ImagePreview 鍥剧墖棰勮缁勪欢
+
+缁勪欢璺緞锛�
+
+`src/components/AttachmentPreview/image/index.vue`
+
+### 5.1 鍩虹鐢ㄦ硶
+
+```vue
+<ImagePreview :file-list="imageList" />
+```
+
+### 5.2 鍙厤缃」
+
+| 灞炴�� | 璇存槑 | 绫诲瀷 | 榛樿鍊� |
+| --- | --- | --- | --- |
+| `fileList` | 鍥剧墖鍒楄〃 | `Array` | `[]` |
+| `thumbSize` | 缂╃暐鍥惧ぇ灏� | `Number` | `72` |
+| `gap` | 缂╃暐鍥鹃棿璺� | `Number` | `10` |
+
+### 5.3 鏁版嵁瑕佹眰
+
+缁勪欢浼氳繃婊ゆ病鏈� `previewURL` 鐨勯」锛屽洜姝ゅ鏋滆姝e父鏄剧ず锛屽缓璁嚦灏戝寘鍚細
+
+```js
+[
+  {
+    previewURL: 'https://xxx/image.jpg',
+    originalFilename: '鍥剧墖1.jpg'
+  }
+]
+```
+
+濡傛灉鍒楄〃涓虹┖锛岀粍浠舵樉绀衡�滄殏鏃犲浘鐗団�濄��
+
+## 6. FileListDialog 闄勪欢寮圭獥缁勪欢
+
+缁勪欢璺緞锛�
+
+`src/components/Dialog/FileList.vue`
+
+杩欎釜缁勪欢閫傚悎涓氬姟琛ㄥ崟鎴栬鎯呴〉閲岀殑鈥滈檮浠剁鐞嗏�濆満鏅紝鑳藉姏鍖呮嫭锛�
+
+- 鏍规嵁涓氬姟涓婚敭鏌ヨ闄勪欢鍒楄〃
+- 鎵撳紑寮圭獥鏌ョ湅闄勪欢
+- 鍦ㄥ脊绐椾腑缁х画涓婁紶闄勪欢
+- 鍒犻櫎闄勪欢
+- 涓嬭浇闄勪欢
+
+### 6.1 缁勪欢灞炴��
+
+| 灞炴�� | 璇存槑 | 绫诲瀷 | 榛樿鍊� |
+| --- | --- | --- | --- |
+| `visible` | 鏄惁鏄剧ず寮圭獥 | `Boolean` | 蹇呬紶 |
+| `recordType` | 涓氬姟绫诲瀷 | `String` | `''` |
+| `recordId` | 涓氬姟涓婚敭 | `Number` | `0` |
+| `title` | 寮圭獥鏍囬 | `String` | `闄勪欢` |
+| `width` | 寮圭獥瀹藉害 | `String` | `50%` |
+| `showActions` | 鏄惁鏄剧ず涓嬭浇/鍒犻櫎鎿嶄綔鍒� | `Boolean` | `true` |
+
+### 6.2 鍩虹鐢ㄦ硶
+
+```vue
+<template>
+  <el-button @click="visible = true">鏌ョ湅闄勪欢</el-button>
+
+  <FileListDialog
+    v-model:visible="visible"
+    record-type="salesLedger"
+    :record-id="rowId"
+    title="闄勪欢鍒楄〃"
+  />
+</template>
+
+<script setup>
+import { ref } from 'vue'
+
+const visible = ref(false)
+const rowId = ref(1001)
+</script>
+```
+
+### 6.3 缁勪欢鍐呴儴渚濊禆鐨勬帴鍙�
+
+`FileListDialog` 鏈韩涓嶇洿鎺ヨ皟鐢� `commonFile.js`锛岃�屾槸渚濊禆锛�
+
+- `attachmentList`
+- `createAttachment`
+- `deleteAttachment`
+
+澶勭悊閫昏緫涓猴細
+
+1. 鎵撳紑寮圭獥鍚庢牴鎹� `recordType + recordId` 鏌ヨ闄勪欢
+2. 鐐瑰嚮鈥滀笂浼犻檮浠垛�濆悗锛屽唴閮ㄤ娇鐢� `AttachmentUpload/file` 鍏堜笂浼犲埌 `/common/upload`
+3. 涓婁紶鎴愬姛鍚庯紝灏嗚繑鍥炵殑鏂囦欢瀵硅薄鍜屽凡鏈夊垪琛ㄤ竴璧锋彁浜ょ粰 `createAttachment`
+4. 鍒犻櫎鏃惰皟鐢� `deleteAttachment`
+5. 涓嬭浇鏃剁洿鎺� `window.open(downloadURL, '_blank')`
+
+鍥犳杩欓噷瑕佺壒鍒敞鎰忥細
+
+- `recordType` 鍜� `recordId` 蹇呴』鏄湁鏁堜笟鍔℃爣璇�
+- 涓婁紶鎴愬姛杩斿洖鐨勬暟鎹紝闇�瑕佽兘琚� `createAttachment` 鐩存帴鎺ユ敹
+- 鍒楄〃涓殑涓嬭浇鍦板潃瀛楁搴斾负 `downloadURL`
+
+## 7. commonFile.js 璇存槑
+
+鏂囦欢璺緞锛�
+
+`src/api/publicApi/commonFile.js`
+
+褰撳墠鏂囦欢鎻愪緵鐨勬槸鍏叡鏂囦欢鍒犻櫎鎺ュ彛锛�
+
+```js
+delCommonFile(ids)
+delCommonFileInvoiceLedger(ids)
+```
+
+瀵瑰簲鎺ュ彛锛�
+
+- `/commonFile/delCommonFile`
+- `/invoiceLedger/delFile`
+
+杩欎袱涓柟娉曟洿閫傚悎宸茬粡鍜屽叿浣撲笟鍔$粦瀹氬悗鐨勨�滃垹闄ゅ凡淇濆瓨闄勪欢鈥濆満鏅紝涓嶈礋璐d笂浼犳枃浠舵湰韬��
+
+绀轰緥锛�
+
+```js
+import { delCommonFile } from '@/api/publicApi/commonFile'
+
+await delCommonFile([1, 2, 3])
+```
+
+## 8. 鎺ㄨ崘浣跨敤鏂瑰紡
+
+### 8.1 鏅�氫笟鍔¤〃鍗曚笂浼犻檮浠�
+
+```vue
+<FileUpload v-model:file-list="form.storageBlobDTOs" />
+```
+
+鎻愪氦琛ㄥ崟鏃剁洿鎺ュ甫涓婏細
+
+```js
+{
+  ...form,
+  storageBlobDTOs: form.storageBlobDTOs
+}
+```
+
+### 8.2 鍥剧墖绫讳笟鍔�
+
+```vue
+<ImageUpload v-model:file-list="form.images" />
+<ImagePreview :file-list="form.images" />
+```
+
+### 8.3 宸茶惤搴撻檮浠剁鐞�
+
+```vue
+<FileListDialog
+  v-model:visible="dialogVisible"
+  :record-type="recordType"
+  :record-id="recordId"
+/>
+```
+
+閫傚悎璇︽儏椤点�佸鎵归〉銆佸彴璐﹂〉杩欑被鈥滄煡鐪嬪苟缁存姢褰撳墠涓氬姟闄勪欢鈥濈殑鍦烘櫙銆�
+
+## 9. 娉ㄦ剰浜嬮」
+
+1. `FileUpload` 鍜� `ImageUpload` 鍙槸璐熻矗鎶婃枃浠跺厛浼犲埌 `/common/upload`锛屼笉绛変簬宸茬粡鍜屼笟鍔℃暟鎹粦瀹氥��
+2. 濡傛灉涓氬姟闇�瑕佹寔涔呭寲闄勪欢鍏崇郴锛屼粛闇�瑕佸湪淇濆瓨琛ㄥ崟鏃舵妸杩斿洖鐨勬枃浠跺璞℃彁浜ょ粰涓氬姟鎺ュ彛銆�
+3. `ImagePreview` 褰撳墠鍙瘑鍒� `previewURL`锛屽鏋滃悗绔彧杩斿洖 `url`锛岄瑙堢粍浠跺皢涓嶄細灞曠ず锛屾渶濂界粺涓�琛ラ綈 `previewURL`銆�
+4. `FileListDialog` 渚濊禆 `recordType`銆乣recordId` 鏌ヨ鍜屼繚瀛橀檮浠跺叧绯伙紝鏂板涓氬姟鏃惰鍏堢‘璁ゅ悗绔叧鑱旀帴鍙e彲鐢ㄣ��
+5. 鍒犻櫎鏈湴鍒楄〃椤瑰拰鍒犻櫎宸蹭繚瀛橀檮浠舵槸涓や欢浜嬶細
+   - 涓婁紶缁勪欢閲岀殑鍒犻櫎锛氬彧浼氫粠褰撳墠鍓嶇缁戝畾鏁扮粍涓Щ闄�
+   - `commonFile.js` / `deleteAttachment`锛氭墠鏄湡姝h皟鐢ㄥ悗绔垹闄�
+
+## 10. 涓�濂楁渶甯歌鐨勯〉闈㈠啓娉�
+
+```vue
+<template>
+  <el-form :model="form">
+    <el-form-item label="闄勪欢">
+      <FileUpload v-model:file-list="form.storageBlobDTOs" :limit="5" />
+    </el-form-item>
+
+    <el-form-item label="鐜板満鍥剧墖">
+      <ImageUpload v-model:file-list="form.images" :limit="9" />
+    </el-form-item>
+
+    <el-form-item label="鍥剧墖棰勮">
+      <ImagePreview :file-list="form.images" />
+    </el-form-item>
+  </el-form>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+
+const form = ref({
+  storageBlobDTOs: [],
+  images: [],
+})
+</script>
+```
+
+濡傛灉浣犵殑鐩爣鏄�滃厛涓婁紶锛屽啀璺熶笟鍔′竴璧蜂繚瀛樷�濓紝杩欏鍐欐硶鍙互鐩存帴浣滀负鍩虹妯℃澘浣跨敤銆�
diff --git a/jsconfig.json b/jsconfig.json
new file mode 100644
index 0000000..365552f
--- /dev/null
+++ b/jsconfig.json
@@ -0,0 +1,10 @@
+{
+  "compilerOptions": {
+    "baseUrl": ".",
+    "paths": {
+      "@/*": ["src/*"],
+      "~/*": ["./*"]
+    }
+  },
+  "include": ["src/**/*.js", "src/**/*.vue", "vite.config.js"]
+}
diff --git a/src/api/basicData/common.js b/src/api/basicData/common.js
new file mode 100644
index 0000000..547c1e1
--- /dev/null
+++ b/src/api/basicData/common.js
@@ -0,0 +1,25 @@
+import request from '@/utils/request'
+
+// 閫氱敤涓婁紶鎺ュ彛锛屾敮鎸� FormData 鎵归噺浼犳枃浠�
+export function uploadFile(data) {
+  return request({
+    url: '/common/upload',
+    method: 'post',
+    data,
+    headers: {
+      'Content-Type': 'multipart/form-data',
+    },
+  })
+}
+
+// 閫氱敤涓婁紶鎺ュ彛锛屾敮鎸� FormData 鎵归噺浼犳枃浠�,姘镐笉杩囨湡锛屾厧鐢�
+export function uploadPublicFile(data) {
+  return request({
+    url: '/common/public/upload',
+    method: 'post',
+    data,
+    headers: {
+      'Content-Type': 'multipart/form-data',
+    },
+  })
+}
diff --git a/src/api/basicData/customer.js b/src/api/basicData/customer.js
new file mode 100644
index 0000000..28f2eab
--- /dev/null
+++ b/src/api/basicData/customer.js
@@ -0,0 +1,75 @@
+import request from '@/utils/request'
+
+export function listCustomer(query) {
+    return request({
+        url: '/basic/customer/list',
+        method: 'get',
+        params: query
+    })
+}
+
+// 鍒嗛厤瀹㈡埛
+export function assignCustomer(data) {
+    return request({
+        url: '/basic/customer/assignCustomer',
+        method: 'post',
+        data
+    })
+}
+
+// 鍥炴敹瀹㈡埛
+export function recycleCustomer(data) {
+    return request({
+        url: '/basic/customer/recycleCustomer',
+        method: 'post',
+        data
+    })
+}
+
+export function shareCustomer(data) {
+    return request({
+        url: '/basic/customer/together',
+        method: 'post',
+        data: data
+    })
+}
+
+export function getCustomer(id) {
+    return request({
+        url: '/basic/customer/' + id,
+        method: 'get'
+    })
+}
+
+export function addCustomer(data) {
+    return request({
+        url: '/basic/customer/addCustomer',
+        method: 'post',
+        data: data
+    })
+}
+
+export function updateCustomer(data) {
+    return request({
+        url: '/basic/customer/updateCustomer',
+        method: 'post',
+        data: data
+    })
+}
+
+export function exportCustomer(query) {
+    return request({
+        url: '/basic/customer/export',
+        method: 'get',
+        params: query,
+        responseType: 'blob'
+    })
+}
+
+export function delCustomer(ids) {
+    return request({
+        url: '/basic/customer/delCustomer',
+        method: 'delete',
+        data: ids
+    })
+}
diff --git a/src/api/basicData/customerFile.js b/src/api/basicData/customerFile.js
index 6a5f049..6602ddc 100644
--- a/src/api/basicData/customerFile.js
+++ b/src/api/basicData/customerFile.js
@@ -1,57 +1,5 @@
-// 瀹㈡埛妗f椤甸潰鎺ュ彛
 import request from '@/utils/request'
 
-// 鍒嗛〉鏌ヨ
-export function listCustomer(query) {
-    return request({
-        url: '/basic/customer/list',
-        method: 'get',
-        params: query
-    })
-}
-// 鏌ヨ瀹㈡埛妗f璇︾粏
-export function getCustomer(id) {
-    return request({
-        url: '/basic/customer/' + id,
-        method: 'get'
-    })
-}
-// 鏂板瀹㈡埛妗f
-export function addCustomer(data) {
-    return request({
-        url: '/basic/customer/addCustomer',
-        method: 'post',
-        data: data
-    })
-}
-// 淇敼瀹㈡埛妗f
-export function updateCustomer(data) {
-    return request({
-        url: '/basic/customer/updateCustomer',
-        method: 'post',
-        data: data
-    })
-}
-// 瀵煎嚭瀹㈡埛妗f
-export function exportCustomer(query) {
-    return request({
-        url: '/basic/customer/export',
-        method: 'get',
-        params: query,
-        responseType: 'blob'
-    })
-}
-// 鍒犻櫎瀹㈡埛妗f
-export function delCustomer(ids) {
-    return request({
-        url: '/basic/customer/delCustomer',
-        method: 'delete',
-        data: ids
-    })
-}
-
-
-// 鏂板瀹㈡埛璺熻繘
 export function addCustomerFollow(data) {
     return request({
         url: '/basic/customer-follow/add',
@@ -60,23 +8,21 @@
     })
 }
 
-// 淇敼瀹㈡埛璺熻繘
 export function updateCustomerFollow(data) {
-  return request({
-    url: '/basic/customer-follow/edit',
-    method: 'put',
-    data: data,
-  })
+    return request({
+        url: '/basic/customer-follow/edit',
+        method: 'put',
+        data: data,
+    })
 }
-// 鍒犻櫎瀹㈡埛璺熻繘
+
 export function delCustomerFollow(id) {
     return request({
-        url: '/basic/customer-follow/'+id,
+        url: '/basic/customer-follow/' + id,
         method: 'delete',
     })
 }
 
-// 鍥炶鎻愰啋-鏂板/鏇存柊
 export function addReturnVisit(data) {
     return request({
         url: '/basic/customer-follow/return-visit',
@@ -84,10 +30,10 @@
         data: data
     })
 }
-// 鑾峰彇鍥炶鎻愰啋璇︽儏
+
 export function getReturnVisit(id) {
     return request({
         url: '/basic/customer-follow/return-visit/' + id,
         method: 'get'
     })
-}
\ No newline at end of file
+}
diff --git a/src/api/basicData/parameterMaintenance.js b/src/api/basicData/parameterMaintenance.js
new file mode 100644
index 0000000..86d889e
--- /dev/null
+++ b/src/api/basicData/parameterMaintenance.js
@@ -0,0 +1,81 @@
+// 鍙傛暟缁存姢椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// 鏌ヨ鍙傛暟鍒楄〃
+export function parameterListPage(query) {
+  return request({
+    url: "/basic/parameter/listPage",
+    method: "get",
+    params: query,
+  });
+}
+
+// 鏂板鍙傛暟
+export function addParameter(data) {
+  return request({
+    url: "/basic/parameter/add",
+    method: "post",
+    data: data,
+  });
+}
+
+// 缂栬緫鍙傛暟
+export function updateParameter(data) {
+  return request({
+    url: "/basic/parameter/update",
+    method: "put",
+    data: data,
+  });
+}
+
+// 鍒犻櫎鍙傛暟
+export function delParameter(ids) {
+  return request({
+    url: "/basic/parameter/del",
+    method: "delete",
+    data: Array.isArray(ids) ? ids : [ids],
+  });
+}
+
+// 鑾峰彇浜у搧绫诲瀷鍒楄〃
+export function getProductTypes() {
+  return request({
+    url: "/basic/product/typeList",
+    method: "get",
+  });
+}
+
+// 鏂板鍩虹鍙傛暟
+export function addBaseParam(data) {
+  return request({
+    url: "/technologyParam/add",
+    method: "post",
+    data: data,
+  });
+}
+
+// 缂栬緫鍩虹鍙傛暟
+export function editBaseParam(data) {
+  return request({
+    url: "/technologyParam/edit",
+    method: "put",
+    data: data,
+  });
+}
+
+// 鏌ヨ鍩虹鍙傛暟鍒楄〃
+export function getBaseParamList(query) {
+  return request({
+    url: "/technologyParam/list",
+    method: "get",
+    params: query,
+  });
+}
+
+// 鍒犻櫎鍩虹鍙傛暟
+export function removeBaseParam(ids) {
+  return request({
+    url: `/technologyParam/remove/` + ids,
+    method: "delete",
+  });
+}
diff --git a/src/api/basicData/productModel.js b/src/api/basicData/productModel.js
index f048f9e..8acc2ef 100644
--- a/src/api/basicData/productModel.js
+++ b/src/api/basicData/productModel.js
@@ -6,4 +6,12 @@
         method: 'get',
         params: query
     })
+}
+
+export function productModelListByUrl(url, query) {
+    return request({
+        url,
+        method: 'get',
+        params: query
+    })
 }
\ No newline at end of file
diff --git a/src/api/basicData/productProcess.js b/src/api/basicData/productProcess.js
index e0208fa..46356fd 100644
--- a/src/api/basicData/productProcess.js
+++ b/src/api/basicData/productProcess.js
@@ -1,10 +1,10 @@
-import request from '@/utils/request'
+import request from "@/utils/request";
 
 // 宸ュ簭鍒楄〃鍒嗛〉鏌ヨ
 export function productProcessListPage(query) {
   return request({
-    url: '/productProcess/listPage',
-    method: 'get',
-    params: query
-  })
+    url: "/technologyOperation/listPage",
+    method: "get",
+    params: query,
+  });
 }
diff --git a/src/api/basicData/storageAttachment.js b/src/api/basicData/storageAttachment.js
new file mode 100644
index 0000000..3e241f6
--- /dev/null
+++ b/src/api/basicData/storageAttachment.js
@@ -0,0 +1,29 @@
+// 闄勪欢椤甸潰鎺ュ彛
+import request from '@/utils/request'
+
+// 闄勪欢鏌ヨ
+export function attachmentList(query) {
+    return request({
+        url: '/storageAttachment/list',
+        method: 'get',
+        params: query
+    })
+}
+
+// 闄勪欢鏂板
+export function createAttachment(data) {
+    return request({
+        url: '/storageAttachment/add',
+        method: 'post',
+        data
+    })
+}
+
+// 闄勪欢鍒犻櫎
+export function deleteAttachment(data) {
+    return request({
+        url: '/storageAttachment/delete',
+        method: 'delete',
+        data
+    })
+}
diff --git a/src/api/collaborativeApproval/approvalManagement.js b/src/api/collaborativeApproval/approvalManagement.js
new file mode 100644
index 0000000..c2ce4c7
--- /dev/null
+++ b/src/api/collaborativeApproval/approvalManagement.js
@@ -0,0 +1,20 @@
+// 瀹℃壒绠$悊閰嶇疆
+import request from "@/utils/request";
+
+// 鏌ヨ瀹℃壒娴佺▼閰嶇疆鑺傜偣鍒楄〃
+export function getApproveProcessConfigNodeList(type) {
+    return request({
+        url: '/approveProcessConfigNode/list',
+        method: 'get',
+        params: { type },
+    })
+}
+
+// 鏂板瀹℃壒娴佺▼閰嶇疆鑺傜偣
+export function addApproveProcessConfigNode(data) {
+    return request({
+        url: '/approveProcessConfigNode/add',
+        method: 'post',
+        data: data,
+    })
+}
diff --git a/src/api/equipmentManagement/sparePartsUsage.js b/src/api/equipmentManagement/sparePartsUsage.js
new file mode 100644
index 0000000..e9384aa
--- /dev/null
+++ b/src/api/equipmentManagement/sparePartsUsage.js
@@ -0,0 +1,36 @@
+import request from "@/utils/request";
+
+/**
+ * 澶囦欢棰嗙敤璁板綍 - 鍒嗛〉鏌ヨ
+ * params: { current, size, sparePartId?, sparePartName?, source?, deviceId?, startTime?, endTime? }
+ */
+export const getSparePartsUsagePage = (params) => {
+  return request({
+    url: "/sparePartsRequisitionRecord/listPage",
+    method: "get",
+    params,
+  });
+};
+
+/**
+ * 澶囦欢棰嗙敤璁板綍 - 鏂板
+ * data 绀轰緥锛�
+ * {
+ *   source: "repair" | "upkeep" | "manual",
+ *   sourceId?: number | string,
+ *   deviceId?: number | string,
+ *   deviceName?: string,
+ *   operatorId?: number | string,
+ *   operator?: string,
+ *   useTime?: string, // YYYY-MM-DD HH:mm:ss
+ *   items: [{ sparePartId: number|string, qty: number }]
+ * }
+ */
+export const addSparePartsUsage = (data) => {
+  return request({
+    url: "/sparePartsUsage/add",
+    method: "post",
+    data,
+  });
+};
+
diff --git a/src/api/inventoryManagement/stockInRecord.js b/src/api/inventoryManagement/stockInRecord.js
index 1746bfe..3142e09 100644
--- a/src/api/inventoryManagement/stockInRecord.js
+++ b/src/api/inventoryManagement/stockInRecord.js
@@ -24,4 +24,21 @@
         method: "delete",
         data: ids,
     });
+};
+
+export const batchDeletePendingStockInRecords = (ids) => {
+    return request({
+        url: "/stockInRecord/pending",
+        method: "delete",
+        data: ids,
+    });
+};
+
+// 鎵归噺瀹℃壒鍏ュ簱璁板綍锛坅pprovalStatus: approved/rejected锛�
+export const batchApproveStockInRecords = (data) => {
+    return request({
+        url: "/stockInRecord/approve",
+        method: "post",
+        data,
+    });
 };
\ No newline at end of file
diff --git a/src/api/inventoryManagement/stockInventory.js b/src/api/inventoryManagement/stockInventory.js
index aed71e2..0ba0943 100644
--- a/src/api/inventoryManagement/stockInventory.js
+++ b/src/api/inventoryManagement/stockInventory.js
@@ -8,6 +8,15 @@
     });
 };
 
+// 鍒嗛〉鏌ヨ鑱斿悎搴撳瓨璁板綍鍒楄〃锛堝寘鍚晢鍝佷俊鎭級
+export const getStockInventoryListPageCombined = (params) => {
+    return request({
+        url: "/stockInventory/pageListCombinedStockInventory",
+        method: "get",
+        params,
+    });
+};
+
 // 鍒涘缓搴撳瓨璁板綍
 export const createStockInventory = (params) => {
     return request({
@@ -21,6 +30,24 @@
 export const subtractStockInventory = (params) => {
     return request({
         url: "/stockInventory/subtractStockInventory",
+        method: "post",
+        data: params,
+    });
+};
+
+// 鏂板鍏ュ簱璁板綍锛堜粎鍒涘缓璁板綍锛屼笉璋冩暣搴撳瓨锛�
+export const addStockInRecordOnly = (params) => {
+    return request({
+        url: "/stockInventory/addStockInRecordOnly",
+        method: "post",
+        data: params,
+    });
+};
+
+// 鏂板鍑哄簱璁板綍锛堜粎鍒涘缓璁板綍锛屼笉璋冩暣搴撳瓨锛�
+export const addStockOutRecordOnly = (params) => {
+    return request({
+        url: "/stockInventory/addStockOutRecordOnly",
         method: "post",
         data: params,
     });
@@ -60,3 +87,11 @@
     });
 };
 
+export const getStockInventoryByModelId = (productModelId) => {
+    return request({
+        url: "/stockInventory/getByModelId",
+        method: "get",
+        params: { productModelId },
+    });
+};
+
diff --git a/src/api/inventoryManagement/stockOut.js b/src/api/inventoryManagement/stockOut.js
index 3d260b3..004ba99 100644
--- a/src/api/inventoryManagement/stockOut.js
+++ b/src/api/inventoryManagement/stockOut.js
@@ -17,3 +17,21 @@
         data: ids,
     });
 }
+
+//鍒犻櫎寰呭鎵瑰嚭搴撲俊鎭�
+export const delPendingStockOut = (ids) => {
+    return request({
+        url: "/stockOutRecord/pending",
+        method: "delete",
+        data: ids,
+    });
+}
+
+// 鎵归噺瀹℃壒鍑哄簱璁板綍锛坅pprovalStatus: approved/rejected锛�
+export const batchApproveStockOutRecords = (data) => {
+    return request({
+        url: "/stockOutRecord/approve",
+        method: "post",
+        data,
+    });
+}
diff --git a/src/api/inventoryManagement/stockUninventory.js b/src/api/inventoryManagement/stockUninventory.js
index 73907c7..a80474e 100644
--- a/src/api/inventoryManagement/stockUninventory.js
+++ b/src/api/inventoryManagement/stockUninventory.js
@@ -26,6 +26,24 @@
     });
 };
 
+// 鏂板鍏ュ簱璁板綍锛堜粎鍒涘缓璁板綍锛屼笉璋冩暣搴撳瓨锛�
+export const addUnqualifiedStockInRecordOnly = (params) => {
+    return request({
+        url: "/stockUninventory/addStockInRecordOnly",
+        method: "post",
+        data: params,
+    });
+};
+
+// 鏂板鍑哄簱璁板綍锛堜粎鍒涘缓璁板綍锛屼笉璋冩暣搴撳瓨锛�
+export const addUnqualifiedStockOutRecordOnly = (params) => {
+    return request({
+        url: "/stockUninventory/addStockOutRecordOnly",
+        method: "post",
+        data: params,
+    });
+};
+
 // 鍐荤粨搴撳瓨璁板綍
 export const frozenStockUninventory = (params) => {
     return request({
diff --git a/src/api/personnelManagement/class.js b/src/api/personnelManagement/class.js
index f5ae299..b254c4a 100644
--- a/src/api/personnelManagement/class.js
+++ b/src/api/personnelManagement/class.js
@@ -71,6 +71,7 @@
     url: "/personalShift/export",
     method: "get",
     params: query,
+    responseType: "blob",
   });
 }
 
@@ -114,4 +115,4 @@
         method: 'get',
         params: query,
     })
-}
\ No newline at end of file
+}
diff --git a/src/api/procurementManagement/procurementInvoiceLedger.js b/src/api/procurementManagement/procurementInvoiceLedger.js
index 038e94f..d0716f9 100644
--- a/src/api/procurementManagement/procurementInvoiceLedger.js
+++ b/src/api/procurementManagement/procurementInvoiceLedger.js
@@ -75,14 +75,6 @@
   });
 }
 
-export function productUploadFile(data) {
-  return request({
-    url: "/file/uploadFile",
-    method: "post",
-    data: data,
-  });
-}
-
 // export function getProductRecordById(params) {
 //   return request({
 //     url: "/purchase/registration/getProductRecordById",
diff --git a/src/api/procurementManagement/purchase_return_order.js b/src/api/procurementManagement/purchase_return_order.js
index 33ec33e..aeb380d 100644
--- a/src/api/procurementManagement/purchase_return_order.js
+++ b/src/api/procurementManagement/purchase_return_order.js
@@ -17,4 +17,22 @@
         method: "post",
         data
     });
+}
+
+// 鏌ョ湅璇︽儏
+// purchaseReturnOrders/selectById/xxx
+export function getPurchaseReturnOrderDetail(id) {
+    return request({
+        url: "/purchaseReturnOrders/selectById/" + id,
+        method: "get",
+    });
+}
+
+// 閲囪喘閫�璐у崟鍒犻櫎
+// POST purchaseReturnOrders/deleteById/xxx
+export function deletePurchaseReturnOrder(id) {
+    return request({
+        url: "/purchaseReturnOrders/deleteById/" + id,
+        method: "post",
+    });
 }
\ No newline at end of file
diff --git a/src/api/productionManagement/processRoute.js b/src/api/productionManagement/processRoute.js
index c13b2fc..a0cb6c8 100644
--- a/src/api/productionManagement/processRoute.js
+++ b/src/api/productionManagement/processRoute.js
@@ -4,7 +4,7 @@
 // 鍒嗛〉鏌ヨ
 export function listPage(query) {
   return request({
-    url: "/processRoute/page",
+    url: "/technologyRouting/page",
     method: "get",
     params: query,
   });
@@ -12,31 +12,38 @@
 
 export function add(data) {
   return request({
-    url: "/processRoute",
+    url: "/technologyRouting/addTechRoute",
     method: "post",
     data: data,
   });
 }
 
+// export function del(ids) {
+//   return request({
+//     url: "/processRoute/" + ids,
+//     method: "delete",
+//   });
+// }
 export function del(ids) {
   return request({
-    url: '/processRoute/' + ids,
-    method: 'delete',
-  })
+    url: "/technologyRouting/delete",
+    method: "delete",
+    data: ids,
+  });
 }
 
 export function update(data) {
   return request({
-    url: '/processRoute',
-    method: 'put',
+    url: "/technologyRouting/editTechRoute",
+    method: "put",
     data: data,
-  })
+  });
 }
 
 // 鑾峰彇璇︽儏
 export function getById(id) {
   return request({
     url: `/processRoute/${id}`,
-    method: 'get',
-  })
-}
\ No newline at end of file
+    method: "get",
+  });
+}
diff --git a/src/api/productionManagement/processRouteFile.js b/src/api/productionManagement/processRouteFile.js
new file mode 100644
index 0000000..e3a9fb4
--- /dev/null
+++ b/src/api/productionManagement/processRouteFile.js
@@ -0,0 +1,28 @@
+import request from "@/utils/request";
+
+// 闄勪欢鍒楄〃
+export function listProcessRouteFiles(query) {
+  return request({
+    url: "/technologyRoutingFile/listPage",
+    method: "get",
+    params: query,
+  });
+}
+
+// 鏂板闄勪欢
+export function addProcessRouteFile(data) {
+  return request({
+    url: "/technologyRoutingFile/add",
+    method: "post",
+    data,
+  });
+}
+
+// 鍒犻櫎闄勪欢
+export function delProcessRouteFile(ids) {
+  return request({
+    url: "/technologyRoutingFile/del",
+    method: "delete",
+    data: ids,
+  });
+}
diff --git a/src/api/productionManagement/processRouteItem.js b/src/api/productionManagement/processRouteItem.js
index 9e81406..3bf4a6e 100644
--- a/src/api/productionManagement/processRouteItem.js
+++ b/src/api/productionManagement/processRouteItem.js
@@ -4,7 +4,7 @@
 // 鍒楄〃鏌ヨ
 export function findProcessRouteItemList(query) {
   return request({
-    url: "/processRouteItem/list",
+    url: "/technologyRoutingOperation/list",
     method: "get",
     params: query,
   });
@@ -12,8 +12,15 @@
 
 export function addOrUpdateProcessRouteItem(data) {
   return request({
-    url: "/processRouteItem",
+    url: "/technologyRoutingOperation/add",
     method: "post",
+    data: data,
+  });
+}
+export function addOrUpdateProcessRouteItem1(data) {
+  return request({
+    url: "/technologyRoutingOperation",
+    method: "put",
     data: data,
   });
 }
@@ -21,7 +28,7 @@
 // 鎺掑簭鎺ュ彛
 export function sortProcessRouteItem(data) {
   return request({
-    url: "/processRouteItem/sort",
+    url: "/technologyRoutingOperation/sort",
     method: "post",
     data: data,
   });
@@ -32,7 +39,54 @@
   // 灏唅d鏁扮粍杞崲涓洪�楀彿鍒嗛殧鐨勫瓧绗︿覆锛屾嫾鎺ュ埌URL鍚庨潰
   const idsStr = Array.isArray(ids) ? ids.join(",") : ids;
   return request({
-    url: `/processRouteItem/batchDelete/${idsStr}`,
+    url: `/technologyRoutingOperation/${idsStr}`,
     method: "delete",
   });
 }
+// 鑾峰彇宸ュ簭鍙傛暟鍒楄〃
+export function getProcessParamList(query) {
+  return request({
+    url: `/technologyRoutingOperationParam/list`,
+    method: "get",
+    params: query,
+  });
+}
+// 宸ヨ壓璺嚎鍙傛暟鏂板
+export function addProcessRouteItemParam(data) {
+  return request({
+    url: "/technologyRoutingOperationParam/add",
+    method: "post",
+    data: data,
+  });
+}
+// 宸ヨ壓璺嚎鍙傛暟淇敼
+export function editProcessRouteItemParam(data) {
+  return request({
+    url: "/technologyRoutingOperationParam",
+    method: "put",
+    data: data,
+  });
+}
+// 宸ヨ壓璺嚎鍙傛暟鍒犻櫎
+export function delProcessRouteItemParam(id) {
+  return request({
+    url: `/technologyRoutingOperationParam/${id}`,
+    method: "delete",
+  });
+}
+// 鎸夊伐鑹鸿矾绾垮伐搴忓悓姝ュ伐搴忓弬鏁�
+export function syncProcessParamItem(data) {
+  return request({
+    url: "/technologyRoutingOperationParam/sync",
+    method: "post",
+    data: data,
+  });
+}
+// 鎸夊伐鑹鸿矾绾垮伐搴忓悓姝ュ伐搴忓弬鏁�-鐢熶骇璁㈠崟
+export function syncProcessParamItemOrder(data) {
+  return request({
+    url: "/productionOrderRoutingOperationParam/sync",
+    method: "post",
+    data: data,
+  });
+}
diff --git a/src/api/productionManagement/productBom.js b/src/api/productionManagement/productBom.js
index 2badea8..517208b 100644
--- a/src/api/productionManagement/productBom.js
+++ b/src/api/productionManagement/productBom.js
@@ -4,7 +4,7 @@
 // 鍒嗛〉鏌ヨ
 export function listPage(query) {
   return request({
-    url: "/productBom/listPage",
+    url: "/technologyBom/listPage",
     method: "get",
     params: query,
   });
@@ -13,16 +13,24 @@
 // 鏂板
 export function add(data) {
   return request({
-    url: "/productBom/add",
+    url: "/technologyBom/add",
     method: "post",
     data: data,
   });
 }
 
+// 澶嶅埗
+export function copy(data) {
+  return request({
+    url: "/technologyBom/copy",
+    method: "post",
+    data: data,
+  });
+}
 // 淇敼
 export function update(data) {
   return request({
-    url: "/productBom/update",
+    url: "/technologyBom/update",
     method: "put",
     data: data,
   });
@@ -31,7 +39,7 @@
 // 鎵归噺鍒犻櫎
 export function batchDelete(ids) {
   return request({
-    url: "/productBom/batchDelete",
+    url: "/technologyBom/batchDelete",
     method: "delete",
     data: ids,
   });
@@ -40,7 +48,7 @@
 // 鏍规嵁浜у搧鍨嬪彿ID鏌ヨBOM
 export function getByModel(productModelId) {
   return request({
-    url: "/productBom/getByModel",
+    url: "/technologyBom/getByModel",
     method: "get",
     params: { productModelId },
   });
@@ -49,7 +57,7 @@
 // 瀵煎嚭BOM
 export function exportBom(bomId) {
   return request({
-    url: "/productBom/exportBom",
+    url: "/technologyBom/exportBom",
     method: "post",
     params: { bomId },
     responseType: "blob",
@@ -59,8 +67,8 @@
 //  涓嬭浇妯℃澘
 export function downloadTemplate() {
   return request({
-    url: "/productBom/downloadTemplate",
+    url: "/technologyBom/downloadTemplate",
     method: "get",
     responseType: "blob",
   });
-}
\ No newline at end of file
+}
diff --git a/src/api/productionManagement/productProcessRoute.js b/src/api/productionManagement/productProcessRoute.js
index e8d5da5..d75c239 100644
--- a/src/api/productionManagement/productProcessRoute.js
+++ b/src/api/productionManagement/productProcessRoute.js
@@ -4,7 +4,7 @@
 // 鍒楄〃鏌ヨ
 export function findProductProcessRouteItemList(query) {
   return request({
-    url: "/productProcessRoute/list",
+    url: "/productionOrderRouting/list",
     method: "get",
     params: query,
   });
@@ -12,7 +12,7 @@
 
 export function addOrUpdateProductProcessRouteItem(data) {
   return request({
-    url: "/productProcessRoute/updateRouteItem",
+    url: "/productionOrderRouting/updateRouteItem",
     method: "post",
     data: data,
   });
@@ -21,7 +21,7 @@
 // 鐢熶骇璁㈠崟涓嬶細鏂板宸ヨ壓璺嚎椤圭洰
 export function addRouteItem(data) {
   return request({
-    url: "/productProcessRoute/addRouteItem",
+    url: "/productionOrderRouting/addRouteItem",
     method: "post",
     data,
   });
@@ -30,7 +30,7 @@
 // 鑾峰彇鐢熶骇璁㈠崟鍏宠仈鐨勫伐鑹鸿矾绾夸富淇℃伅
 export function listMain(orderId) {
   return request({
-    url: "/productProcessRoute/listMain",
+    url: "/productionOrderRouting/listMain",
     method: "get",
     params: { orderId },
   });
@@ -39,7 +39,7 @@
 // 鍒犻櫎宸ヨ壓璺嚎椤圭洰锛堣矾鐢卞悗鎷兼帴 id锛�
 export function deleteRouteItem(id) {
   return request({
-    url: `/productProcessRoute/deleteRouteItem/${id}`,
+    url: `/productionOrderRouting/deleteRouteItem/${id}`,
     method: "delete",
   });
 }
@@ -47,8 +47,39 @@
 // 鐢熶骇璁㈠崟涓嬶細鎺掑簭宸ヨ壓璺嚎椤圭洰
 export function sortRouteItem(data) {
   return request({
-    url: "/productProcessRoute/sortRouteItem",
+    url: "/productionOrderRouting/sortRouteItem",
     method: "post",
     data,
   });
 }
+// 鑾峰彇宸ュ簭鍙傛暟鍒楄〃-鐢熶骇璁㈠崟
+export function findProcessParamListOrder(query) {
+  return request({
+    url: `/productionOrderRoutingOperationParam/list`,
+    method: "get",
+    params: query,
+  });
+}
+// 宸ヨ壓璺嚎鍙傛暟鏂板-鐢熶骇璁㈠崟
+export function addProcessRouteItemParamOrder(data) {
+  return request({
+    url: "/productionOrderRoutingOperationParam",
+    method: "post",
+    data: data,
+  });
+}
+// 宸ヨ壓璺嚎鍙傛暟淇敼-鐢熶骇璁㈠崟
+export function editProcessRouteItemParamOrder(data) {
+  return request({
+    url: "/productionOrderRoutingOperationParam",
+    method: "put",
+    data: data,
+  });
+}
+// 宸ヨ壓璺嚎鍙傛暟鍒犻櫎-鐢熶骇璁㈠崟
+export function delProcessRouteItemParamOrder(id) {
+  return request({
+    url: `/productionOrderRoutingOperationParam/${id}`,
+    method: "delete",
+  });
+}
diff --git a/src/api/productionManagement/productStructure.js b/src/api/productionManagement/productStructure.js
index e69e14a..c55c93e 100644
--- a/src/api/productionManagement/productStructure.js
+++ b/src/api/productionManagement/productStructure.js
@@ -4,14 +4,43 @@
 // 鍒嗛〉鏌ヨ
 export function queryList(id) {
   return request({
-    url: "/productStructure/listBybomId/" + id,
+    url: "/technologyBomStructure/listByBomId/" + id,
     method: "get",
   });
 }
-
+// 鍒嗛〉鏌ヨ-浜у搧璁㈠崟
+export function queryList2(id) {
+  return request({
+    url: "/productionBomStructure/listByBomId/" + id,
+    method: "get",
+  });
+}
 export function add(data) {
   return request({
-    url: "/productStructure",
+    url: "/productStructure/" + data.bomId,
+    method: "post",
+    data: data.children,
+  });
+}
+
+export function addBomDetail(data) {
+  return request({
+    url: "/technologyBomStructure",
+    method: "post",
+    data: data,
+  });
+}
+// 鍒嗛〉鏌ヨ-浜у搧璁㈠崟
+// export function queryList2(id) {
+//   return request({
+//     url: "/productionOrderStructure/getBomStructs/" + id,
+//     method: "get",
+//   });
+// }
+
+export function add2(data) {
+  return request({
+    url: "/productionBomStructure/addOrUpdateBomStructs",
     method: "post",
     data: data,
   });
diff --git a/src/api/productionManagement/productionCosting.js b/src/api/productionManagement/productionCosting.js
index ebf7daa..81e6b1b 100644
--- a/src/api/productionManagement/productionCosting.js
+++ b/src/api/productionManagement/productionCosting.js
@@ -14,7 +14,7 @@
 // salesLedger/productionAccounting/page
 export function salesLedgerProductionAccountingList(query) {
   return request({
-    url: "/salesLedger/productionAccounting/page",
+    url: "/productionAccount/listPage",
     method: "get",
     params: query,
   });
@@ -24,7 +24,7 @@
 //
 export function salesLedgerProductionAccountingListProductionDetails(query) {
   return request({
-    url: "/salesLedger/productionAccounting/listProductionDetails",
+    url: "/productionAccount/listProductionDetails",
     method: "get",
     params: query,
   });
diff --git a/src/api/productionManagement/productionOrder.js b/src/api/productionManagement/productionOrder.js
index 6c8dbe2..b84a188 100644
--- a/src/api/productionManagement/productionOrder.js
+++ b/src/api/productionManagement/productionOrder.js
@@ -12,7 +12,7 @@
 
 export function productOrderListPage(query) {
   return request({
-    url: "/productOrder/page",
+    url: "/productionOrder/page",
     method: "get",
     params: query,
   });
@@ -30,7 +30,7 @@
 // 鐢熶骇璁㈠崟-缁戝畾宸ヨ壓璺嚎
 export function bindingRoute(data) {
   return request({
-    url: "/productOrder/bindingRoute",
+    url: "/productionOrder/bindingRoute",
     method: "post",
     data,
   });
@@ -39,7 +39,16 @@
 // 鐢熶骇璁㈠崟-鏂板
 export function addProductOrder(data) {
   return request({
-    url: "/productOrder/addProductOrder",
+    url: "/productionOrder/addOrder",
+    method: "post",
+    data: data,
+  });
+}
+
+// 鐢熶骇璁㈠崟-淇敼
+export function updateProductOrder(data) {
+  return request({
+    url: "/productionOrder/updateOrder",
     method: "post",
     data: data,
   });
@@ -47,8 +56,9 @@
 
 export function delProductOrder(ids) {
   return request({
-    url: `/productOrder/${ids}`,
+    url: `/productionOrder/delete`,
     method: "delete",
+    data: ids,
   });
 }
 
@@ -58,6 +68,92 @@
     url: "/productOrder/listProcessBom",
     method: "get",
     params: query,
+  });
+}
+
+// 鐢熶骇璁㈠崟-棰嗘枡鍙拌处鍒楄〃
+export function listMaterialPickingLedger(query) {
+  return request({
+    url: "/productOrderMaterial/list",
+    method: "get",
+    params: query,
+  });
+}
+
+// 鐢熶骇璁㈠崟-淇濆瓨棰嗘枡鍙拌处
+// export function saveMaterialPickingLedger(data) {
+//   return request({
+//     url: "/productOrderMaterial/save",
+//     method: "post",
+//     data,
+//   });
+// }
+export function saveMaterialPickingLedger(data) {
+  return request({
+    url: "/productionOrderPick/savePick",
+    method: "post",
+    data,
+  });
+}
+export function updateMaterialPickingLedger(data) {
+  return request({
+    url: "/productionOrderPick/updatePick",
+    method: "post",
+    data,
+  });
+}
+
+// 鐢熶骇璁㈠崟婧簮璇︽儏
+export function getOrderDetail(npsNo) {
+  return request({
+    url: "/productionOrder/ordeDetail",
+    method: "get",
+    params: { npsNo },
+  });
+}
+// 鐢熶骇璁㈠崟-棰嗘枡璇︽儏鍒楄〃
+// export function listMaterialPickingDetail(query) {
+//   return request({
+//     url: "/productOrderMaterial/detailList",
+//     method: "get",
+//     params: query,
+//   });
+// }
+export function listMaterialPickingBom(productionOrderId) {
+  return request({
+    url: "/productionOrder/pick/" + productionOrderId,
+    method: "get",
+  });
+}
+export function listMaterialPickingDetail(productionOrderId) {
+  return request({
+    url: "/productionOrderPick/detail/" + productionOrderId,
+    method: "get",
+  });
+}
+// 鐢熶骇璁㈠崟-琛ユ枡璁板綍鍒楄〃
+export function listMaterialSupplementRecord(query) {
+  return request({
+    url: "/productionOrderPickRecord/feeding",
+    method: "get",
+    params: query,
+  });
+}
+
+// 鐢熶骇璁㈠崟-鑾峰彇鏉ユ簮鏁版嵁
+export function getProductOrderSource(id) {
+  return request({
+    url: `/productionOrder/source/${id}`,
+    method: "get",
+  });
+}
+
+// 鐢熶骇璁㈠崟-閫�鏂欑‘璁�
+export function confirmMaterialReturn(data) {
+  return request({
+    url: "/productOrderMaterial/confirmReturn",
+    method: "post",
+    data,
   });
 }
 
@@ -129,4 +225,4 @@
     method: "post",
     data: data,
   });
-}
\ No newline at end of file
+}
diff --git a/src/api/productionManagement/productionProcess.js b/src/api/productionManagement/productionProcess.js
index d3a453c..783f584 100644
--- a/src/api/productionManagement/productionProcess.js
+++ b/src/api/productionManagement/productionProcess.js
@@ -4,7 +4,7 @@
 // 鍒嗛〉鏌ヨ
 export function listPage(query) {
   return request({
-    url: "/productProcess/listPage",
+    url: "/technologyOperation/listPage",
     method: "get",
     params: query,
   });
@@ -12,15 +12,23 @@
 
 export function processList(query) {
   return request({
-    url: "/productProcess/list",
+    url: "/technologyOperation/listPage",
     method: "get",
     params: query,
   });
 }
 
+// 宸ュ簭鏌ヨ
+export function list(query) {
+  return request({
+    url: "/technologyOperation/listPage",
+    method: "get",
+    params: query,
+  });
+}
 export function add(data) {
   return request({
-    url: "/productProcess",
+    url: "/technologyOperation/add",
     method: "post",
     data: data,
   });
@@ -28,32 +36,24 @@
 
 export function del(data) {
   return request({
-    url: '/productProcess/batchDelete',
-    method: 'delete',
+    url: "/technologyOperation/batchDelete",
+    method: "delete",
     data: data,
-  })
+  });
 }
 
 export function update(data) {
   return request({
-    url: '/productProcess/update',
-    method: 'put',
+    url: "/technologyOperation/update",
+    method: "put",
     data: data,
-  })
-}
-
-// 宸ュ簭鏌ヨ
-export function list() {
-    return request({
-        url: "/productProcess/list",
-        method: "get",
-    });
+  });
 }
 
 // 瀵煎叆鏁版嵁
 export function importData(data) {
   return request({
-    url: "/productProcess/importData",
+    url: "/technologyOperation/importData",
     method: "post",
     data: data,
   });
@@ -62,8 +62,43 @@
 // 涓嬭浇妯℃澘
 export function downloadTemplate() {
   return request({
-    url: "/productProcess/downloadTemplate",
+    url: "/technologyOperation/downloadTemplate",
     method: "post",
     responseType: "blob",
   });
-}
\ No newline at end of file
+}
+
+// 鑾峰彇宸ュ簭鍙傛暟鍒楄〃
+export function getProcessParamList(params) {
+  return request({
+    url: `/technologyOperationParam/list`,
+    method: "get",
+    params,
+  });
+}
+
+// 娣诲姞宸ュ簭鍙傛暟
+export function addProcessParam(data) {
+  return request({
+    url: "/technologyOperationParam/",
+    method: "post",
+    data: data,
+  });
+}
+
+// 缂栬緫宸ュ簭鍙傛暟
+export function editProcessParam(data) {
+  return request({
+    url: "/technologyOperationParam/",
+    method: "post",
+    data: data,
+  });
+}
+
+// 鍒犻櫎宸ュ簭鍙傛暟
+export function deleteProcessParam(id) {
+  return request({
+    url: `/technologyOperationParam/batchDelete/${id}`,
+    method: "delete",
+  });
+}
diff --git a/src/api/productionManagement/workOrder.js b/src/api/productionManagement/workOrder.js
index 7e8bd86..b3050c9 100644
--- a/src/api/productionManagement/workOrder.js
+++ b/src/api/productionManagement/workOrder.js
@@ -2,7 +2,7 @@
 
 export function productWorkOrderPage(query) {
   return request({
-    url: "/productWorkOrder/page",
+    url: "/productionOperationTask/page",
     method: "get",
     params: query,
   });
@@ -10,7 +10,7 @@
 
 export function updateProductWorkOrder(data) {
   return request({
-    url: "/productWorkOrder/updateProductWorkOrder",
+    url: "/productionOperationTask/updateProductWorkOrder",
     method: "post",
     data: data,
   });
@@ -24,12 +24,74 @@
   });
 }
 
+export function assignProductWorkOrder(data) {
+  return request({
+    url: "/productionOperationTask/assign",
+    method: "post",
+    data: data,
+  });
+}
+
 // 涓嬭浇宸ュ崟娴佽浆鍗★紙杩斿洖鏂囦欢娴侊級
 export function downProductWorkOrder(id) {
   return request({
-    url: "/productWorkOrder/down",
+    url: "/productionOperationTask/down",
     method: "post",
     data: { id },
     responseType: "blob",
   });
 }
+
+// 宸ュ崟-褰撳墠宸ュ簭鐗╂枡鍙拌处
+export function listWorkOrderMaterialLedger(query) {
+  return request({
+    url: "/productOrderMaterial/reportMaterials",
+    method: "get",
+    params: query,
+  });
+}
+
+// 宸ュ崟-琛ユ枡
+export function addWorkOrderMaterialSupplement(data) {
+  return request({
+    url: "/productionOperationTask/material/supplement",
+    method: "post",
+    data,
+  });
+}
+
+// 宸ュ崟-閫�鏂�
+export function addWorkOrderMaterialReturn(data) {
+  return request({
+    url: "/productionOperationTask/material/return",
+    method: "post",
+    data,
+  });
+}
+
+// 宸ュ崟-琛ユ枡璁板綍
+export function listWorkOrderMaterialSupplementRecord(query) {
+  return request({
+    url: "/productionOperationTask/material/supplementRecord",
+    method: "get",
+    params: query,
+  });
+}
+
+// 宸ュ崟-棰嗙敤锛堟彁浜ゅ疄闄呴鐢ㄦ暟閲忥級
+export function pickWorkOrderMaterial(data) {
+  return request({
+    url: "/productionOperationTask/material/pick",
+    method: "post",
+    data,
+  });
+}
+
+// 鑾峰彇宸ュ簭缁熻鏁版嵁
+export function getOperationStatistics(query) {
+  return request({
+    url: "/productionOperationTask/getOperation",
+    method: "get",
+    params: query,
+  });
+}
diff --git a/src/api/productionPlan/productionPlan.js b/src/api/productionPlan/productionPlan.js
new file mode 100644
index 0000000..5bcff27
--- /dev/null
+++ b/src/api/productionPlan/productionPlan.js
@@ -0,0 +1,79 @@
+// 鐢熶骇璁㈠崟椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+export function productionPlanListPage(query) {
+  return request({
+    url: "/productionPlan/listPage",
+    method: "get",
+    params: query,
+  });
+}
+
+// 鎷夊彇鏁版嵁
+export function loadProdData(query) {
+  return request({
+    url: "/productionPlan/loadProdData",
+    method: "get",
+    params: query,
+  });
+}
+
+export function summaryByProductType(query) {
+  return request({
+    url: "/productionPlan/summaryByProductType",
+    method: "get",
+    params: query,
+  });
+}
+
+// 瀵煎嚭鐢熶骇璁″垝
+export function exportProductionPlan(bomId) {
+  return request({
+    url: "/productionPlan/export",
+    method: "post",
+    params: { bomId },
+    responseType: "blob",
+  });
+}
+
+// 鐢熶骇璁″垝-鏂板淇敼
+export function productionPlanAdd(query) {
+  return request({
+    url: "/productionPlan/addProductionPlan",
+    method: "post",
+    data: query,
+  });
+}
+export function productionPlanUpdate(query) {
+  return request({
+    url: "/productionPlan/updateProductionPlan",
+    method: "put",
+    data: query,
+  });
+}
+
+// 鐢熶骇璁″垝-鍒犻櫎
+export function productionPlanDelete(data) {
+  return request({
+    url: "/productionPlan/deleteProductionPlan",
+    method: "delete",
+    data,
+  });
+}
+// 鍚堝苟涓嬪彂
+export function productionPlanCombine(query) {
+  return request({
+    url: "/productionPlan/combine",
+    method: "post",
+    data: query,
+  });
+}
+
+// 杩借釜杩涘害
+export function trackProgressByNo(query) {
+  return request({
+    url: "/track/trackProgressByNo",
+    method: "get",
+    params: query,
+  });
+}
diff --git a/src/api/salesManagement/deliveryLedger.js b/src/api/salesManagement/deliveryLedger.js
index 4be5829..b85582c 100644
--- a/src/api/salesManagement/deliveryLedger.js
+++ b/src/api/salesManagement/deliveryLedger.js
@@ -11,6 +11,13 @@
 }
 
 // 淇敼鍙戣揣鍙拌处
+export function getDeliveryDetail(id) {
+  return request({
+    url: `/shippingInfo/getDateil/${id}`,
+    method: "get",
+  });
+}
+
 export function addOrUpdateDeliveryLedger(query) {
   return request({
     url: "/shippingInfo/update",
diff --git a/src/api/system/appVersion.js b/src/api/system/appVersion.js
new file mode 100644
index 0000000..fc7bdc5
--- /dev/null
+++ b/src/api/system/appVersion.js
@@ -0,0 +1,19 @@
+import request from "@/utils/request";
+
+// 鏌ヨ APP 鐗堟湰鍒嗛〉鍒楄〃
+export function listAppVersion(params) {
+  return request({
+    url: "/app/getAllVersion",
+    method: "get",
+    params,
+  });
+}
+
+// 涓婁紶 APK
+export function add(data) {
+  return request({
+    url: "/app/add",
+    method: "post",
+    data
+  });
+}
diff --git a/src/assets/styles/element-ui.scss b/src/assets/styles/element-ui.scss
index 8c741af..f296f9d 100644
--- a/src/assets/styles/element-ui.scss
+++ b/src/assets/styles/element-ui.scss
@@ -47,50 +47,57 @@
 }
 
 // to fixed https://github.com/ElemeFE/element/issues/2461
-.el-dialog {
-  transform: none;
-  left: 0;
-  position: relative;
-  margin: 0 auto;
-  border-radius: 8px;
-  padding: 0 !important;
-}
-.el-dialog__header {
-  background: #f5f6f7;
-  padding: 12px 16px;
-  border-radius: 8px 8px 0 0;
-}
-.el-dialog__title {
-  font-weight: 400;
-  font-size: 16px;
-  color: #2e3033;
-}
-.el-dialog__body {
-  padding: 16px 40px 0 40px;
-  max-height: 74vh;
-  overflow-y: auto;
-}
-.el-dialog__footer {
-  text-align: center;
-  padding: 16px;
-}
-.el-message-box {
-  padding: 0 !important;
-  border-radius: 8px;
-}
-.el-message-box__header {
-  background: #f5f6f7;
-  padding: 12px 16px;
-  border-radius: 8px 8px 0 0;
-}
-.el-message-box__title {
-  font-weight: 400;
-  font-size: 16px;
-  color: #2e3033;
-}
-.el-message-box__content {
-  padding: 16px 40px 0 40px;
-}
+.el-dialog {
+  transform: none;
+  left: 0;
+  position: relative;
+  margin: 0 auto;
+  border-radius: 24px;
+  padding: 0 !important;
+  border: 1px solid var(--surface-border);
+  box-shadow: var(--shadow-md);
+  background: rgba(255, 255, 255, 0.96);
+}
+.el-dialog__header {
+  background: linear-gradient(180deg, rgba(247, 250, 248, 0.98), rgba(242, 247, 244, 0.88));
+  padding: 18px 24px 14px;
+  border-bottom: 1px solid var(--surface-border);
+  border-radius: 24px 24px 0 0;
+}
+.el-dialog__title {
+  font-weight: 600;
+  font-size: 17px;
+  color: var(--text-primary);
+}
+.el-dialog__body {
+  padding: 24px 24px 0;
+  max-height: 74vh;
+  overflow-y: auto;
+}
+.el-dialog__footer {
+  text-align: center;
+  padding: 18px 24px 24px;
+}
+.el-message-box {
+  padding: 0 !important;
+  border-radius: 22px;
+  border: 1px solid var(--surface-border);
+  box-shadow: var(--shadow-md);
+}
+.el-message-box__header {
+  background: linear-gradient(180deg, rgba(247, 250, 248, 0.98), rgba(242, 247, 244, 0.88));
+  padding: 18px 24px 14px;
+  border-bottom: 1px solid var(--surface-border);
+  border-radius: 22px 22px 0 0;
+}
+.el-message-box__title {
+  font-weight: 600;
+  font-size: 17px;
+  color: var(--text-primary);
+}
+.el-message-box__content {
+  padding: 24px 24px 0;
+}
 .el-message-box__container {
   justify-content: center;
 }
@@ -105,12 +112,12 @@
     margin-right: 12px;
   }
 }
-.el-table__expanded-cell {
-  padding: 0 !important;
-  .el-table__header-wrapper {
-    background-color: #f5f8ff !important;
-  }
-}
+.el-table__expanded-cell {
+  padding: 0 !important;
+  .el-table__header-wrapper {
+    background-color: var(--surface-soft) !important;
+  }
+}
 
 // refine element ui upload
 .upload-container {
@@ -149,6 +156,86 @@
   display: none;
 }
 
-.el-dropdown .el-dropdown-link {
-  color: var(--el-color-primary) !important;
-}
+.el-dropdown .el-dropdown-link {
+  color: var(--el-color-primary) !important;
+}
+
+.el-button {
+  border-radius: 12px;
+  font-weight: 600;
+  box-shadow: none !important;
+}
+
+.el-button--primary {
+  --el-button-bg-color: var(--el-color-primary);
+  --el-button-border-color: var(--el-color-primary);
+  --el-button-hover-bg-color: var(--el-color-primary-light-3);
+  --el-button-hover-border-color: var(--el-color-primary-light-3);
+  --el-button-active-bg-color: var(--el-color-primary-dark-2);
+  --el-button-active-border-color: var(--el-color-primary-dark-2);
+}
+
+.el-input__wrapper,
+.el-textarea__inner,
+.el-select__wrapper,
+.el-date-editor.el-input__wrapper,
+.el-date-editor .el-input__wrapper {
+  border-radius: 12px;
+  box-shadow: 0 0 0 1px rgba(216, 225, 219, 0.92) inset !important;
+  background: rgba(255, 255, 255, 0.9);
+}
+
+.el-input__wrapper.is-focus,
+.el-select__wrapper.is-focused,
+.el-textarea__inner:focus {
+  box-shadow: 0 0 0 1px rgba(0, 47, 167, 0.28) inset !important;
+}
+
+.el-card {
+  border: 1px solid var(--surface-border);
+  box-shadow: var(--shadow-sm);
+  background: rgba(255, 255, 255, 0.88);
+}
+
+.el-table {
+  --el-table-border-color: var(--surface-border);
+  --el-table-header-bg-color: var(--surface-soft);
+  --el-table-row-hover-bg-color: #f1f6f4;
+  --el-table-current-row-bg-color: #e9f0ed;
+  border-radius: 18px;
+}
+
+.el-table th.el-table__cell {
+  background: var(--surface-soft) !important;
+  color: var(--text-secondary);
+  font-weight: 600;
+}
+
+.el-table tr,
+.el-table td.el-table__cell,
+.el-table__body tr > td.el-table__cell {
+  background: var(--surface-base) !important;
+}
+
+.el-table .el-table__body tr:hover > td.el-table__cell {
+  background: var(--el-table-row-hover-bg-color) !important;
+}
+
+.el-table .el-table__body tr.current-row > td.el-table__cell {
+  background: var(--el-table-current-row-bg-color) !important;
+}
+
+.el-table .el-table__footer-wrapper {
+  border-top: 1px solid var(--surface-border);
+}
+
+.el-table .el-table__footer-wrapper tbody td.el-table__cell,
+.el-table .el-table__footer-wrapper tfoot td.el-table__cell {
+  background: var(--surface-base) !important;
+  border-top: 1px solid var(--surface-border);
+  font-weight: 600;
+}
+
+.el-pagination {
+  margin-top: 18px;
+}
diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss
index 13d26ee..f905a36 100644
--- a/src/assets/styles/index.scss
+++ b/src/assets/styles/index.scss
@@ -12,11 +12,16 @@
   -moz-osx-font-smoothing: grayscale;
   -webkit-font-smoothing: antialiased;
   text-rendering: optimizeLegibility;
-  font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
+  font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
+  background:
+    radial-gradient(circle at top left, rgba(214, 226, 219, 0.8), transparent 28%),
+    linear-gradient(180deg, #f7faf8 0%, var(--app-bg) 100%);
+  color: var(--text-primary);
 }
 
 label {
-  font-weight: 700;
+  font-weight: 600;
+  color: var(--text-secondary);
 }
 
 html {
@@ -26,6 +31,12 @@
 
 #app {
   height: 100%;
+}
+
+html,
+body,
+#app {
+  background-color: var(--app-bg);
 }
 
 *,
@@ -123,7 +134,7 @@
 
 //main-container鍏ㄥ眬鏍峰紡
 .app-container {
-  padding: 20px;
+  padding: 20px 24px 24px;
 }
 .search_form {
   display: flex;
@@ -131,15 +142,17 @@
   justify-content: space-between;
   .search_title {
     font-size: 14px;
-    font-weight: 700;
-    color: #333333;
+    font-weight: 600;
+    letter-spacing: 0.04em;
+    color: var(--text-secondary);
   }
 }
 .table_list {
-  height: calc(100vh - 11em);
-  margin-top: 20px;
-  background: #fff;
-  padding: 18px
+  background: rgba(255, 255, 255, 0.88);
+  border: 1px solid var(--surface-border);
+  border-radius: var(--radius-md);
+  box-shadow: var(--shadow-sm);
+  padding: 18px;
 }
 .components-container {
   margin: 30px 50px;
@@ -176,11 +189,11 @@
 
 .link-type,
 .link-type:focus {
-  color: #337ab7;
+  color: var(--el-color-primary);
   cursor: pointer;
 
   &:hover {
-    color: rgb(32, 160, 255);
+    color: #165e57;
   }
 }
 
@@ -193,3 +206,17 @@
     margin-bottom: 10px;
   }
 }
+
+.app-container,
+.table_list,
+.components-container {
+  .el-card,
+  .el-dialog,
+  .el-drawer,
+  .el-table,
+  .el-descriptions,
+  .el-collapse-item__wrap,
+  .el-tabs__content {
+    border-radius: var(--radius-md);
+  }
+}
diff --git a/src/assets/styles/sidebar.scss b/src/assets/styles/sidebar.scss
index 8534448..be7b7a7 100644
--- a/src/assets/styles/sidebar.scss
+++ b/src/assets/styles/sidebar.scss
@@ -4,7 +4,7 @@
     transition: margin-left 0.28s;
     margin-left: $base-sidebar-width;
     position: relative;
-    background: #f5f7fb;
+    background: transparent;
   }
 
   .sidebarHide {
@@ -22,8 +22,9 @@
     left: 0;
     z-index: 1001;
     overflow: hidden;
-    -webkit-box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
-    box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
+    padding: 12px 0 16px 16px;
+    background: transparent;
+    box-shadow: none;
 
     // reset element-ui css
     .horizontal-collapse-transition {
@@ -45,7 +46,8 @@
 
     &.has-logo {
       .el-scrollbar {
-        height: calc(100% - 50px);
+        height: calc(100% - 72px);
+        margin-top: 10px;
       }
     }
 
@@ -63,11 +65,16 @@
       margin-right: 16px;
     }
 
-    .el-menu {
-      border: none;
-      height: 100%;
-      width: 100% !important;
-    }
+    .el-menu {
+      border: none;
+      height: 100%;
+      width: 100% !important;
+      padding: 10px 8px 18px;
+      border-radius: 22px;
+      background: var(--menu-surface);
+      backdrop-filter: blur(18px);
+      box-shadow: var(--shadow-sm);
+    }
 
     .el-menu-item,
     .menu-title {
@@ -80,80 +87,144 @@
       display: inline-block !important;
     }
 
-    // menu hover
-    .sub-menu-title-noDropdown,
-    .el-sub-menu__title {
-      &:hover {
-        background-color: rgba(212, 221, 255, 0.8) !important;
-      }
-    }
+    // menu hover
+    .submenu-title-noDropdown,
+    .el-sub-menu__title {
+      &:hover {
+        background-color: var(--menu-hover) !important;
+        border-radius: 14px;
+      }
+    }
     & .theme-light .is-active > .el-sub-menu__title {
-      color: #fff !important;
+      color: var(--current-color) !important;
     }
 
-    & .nest-menu .el-sub-menu > .el-sub-menu__title,
-    & .el-sub-menu .el-menu-item {
-      min-width: $base-sidebar-width !important;
+    & .nest-menu .el-sub-menu > .el-sub-menu__title,
+    & .el-sub-menu .el-menu-item {
+      min-width: 0 !important;
+      margin: 0 12px 6px;
+      width: calc(100% - 24px);
+      padding-left: 8px !important;
+      padding-right: 8px !important;
+      box-sizing: border-box;
+
+      &:hover {
+        background-color: var(--menu-hover) !important;
+      }
+      &.is-active {
+        background-color: var(--menu-active-bg) !important;
+        border-radius: 14px;
+      }
+    }
 
-      &:hover {
-        background-color: rgba(212, 221, 255, 0.8) !important;
-      }
-      &.is-active {
-        background-color: #fff !important;
-      }
-    }
+    & .theme-light .nest-menu .el-sub-menu > .el-sub-menu__title,
+    & .theme-light .el-sub-menu .el-menu-item {
+      //background-color: transparent;
+
+      &:hover {
+        background-color: var(--menu-hover) !important;
+        border-radius: 14px;
+      }
+    }
+  }
 
-    & .theme-light .nest-menu .el-sub-menu > .el-sub-menu__title,
-    & .theme-light .el-sub-menu .el-menu-item {
-      //background-color: transparent;
+  .hideSidebar {
+    .sidebar-container {
+      width: 68px !important;
+      padding-left: 0;
+      padding-right: 0;
+    }
+
+    .main-container {
+      margin-left: 84px;
+    }
 
-      &:hover {
-        background-color: rgba(212, 221, 255, 0.8) !important;
-      }
-    }
-  }
-
-  .hideSidebar {
-    .sidebar-container {
-      width: 54px !important;
-    }
-
-    .main-container {
-      margin-left: 54px;
-    }
-
-    .sub-menu-title-noDropdown {
-      padding: 0 !important;
-      position: relative;
-
-      .el-tooltip {
-        padding: 0 !important;
-
-        .svg-icon {
-          margin-left: 20px;
-        }
-      }
-    }
-    .el-sub-menu {
-      overflow: hidden;
-
-      & > .el-sub-menu__title {
-        padding: 0 !important;
-
-        .svg-icon {
-          margin-left: 20px;
-        }
-      }
-    }
-
-    .el-menu--collapse {
-      .el-sub-menu {
-        & > .el-sub-menu__title {
-          & > span {
-            height: 0;
-            width: 0;
-            overflow: hidden;
-            visibility: hidden;
+    .submenu-title-noDropdown {
+      padding: 0 !important;
+      position: relative;
+      display: flex !important;
+      align-items: center;
+      justify-content: center;
+
+      .svg-icon {
+        margin-right: 0;
+      }
+
+      .el-tooltip {
+        padding: 0 !important;
+        display: inline-flex !important;
+        align-items: center;
+        justify-content: center;
+        width: 100%;
+
+        .svg-icon {
+          margin-left: 0;
+        }
+      }
+
+      .el-menu-tooltip__trigger {
+        width: 100%;
+        display: inline-flex !important;
+        align-items: center;
+        justify-content: center;
+
+        .svg-icon {
+          width: 18px;
+          height: 18px;
+          margin-right: 0;
+          flex-shrink: 0;
+        }
+      }
+    }
+    .el-sub-menu {
+      overflow: hidden;
+
+      & > .el-sub-menu__title {
+        padding: 0 !important;
+        display: flex !important;
+        align-items: center;
+        justify-content: center;
+
+        .svg-icon {
+          margin-left: 0;
+          margin-right: 0;
+        }
+      }
+    }
+
+    .el-menu--collapse {
+      width: 100% !important;
+      padding: 10px 6px 18px;
+
+      > .el-menu-item,
+      .el-sub-menu {
+        & > .el-sub-menu__title,
+        &.el-menu-item {
+          margin: 0 0 6px;
+          width: 100%;
+          padding-left: 0 !important;
+          padding-right: 0 !important;
+          box-sizing: border-box;
+          display: flex !important;
+          align-items: center;
+          justify-content: center;
+
+          .svg-icon {
+            width: 18px;
+            height: 18px;
+            margin-right: 0;
+            flex-shrink: 0;
+          }
+
+          &:hover {
+            border-radius: 14px;
+          }
+
+          & > span {
+            height: 0;
+            width: 0;
+            overflow: hidden;
+            visibility: hidden;
             display: inline-block;
           }
           & > i {
@@ -162,11 +233,11 @@
             overflow: hidden;
             visibility: hidden;
             display: inline-block;
-          }
-        }
-      }
-    }
-  }
+          }
+        }
+      }
+    }
+  }
 
   .el-menu--collapse .el-menu .el-sub-menu {
     min-width: $base-sidebar-width !important;
@@ -208,24 +279,36 @@
     }
   }
 
-  .nest-menu .el-sub-menu > .el-sub-menu__title,
-  .el-menu-item {
-    &:hover {
-      // you can use $sub-menuHover
-      background-color: rgba(212, 221, 255, 0.56) !important;
-    }
-    &.is-active {
-      background-color: rgba(212, 221, 255, 0.56) !important;
-    }
-  }
+  .nest-menu .el-sub-menu > .el-sub-menu__title,
+  .el-menu-item {
+    min-width: 0 !important;
+    margin: 0 12px 6px;
+    width: calc(100% - 24px);
+    padding-left: 8px !important;
+    padding-right: 8px !important;
+    box-sizing: border-box;
+
+    &:hover {
+      // you can use $sub-menuHover
+      background-color: var(--menu-hover) !important;
+    }
+    &.is-active {
+      background-color: var(--menu-active-bg) !important;
+      border-radius: 14px;
+    }
+  }
 
   // the scroll bar appears when the sub-menu is too long
   > .el-menu--popup {
     max-height: 100vh;
     overflow-y: auto;
+    padding: 8px;
+    border-radius: 18px;
+    border: 1px solid var(--surface-border);
+    box-shadow: var(--shadow-md);
 
     &::-webkit-scrollbar-track-piece {
-      background: #d3dce6;
+      background: #dfe7e1;
     }
 
     &::-webkit-scrollbar {
@@ -233,7 +316,7 @@
     }
 
     &::-webkit-scrollbar-thumb {
-      background: #99a9bf;
+      background: #9aa79e;
       border-radius: 20px;
     }
   }
diff --git a/src/assets/styles/variables.module.scss b/src/assets/styles/variables.module.scss
index 3194051..55b3822 100644
--- a/src/assets/styles/variables.module.scss
+++ b/src/assets/styles/variables.module.scss
@@ -1,221 +1,225 @@
-// base color
-$blue: #324157;
-$light-blue: #333c46;
-$red: #c03639;
-$pink: #e65d6e;
-$green: #30b08f;
-$tiffany: #4ab7bd;
-$yellow: #fec171;
-$panGreen: #30b08f;
-
-// 榛樿涓婚鍙橀噺
-$menuText: #bfcbd9;
-$menuActiveText: #409eff;
-$menuBg: #304156;
-$menuHover: #263445;
-
-// 娴呰壊涓婚theme-light
-$menuLightBg: #002fa7;
-$menuLightHover: #f0f1f5;
-$menuLightText: #fff;
-$menuLightActiveText: #002fa7;
-
-// 鍩虹鍙橀噺
-$base-sidebar-width: 200px;
-$sideBarWidth: 200px;
-
-// 鑿滃崟鏆楄壊鍙橀噺
-$base-menu-color: #bfcbd9;
-$base-menu-color-active: #f4f4f5;
-$base-menu-background: #304156;
-$base-sub-menu-background: #1f2d3d;
-$base-sub-menu-hover: #fff;
-
-// 缁勪欢鍙橀噺
-$--color-primary: #409eff;
-$--color-success: #67c23a;
-$--color-warning: #e6a23c;
-$--color-danger: #f56c6c;
-$--color-info: #909399;
-
-:export {
-  menuText: $menuText;
-  menuActiveText: $menuActiveText;
-  menuBg: $menuBg;
-  menuHover: $menuHover;
-  menuLightBg: $menuLightBg;
-  menuLightHover: $menuLightHover;
-  menuLightText: $menuLightText;
-  menuLightActiveText: $menuLightActiveText;
-  sideBarWidth: $sideBarWidth;
-  // 瀵煎嚭鍩虹棰滆壊
-  blue: $blue;
-  lightBlue: $light-blue;
-  red: $red;
-  pink: $pink;
-  green: $green;
-  tiffany: $tiffany;
-  yellow: $yellow;
-  panGreen: $panGreen;
-  // 瀵煎嚭缁勪欢棰滆壊
-  colorPrimary: $--color-primary;
-  colorSuccess: $--color-success;
-  colorWarning: $--color-warning;
-  colorDanger: $--color-danger;
-  colorInfo: $--color-info;
-}
-
-// CSS鍙橀噺瀹氫箟
-:root {
-  /* 浜壊妯″紡鍙橀噺 */
-  --sidebar-bg: #{$menuBg};
-  --sidebar-text: #{$menuText};
-  --menu-hover: #{$menuHover};
-
-  --navbar-bg: #ffffff;
-  --navbar-text: #303133;
-
-  /* splitpanes default-theme 鍙橀噺 */
-  --splitpanes-default-bg: #ffffff;
-}
-
-// 鏆楅粦妯″紡鍙橀噺
-html.dark {
-  /* 榛樿閫氱敤 */
-  --el-bg-color: #141414;
-  --el-bg-color-overlay: #1d1e1f;
-  --el-text-color-primary: #ffffff;
-  --el-text-color-regular: #d0d0d0;
-  --el-border-color: #434343;
-  --el-border-color-light: #434343;
-
-  /* 渚ц竟鏍� */
-  --sidebar-bg: #141414;
-  --sidebar-text: #ffffff;
-  --menu-hover: #2d2d2d;
-  --menu-active-text: #{$menuActiveText};
-
-  /* 椤堕儴瀵艰埅鏍� */
-  --navbar-bg: #141414;
-  --navbar-text: #ffffff;
-  --navbar-hover: #141414;
-
-  /* 鏍囩鏍� */
-  --tags-bg: #141414;
-  --tags-item-bg: #1d1e1f;
-  --tags-item-border: #303030;
-  --tags-item-text: #d0d0d0;
-  --tags-item-hover: #2d2d2d;
-  --tags-close-hover: #64666a;
-
-  /* splitpanes 缁勪欢鏆楅粦妯″紡鍙橀噺 */
-  --splitpanes-bg: #141414;
-  --splitpanes-border: #303030;
-  --splitpanes-splitter-bg: #1d1e1f;
-  --splitpanes-splitter-hover-bg: #2d2d2d;
-
-  /* blockquote 鏆楅粦妯″紡鍙橀噺 */
-  --blockquote-bg: #1d1e1f;
-  --blockquote-border: #303030;
-  --blockquote-text: #d0d0d0;
-
-  /* Cron 鏃堕棿琛ㄨ揪寮� 妯″紡鍙橀噺 */
-  --cron-border: #303030;
-
-  /* splitpanes default-theme 鏆楅粦妯″紡鍙橀噺 */
-  --splitpanes-default-bg: #141414;
-
-  /* 渚ц竟鏍忚彍鍗曡鐩� */
-  .sidebar-container {
-    .el-menu-item,
-    .menu-title {
-      color: var(--el-text-color-regular);
-    }
-    & .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
-    & .theme-dark .el-sub-menu .el-menu-item {
-      background-color: var(--el-bg-color) !important;
-    }
-  }
-
-  /* 椤堕儴鏍忔爮鑿滃崟瑕嗙洊 */
-  .el-menu--horizontal {
-    .el-menu-item {
-      &:not(.is-disabled) {
-        &:hover,
-        &:focus {
-          background-color: var(--navbar-hover) !important;
-        }
-      }
-    }
-  }
-
-  /* 鍒嗗壊绐楁牸瑕嗙洊 */
-  .splitpanes {
-    background-color: var(--splitpanes-bg);
-
-    .splitpanes__pane {
-      background-color: var(--splitpanes-bg);
-      border-color: var(--splitpanes-border);
-    }
-
-    .splitpanes__splitter {
-      background-color: var(--splitpanes-splitter-bg);
-      border-color: var(--splitpanes-border);
-
-      &:hover {
-        background-color: var(--splitpanes-splitter-hover-bg);
-      }
-
-      &:before,
-      &:after {
-        background-color: var(--splitpanes-border);
-      }
-    }
-  }
-
-  /* 琛ㄦ牸鏍峰紡瑕嗙洊 */
-  .el-table {
-    --el-table-header-bg-color: var(--el-bg-color-overlay) !important;
-    --el-table-header-text-color: var(--el-text-color-regular) !important;
-    --el-table-border-color: var(--el-border-color-light) !important;
-    --el-table-row-hover-bg-color: var(--el-bg-color-overlay) !important;
-
-    .el-table__header-wrapper,
-    .el-table__fixed-header-wrapper {
-      th {
-        background-color: var(--el-bg-color-overlay, #f0f1f5) !important;
-        color: var(--el-text-color-regular, #515a6e);
-      }
-    }
-  }
-
-  /* 鏍戠粍浠堕珮浜牱寮忚鐩� */
-  .el-tree {
-    .el-tree-node.is-current > .el-tree-node__content {
-      background-color: var(--el-bg-color-overlay) !important;
-      color: var(--el-color-primary);
-    }
-
-    .el-tree-node__content:hover {
-      background-color: var(--el-bg-color-overlay);
-    }
-  }
-
-  /* 涓嬫媺鑿滃崟鏍峰紡瑕嗙洊 */
-  .el-dropdown-menu__item:not(.is-disabled):focus,
-  .el-dropdown-menu__item:not(.is-disabled):hover {
-    background-color: var(--navbar-hover) !important;
-  }
-
-  /* blockquote鏍峰紡瑕嗙洊 */
-  blockquote {
-    background-color: var(--blockquote-bg) !important;
-    border-left-color: var(--blockquote-border) !important;
-    color: var(--blockquote-text) !important;
-  }
-
-  /* 鏃堕棿琛ㄨ揪寮忔爣棰樻牱寮忚鐩� */
-  .popup-result .title {
-    background: var(--cron-border);
-  }
-}
+// base color
+$blue: #324157;
+$light-blue: #333c46;
+$red: #c03639;
+$pink: #e65d6e;
+$green: #30b08f;
+$tiffany: #4ab7bd;
+$yellow: #fec171;
+$panGreen: #30b08f;
+
+// menu palette
+$menuText: #677287;
+$menuActiveText: #1f7a72;
+$menuBg: #f4f7f4;
+$menuHover: #e7eeea;
+
+// light theme
+$menuLightBg: #f4f7f4;
+$menuLightHover: #e7eeea;
+$menuLightText: #3b4658;
+$menuLightActiveText: #1f7a72;
+
+// layout
+$base-sidebar-width: 216px;
+$sideBarWidth: 216px;
+
+// sidebar
+$base-menu-color: #677287;
+$base-menu-color-active: #1f7a72;
+$base-menu-background: #f4f7f4;
+$base-sub-menu-background: #eef3ef;
+$base-sub-menu-hover: #ffffff;
+
+// component
+$--color-primary: #1f7a72;
+$--color-success: #67c23a;
+$--color-warning: #d89b41;
+$--color-danger: #d25b52;
+$--color-info: #7d8797;
+
+:export {
+  menuText: $menuText;
+  menuActiveText: $menuActiveText;
+  menuBg: $menuBg;
+  menuHover: $menuHover;
+  menuLightBg: $menuLightBg;
+  menuLightHover: $menuLightHover;
+  menuLightText: $menuLightText;
+  menuLightActiveText: $menuLightActiveText;
+  sideBarWidth: $sideBarWidth;
+  blue: $blue;
+  lightBlue: $light-blue;
+  red: $red;
+  pink: $pink;
+  green: $green;
+  tiffany: $tiffany;
+  yellow: $yellow;
+  panGreen: $panGreen;
+  colorPrimary: $--color-primary;
+  colorSuccess: $--color-success;
+  colorWarning: $--color-warning;
+  colorDanger: $--color-danger;
+  colorInfo: $--color-info;
+}
+
+:root {
+  --sidebar-bg: #{$menuBg};
+  --sidebar-text: #{$menuText};
+  --sidebar-muted: #93a0b1;
+  --menu-hover: #{$menuHover};
+  --menu-active-bg: #dfe9e4;
+  --menu-surface: rgba(255, 255, 255, 0.72);
+
+  --app-bg: #eef2ee;
+  --app-bg-accent: #dfe8e2;
+  --surface-base: #ffffff;
+  --surface-soft: #f7faf8;
+  --surface-muted: #eff4f1;
+  --surface-border: #d8e1db;
+  --surface-border-strong: #c9d5ce;
+  --text-primary: #21313f;
+  --text-secondary: #5f6d7e;
+  --text-tertiary: #8a98a8;
+  --shadow-sm: 0 10px 30px rgba(31, 49, 38, 0.06);
+  --shadow-md: 0 18px 50px rgba(31, 49, 38, 0.1);
+  --radius-lg: 24px;
+  --radius-md: 18px;
+  --radius-sm: 12px;
+
+  --navbar-bg: rgba(255, 255, 255, 0.78);
+  --navbar-text: #21313f;
+  --navbar-hover: rgba(31, 122, 114, 0.08);
+
+  --tags-bg: transparent;
+  --tags-item-bg: rgba(255, 255, 255, 0.74);
+  --tags-item-border: rgba(201, 213, 206, 0.88);
+  --tags-item-text: #5f6d7e;
+  --tags-item-hover: rgba(31, 122, 114, 0.08);
+  --tags-close-hover: rgba(31, 122, 114, 0.18);
+
+  --splitpanes-default-bg: #ffffff;
+}
+
+html.dark {
+  --el-bg-color: #141414;
+  --el-bg-color-overlay: #1d1e1f;
+  --el-text-color-primary: #ffffff;
+  --el-text-color-regular: #d0d0d0;
+  --el-border-color: #434343;
+  --el-border-color-light: #434343;
+
+  --sidebar-bg: #141414;
+  --sidebar-text: #ffffff;
+  --menu-hover: #2d2d2d;
+  --menu-active-text: #{$menuActiveText};
+
+  --navbar-bg: #141414;
+  --navbar-text: #ffffff;
+  --navbar-hover: #141414;
+
+  --tags-bg: #141414;
+  --tags-item-bg: #1d1e1f;
+  --tags-item-border: #303030;
+  --tags-item-text: #d0d0d0;
+  --tags-item-hover: #2d2d2d;
+  --tags-close-hover: #64666a;
+
+  --splitpanes-bg: #141414;
+  --splitpanes-border: #303030;
+  --splitpanes-splitter-bg: #1d1e1f;
+  --splitpanes-splitter-hover-bg: #2d2d2d;
+
+  --blockquote-bg: #1d1e1f;
+  --blockquote-border: #303030;
+  --blockquote-text: #d0d0d0;
+  --cron-border: #303030;
+  --splitpanes-default-bg: #141414;
+
+  .sidebar-container {
+    .el-menu-item,
+    .menu-title {
+      color: var(--el-text-color-regular);
+    }
+
+    & .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
+    & .theme-dark .el-sub-menu .el-menu-item {
+      background-color: var(--el-bg-color) !important;
+    }
+  }
+
+  .el-menu--horizontal {
+    .el-menu-item {
+      &:not(.is-disabled) {
+        &:hover,
+        &:focus {
+          background-color: var(--navbar-hover) !important;
+        }
+      }
+    }
+  }
+
+  .splitpanes {
+    background-color: var(--splitpanes-bg);
+
+    .splitpanes__pane {
+      background-color: var(--splitpanes-bg);
+      border-color: var(--splitpanes-border);
+    }
+
+    .splitpanes__splitter {
+      background-color: var(--splitpanes-splitter-bg);
+      border-color: var(--splitpanes-border);
+
+      &:hover {
+        background-color: var(--splitpanes-splitter-hover-bg);
+      }
+
+      &:before,
+      &:after {
+        background-color: var(--splitpanes-border);
+      }
+    }
+  }
+
+  .el-table {
+    --el-table-header-bg-color: var(--el-bg-color-overlay) !important;
+    --el-table-header-text-color: var(--el-text-color-regular) !important;
+    --el-table-border-color: var(--el-border-color-light) !important;
+    --el-table-row-hover-bg-color: var(--el-bg-color-overlay) !important;
+
+    .el-table__header-wrapper,
+    .el-table__fixed-header-wrapper {
+      th {
+        background-color: var(--el-bg-color-overlay, #f0f1f5) !important;
+        color: var(--el-text-color-regular, #515a6e);
+      }
+    }
+  }
+
+  .el-tree {
+    .el-tree-node.is-current > .el-tree-node__content {
+      background-color: var(--el-bg-color-overlay) !important;
+      color: var(--el-color-primary);
+    }
+
+    .el-tree-node__content:hover {
+      background-color: var(--el-bg-color-overlay);
+    }
+  }
+
+  .el-dropdown-menu__item:not(.is-disabled):focus,
+  .el-dropdown-menu__item:not(.is-disabled):hover {
+    background-color: var(--navbar-hover) !important;
+  }
+
+  blockquote {
+    background-color: var(--blockquote-bg) !important;
+    border-left-color: var(--blockquote-border) !important;
+    color: var(--blockquote-text) !important;
+  }
+
+  .popup-result .title {
+    background: var(--cron-border);
+  }
+}
diff --git a/src/components/AIChatSidebar/index.vue b/src/components/AIChatSidebar/index.vue
new file mode 100644
index 0000000..369d75d
--- /dev/null
+++ b/src/components/AIChatSidebar/index.vue
@@ -0,0 +1,4423 @@
+<template>
+  <div class="ai-chat-sidebar-wrapper">
+    <!-- 鎮诞鍥炬爣 -->
+    <div class="ai-chat-trigger" @click="toggleSidebar" v-show="!visible">
+      <el-tooltip :content="currentAssistant.tooltip" placement="left">
+        <div class="trigger-icon">
+          <el-icon :size="30" color="#fff"><component :is="currentAssistant.icon" /></el-icon>
+        </div>
+      </el-tooltip>
+    </div>
+
+    <!-- 渚ц竟鏍忓璇濇 -->
+    <el-drawer
+        v-model="visible"
+        :size="drawerSize"
+        direction="rtl"
+        :with-header="true"
+        class="ai-chat-drawer"
+        :modal="false"
+        modal-class="ai-chat-overlay"
+        :show-close="false"
+        :append-to-body="false"
+        @close="handleClose"
+    >
+      <template #header>
+        <div class="drawer-header">
+          <div class="header-left">
+            <el-icon :size="20" class="header-icon"><component :is="currentAssistant.icon" /></el-icon>
+            <span class="title">{{ currentAssistant.title }}</span>
+          </div>
+          <div v-if="showAssistantSwitch" class="assistant-switcher">
+            <el-radio-group v-model="selectedAssistantKey" size="small">
+              <el-radio-button
+                  v-for="assistant in assistants"
+                  :key="assistant.key"
+                  :label="assistant.key"
+              >
+                {{ assistant.label }}
+              </el-radio-button>
+            </el-radio-group>
+          </div>
+          <div class="header-actions">
+            <el-tooltip content="浼氳瘽鍘嗗彶" placement="bottom">
+              <el-button link class="header-action-btn" @click="handleToggleHistory">
+                <el-icon :size="18"><Timer /></el-icon>
+              </el-button>
+            </el-tooltip>
+            <el-tooltip content="寮�鍚柊浼氳瘽" placement="bottom">
+              <el-button link class="header-action-btn" @click="handleNewChat">
+                <el-icon :size="18"><Plus /></el-icon>
+              </el-button>
+            </el-tooltip>
+            <div class="action-divider"></div>
+            <el-tooltip content="鍏抽棴" placement="bottom">
+              <el-button link class="header-action-btn close-btn" @click="handleManualClose">
+                <el-icon :size="18"><Close /></el-icon>
+              </el-button>
+            </el-tooltip>
+          </div>
+        </div>
+      </template>
+
+      <div class="chat-container">
+        <!-- 鍘嗗彶浼氳瘽鍒楄〃 -->
+        <div v-if="showHistory" class="history-panel">
+          <div class="history-header">
+            <span>鏈�杩戜細璇�</span>
+            <el-button link type="primary" @click="showHistory = false">杩斿洖瀵硅瘽</el-button>
+          </div>
+          <el-skeleton :loading="loadingSessions" animated>
+            <template #template>
+              <div v-for="i in 5" :key="i" style="padding: 10px">
+                <el-skeleton-item variant="p" style="width: 80%" />
+              </div>
+            </template>
+            <div class="session-list">
+              <div
+                  v-for="session in sessions"
+                  :key="session.memoryId"
+                  :class="['session-item', { active: uuid === session.memoryId }]"
+                  @click="selectSession(session)"
+              >
+                <el-icon><ChatDotSquare /></el-icon>
+                <span class="session-name" :title="session.lastMessage || '鏂颁細璇�'">
+                  {{ session.lastMessage || '鏂颁細璇�' }}
+                </span>
+                <el-button
+                    link
+                    type="danger"
+                    class="delete-btn"
+                    @click.stop="handleDeleteSession(session.memoryId)"
+                >
+                  <el-icon><Delete /></el-icon>
+                </el-button>
+              </div>
+              <el-empty v-if="sessions.length === 0" :description="currentAssistant.emptySessionText" />
+            </div>
+          </el-skeleton>
+        </div>
+
+        <div v-else class="chat-main">
+          <div :class="['chat-hero', { compact: hasMessages }]">
+            <div :class="['assistant-stand', { thinking: isSending, compact: hasMessages }]">
+              <div class="assistant-halo"></div>
+              <div class="assistant-scan-ring"></div>
+              <div class="assistant-orbit assistant-orbit-a"></div>
+              <div class="assistant-orbit assistant-orbit-b"></div>
+              <div class="assistant-bot">
+                <div class="assistant-bot-antenna assistant-bot-antenna-left"></div>
+                <div class="assistant-bot-antenna assistant-bot-antenna-right"></div>
+                <div class="assistant-bot-head">
+                  <div class="assistant-bot-head-glow"></div>
+                  <div class="assistant-bot-eye assistant-bot-eye-left"></div>
+                  <div class="assistant-bot-eye assistant-bot-eye-right"></div>
+                  <div class="assistant-bot-mouth"></div>
+                </div>
+                <div class="assistant-bot-neck"></div>
+                <div class="assistant-bot-body">
+                  <div class="assistant-bot-core">
+                    <div class="assistant-bot-core-ring"></div>
+                    <el-icon :size="22"><component :is="currentAssistant.icon" /></el-icon>
+                  </div>
+                  <div class="assistant-bot-arm assistant-bot-arm-left"></div>
+                  <div class="assistant-bot-arm assistant-bot-arm-right"></div>
+                </div>
+              </div>
+              <div class="assistant-status">
+                <span class="assistant-status-dot"></span>
+                {{ isSending ? '鎬濊�冧腑...' : currentAssistant.label }}
+              </div>
+              <div class="assistant-base assistant-base-lg"></div>
+              <div class="assistant-base assistant-base-md"></div>
+              <div class="assistant-base assistant-base-sm"></div>
+            </div>
+
+            <div :class="['welcome-card', { compact: hasMessages }]">
+              <div class="welcome-eyebrow">鏅鸿兘鍔╂墜</div>
+              <h3 class="welcome-title">
+                鎮ㄥソ
+                <br />
+                鎴戞槸{{ currentAssistant.label }}鍒嗘瀽瑙h鍔╂墜
+              </h3>
+              <p class="welcome-desc">
+                {{ currentAssistant.description || '鎴戝彲浠ュ洿缁曚笟鍔¢棶棰樻彁渚涜В璇汇�佹煡璇㈠缓璁拰鍒嗘瀽鏀寔锛屽府鍔╀綘鏇村揩瀹屾垚鍒ゆ柇涓庡鐞嗐��' }}
+              </p>
+
+              <div class="quick-prompt-list">
+                <button
+                    v-for="prompt in displayedQuickPrompts"
+                    :key="prompt"
+                    type="button"
+                    class="quick-prompt-btn"
+                    :disabled="isSending"
+                    @click="sendQuickPrompt(prompt)"
+                >
+                  {{ prompt }}
+                </button>
+              </div>
+
+              <button
+                  v-if="quickPrompts.length > quickPromptLimit"
+                  type="button"
+                  class="more-prompts-btn"
+                  @click="refreshQuickPrompts"
+              >
+                <el-icon><RefreshRight /></el-icon>
+                <span>鎹竴鎹�</span>
+              </button>
+            </div>
+          </div>
+
+          <div v-show="!hasMessages" class="hero-dot-grid" aria-hidden="true">
+            <span v-for="dot in 28" :key="dot"></span>
+          </div>
+
+          <div class="message-list" ref="messageListRef">
+            <div
+                v-for="(message, index) in messages"
+                :key="index"
+                :class="['message-item', message.isUser ? 'user-message' : 'bot-message']"
+            >
+              <div class="avatar">
+                <el-icon v-if="message.isUser"><User /></el-icon>
+                <el-icon v-else><Cpu /></el-icon>
+              </div>
+              <div class="message-content">
+                <!-- 鏂囨湰鍐呭 -->
+                <div class="text-box" v-html="message.htmlContent"></div>
+
+                <!-- 鍥捐〃鍐呭 -->
+                <div v-if="message.chartOptions && message.chartRenderReady" class="charts-wrapper">
+                  <div
+                      v-for="(option, key) in message.chartOptions"
+                      :key="key"
+                      class="chart-item"
+                      :id="`ai-chart-${index}-${key}`"
+                  ></div>
+                </div>
+
+                <!-- 琛ㄦ牸鍐呭 -->
+                <div v-if="message.type === 'todo_list' && message.tableData" class="table-wrapper">
+                  <el-table :data="message.tableData.items" border stripe size="small" style="width: 100%">
+                    <el-table-column
+                        v-for="col in message.tableData.columns"
+                        :key="col"
+                        :prop="col"
+                        :label="columnLabelMap[col] || col"
+                        min-width="100"
+                        show-overflow-tooltip
+                    />
+                  </el-table>
+                </div>
+
+                <!-- 鎵撳瓧涓姩鐢� -->
+                <div v-if="message.purchaseAnalysisData" class="purchase-confirm-card">
+                  <div class="purchase-confirm-header">
+                    <span>{{ businessTypeLabelMap[message.purchaseAnalysisData.businessType] || message.purchaseAnalysisData.businessType || '閲囪喘涓氬姟' }}</span>
+                    <el-tag size="small" type="success" v-if="message.purchaseAnalysisData.confidence !== undefined">
+                      缃俊搴� {{ formatPercent(message.purchaseAnalysisData.confidence) }}
+                    </el-tag>
+                  </div>
+                  <div class="purchase-confirm-desc">
+                    {{ getPurchaseConfirmDescription(message.purchaseAnalysisData) }}
+                  </div>
+                  <div v-if="isPurchasePayloadEmpty(message.purchaseAnalysisData.payload)" class="purchase-empty-state">
+                    <div class="empty-title">娌℃湁璇嗗埆鍒板彲鐩存帴鎻愪氦鐨勯噰璐彴璐︿俊鎭�</div>
+                    <div class="empty-desc">褰撳墠鏂囦欢閲岀己灏戦噰璐悎鍚屽彿銆佷緵搴斿晢銆侀」鐩�佹棩鏈熴�佺墿鏂欐槑缁嗙瓑鍏抽敭鍐呭銆傝涓婁紶鏇村畬鏁寸殑鍚堝悓銆佽鍗曟垨鏄庣粏琛紝鎴栧湪涓嬫柟琛ュ厖鏁版嵁鍚庡啀纭銆�</div>
+                  </div>
+                  <div v-if="message.purchaseAnalysisData.warnings?.length" class="purchase-alert warning">
+                    <strong>椋庨櫓鎻愮ず</strong>
+                    <ul>
+                      <li v-for="(warning, warningIndex) in message.purchaseAnalysisData.warnings" :key="warningIndex">
+                        {{ formatPreviewItem(warning) }}
+                      </li>
+                    </ul>
+                  </div>
+                  <div v-if="getVisiblePurchaseMissingFields(message.purchaseAnalysisData).length" class="purchase-alert missing">
+                    <strong>闇�瑕佽ˉ鍏� {{ getVisiblePurchaseMissingFields(message.purchaseAnalysisData).length }} 椤�</strong>
+                    <el-tag
+                        v-for="field in getVisiblePurchaseMissingFields(message.purchaseAnalysisData)"
+                        :key="field"
+                        size="small"
+                        type="danger"
+                    >
+                      {{ field }}
+                    </el-tag>
+                  </div>
+                  <div v-if="message.purchaseAnalysisData.preview?.length" class="purchase-preview">
+                    <div class="purchase-section-title">纭鎽樿</div>
+                    <ul>
+                      <li v-for="(item, previewIndex) in message.purchaseAnalysisData.preview" :key="previewIndex">
+                        {{ formatPreviewItem(item) }}
+                      </li>
+                    </ul>
+                  </div>
+                  <div class="purchase-section-title">琛ュ厖鎴栫‘璁ゆ暟鎹�</div>
+                  <div class="payload-toolbar">
+                    <el-button
+                        size="small"
+                        plain
+                        :disabled="message.confirming || message.confirmed"
+                        @click="addPurchaseRootField(message)"
+                    >
+                      <el-icon><Plus /></el-icon>
+                      鏂板椤跺眰瀛楁
+                    </el-button>
+                  </div>
+                  <div class="payload-tree-table-wrapper">
+                    <el-table
+                        :data="message.payloadTreeData || []"
+                        row-key="id"
+                        border
+                        stripe
+                        size="small"
+                        default-expand-all
+                        :tree-props="{ children: 'children' }"
+                        empty-text="鏆傛棤寰呯‘璁ゆ暟鎹�"
+                    >
+                      <el-table-column label="瀛楁" min-width="240">
+                        <template #default="{ row }">
+                          <div class="payload-key-cell">
+                            <template v-if="row.parentType === 'object'">
+                              <el-input
+                                  v-if="row.keyEditable"
+                                  v-model="row.key"
+                                  size="small"
+                                  :disabled="message.confirming || message.confirmed"
+                                  placeholder="瀛楁鍚�"
+                              />
+                              <div v-else class="payload-fixed-key" :title="row.key">
+                                <span>{{ getPurchaseFieldLabel(row.key) }}</span>
+                                <small v-if="getPurchaseFieldLabel(row.key) !== row.key">{{ row.key }}</small>
+                              </div>
+                            </template>
+                            <span v-else class="payload-array-index">{{ getPurchaseArrayItemLabel(row, message) }}</span>
+                          </div>
+                        </template>
+                      </el-table-column>
+                      <el-table-column label="绫诲瀷" width="130" align="center">
+                        <template #default="{ row }">
+                          <el-select
+                              v-model="row.valueType"
+                              size="small"
+                              :disabled="message.confirming || message.confirmed"
+                              @change="handlePurchaseNodeTypeChange(message, row)"
+                          >
+                            <el-option
+                                v-for="option in purchaseValueTypeOptions"
+                                :key="option.value"
+                                :label="option.label"
+                                :value="option.value"
+                            />
+                          </el-select>
+                        </template>
+                      </el-table-column>
+                      <el-table-column label="鍊�" min-width="250">
+                        <template #default="{ row }">
+                          <div v-if="row.valueType === 'object'" class="payload-container-cell">
+                            瀵硅薄锛坽{ row.children?.length || 0 }}锛�
+                          </div>
+                          <div v-else-if="row.valueType === 'array'" class="payload-container-cell">
+                            鏁扮粍锛坽{ row.children?.length || 0 }}锛�
+                          </div>
+                          <el-switch
+                              v-else-if="row.valueType === 'boolean'"
+                              v-model="row.value"
+                              size="small"
+                              :disabled="message.confirming || message.confirmed"
+                          />
+                          <span v-else-if="row.valueType === 'null'" class="payload-null-value">null</span>
+                          <el-input
+                              v-else
+                              v-model="row.value"
+                              size="small"
+                              :placeholder="row.valueType === 'number' ? '璇疯緭鍏ユ暟瀛�' : '璇疯緭鍏ュ唴瀹�'"
+                              :disabled="message.confirming || message.confirmed"
+                          />
+                        </template>
+                      </el-table-column>
+                      <el-table-column label="鎿嶄綔" width="180" align="center">
+                        <template #default="{ row }">
+                          <div class="payload-row-actions">
+                            <el-tooltip v-if="row.valueType === 'object'" content="鏂板瀛楁" placement="top">
+                              <el-button
+                                  :icon="Plus"
+                                  circle
+                                  size="small"
+                                  text
+                                  type="primary"
+                                  :disabled="message.confirming || message.confirmed"
+                                  @click="addPurchaseChildNode(message, row)"
+                              />
+                            </el-tooltip>
+                            <el-tooltip v-else-if="row.valueType === 'array'" content="鏂板鏁扮粍椤�" placement="top">
+                              <el-button
+                                  :icon="Plus"
+                                  circle
+                                  size="small"
+                                  text
+                                  type="primary"
+                                  :disabled="message.confirming || message.confirmed"
+                                  @click="addPurchaseChildNode(message, row)"
+                              />
+                            </el-tooltip>
+                            <el-tooltip v-if="row.parentType === 'array'" content="鏂板鍚岀骇椤�" placement="top">
+                              <el-button
+                                  :icon="Plus"
+                                  circle
+                                  size="small"
+                                  text
+                                  type="primary"
+                                  :disabled="message.confirming || message.confirmed"
+                                  @click="addPurchaseSiblingNode(message, row)"
+                              />
+                            </el-tooltip>
+                            <el-tooltip content="鍒犻櫎褰撳墠椤�" placement="top">
+                              <el-button
+                                  :icon="Delete"
+                                  circle
+                                  size="small"
+                                  text
+                                  type="danger"
+                                  :disabled="message.confirming || message.confirmed"
+                                  @click="removePurchaseNode(message, row)"
+                              />
+                            </el-tooltip>
+                          </div>
+                        </template>
+                      </el-table-column>
+                    </el-table>
+                  </div>
+                  <div class="payload-editor-tip">
+                    鏃ユ湡璇峰~鍐� yyyy-MM-dd锛屼緥濡� 2026-04-30銆備骇鍝佹槑缁嗗缓璁斁鍦ㄦ瘡鏉¢噰璐彴璐︾殑 productData 涓紝纭鏃朵細鑷姩鍏煎鏃ф牸寮忓苟娓呯悊瀹℃壒瀛楁銆�
+                  </div>
+                  <div class="purchase-confirm-actions">
+                    <span v-if="message.confirmResult" :class="['confirm-result', message.confirmed ? 'success' : 'error']">
+                      {{ message.confirmResult }}
+                    </span>
+                    <el-button
+                        type="primary"
+                        size="small"
+                        :loading="message.confirming"
+                        :disabled="message.confirmed || isSending"
+                        @click="confirmPurchaseAnalysisFromTable(message)"
+                    >
+                      纭骞舵墽琛�
+                    </el-button>
+                  </div>
+                </div>
+
+                <div v-if="message.isTyping" class="typing-indicator">
+                  <span class="dot"></span>
+                  <span class="dot"></span>
+                  <span class="dot"></span>
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <div class="input-area">
+            <div class="input-actions">
+              <el-button link class="utility-action-btn" type="primary" size="small" @click="handleNewChat">
+                <el-icon><Plus /></el-icon>鏂颁細璇�
+              </el-button>
+              <el-button v-if="isSending" link class="utility-action-btn stop-action-btn" type="danger" size="small" @click="stopGeneration">
+                <el-icon><VideoPause /></el-icon>鍋滄鐢熸垚
+              </el-button>
+              <el-upload
+                  v-if="currentAssistant.allowFileUpload"
+                  class="file-upload-trigger"
+                  action="#"
+                  :auto-upload="false"
+                  :show-file-list="false"
+                  v-model:file-list="uploadFileList"
+                  :multiple="currentAssistant.allowMultipleFileUpload"
+                  :on-change="handleFileChange"
+                  :disabled="isSending"
+              >
+                <el-button link class="utility-action-btn upload-action-btn" type="primary" size="small" :disabled="isSending">
+                  <el-icon><Upload /></el-icon>鍒嗘瀽鏂囦欢
+                </el-button>
+              </el-upload>
+            </div>
+            <div class="input-box">
+              <div v-if="selectedFiles.length" class="selected-file-list">
+                <div v-for="(file, fileIndex) in selectedFiles" :key="`${file.name}-${fileIndex}`" class="selected-file-tag">
+                  <el-icon><Document /></el-icon>
+                  <span class="file-name">{{ file.name }}</span>
+                  <el-icon class="remove-file" @click="removeSelectedFile(fileIndex)"><Close /></el-icon>
+                </div>
+              </div>
+              <el-input
+                  v-model="inputMessage"
+                  type="textarea"
+                  :rows="selectedFiles.length ? 2 : 3"
+                  :placeholder="currentAssistant.placeholder"
+                  resize="none"
+                  @keydown.enter.exact.prevent="sendMessage"
+              />
+              <el-button
+                  type="primary"
+                  class="send-btn"
+                  :disabled="isSending || (!inputMessage.trim() && !selectedFiles.length)"
+                  @click="sendMessage"
+                  aria-label="鍙戦��"
+              >
+                <el-icon><Promotion /></el-icon>
+              </el-button>
+            </div>
+          </div>
+        </div>
+      </div>
+    </el-drawer>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted, nextTick, watch, computed } from 'vue'
+import request from '@/utils/request'
+import * as echarts from 'echarts'
+import { Cpu, User, Plus, Timer, Delete, ChatDotSquare, VideoPause, Upload, Document, Close, ShoppingCart, Promotion, RefreshRight } from '@element-plus/icons-vue'
+import { ElMessage } from 'element-plus'
+
+const props = defineProps({
+  assistants: {
+    type: Array,
+    default: () => []
+  },
+  defaultAssistant: {
+    type: String,
+    default: ''
+  }
+})
+
+const builtInAssistants = [
+  {
+    key: 'general',
+    label: '寰呭姙鍔╃悊',
+    title: '寰呭姙鏅鸿兘鍔╃悊',
+    tooltip: '寰呭姙鍔╂墜',
+    icon: Cpu,
+    apiBase: '/xiaozhi',
+    storageKey: 'ai_chat_uuid',
+    placeholder: '璇疯緭鍏ユ偍鐨勯棶棰�... (Enter 鍙戦��, Shift+Enter 鎹㈣)',
+    welcomeMessage: '浣犲ソ',
+    description: '鎴戝彲浠ュ洖绛斾綘鐨勯棶棰橈紝涓轰綘鎻愪緵涓氬姟鏁版嵁瑙h淇℃伅銆佸鐞嗗缓璁拰杈呭姪鍐崇瓥鏀寔銆�',
+    allowFileUpload: true,
+    emptySessionText: '鏆傛棤鍘嗗彶浼氳瘽'
+  },
+  {
+    key: 'purchase',
+    label: '閲囪喘鍔╃悊',
+    title: '閲囪喘鏅鸿兘鍔╃悊',
+    tooltip: '閲囪喘鏅鸿兘鍔╃悊',
+    icon: ShoppingCart,
+    apiBase: '/purchase-ai',
+    storageKey: 'purchase_ai_chat_uuid',
+    placeholder: '璇疯緭鍏ラ噰璐棶棰�... (Enter 鍙戦��, Shift+Enter 鎹㈣)',
+    welcomeMessage: '浣犲ソ',
+    description: '鎴戝彲浠ュ崗鍔╀綘鍒嗘瀽閲囪喘璁㈠崟銆佸埌璐ц繘搴︺�佷緵搴斿晢琛ㄧ幇鍜屼粯娆炬儏鍐碉紝甯姪浣犲揩閫熷畾浣嶉噰璐紓甯搞��',
+    allowFileUpload: true,
+    allowMultipleFileUpload: true,
+    fileAnalyzeUrl: '/purchase-ai/analyze-files',
+    emptySessionText: '鏆傛棤閲囪喘浼氳瘽'
+  }
+]
+
+const assistants = computed(() => props.assistants?.length ? props.assistants : builtInAssistants)
+const selectedAssistantKey = ref(props.defaultAssistant || assistants.value[0]?.key || 'general')
+const currentAssistant = computed(() => assistants.value.find(item => item.key === selectedAssistantKey.value) || assistants.value[0] || builtInAssistants[0])
+const showAssistantSwitch = computed(() => assistants.value.length > 1)
+const assistantQuickPromptMap = {
+  general: [
+    '鎴戝綋鍓嶆湁鍝簺瀹℃壒寰呭姙闇�瑕佸鐞嗭紵',
+    '甯垜鍒楀嚭浠婂ぉ鏂板鐨勫鎵瑰緟鍔炪��',
+    '褰撳墠寰呮垜瀹℃壒鐨勫崟鎹紝鎸夋椂闂村�掑簭鍒楀嚭鏉ャ��',
+    '鎴戝彂璧风殑瀹℃壒閲岋紝鍝簺杩樺湪澶勭悊涓紵',
+    '鏌ヨ娴佺▼缂栧彿 XXX 鐨勫鎵硅鎯呫��',
+    '娴佺▼缂栧彿 XXX 鐜板湪鍗″湪鍝釜瀹℃壒鑺傜偣锛熷綋鍓嶅鎵逛汉鏄皝锛�',
+    '甯垜鏌ョ湅娴佺▼缂栧彿 XXX 鐨勫鎵规祦杞褰曘��',
+    '杩�7澶╂垜鐨勫鎵瑰緟鍔炵粺璁℃儏鍐垫�庝箞鏍凤紵',
+    '鏈湀鎴戠殑瀹℃壒涓紝閫氳繃銆侀┏鍥炪�佸鐞嗕腑鍚勬湁澶氬皯锛�',
+    '杩�30澶╁悇绫诲瀷瀹℃壒鏁伴噺鍒嗗竷鏄粈涔堬紵',
+    '甯垜瀹℃壒閫氳繃娴佺▼缂栧彿 XXX锛屽娉ㄢ�滃悓鎰忊�濄��',
+    '甯垜椹冲洖娴佺▼缂栧彿 XXX锛屽娉ㄢ�滆琛ュ厖璇存槑鈥濄��',
+    '鎾ら攢鎴戝垰鍒氬娴佺▼缂栧彿 XXX 鐨勫鎵规搷浣溿��',
+    '甯垜淇敼娴佺▼缂栧彿 XXX 鐨勫娉ㄤ负鈥滃凡琛ュ厖闄勪欢鈥濄��',
+    '鍒犻櫎鎴戝彂璧风殑娴佺▼缂栧彿 XXX銆�'
+  ],
+  purchase: [
+    '鏈湀閲囪喘閲戦鎺掑悕鍓嶅崄鐨勭墿鏂欐湁鍝簺锛�',
+    '鍝簺閲囪喘璁㈠崟杩樻湭鍏ュ簱锛�',
+    '鏈�杩�7澶╀緵搴斿晢鍒拌揣寮傚父鏈夊摢浜涳紵',
+    '甯垜缁熻寰呬粯娆鹃噰璐崟',
+    '鍒楀嚭鏈湀閲囪喘閫�璐ф儏鍐�'
+  ]
+}
+const quickPromptLimit = 3
+const quickPromptStart = ref(0)
+const quickPrompts = computed(() => {
+  const assistant = currentAssistant.value || {}
+  if (Array.isArray(assistant.quickPrompts) && assistant.quickPrompts.length) {
+    return assistant.quickPrompts
+  }
+  return assistantQuickPromptMap[assistant.key] || assistantQuickPromptMap.general
+})
+const displayedQuickPrompts = computed(() => {
+  const prompts = quickPrompts.value || []
+  if (prompts.length <= quickPromptLimit) return prompts
+
+  const result = []
+  for (let i = 0; i < quickPromptLimit; i++) {
+    result.push(prompts[(quickPromptStart.value + i) % prompts.length])
+  }
+  return result
+})
+const hasMessages = computed(() => messages.value.length > 0)
+
+const visible = ref(false)
+const windowWidth = ref(window.innerWidth)
+const drawerSize = computed(() => {
+  if (windowWidth.value < 768) return '100%'
+  if (windowWidth.value < 1200) return '50%'
+  return '50%'
+})
+const messageListRef = ref(null)
+const isSending = ref(false)
+const currentAbortController = ref(null)
+const inputMessage = ref('')
+const selectedFiles = ref([])
+const uploadFileList = ref([])
+const messages = ref([])
+const uuid = ref('')
+const chartInstances = ref({})
+const resizeHandlers = ref([])
+const outputState = ref({})
+const businessTypeLabelMap = {
+  purchase_ledger: '閲囪喘鍙拌处',
+  payment_registration: '浠樻鐧昏',
+  purchase_return_order: '閲囪喘閫�璐у崟',
+  unknown: '鏈煡閲囪喘涓氬姟'
+}
+const purchasePayloadFieldLabelMap = {
+  purchaseLedgers: '閲囪喘鍙拌处',
+  productData: '浜у搧鏄庣粏',
+  purchaseContractNumber: '閲囪喘鍚堝悓鍙�',
+  purchaseContractNo: '閲囪喘鍚堝悓鍙�',
+  purchaseOrderNumber: '閲囪喘鍚堝悓鍙�',
+  salesContractNo: '閿�鍞悎鍚屽彿',
+  salesContractNumber: '閿�鍞悎鍚屽彿',
+  salesOrderNumber: '閿�鍞悎鍚屽彿',
+  salesContractNoId: '閿�鍞悎鍚孖D',
+  approveUserIds: '瀹℃壒鐢ㄦ埛ID鍒楄〃',
+  entryDateStart: '褰曞叆寮�濮嬫棩鏈�',
+  entryDateEnd: '褰曞叆缁撴潫鏃ユ湡',
+  id: 'ID',
+  supplierId: '渚涘簲鍟咺D',
+  projectName: '椤圭洰鍚嶇О',
+  supplierName: '渚涘簲鍟嗗悕绉�',
+  isWhite: '鏄惁鐧藉悕鍗�',
+  recorderId: '褰曞叆浜篒D',
+  recorderName: '褰曞叆浜�',
+  contractDate: '鎵ц鏃ユ湡',
+  executionDate: '鎵ц鏃ユ湡',
+  inputPerson: '褰曞叆浜�',
+  inputDate: '褰曞叆鏃ユ湡',
+  entryDate: '褰曞叆鏃ユ湡',
+  paymentMethod: '浠樻鏂瑰紡',
+  auditors: '瀹℃壒浜�',
+  approverId: '瀹℃壒浜篒D',
+  approvalStatus: '瀹℃壒鐘舵��',
+  remark: '澶囨敞',
+  remarks: '澶囨敞',
+  attachmentMaterials: '闄勪欢鏉愭枡',
+  createdAt: '鍒涘缓鏃堕棿',
+  updatedAt: '鏇存柊鏃堕棿',
+  salesLedgerId: '閿�鍞彴璐D',
+  hasChildren: '鏄惁鏈夊瓙椤�',
+  Type: '绫诲瀷',
+  type: '绫诲瀷',
+  tempFileIds: '涓存椂鏂囦欢ID',
+  SalesLedgerFiles: '閿�鍞彴璐﹂檮浠�',
+  phoneNumber: '鑱旂郴鐢佃瘽',
+  businessPersonId: '涓氬姟鍛業D',
+  productId: '浜у搧ID',
+  productModelId: '浜у搧鍨嬪彿ID',
+  invoiceNumber: '鍙戠エ鍙风爜',
+  invoiceAmount: '鍙戠エ閲戦',
+  ticketRegistrationId: '寮�绁ㄧ櫥璁癐D',
+  contractAmount: '鍚堝悓閲戦',
+  receiptPaymentAmount: '宸叉敹浠樻閲戦',
+  unReceiptPaymentAmount: '鏈敹浠樻閲戦',
+  templateName: '妯℃澘鍚嶇О',
+  productCategory: '浜у搧绫诲埆',
+  specificationModel: '瑙勬牸鍨嬪彿',
+  unit: '鍗曚綅',
+  taxRate: '绋庣巼',
+  taxInclusiveUnitPrice: '鍚◣鍗曚环',
+  priceWithTax: '鍚◣鍗曚环',
+  quantity: '鏁伴噺',
+  taxInclusiveTotalPrice: '鍚◣鎬讳环',
+  totalPriceWithTax: '鍚◣鎬讳环',
+  invoiceType: '鍙戠エ绫诲瀷',
+  inventoryWarningQuantity: '搴撳瓨棰勮鏁伴噺',
+  isInspected: '鏄惁璐ㄦ',
+  isChecked: '鏄惁璐ㄦ'
+}
+const purchasePayloadFieldKeyMap = {
+  閲囪喘鍙拌处: 'purchaseLedgers',
+  浜у搧鏄庣粏: 'productData',
+  閲囪喘鍚堝悓鍙�: 'purchaseContractNumber',
+  閲囪喘鍗曞彿: 'purchaseContractNumber',
+  閲囪喘璁㈠崟鍙�: 'purchaseContractNumber',
+  閿�鍞悎鍚屽彿: 'salesContractNo',
+  閿�鍞崟鍙�: 'salesContractNo',
+  閿�鍞鍗曞彿: 'salesContractNo',
+  閿�鍞悎鍚孖D: 'salesContractNoId',
+  瀹℃壒鐢ㄦ埛ID鍒楄〃: 'approveUserIds',
+  褰曞叆寮�濮嬫棩鏈�: 'entryDateStart',
+  褰曞叆缁撴潫鏃ユ湡: 'entryDateEnd',
+  ID: 'id',
+  椤圭洰鍚嶇О: 'projectName',
+  渚涘簲鍟咺D: 'supplierId',
+  渚涘簲鍟嗗悕绉�: 'supplierName',
+  鏄惁鐧藉悕鍗�: 'isWhite',
+  褰曞叆浜篒D: 'recorderId',
+  褰曞叆浜�: 'recorderName',
+  绛捐鏃ユ湡: 'executionDate',
+  鎵ц鏃ユ湡: 'executionDate',
+  褰曞叆鏃ユ湡: 'entryDate',
+  浠樻鏂瑰紡: 'paymentMethod',
+  瀹℃牳浜�: 'approverId',
+  瀹℃壒浜�: 'approverId',
+  瀹℃壒浜篒D: 'approverId',
+  瀹℃壒鐘舵��: 'approvalStatus',
+  澶囨敞: 'remarks',
+  闄勪欢鏉愭枡: 'attachmentMaterials',
+  鍒涘缓鏃堕棿: 'createdAt',
+  鏇存柊鏃堕棿: 'updatedAt',
+  閿�鍞彴璐D: 'salesLedgerId',
+  鏄惁鏈夊瓙椤�: 'hasChildren',
+  绫诲瀷: 'type',
+  涓存椂鏂囦欢ID: 'tempFileIds',
+  閿�鍞彴璐﹂檮浠�: 'SalesLedgerFiles',
+  鑱旂郴鐢佃瘽: 'phoneNumber',
+  涓氬姟鍛業D: 'businessPersonId',
+  浜у搧ID: 'productId',
+  浜у搧鍨嬪彿ID: 'productModelId',
+  鍙戠エ鍙风爜: 'invoiceNumber',
+  鍙戠エ閲戦: 'invoiceAmount',
+  寮�绁ㄧ櫥璁癐D: 'ticketRegistrationId',
+  鍚堝悓閲戦: 'contractAmount',
+  宸叉敹浠樻閲戦: 'receiptPaymentAmount',
+  鏈敹浠樻閲戦: 'unReceiptPaymentAmount',
+  妯℃澘鍚嶇О: 'templateName',
+  浜у搧绫诲埆: 'productCategory',
+  浜у搧鍚嶇О: 'productCategory',
+  瑙勬牸鍨嬪彿: 'specificationModel',
+  鍗曚綅: 'unit',
+  绋庣巼: 'taxRate',
+  鍚◣鍗曚环: 'taxInclusiveUnitPrice',
+  鏁伴噺: 'quantity',
+  鍚◣鎬讳环: 'taxInclusiveTotalPrice',
+  鍙戠エ绫诲瀷: 'invoiceType',
+  搴撳瓨棰勮鏁伴噺: 'inventoryWarningQuantity',
+  鏄惁璐ㄦ: 'isInspected',
+  purchaseLedgers: 'purchaseLedgers',
+  productData: 'productData',
+  purchaseContractNumber: 'purchaseContractNumber',
+  purchaseContractNo: 'purchaseContractNumber',
+  purchaseOrderNumber: 'purchaseContractNumber',
+  salesContractNo: 'salesContractNo',
+  salesContractNumber: 'salesContractNo',
+  salesOrderNumber: 'salesContractNo',
+  contractDate: 'executionDate',
+  inputPerson: 'recorderName',
+  inputDate: 'entryDate',
+  auditors: 'approverId',
+  remark: 'remarks',
+  productCategory: 'productCategory',
+  productName: 'productCategory',
+  specificationModel: 'specificationModel',
+  unit: 'unit',
+  taxRate: 'taxRate',
+  priceWithTax: 'taxInclusiveUnitPrice',
+  taxInclusiveUnitPrice: 'taxInclusiveUnitPrice',
+  quantity: 'quantity',
+  totalPriceWithTax: 'taxInclusiveTotalPrice',
+  taxInclusiveTotalPrice: 'taxInclusiveTotalPrice',
+  invoiceType: 'invoiceType',
+  inventoryWarningQuantity: 'inventoryWarningQuantity',
+  isInspected: 'isInspected',
+  isChecked: 'isInspected'
+}
+
+// 鍘嗗彶浼氳瘽鐩稿叧
+const purchaseValueTypeOptions = [
+  { label: '鏂囨湰', value: 'string' },
+  { label: '鏁板瓧', value: 'number' },
+  { label: '甯冨皵', value: 'boolean' },
+  { label: '绌哄��', value: 'null' },
+  { label: '瀵硅薄', value: 'object' },
+  { label: '鏁扮粍', value: 'array' }
+]
+const purchaseContainerValueTypes = new Set(['object', 'array'])
+const purchaseHiddenFieldKeySet = new Set(['templatename', 'approvalstatus', 'phonenumber', 'type'])
+const purchaseHiddenKeyWordList = [
+  'attachment',
+  'file',
+  'invoice',
+  'ticketregistration',
+  'receiptpayment',
+  'payment'
+]
+const purchaseHiddenChineseKeywordList = ['闄勪欢', '寮�绁�', '鏉ョエ', '鍥炴', '浠樻']
+let purchasePayloadTreeNodeSeed = 0
+
+const shouldHidePurchaseField = (fieldKey = '') => {
+  const rawKey = String(fieldKey || '')
+  if (!rawKey) return false
+  const normalizedFieldKey = purchasePayloadFieldKeyMap[rawKey] || rawKey
+  const lowerKey = String(normalizedFieldKey).toLowerCase()
+
+  if (lowerKey.endsWith('id') || lowerKey.endsWith('ids')) return true
+  if (purchaseHiddenFieldKeySet.has(lowerKey)) return true
+  if (purchaseHiddenKeyWordList.some(keyword => lowerKey.includes(keyword))) return true
+  if (purchaseHiddenChineseKeywordList.some(keyword => rawKey.includes(keyword))) return true
+  return false
+}
+
+const showHistory = ref(false)
+const sessions = ref([])
+const loadingSessions = ref(false)
+
+const abortCurrentRequest = () => {
+  if (!currentAbortController.value) return
+
+  currentAbortController.value.abort()
+  currentAbortController.value = null
+  isSending.value = false
+
+  const lastMsg = messages.value[messages.value.length - 1]
+  if (lastMsg && !lastMsg.isUser) {
+    lastMsg.isTyping = false
+  }
+}
+
+const toggleHistory = () => {
+  showHistory.value = !showHistory.value
+  if (showHistory.value) {
+    loadSessions()
+  }
+}
+
+const handleToggleHistory = () => {
+  if (isSending.value) {
+    abortCurrentRequest()
+  }
+  toggleHistory()
+}
+
+const loadSessions = async () => {
+  loadingSessions.value = true
+  try {
+    const res = await request.get(`${currentAssistant.value.apiBase}/history/sessions`)
+    if (res.code === 200) {
+      sessions.value = res.data || []
+    }
+  } catch (err) {
+    console.error('Failed to load sessions', err)
+  } finally {
+    loadingSessions.value = false
+  }
+}
+
+const selectSession = async (session) => {
+  showHistory.value = false
+  uuid.value = session.memoryId
+  localStorage.setItem(currentAssistant.value.storageKey, uuid.value)
+
+  // 鍔犺浇浼氳瘽娑堟伅
+  try {
+    const res = await request.get(`${currentAssistant.value.apiBase}/history/messages/${uuid.value}`)
+    if (res.code === 200) {
+      disposeCharts()
+      messages.value = []
+      const historyMsgs = res.data || []
+
+      // 閲嶆柊鏋勯�犳秷鎭垪琛ㄥ苟瑙f瀽
+      historyMsgs.forEach((msg, idx) => {
+        const isUser = msg.role === 'user'
+        const botMsgIndex = messages.value.length
+
+        const messageObj = {
+          isUser,
+          content: msg.content,
+          htmlContent: '',
+          isTyping: false,
+          chartOptions: null,
+          chartRenderReady: false,
+          type: '',
+          tableData: null,
+          payloadTreeData: null,
+          payloadHiddenData: null
+        }
+
+        messages.value.push(messageObj)
+
+        if (!isUser) {
+          outputState.value[botMsgIndex] = {
+            isPaused: false,
+            jsonBlockStartPos: -1,
+            jsBlockStartPos: -1,
+            blockEndPos: -1,
+            hasRenderedChart: false
+          }
+
+          // 瑙f瀽鍘嗗彶娑堟伅涓殑 JSON
+          const extracted = extractEmbeddedSuccessJson(msg.content)
+          if (extracted) {
+            applyStructuredMessageData(messageObj, extracted.data, botMsgIndex)
+          }
+
+          updateOutputState(msg.content, botMsgIndex)
+          messageObj.htmlContent = convertStreamOutput(msg.content, botMsgIndex)
+        } else {
+          messageObj.htmlContent = convertTextToHtml(msg.content)
+        }
+      })
+      scrollToBottom()
+    }
+  } catch (err) {
+    console.error('Failed to load messages', err)
+  }
+}
+
+const handleDeleteSession = async (memoryId) => {
+  try {
+    const res = await request.delete(`${currentAssistant.value.apiBase}/history/${memoryId}`)
+    if (res.code === 200) {
+      loadSessions()
+      if (uuid.value === memoryId) {
+        newChat()
+      }
+    }
+  } catch (err) {
+    console.error('Failed to delete session', err)
+  }
+}
+
+const columnLabelMap = {
+  approveId: '瀹℃壒缂栧彿',
+  approveType: '瀹℃壒绫诲瀷',
+  approveUserName: '瀹℃壒浜�',
+  approveUserCurrentName: '褰撳墠澶勭悊浜�',
+  approveReason: '瀹℃壒鍘熷洜',
+  approveStatus: '瀹℃壒鐘舵��',
+  createTime: '鍒涘缓鏃堕棿'
+}
+
+onMounted(() => {
+  initUUID()
+  // 鍒濆娆㈣繋
+  if (messages.value.length === 0) {
+    hello()
+  }
+  window.addEventListener('resize', handleWindowResize)
+})
+
+onUnmounted(() => {
+  disposeCharts()
+  window.removeEventListener('resize', handleWindowResize)
+})
+
+watch(selectedAssistantKey, (nextKey, prevKey) => {
+  if (!prevKey || nextKey === prevKey) return
+
+  abortCurrentRequest()
+  disposeCharts()
+  messages.value = []
+  outputState.value = {}
+  sessions.value = []
+  showHistory.value = false
+  selectedFiles.value = []
+  uploadFileList.value = []
+  inputMessage.value = ''
+  quickPromptStart.value = 0
+  initUUID()
+  hello()
+})
+
+const handleWindowResize = () => {
+  windowWidth.value = window.innerWidth
+}
+
+const toggleSidebar = () => {
+  visible.value = !visible.value
+  if (visible.value) {
+    scrollToBottom()
+  }
+}
+
+const handleClose = () => {
+  visible.value = false
+}
+
+const handleManualClose = () => {
+  if (isSending.value) {
+    abortCurrentRequest()
+  }
+  handleClose()
+}
+
+const initUUID = () => {
+  let storedUUID = localStorage.getItem(currentAssistant.value.storageKey)
+  if (!storedUUID) {
+    storedUUID = Math.random().toString(36).substring(2, 10) + Date.now().toString(36).substring(4)
+    localStorage.setItem(currentAssistant.value.storageKey, storedUUID)
+  }
+  uuid.value = storedUUID
+}
+
+const hello = () => {
+  sendRequest(currentAssistant.value.welcomeMessage || '浣犲ソ')
+}
+
+const newChat = () => {
+  disposeCharts()
+  messages.value = []
+  outputState.value = {}
+  sessions.value = []
+  showHistory.value = false
+  selectedFiles.value = []
+  uploadFileList.value = []
+  quickPromptStart.value = 0
+  localStorage.removeItem(currentAssistant.value.storageKey)
+  initUUID()
+  hello()
+}
+
+const handleNewChat = () => {
+  if (isSending.value) {
+    abortCurrentRequest()
+  }
+  newChat()
+}
+
+const sendQuickPrompt = (prompt) => {
+  if (!prompt || isSending.value) return
+  inputMessage.value = prompt
+  sendMessage()
+}
+
+const refreshQuickPrompts = () => {
+  const prompts = quickPrompts.value || []
+  if (prompts.length <= quickPromptLimit) return
+  quickPromptStart.value = (quickPromptStart.value + quickPromptLimit) % prompts.length
+}
+
+const disposeCharts = () => {
+  Object.values(chartInstances.value).forEach(chart => chart.dispose())
+  resizeHandlers.value.forEach(handler => window.removeEventListener('resize', handler))
+  chartInstances.value = {}
+  resizeHandlers.value = []
+}
+
+const extractEmbeddedSuccessJson = (text) => {
+  if (!text || typeof text !== 'string') return null
+
+  const startMatch = text.match(/\{\s*"success"\s*:/)
+  if (!startMatch) return null
+  const startIdx = startMatch.index ?? -1
+  if (startIdx < 0) return null
+
+  for (let i = startIdx; i < text.length; i++) {
+    if (text[i] !== '{') continue
+
+    let depth = 0
+    let inString = false
+    let escaped = false
+
+    for (let j = i; j < text.length; j++) {
+      const char = text[j]
+
+      if (inString) {
+        if (escaped) {
+          escaped = false
+        } else if (char === '\\') {
+          escaped = true
+        } else if (char === '"') {
+          inString = false
+        }
+        continue
+      }
+
+      if (char === '"') {
+        inString = true
+        continue
+      }
+
+      if (char === '{') {
+        depth++
+      } else if (char === '}') {
+        depth--
+        if (depth === 0) {
+          const candidate = text.slice(i, j + 1)
+          try {
+            const parsed = JSON.parse(candidate)
+            if (parsed?.success === true) {
+              return {
+                data: parsed,
+                startIdx: i,
+                endIdx: j + 1
+              }
+            }
+          } catch (err) {
+            continue
+          }
+        }
+      }
+    }
+  }
+
+  return null
+}
+
+const applyStructuredMessageData = (messageObj, parsedData, msgIndex, shouldRenderCharts = true) => {
+  if (!messageObj || !parsedData?.success) return
+
+  messageObj.type = parsedData.type || ''
+
+  if (messageObj.type === 'todo_list' && parsedData.data) {
+    messageObj.tableData = parsedData.data
+  }
+
+  if (parsedData.action === 'confirm_required' && parsedData.businessType) {
+    messageObj.type = 'purchase_analysis_confirm'
+    messageObj.purchaseAnalysisData = parsedData
+    if (!Array.isArray(messageObj.payloadTreeData) || !messageObj.payloadTreeData.length) {
+      initializePurchasePayloadTree(messageObj, parsedData.payload || {})
+    }
+    if (!messageObj.payloadText) {
+      const payloadFromTree = buildPurchasePayloadFromNodes(messageObj.payloadTreeData, 'object')
+      const payloadWithHidden = mergePurchasePayloadWithHidden(payloadFromTree, messageObj.payloadHiddenData)
+      messageObj.payloadText = JSON.stringify(localizePurchasePayload(payloadWithHidden), null, 2)
+    }
+    messageObj.confirmResult = ''
+    messageObj.confirmed = false
+    messageObj.confirming = false
+  }
+
+  const chartOptions = getStructuredChartOptions(parsedData)
+  if (chartOptions && Object.keys(chartOptions).length > 0) {
+    messageObj.chartOptions = chartOptions
+    messageObj.chartRenderReady = true
+
+    if (shouldRenderCharts) {
+      renderCharts(msgIndex, messageObj.chartOptions)
+      if (outputState.value[msgIndex]) {
+        outputState.value[msgIndex].hasRenderedChart = true
+      }
+    }
+  }
+}
+
+const getStructuredChartOptions = (parsedData) => {
+  if (!parsedData?.success) return null
+
+  if (parsedData.charts && Object.keys(parsedData.charts).length > 0) {
+    return parsedData.charts
+  }
+
+  if (parsedData.type === 'purchase_material_rank') {
+    return buildPurchaseMaterialRankCharts(parsedData)
+  }
+
+  return null
+}
+
+const buildPurchaseMaterialRankCharts = (parsedData) => {
+  const items = Array.isArray(parsedData?.data?.items) ? parsedData.data.items : []
+  if (!items.length) return null
+
+  const names = items.map(item => item.productCategory || '-')
+  const amounts = items.map(item => Number(item.amount) || 0)
+
+  return {
+    purchaseMaterialAmountRank: {
+      title: {
+        text: '\u91c7\u8d2d\u7269\u6599\u91d1\u989d\u6392\u884c',
+        left: 'center',
+        textStyle: {
+          fontSize: 14,
+          fontWeight: 600,
+          color: '#1a1a2e'
+        }
+      },
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: {
+          type: 'shadow'
+        },
+        formatter(params) {
+          const dataIndex = params?.[0]?.dataIndex ?? 0
+          const item = items[dataIndex] || {}
+          const amount = Number(item.amount) || 0
+          const quantity = Number(item.quantity) || 0
+          return [
+            `${item.productCategory || '-'}`,
+            `${params?.[0]?.marker || ''} \u91d1\u989d\uff1a${formatCurrency(amount)}`,
+            `\u89c4\u683c\u578b\u53f7\uff1a${item.specificationModel || '-'}`,
+            `\u6570\u91cf\uff1a${quantity}${item.unit || ''}`
+          ].join('<br/>')
+        }
+      },
+      grid: {
+        left: '3%',
+        right: '4%',
+        bottom: names.some(name => String(name).length > 6) ? 72 : 48,
+        top: 48,
+        containLabel: true
+      },
+      xAxis: {
+        type: 'category',
+        data: names,
+        axisLabel: {
+          interval: 0,
+          rotate: names.some(name => String(name).length > 6) ? 28 : 0,
+          color: '#4b5563'
+        }
+      },
+      yAxis: {
+        type: 'value',
+        name: '\u91d1\u989d(\u5143)',
+        axisLabel: {
+          color: '#4b5563',
+          formatter: value => formatCompactNumber(value)
+        }
+      },
+      series: [{
+        name: '\u91c7\u8d2d\u91d1\u989d',
+        type: 'bar',
+        barMaxWidth: 36,
+        data: amounts,
+        itemStyle: {
+          borderRadius: [6, 6, 0, 0],
+          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            { offset: 0, color: '#2f7cf6' },
+            { offset: 1, color: '#55d7ff' }
+          ])
+        },
+        label: {
+          show: true,
+          position: 'top',
+          color: '#1f2937',
+          formatter: params => formatCompactNumber(params.value)
+        }
+      }]
+    }
+  }
+}
+
+const formatCurrency = (value) => {
+  const amount = Number(value) || 0
+  return `\u00a5${amount.toLocaleString('zh-CN', { maximumFractionDigits: 2 })}`
+}
+
+const formatCompactNumber = (value) => {
+  const amount = Number(value) || 0
+  if (Math.abs(amount) >= 10000) {
+    return `${(amount / 10000).toFixed(2).replace(/\.?0+$/, '')}\u4e07`
+  }
+  return amount.toLocaleString('zh-CN', { maximumFractionDigits: 2 })
+}
+
+const formatPercent = (value) => {
+  const number = Number(value)
+  if (Number.isNaN(number)) return '-'
+  return `${Math.round(number * 100)}%`
+}
+
+const formatPreviewItem = (item) => {
+  if (item === null || item === undefined) return '-'
+  if (typeof item === 'string') return item
+  try {
+    return JSON.stringify(item)
+  } catch (err) {
+    return String(item)
+  }
+}
+
+const hasMeaningfulPayloadValue = (value) => {
+  if (value === null || value === undefined) return false
+  if (typeof value === 'string') return value.trim() !== ''
+  if (Array.isArray(value)) return value.some(item => hasMeaningfulPayloadValue(item))
+  if (typeof value === 'object') return Object.values(value).some(item => hasMeaningfulPayloadValue(item))
+  return true
+}
+
+const mergeMappedPayloadValue = (existingValue, incomingValue) => {
+  if (existingValue === undefined) return incomingValue
+
+  const existingHasValue = hasMeaningfulPayloadValue(existingValue)
+  const incomingHasValue = hasMeaningfulPayloadValue(incomingValue)
+
+  if (existingHasValue && !incomingHasValue) return existingValue
+  if (!existingHasValue && incomingHasValue) return incomingValue
+
+  if (
+    existingValue &&
+    incomingValue &&
+    typeof existingValue === 'object' &&
+    typeof incomingValue === 'object' &&
+    !Array.isArray(existingValue) &&
+    !Array.isArray(incomingValue)
+  ) {
+    return { ...existingValue, ...incomingValue }
+  }
+
+  return incomingValue
+}
+
+const mapPayloadKeys = (value, keyMap) => {
+  if (Array.isArray(value)) {
+    return value.map(item => mapPayloadKeys(item, keyMap))
+  }
+  if (value && typeof value === 'object') {
+    return Object.entries(value).reduce((result, [key, item]) => {
+      const mappedKey = keyMap[key] || key
+      const mappedValue = mapPayloadKeys(item, keyMap)
+      result[mappedKey] = mergeMappedPayloadValue(result[mappedKey], mappedValue)
+      return result
+    }, {})
+  }
+  return value
+}
+
+const clonePurchasePayloadValue = (value) => {
+  if (Array.isArray(value)) {
+    return value.map(item => clonePurchasePayloadValue(item))
+  }
+  if (value && typeof value === 'object') {
+    return Object.entries(value).reduce((result, [key, item]) => {
+      result[key] = clonePurchasePayloadValue(item)
+      return result
+    }, {})
+  }
+  return value
+}
+
+const splitPurchasePayloadByVisibility = (value) => {
+  if (Array.isArray(value)) {
+    const splitItems = value.map(item => splitPurchasePayloadByVisibility(item))
+    const visible = splitItems.map(item => item.visible)
+    const hidden = splitItems.map(item => item.hidden)
+    return { visible, hidden }
+  }
+
+  if (value && typeof value === 'object') {
+    const visible = {}
+    const hidden = {}
+
+    Object.entries(value).forEach(([key, item]) => {
+      if (shouldHidePurchaseField(key)) {
+        hidden[key] = clonePurchasePayloadValue(item)
+        return
+      }
+      const child = splitPurchasePayloadByVisibility(item)
+      visible[key] = child.visible
+      if (hasMeaningfulPayloadValue(child.hidden)) {
+        hidden[key] = child.hidden
+      }
+    })
+
+    return { visible, hidden }
+  }
+
+  return { visible: value, hidden: undefined }
+}
+
+const mergePurchasePayloadWithHidden = (visibleValue, hiddenValue) => {
+  if (hiddenValue === undefined || hiddenValue === null) return visibleValue
+  if (visibleValue === undefined || visibleValue === null) return clonePurchasePayloadValue(hiddenValue)
+
+  if (Array.isArray(visibleValue) && Array.isArray(hiddenValue)) {
+    const maxLength = Math.max(visibleValue.length, hiddenValue.length)
+    const merged = []
+    for (let i = 0; i < maxLength; i++) {
+      merged[i] = mergePurchasePayloadWithHidden(visibleValue[i], hiddenValue[i])
+    }
+    return merged
+  }
+
+  if (
+    visibleValue &&
+    hiddenValue &&
+    typeof visibleValue === 'object' &&
+    typeof hiddenValue === 'object' &&
+    !Array.isArray(visibleValue) &&
+    !Array.isArray(hiddenValue)
+  ) {
+    const merged = { ...clonePurchasePayloadValue(hiddenValue) }
+    Object.entries(visibleValue).forEach(([key, item]) => {
+      merged[key] = mergePurchasePayloadWithHidden(item, merged[key])
+    })
+    return merged
+  }
+
+  return visibleValue
+}
+
+const localizePurchasePayload = (payload) => mapPayloadKeys(payload, purchasePayloadFieldLabelMap)
+
+const normalizePurchasePayload = (payload) => mapPayloadKeys(payload, purchasePayloadFieldKeyMap)
+
+const createPurchasePayloadNodeId = () => `purchase-node-${Date.now()}-${purchasePayloadTreeNodeSeed++}`
+
+const detectPurchaseValueType = (value) => {
+  if (Array.isArray(value)) return 'array'
+  if (value === null) return 'null'
+  const valueType = typeof value
+  if (valueType === 'number') return 'number'
+  if (valueType === 'boolean') return 'boolean'
+  if (valueType === 'object') return 'object'
+  return 'string'
+}
+
+const normalizePurchaseNodeValueForEdit = (value, valueType) => {
+  if (valueType === 'number') return value === null || value === undefined ? '' : String(value)
+  if (valueType === 'boolean') return Boolean(value)
+  if (valueType === 'null') return ''
+  return value === null || value === undefined ? '' : String(value)
+}
+
+const createPurchaseTreeNode = ({
+  key = '',
+  parentType = 'object',
+  keyEditable = false,
+  valueType = 'string',
+  value = '',
+  children = []
+} = {}) => ({
+  id: createPurchasePayloadNodeId(),
+  key,
+  parentType,
+  keyEditable,
+  valueType,
+  value,
+  children
+})
+
+const reorderPurchaseObjectEntries = (value) => {
+  const entries = Object.entries(value || {})
+  const productDataIndex = entries.findIndex(([key]) => key === 'productData')
+  if (productDataIndex <= -1 || productDataIndex === entries.length - 1) {
+    return entries
+  }
+  const [productDataEntry] = entries.splice(productDataIndex, 1)
+  entries.push(productDataEntry)
+  return entries
+}
+
+const buildPurchasePayloadTreeNodes = (value, parentType = 'object') => {
+  if (Array.isArray(value)) {
+    return value.map(item => {
+      const itemType = detectPurchaseValueType(item)
+      const node = createPurchaseTreeNode({
+        key: '',
+        parentType: 'array',
+        keyEditable: false,
+        valueType: itemType,
+        value: normalizePurchaseNodeValueForEdit(item, itemType)
+      })
+      if (purchaseContainerValueTypes.has(itemType)) {
+        node.children = buildPurchasePayloadTreeNodes(item, itemType)
+      }
+      return node
+    })
+  }
+
+  if (value && typeof value === 'object') {
+    return reorderPurchaseObjectEntries(value).map(([key, item]) => {
+      const itemType = detectPurchaseValueType(item)
+      const node = createPurchaseTreeNode({
+        key,
+        parentType,
+        keyEditable: false,
+        valueType: itemType,
+        value: normalizePurchaseNodeValueForEdit(item, itemType)
+      })
+      if (purchaseContainerValueTypes.has(itemType)) {
+        node.children = buildPurchasePayloadTreeNodes(item, itemType)
+      }
+      return node
+    })
+  }
+
+  return []
+}
+
+const initializePurchasePayloadTree = (messageObj, payload = {}) => {
+  const sourcePayload = payload && typeof payload === 'object' && !Array.isArray(payload)
+    ? payload
+    : {}
+  const { visible, hidden } = splitPurchasePayloadByVisibility(sourcePayload)
+  const visiblePayload = visible && typeof visible === 'object' && !Array.isArray(visible) ? visible : {}
+  messageObj.payloadTreeData = buildPurchasePayloadTreeNodes(visiblePayload, 'object')
+  messageObj.payloadHiddenData = hidden && typeof hidden === 'object' ? hidden : {}
+}
+
+const getPurchaseFieldLabel = (fieldKey) => purchasePayloadFieldLabelMap[fieldKey] || fieldKey || '瀛楁'
+
+const createPurchaseDefaultNode = (parentType = 'object') => createPurchaseTreeNode({
+  key: parentType === 'object' ? 'newField' : '',
+  parentType,
+  keyEditable: parentType === 'object',
+  valueType: 'string',
+  value: ''
+})
+
+const getPurchaseScalarNodeValue = (node) => {
+  if (node.valueType === 'null') return null
+  if (node.valueType === 'boolean') return Boolean(node.value)
+  if (node.valueType === 'number') {
+    const text = String(node.value ?? '').trim()
+    if (!text) return null
+    const numberValue = Number(text)
+    return Number.isFinite(numberValue) ? numberValue : text
+  }
+  return node.value === null || node.value === undefined ? '' : String(node.value)
+}
+
+const buildPurchasePayloadFromNodes = (nodes, parentType = 'object') => {
+  if (!Array.isArray(nodes)) {
+    return parentType === 'array' ? [] : {}
+  }
+
+  if (parentType === 'array') {
+    return nodes.map(node => {
+      if (purchaseContainerValueTypes.has(node.valueType)) {
+        return buildPurchasePayloadFromNodes(node.children, node.valueType)
+      }
+      return getPurchaseScalarNodeValue(node)
+    })
+  }
+
+  return nodes.reduce((result, node, index) => {
+    const rawKey = String(node.key ?? '').trim()
+    const key = rawKey || `field_${index + 1}`
+    if (purchaseContainerValueTypes.has(node.valueType)) {
+      result[key] = buildPurchasePayloadFromNodes(node.children, node.valueType)
+    } else {
+      result[key] = getPurchaseScalarNodeValue(node)
+    }
+    return result
+  }, {})
+}
+
+const findPurchaseNodeLocation = (nodes, targetId, parentNode = null) => {
+  if (!Array.isArray(nodes)) return null
+  for (let index = 0; index < nodes.length; index++) {
+    const node = nodes[index]
+    if (node.id === targetId) {
+      return {
+        siblings: nodes,
+        index,
+        node,
+        parentNode
+      }
+    }
+    const next = findPurchaseNodeLocation(node.children, targetId, node)
+    if (next) return next
+  }
+  return null
+}
+
+const getPurchaseArrayItemLabel = (row, message) => {
+  const location = findPurchaseNodeLocation(message?.payloadTreeData, row.id)
+  return `[${(location?.index ?? 0) + 1}]`
+}
+
+const handlePurchaseNodeTypeChange = (message, row) => {
+  if (!message || !row) return
+  if (purchaseContainerValueTypes.has(row.valueType)) {
+    row.children = []
+    row.value = ''
+    return
+  }
+  row.children = []
+  if (row.valueType === 'boolean') {
+    row.value = false
+  } else if (row.valueType === 'null') {
+    row.value = ''
+  } else {
+    row.value = ''
+  }
+}
+
+const addPurchaseRootField = (message) => {
+  if (!message) return
+  if (!Array.isArray(message.payloadTreeData)) {
+    message.payloadTreeData = []
+  }
+  message.payloadTreeData.push(createPurchaseDefaultNode('object'))
+}
+
+const addPurchaseChildNode = (message, row) => {
+  if (!message || !row || !purchaseContainerValueTypes.has(row.valueType)) return
+  if (!Array.isArray(row.children)) {
+    row.children = []
+  }
+  row.children.push(createPurchaseDefaultNode(row.valueType))
+}
+
+const addPurchaseSiblingNode = (message, row) => {
+  if (!message || !row) return
+  const location = findPurchaseNodeLocation(message.payloadTreeData, row.id)
+  if (!location || location.node.parentType !== 'array') return
+  location.siblings.splice(location.index + 1, 0, createPurchaseDefaultNode('array'))
+}
+
+const removePurchaseNode = (message, row) => {
+  if (!message || !row) return
+  const location = findPurchaseNodeLocation(message.payloadTreeData, row.id)
+  if (!location) return
+  location.siblings.splice(location.index, 1)
+}
+
+const hasPurchaseNodeValidationError = (nodes, parentType = 'object') => {
+  if (!Array.isArray(nodes)) return false
+  return nodes.some((node) => {
+    if (parentType === 'object' && !String(node.key ?? '').trim()) {
+      return true
+    }
+    if (node.valueType === 'number') {
+      const text = String(node.value ?? '').trim()
+      if (text && !Number.isFinite(Number(text))) {
+        return true
+      }
+    }
+    if (purchaseContainerValueTypes.has(node.valueType)) {
+      return hasPurchaseNodeValidationError(node.children, node.valueType)
+    }
+    return false
+  })
+}
+
+const purchaseDateFieldKeys = new Set([
+  'entryDateStart',
+  'entryDateEnd',
+  'entryDate',
+  'executionDate',
+  'contractDate',
+  'inputDate',
+  'createdAt',
+  'updatedAt'
+])
+
+const purchaseLedgerAllowedFieldKeys = new Set([
+  'entryDateStart',
+  'entryDateEnd',
+  'id',
+  'purchaseContractNumber',
+  'supplierId',
+  'supplierName',
+  'isWhite',
+  'recorderId',
+  'recorderName',
+  'salesContractNo',
+  'salesContractNoId',
+  'projectName',
+  'entryDate',
+  'executionDate',
+  'remarks',
+  'attachmentMaterials',
+  'createdAt',
+  'updatedAt',
+  'salesLedgerId',
+  'hasChildren',
+  'Type',
+  'productData',
+  'tempFileIds',
+  'SalesLedgerFiles',
+  'phoneNumber',
+  'businessPersonId',
+  'productId',
+  'productModelId',
+  'invoiceNumber',
+  'invoiceAmount',
+  'ticketRegistrationId',
+  'contractAmount',
+  'receiptPaymentAmount',
+  'unReceiptPaymentAmount',
+  'type',
+  'paymentMethod',
+  'approvalStatus',
+  'templateName'
+])
+
+const purchaseApprovalFieldKeys = new Set([
+  'approveUserIds',
+  'approverId',
+  'auditors',
+  '瀹℃牳浜�',
+  '瀹℃壒浜�',
+  '瀹℃壒浜篒D',
+  '瀹℃壒鐢ㄦ埛ID鍒楄〃'
+])
+
+const normalizePurchaseProductRecord = (record) => {
+  if (!record || typeof record !== 'object' || Array.isArray(record)) return record
+  return mapPayloadKeys(record, purchasePayloadFieldKeyMap)
+}
+
+const getPurchaseProductMatchKey = (record) => {
+  if (!record || typeof record !== 'object') return ''
+  return record.purchaseContractNumber ||
+    record.purchaseContractNo ||
+    record.salesContractNo ||
+    record.salesContractNumber ||
+    ''
+}
+
+const mergeLegacyProductDataIntoLedgers = (payload) => {
+  if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return payload
+  if (!Array.isArray(payload.purchaseLedgers) || !Array.isArray(payload.productData) || !payload.productData.length) {
+    return payload
+  }
+
+  const ledgers = payload.purchaseLedgers.map(ledger => ({
+    ...ledger,
+    productData: Array.isArray(ledger.productData)
+      ? ledger.productData.map(normalizePurchaseProductRecord)
+      : []
+  }))
+  const unmatchedProducts = []
+
+  payload.productData.map(normalizePurchaseProductRecord).forEach(product => {
+    const productMatchKey = getPurchaseProductMatchKey(product)
+    const matchedLedger = ledgers.find(ledger => {
+      const ledgerKeys = [
+        ledger.purchaseContractNumber,
+        ledger.purchaseContractNo,
+        ledger.salesContractNo,
+        ledger.salesContractNumber
+      ].filter(Boolean)
+      return productMatchKey && ledgerKeys.includes(productMatchKey)
+    })
+
+    if (matchedLedger) {
+      matchedLedger.productData.push(product)
+    } else if (ledgers.length === 1) {
+      ledgers[0].productData.push(product)
+    } else {
+      unmatchedProducts.push(product)
+    }
+  })
+
+  const nextPayload = {
+    ...payload,
+    purchaseLedgers: ledgers
+  }
+
+  if (unmatchedProducts.length) {
+    nextPayload.productData = unmatchedProducts
+  } else {
+    delete nextPayload.productData
+  }
+
+  return nextPayload
+}
+
+const filterPurchaseLedgerRecord = (record) => {
+  if (!record || typeof record !== 'object' || Array.isArray(record)) return record
+  const normalizedRecord = {
+    ...record,
+    productData: Array.isArray(record.productData)
+      ? record.productData.map(normalizePurchaseProductRecord)
+      : record.productData
+  }
+  return Object.entries(normalizedRecord).reduce((result, [key, value]) => {
+    if (purchaseLedgerAllowedFieldKeys.has(key)) {
+      result[key] = value
+    }
+    return result
+  }, {})
+}
+
+const sanitizePurchasePayloadForSubmit = (payload, businessType) => {
+  if (businessType !== 'purchase_ledger' || !payload || typeof payload !== 'object') return payload
+
+  const sanitized = mergeLegacyProductDataIntoLedgers(Array.isArray(payload) ? [...payload] : { ...payload })
+  if (Array.isArray(sanitized.purchaseLedgers)) {
+    sanitized.purchaseLedgers = sanitized.purchaseLedgers.map(filterPurchaseLedgerRecord)
+  }
+
+  purchaseApprovalFieldKeys.forEach(key => {
+    if (!Array.isArray(sanitized)) {
+      delete sanitized[key]
+    }
+  })
+
+  return sanitized
+}
+
+const getVisiblePurchaseMissingFields = (analysisData) => {
+  const fields = Array.isArray(analysisData?.missingFields) ? analysisData.missingFields : []
+  const visibleFields = analysisData?.businessType === 'purchase_ledger'
+    ? fields.filter(field => {
+      if (purchaseApprovalFieldKeys.has(field)) return false
+      const normalizedField = purchasePayloadFieldKeyMap[field] || field
+      return !shouldHidePurchaseField(normalizedField) && !shouldHidePurchaseField(field)
+    })
+    : fields
+  return visibleFields.map(field => purchasePayloadFieldLabelMap[field] || field)
+}
+
+const formatDateParts = (year, month, day) => {
+  const normalizedYear = Number(year)
+  const normalizedMonth = Number(month)
+  const normalizedDay = Number(day)
+  if (!normalizedYear || !normalizedMonth || !normalizedDay) return ''
+
+  const date = new Date(normalizedYear, normalizedMonth - 1, normalizedDay)
+  if (
+    date.getFullYear() !== normalizedYear ||
+    date.getMonth() !== normalizedMonth - 1 ||
+    date.getDate() !== normalizedDay
+  ) {
+    return ''
+  }
+
+  return [
+    String(normalizedYear).padStart(4, '0'),
+    String(normalizedMonth).padStart(2, '0'),
+    String(normalizedDay).padStart(2, '0')
+  ].join('-')
+}
+
+const normalizeDateString = (value) => {
+  if (typeof value !== 'string') return value
+  const text = value.trim()
+  if (!text) return value
+
+  let match = text.match(/^(\d{4})-(\d{1,2})-(\d{1,2})(?:[T\s].*)?$/)
+  if (match) return formatDateParts(match[1], match[2], match[3]) || value
+
+  match = text.match(/^(\d{4})\/(\d{1,2})\/(\d{1,2})(?:\s.*)?$/)
+  if (match) return formatDateParts(match[1], match[2], match[3]) || value
+
+  match = text.match(/^(\d{4})骞�(\d{1,2})鏈�(\d{1,2})鏃�?(?:\s.*)?$/)
+  if (match) return formatDateParts(match[1], match[2], match[3]) || value
+
+  match = text.match(/^(\d{1,2})\/(\d{1,2})\/(\d{2}|\d{4})(?:\s.*)?$/)
+  if (match) {
+    const year = match[3].length === 2 ? Number(`20${match[3]}`) : Number(match[3])
+    return formatDateParts(year, match[1], match[2]) || value
+  }
+
+  return value
+}
+
+const normalizePurchasePayloadDates = (value, key = '') => {
+  if (Array.isArray(value)) {
+    return value.map(item => normalizePurchasePayloadDates(item, key))
+  }
+  if (value && typeof value === 'object') {
+    return Object.entries(value).reduce((result, [itemKey, item]) => {
+      result[itemKey] = normalizePurchasePayloadDates(item, itemKey)
+      return result
+    }, {})
+  }
+  return purchaseDateFieldKeys.has(key) ? normalizeDateString(value) : value
+}
+
+const isEmptyValue = (value) => {
+  if (value === null || value === undefined || value === '') return true
+  if (Array.isArray(value)) return value.every(item => isEmptyValue(item))
+  if (typeof value === 'object') return Object.values(value).every(item => isEmptyValue(item))
+  return false
+}
+
+const isPurchasePayloadEmpty = (payload) => isEmptyValue(payload)
+
+const getPurchaseConfirmDescription = (analysisData) => {
+  if (!analysisData) return ''
+  if (isPurchasePayloadEmpty(analysisData.payload)) {
+    return '鎴戞病鏈変粠鏂囦欢涓彁鍙栧埌瀹屾暣鐨勯噰璐笟鍔℃暟鎹紝鏆傛椂涓嶈兘鐩存帴鐢熸垚閲囪喘鍙拌处銆�'
+  }
+  return analysisData.description || '宸叉暣鐞嗗嚭寰呯‘璁ょ殑閲囪喘涓氬姟鏁版嵁锛岃鏍稿鍚庢彁浜ゃ��'
+}
+
+const confirmPurchaseAnalysis = async (message) => {
+  if (!message?.purchaseAnalysisData || message.confirming || message.confirmed) return
+
+  let payload
+  try {
+    const parsedPayload = message.payloadText?.trim() ? JSON.parse(message.payloadText) : {}
+    payload = sanitizePurchasePayloadForSubmit(
+      normalizePurchasePayloadDates(normalizePurchasePayload(parsedPayload)),
+      message.purchaseAnalysisData.businessType
+    )
+  } catch (err) {
+    message.confirmResult = '寰呮彁浜ゆ暟鎹笉鏄悎娉� JSON锛岃淇敼鍚庡啀纭'
+    message.confirmed = false
+    return
+  }
+
+  message.confirming = true
+  message.confirmResult = ''
+
+  try {
+    const res = await request.post(`${currentAssistant.value.apiBase}/analyze-files/confirm`, {
+      businessType: message.purchaseAnalysisData.businessType,
+      payload
+    })
+    message.confirmed = true
+    message.confirmResult = res?.msg || '纭鎴愬姛锛屼笟鍔″鐞嗗凡鎻愪氦'
+    ElMessage.success(message.confirmResult)
+  } catch (err) {
+    message.confirmed = false
+    message.confirmResult = err?.message || '纭澶辫触锛岃妫�鏌ユ暟鎹悗閲嶈瘯'
+  } finally {
+    message.confirming = false
+  }
+}
+
+const confirmPurchaseAnalysisFromTable = async (message) => {
+  if (!message?.purchaseAnalysisData || message.confirming || message.confirmed) return
+
+  if (!Array.isArray(message.payloadTreeData)) {
+    initializePurchasePayloadTree(message, message.purchaseAnalysisData.payload || {})
+  }
+  if (hasPurchaseNodeValidationError(message.payloadTreeData, 'object')) {
+    message.confirmResult = '璇峰厛琛ュ叏瀛楁鍚嶏紝骞剁‘淇濇暟瀛楀瓧娈靛~鍐欏悎娉曟暟瀛�'
+    message.confirmed = false
+    return
+  }
+
+  let payload
+  try {
+    const draftPayload = buildPurchasePayloadFromNodes(message.payloadTreeData, 'object')
+    const mergedPayload = mergePurchasePayloadWithHidden(draftPayload, message.payloadHiddenData)
+    const normalizedPayload = normalizePurchasePayload(mergedPayload)
+    payload = sanitizePurchasePayloadForSubmit(
+      normalizePurchasePayloadDates(normalizedPayload),
+      message.purchaseAnalysisData.businessType
+    )
+    message.payloadText = JSON.stringify(localizePurchasePayload(normalizedPayload), null, 2)
+  } catch (err) {
+    message.confirmResult = '寰呮彁浜ゆ暟鎹牸寮忔湁璇紝璇锋鏌ュ悗鍐嶇‘璁�'
+    message.confirmed = false
+    return
+  }
+
+  message.confirming = true
+  message.confirmResult = ''
+
+  try {
+    const res = await request.post(`${currentAssistant.value.apiBase}/analyze-files/confirm`, {
+      businessType: message.purchaseAnalysisData.businessType,
+      payload
+    })
+    message.confirmed = true
+    message.confirmResult = res?.msg || '纭鎴愬姛锛屼笟鍔″鐞嗗凡鎻愪氦'
+    ElMessage.success(message.confirmResult)
+  } catch (err) {
+    message.confirmed = false
+    message.confirmResult = err?.message || '纭澶辫触锛岃妫�鏌ユ暟鎹悗閲嶈瘯'
+  } finally {
+    message.confirming = false
+  }
+}
+
+const scrollToBottom = () => {
+  nextTick(() => {
+    if (messageListRef.value) {
+      messageListRef.value.scrollTop = messageListRef.value.scrollHeight
+    }
+  })
+}
+
+const handleFileChange = (file, fileList = []) => {
+  if (!file) return
+  const nextFiles = currentAssistant.value.allowMultipleFileUpload
+    ? fileList.map(item => item.raw).filter(Boolean)
+    : [file.raw].filter(Boolean)
+
+  const validFiles = nextFiles.filter(rawFile => {
+    const isLt10M = rawFile.size / 1024 / 1024 < 10
+    if (!isLt10M) {
+      ElMessage.error(`${rawFile.name} 鏂囦欢澶у皬涓嶈兘瓒呰繃 10MB!`)
+    }
+    return isLt10M
+  })
+
+  selectedFiles.value = validFiles
+  uploadFileList.value = fileList.filter(item => item.raw && validFiles.includes(item.raw))
+}
+
+const removeSelectedFile = (index) => {
+  selectedFiles.value.splice(index, 1)
+  uploadFileList.value.splice(index, 1)
+}
+
+const analyzeFiles = async (files, message = '') => {
+  const uploadFiles = Array.isArray(files) ? files : [files].filter(Boolean)
+  if (!uploadFiles.length) return
+  if (isSending.value) return
+  isSending.value = true
+  currentAbortController.value = new AbortController()
+
+  const fileNames = uploadFiles.map(file => file.name).join('銆�')
+  const userMsg = message ? `${message}\n[涓婁紶鏂囦欢鍒嗘瀽] ${fileNames}` : `[涓婁紶鏂囦欢鍒嗘瀽] ${fileNames}`
+  messages.value.push({
+    isUser: true,
+    content: userMsg,
+    htmlContent: convertTextToHtml(userMsg),
+    isTyping: false
+  })
+
+  const botMsgIndex = messages.value.length
+  messages.value.push({
+    isUser: false,
+    content: '',
+    htmlContent: '',
+    isTyping: true,
+    chartOptions: null,
+    chartRenderReady: false,
+    type: '',
+    tableData: null,
+    payloadTreeData: null,
+    payloadHiddenData: null
+  })
+
+  outputState.value[botMsgIndex] = {
+    isPaused: false,
+    jsonBlockStartPos: -1,
+    jsBlockStartPos: -1,
+    blockEndPos: -1,
+    hasRenderedChart: false
+  }
+
+  scrollToBottom()
+
+  const formData = new FormData()
+  const fileFieldName = currentAssistant.value.allowMultipleFileUpload ? 'files' : 'file'
+  uploadFiles.forEach(file => formData.append(fileFieldName, file))
+  formData.append('memoryId', uuid.value)
+  if (message.trim()) {
+    formData.append('message', message.trim())
+  }
+
+  const analyzeUrl = currentAssistant.value.fileAnalyzeUrl || `${currentAssistant.value.apiBase}/analyze-file`
+  request.post(analyzeUrl, formData, {
+    headers: {
+      'Content-Type': 'multipart/form-data'
+    },
+    signal: currentAbortController.value.signal,
+    onDownloadProgress: (e) => {
+      const fullText = e.target ? e.target.responseText : (e.event ? e.event.target.responseText : '')
+      if (!fullText) return
+
+      const currentMsg = messages.value[botMsgIndex]
+      if (!currentMsg) return
+
+      currentMsg.content = fullText
+
+      // 瑙f瀽 JSON 鏁版嵁锛堥拡瀵瑰祵鍏ュ紡 JSON锛�
+      const extracted = extractEmbeddedSuccessJson(fullText)
+      if (extracted) {
+        applyStructuredMessageData(currentMsg, extracted.data, botMsgIndex, !outputState.value[botMsgIndex].hasRenderedChart)
+      }
+
+      updateOutputState(fullText, botMsgIndex)
+      currentMsg.htmlContent = convertStreamOutput(fullText, botMsgIndex)
+      scrollToBottom()
+    }
+  }).then(() => {
+    const currentMsg = messages.value[botMsgIndex]
+    currentMsg.isTyping = false
+    isSending.value = false
+    currentAbortController.value = null
+
+    const extracted = extractEmbeddedSuccessJson(currentMsg.content)
+    if (extracted) {
+      applyStructuredMessageData(currentMsg, extracted.data, botMsgIndex, !outputState.value[botMsgIndex].hasRenderedChart)
+    }
+
+    // 鏈�缁堣В鏋愮‘淇濆浘琛ㄦ覆鏌�
+    if (currentMsg.chartOptions && !outputState.value[botMsgIndex].hasRenderedChart) {
+      renderCharts(botMsgIndex, currentMsg.chartOptions)
+      outputState.value[botMsgIndex].hasRenderedChart = true
+    }
+  }).catch(err => {
+    if (err.name === 'CanceledError' || err.name === 'AbortError') {
+      console.log('Analysis aborted by user')
+      isSending.value = false
+      currentAbortController.value = null
+      return
+    }
+    console.error('File analysis error:', err)
+    const errorMsg = '鎶辨瓑锛屾枃浠跺垎鏋愯繃绋嬩腑閬囧埌浜嗕竴鐐归棶棰橈紝璇风◢鍚庡啀璇曘��'
+    if (messages.value[botMsgIndex]) {
+      messages.value[botMsgIndex].content = errorMsg
+      messages.value[botMsgIndex].htmlContent = convertTextToHtml(errorMsg)
+      messages.value[botMsgIndex].isTyping = false
+    }
+    isSending.value = false
+    currentAbortController.value = null
+  })
+}
+
+const sendMessage = () => {
+  const msg = inputMessage.value?.trim() || ''
+  if ((msg || selectedFiles.value.length) && !isSending.value) {
+    if (selectedFiles.value.length) {
+      analyzeFiles([...selectedFiles.value], msg)
+      selectedFiles.value = []
+      uploadFileList.value = []
+    } else {
+      sendRequest(msg)
+    }
+    inputMessage.value = ''
+  }
+}
+
+const stopGeneration = () => {
+  abortCurrentRequest()
+}
+
+const sendRequest = (message) => {
+  isSending.value = true
+  currentAbortController.value = new AbortController()
+
+  // 鐢ㄦ埛娑堟伅
+  messages.value.push({
+    isUser: true,
+    content: message,
+    htmlContent: convertTextToHtml(message),
+    isTyping: false
+  })
+
+  // 鏈哄櫒浜哄崰浣�
+  const botMsgIndex = messages.value.length
+  const botMsg = {
+    isUser: false,
+    content: '',
+    htmlContent: '',
+    isTyping: true,
+    chartOptions: null,
+    chartRenderReady: false,
+    type: '',
+    tableData: null,
+    payloadTreeData: null,
+    payloadHiddenData: null
+  }
+  messages.value.push(botMsg)
+
+  outputState.value[botMsgIndex] = {
+    isPaused: false,
+    jsonBlockStartPos: -1,
+    jsBlockStartPos: -1,
+    blockEndPos: -1,
+    hasRenderedChart: false
+  }
+
+  scrollToBottom()
+
+  request.post(`${currentAssistant.value.apiBase}/chat`,
+      { memoryId: uuid.value, message },
+      {
+        signal: currentAbortController.value.signal,
+        onDownloadProgress: (e) => {
+          // 鍏煎涓嶅悓鐗堟湰鐨� axios 鑾峰彇鍝嶅簲鏂囨湰鐨勬柟寮�
+          const fullText = e.target ? e.target.responseText : (e.event ? e.event.target.responseText : '')
+          if (!fullText) return
+
+          const currentMsg = messages.value[botMsgIndex]
+          if (!currentMsg) return
+
+          currentMsg.content = fullText
+
+          // 灏濊瘯鎻愬彇骞惰В鏋� JSON
+          const extracted = extractEmbeddedSuccessJson(fullText)
+          if (extracted) {
+            applyStructuredMessageData(currentMsg, extracted.data, botMsgIndex)
+          } else {
+            const extractJson = (text) => {
+              const startIdx = text.indexOf('{"success": true')
+              if (startIdx === -1) return null
+
+              // 浠庡悗寰�鍓嶆壘鏈�鍚庝竴涓� '}'
+              const lastBraceIdx = text.lastIndexOf('}')
+              if (lastBraceIdx === -1 || lastBraceIdx < startIdx) return null
+
+              const potentialJson = text.substring(startIdx, lastBraceIdx + 1)
+              try {
+                return JSON.parse(potentialJson)
+              } catch (err) {
+                return null
+              }
+            }
+
+            const parsedData = extractJson(fullText)
+            if (parsedData) {
+              applyStructuredMessageData(currentMsg, parsedData, botMsgIndex, true)
+            }
+
+          }
+
+          updateOutputState(fullText, botMsgIndex)
+          currentMsg.htmlContent = convertStreamOutput(fullText, botMsgIndex)
+          scrollToBottom()
+        }
+      }
+  ).then(() => {
+    const currentMsg = messages.value[botMsgIndex]
+    currentMsg.isTyping = false
+    isSending.value = false
+    currentAbortController.value = null
+
+    // 鏈�缁堣В鏋�
+    const extracted = extractEmbeddedSuccessJson(currentMsg.content)
+    if (extracted) {
+      applyStructuredMessageData(currentMsg, extracted.data, botMsgIndex)
+    } else {
+      const extractJson = (text) => {
+        const startIdx = text.indexOf('{"success": true')
+        if (startIdx === -1) return null
+        const lastBraceIdx = text.lastIndexOf('}')
+        if (lastBraceIdx === -1 || lastBraceIdx < startIdx) return null
+        const potentialJson = text.substring(startIdx, lastBraceIdx + 1)
+        try {
+          return JSON.parse(potentialJson)
+        } catch (err) {
+          return null
+        }
+      }
+
+      const finalParsed = extractJson(currentMsg.content)
+      if (finalParsed) {
+        applyStructuredMessageData(currentMsg, finalParsed, botMsgIndex)
+      }
+    }
+  }).catch(err => {
+    if (err.name === 'CanceledError' || err.name === 'AbortError') {
+      console.log('Request aborted by user')
+      return
+    }
+    console.error('AI Chat Error:', err)
+    const errorMsg = '鎶辨瓑锛屾垜鐜板湪閬囧埌浜嗕竴鐐归棶棰橈紝璇风◢鍚庡啀璇曘��'
+    if (messages.value[botMsgIndex]) {
+      messages.value[botMsgIndex].content = errorMsg
+      messages.value[botMsgIndex].htmlContent = convertTextToHtml(errorMsg)
+      messages.value[botMsgIndex].isTyping = false
+    }
+    isSending.value = false
+    currentAbortController.value = null
+  })
+}
+
+const updateOutputState = (text, msgIndex) => {
+  const state = outputState.value[msgIndex]
+  if (state.jsonBlockStartPos === -1) {
+    const pos = text.indexOf('```json')
+    if (pos !== -1) { state.jsonBlockStartPos = pos; state.isPaused = true }
+  }
+  if (state.jsBlockStartPos === -1) {
+    const pos = text.indexOf('```javascript') !== -1 ? text.indexOf('```javascript') : text.indexOf('```js')
+    if (pos !== -1) { state.jsBlockStartPos = pos; state.isPaused = true }
+  }
+  if ((state.jsonBlockStartPos !== -1 || state.jsBlockStartPos !== -1) && state.blockEndPos === -1) {
+    const startCheck = state.jsonBlockStartPos !== -1 ? state.jsonBlockStartPos + 7 : state.jsBlockStartPos + (text.includes('javascript') ? 13 : 5)
+    const endPos = text.indexOf('```', startCheck)
+    if (endPos !== -1) { state.blockEndPos = endPos + 3; state.isPaused = false }
+  }
+}
+
+const convertTextToHtml = (text) => {
+  if (!text) return ''
+  return text
+      .replace(/&/g, '&amp;')
+      .replace(/</g, '&lt;')
+      .replace(/>/g, '&gt;')
+      .replace(/\n/g, '<br>')
+}
+
+const convertStreamOutput = (output, msgIndex) => {
+  if (!output) return ''
+  const state = outputState.value[msgIndex]
+  let display = output
+
+  // 灏濊瘯鎻愬彇 JSON 閮ㄥ垎
+  const extracted = extractEmbeddedSuccessJson(output)
+  const startMatch = output.match(/\{\s*"success"\s*:/)
+  const startIdx = extracted ? extracted.startIdx : (startMatch?.index ?? -1)
+
+  // 濡傛灉杩樺湪浠g爜鍧椾腑涓旀湭缁撴潫锛屾樉绀烘彁绀烘枃瀛�
+  if (state && ((state.jsonBlockStartPos !== -1) || (state.jsBlockStartPos !== -1)) && state.blockEndPos === -1) {
+    const startPos = state.jsonBlockStartPos !== -1 ? state.jsonBlockStartPos : state.jsBlockStartPos
+    const textBeforeBlock = display.substring(0, startPos).trim()
+    display = textBeforeBlock || '姝e湪鍒嗘瀽鏁版嵁骞剁敓鎴愬浘琛�...'
+    return convertTextToHtml(display)
+  }
+
+  if (extracted) {
+    const parsed = extracted.data
+    display = (output.substring(0, extracted.startIdx) + output.substring(extracted.endIdx)).trim()
+
+    if (/^[\s}\],锛屻��.:锛�;锛沒+$/.test(display)) {
+      display = ''
+    }
+
+    if (parsed.description) {
+      display = parsed.action === 'confirm_required'
+        ? getPurchaseConfirmDescription(parsed)
+        : parsed.description
+    }
+
+    if (!display) {
+      if (parsed.type === 'todo_list') {
+        display = '宸蹭负鎮ㄦ暣鐞嗗ソ鐩稿叧鏁版嵁銆�'
+      } else if (parsed.charts && Object.keys(parsed.charts).length > 0) {
+        display = '宸蹭负鎮ㄧ敓鎴愬垎鏋愬浘琛ㄣ��'
+      } else {
+        display = '姝e湪涓烘偍灞曠ず鍒嗘瀽缁撴灉...'
+      }
+    }
+  } else if (startIdx !== -1) {
+    const lastBraceIdx = output.lastIndexOf('}')
+    if (lastBraceIdx !== -1 && lastBraceIdx > startIdx) {
+      const potentialJson = output.substring(startIdx, lastBraceIdx + 1)
+      try {
+        const parsed = JSON.parse(potentialJson)
+        // 鎴愬姛瑙f瀽锛岀Щ闄� JSON 閮ㄥ垎鏄剧ず鏂囧瓧
+        display = (output.substring(0, startIdx) + output.substring(lastBraceIdx + 1)).trim()
+
+        if (/^[\s}\],锛屻��.:锛�;锛沒+$/.test(display)) {
+          display = ''
+        }
+
+        if (parsed.description) {
+          display = parsed.action === 'confirm_required'
+            ? getPurchaseConfirmDescription(parsed)
+            : parsed.description
+        }
+
+        if (!display) {
+          if (parsed.type === 'todo_list') {
+            display = '宸蹭负鎮ㄦ暣鐞嗗ソ鐩稿叧鏁版嵁锛�'
+          } else if (parsed.charts && Object.keys(parsed.charts).length > 0) {
+            display = '宸蹭负鎮ㄧ敓鎴愬垎鏋愬浘琛細'
+          } else {
+            display = '姝e湪涓烘偍灞曠ず鍒嗘瀽缁撴灉...'
+          }
+        }
+      } catch (e) {
+        // 瑙f瀽澶辫触锛岃鏄� JSON 杩樺湪浼犺緭涓垨鏍煎紡涓嶆纭�
+        display = output.substring(0, startIdx).trim() || '姝e湪鍒嗘瀽鏁版嵁骞剁敓鎴愬浘琛�...'
+      }
+    } else {
+      // 鎵惧埌浜嗗紑濮嬩絾杩樻病鎵惧埌缁撴潫
+      display = output.substring(0, startIdx).trim() || '姝e湪鍒嗘瀽鏁版嵁骞剁敓鎴愬浘琛�...'
+    }
+  }
+
+  let html = convertTextToHtml(display)
+
+  // 杩樺師浠g爜鍧�
+  html = html.replace(/```(javascript|js)([\s\S]*?)```/g, '<pre class="code-block js-code">$2</pre>')
+  html = html.replace(/```([\s\S]*?)```/g, '<pre class="code-block">$1</pre>')
+
+  return html || '...'
+}
+
+const renderCharts = (msgIndex, chartOptions) => {
+  nextTick(() => {
+    Object.keys(chartOptions).forEach(key => {
+      const id = `ai-chart-${msgIndex}-${key}`
+      const tryInit = (count = 0) => {
+        const dom = document.getElementById(id)
+        if (dom) {
+          if (chartInstances.value[id]) {
+            // 濡傛灉宸茬粡鍒濆鍖栬繃锛岀洿鎺ユ洿鏂版暟鎹�
+            const chart = chartInstances.value[id]
+            const option = normalizeAiChartOption(chartOptions[key])
+            if (option) chart.setOption(option)
+            return
+          }
+
+          const chart = echarts.init(dom)
+          chartInstances.value[id] = chart
+          const option = normalizeAiChartOption(chartOptions[key])
+          if (option) {
+            chart.setOption(option)
+          } else {
+            console.warn('Invalid chart option for:', id, chartOptions[key])
+          }
+
+          const handler = () => chart.resize()
+          resizeHandlers.value.push(handler)
+          window.addEventListener('resize', handler)
+        } else if (count < 15) { // 绋嶅井澧炲姞閲嶈瘯娆℃暟
+          setTimeout(() => tryInit(count + 1), 200)
+        }
+      }
+      tryInit()
+    })
+  })
+}
+
+// 鏍煎紡鍖� AI 杩斿洖鐨勫浘琛ㄩ厤缃紝灏嗗叾杞崲涓烘爣鍑嗙殑 ECharts 閰嶇疆
+const formatChartOption = (rawOption) => {
+  if (!rawOption) return null
+
+  // 濡傛灉宸茬粡鏄爣鍑� ECharts 閰嶇疆锛堝寘鍚� series锛夛紝鍒欑洿鎺ヨ繑鍥�
+  const hasSeries = rawOption.series && Array.isArray(rawOption.series)
+
+  // 灏濊瘯杞崲绠�鏄撴牸寮�
+  try {
+    const isPie = rawOption.type === 'pie' || (rawOption.title && rawOption.title.includes('鍗犳瘮'))
+
+    const option = {
+      title: {
+        text: rawOption.title || '',
+        left: 'center',
+        textStyle: { fontSize: 14 }
+      },
+      tooltip: {
+        trigger: isPie ? 'item' : 'axis'
+      },
+      legend: {
+        bottom: '0'
+      },
+      grid: {
+        left: '3%',
+        right: '4%',
+        bottom: '15%',
+        containLabel: true
+      },
+      xAxis: isPie ? undefined : {
+        type: 'category',
+        data: rawOption.xAxisData || (Array.isArray(rawOption.xAxis) ? rawOption.xAxis : []),
+        name: typeof rawOption.xAxis === 'string' ? rawOption.xAxis : ''
+      },
+      yAxis: isPie ? undefined : {
+        type: 'value',
+        name: typeof rawOption.yAxis === 'string' ? rawOption.yAxis : ''
+      },
+      series: rawOption.series || [{
+        name: rawOption.title || '鏁板��',
+        type: rawOption.type || 'line',
+        data: rawOption.seriesData || (Array.isArray(rawOption.data) ? rawOption.data : []),
+        smooth: true,
+        radius: isPie ? '50%' : undefined
+      }]
+    }
+
+    // 閽堝楗煎浘鐨勭壒娈婂鐞�
+    if (isPie && !option.series[0].data && Array.isArray(rawOption.data)) {
+      option.series[0].data = rawOption.data
+    }
+
+    return option
+  } catch (err) {
+    console.error('Chart option conversion failed:', err)
+    return null
+  }
+}
+
+const normalizeAiChartOption = (rawOption) => {
+  if (!rawOption) return null
+
+  try {
+    const hasSeries = Array.isArray(rawOption.series) && rawOption.series.length > 0
+    const firstSeriesType = hasSeries ? rawOption.series[0]?.type : rawOption.type
+    const titleConfig = rawOption.title && typeof rawOption.title === 'object'
+      ? rawOption.title
+      : null
+    const tooltipConfig = rawOption.tooltip && typeof rawOption.tooltip === 'object'
+      ? rawOption.tooltip
+      : null
+    const legendConfig = rawOption.legend && typeof rawOption.legend === 'object'
+      ? rawOption.legend
+      : null
+    const rawXAxisConfig = rawOption.xAxis && typeof rawOption.xAxis === 'object' && !Array.isArray(rawOption.xAxis)
+      ? rawOption.xAxis
+      : null
+    const rawYAxisConfig = rawOption.yAxis && typeof rawOption.yAxis === 'object' && !Array.isArray(rawOption.yAxis)
+      ? rawOption.yAxis
+      : null
+    const titleText = typeof rawOption.title === 'string' ? rawOption.title : rawOption.title?.text || ''
+    const isPie = firstSeriesType === 'pie' || titleText.includes('鍗犳瘮')
+    const baseXAxisData = Array.isArray(rawOption.xAxisData)
+        ? rawOption.xAxisData
+        : (Array.isArray(rawOption.xAxis) ? rawOption.xAxis : (Array.isArray(rawXAxisConfig?.data) ? rawXAxisConfig.data : []))
+    const fallbackSeries = [{
+      name: titleText || '鏁版嵁',
+      type: rawOption.type || 'line',
+      data: Array.isArray(rawOption.seriesData) ? rawOption.seriesData : (Array.isArray(rawOption.data) ? rawOption.data : [])
+    }]
+    const normalizedSeries = (hasSeries ? rawOption.series : fallbackSeries).map((seriesItem, index) => {
+      const seriesType = seriesItem?.type || rawOption.type || 'line'
+      const nextSeries = {
+        ...seriesItem,
+        name: seriesItem?.name || titleText || `绯诲垪${index + 1}`,
+        type: seriesType
+      }
+
+      if (isPie) {
+        nextSeries.radius = nextSeries.radius || '55%'
+        nextSeries.data = Array.isArray(nextSeries.data) ? nextSeries.data : (Array.isArray(rawOption.data) ? rawOption.data : [])
+      } else {
+        nextSeries.smooth = typeof nextSeries.smooth === 'boolean' ? nextSeries.smooth : seriesType === 'line'
+        nextSeries.data = Array.isArray(nextSeries.data) ? nextSeries.data : []
+      }
+
+      return nextSeries
+    })
+    const categorySource = !isPie
+      ? normalizedSeries.find(seriesItem => Array.isArray(seriesItem.data) && seriesItem.data.every(item => item && typeof item === 'object' && 'name' in item && 'value' in item))
+      : null
+    const xAxisData = categorySource
+      ? categorySource.data.map(item => item.name)
+      : baseXAxisData
+    const finalSeries = !isPie && categorySource
+      ? normalizedSeries.map(seriesItem => ({
+          ...seriesItem,
+          data: Array.isArray(seriesItem.data)
+            ? seriesItem.data.map(item => (item && typeof item === 'object' && 'value' in item ? item.value : item))
+            : []
+        }))
+      : normalizedSeries
+
+    return {
+      title: titleConfig || {
+        text: titleText,
+        left: 'center',
+        textStyle: { fontSize: 14 }
+      },
+      tooltip: tooltipConfig || {
+        trigger: isPie ? 'item' : 'axis'
+      },
+      legend: legendConfig || {
+        bottom: '0'
+      },
+      grid: isPie ? undefined : {
+        left: '3%',
+        right: '4%',
+        bottom: '15%',
+        containLabel: true
+      },
+      xAxis: isPie ? undefined : {
+        ...(rawXAxisConfig || {}),
+        type: 'category',
+        data: xAxisData,
+        name: typeof rawOption.xAxis === 'string' ? rawOption.xAxis : (rawXAxisConfig?.name || '')
+      },
+      yAxis: isPie ? undefined : (rawYAxisConfig || {
+        type: 'value',
+        name: typeof rawOption.yAxis === 'string' ? rawOption.yAxis : ''
+      }),
+      series: finalSeries
+    }
+  } catch (err) {
+    console.error('AI chart normalization failed:', err, rawOption)
+    return formatChartOption(rawOption)
+  }
+}
+
+watch(messages, () => scrollToBottom(), { deep: true })
+</script>
+
+<style lang="scss">
+.ai-chat-overlay {
+  pointer-events: none !important;
+  background: transparent !important;
+}
+
+.ai-chat-overlay .el-drawer {
+  pointer-events: auto;
+}
+</style>
+
+<style scoped lang="scss">
+$primary-blue: #0055d4;
+$secondary-blue: #2e8ce0;
+$light-blue: #7ab8ff;
+$pale-blue: #c5dcff;
+$ice-white: #e8f2ff;
+$deep-blue: #003b8e;
+$deepest-blue: #002b66;
+$gradient-blue: linear-gradient(145deg, #004fc7 0%, #0066e0 40%, #2580e8 70%, #5a9fe0 100%);
+$gradient-dark: linear-gradient(145deg, #003b8e 0%, #0055d4 50%, #0077e8 100%);
+$gradient-ice: linear-gradient(180deg, #e0ecff 0%, #d4e5ff 50%, #e8f0ff 100%);
+$shadow-blue: 0 8px 40px rgba(0, 85, 212, 0.35);
+$shadow-deep: 0 12px 48px rgba(0, 40, 120, 0.4);
+$shadow-card: 0 6px 24px rgba(0, 51, 136, 0.12);
+
+.ai-chat-sidebar-wrapper {
+  position: static;
+  z-index: 2000;
+  pointer-events: auto;
+
+  :deep(.el-drawer) {
+    pointer-events: auto;
+  }
+}
+
+.ai-chat-trigger {
+  pointer-events: auto;
+  position: fixed;
+  right: 24px;
+  bottom: 100px;
+  width: 56px;
+  height: 56px;
+  background: $gradient-dark;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  box-shadow: $shadow-deep, 0 0 0 2px rgba(0, 85, 212, 0.3) inset, 0 0 30px rgba(0, 119, 232, 0.2);
+  transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+  z-index: 2001;
+  animation: triggerPulse 3s ease-in-out infinite;
+
+  &::before {
+    content: '';
+    position: absolute;
+    inset: -6px;
+    background: linear-gradient(135deg, rgba(0, 85, 212, 0.4), rgba(0, 136, 232, 0.3), rgba(90, 159, 224, 0.2));
+    border-radius: 50%;
+    z-index: -1;
+    filter: blur(16px);
+    animation: glowPulse 2s ease-in-out infinite alternate;
+  }
+
+  &::after {
+    content: '';
+    position: absolute;
+    inset: 0;
+    border-radius: 50%;
+    background: linear-gradient(135deg, rgba(255, 255, 255, 0.3) 0%, transparent 50%);
+    pointer-events: none;
+  }
+
+  &:hover {
+    transform: scale(1.12) translateY(-4px);
+    box-shadow: $shadow-deep, 0 0 0 3px rgba(0, 136, 232, 0.4) inset, 0 0 50px rgba(0, 136, 232, 0.3);
+
+    &::before {
+      animation: glowPulse 1s ease-in-out infinite alternate;
+    }
+
+    .trigger-icon {
+      transform: rotate(-8deg) scale(1.05);
+      filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.5));
+    }
+  }
+
+  .trigger-icon {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    transition: all 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+    color: #fff;
+    filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));
+  }
+}
+
+@keyframes triggerPulse {
+  0%, 100% {
+    box-shadow: $shadow-blue, 0 0 0 2px rgba(0, 85, 212, 0.25) inset, 0 0 20px rgba(0, 119, 232, 0.15);
+  }
+  50% {
+    box-shadow: $shadow-deep, 0 0 0 3px rgba(0, 136, 232, 0.35) inset, 0 0 40px rgba(0, 136, 232, 0.25);
+  }
+}
+
+@keyframes glowPulse {
+  0% {
+    opacity: 0.6;
+    transform: scale(1);
+  }
+  100% {
+    opacity: 1;
+    transform: scale(1.1);
+  }
+}
+
+.ai-chat-drawer {
+  :deep(.el-drawer__body) {
+    padding: 0;
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+  }
+  :deep(.el-drawer__header) {
+    margin-bottom: 0;
+    padding: 0;
+    background: $gradient-dark;
+    color: #fff;
+  }
+}
+
+.drawer-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  width: 100%;
+  padding: 18px 20px;
+  background: $gradient-dark;
+  position: relative;
+  overflow: hidden;
+
+  &::before {
+    content: '';
+    position: absolute;
+    top: -60%;
+    right: -25%;
+    width: 250px;
+    height: 250px;
+    background: radial-gradient(circle, rgba(0, 136, 232, 0.4) 0%, transparent 70%);
+    pointer-events: none;
+    animation: headerGlow 4s ease-in-out infinite alternate;
+  }
+
+  &::after {
+    content: '';
+    position: absolute;
+    bottom: -40%;
+    left: -15%;
+    width: 200px;
+    height: 200px;
+    background: radial-gradient(circle, rgba(0, 85, 212, 0.3) 0%, transparent 70%);
+    pointer-events: none;
+    animation: headerGlow 5s ease-in-out infinite alternate-reverse;
+  }
+
+  .header-left {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+    position: relative;
+    z-index: 1;
+
+    .header-icon {
+      color: rgba(255, 255, 255, 0.95);
+      filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.2));
+      animation: iconFloat 3s ease-in-out infinite;
+    }
+
+    .title {
+      font-size: 17px;
+      font-weight: 600;
+      color: #fff;
+      text-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
+      letter-spacing: 0.5px;
+    }
+  }
+
+  .header-actions {
+    display: flex;
+    align-items: center;
+    gap: 10px;
+    position: relative;
+    z-index: 1;
+
+    .action-divider {
+      width: 1px;
+      height: 16px;
+      background: rgba(255, 255, 255, 0.2);
+      margin: 0 2px;
+    }
+
+    :deep(.el-button) {
+      color: rgba(255, 255, 255, 0.85);
+      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+      background: rgba(255, 255, 255, 0.12);
+      border: 1px solid rgba(255, 255, 255, 0.1);
+      border-radius: 8px;
+      padding: 8px;
+      height: 32px;
+      width: 32px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      &:hover {
+        color: #fff;
+        background: rgba(255, 255, 255, 0.25);
+        border-color: rgba(255, 255, 255, 0.3);
+        transform: translateY(-1px);
+        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+      }
+
+      &:active {
+        transform: translateY(0);
+      }
+
+      &.close-btn {
+        background: rgba(255, 255, 255, 0.1);
+        &:hover {
+          background: rgba(245, 108, 108, 0.8);
+          border-color: rgba(245, 108, 108, 0.5);
+        }
+      }
+    }
+
+    :deep(.header-action-btn) {
+      position: relative;
+      overflow: hidden;
+      background: linear-gradient(180deg, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.08));
+      border: 1px solid rgba(255, 255, 255, 0.16);
+      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14), 0 10px 18px rgba(0, 0, 0, 0.12);
+
+      &::before {
+        content: '';
+        position: absolute;
+        inset: 0;
+        background: linear-gradient(135deg, rgba(255, 255, 255, 0.22), transparent 55%);
+        pointer-events: none;
+      }
+
+      &::after {
+        content: '';
+        position: absolute;
+        top: -120%;
+        left: -40%;
+        width: 60%;
+        height: 260%;
+        background: linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.28), transparent);
+        transform: rotate(24deg);
+        opacity: 0;
+        transition: all 0.35s ease;
+      }
+
+      &:hover::after {
+        left: 100%;
+        opacity: 1;
+      }
+    }
+  }
+
+  .assistant-switcher {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex: 1;
+    padding: 0 12px;
+    position: relative;
+    z-index: 1;
+
+    :deep(.el-radio-group) {
+      display: flex;
+      gap: 6px;
+      flex-wrap: wrap;
+      justify-content: center;
+      padding: 4px;
+      border-radius: 999px;
+      background: linear-gradient(180deg, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0.08));
+      border: 1px solid rgba(255, 255, 255, 0.12);
+      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14), 0 10px 18px rgba(0, 0, 0, 0.1);
+    }
+
+    :deep(.el-radio-button__inner) {
+      border-radius: 999px;
+      border: 1px solid rgba(255, 255, 255, 0.18);
+      background: rgba(255, 255, 255, 0.12);
+      color: rgba(255, 255, 255, 0.86);
+      box-shadow: none;
+      padding: 7px 14px;
+      font-weight: 500;
+    }
+
+    :deep(.el-radio-button:first-child .el-radio-button__inner),
+    :deep(.el-radio-button:last-child .el-radio-button__inner) {
+      border-radius: 999px;
+    }
+
+    :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
+      background: #fff;
+      color: $primary-blue;
+      border-color: #fff;
+      box-shadow: 0 6px 14px rgba(0, 40, 120, 0.16);
+    }
+  }
+}
+
+@keyframes headerGlow {
+  0% {
+    opacity: 0.6;
+    transform: scale(1);
+  }
+  100% {
+    opacity: 1;
+    transform: scale(1.15);
+  }
+}
+
+@keyframes iconFloat {
+  0%, 100% {
+    transform: translateY(0) rotate(0);
+  }
+  50% {
+    transform: translateY(-2px) rotate(3deg);
+  }
+}
+
+.chat-container {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  width: 100%;
+  background: $ice-white;
+  position: relative;
+  overflow: hidden;
+
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    height: 240px;
+    background: linear-gradient(180deg, rgba(0, 85, 212, 0.06) 0%, transparent 100%);
+    pointer-events: none;
+  }
+}
+
+.history-panel {
+  position: absolute;
+  inset: 0;
+  background: linear-gradient(180deg, #fff 0%, $ice-white 100%);
+  z-index: 10;
+  display: flex;
+  flex-direction: column;
+  box-shadow: -8px 0 32px rgba(0, 85, 212, 0.15);
+
+  .history-header {
+    padding: 18px 20px;
+    border-bottom: 1px solid rgba(0, 85, 212, 0.12);
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    font-weight: 600;
+    font-size: 14px;
+    color: $deep-blue;
+    background: linear-gradient(135deg, rgba(0, 85, 212, 0.08) 0%, rgba(0, 136, 232, 0.05) 100%);
+    position: relative;
+
+    &::after {
+      content: '';
+      position: absolute;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      height: 1px;
+      background: linear-gradient(90deg, transparent, rgba(0, 85, 212, 0.2), transparent);
+    }
+  }
+
+  .session-list {
+    flex: 1;
+    overflow-y: auto;
+    padding: 12px 16px;
+
+    &::-webkit-scrollbar {
+      width: 8px;
+    }
+    &::-webkit-scrollbar-thumb {
+      background: linear-gradient(180deg, $secondary-blue, $primary-blue);
+      border-radius: 4px;
+      box-shadow: 0 0 6px rgba(0, 85, 212, 0.25);
+    }
+
+    .session-item {
+      display: flex;
+      align-items: center;
+      padding: 14px 16px;
+      margin-bottom: 6px;
+      border-radius: 12px;
+      cursor: pointer;
+      transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+      gap: 12px;
+      position: relative;
+      border: 1px solid transparent;
+      background: #fff;
+      animation: sessionSlideIn 0.35s ease;
+
+      @keyframes sessionSlideIn {
+        from {
+          opacity: 0;
+          transform: translateX(-15px);
+        }
+        to {
+          opacity: 1;
+          transform: translateX(0);
+        }
+      }
+
+      &:hover {
+        background: linear-gradient(135deg, rgba(0, 85, 212, 0.06) 0%, rgba(0, 136, 232, 0.08) 100%);
+        border-color: rgba(0, 85, 212, 0.12);
+        box-shadow: 0 4px 16px rgba(0, 85, 212, 0.1);
+        transform: translateX(4px);
+
+        .delete-btn {
+          opacity: 1;
+          transform: scale(1);
+        }
+      }
+
+      &.active {
+        background: linear-gradient(135deg, rgba(0, 85, 212, 0.12) 0%, rgba(0, 136, 232, 0.15) 100%);
+        border-color: rgba(0, 85, 212, 0.25);
+        color: $primary-blue;
+        box-shadow: 0 4px 16px rgba(0, 85, 212, 0.15);
+
+        .el-icon {
+          color: $primary-blue;
+        }
+      }
+
+      .el-icon {
+        font-size: 18px;
+        flex-shrink: 0;
+        color: $secondary-blue;
+        transition: color 0.2s;
+      }
+
+      .session-name {
+        flex: 1;
+        font-size: 13px;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        color: #1a1a2e;
+        font-weight: 500;
+      }
+
+      .delete-btn {
+        opacity: 0;
+        transform: scale(0.8);
+        transition: all 0.25s ease;
+        padding: 6px;
+        border-radius: 6px;
+        color: #c0c4cc;
+
+        &:hover {
+          color: #fff;
+          background: rgba(245, 108, 108, 0.85);
+          transform: scale(1.1) rotate(8deg);
+        }
+      }
+    }
+  }
+}
+
+.chat-main {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  flex: 1;
+  overflow: hidden;
+}
+
+.message-list {
+  flex: 1;
+  overflow-y: auto;
+  padding: 24px 20px;
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+  background: linear-gradient(180deg, transparent 0%, rgba(0, 85, 212, 0.02) 100%);
+
+  &::-webkit-scrollbar {
+    width: 8px;
+  }
+  &::-webkit-scrollbar-thumb {
+    background: linear-gradient(180deg, $secondary-blue, $primary-blue);
+    border-radius: 4px;
+    box-shadow: 0 0 8px rgba(0, 85, 212, 0.3);
+  }
+}
+
+.message-item {
+  display: flex;
+  gap: 14px;
+  width: 100%;
+  animation: messageSlideIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+
+  @keyframes messageSlideIn {
+    from {
+      opacity: 0;
+      transform: translateY(20px) scale(0.95);
+    }
+    to {
+      opacity: 1;
+      transform: translateY(0) scale(1);
+    }
+  }
+
+  .avatar {
+    width: 42px;
+    height: 42px;
+    border-radius: 14px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-shrink: 0;
+    font-size: 24px;
+    position: relative;
+    overflow: hidden;
+
+    &::before {
+      content: '';
+      position: absolute;
+      inset: 0;
+      background: inherit;
+      filter: blur(10px);
+      opacity: 0.5;
+      z-index: -1;
+    }
+
+    &::after {
+      content: '';
+      position: absolute;
+      top: -50%;
+      left: -50%;
+      width: 200%;
+      height: 200%;
+      background: linear-gradient(45deg, transparent 40%, rgba(255, 255, 255, 0.2) 50%, transparent 60%);
+      animation: shimmer 3s infinite;
+    }
+  }
+
+  .message-content {
+    flex: 1;
+    overflow-x: hidden;
+    display: flex;
+    flex-direction: column;
+    max-width: calc(100% - 56px);
+
+    .text-box {
+      padding: 14px 20px;
+      border-radius: 18px;
+      font-size: 14px;
+      line-height: 1.7;
+      word-break: break-word;
+      max-width: 100%;
+      width: fit-content;
+      overflow-x: auto;
+      transition: all 0.3s ease;
+      position: relative;
+
+      &::-webkit-scrollbar {
+        height: 4px;
+      }
+      &::-webkit-scrollbar-thumb {
+        background: rgba(0, 85, 212, 0.25);
+        border-radius: 2px;
+      }
+    }
+  }
+
+  &.bot-message {
+    .message-content {
+      align-items: flex-start;
+    }
+    .avatar {
+      background: $gradient-dark;
+      color: #fff;
+      box-shadow: 0 6px 20px rgba(0, 85, 212, 0.35);
+    }
+    .text-box {
+      background: #fff;
+      color: #1a1a2e;
+      box-shadow: $shadow-card;
+      border: 1px solid rgba(0, 85, 212, 0.08);
+      border-top-left-radius: 6px;
+
+      &::before {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        height: 1px;
+        background: linear-gradient(90deg, rgba(0, 85, 212, 0.15), transparent);
+      }
+    }
+  }
+
+  &.user-message {
+    flex-direction: row-reverse;
+    .message-content {
+      align-items: flex-end;
+    }
+    .avatar {
+      background: linear-gradient(145deg, #5a9fe0, #3d8bd4);
+      color: #fff;
+      box-shadow: 0 6px 20px rgba(0, 85, 212, 0.4);
+    }
+    .text-box {
+      background: $gradient-dark;
+      color: #fff;
+      border-top-right-radius: 6px;
+      box-shadow: 0 6px 24px rgba(0, 85, 212, 0.3);
+
+      &::before {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        height: 1px;
+        background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3));
+      }
+    }
+  }
+}
+
+@keyframes shimmer {
+  0% {
+    transform: translateX(-100%) rotate(45deg);
+  }
+  100% {
+    transform: translateX(100%) rotate(45deg);
+  }
+}
+
+.charts-wrapper {
+  margin-top: 12px;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  overflow-x: auto;
+  width: 100%;
+  padding-bottom: 8px;
+
+  &::-webkit-scrollbar {
+    height: 6px;
+  }
+  &::-webkit-scrollbar-thumb {
+    background: linear-gradient(90deg, $light-blue, $secondary-blue);
+    border-radius: 3px;
+  }
+}
+
+.chart-item {
+  width: 100%;
+  min-width: 300px;
+  height: 300px;
+  border-radius: 12px;
+  padding: 12px;
+  margin-bottom: 12px;
+}
+
+.table-wrapper {
+  margin-top: 12px;
+  background: #fff;
+  border-radius: 12px;
+  overflow: hidden;
+  overflow-x: auto;
+  width: 100%;
+  box-shadow: $shadow-card;
+  border: 1px solid rgba(0, 122, 255, 0.06);
+
+  &::-webkit-scrollbar {
+    height: 6px;
+  }
+  &::-webkit-scrollbar-thumb {
+    background: linear-gradient(90deg, $light-blue, $secondary-blue);
+    border-radius: 3px;
+  }
+
+  .el-table {
+    min-width: 300px;
+    --el-table-border-color: rgba(0, 122, 255, 0.08);
+    --el-table-header-bg-color: $ice-white;
+  }
+}
+
+.purchase-confirm-card {
+  margin-top: 12px;
+  width: 100%;
+  background: #fff;
+  border: 1px solid rgba(0, 85, 212, 0.12);
+  border-radius: 12px;
+  box-shadow: $shadow-card;
+  padding: 14px;
+  color: #1a1a2e;
+}
+
+.purchase-confirm-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px;
+  font-size: 15px;
+  font-weight: 700;
+  margin-bottom: 12px;
+}
+
+.purchase-confirm-desc {
+  margin-bottom: 12px;
+  color: #374151;
+  font-size: 13px;
+  line-height: 1.6;
+}
+
+.purchase-empty-state {
+  margin-bottom: 12px;
+  padding: 12px;
+  border-radius: 10px;
+  background: linear-gradient(135deg, rgba(255, 247, 237, 0.96), rgba(255, 255, 255, 0.98));
+  border: 1px solid rgba(245, 158, 11, 0.25);
+
+  .empty-title {
+    font-size: 14px;
+    font-weight: 700;
+    color: #92400e;
+    margin-bottom: 6px;
+  }
+
+  .empty-desc {
+    color: #78350f;
+    font-size: 13px;
+    line-height: 1.6;
+  }
+}
+
+.purchase-alert {
+  border-radius: 8px;
+  padding: 10px 12px;
+  margin-bottom: 10px;
+  font-size: 13px;
+
+  ul {
+    margin: 6px 0 0;
+    padding-left: 18px;
+  }
+
+  &.warning {
+    background: rgba(230, 162, 60, 0.12);
+    color: #9a5b00;
+  }
+
+  &.missing {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    gap: 8px;
+    background: rgba(245, 108, 108, 0.1);
+    color: #b42318;
+  }
+}
+
+.purchase-preview {
+  margin-bottom: 12px;
+
+  ul {
+    margin: 6px 0 0;
+    padding-left: 18px;
+    font-size: 13px;
+    line-height: 1.7;
+  }
+}
+
+.purchase-section-title {
+  margin: 10px 0 6px;
+  font-size: 13px;
+  font-weight: 700;
+  color: $deep-blue;
+}
+
+.payload-toolbar {
+  display: flex;
+  justify-content: flex-end;
+  margin-bottom: 8px;
+}
+
+.payload-tree-table-wrapper {
+  border: 1px solid rgba(0, 85, 212, 0.1);
+  border-radius: 10px;
+  overflow: auto;
+
+  :deep(.el-table) {
+    --el-table-header-bg-color: #f5f8ff;
+    --el-table-border-color: rgba(0, 85, 212, 0.08);
+  }
+}
+
+.payload-key-cell {
+  display: flex;
+  align-items: center;
+  min-height: 28px;
+}
+
+.payload-fixed-key {
+  display: flex;
+  flex-direction: column;
+  gap: 2px;
+  line-height: 1.3;
+  color: #1f2937;
+
+  small {
+    font-size: 11px;
+    color: #6b7280;
+  }
+}
+
+.payload-array-index {
+  font-size: 12px;
+  color: #475467;
+}
+
+.payload-container-cell {
+  color: #344054;
+  font-size: 12px;
+}
+
+.payload-null-value {
+  color: #6b7280;
+  font-size: 12px;
+}
+
+.payload-row-actions {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 4px;
+}
+
+.payload-editor-tip {
+  margin-top: 6px;
+  font-size: 12px;
+  line-height: 1.5;
+  color: #6b7280;
+}
+
+.purchase-confirm-actions {
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  gap: 12px;
+  margin-top: 12px;
+
+  .confirm-result {
+    flex: 1;
+    font-size: 13px;
+
+    &.success {
+      color: #1f9d55;
+    }
+
+    &.error {
+      color: #d93025;
+    }
+  }
+}
+
+.input-area {
+  padding: 18px 20px;
+  background: linear-gradient(180deg, rgba(232, 242, 255, 0.95) 0%, #fff 100%);
+  border-top: 1px solid rgba(0, 85, 212, 0.1);
+  position: relative;
+
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 20px;
+    right: 20px;
+    height: 1px;
+    background: linear-gradient(90deg, transparent, rgba(0, 85, 212, 0.15), transparent);
+  }
+
+  .input-actions {
+    display: flex;
+    gap: 14px;
+    margin-bottom: 12px;
+    align-items: center;
+
+    .file-upload-trigger {
+      display: inline-flex;
+      align-items: center;
+    }
+
+    :deep(.utility-action-btn) {
+      position: relative;
+      height: 34px;
+      padding: 0 14px;
+      border-radius: 999px;
+      border: 1px solid rgba(92, 119, 255, 0.18);
+      background: linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(236, 243, 255, 0.98));
+      color: $primary-blue;
+      font-weight: 600;
+      box-shadow: 0 10px 20px rgba(0, 85, 212, 0.08);
+      transition: all 0.25s ease;
+
+      .el-icon {
+        margin-right: 5px;
+      }
+
+      &:hover:not(.is-disabled) {
+        color: #fff;
+        border-color: transparent;
+        background: linear-gradient(135deg, #1f6dff 0%, #6b38ef 100%);
+        box-shadow: 0 14px 24px rgba(64, 90, 255, 0.2);
+        transform: translateY(-1px);
+      }
+    }
+
+    :deep(.stop-action-btn) {
+      border-color: rgba(255, 99, 123, 0.18);
+      color: #d33e5e;
+
+      &:hover:not(.is-disabled) {
+        background: linear-gradient(135deg, #f5536e 0%, #a33cff 100%);
+      }
+    }
+  }
+
+  .input-box {
+    padding: 16px;
+    position: relative;
+    background: #fff;
+    border: 2px solid rgba(0, 85, 212, 0.12);
+    border-radius: 16px;
+    margin: 0 4px;
+    transition: all 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+
+    &:focus-within {
+      border-color: $primary-blue;
+      box-shadow: 0 0 0 4px rgba(0, 85, 212, 0.12), $shadow-deep;
+      transform: translateY(-2px);
+      background: #fff;
+    }
+
+    .selected-file-list {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 8px;
+      margin-bottom: 12px;
+    }
+
+    .selected-file-tag {
+      display: flex;
+      align-items: center;
+      background: linear-gradient(135deg, rgba(0, 85, 212, 0.1) 0%, rgba(0, 136, 232, 0.15) 100%);
+      border: 1px solid rgba(0, 85, 212, 0.2);
+      border-radius: 10px;
+      padding: 8px 12px;
+      gap: 10px;
+      width: fit-content;
+      max-width: 100%;
+      animation: tagSlideIn 0.3s ease;
+
+      @keyframes tagSlideIn {
+        from {
+          opacity: 0;
+          transform: translateX(-10px);
+        }
+        to {
+          opacity: 1;
+          transform: translateX(0);
+        }
+      }
+
+      .el-icon {
+        color: $primary-blue;
+        font-size: 18px;
+      }
+
+      .file-name {
+        font-size: 13px;
+        color: $deep-blue;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        font-weight: 600;
+      }
+
+      .remove-file {
+        cursor: pointer;
+        color: $secondary-blue;
+        transition: all 0.2s;
+        padding: 4px;
+        border-radius: 50%;
+
+        &:hover {
+          color: #fff;
+          background: rgba(245, 108, 108, 0.8);
+          transform: scale(1.1) rotate(90deg);
+        }
+      }
+    }
+
+    :deep(.el-textarea__inner) {
+      padding: 0;
+      padding-bottom: 35px;
+      border: none;
+      box-shadow: none;
+      background: transparent;
+      font-family: inherit;
+      font-size: 14px;
+      line-height: 1.6;
+      color: #1a1a2e;
+
+      &::placeholder {
+        color: #7ab8ff;
+      }
+
+      &:focus {
+        box-shadow: none;
+      }
+    }
+
+    .send-btn {
+      position: absolute;
+      right: 16px;
+      bottom: 16px;
+      padding: 10px 22px;
+      background: $gradient-dark;
+      border: none;
+      border-radius: 10px;
+      font-weight: 600;
+      font-size: 14px;
+      color: #fff;
+      box-shadow: 0 6px 20px rgba(0, 85, 212, 0.4);
+      transition: all 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+      overflow: hidden;
+      display: inline-flex;
+      align-items: center;
+      gap: 6px;
+      letter-spacing: 0.3px;
+
+      &::before {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: -100%;
+        width: 100%;
+        height: 100%;
+        background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
+        transition: left 0.5s;
+      }
+
+      &:hover:not(:disabled) {
+        transform: translateY(-3px) scale(1.02);
+        box-shadow: 0 10px 30px rgba(0, 85, 212, 0.5);
+
+        &::before {
+          left: 100%;
+        }
+      }
+
+      &:active:not(:disabled) {
+        transform: translateY(-1px) scale(0.98);
+      }
+
+      &:disabled {
+        background: linear-gradient(145deg, #b0b0b0, #c5c5c5);
+        box-shadow: none;
+        cursor: not-allowed;
+      }
+
+      .el-icon {
+        font-size: 15px;
+        transform: translateY(-1px);
+      }
+    }
+  }
+}
+
+.typing-indicator {
+  display: flex;
+  gap: 5px;
+  padding: 10px 14px;
+  background: #fff;
+  border-radius: 14px;
+  width: fit-content;
+  box-shadow: $shadow-card;
+  margin-top: 6px;
+  border: 1px solid rgba(0, 122, 255, 0.06);
+  border-top-left-radius: 4px;
+
+  .dot {
+    width: 7px;
+    height: 7px;
+    background: $secondary-blue;
+    border-radius: 50%;
+    animation: typing 1.4s infinite ease-in-out;
+
+    &:nth-child(2) {
+      animation-delay: 0.2s;
+      background: $primary-blue;
+    }
+    &:nth-child(3) {
+      animation-delay: 0.4s;
+      background: $deep-blue;
+    }
+  }
+}
+
+@keyframes typing {
+  0%, 80%, 100% {
+    transform: scale(0.6);
+    opacity: 0.4;
+  }
+  40% {
+    transform: scale(1);
+    opacity: 1;
+  }
+}
+
+.code-block {
+  background: linear-gradient(145deg, #1a1a2e, #16213e);
+  color: #a8d8ff;
+  padding: 14px;
+  border-radius: 10px;
+  font-family: 'Fira Code', 'Consolas', monospace;
+  margin: 10px 0;
+  overflow-x: auto;
+  border: 1px solid rgba(90, 200, 250, 0.15);
+  box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.2);
+
+  &.js-code {
+    color: #5ac8fa;
+  }
+}
+
+.chat-main {
+  background:
+    radial-gradient(circle at top left, rgba(46, 140, 224, 0.12) 0%, transparent 34%),
+    linear-gradient(180deg, #fff 0%, #f7fbff 46%, #fff 100%);
+}
+
+.chat-hero {
+  display: grid;
+  grid-template-columns: 164px minmax(0, 1fr);
+  gap: 18px;
+  align-items: start;
+  padding: 14px 18px 6px;
+
+  &.compact {
+    grid-template-columns: 122px minmax(0, 1fr);
+    gap: 12px;
+    padding: 8px 18px 2px;
+  }
+}
+
+.assistant-stand {
+  position: relative;
+  min-height: 252px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
+  padding-top: 18px;
+  overflow: hidden;
+
+  &.compact {
+    min-height: 176px;
+    padding-top: 8px;
+  }
+
+  &.thinking {
+    .assistant-halo {
+      opacity: 1;
+      transform: scale(1.08);
+      filter: blur(8px);
+    }
+
+    .assistant-scan-ring {
+      opacity: 1;
+      animation-duration: 1.6s;
+    }
+
+    .assistant-orbit {
+      opacity: 1;
+    }
+
+    .assistant-bot {
+      transform: translateY(-4px) scale(1.02);
+    }
+
+    .assistant-bot-head {
+      box-shadow: 0 0 30px rgba(80, 157, 255, 0.36);
+    }
+
+    .assistant-bot-eye {
+      animation: robotBlinkFast 1.1s infinite;
+      box-shadow: 0 0 16px rgba(72, 186, 255, 0.95);
+    }
+
+    .assistant-bot-mouth {
+      width: 28px;
+      opacity: 1;
+      animation: robotTalk 1.2s ease-in-out infinite;
+    }
+
+    .assistant-bot-core {
+      animation: corePulse 1.4s ease-in-out infinite;
+      box-shadow: 0 0 24px rgba(78, 120, 255, 0.26);
+    }
+
+    .assistant-bot-core-ring {
+      animation: coreRotate 3s linear infinite;
+    }
+
+    .assistant-status {
+      color: #6a3bee;
+      box-shadow: 0 10px 22px rgba(106, 59, 238, 0.14);
+    }
+
+    .assistant-status-dot {
+      background: #6a3bee;
+      box-shadow: 0 0 12px rgba(106, 59, 238, 0.9);
+      animation: thinkingDot 1s ease-in-out infinite;
+    }
+
+    .assistant-base-sm {
+      box-shadow: 0 0 24px rgba(255, 93, 122, 0.48);
+    }
+  }
+}
+
+.assistant-halo {
+  position: absolute;
+  top: 22px;
+  width: 130px;
+  height: 130px;
+  border-radius: 50%;
+  background: radial-gradient(circle, rgba(46, 140, 224, 0.3) 0%, rgba(0, 85, 212, 0.18) 42%, rgba(113, 54, 244, 0.12) 60%, transparent 78%);
+  filter: blur(6px);
+  opacity: 0.82;
+  transition: all 0.35s ease;
+}
+
+.assistant-scan-ring {
+  position: absolute;
+  top: 40px;
+  width: 132px;
+  height: 132px;
+  border-radius: 50%;
+  border: 1px solid rgba(90, 159, 224, 0.22);
+  box-shadow: inset 0 0 16px rgba(255, 255, 255, 0.25);
+  opacity: 0.55;
+  animation: scanRing 4s linear infinite;
+}
+
+.assistant-orbit {
+  position: absolute;
+  top: 52px;
+  width: 150px;
+  height: 150px;
+  border-radius: 50%;
+  border: 1px dashed rgba(92, 135, 255, 0.22);
+  opacity: 0.45;
+}
+
+.assistant-orbit-a {
+  animation: orbitRotate 8s linear infinite;
+}
+
+.assistant-orbit-b {
+  width: 118px;
+  height: 118px;
+  top: 68px;
+  border-color: rgba(255, 108, 150, 0.22);
+  animation: orbitRotateReverse 5.6s linear infinite;
+}
+
+.assistant-bot {
+  position: relative;
+  z-index: 1;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  margin-top: 12px;
+  transition: transform 0.35s ease;
+}
+
+.assistant-bot-antenna {
+  position: absolute;
+  top: -4px;
+  width: 4px;
+  height: 20px;
+  border-radius: 999px;
+  background: linear-gradient(180deg, #fefefe, #aac9ff);
+
+  &::before {
+    content: '';
+    position: absolute;
+    top: -6px;
+    left: 50%;
+    width: 10px;
+    height: 10px;
+    border-radius: 50%;
+    transform: translateX(-50%);
+    background: linear-gradient(135deg, #54bfff, #7a41ff);
+    box-shadow: 0 0 14px rgba(84, 191, 255, 0.65);
+  }
+}
+
+.assistant-bot-antenna-left {
+  left: 36px;
+  transform: rotate(-14deg);
+}
+
+.assistant-bot-antenna-right {
+  right: 36px;
+  transform: rotate(14deg);
+}
+
+.assistant-bot-head {
+  position: relative;
+  width: 92px;
+  height: 78px;
+  border-radius: 28px;
+  background: linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, #e8f1ff 100%);
+  border: 1px solid rgba(0, 85, 212, 0.14);
+  box-shadow: 0 16px 32px rgba(0, 85, 212, 0.14);
+}
+
+.assistant-bot-head-glow {
+  position: absolute;
+  inset: 10px 16px auto;
+  height: 20px;
+  border-radius: 999px;
+  background: linear-gradient(180deg, rgba(0, 85, 212, 0.16), transparent);
+}
+
+.assistant-bot-eye {
+  position: absolute;
+  top: 30px;
+  width: 16px;
+  height: 16px;
+  border-radius: 50%;
+  background: radial-gradient(circle, #8ef0ff 0%, #56c0ff 42%, #2869ff 100%);
+  box-shadow: 0 0 12px rgba(72, 186, 255, 0.72);
+  animation: robotBlink 3.2s infinite;
+}
+
+.assistant-bot-eye-left {
+  left: 22px;
+}
+
+.assistant-bot-eye-right {
+  right: 22px;
+}
+
+.assistant-bot-mouth {
+  position: absolute;
+  left: 50%;
+  bottom: 16px;
+  width: 22px;
+  height: 4px;
+  transform: translateX(-50%);
+  border-radius: 999px;
+  background: linear-gradient(90deg, rgba(72, 186, 255, 0.2), rgba(72, 186, 255, 0.9), rgba(72, 186, 255, 0.2));
+}
+
+.assistant-bot-neck {
+  width: 16px;
+  height: 10px;
+  border-radius: 0 0 10px 10px;
+  background: linear-gradient(180deg, #dceaff, #bdd5ff);
+  margin-top: -2px;
+}
+
+.assistant-bot-body {
+  position: relative;
+  width: 104px;
+  height: 92px;
+  margin-top: 2px;
+  border-radius: 28px 28px 34px 34px;
+  background: linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, #e3eeff 100%);
+  border: 1px solid rgba(0, 85, 212, 0.14);
+  box-shadow: 0 18px 36px rgba(0, 85, 212, 0.16);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.assistant-bot-arm {
+  position: absolute;
+  top: 18px;
+  width: 16px;
+  height: 44px;
+  border-radius: 999px;
+  background: linear-gradient(180deg, #eff5ff, #c7dbff);
+  border: 1px solid rgba(0, 85, 212, 0.12);
+}
+
+.assistant-bot-arm-left {
+  left: -10px;
+  transform: rotate(16deg);
+}
+
+.assistant-bot-arm-right {
+  right: -10px;
+  transform: rotate(-16deg);
+}
+
+.assistant-bot-core {
+  position: relative;
+  width: 46px;
+  height: 46px;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: $primary-blue;
+  background: radial-gradient(circle, rgba(255, 255, 255, 1) 0%, #dae8ff 55%, #adc7ff 100%);
+}
+
+.assistant-bot-core-ring {
+  position: absolute;
+  inset: -6px;
+  border-radius: 50%;
+  border: 1px solid rgba(88, 135, 255, 0.3);
+  border-top-color: rgba(255, 96, 139, 0.85);
+  border-right-color: rgba(79, 145, 255, 0.9);
+}
+
+.assistant-status {
+  position: relative;
+  z-index: 1;
+  margin-top: 14px;
+  padding: 6px 12px;
+  border-radius: 999px;
+  font-size: 12px;
+  font-weight: 600;
+  color: $deep-blue;
+  background: rgba(255, 255, 255, 0.95);
+  border: 1px solid rgba(0, 85, 212, 0.12);
+  box-shadow: 0 8px 20px rgba(0, 85, 212, 0.08);
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+}
+
+.assistant-status-dot {
+  width: 8px;
+  height: 8px;
+  border-radius: 50%;
+  background: #2e8ce0;
+  box-shadow: 0 0 10px rgba(46, 140, 224, 0.72);
+}
+
+.assistant-base {
+  position: absolute;
+  bottom: 0;
+  left: 50%;
+  transform: translateX(-50%);
+  border-radius: 50%;
+  border: 2px solid rgba(255, 93, 122, 0.22);
+  background: radial-gradient(circle, rgba(255, 255, 255, 0.9) 0%, rgba(255, 111, 145, 0.1) 70%, transparent 100%);
+}
+
+.assistant-base-lg {
+  width: 118px;
+  height: 30px;
+}
+
+.assistant-base-md {
+  bottom: 6px;
+  width: 88px;
+  height: 20px;
+  border-color: rgba(255, 93, 122, 0.34);
+}
+
+.assistant-base-sm {
+  bottom: 11px;
+  width: 54px;
+  height: 10px;
+  background: linear-gradient(90deg, rgba(255, 93, 122, 0.95), rgba(255, 173, 188, 0.9));
+  border: none;
+  box-shadow: 0 0 18px rgba(255, 93, 122, 0.38);
+}
+
+@keyframes robotBlink {
+  0%, 44%, 48%, 100% {
+    transform: scaleY(1);
+  }
+  46% {
+    transform: scaleY(0.14);
+  }
+}
+
+@keyframes robotBlinkFast {
+  0%, 100% {
+    transform: scaleY(1);
+  }
+  50% {
+    transform: scaleY(0.3);
+  }
+}
+
+@keyframes robotTalk {
+  0%, 100% {
+    transform: translateX(-50%) scaleX(1);
+  }
+  50% {
+    transform: translateX(-50%) scaleX(1.35);
+  }
+}
+
+@keyframes corePulse {
+  0%, 100% {
+    transform: scale(1);
+    filter: brightness(1);
+  }
+  50% {
+    transform: scale(1.08);
+    filter: brightness(1.08);
+  }
+}
+
+@keyframes coreRotate {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+@keyframes orbitRotate {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+@keyframes orbitRotateReverse {
+  from {
+    transform: rotate(360deg);
+  }
+  to {
+    transform: rotate(0deg);
+  }
+}
+
+@keyframes scanRing {
+  0%, 100% {
+    transform: scale(0.96);
+    opacity: 0.42;
+  }
+  50% {
+    transform: scale(1.04);
+    opacity: 0.86;
+  }
+}
+
+@keyframes thinkingDot {
+  0%, 100% {
+    transform: scale(1);
+  }
+  50% {
+    transform: scale(1.35);
+  }
+}
+
+.welcome-card {
+  position: relative;
+  padding: 14px 14px 12px;
+  border-radius: 16px;
+  background:
+    linear-gradient(#fff, #fff) padding-box,
+    linear-gradient(135deg, rgba(255, 64, 96, 0.85), rgba(117, 65, 255, 0.9)) border-box;
+  border: 1px solid transparent;
+  box-shadow: 0 16px 36px rgba(0, 85, 212, 0.12);
+
+  &.compact {
+    padding: 10px 12px;
+    border-radius: 12px;
+    box-shadow: 0 8px 16px rgba(0, 85, 212, 0.07);
+
+    .welcome-eyebrow {
+      margin-bottom: 4px;
+    }
+
+    .welcome-title {
+      font-size: 17px;
+      line-height: 1.3;
+
+      br {
+        display: none;
+      }
+    }
+
+    .welcome-desc {
+      margin-top: 6px;
+      font-size: 12px;
+      line-height: 1.55;
+    }
+
+    .quick-prompt-list {
+      margin-top: 10px;
+      gap: 6px;
+    }
+
+    .quick-prompt-btn {
+      padding: 8px 10px;
+      font-size: 12px;
+      border-radius: 7px;
+    }
+
+    .more-prompts-btn {
+      margin-top: 8px;
+      font-size: 12px;
+    }
+  }
+}
+
+.welcome-eyebrow {
+  font-size: 11px;
+  font-weight: 700;
+  letter-spacing: 2px;
+  color: rgba(0, 85, 212, 0.58);
+  margin-bottom: 8px;
+}
+
+.welcome-title {
+  margin: 0;
+  font-size: 26px;
+  line-height: 1.2;
+  font-weight: 800;
+  color: #172033;
+}
+
+.welcome-desc {
+  margin: 10px 0 0;
+  font-size: 13px;
+  line-height: 1.7;
+  color: #5f6980;
+}
+
+.quick-prompt-list {
+  display: grid;
+  gap: 8px;
+  margin-top: 14px;
+}
+
+.quick-prompt-btn {
+  width: 100%;
+  border: none;
+  border-radius: 10px;
+  padding: 11px 14px;
+  text-align: left;
+  font-size: 13px;
+  font-weight: 600;
+  color: #fff;
+  cursor: pointer;
+  background: linear-gradient(90deg, #ff4c55 0%, #7c38ef 100%);
+  box-shadow: 0 12px 22px rgba(124, 56, 239, 0.18);
+  transition: transform 0.25s ease, box-shadow 0.25s ease, opacity 0.2s ease;
+  position: relative;
+  overflow: hidden;
+
+  &::before {
+    content: '';
+    position: absolute;
+    inset: 0;
+    background: linear-gradient(135deg, rgba(255, 255, 255, 0.22), transparent 56%);
+    pointer-events: none;
+  }
+
+  &::after {
+    content: '';
+    position: absolute;
+    top: -120%;
+    left: -30%;
+    width: 45%;
+    height: 260%;
+    background: linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.3), transparent);
+    transform: rotate(22deg);
+    opacity: 0;
+    transition: all 0.35s ease;
+  }
+
+  &:hover:not(:disabled) {
+    transform: translateY(-2px) scale(1.01);
+    box-shadow: 0 16px 28px rgba(124, 56, 239, 0.24);
+
+    &::after {
+      left: 100%;
+      opacity: 1;
+    }
+  }
+
+  &:disabled {
+    cursor: not-allowed;
+    opacity: 0.65;
+  }
+}
+
+.more-prompts-btn {
+  margin-top: 10px;
+  padding: 0 12px;
+  height: 32px;
+  border: 1px solid rgba(208, 65, 81, 0.12);
+  border-radius: 999px;
+  background: linear-gradient(180deg, rgba(255, 255, 255, 0.96), rgba(255, 241, 245, 0.96));
+  color: #d04151;
+  font-size: 13px;
+  font-weight: 600;
+  cursor: pointer;
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+  box-shadow: 0 10px 18px rgba(208, 65, 81, 0.08);
+  transition: all 0.25s ease;
+
+  &:hover {
+    transform: translateY(-1px);
+    background: linear-gradient(135deg, #ff5570 0%, #8a3df6 100%);
+    border-color: transparent;
+    color: #fff;
+    box-shadow: 0 14px 24px rgba(138, 61, 246, 0.18);
+  }
+}
+
+.hero-dot-grid {
+  display: grid;
+  grid-template-columns: repeat(14, 1fr);
+  gap: 7px;
+  padding: 0 18px 14px;
+
+  span {
+    display: block;
+    width: 100%;
+    aspect-ratio: 1;
+    border-radius: 2px;
+    background: linear-gradient(135deg, rgba(255, 110, 138, 0.95), rgba(255, 190, 201, 0.55));
+  }
+}
+
+.message-list {
+  padding: 8px 18px 18px;
+  gap: 16px;
+  background: transparent;
+}
+
+.input-area {
+  padding: 12px 18px 16px;
+  background: #fff;
+  border-top: none;
+
+  &::before {
+    display: none;
+  }
+
+  .input-box {
+    padding: 14px 16px 16px;
+    border: 1px solid rgba(123, 56, 239, 0.9);
+    border-radius: 22px;
+    margin: 0;
+    transition: all 0.25s ease;
+    box-shadow: 0 14px 34px rgba(0, 85, 212, 0.08);
+
+    &:focus-within {
+      border-color: #7c38ef;
+      box-shadow: 0 0 0 3px rgba(124, 56, 239, 0.1), 0 18px 40px rgba(0, 85, 212, 0.12);
+      transform: none;
+    }
+
+    :deep(.el-textarea__inner) {
+      padding-right: 58px;
+      padding-bottom: 0;
+      min-height: 104px;
+
+      &::placeholder {
+        color: #a0a9bc;
+      }
+    }
+
+    .send-btn {
+      right: 25px;
+      top: 50%;
+      transform: translateY(-50%);
+      width: 36px;
+      min-width: 36px;
+      height: 36px;
+      padding: 0;
+      background: linear-gradient(135deg, #ff5570 0%, #7a36f2 58%, #2d79ff 100%);
+      border-radius: 50%;
+      box-shadow: 0 12px 24px rgba(109, 50, 236, 0.24);
+      transition: all 0.25s ease;
+      gap: 0;
+
+      &:hover:not(:disabled) {
+        transform: translateY(calc(-50% - 1px)) scale(1.04);
+        box-shadow: 0 16px 28px rgba(109, 50, 236, 0.3);
+      }
+
+      &:active:not(:disabled) {
+        transform: translateY(-50%) scale(0.96);
+      }
+
+      .el-icon {
+        margin: 0;
+        font-size: 16px;
+        transform: translate(0, -1px);
+      }
+    }
+  }
+}
+
+@media (max-width: 767px) {
+  .chat-hero {
+    grid-template-columns: 1fr;
+    gap: 10px;
+    padding: 14px 14px 6px;
+
+    &.compact {
+      padding: 8px 14px 4px;
+    }
+  }
+
+  .assistant-stand {
+    min-height: 184px;
+  }
+
+  .welcome-card {
+    padding: 12px 12px 10px;
+  }
+
+  .welcome-title {
+    font-size: 21px;
+  }
+
+  .hero-dot-grid {
+    grid-template-columns: repeat(12, 1fr);
+    gap: 6px;
+    padding: 0 14px 12px;
+  }
+
+  .message-list {
+    padding: 8px 14px 14px;
+  }
+
+  .input-area {
+    padding: 10px 14px 14px;
+  }
+
+  .input-area .input-actions {
+    gap: 10px;
+    flex-wrap: wrap;
+  }
+}
+</style>
diff --git a/src/components/AttachmentPreview/image/index.vue b/src/components/AttachmentPreview/image/index.vue
new file mode 100644
index 0000000..1c6039b
--- /dev/null
+++ b/src/components/AttachmentPreview/image/index.vue
@@ -0,0 +1,76 @@
+<script setup>
+const props = defineProps({
+  fileList: {
+    type: Array,
+    default: () => [],
+  },
+  thumbSize: {
+    type: Number,
+    default: 72,
+  },
+  gap: {
+    type: Number,
+    default: 10,
+  },
+})
+
+const normalizedList = computed(() => {
+  return (props.fileList || [])
+    .filter((item) => item && item.previewURL)
+    .map((item, index) => ({
+      id: item.id ?? index,
+      name: item.originalFilename || `image-${index + 1}`,
+      url: item.previewURL,
+    }))
+})
+const previewUrls = computed(() => normalizedList.value.map((item) => item.url))
+</script>
+
+<template>
+  <div class="attachment-image-preview">
+    <div v-if="!normalizedList.length" class="empty">鏆傛棤鍥剧墖</div>
+
+    <div v-else class="thumbs" :style="{ gap: `${gap}px` }">
+      <el-image
+        v-for="(item, index) in normalizedList"
+        :key="item.id"
+        class="thumb"
+        :style="{ width: `${thumbSize}px`, height: `${thumbSize}px` }"
+        :src="item.url"
+        :preview-src-list="previewUrls"
+        :initial-index="index"
+        fit="cover"
+        preview-teleported
+      />
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.attachment-image-preview {
+  width: 100%;
+}
+
+.empty {
+  height: 120px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: var(--el-text-color-secondary);
+  border: 1px dashed var(--el-border-color);
+  border-radius: 8px;
+}
+
+.thumbs {
+  display: flex;
+  flex-wrap: wrap;
+}
+
+.thumb {
+  border: 1px solid var(--el-border-color);
+  border-radius: 6px;
+  overflow: hidden;
+  cursor: pointer;
+  background: #fff;
+}
+</style>
diff --git a/src/components/AttachmentUpload/file/index.vue b/src/components/AttachmentUpload/file/index.vue
new file mode 100644
index 0000000..1e4508c
--- /dev/null
+++ b/src/components/AttachmentUpload/file/index.vue
@@ -0,0 +1,309 @@
+<script setup>
+import { UploadFilled } from '@element-plus/icons-vue'
+import { uploadFile } from '@/api/basicData/common'
+
+const props = defineProps({
+  fileList: {
+    type: Array,
+    default: () => [],
+  },
+  index: {
+    type: Number,
+    default: -1,
+  },
+  childrenKey: {
+    type: String,
+    default: 'files',
+  },
+  limit: {
+    type: Number,
+    default: 10,
+  },
+  fileSize: {
+    type: Number,
+    default: 50,
+  },
+  fileType: {
+    type: Array,
+    default: () => [],
+  },
+  buttonText: {
+    type: String,
+    default: '鍗曞嚮閫夋嫨鏂囦欢',
+  },
+  disabled: {
+    type: Boolean,
+    default: false,
+  },
+  uploadFieldName: {
+    type: String,
+    default: 'files',
+  },
+})
+
+const emit = defineEmits(['update:fileList', 'change'])
+const { proxy } = getCurrentInstance()
+
+const uploadRef = ref()
+const uploadQueueTimer = ref(null)
+const uploading = ref(false)
+const queuedUidSet = ref(new Set())
+const innerList = ref([])
+
+function readListFromProps() {
+  if (props.index > -1) {
+    const row = props.fileList?.[props.index]
+    return Array.isArray(row?.[props.childrenKey]) ? row[props.childrenKey] : []
+  }
+  return Array.isArray(props.fileList) ? props.fileList : []
+}
+
+watch(
+  () => props.fileList,
+  () => {
+    innerList.value = [...readListFromProps()]
+  },
+  { deep: true, immediate: true },
+)
+
+const currentList = computed({
+  get() {
+    return innerList.value
+  },
+  set(value) {
+    const nextList = Array.isArray(value) ? value : []
+    innerList.value = nextList
+
+    if (props.index > -1) {
+      const nextModelValue = Array.isArray(props.fileList) ? [...props.fileList] : []
+      const currentRow = nextModelValue[props.index] || {}
+      nextModelValue[props.index] = {
+        ...currentRow,
+        [props.childrenKey]: nextList,
+      }
+      emit('update:fileList', nextModelValue)
+      emit('change', nextList, nextModelValue)
+      return
+    }
+
+    emit('update:fileList', nextList)
+    emit('change', nextList, nextList)
+  },
+})
+
+const displayFileList = computed(() => {
+  return currentList.value.map((item, index) => ({
+    uid: getItemUid(item, index),
+    name: getItemName(item, index),
+    url: getItemUrl(item),
+    status: 'success',
+    rawData: item,
+  }))
+})
+
+const uploadTip = computed(() => {
+  if (!props.fileType.length) return `鍗曚釜鏂囦欢涓嶈秴杩� ${props.fileSize}MB`
+  return `鏀寔 ${props.fileType.join('/')}锛屽崟涓枃浠朵笉瓒呰繃 ${props.fileSize}MB`
+})
+
+function getItemUid(item, index) {
+  if (item?.id !== undefined && item?.id !== null) return `${item.id}`
+  return `${getItemName(item, index)}-${getItemUrl(item) || index}`
+}
+
+function getItemUrl(item) {
+  if (!item) return ''
+  if (typeof item === 'string') return item
+  return item.url || item.downloadURL || item.previewURL || item.previewUrl || ''
+}
+
+function getItemName(item, index = 0) {
+  if (!item) return `file-${index + 1}`
+  if (typeof item === 'string') return `file-${index + 1}`
+  return item.name || item.originalFilename || item.fileName || item.uidFilename || `file-${index + 1}`
+}
+
+function normalizeResponseItem(item, index) {
+  if (typeof item === 'string') {
+    return {
+      name: `file-${currentList.value.length + index + 1}`,
+      url: item,
+    }
+  }
+  return Object.assign({}, item, {
+    url: item.url || item.downloadURL || item.previewURL || item.previewUrl || '',
+    name: item.name || item.originalFilename || item.fileName || item.uidFilename || `file-${currentList.value.length + index + 1}`,
+  })
+}
+
+function extractResponseArray(response) {
+  if (Array.isArray(response)) return response
+  if (Array.isArray(response?.data)) return response.data
+  if (Array.isArray(response?.data?.data)) return response.data.data
+  if (Array.isArray(response?.payload)) return response.payload
+  if (Array.isArray(response?.payload?.data)) return response.payload.data
+  if (Array.isArray(response?.rows)) return response.rows
+  if (Array.isArray(response?.result)) return response.result
+  return []
+}
+
+function validateFile(rawFile) {
+  const extension = rawFile.name.includes('.')
+    ? rawFile.name.slice(rawFile.name.lastIndexOf('.') + 1).toLowerCase()
+    : ''
+
+  if (props.fileType.length) {
+    const isValidType = props.fileType.some((type) => {
+      const normalizedType = String(type).toLowerCase()
+      return rawFile.type.toLowerCase().includes(normalizedType) || extension === normalizedType
+    })
+    if (!isValidType) {
+      proxy.$modal.msgError(`璇蜂笂浼� ${props.fileType.join('/')} 鏍煎紡鐨勬枃浠禶)
+      return false
+    }
+  }
+
+  const isWithinSize = rawFile.size / 1024 / 1024 <= props.fileSize
+  if (!isWithinSize) {
+    proxy.$modal.msgError(`鏂囦欢澶у皬涓嶈兘瓒呰繃 ${props.fileSize}MB`)
+    return false
+  }
+
+  return true
+}
+
+function scheduleUpload(uploadFiles) {
+  clearTimeout(uploadQueueTimer.value)
+  uploadQueueTimer.value = setTimeout(() => {
+    const readyFiles = uploadFiles.filter((file) => file.raw && !queuedUidSet.value.has(file.uid))
+    if (!readyFiles.length) return
+
+    const remainCount = props.limit - currentList.value.length
+    if (remainCount <= 0) {
+      proxy.$modal.msgError(`鏈�澶氫笂浼� ${props.limit} 涓枃浠禶)
+      uploadRef.value?.clearFiles()
+      return
+    }
+
+    const selectedFiles = readyFiles.slice(0, remainCount)
+    if (selectedFiles.length < readyFiles.length) {
+      proxy.$modal.msgWarning(`鏈�澶氫笂浼� ${props.limit} 涓枃浠讹紝瓒呭嚭閮ㄥ垎宸插拷鐣)
+    }
+
+    selectedFiles.forEach((file) => queuedUidSet.value.add(file.uid))
+    uploadSelectedFiles(selectedFiles)
+  }, 0)
+}
+
+async function uploadSelectedFiles(files) {
+  const validFiles = files.filter((file) => validateFile(file.raw))
+  const invalidFiles = files.filter((file) => !validFiles.includes(file))
+
+  invalidFiles.forEach((file) => queuedUidSet.value.delete(file.uid))
+
+  if (!validFiles.length) {
+    uploadRef.value?.clearFiles()
+    return
+  }
+
+  const formData = new FormData()
+  validFiles.forEach((file) => {
+    formData.append(props.uploadFieldName, file.raw)
+  })
+
+  uploading.value = true
+  proxy.$modal.loading('鏂囦欢涓婁紶涓紝璇风◢鍊�...')
+
+  try {
+    const response = await uploadFile(formData)
+    const responseList = extractResponseArray(response).map((item, index) => normalizeResponseItem(item, index))
+
+    if (!responseList.length) {
+      proxy.$modal.msgError('涓婁紶鎺ュ彛鏈繑鍥炴暟缁勬暟鎹�')
+      return
+    }
+
+    currentList.value = [...currentList.value, ...responseList]
+    proxy.$modal.msgSuccess('涓婁紶鎴愬姛')
+  } catch (error) {
+    proxy.$modal.msgError(error?.message || '涓婁紶澶辫触')
+  } finally {
+    validFiles.forEach((file) => queuedUidSet.value.delete(file.uid))
+    invalidFiles.forEach((file) => queuedUidSet.value.delete(file.uid))
+    uploadRef.value?.clearFiles()
+    uploading.value = false
+    proxy.$modal.closeLoading()
+  }
+}
+
+function handleChange(file, uploadFiles) {
+  if (props.disabled || uploading.value) return
+  scheduleUpload(uploadFiles)
+}
+
+function handleRemove(file) {
+  const targetUrl = file.url || getItemUrl(file.rawData)
+  const nextList = currentList.value.filter((item, index) => {
+    const itemUrl = getItemUrl(item)
+    const itemName = getItemName(item, index)
+    return !(itemUrl === targetUrl && itemName === file.name)
+  })
+  currentList.value = nextList
+}
+
+function handleExceed() {
+  proxy.$modal.msgError(`鏈�澶氫笂浼� ${props.limit} 涓枃浠禶)
+}
+
+function openFile(file) {
+  const fileUrl = file.url || getItemUrl(file.rawData)
+  if (!fileUrl) return
+  window.open(fileUrl, '_blank')
+}
+
+onBeforeUnmount(() => {
+  clearTimeout(uploadQueueTimer.value)
+})
+</script>
+
+<template>
+  <div class="attachment-upload-file">
+    <el-upload
+      ref="uploadRef"
+      drag
+      :auto-upload="false"
+      :multiple="true"
+      :show-file-list="true"
+      :file-list="displayFileList"
+      :disabled="disabled || uploading"
+      :limit="limit"
+      :on-change="handleChange"
+      :on-remove="handleRemove"
+      :on-exceed="handleExceed"
+      :on-preview="openFile"
+    >
+      <el-icon class="upload-drag-icon"><UploadFilled /></el-icon>
+      <div class="el-upload__text">
+        灏嗘枃浠舵嫋鍒版澶勶紝鎴� <em>{{ buttonText }}</em>
+      </div>
+      <div class="upload-tip">{{ uploadTip }}</div>
+    </el-upload>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.attachment-upload-file {
+  width: 100%;
+}
+
+.upload-drag-icon {
+  font-size: 40px;
+  color: var(--el-text-color-secondary);
+}
+
+.upload-tip {
+  margin-top: 8px;
+  color: var(--el-text-color-secondary);
+  font-size: 12px;
+}
+</style>
diff --git a/src/components/AttachmentUpload/image/index.vue b/src/components/AttachmentUpload/image/index.vue
new file mode 100644
index 0000000..8243f9c
--- /dev/null
+++ b/src/components/AttachmentUpload/image/index.vue
@@ -0,0 +1,335 @@
+<script setup>
+import {Plus} from '@element-plus/icons-vue'
+import {uploadFile} from '@/api/basicData/common'
+
+const props = defineProps({
+  fileList: {
+    type: Array,
+    default: () => [],
+  },
+  index: {
+    type: Number,
+    default: -1,
+  },
+  childrenKey: {
+    type: String,
+    default: 'images',
+  },
+  limit: {
+    type: Number,
+    default: 10,
+  },
+  fileSize: {
+    type: Number,
+    default: 10,
+  },
+  fileType: {
+    type: Array,
+    default: () => ['png', 'jpg', 'jpeg', 'webp'],
+  },
+  buttonText: {
+    type: String,
+    default: '涓婁紶鍥剧墖',
+  },
+  disabled: {
+    type: Boolean,
+    default: false,
+  },
+  uploadFieldName: {
+    type: String,
+    default: 'files',
+  },
+})
+
+const emit = defineEmits(['update:fileList', 'change'])
+const {proxy} = getCurrentInstance()
+
+const uploadRef = ref()
+const previewVisible = ref(false)
+const previewUrl = ref('')
+const uploadQueueTimer = ref(null)
+const uploading = ref(false)
+const queuedUidSet = ref(new Set())
+
+const currentList = computed({
+  get() {
+    if (props.index > -1) {
+      const row = props.fileList?.[props.index]
+      return Array.isArray(row?.[props.childrenKey]) ? row[props.childrenKey] : []
+    }
+    return Array.isArray(props.fileList) ? props.fileList : []
+  },
+  set(value) {
+    const nextList = Array.isArray(value) ? value : []
+    if (props.index > -1) {
+      const nextModelValue = Array.isArray(props.fileList) ? [...props.fileList] : []
+      const currentRow = nextModelValue[props.index] || {}
+      nextModelValue[props.index] = {
+        ...currentRow,
+        [props.childrenKey]: nextList,
+      }
+      emit('update:fileList', nextModelValue)
+      emit('change', nextList, nextModelValue)
+      return
+    }
+    emit('update:fileList', nextList)
+    emit('change', nextList, nextList)
+  },
+})
+
+const displayFileList = computed(() => {
+  return currentList.value.map((item, index) => ({
+    uid: getItemUid(item, index),
+    name: getItemName(item, index),
+    url: getItemUrl(item),
+    status: 'success',
+    rawData: item,
+  }))
+})
+
+const uploadTip = computed(() => {
+  return `鏀寔 ${props.fileType.join('/')}锛屽崟寮犱笉瓒呰繃 ${props.fileSize}MB锛屾渶澶氫笂浼� ${props.limit} 寮犲浘鐗嘸
+})
+
+function getItemUid(item, index) {
+  if (item?.id !== undefined && item?.id !== null) return `${item.id}`
+  return `${getItemName(item, index)}-${getItemUrl(item) || index}`
+}
+
+function getItemUrl(item) {
+  if (!item) return ''
+  if (typeof item === 'string') return item
+  return item.url || item.previewURL || ''
+}
+
+function getItemName(item, index = 0) {
+  if (!item) return `image-${index + 1}`
+  if (typeof item === 'string') return `image-${index + 1}`
+  return item.name || item.fileName || item.originalFilename || `image-${index + 1}`
+}
+
+function normalizeResponseItem(item, index) {
+  if (typeof item === 'string') {
+    return {
+      name: `image-${currentList.value.length + index + 1}`,
+      url: item,
+    }
+  }
+  return Object.assign({}, item, {
+    url: item.url || item.previewURL || item.previewUrl || '',
+    name: item.name || item.originalFilename || item.fileName || `image-${currentList.value.length + index + 1}`,
+  })
+}
+
+function extractResponseArray(response) {
+  if (Array.isArray(response)) return response
+  if (Array.isArray(response?.data)) return response.data
+  if (Array.isArray(response?.data?.data)) return response.data.data
+  if (Array.isArray(response?.payload)) return response.payload
+  if (Array.isArray(response?.payload?.data)) return response.payload.data
+  if (Array.isArray(response?.rows)) return response.rows
+  if (Array.isArray(response?.result)) return response.result
+  return []
+}
+
+function validateFile(rawFile) {
+  let isValidType = false
+  const extension = rawFile.name.includes('.')
+      ? rawFile.name.slice(rawFile.name.lastIndexOf('.') + 1).toLowerCase()
+      : ''
+
+  if (props.fileType.length) {
+    isValidType = props.fileType.some((type) => {
+      const normalizedType = String(type).toLowerCase()
+      return rawFile.type.toLowerCase().includes(normalizedType) || extension === normalizedType
+    })
+  } else {
+    isValidType = rawFile.type.includes('image')
+  }
+
+  if (!isValidType) {
+    proxy.$modal.msgError(`璇蜂笂浼� ${props.fileType.join('/')} 鏍煎紡鐨勫浘鐗嘸)
+    return false
+  }
+
+  const isWithinSize = rawFile.size / 1024 / 1024 <= props.fileSize
+  if (!isWithinSize) {
+    proxy.$modal.msgError(`鍥剧墖澶у皬涓嶈兘瓒呰繃 ${props.fileSize}MB`)
+    return false
+  }
+
+  return true
+}
+
+function scheduleUpload(uploadFiles) {
+  clearTimeout(uploadQueueTimer.value)
+  uploadQueueTimer.value = setTimeout(() => {
+    const readyFiles = uploadFiles.filter((file) => file.raw && !queuedUidSet.value.has(file.uid))
+    if (!readyFiles.length) return
+
+    const remainCount = props.limit - currentList.value.length
+    if (remainCount <= 0) {
+      proxy.$modal.msgError(`鏈�澶氫笂浼� ${props.limit} 寮犲浘鐗嘸)
+      uploadRef.value?.clearFiles()
+      return
+    }
+
+    const selectedFiles = readyFiles.slice(0, remainCount)
+    if (selectedFiles.length < readyFiles.length) {
+      proxy.$modal.msgWarning(`鏈�澶氫笂浼� ${props.limit} 寮犲浘鐗囷紝瓒呭嚭閮ㄥ垎宸插拷鐣)
+    }
+
+    selectedFiles.forEach((file) => queuedUidSet.value.add(file.uid))
+    uploadSelectedFiles(selectedFiles)
+  }, 0)
+}
+
+async function uploadSelectedFiles(files) {
+  const validFiles = files.filter((file) => validateFile(file.raw))
+  const invalidFiles = files.filter((file) => !validFiles.includes(file))
+
+  invalidFiles.forEach((file) => queuedUidSet.value.delete(file.uid))
+
+  if (!validFiles.length) {
+    uploadRef.value?.clearFiles()
+    return
+  }
+
+  const formData = new FormData()
+  validFiles.forEach((file) => {
+    formData.append(props.uploadFieldName, file.raw)
+  })
+
+  uploading.value = true
+  proxy.$modal.loading('鍥剧墖涓婁紶涓紝璇风◢鍊�...')
+
+  try {
+    const response = await uploadFile(formData)
+    const responseList = extractResponseArray(response).map((item, index) => normalizeResponseItem(item, index))
+
+    if (!responseList.length) {
+      proxy.$modal.msgError('涓婁紶鎺ュ彛鏈繑鍥炴暟缁勬暟鎹�')
+      return
+    }
+    console.log('responseList', responseList)
+
+    currentList.value = [...currentList.value, ...responseList]
+    console.log('currentList.value', currentList.value)
+    proxy.$modal.msgSuccess('涓婁紶鎴愬姛')
+  } catch (error) {
+    proxy.$modal.msgError(error?.message || '涓婁紶澶辫触')
+  } finally {
+    validFiles.forEach((file) => queuedUidSet.value.delete(file.uid))
+    invalidFiles.forEach((file) => queuedUidSet.value.delete(file.uid))
+    uploadRef.value?.clearFiles()
+    uploading.value = false
+    proxy.$modal.closeLoading()
+  }
+}
+
+function handleChange(file, uploadFiles) {
+  if (props.disabled || uploading.value) return
+  scheduleUpload(uploadFiles)
+}
+
+function handleRemove(file) {
+  const targetUrl = file.url || getItemUrl(file.rawData)
+  const nextList = currentList.value.filter((item, index) => {
+    const itemUrl = getItemUrl(item)
+    const itemName = getItemName(item, index)
+    return !(itemUrl === targetUrl && itemName === file.name)
+  })
+  currentList.value = nextList
+}
+
+function handlePreview(file) {
+  previewUrl.value = file.url || getItemUrl(file.rawData)
+  previewVisible.value = true
+}
+
+function handleExceed() {
+  proxy.$modal.msgError(`鏈�澶氫笂浼� ${props.limit} 寮犲浘鐗嘸)
+}
+
+onBeforeUnmount(() => {
+  clearTimeout(uploadQueueTimer.value)
+})
+</script>
+
+<template>
+  <div class="attachment-upload-image">
+    <el-upload
+        ref="uploadRef"
+        :auto-upload="false"
+        :multiple="true"
+        :show-file-list="true"
+        :file-list="displayFileList"
+        list-type="picture-card"
+        accept="image/*"
+        :disabled="disabled || uploading"
+        :limit="limit"
+        :on-change="handleChange"
+        :on-remove="handleRemove"
+        :on-preview="handlePreview"
+        :on-exceed="handleExceed"
+    >
+      <div class="upload-trigger">
+        <el-icon>
+          <Plus/>
+        </el-icon>
+        <span>{{ buttonText }}</span>
+      </div>
+    </el-upload>
+
+    <div class="upload-tip">
+      {{ uploadTip }}
+    </div>
+
+    <el-dialog v-model="previewVisible" title="鍥剧墖棰勮" width="720px" append-to-body>
+      <img class="preview-image" :src="previewUrl" alt="preview"/>
+    </el-dialog>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.attachment-upload-image {
+  width: 100%;
+}
+
+.upload-trigger {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  gap: 6px;
+  color: var(--el-text-color-secondary);
+  font-size: 12px;
+  line-height: 1.2;
+}
+
+.upload-tip {
+  margin-top: 8px;
+  color: var(--el-text-color-secondary);
+  font-size: 12px;
+}
+
+.preview-image {
+  display: block;
+  max-width: 100%;
+  margin: 0 auto;
+}
+
+:deep(.el-upload-list--picture-card) {
+  margin: 0;
+}
+
+:deep(.el-upload--picture-card) {
+  width: 132px;
+  height: 132px;
+}
+
+:deep(.el-upload-list--picture-card .el-upload-list__item) {
+  width: 132px;
+  height: 132px;
+}
+</style>
diff --git a/src/components/Breadcrumb/index.vue b/src/components/Breadcrumb/index.vue
index 5385ec4..0ff79ef 100644
--- a/src/components/Breadcrumb/index.vue
+++ b/src/components/Breadcrumb/index.vue
@@ -85,15 +85,34 @@
 </script>
 
 <style lang='scss' scoped>
-.app-breadcrumb.el-breadcrumb {
-  display: inline-block;
-  font-size: 14px;
-  line-height: 50px;
-  margin-left: 8px;
-
-  .no-redirect {
-    color: #002FA7;
-    cursor: text;
-  }
-}
-</style>
\ No newline at end of file
+.app-breadcrumb.el-breadcrumb {
+  display: inline-block;
+  font-size: 14px;
+  line-height: 56px;
+  margin-left: 8px;
+
+  :deep(.el-breadcrumb__inner) {
+    color: var(--text-secondary);
+    font-weight: 500;
+    transition: color 0.2s ease;
+  }
+
+  :deep(.el-breadcrumb__separator) {
+    color: var(--text-tertiary);
+  }
+
+  a {
+    color: var(--text-secondary);
+
+    &:hover {
+      color: var(--current-color);
+    }
+  }
+
+  .no-redirect {
+    color: var(--current-color);
+    font-weight: 600;
+    cursor: text;
+  }
+}
+</style>
diff --git a/src/components/Dialog/FileList.vue b/src/components/Dialog/FileList.vue
new file mode 100644
index 0000000..6e0ca23
--- /dev/null
+++ b/src/components/Dialog/FileList.vue
@@ -0,0 +1,245 @@
+<template>
+  <el-dialog v-model="isShow"
+             :title="title"
+             :width="width"
+             @close="handleClose"
+             class="attachment-dialog">
+    <!-- 宸ュ叿鏍� -->
+    <div v-if="editable"
+         class="toolbar">
+      <el-button type="primary"
+                 size="small"
+                 @click="handleUpload">
+        涓婁紶闄勪欢
+      </el-button>
+    </div>
+    <!-- 涓婁紶缁勪欢寮圭獥 -->
+    <el-dialog v-model="uploadDialogVisible"
+               title="涓婁紶闄勪欢"
+               width="50%"
+               @close="closeUpload">
+      <AttachmentUpload v-model:file-list="newFileList" />
+      <template #footer>
+        <el-button @click="saveUpload">淇濆瓨</el-button>
+        <el-button @click="closeUpload">鍏抽棴</el-button>
+      </template>
+    </el-dialog>
+    <!-- 鏂囦欢鍒楄〃琛ㄦ牸 -->
+    <div class="table-container">
+      <el-table :data="tableData"
+                border
+                class="attachment-table"
+                :height="tableData.length > 0 ? 'auto' : '120px'">
+        <el-table-column label="闄勪欢鍚嶇О"
+                         prop="originalFilename"
+                         show-overflow-tooltip />
+        <el-table-column v-if="showActions"
+                         fixed="right"
+                         label="鎿嶄綔"
+                         :width="120"
+                         align="center">
+          <template #default="scope">
+            <el-button link
+                       type="primary"
+                       size="small"
+                       class="download-link"
+                       @click="downloadFile(scope.row.downloadURL)">
+              涓嬭浇
+            </el-button>
+            <el-button v-if="editable"
+                       link
+                       type="danger"
+                       size="small"
+                       @click="handleDelete(scope.row)">
+              鍒犻櫎
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+  </el-dialog>
+</template>
+
+<script setup>
+  import { ref, computed, getCurrentInstance, onMounted, watch } from "vue";
+  import AttachmentUpload from "@/components/AttachmentUpload/file/index.vue";
+  import {
+    attachmentList,
+    deleteAttachment,
+    createAttachment,
+  } from "@/api/basicData/storageAttachment.js";
+
+  const props = defineProps({
+    visible: {
+      type: Boolean,
+      required: true,
+    },
+    recordType: {
+      type: String,
+      default: "",
+      required: true,
+    },
+    recordId: {
+      type: Number,
+      default: 0,
+      required: true,
+    },
+    title: {
+      type: String,
+      default: "闄勪欢",
+    },
+    width: {
+      type: String,
+      default: "50%",
+    },
+    showActions: {
+      type: Boolean,
+      default: true,
+    },
+    editable: {
+      type: Boolean,
+      default: true,
+    },
+  });
+
+  const emit = defineEmits(["close", "download", "upload", "delete"]);
+
+  const { proxy } = getCurrentInstance();
+  const tableData = ref([]);
+  const uploadDialogVisible = ref(false);
+  const newFileList = ref([]);
+
+  const isShow = computed({
+    get() {
+      return props.visible;
+    },
+    set(val) {
+      emit("update:visible", val);
+    },
+  });
+
+  const handleClose = () => {
+    isShow.value = false;
+  };
+
+  const handleUpload = () => {
+    uploadDialogVisible.value = true;
+  };
+
+  const saveUpload = async () => {
+    // 妫�鏌ユ槸鍚︽湁鏂颁笂浼犵殑鏂囦欢
+    if (newFileList.value.length > 0) {
+      try {
+        await createAttachment({
+          application: "file",
+          recordType: props.recordType,
+          recordId: props.recordId,
+          storageBlobDTOs: [...newFileList.value, ...tableData.value],
+        });
+        newFileList.value = [];
+        // 鍒锋柊鍒楄〃
+        setList();
+      } catch (error) {
+        proxy?.$modal?.msgError("涓婁紶澶辫触");
+      }
+    }
+    uploadDialogVisible.value = false;
+  };
+
+  const closeUpload = () => {
+    newFileList.value = [];
+    uploadDialogVisible.value = false;
+  };
+
+  const handleDelete = async (row, index) => {
+    try {
+      await deleteAttachment([row.storageAttachmentId]);
+      proxy?.$modal?.msgSuccess("鍒犻櫎鎴愬姛");
+      setList();
+    } catch (error) {
+      proxy?.$modal?.msgError("鍒犻櫎澶辫触");
+    }
+  };
+
+  const setList = () => {
+    attachmentList({
+      recordType: props.recordType,
+      recordId: props.recordId,
+    }).then(res => {
+      if (res && res.data) {
+        tableData.value = res.data || [];
+      }
+    });
+  };
+
+  const downloadFile = url => {
+    window.open(url, "_blank");
+  };
+  onMounted(() => {
+    setList();
+  });
+</script>
+
+<style scoped>
+  .attachment-dialog {
+    border-radius: 12px;
+  }
+
+  .toolbar {
+    margin-bottom: 16px;
+    text-align: right;
+  }
+
+  .table-container {
+    max-height: 40vh;
+    overflow-y: auto;
+    min-height: 120px;
+    padding-bottom: 16px;
+    box-sizing: border-box;
+    will-change: scroll-position;
+    transform: translateZ(0);
+    -webkit-overflow-scrolling: touch;
+  }
+
+  :deep(.el-table) {
+    margin-bottom: 0;
+  }
+
+  :deep(.el-table__body-wrapper) {
+    overflow-y: auto;
+    will-change: transform;
+    transform: translateZ(0);
+  }
+
+  :deep(.el-table__body tr) {
+    transition: none;
+  }
+
+  :deep(.el-dialog__footer) {
+    padding-top: 12px;
+    border-top: 1px solid #e9ecef;
+  }
+
+  .attachment-table {
+    border-radius: 8px;
+  }
+
+  :deep(.el-dialog__header) {
+    background-color: #f8f9fa;
+    border-bottom: 1px solid #e9ecef;
+    padding: 16px 20px;
+  }
+
+  :deep(.el-dialog__title) {
+    font-size: 16px;
+    font-weight: 600;
+  }
+
+  :deep(.el-dialog__body) {
+    padding: 16px 20px;
+  }
+
+  :deep(.el-table__empty-text) {
+    color: #999;
+  }
+</style>
\ No newline at end of file
diff --git a/src/components/Dialog/FileListDialog.vue b/src/components/Dialog/FileListDialog.vue
index fc82411..6fea795 100644
--- a/src/components/Dialog/FileListDialog.vue
+++ b/src/components/Dialog/FileListDialog.vue
@@ -77,6 +77,7 @@
                 @pagination="paginationSearch"
                 @change="handleChange" />
   </el-dialog>
+<!-- // todo 闄勪欢棰勮鐩稿叧 -->
   <filePreview v-if="showPreview"
                ref="filePreviewRef" />
 </template>
diff --git a/src/components/Editor/index.vue b/src/components/Editor/index.vue
index c283e42..8f0eebd 100644
--- a/src/components/Editor/index.vue
+++ b/src/components/Editor/index.vue
@@ -5,11 +5,11 @@
       :before-upload="handleBeforeUpload"
       :on-success="handleUploadSuccess"
       :on-error="handleUploadError"
-      name="file"
+      name="files"
       :show-file-list="false"
       :headers="headers"
       class="editor-img-uploader"
-      v-if="type == 'url'"
+      v-if="type === 'url'"
     >
       <i ref="uploadRef" class="editor-img-uploader"></i>
     </el-upload>
@@ -27,15 +27,15 @@
 </template>
 
 <script setup>
-import axios from "axios";
 import { QuillEditor } from "@vueup/vue-quill";
 import "@vueup/vue-quill/dist/vue-quill.snow.css";
 import { getToken } from "@/utils/auth";
+import {uploadPublicFile} from "@/api/basicData/common.js";
 
 const { proxy } = getCurrentInstance();
 
 const quillEditorRef = ref();
-const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 涓婁紶鐨勫浘鐗囨湇鍔″櫒鍦板潃
+const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/public/upload"); // 涓婁紶鐨勫浘鐗囨湇鍔″櫒鍦板潃
 const headers = ref({
   Authorization: "Bearer " + getToken(),
 });
@@ -157,21 +157,40 @@
 function handleUploadSuccess(res, file) {
   // 濡傛灉涓婁紶鎴愬姛
   if (res.code == 200) {
+    const imageUrl = resolveImageUrl(res);
+    if (!imageUrl) {
+      proxy.$modal.msgError("鏈幏鍙栧埌鍥剧墖鍦板潃");
+      return;
+    }
     // 鑾峰彇瀵屾枃鏈疄渚�
     let quill = toRaw(quillEditorRef.value).getQuill();
     // 鑾峰彇鍏夋爣浣嶇疆
-    let length = quill.selection.savedRange.index;
+    const selection = quill.getSelection(true);
+    const length = selection ? selection.index : quill.getLength();
     // 鎻掑叆鍥剧墖锛宺es.url涓烘湇鍔″櫒杩斿洖鐨勫浘鐗囬摼鎺ュ湴鍧�
-    quill.insertEmbed(
-      length,
-      "image",
-      import.meta.env.VITE_APP_BASE_API + res.fileName
-    );
+    quill.insertEmbed(length, "image", imageUrl);
     // 璋冩暣鍏夋爣鍒版渶鍚�
     quill.setSelection(length + 1);
   } else {
     proxy.$modal.msgError("鍥剧墖鎻掑叆澶辫触");
   }
+}
+
+function resolveImageUrl(res) {
+  if (!res) return "";
+  // 鍏煎鏂版帴鍙�: data[0].previewURL
+  const previewURL = res?.data?.[0]?.previewURL;
+  if (previewURL) {
+    return previewURL;
+  }
+  // 鍏煎鏃ф帴鍙�
+  if (res.url) {
+    return res.url;
+  }
+  if (res.fileName) {
+    return `${import.meta.env.VITE_APP_BASE_API}${res.fileName}`;
+  }
+  return "";
 }
 
 // 涓婁紶澶辫触澶勭悊
@@ -196,17 +215,10 @@
 
 function insertImage(file) {
   const formData = new FormData();
-  formData.append("file", file);
-  axios
-    .post(uploadUrl.value, formData, {
-      headers: {
-        "Content-Type": "multipart/form-data",
-        Authorization: headers.value.Authorization,
-      },
-    })
-    .then((res) => {
-      handleUploadSuccess(res.data);
-    });
+  formData.append("files", file);
+  uploadPublicFile(formData).then((res) => {
+    handleUploadSuccess(res)
+  })
 }
 </script>
 
diff --git a/src/components/ImagePreview/index.vue b/src/components/ImagePreview/index.vue
deleted file mode 100644
index 00212c5..0000000
--- a/src/components/ImagePreview/index.vue
+++ /dev/null
@@ -1,92 +0,0 @@
-<template>
-  <el-image
-    :src="`${realSrc}`"
-    fit="cover"
-    :style="`width:${realWidth};height:${realHeight};`"
-    :preview-src-list="realSrcList"
-    preview-teleported
-  >
-    <template #error>
-      <div class="image-slot">
-        <el-icon><picture-filled /></el-icon>
-      </div>
-    </template>
-  </el-image>
-</template>
-
-<script setup>
-import { isExternal } from "@/utils/validate"
-
-const props = defineProps({
-  src: {
-    type: String,
-    default: ""
-  },
-  width: {
-    type: [Number, String],
-    default: ""
-  },
-  height: {
-    type: [Number, String],
-    default: ""
-  }
-})
-
-const realSrc = computed(() => {
-  if (!props.src) {
-    return
-  }
-  let real_src = props.src.split(",")[0]
-  if (isExternal(real_src)) {
-    return real_src
-  }
-  return import.meta.env.VITE_APP_BASE_API + real_src
-})
-
-const realSrcList = computed(() => {
-  if (!props.src) {
-    return
-  }
-  let real_src_list = props.src.split(",")
-  let srcList = []
-  real_src_list.forEach(item => {
-    if (isExternal(item)) {
-      return srcList.push(item)
-    }
-    return srcList.push(import.meta.env.VITE_APP_BASE_API + item)
-  })
-  return srcList
-})
-
-const realWidth = computed(() =>
-  typeof props.width == "string" ? props.width : `${props.width}px`
-)
-
-const realHeight = computed(() =>
-  typeof props.height == "string" ? props.height : `${props.height}px`
-)
-</script>
-
-<style lang="scss" scoped>
-.el-image {
-  border-radius: 5px;
-  background-color: #ebeef5;
-  box-shadow: 0 0 5px 1px #ccc;
-  :deep(.el-image__inner) {
-    transition: all 0.3s;
-    cursor: pointer;
-    &:hover {
-      transform: scale(1.2);
-    }
-  }
-  :deep(.image-slot) {
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    width: 100%;
-    height: 100%;
-    color: #909399;
-    font-size: 30px;
-  }
-}
-</style>
diff --git a/src/components/ImageUpload/index.vue b/src/components/ImageUpload/index.vue
deleted file mode 100644
index 9670c72..0000000
--- a/src/components/ImageUpload/index.vue
+++ /dev/null
@@ -1,256 +0,0 @@
-<template>
-  <div class="component-upload-image">
-    <el-upload
-      multiple
-      :action="uploadImgUrl"
-      list-type="picture-card"
-      :on-success="handleUploadSuccess"
-      :before-upload="handleBeforeUpload"
-      :data="data"
-      :limit="limit"
-      :on-error="handleUploadError"
-      :on-exceed="handleExceed"
-      ref="imageUpload"
-      :before-remove="handleDelete"
-      :show-file-list="true"
-      :headers="headers"
-      :file-list="fileList"
-      :on-preview="handlePictureCardPreview"
-      :class="{ hide: fileList.length >= limit }"
-    >
-      <el-icon class="avatar-uploader-icon"><plus /></el-icon>
-    </el-upload>
-    <!-- 涓婁紶鎻愮ず -->
-    <div class="el-upload__tip" v-if="showTip">
-      璇蜂笂浼�
-      <template v-if="fileSize">
-        澶у皬涓嶈秴杩� <b style="color: #f56c6c">{{ fileSize }}MB</b>
-      </template>
-      <template v-if="fileType">
-        鏍煎紡涓� <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
-      </template>
-      鐨勬枃浠�
-    </div>
-
-    <el-dialog
-      v-model="dialogVisible"
-      title="棰勮"
-      width="800px"
-      append-to-body
-    >
-      <img
-        :src="dialogImageUrl"
-        style="display: block; max-width: 100%; margin: 0 auto"
-      />
-    </el-dialog>
-  </div>
-</template>
-
-<script setup>
-import { getToken } from "@/utils/auth";
-import { isExternal } from "@/utils/validate";
-import Sortable from "sortablejs";
-
-const props = defineProps({
-  modelValue: [String, Object, Array],
-  // 涓婁紶鎺ュ彛鍦板潃
-  action: {
-    type: String,
-    default: "/common/upload",
-  },
-  // 涓婁紶鎼哄甫鐨勫弬鏁�
-  data: {
-    type: Object,
-  },
-  // 鍥剧墖鏁伴噺闄愬埗
-  limit: {
-    type: Number,
-    default: 5,
-  },
-  // 澶у皬闄愬埗(MB)
-  fileSize: {
-    type: Number,
-    default: 5,
-  },
-  // 鏂囦欢绫诲瀷, 渚嬪['png', 'jpg', 'jpeg']
-  fileType: {
-    type: Array,
-    default: () => ["png", "jpg", "jpeg"],
-  },
-  // 鏄惁鏄剧ず鎻愮ず
-  isShowTip: {
-    type: Boolean,
-    default: true,
-  },
-  // 鎷栧姩鎺掑簭
-  drag: {
-    type: Boolean,
-    default: true,
-  },
-});
-
-const { proxy } = getCurrentInstance();
-const emit = defineEmits();
-const number = ref(0);
-const uploadList = ref([]);
-const dialogImageUrl = ref("");
-const dialogVisible = ref(false);
-const baseUrl = import.meta.env.VITE_APP_BASE_API;
-const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + props.action); // 涓婁紶鐨勫浘鐗囨湇鍔″櫒鍦板潃
-const headers = ref({ Authorization: "Bearer " + getToken() });
-const fileList = ref([]);
-const showTip = computed(
-  () => props.isShowTip && (props.fileType || props.fileSize)
-);
-
-watch(
-  () => props.modelValue,
-  (val) => {
-    if (val) {
-      // 棣栧厛灏嗗�艰浆涓烘暟缁�
-      const list = Array.isArray(val) ? val : props.modelValue.split(",");
-      // 鐒跺悗灏嗘暟缁勮浆涓哄璞℃暟缁�
-      fileList.value = list.map((item) => {
-        if (typeof item === "string") {
-          if (item.indexOf(baseUrl) === -1 && !isExternal(item)) {
-            item = { name: baseUrl + item, url: baseUrl + item };
-          } else {
-            item = { name: item, url: item };
-          }
-        }
-        return item;
-      });
-    } else {
-      fileList.value = [];
-      return [];
-    }
-  },
-  { deep: true, immediate: true }
-);
-
-// 涓婁紶鍓峫oading鍔犺浇
-function handleBeforeUpload(file) {
-  let isImg = false;
-  if (props.fileType.length) {
-    let fileExtension = "";
-    if (file.name.lastIndexOf(".") > -1) {
-      fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
-    }
-    isImg = props.fileType.some((type) => {
-      if (file.type.indexOf(type) > -1) return true;
-      if (fileExtension && fileExtension.indexOf(type) > -1) return true;
-      return false;
-    });
-  } else {
-    isImg = file.type.indexOf("image") > -1;
-  }
-  if (!isImg) {
-    proxy.$modal.msgError(
-      `鏂囦欢鏍煎紡涓嶆纭紝璇蜂笂浼�${props.fileType.join("/")}鍥剧墖鏍煎紡鏂囦欢!`
-    );
-    return false;
-  }
-  if (file.name.includes(",")) {
-    proxy.$modal.msgError("鏂囦欢鍚嶄笉姝g‘锛屼笉鑳藉寘鍚嫳鏂囬�楀彿!");
-    return false;
-  }
-  if (props.fileSize) {
-    const isLt = file.size / 1024 / 1024 < props.fileSize;
-    if (!isLt) {
-      proxy.$modal.msgError(`涓婁紶澶村儚鍥剧墖澶у皬涓嶈兘瓒呰繃 ${props.fileSize} MB!`);
-      return false;
-    }
-  }
-  proxy.$modal.loading("姝e湪涓婁紶鍥剧墖锛岃绋嶅��...");
-  number.value++;
-}
-
-// 鏂囦欢涓暟瓒呭嚭
-function handleExceed() {
-  proxy.$modal.msgError(`涓婁紶鏂囦欢鏁伴噺涓嶈兘瓒呰繃 ${props.limit} 涓�!`);
-}
-
-// 涓婁紶鎴愬姛鍥炶皟
-function handleUploadSuccess(res, file) {
-  if (res.code === 200) {
-    uploadList.value.push({ name: res.fileName, url: res.fileName });
-    uploadedSuccessfully();
-  } else {
-    number.value--;
-    proxy.$modal.closeLoading();
-    proxy.$modal.msgError(res.msg);
-    proxy.$refs.imageUpload.handleRemove(file);
-    uploadedSuccessfully();
-  }
-}
-
-// 鍒犻櫎鍥剧墖
-function handleDelete(file) {
-  const findex = fileList.value.map((f) => f.name).indexOf(file.name);
-  if (findex > -1 && uploadList.value.length === number.value) {
-    fileList.value.splice(findex, 1);
-    emit("update:modelValue", listToString(fileList.value));
-    return false;
-  }
-}
-
-// 涓婁紶缁撴潫澶勭悊
-function uploadedSuccessfully() {
-  if (number.value > 0 && uploadList.value.length === number.value) {
-    fileList.value = fileList.value
-      .filter((f) => f.url !== undefined)
-      .concat(uploadList.value);
-    uploadList.value = [];
-    number.value = 0;
-    emit("update:modelValue", listToString(fileList.value));
-    proxy.$modal.closeLoading();
-  }
-}
-
-// 涓婁紶澶辫触
-function handleUploadError() {
-  proxy.$modal.msgError("涓婁紶鍥剧墖澶辫触");
-  proxy.$modal.closeLoading();
-}
-
-// 棰勮
-function handlePictureCardPreview(file) {
-  dialogImageUrl.value = file.url;
-  dialogVisible.value = true;
-}
-
-// 瀵硅薄杞垚鎸囧畾瀛楃涓插垎闅�
-function listToString(list, separator) {
-  let strs = "";
-  separator = separator || ",";
-  for (let i in list) {
-    if (undefined !== list[i].url && list[i].url.indexOf("blob:") !== 0) {
-      strs += list[i].url.replace(baseUrl, "") + separator;
-    }
-  }
-  return strs != "" ? strs.substr(0, strs.length - 1) : "";
-}
-
-// 鍒濆鍖栨嫋鎷芥帓搴�
-onMounted(() => {
-  if (props.drag) {
-    nextTick(() => {
-      const element = document.querySelector(".el-upload-list");
-      Sortable.create(element, {
-        onEnd: (evt) => {
-          const movedItem = fileList.value.splice(evt.oldIndex, 1)[0];
-          fileList.value.splice(evt.newIndex, 0, movedItem);
-          emit("update:modelValue", listToString(fileList.value));
-        },
-      });
-    });
-  }
-});
-</script>
-
-<style scoped lang="scss">
-// .el-upload--picture-card 鎺у埗鍔犲彿閮ㄥ垎
-:deep(.hide .el-upload--picture-card) {
-  display: none;
-}
-</style>
diff --git a/src/components/PIMTable/PIMTable.vue b/src/components/PIMTable/PIMTable.vue
index a418280..f99b1eb 100644
--- a/src/components/PIMTable/PIMTable.vue
+++ b/src/components/PIMTable/PIMTable.vue
@@ -1,82 +1,76 @@
 <template>
-  <el-table
-    ref="multipleTable"
-    v-loading="tableLoading"
-    :border="border"
-    :data="tableData"
-    :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
-    :height="height"
-    :highlight-current-row="highlightCurrentRow"
-    :row-class-name="rowClassName"
-    :row-style="rowStyle"
-    :row-key="rowKey"
-    :style="tableStyle"
-    tooltip-effect="dark"
-    :expand-row-keys="expandRowKeys"
-    :show-summary="isShowSummary"
-    :summary-method="summaryMethod"
-    @row-click="rowClick"
-    @current-change="currentChange"
-    @selection-change="handleSelectionChange"
-    @expand-change="expandChange"
-    class="lims-table"
-  >
-    <el-table-column
-      align="center"
-      type="selection"
-      width="55"
-      v-if="isSelection"
-    />
-    <el-table-column align="center" label="搴忓彿" type="index" width="60" />
-
-    <el-table-column
-      v-for="(item, index) in column"
-      :key="index"
-      :column-key="item.columnKey"
-      :filter-method="item.filterHandler"
-      :filter-multiple="item.filterMultiple"
-      :filtered-value="item.filteredValue"
-      :filters="item.filters"
-      :fixed="item.fixed"
-      :label="item.label"
-      :prop="item.prop"
-      :show-overflow-tooltip="item.dataType !== 'action' && item.dataType !== 'slot'"
-      :align="item.align"
-      :sortable="!!item.sortable"
-      :type="item.type"
-      :width="item.width"
-    >
+  <el-table ref="multipleTable"
+            v-loading="tableLoading"
+            :border="border"
+            :data="tableData"
+            :header-cell-style="mergedHeaderCellStyle"
+            :height="height"
+            :highlight-current-row="highlightCurrentRow"
+            :row-class-name="rowClassName"
+            :row-style="rowStyle"
+            :row-key="rowKey"
+            :style="tableStyle"
+            tooltip-effect="dark"
+            :expand-row-keys="expandRowKeys"
+            :show-summary="isShowSummary"
+            :summary-method="summaryMethod"
+            @row-click="rowClick"
+            @current-change="currentChange"
+            @selection-change="handleSelectionChange"
+            @expand-change="expandChange"
+            class="lims-table">
+    <el-table-column align="center"
+                     type="selection"
+                     :selectable="selectable"
+                     width="55"
+                     v-if="isSelection" />
+    <el-table-column align="center"
+                     label="搴忓彿"
+                     type="index"
+                     width="60" />
+    <el-table-column v-for="(item, index) in column"
+                     :key="index"
+                     :column-key="item.columnKey"
+                     :filter-method="item.filterHandler"
+                     :filter-multiple="item.filterMultiple"
+                     :filtered-value="item.filteredValue"
+                     :filters="item.filters"
+                     :fixed="item.fixed"
+                     :label="item.label"
+                     :prop="item.prop"
+                     :show-overflow-tooltip="item.dataType !== 'action' && item.dataType !== 'slot'"
+                     :align="item.align"
+                     :sortable="!!item.sortable"
+                     :type="item.type"
+                     :width="item.width"
+                     :minWidth="item.minWidth">
       <template #header="scope">
-        <div class="pim-table-header-cell">
+        <div class="pim-table-header-cell"
+             :class="{ 'has-extra': item.headerSlot }">
           <div class="pim-table-header-title">
             {{ item.label }}
           </div>
-          <div v-if="item.headerSlot" class="pim-table-header-extra">
-            <slot :name="item.headerSlot" :column="scope.column" />
+          <div v-if="item.headerSlot"
+               class="pim-table-header-extra">
+            <slot :name="item.headerSlot"
+                  :column="scope.column" />
           </div>
         </div>
       </template>
-      <template
-        v-if="item.hasOwnProperty('colunmTemplate')"
-        #[item.colunmTemplate]="scope"
-      >
-        <slot
-          v-if="item.theadSlot"
-          :name="item.theadSlot"
-          :index="scope.$index"
-          :row="scope.row"
-        />
+      <template v-if="item.hasOwnProperty('colunmTemplate')"
+                #[item.colunmTemplate]="scope">
+        <slot v-if="item.theadSlot"
+              :name="item.theadSlot"
+              :index="scope.$index"
+              :row="scope.row" />
       </template>
-
       <template #default="scope">
         <!-- 鎻掓Ы -->
         <div v-if="item.dataType == 'slot'">
-          <slot
-            v-if="item.slot"
-            :index="scope.$index"
-            :name="item.slot"
-            :row="scope.row"
-          />
+          <slot v-if="item.slot"
+                :index="scope.$index"
+                :name="item.slot"
+                :row="scope.row" />
         </div>
         <!-- 杩涘害鏉� -->
         <div v-else-if="item.dataType == 'progress'">
@@ -84,127 +78,111 @@
         </div>
         <!-- 鍥剧墖 -->
         <div v-else-if="item.dataType == 'image'">
-          <img
-            :src="javaApi + '/img/' + scope.row[item.prop]"
-            alt=""
-            style="width: 40px; height: 40px; margin-top: 10px"
-          />
+          <img :src="javaApi + '/img/' + scope.row[item.prop]"
+               alt=""
+               style="width: 40px; height: 40px; margin-top: 10px" />
         </div>
-
         <!-- tag -->
         <div v-else-if="item.dataType == 'tag'">
-          <el-tag
-            v-if="
+          <el-tag v-if="
               typeof dataTypeFn(scope.row[item.prop], item.formatData) ===
               'string'
             "
-            :title="formatters(scope.row[item.prop], item.formatData)"
-            :type="formatType(scope.row[item.prop], item.formatType)"
-          >
+                  :title="formatters(scope.row[item.prop], item.formatData)"
+                  :type="formatType(scope.row[item.prop], item.formatType)">
             {{ formatters(scope.row[item.prop], item.formatData) }}
           </el-tag>
-
-          <el-tag
-            v-for="(tag, index) in dataTypeFn(
+          <el-tag v-for="(tag, index) in dataTypeFn(
               scope.row[item.prop],
               item.formatData
             )"
-            v-else-if="
+                  v-else-if="
               typeof dataTypeFn(scope.row[item.prop], item.formatData) ===
               'object'
             "
-            :key="index"
-            :title="formatters(scope.row[item.prop], item.formatData)"
-            :type="formatType(tag, item.formatType)"
-          >
+                  :key="index"
+                  :title="formatters(scope.row[item.prop], item.formatData)"
+                  :type="formatType(tag, item.formatType)">
             {{ item.tagGroup ? tag[item.tagGroup.label] ?? tag : tag }}
           </el-tag>
-
-          <el-tag
-            v-else
-            :title="formatters(scope.row[item.prop], item.formatData)"
-            :type="formatType(scope.row[item.prop], item.formatType)"
-          >
+          <el-tag v-else
+                  :title="formatters(scope.row[item.prop], item.formatData)"
+                  :type="formatType(scope.row[item.prop], item.formatType)">
             {{ formatters(scope.row[item.prop], item.formatData) }}
           </el-tag>
         </div>
-
         <!-- 鎸夐挳 -->
-        <div v-else-if="item.dataType == 'action'" @click.stop>
-          <template v-for="(o, key) in item.operation" :key="key">
-            <el-button
-              v-show="o.type != 'upload'"
-              v-if="o.showHide ? o.showHide(scope.row) : true"
-              :disabled="o.disabled ? o.disabled(scope.row) : false"
-              :plain="o.plain"
-              type="primary"
-              :style="{
-                color:
-                  o.name === '鍒犻櫎' || o.name === 'delete'
-                    ? '#f56c6c'
-                    : o.color,
+        <div v-else-if="item.dataType == 'action'"
+             @click.stop>
+          <template v-for="(o, key) in item.operation"
+                    :key="key">
+            <el-button v-show="o.type != 'upload'"
+                       v-if="o.showHide ? o.showHide(scope.row) : true"
+                       :disabled="isOperationDisabled(o, scope.row)"
+                       :plain="o.plain"
+                       type="primary"
+                       :style="{
+                color: getOperationColor(o, scope.row),
+                fontWeight: 'bold',
               }"
-              link
-              @click.stop="o.clickFun(scope.row)"
-              :key="key"
-            >
+                       link
+                       @click.stop="o.clickFun(scope.row)"
+                       :key="key">
               {{ o.name }}
             </el-button>
-            <el-upload
-              :action="
+            <el-upload :action="
                 javaApi +
                 o.url +
                 '?id=' +
                 (o.uploadIdFun ? o.uploadIdFun(scope.row) : scope.row.id)
               "
-              ref="uploadRef"
-              :multiple="o.multiple ? o.multiple : false"
-              :limit="1"
-              :disabled="o.disabled ? o.disabled(scope.row) : false"
-              :accept="
+                       ref="uploadRef"
+                       :multiple="o.multiple ? o.multiple : false"
+                       :limit="1"
+                       :disabled="isOperationDisabled(o, scope.row)"
+                       :accept="
                 o.accept
                   ? o.accept
                   : '.jpg,.jpeg,.png,.gif,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip,.rar'
               "
-              v-if="o.type == 'upload'"
-              style="display: inline-block; width: 50px"
-              v-show="o.showHide ? o.showHide(scope.row) : true"
-              :headers="uploadHeader"
-              :before-upload="(file) => beforeUpload(file, scope.$index)"
-              :on-change="
+                       v-if="o.type == 'upload'"
+                       style="display: inline-block; width: 50px"
+                       v-show="o.showHide ? o.showHide(scope.row) : true"
+                       :headers="uploadHeader"
+                       :before-upload="(file) => beforeUpload(file, scope.$index)"
+                       :on-change="
                 (file, fileList) => handleChange(file, fileList, scope.$index)
               "
-              :on-error="
+                       :on-error="
                 (error, file, fileList) =>
                   onError(error, file, fileList, scope.$index)
               "
-              :on-success="
+                       :on-success="
                 (response, file, fileList) =>
                   handleSuccessUp(response, file, fileList, scope.$index)
               "
-              :on-exceed="onExceed"
-              :show-file-list="false"
-            >
-              <el-button
-                link
-                type="primary"
-                :disabled="o.disabled ? o.disabled(scope.row) : false"
-                >{{ o.name }}</el-button
-              >
+                       :on-exceed="onExceed"
+                       :show-file-list="false">
+              <el-button link
+                         type="primary"
+                         :disabled="isOperationDisabled(o, scope.row)"
+                         :style="{
+                  color: getOperationColor(o, scope.row),
+                }">{{ o.name }}</el-button>
             </el-upload>
           </template>
         </div>
         <!-- 鍙偣鍑荤殑鏂囧瓧 -->
-        <div
-          v-else-if="item.dataType == 'link'"
-          class="cell link"
-          style="width: 100%"
-          @click="goLink(scope.row, item.linkMethod)"
-        >
+        <div v-else-if="item.dataType == 'link'"
+             class="cell link"
+             style="width: 100%"
+             @click="goLink(scope.row, item.linkMethod)">
           <span v-if="!item.formatData">{{ scope.row[item.prop] }}</span>
         </div>
         <!-- 榛樿绾睍绀烘暟鎹� -->
-        <div v-else class="cell" style="width: 100%">
+        <div v-else
+             class="cell"
+             style="width: 100%">
           <span v-if="!item.formatData">{{ scope.row[item.prop] }}</span>
           <span v-else>{{
             formatters(scope.row[item.prop], item.formatData)
@@ -213,244 +191,337 @@
       </template>
     </el-table-column>
   </el-table>
-  <pagination
-		v-if="isShowPagination"
-    :total="page.total"
-    :layout="page.layout"
-    :page="page.current"
-    :limit="page.size"
-    @pagination="paginationSearch"
-  />
+  <pagination v-if="isShowPagination"
+              :total="page.total"
+              :layout="page.layout"
+              :page="page.current"
+              :limit="page.size"
+              @pagination="paginationSearch" />
 </template>
 
 <script setup>
-import pagination from "./Pagination.vue";
-import { ref, inject, getCurrentInstance } from "vue";
-import { ElMessage } from "element-plus";
+  import pagination from "./Pagination.vue";
+  import { computed, ref, inject, getCurrentInstance } from "vue";
+  import { ElMessage } from "element-plus";
 
-// 鑾峰彇鍏ㄥ眬鐨� uploadHeader
-const { proxy } = getCurrentInstance();
-const uploadHeader = proxy.uploadHeader;
-const javaApi = proxy.javaApi;
+  // 鑾峰彇鍏ㄥ眬鐨� uploadHeader
+  const { proxy } = getCurrentInstance();
+  const uploadHeader = proxy.uploadHeader;
+  const javaApi = proxy.javaApi;
 
-const emit = defineEmits(["pagination", "expand-change", "selection-change", "row-click"]);
+  const emit = defineEmits([
+    "pagination",
+    "expand-change",
+    "selection-change",
+    "row-click",
+  ]);
 
-// Filters
-const typeFn = (val, row) => {
-  return typeof val === "function" ? val(row) : val;
-};
+  // Filters
+  const typeFn = (val, row) => {
+    return typeof val === "function" ? val(row) : val;
+  };
 
-const formatters = (val, format) => {
-  return typeof format === "function" ? format(val) : val;
-};
+  const formatters = (val, format) => {
+    return typeof format === "function" ? format(val) : val;
+  };
 
-// Props锛堜娇鐢� defineProps 鐨勯潪 TS 褰㈠紡锛�
-const props = defineProps({
-  tableLoading: {
-    type: Boolean,
-    default: false,
-  },
-  height: {
-    type: [Number, String],
-    default: "calc(100vh - 22em)",
-  },
-  expandRowKeys: {
-    type: Array,
-    default: () => [],
-  },
-  summaryMethod: {
-    type: Function,
-    default: () => {},
-  },
-  rowClick: {
-    type: Function,
-    default: () => {},
-  },
-  currentChange: {
-    type: Function,
-    default: () => {},
-  },
-  border: {
-    type: Boolean,
-    default: true,
-  },
-  isSelection: {
-    type: Boolean,
-    default: false,
-  },
-	isShowPagination: {
-    type: Boolean,
-    default: true,
-  },
-  isShowSummary: {
-    type: Boolean,
-    default: false,
-  },
-  highlightCurrentRow: {
-    type: Boolean,
-    default: false,
-  },
-  headerCellStyle: {
-    type: Object,
-    default: () => ({}),
-  },
-  column: {
-    type: Array,
-    default: () => [],
-  },
-  rowClassName: {
-    type: Function,
-    default: () => "",
-  },
-  rowStyle: {
-    type: [Object, Function],
-    default: () => ({}),
-  },
-  tableData: {
-    type: Array,
-    default: () => [],
-  },
-  rowKey: {
-    type: String,
-    default: 'id',
-  },
-  page: {
-    type: Object,
-    default: () => ({
-      total: 0,
-      current: 0,
-      size: 10,
-      layout: "total, sizes, prev, pager, next, jumper",
-    }),
-  },
-  total: {
-    type: Number,
-    default: 0,
-  },
-  tableStyle: {
-    type: [String, Object],
-    default: () => ({ width: "100%" }),
-  },
-});
+  // Props锛堜娇鐢� defineProps 鐨勯潪 TS 褰㈠紡锛�
+  const props = defineProps({
+    tableLoading: {
+      type: Boolean,
+      default: false,
+    },
+    height: {
+      type: [Number, String],
+      default: "calc(100vh - 22em)",
+    },
+    expandRowKeys: {
+      type: Array,
+      default: () => [],
+    },
+    summaryMethod: {
+      type: Function,
+      default: () => {},
+    },
+    rowClick: {
+      type: Function,
+      default: () => {},
+    },
+    currentChange: {
+      type: Function,
+      default: () => {},
+    },
+    border: {
+      type: Boolean,
+      default: true,
+    },
+    isSelection: {
+      type: Boolean,
+      default: false,
+    },
+    selectable: {
+      type: Function,
+      default: () => true,
+    },
+    isShowPagination: {
+      type: Boolean,
+      default: true,
+    },
+    isShowSummary: {
+      type: Boolean,
+      default: false,
+    },
+    highlightCurrentRow: {
+      type: Boolean,
+      default: false,
+    },
+    headerCellStyle: {
+      type: Object,
+      default: () => ({}),
+    },
+    column: {
+      type: Array,
+      default: () => [],
+    },
+    rowClassName: {
+      type: Function,
+      default: () => "",
+    },
+    rowStyle: {
+      type: [Object, Function],
+      default: () => ({}),
+    },
+    tableData: {
+      type: Array,
+      default: () => [],
+    },
+    rowKey: {
+      type: String,
+      default: "id",
+    },
+    page: {
+      type: Object,
+      default: () => ({
+        total: 0,
+        current: 0,
+        size: 10,
+        layout: "total, sizes, prev, pager, next, jumper",
+      }),
+    },
+    total: {
+      type: Number,
+      default: 0,
+    },
+    tableStyle: {
+      type: [String, Object],
+      default: () => ({ width: "100%" }),
+    },
+  });
 
-// Data
-const uploadRefs = ref([]);
-const currentFiles = ref({});
-const uploadKeys = ref({});
+  const mergedHeaderCellStyle = computed(() => ({
+    background: "var(--surface-soft)",
+    color: "var(--text-secondary)",
+    fontWeight: 600,
+    ...props.headerCellStyle,
+  }));
 
-const indexMethod = (index) => {
-  return (props.page.current - 1) * props.page.size + index + 1;
-};
+  // Data
+  const uploadRefs = ref([]);
+  const currentFiles = ref({});
+  const uploadKeys = ref({});
 
-// 鐐瑰嚮 link 浜嬩欢
-const goLink = (row, linkMethod) => {
-  if (!linkMethod) {
-    return ElMessage.warning("璇烽厤缃� link 浜嬩欢");
-  }
-  const parentMethod = getParentMethod(linkMethod);
-  if (typeof parentMethod === "function") {
-    parentMethod(row);
-  } else {
-    console.warn(`鐖剁粍浠朵腑鏈壘鍒版柟娉�: ${linkMethod}`);
-  }
-};
+  const indexMethod = index => {
+    return (props.page.current - 1) * props.page.size + index + 1;
+  };
 
-// 鑾峰彇鐖剁粍浠舵柟娉曪紙绀轰緥瀹炵幇锛�
-const getParentMethod = (methodName) => {
-  const parentMethods = inject("parentMethods", {});
-  return parentMethods[methodName];
-};
-
-const dataTypeFn = (val, format) => {
-  if (typeof format === "function") {
-    return format(val);
-  } else return val;
-};
-
-const formatType = (val, format) => {
-  if (typeof format === "function") {
-    return format(val);
-  } else return "";
-};
-
-// 鏂囦欢鍙樺寲澶勭悊
-const handleChange = (file, fileList, index) => {
-  if (fileList.length > 1) {
-    const earliestFile = fileList[0];
-    uploadRefs.value[index]?.handleRemove(earliestFile);
-  }
-  currentFiles.value[index] = file;
-};
-
-// 鏂囦欢涓婁紶鍓嶆牎楠�
-const beforeUpload = (rawFile, index) => {
-  currentFiles.value[index] = {};
-  if (rawfile.size > 1024 * 1024 * 10 * 10) {
-    ElMessage.error("涓婁紶鏂囦欢涓嶈秴杩�10M");
-    return false;
-  }
-  return true;
-};
-
-// 涓婁紶鎴愬姛
-const handleSuccessUp = (response, file, fileList, index) => {
-  if (response.code == 200) {
-    if (uploadRefs[index]) {
-      uploadRefs[index].clearFiles();
+  // 鐐瑰嚮 link 浜嬩欢
+  const goLink = (row, linkMethod) => {
+    if (!linkMethod) {
+      return ElMessage.warning("璇烽厤缃� link 浜嬩欢");
     }
-    currentFiles[index] = file;
-    ElMessage.success("涓婁紶鎴愬姛");
-    resetUploadComponent(index);
-  } else {
-    ElMessage.error(response.message);
-  }
-};
+    const parentMethod = getParentMethod(linkMethod);
+    if (typeof parentMethod === "function") {
+      parentMethod(row);
+    } else {
+      console.warn(`鐖剁粍浠朵腑鏈壘鍒版柟娉�: ${linkMethod}`);
+    }
+  };
 
-const resetUploadComponent = (index) => {
-  uploadKeys[index] = Date.now();
-};
+  // 鑾峰彇鐖剁粍浠舵柟娉曪紙绀轰緥瀹炵幇锛�
+  const getParentMethod = methodName => {
+    const parentMethods = inject("parentMethods", {});
+    return parentMethods[methodName];
+  };
 
-// 涓婁紶澶辫触
-const onError = (error, file, fileList, index) => {
-  ElMessage.error("鏂囦欢涓婁紶澶辫触锛岃閲嶈瘯");
-  if (uploadRefs.value[index]) {
-    uploadRefs.value[index].clearFiles();
-  }
-};
+  const dataTypeFn = (val, format) => {
+    if (typeof format === "function") {
+      return format(val);
+    } else return val;
+  };
+  const validTagTypes = ["primary", "success", "info", "warning", "danger"];
 
-// 鏂囦欢鏁伴噺瓒呴檺鎻愮ず
-const onExceed = () => {
-  ElMessage.warning("瓒呭嚭鏂囦欢涓暟");
-};
+  const formatType = (val, format) => {
+    const type = typeof format === "function" ? format(val) : undefined;
+    return validTagTypes.includes(type) ? type : undefined;
+  };
 
-const paginationSearch = ({ page, limit }) => {
-  emit("pagination", { page: page, limit: limit });
-};
+  const isOperationDisabled = (operation, row) => {
+    if (!operation?.disabled) return false;
+    return typeof operation.disabled === "function"
+      ? !!operation.disabled(row)
+      : !!operation.disabled;
+  };
 
-const rowClick = (row) => {
-  emit("row-click", row);
-};
+  const parseHexToRgb = hex => {
+    const normalized = String(hex || "")
+      .trim()
+      .replace("#", "");
+    if (normalized.length === 3) {
+      const r = parseInt(normalized[0] + normalized[0], 16);
+      const g = parseInt(normalized[1] + normalized[1], 16);
+      const b = parseInt(normalized[2] + normalized[2], 16);
+      if ([r, g, b].some(n => Number.isNaN(n))) return null;
+      return { r, g, b };
+    }
+    if (normalized.length === 6 || normalized.length === 8) {
+      const r = parseInt(normalized.slice(0, 2), 16);
+      const g = parseInt(normalized.slice(2, 4), 16);
+      const b = parseInt(normalized.slice(4, 6), 16);
+      if ([r, g, b].some(n => Number.isNaN(n))) return null;
+      return { r, g, b };
+    }
+    return null;
+  };
 
-const expandChange = (row, expandedRows) => {
-  emit("expand-change", row, expandedRows);
-};
+  const fadeColor = (color, alpha = 0.35) => {
+    const c = String(color || "").trim();
+    if (!c) return undefined;
+    if (c.startsWith("#")) {
+      const rgb = parseHexToRgb(c);
+      if (!rgb) return c;
+      return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})`;
+    }
+    const rgbMatch = c.match(
+      /^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)(?:\s*,\s*[\d.]+\s*)?\)$/i
+    );
+    if (rgbMatch) {
+      const r = Number(rgbMatch[1]);
+      const g = Number(rgbMatch[2]);
+      const b = Number(rgbMatch[3]);
+      if ([r, g, b].some(n => Number.isNaN(n))) return c;
+      return `rgba(${r}, ${g}, ${b}, ${alpha})`;
+    }
+    if (c.includes("--el-color-primary")) {
+      return "var(--el-color-primary-light-5)";
+    }
+    if (c.includes("--el-color-danger")) {
+      return "var(--el-color-danger-light-5)";
+    }
+    return "var(--el-text-color-disabled)";
+  };
 
-const handleSelectionChange = (newSelection) => {
-  emit("selection-change", newSelection);
-};
+  const getOperationColor = (operation, row) => {
+    const baseColor =
+      operation?.name === "鍒犻櫎" || operation?.name === "delete"
+        ? "#D93025"
+        : operation?.name === "璇︽儏"
+        ? "#67C23A"
+        : operation?.color || "var(--el-color-primary)";
+
+    if (isOperationDisabled(operation, row)) {
+      return fadeColor(baseColor, 0.35);
+    }
+    return baseColor;
+  };
+
+  // 鏂囦欢鍙樺寲澶勭悊
+  const handleChange = (file, fileList, index) => {
+    if (fileList.length > 1) {
+      const earliestFile = fileList[0];
+      uploadRefs.value[index]?.handleRemove(earliestFile);
+    }
+    currentFiles.value[index] = file;
+  };
+
+  // 鏂囦欢涓婁紶鍓嶆牎楠�
+  const beforeUpload = (rawFile, index) => {
+    currentFiles.value[index] = {};
+    if (rawfile.size > 1024 * 1024 * 10 * 10) {
+      ElMessage.error("涓婁紶鏂囦欢涓嶈秴杩�10M");
+      return false;
+    }
+    return true;
+  };
+
+  // 涓婁紶鎴愬姛
+  const handleSuccessUp = (response, file, fileList, index) => {
+    if (response.code == 200) {
+      if (uploadRefs[index]) {
+        uploadRefs[index].clearFiles();
+      }
+      currentFiles[index] = file;
+      ElMessage.success("涓婁紶鎴愬姛");
+      resetUploadComponent(index);
+    } else {
+      ElMessage.error(response.message);
+    }
+  };
+
+  const resetUploadComponent = index => {
+    uploadKeys[index] = Date.now();
+  };
+
+  // 涓婁紶澶辫触
+  const onError = (error, file, fileList, index) => {
+    ElMessage.error("鏂囦欢涓婁紶澶辫触锛岃閲嶈瘯");
+    if (uploadRefs.value[index]) {
+      uploadRefs.value[index].clearFiles();
+    }
+  };
+
+  // 鏂囦欢鏁伴噺瓒呴檺鎻愮ず
+  const onExceed = () => {
+    ElMessage.warning("瓒呭嚭鏂囦欢涓暟");
+  };
+
+  const paginationSearch = ({ page, limit }) => {
+    emit("pagination", { page: page, limit: limit });
+  };
+
+  const rowClick = row => {
+    emit("row-click", row);
+  };
+
+  const expandChange = (row, expandedRows) => {
+    emit("expand-change", row, expandedRows);
+  };
+
+  const handleSelectionChange = newSelection => {
+    emit("selection-change", newSelection);
+  };
 </script>
 
 <style scoped lang="scss">
-.cell {
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  padding-right: 0 !important;
-  padding-left: 0 !important;
-}
+  .lims-table {
+    border: 1px solid var(--surface-border);
+    border-radius: 18px;
+    background: rgba(255, 255, 255, 0.9);
+  }
 
-.pim-table-header-extra :deep(.el-input),
-.pim-table-header-extra :deep(.el-select) {
-  width: 100%;
-}
+  .cell {
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    padding-right: 0 !important;
+    padding-left: 0 !important;
+  }
+
+  .pim-table-header-extra :deep(.el-input),
+  .pim-table-header-extra :deep(.el-select) {
+    width: 100%;
+  }
+
+  .pim-table-header-title {
+    font-weight: 600;
+  }
 </style>
diff --git a/src/components/PIMTable/Pagination.vue b/src/components/PIMTable/Pagination.vue
index 7639e64..001f19a 100644
--- a/src/components/PIMTable/Pagination.vue
+++ b/src/components/PIMTable/Pagination.vue
@@ -91,7 +91,6 @@
 <style scoped>
 .pagination-container {
   background: #fff;
-  padding: 16px 0;
   margin-top: 0;
 }
 .pagination-container.hidden {
diff --git a/src/components/PageHeader/index.vue b/src/components/PageHeader/index.vue
index d8fc6fa..60d3961 100644
--- a/src/components/PageHeader/index.vue
+++ b/src/components/PageHeader/index.vue
@@ -43,6 +43,11 @@
 <style scoped>
 .page-header-wrapper {
   margin-bottom: 16px;
+  padding: 16px 18px;
+  border: 1px solid var(--surface-border);
+  border-radius: var(--radius-md);
+  background: rgba(255, 255, 255, 0.82);
+  box-shadow: var(--shadow-sm);
 }
 
 .page-header-wrapper :deep(.el-page-header__extra) {
@@ -50,4 +55,9 @@
   align-items: center;
   gap: 8px;
 }
+
+.page-header-wrapper :deep(.el-page-header__content) {
+  font-weight: 600;
+  color: var(--text-primary);
+}
 </style>
diff --git a/src/components/ProcessParamListDialog.vue b/src/components/ProcessParamListDialog.vue
new file mode 100644
index 0000000..38e892d
--- /dev/null
+++ b/src/components/ProcessParamListDialog.vue
@@ -0,0 +1,642 @@
+<template>
+  <el-dialog v-model="visible"
+             :title="title"
+             width="800px"
+             destroy-on-close>
+    <div class="param-list-container">
+      <div class="params-header">
+        <span>鍙傛暟鍒楄〃</span>
+        <div>
+          <el-button v-if="editable"
+                     type="primary"
+                     link
+                     size="small"
+                     @click="handleAddParam">
+            <el-icon>
+              <Plus />
+            </el-icon>鏂板
+          </el-button>
+          <!-- <el-button v-if="editable"
+                     type="primary"
+                     link
+                     size="small"
+                     @click="getsyncProcessParamItem">
+            <el-icon>
+              <Refresh />
+            </el-icon>鍚屾宸ュ簭鍙傛暟
+          </el-button> -->
+        </div>
+      </div>
+      <div class="params-list">
+        <div v-for="param in paramList"
+             :key="param.id"
+             class="param-item">
+          <div class="param-info">
+            <span class="param-code">{{ param.paramName }}</span>
+            <span class="param-value">
+              鏍囧噯鍊硷細{{ param.standardValue || "-" }} {{ param.unit }}
+            </span>
+          </div>
+          <div class="param-actions">
+            <el-button v-if="editable"
+                       link
+                       type="primary"
+                       size="small"
+                       @click="handleEditParam(param)">
+              缂栬緫
+            </el-button>
+            <el-button v-if="editable"
+                       link
+                       type="danger"
+                       size="small"
+                       @click="handleDeleteParam(param)">
+              鍒犻櫎
+            </el-button>
+          </div>
+        </div>
+        <el-empty v-if="!paramList || paramList.length === 0"
+                  description="鏆傛棤鍙傛暟"
+                  :image-size="50" />
+      </div>
+    </div>
+    <!-- 閫夋嫨鍙傛暟瀵硅瘽妗� -->
+    <el-dialog v-model="selectParamDialogVisible"
+               title="閫夋嫨鍙傛暟"
+               width="1000px">
+      <div class="param-select-container">
+        <!-- 宸︿晶鍙傛暟鍒楄〃 -->
+        <div class="param-list-area">
+          <div class="area-title">鍙�夊弬鏁�</div>
+          <div class="search-box">
+            <el-input v-model="paramSearchKeyword"
+                      placeholder="璇疯緭鍏ュ弬鏁板悕绉版悳绱�"
+                      clearable
+                      size="small"
+                      @input="getBaseParamListData">
+              <template #prefix>
+                <el-icon>
+                  <Search />
+                </el-icon>
+              </template>
+            </el-input>
+          </div>
+          <el-table :data="filteredParamList"
+                    height="400"
+                    border
+                    highlight-current-row
+                    @current-change="handleSelectParam">
+            <el-table-column prop="paramName"
+                             label="鍙傛暟鍚嶇О" />
+            <el-table-column prop="paramType"
+                             label="鍙傛暟绫诲瀷">
+              <template #default="scope">
+                <el-tag size="small"
+                        :type="getParamTypeTag(scope.row.paramType)">{{ getParamTypeText(scope.row.paramType) }}</el-tag>
+              </template>
+            </el-table-column>
+          </el-table>
+          <!-- 鍒嗛〉鎺т欢 -->
+          <div class="pagination-container"
+               style="margin-top: 10px;">
+            <el-pagination :current-page="paramPage.current"
+                           :page-size="paramPage.size"
+                           :page-sizes="[10, 20, 50, 100]"
+                           layout="total, sizes, prev, pager, next, jumper"
+                           :total="paramPage.total"
+                           @size-change="getBaseParamListData"
+                           @current-change="getBaseParamListData"
+                           size="small" />
+          </div>
+        </div>
+        <!-- 鍙充晶鍙傛暟璇︽儏 -->
+        <div class="param-detail-area">
+          <div class="area-title">鍙傛暟璇︽儏</div>
+          <el-form v-if="selectedParam"
+                   :model="selectedParam"
+                   label-width="100px"
+                   class="param-detail-form">
+            <el-form-item label="鍙傛暟鍚嶇О">
+              <span class="detail-text">{{ selectedParam.paramName }}</span>
+            </el-form-item>
+            <el-form-item label="鍙傛暟绫诲瀷">
+              <el-tag size="small"
+                      :type="getParamTypeTag(selectedParam.paramType)">{{ getParamTypeText(selectedParam.paramType) }}</el-tag>
+            </el-form-item>
+            <el-form-item label="鍙傛暟鏍煎紡">
+              <span class="detail-text">{{ selectedParam.paramFormat || '-' }}</span>
+            </el-form-item>
+            <el-form-item label="鍗曚綅">
+              <span class="detail-text">{{ selectedParam.unit || '-' }}</span>
+            </el-form-item>
+            <el-form-item label="鏍囧噯鍊�">
+              <el-input v-model="selectedParam.standardValue"
+                        placeholder="璇疯緭鍏ラ粯璁ゅ��" />
+            </el-form-item>
+            <el-form-item label="鏄惁蹇呭~">
+              <el-switch :active-value="1"
+                         :inactive-value="0"
+                         v-model="selectedParam.isRequired" />
+            </el-form-item>
+          </el-form>
+          <el-empty v-else
+                    description="璇蜂粠宸︿晶閫夋嫨鍙傛暟"
+                    :image-size="100" />
+        </div>
+      </div>
+      <template #footer>
+        <el-button type="primary" @click="handleParamSelectSubmit">纭畾</el-button>
+        <el-button @click="selectParamDialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </el-dialog>
+    <!-- 缂栬緫鍙傛暟瀵硅瘽妗� -->
+    <el-dialog v-model="editParamDialogVisible"
+               title="缂栬緫鍙傛暟"
+               width="600px">
+      <el-form :model="editParamForm"
+               :rules="editParamRules"
+               ref="editParamFormRef"
+               label-width="120px">
+        <el-form-item label="鍙傛暟鍚嶇О">
+          <span class="detail-text">{{ editParamForm.paramName }}</span>
+        </el-form-item>
+        <el-form-item label="鍙傛暟绫诲瀷">
+          <el-tag size="small"
+                  :type="getParamTypeTag(editParamForm.paramType)">
+            {{ getParamTypeText(editParamForm.paramType) }}
+          </el-tag>
+        </el-form-item>
+        <el-form-item label="鍙傛暟鏍煎紡">
+          <span class="detail-text">{{ editParamForm.paramFormat || '-' }}</span>
+        </el-form-item>
+        <el-form-item label="鍗曚綅">
+          <span class="detail-text">{{ editParamForm.unit || '-' }}</span>
+        </el-form-item>
+        <el-form-item label="鏍囧噯鍊�"
+                      prop="standardValue">
+          <el-input v-model="editParamForm.standardValue"
+                    placeholder="璇疯緭鍏ユ爣鍑嗗��" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="handleEditParamSubmit">纭畾</el-button>
+        <el-button @click="editParamDialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </el-dialog>
+  </el-dialog>
+</template>
+
+<script setup>
+  import { ref, computed, watch } from "vue";
+  import { ElMessage, ElMessageBox } from "element-plus";
+  import { Plus, Search } from "@element-plus/icons-vue";
+  import {
+    delProcessRouteItemParam,
+    editProcessRouteItemParam,
+    addProcessRouteItemParam,
+  } from "@/api/productionManagement/processRouteItem.js";
+  import {
+    addProcessRouteItemParamOrder,
+    delProcessRouteItemParamOrder,
+    editProcessRouteItemParamOrder,
+  } from "@/api/productionManagement/productProcessRoute.js";
+
+  import { getBaseParamList } from "@/api/basicData/parameterMaintenance.js";
+
+  const props = defineProps({
+    modelValue: {
+      type: Boolean,
+      default: false,
+    },
+    title: {
+      type: String,
+      default: "鍙傛暟鍒楄〃",
+    },
+    routeId: {
+      type: Number,
+      default: 0,
+    },
+    process: {
+      type: Object,
+      default: () => ({}),
+    },
+    paramList: {
+      type: Array,
+      default: () => [],
+    },
+    editable: {
+      type: Boolean,
+      default: true,
+    },
+    orderId: {
+      type: Number,
+      default: 0,
+    },
+    pageType: {
+      type: String,
+      default: "route",
+    },
+  });
+
+  const emit = defineEmits(["update:modelValue", "refresh"]);
+
+  const visible = computed({
+    get: () => props.modelValue,
+    set: value => emit("update:modelValue", value),
+  });
+
+  // 鍝嶅簲寮忔暟鎹�
+  const selectParamDialogVisible = ref(false);
+  const editParamDialogVisible = ref(false);
+  const paramSearchKeyword = ref("");
+  const selectedParam = ref(null);
+  const filteredParamList = ref([]);
+  const paramPage = ref({
+    current: 1,
+    size: 10,
+    total: 0,
+  });
+  const editParamForm = ref({
+    id: null,
+    processId: null,
+    paramId: null,
+    paramName: "",
+    standardValue: null,
+    isRequired: 0,
+    paramType: null,
+    paramFormat: "",
+    unit: "",
+  });
+  const editParamRules = ref({
+    // standardValue: [{ required: true, message: "璇疯緭鍏ユ爣鍑嗗��", trigger: "blur" }],
+  });
+  const editParamFormRef = ref(null);
+
+  // 鏂板鍙傛暟
+  const handleAddParam = () => {
+    selectedParam.value = null;
+    paramSearchKeyword.value = "";
+    paramPage.value.current = 1;
+    // 鑾峰彇鍙�夊弬鏁板垪琛�
+    getBaseParamListData();
+    selectParamDialogVisible.value = true;
+  };
+
+  // 缂栬緫鍙傛暟
+  const handleEditParam = param => {
+    editParamForm.value = {
+      id: param.id,
+      processId: props.process.id,
+      paramId: param.paramId,
+      paramName: param.parameterName || param.paramName,
+      standardValue: param.standardValue,
+      isRequired: param.isRequired || 0,
+      paramType: param.parameterType || param.paramType,
+      paramFormat: param.parameterFormat || param.paramFormat,
+      unit: param.unit || param.unit,
+    };
+    editParamDialogVisible.value = true;
+  };
+
+  // 鍒犻櫎鍙傛暟
+  const handleDeleteParam = param => {
+    ElMessageBox.confirm("纭畾瑕佸垹闄よ鍙傛暟鍚楋紵", "鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        // 璋冪敤API鍒犻櫎鍙傛暟
+        if (props.pageType === "order") {
+          delProcessRouteItemParamOrder(param.id)
+            .then(res => {
+              ElMessage.success("鍒犻櫎鎴愬姛");
+              emit("refresh");
+            })
+            .catch(err => {
+              ElMessage.error("鍒犻櫎鍙傛暟澶辫触");
+              console.error("鍒犻櫎鍙傛暟澶辫触锛�", err);
+            });
+        } else {
+          delProcessRouteItemParam(param.id)
+            .then(res => {
+              ElMessage.success("鍒犻櫎鎴愬姛");
+              emit("refresh");
+            })
+            .catch(err => {
+              ElMessage.error("鍒犻櫎鍙傛暟澶辫触");
+              console.error("鍒犻櫎鍙傛暟澶辫触锛�", err);
+            });
+        }
+      })
+      .catch(() => {});
+  };
+  const getsyncProcessParamItem = () => {
+    emit("getsyncProcessParamItem");
+  };
+
+  // 鑾峰彇鍙�夊弬鏁板垪琛�
+  const getBaseParamListData = () => {
+    console.log(paramPage, "paramPage.size");
+
+    getBaseParamList({
+      paramName: paramSearchKeyword.value,
+      current: paramPage.value.current,
+      size: paramPage.value.size,
+    }).then(res => {
+      if (res.code === 200) {
+        filteredParamList.value = res.data?.records || [];
+        paramPage.value.total = res.data.total || 0;
+      } else {
+        ElMessage.error(res.msg || "鏌ヨ澶辫触");
+      }
+    });
+  };
+
+  // 閫夋嫨鍙傛暟
+  const handleSelectParam = param => {
+    selectedParam.value = param;
+  };
+
+  // 鎻愪氦閫夋嫨鍙傛暟
+  const handleParamSelectSubmit = () => {
+    if (!selectedParam.value) {
+      ElMessage.warning("璇峰厛閫夋嫨涓�涓弬鏁�");
+      return;
+    }
+
+    if (!props.process || !props.process.id) {
+      ElMessage.error("宸ヨ壓璺嚎椤圭洰淇℃伅涓嶅畬鏁�");
+      return;
+    }
+
+    // 璋冪敤API鏂板鍙傛暟
+    if (props.pageType === "order") {
+      addProcessRouteItemParamOrder({
+        productionOrderId: Number(props.orderId),
+        productionOrderRoutingOperationId: props.process.id,
+        technologyRoutingOperationParamId: props.process.id,
+        paramId: selectedParam.value.id,
+        standardValue: selectedParam.value.standardValue || "",
+        isRequired: selectedParam.value.isRequired || 0,
+      })
+        .then(res => {
+          if (res.code === 200) {
+            ElMessage.success("娣诲姞鍙傛暟鎴愬姛");
+            selectParamDialogVisible.value = false;
+            emit("refresh");
+          } else {
+            ElMessage.error(res.msg || "娣诲姞鍙傛暟澶辫触");
+          }
+        })
+        .catch(err => {
+          ElMessage.error("娣诲姞鍙傛暟澶辫触");
+          console.error("娣诲姞鍙傛暟澶辫触锛�", err);
+        });
+    } else {
+      console.log(selectedParam.value, "selectedParam");
+
+      addProcessRouteItemParam({
+        technologyRoutingOperationId: props.process.id,
+        paramId: selectedParam.value.id,
+        standardValue: selectedParam.value.standardValue || "",
+        isRequired: selectedParam.value.isRequired || 0,
+      })
+        .then(res => {
+          if (res.code === 200) {
+            ElMessage.success("娣诲姞鍙傛暟鎴愬姛");
+            selectParamDialogVisible.value = false;
+            emit("refresh");
+          } else {
+            ElMessage.error(res.msg || "娣诲姞鍙傛暟澶辫触");
+          }
+        })
+        .catch(err => {
+          ElMessage.error("娣诲姞鍙傛暟澶辫触");
+          console.error("娣诲姞鍙傛暟澶辫触锛�", err);
+        });
+    }
+  };
+
+  // 鎻愪氦缂栬緫鍙傛暟
+  const handleEditParamSubmit = () => {
+    if (!editParamFormRef.value) return;
+    editParamFormRef.value.validate(valid => {
+      if (valid) {
+        if (props.pageType === "order") {
+          editProcessRouteItemParamOrder({
+            id: editParamForm.value.id,
+            standardValue: editParamForm.value.standardValue || "",
+            isRequired: editParamForm.value.isRequired || 0,
+            // productionOrderRoutingOperationId: props.process.id,
+          })
+            .then(res => {
+              if (res.code === 200) {
+                ElMessage.success("缂栬緫鎴愬姛");
+                editParamDialogVisible.value = false;
+                emit("refresh");
+              } else {
+                ElMessage.error(res.msg || "缂栬緫澶辫触");
+              }
+            })
+            .catch(err => {
+              ElMessage.error("缂栬緫鍙傛暟澶辫触");
+              console.error("缂栬緫鍙傛暟澶辫触锛�", err);
+            });
+        } else {
+          // 璋冪敤API淇敼鍙傛暟
+          editProcessRouteItemParam({
+            id: editParamForm.value.id,
+            technologyRoutingOperationId: props.process.id,
+            paramId: editParamForm.value.paramId,
+            standardValue: editParamForm.value.standardValue || "",
+            isRequired: editParamForm.value.isRequired || 0,
+          })
+            .then(res => {
+              if (res.code === 200) {
+                ElMessage.success("缂栬緫鎴愬姛");
+                editParamDialogVisible.value = false;
+                emit("refresh");
+              } else {
+                ElMessage.error(res.msg || "缂栬緫澶辫触");
+              }
+            })
+            .catch(err => {
+              ElMessage.error("缂栬緫鍙傛暟澶辫触");
+              console.error("缂栬緫鍙傛暟澶辫触锛�", err);
+            });
+        }
+      }
+    });
+  };
+
+  // 鑾峰彇鍙傛暟绫诲瀷鏍囩
+  const getParamTypeTag = type => {
+    const typeMap = {
+      1: "primary",
+      2: "info",
+      3: "warning",
+      4: "success",
+    };
+    return typeMap[type] || "default";
+  };
+
+  // 鑾峰彇鍙傛暟绫诲瀷鏂囨湰
+  const getParamTypeText = type => {
+    const typeMap = {
+      1: "鏁板�兼牸寮�",
+      2: "鏂囨湰鏍煎紡",
+      3: "涓嬫媺閫夐」",
+      4: "鏃堕棿鏍煎紡",
+    };
+    return typeMap[type] || type;
+  };
+
+  watch(
+    () => props.modelValue,
+    newVal => {
+      if (!newVal) {
+        // 寮圭獥鍏抽棴鏃堕噸缃暟鎹�
+        selectParamDialogVisible.value = false;
+        editParamDialogVisible.value = false;
+        selectedParam.value = null;
+        paramSearchKeyword.value = "";
+        paramPage.value.current = 1;
+        filteredParamList.value = [];
+        editParamForm.value = {
+          id: null,
+          processId: null,
+          paramId: null,
+          paramName: "",
+          standardValue: null,
+          isRequired: 0,
+          paramType: null,
+          paramFormat: "",
+          unit: "",
+        };
+      }
+    }
+  );
+</script>
+
+<style scoped>
+  .param-list-container {
+    padding: 10px 0;
+  }
+
+  .params-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 15px;
+    padding-bottom: 10px;
+    border-bottom: 1px solid #e4e7ed;
+  }
+
+  .params-header span {
+    font-size: 16px;
+    font-weight: 500;
+    color: #303133;
+  }
+
+  .params-list {
+    max-height: 400px;
+    overflow-y: auto;
+  }
+
+  .param-item {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 12px 16px;
+    margin-bottom: 8px;
+    background-color: #f9f9f9;
+    border-radius: 4px;
+    transition: all 0.3s ease;
+  }
+
+  .param-item:hover {
+    background-color: #ecf5ff;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  }
+
+  .param-info {
+    display: flex;
+    align-items: center;
+    gap: 20px;
+    flex: 1;
+  }
+
+  .param-code {
+    font-weight: 500;
+    color: #303133;
+    min-width: 120px;
+  }
+
+  .param-value {
+    color: #606266;
+    font-size: 14px;
+  }
+
+  .param-actions {
+    display: flex;
+    gap: 10px;
+  }
+
+  /* 婊氬姩鏉℃牱寮� */
+  .params-list::-webkit-scrollbar {
+    width: 6px;
+  }
+
+  .params-list::-webkit-scrollbar-track {
+    background: #f1f1f1;
+    border-radius: 3px;
+  }
+
+  .params-list::-webkit-scrollbar-thumb {
+    background: #c1c1c1;
+    border-radius: 3px;
+  }
+
+  .params-list::-webkit-scrollbar-thumb:hover {
+    background: #a8a8a8;
+  }
+
+  /* 閫夋嫨鍙傛暟瀵硅瘽妗嗘牱寮� */
+  .param-select-container {
+    display: flex;
+    gap: 20px;
+  }
+
+  .param-list-area {
+    flex: 1;
+    min-width: 400px;
+  }
+
+  .param-detail-area {
+    flex: 1;
+    min-width: 300px;
+  }
+
+  .area-title {
+    font-size: 14px;
+    font-weight: 500;
+    margin-bottom: 10px;
+    color: #303133;
+  }
+
+  .search-box {
+    display: flex;
+    gap: 10px;
+    margin-bottom: 10px;
+  }
+
+  .param-detail-form {
+    background: #f9f9f9;
+    padding: 15px;
+    border-radius: 4px;
+  }
+
+  .detail-text {
+    font-weight: 500;
+  }
+</style>
\ No newline at end of file
diff --git a/src/components/ProjectManagement/ProgressReportDialog.vue b/src/components/ProjectManagement/ProgressReportDialog.vue
index d9402c2..dc6afe7 100644
--- a/src/components/ProjectManagement/ProgressReportDialog.vue
+++ b/src/components/ProjectManagement/ProgressReportDialog.vue
@@ -116,25 +116,14 @@
       </el-row>
 
       <el-form-item label="闄勪欢" prop="attachmentIds">
-        <el-upload
-          v-model:file-list="fileList"
-          :action="upload.url"
-          :headers="upload.headers"
-          multiple
-          name="files"
-          :on-success="handleUploadSuccess"
-          :on-error="handleUploadError"
-          :on-remove="handleRemove"
-        >
-          <el-button type="primary">涓婁紶鏂囦欢</el-button>
-        </el-upload>
+        <FileUpload v-model:file-list="form.storageBlobDTOs" />
       </el-form-item>
     </el-form>
 
     <template #footer>
       <div class="dialog-footer">
-        <el-button @click="visible = false">鍙栨秷</el-button>
         <el-button type="primary" @click="submit">纭畾</el-button>
+        <el-button @click="visible = false">鍙栨秷</el-button>
       </div>
     </template>
   </el-dialog>
@@ -144,6 +133,7 @@
 import { computed, reactive, ref, watch } from 'vue'
 import { ElMessage } from 'element-plus'
 import { getToken } from '@/utils/auth'
+import FileUpload from "@/components/AttachmentUpload/file/index.vue";
 
 const props = defineProps({
   modelValue: { type: Boolean, default: false },
@@ -161,11 +151,6 @@
 })
 
 const formRef = ref()
-const fileList = ref([])
-const upload = reactive({
-  url: import.meta.env.VITE_APP_BASE_API + '/basic/customer-follow/upload',
-  headers: { Authorization: 'Bearer ' + getToken() }
-})
 
 const form = ref({
   planNodeId: undefined,
@@ -180,7 +165,7 @@
   managerName: '',
   departmentName: '',
   remark: '',
-  attachmentIds: []
+  storageBlobDTOs: []
 })
 
 const rules = {
@@ -217,9 +202,8 @@
     managerName: info.managerName || '',
     departmentName: info.departmentName || '',
     remark: '',
-    attachmentIds: []
+    storageBlobDTOs: []
   }
-  fileList.value = []
 }
 
 watch(
@@ -237,30 +221,6 @@
   form.value.completionProgress = 100
   form.value.totalProgress = 100
   if (!form.value.actualEndTime) form.value.actualEndTime = form.value.reportDate || ''
-}
-
-function handleUploadError() {
-  ElMessage.error('涓婁紶鏂囦欢澶辫触')
-}
-
-function handleUploadSuccess(res, file) {
-  if (res?.code !== 200) {
-    ElMessage.error(res?.msg || '涓婁紶澶辫触')
-    return
-  }
-  const attachmentId = res?.data?.id ?? res?.data?.tempId ?? ''
-  if (!attachmentId) return
-  form.value.attachmentIds.push(attachmentId)
-  try {
-    file.attachmentId = attachmentId
-  } catch (e) {}
-  ElMessage.success('涓婁紶鎴愬姛')
-}
-
-function handleRemove(file) {
-  const attachmentId = file?.attachmentId
-  if (!attachmentId) return
-  form.value.attachmentIds = (form.value.attachmentIds || []).filter(id => id !== attachmentId)
 }
 
 async function submit() {
diff --git a/src/components/PurchaseAIChatSidebar/index.vue b/src/components/PurchaseAIChatSidebar/index.vue
new file mode 100644
index 0000000..ecc46be
--- /dev/null
+++ b/src/components/PurchaseAIChatSidebar/index.vue
@@ -0,0 +1,26 @@
+<template>
+  <AIChatSidebar :assistants="assistants" default-assistant="purchase" />
+</template>
+
+<script setup>
+import { ShoppingCart } from '@element-plus/icons-vue'
+import AIChatSidebar from '@/components/AIChatSidebar/index.vue'
+
+const assistants = [
+  {
+    key: 'purchase',
+    label: '閲囪喘鍔╃悊',
+    title: '閲囪喘鏅鸿兘鍔╃悊',
+    tooltip: '閲囪喘鏅鸿兘鍔╃悊',
+    icon: ShoppingCart,
+    apiBase: '/purchase-ai',
+    storageKey: 'purchase_ai_chat_uuid',
+    placeholder: '璇疯緭鍏ラ噰璐棶棰�... (Enter 鍙戦��, Shift+Enter 鎹㈣)',
+    welcomeMessage: '浣犲ソ',
+    allowFileUpload: true,
+    allowMultipleFileUpload: true,
+    fileAnalyzeUrl: '/purchase-ai/analyze-files',
+    emptySessionText: '鏆傛棤閲囪喘浼氳瘽'
+  }
+]
+</script>
diff --git a/src/components/SvgIcon/index.vue b/src/components/SvgIcon/index.vue
index 41f64c9..2b8368f 100644
--- a/src/components/SvgIcon/index.vue
+++ b/src/components/SvgIcon/index.vue
@@ -9,7 +9,7 @@
   props: {
     iconClass: {
       type: String,
-      required: true
+      default: ''
     },
     className: {
       type: String,
diff --git a/src/layout/components/AppMain.vue b/src/layout/components/AppMain.vue
index 1e7a78b..011e118 100644
--- a/src/layout/components/AppMain.vue
+++ b/src/layout/components/AppMain.vue
@@ -43,16 +43,17 @@
   width: 100%;
   position: relative;
   overflow: hidden;
-  background: #F5F7FB;
+  background: transparent;
 }
 
 .route-view-wrapper {
   width: 100%;
   height: 100%;
+  padding: 120px 16px 24px 0;
 }
 
 .fixed-header + .app-main {
-  padding-top: 50px;
+  padding-top: 0;
 }
 
 .hasTagsView {
@@ -62,7 +63,7 @@
   }
 
   .fixed-header + .app-main {
-    padding-top: 84px;
+    padding-top: 0;
   }
 }
 </style>
@@ -81,11 +82,11 @@
 }
 
 ::-webkit-scrollbar-track {
-  background-color: #f1f1f1;
+  background-color: rgba(218, 225, 220, 0.8);
 }
 
 ::-webkit-scrollbar-thumb {
-  background-color: #c0c0c0;
+  background-color: #b2bdb5;
   border-radius: 3px;
 }
 </style>
diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue
index d4e938d..cb08221 100644
--- a/src/layout/components/Navbar.vue
+++ b/src/layout/components/Navbar.vue
@@ -158,15 +158,19 @@
 </script>
 
 <style lang='scss' scoped>
-.navbar {
-  height: 50px;
-  overflow: hidden;
-  position: relative;
-  background: var(--navbar-bg);
-  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
+.navbar {
+  height: 56px;
+  overflow: hidden;
+  position: relative;
+  background: var(--navbar-bg);
+  border: 1px solid rgba(216, 225, 219, 0.9);
+  border-radius: 22px;
+  backdrop-filter: blur(18px);
+  box-shadow: var(--shadow-sm);
+  padding: 0 18px;
 
   .hamburger-container {
-    line-height: 46px;
+    line-height: 52px;
     height: 100%;
     float: left;
     cursor: pointer;
@@ -174,7 +178,7 @@
     -webkit-tap-highlight-color: transparent;
 
     &:hover {
-      background: rgba(0, 0, 0, 0.025);
+      background: var(--navbar-hover);
     }
   }
 
@@ -192,30 +196,32 @@
     vertical-align: top;
   }
 
-  .right-menu {
-    float: right;
-    height: 100%;
-    line-height: 50px;
-    display: flex;
+  .right-menu {
+    float: right;
+    height: 100%;
+    align-items: center;
+    display: flex;
 
     &:focus {
       outline: none;
     }
 
-    .right-menu-item {
-      display: inline-block;
-      padding: 0 8px;
-      height: 100%;
-      font-size: 18px;
-      color: var(--navbar-text);
-      vertical-align: text-bottom;
+    .right-menu-item {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      padding: 0 8px;
+      height: 100%;
+      font-size: 18px;
+      color: var(--navbar-text);
+      border-radius: 14px;
 
       &.hover-effect {
         cursor: pointer;
         transition: background 0.3s;
 
         &:hover {
-          background: rgba(0, 0, 0, 0.025);
+          background: var(--navbar-hover);
         }
       }
 
@@ -234,7 +240,7 @@
     }
 
     .notification-container {
-      margin-right: 20px;
+      margin-right: 12px;
       display: flex;
       align-items: center;
       cursor: pointer;
@@ -246,28 +252,43 @@
       }
     }
 
-    .avatar-container {
-      margin-right: 40px;
-
-      .avatar-wrapper {
-        margin-top: 5px;
-        position: relative;
-
-        .user-avatar {
-          cursor: pointer;
-          width: 40px;
-          height: 40px;
-          border-radius: 50px;
-        }
-
-        i {
-          cursor: pointer;
-          position: absolute;
-          right: -20px;
-          top: 14px;
-          font-size: 12px;
-        }
-      }
+    .avatar-container {
+      margin-right: 4px;
+      height: 100%;
+      display: flex;
+      align-items: center;
+
+      :deep(.el-dropdown) {
+        height: 100%;
+        display: flex;
+        align-items: center;
+      }
+
+      .avatar-wrapper {
+        position: relative;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        gap: 10px;
+        padding: 6px 10px 6px 6px;
+        height: 44px;
+        border-radius: 999px;
+        background: rgba(247, 250, 248, 0.92);
+        border: 1px solid var(--surface-border);
+
+        .user-avatar {
+          cursor: pointer;
+          width: 34px;
+          height: 34px;
+          border-radius: 50px;
+        }
+
+        i {
+          cursor: pointer;
+          position: static;
+          font-size: 12px;
+        }
+      }
     }
   }
 }
@@ -275,8 +296,11 @@
 </style>
 
 <style lang="scss">
-.notification-popover {
-  padding: 0 !important;
+.notification-popover {
+  padding: 0 !important;
+  border-radius: 20px !important;
+  border: 1px solid var(--surface-border) !important;
+  box-shadow: var(--shadow-md) !important;
   
   .el-popover__title {
     display: none;
diff --git a/src/layout/components/Sidebar/Logo.vue b/src/layout/components/Sidebar/Logo.vue
index f8f1da3..57c2b4b 100644
--- a/src/layout/components/Sidebar/Logo.vue
+++ b/src/layout/components/Sidebar/Logo.vue
@@ -1,129 +1,142 @@
-<template>
-  <div class="sidebar-logo-container" :class="{ 'collapse': collapse }">
-    <transition name="sidebarLogoFade">
-      <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
-        <img v-if="logoUrl" :src="logoUrl" class="sidebar-logo" @error="handleImageError" alt="鍏徃Logo" />
-        <h1 class="sidebar-title">{{ title }}</h1>
-      </router-link>
-      <router-link v-else key="expand" class="sidebar-logo-link" to="/">
-        <img v-if="logoUrl" :src="logoUrl" class="sidebar-logo" @error="handleImageError" alt="鍏徃Logo" />
-        <h1 class="sidebar-title">{{ title }}</h1>
-      </router-link>
-    </transition>
-  </div>
-</template>
-
-<script setup>
-import { ref, computed, onMounted, watch } from 'vue'
-import useUserStore from '@/store/modules/user'
-import defaultLogo from '@/assets/logo/logo.png' // 瀵煎叆榛樿logo
-
-defineProps({
-  collapse: {
-    type: Boolean,
-    required: true
-  }
-})
-
-const title = import.meta.env.VITE_APP_TITLE
-const userStore = useUserStore()
-
-// 澶勭悊宸ュ巶鍚嶇О锛岀敓鎴愬悎娉曠殑鏂囦欢鍚�
-const cleanFactoryName = computed(() => {
-  if (!userStore.currentFactoryName) return ''
-  return userStore.currentFactoryName.trim()
-})
-
-// 鍔ㄦ�乴ogo璺緞
-const logoUrl = ref('')
-
-// 妫�鏌ogo鏄惁瀛樺湪骞惰缃畊rl
-const updateLogoUrl = () => {
-  if (!cleanFactoryName.value) {
-    logoUrl.value = defaultLogo
-    return
-  }
-
-  // 浣跨敤Vite鐨勫姩鎬佸鍏�
-  try {
-    const dynamicLogo = import.meta.glob('/src/assets/logo/*.png', { eager: true })
-    const logoPath = `/src/assets/logo/${cleanFactoryName.value}.png`
-
-    if (dynamicLogo[logoPath]) {
-      logoUrl.value = dynamicLogo[logoPath].default
-    } else {
-      logoUrl.value = defaultLogo
-    }
-  } catch (error) {
-    console.error('鍔犺浇宸ュ巶Logo澶辫触:', error)
-    logoUrl.value = defaultLogo
-  }
-}
-
-// 鍒濆鍖栧拰鐩戝惉鍙樺寲
-onMounted(() => {
-  updateLogoUrl()
-
-  // 鐩戝惉宸ュ巶鍚嶇О鍙樺寲
-  watch(() => userStore.currentFactoryName, updateLogoUrl)
-})
-
-// 鍥剧墖鍔犺浇閿欒澶勭悊
-const handleImageError = (event) => {
-  console.warn('Logo鍔犺浇澶辫触锛屼娇鐢ㄩ粯璁ogo')
-  logoUrl.value = defaultLogo
-}
-</script>
-
-<style lang="scss" scoped>
-@import '@/assets/styles/variables.module.scss';
-
-.sidebarLogoFade-enter-active {
-  transition: opacity 1.5s;
-}
-
-.sidebarLogoFade-enter,
-.sidebarLogoFade-leave-to {
-  opacity: 0;
-}
-
-.sidebar-logo-container {
-  position: relative;
-  width: 100% !important;
-  height: 50px !important;
-  line-height: 50px;
-  background: #fff;
-  text-align: center;
-  overflow: hidden;
-
-  & .sidebar-logo-link {
-    height: 100%;
-    width: 100%;
-
-    & .sidebar-logo {
-      width: 100%;
-      height: 100%;
-      // height: 32px;
-      vertical-align: middle;
-      margin-right: 12px;
-    }
-
-    & .sidebar-title {
-      display: inline-block;
-      margin: 0;
-      color: v-bind(getLogoTextColor);
-      font-weight: 600;
-      line-height: 50px;
-      font-size: 14px;
-      font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
-      vertical-align: middle;
-    }
-  }
-
-  &.collapse {
-    .sidebar-logo {
-      margin-right: 0px;
-    }
-  }
-}
-</style>
+<template>
+  <div class="sidebar-logo-container" :class="{ collapse }">
+    <transition name="sidebarLogoFade">
+      <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
+        <img :src="faviconUrl" class="sidebar-logo sidebar-favicon" alt="绔欑偣鍥炬爣" />
+      </router-link>
+      <router-link v-else key="expand" class="sidebar-logo-link" to="/">
+        <img v-if="logoUrl" :src="logoUrl" class="sidebar-logo" @error="handleImageError" alt="鍏徃Logo" />
+        <h1 v-if="!logoUrl" class="sidebar-title">{{ title }}</h1>
+      </router-link>
+    </transition>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, watch } from 'vue'
+import useUserStore from '@/store/modules/user'
+import defaultLogo from '@/assets/logo/logo.png'
+
+defineProps({
+  collapse: {
+    type: Boolean,
+    required: true
+  }
+})
+
+const title = import.meta.env.VITE_APP_TITLE
+const userStore = useUserStore()
+const baseUrl = import.meta.env.BASE_URL || '/'
+const faviconUrl = `${baseUrl.replace(/\/?$/, '/') }favicon.ico`.replace(/([^:]\/)\/+/g, '$1')
+
+const cleanFactoryName = computed(() => {
+  if (!userStore.currentFactoryName) return ''
+  return userStore.currentFactoryName.trim()
+})
+
+const logoUrl = ref('')
+
+const updateLogoUrl = () => {
+  if (!cleanFactoryName.value) {
+    logoUrl.value = defaultLogo
+    return
+  }
+
+  try {
+    const dynamicLogo = import.meta.glob('/src/assets/logo/*.png', { eager: true })
+    const logoPath = `/src/assets/logo/${cleanFactoryName.value}.png`
+
+    if (dynamicLogo[logoPath]) {
+      logoUrl.value = dynamicLogo[logoPath].default
+    } else {
+      logoUrl.value = defaultLogo
+    }
+  } catch (error) {
+    console.error('鍔犺浇宸ュ巶 Logo 澶辫触:', error)
+    logoUrl.value = defaultLogo
+  }
+}
+
+onMounted(() => {
+  updateLogoUrl()
+  watch(() => userStore.currentFactoryName, updateLogoUrl)
+})
+
+const handleImageError = () => {
+  logoUrl.value = defaultLogo
+}
+</script>
+
+<style lang="scss" scoped>
+@import '@/assets/styles/variables.module.scss';
+
+.sidebarLogoFade-enter-active {
+  transition: opacity 1.5s;
+}
+
+.sidebarLogoFade-enter,
+.sidebarLogoFade-leave-to {
+  opacity: 0;
+}
+
+.sidebar-logo-container {
+  position: relative;
+  width: 100% !important;
+  height: 56px !important;
+  line-height: 56px;
+  background: rgba(255, 255, 255, 0.78);
+  border: 1px solid var(--surface-border);
+  border-radius: 22px;
+  text-align: center;
+  overflow: hidden;
+  box-shadow: var(--shadow-sm);
+
+  .sidebar-logo-link {
+    height: 100%;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding: 0 18px 0 14px;
+  }
+
+  .sidebar-logo {
+    width: auto;
+    max-width: 250px;
+    max-height: 50px;
+    height: auto;
+    vertical-align: middle;
+    object-fit: contain;
+    object-position: center;
+  }
+
+  .sidebar-title {
+    display: inline-block;
+    margin: 0;
+    color: var(--text-primary);
+    font-weight: 600;
+    line-height: 1.2;
+    font-size: 14px;
+    font-family: "Segoe UI", "PingFang SC", sans-serif;
+    vertical-align: middle;
+  }
+
+  &.collapse {
+    .sidebar-logo-link {
+      padding: 0;
+    }
+
+    .sidebar-logo {
+      max-width: 30px;
+      max-height: 30px;
+    }
+
+    .sidebar-favicon {
+      width: 24px;
+      height: 24px;
+      max-width: 24px;
+      max-height: 24px;
+    }
+  }
+}
+</style>
diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue
index 9044945..0692dda 100644
--- a/src/layout/components/Sidebar/index.vue
+++ b/src/layout/components/Sidebar/index.vue
@@ -1,94 +1,142 @@
-<template>
-  <div :class="{ 'has-logo': showLogo }" class="sidebar-container">
-    <logo v-if="showLogo" :collapse="isCollapse" />
-    <el-scrollbar wrap-class="scrollbar-wrapper">
-      <el-menu :default-active="activeMenu" :collapse="isCollapse" :background-color="getMenuBackground"
-        :text-color="getMenuTextColor" :unique-opened="true" :active-text-color="theme" :collapse-transition="false"
-        mode="vertical" :class="sideTheme">
-        <sidebar-item v-for="(route, index) in sidebarRouters" :key="route.path + index" :item="route"
-          :base-path="route.path" />
-      </el-menu>
-    </el-scrollbar>
-  </div>
-</template>
-
-<script setup>
-import Logo from './Logo'
-import SidebarItem from './SidebarItem'
-import variables from '@/assets/styles/variables.module.scss'
-import useAppStore from '@/store/modules/app'
-import useSettingsStore from '@/store/modules/settings'
-import usePermissionStore from '@/store/modules/permission'
-
-const route = useRoute()
-const appStore = useAppStore()
-const settingsStore = useSettingsStore()
-const permissionStore = usePermissionStore()
-
-const sidebarRouters = computed(() => permissionStore.sidebarRouters)
-const showLogo = computed(() => settingsStore.sidebarLogo)
-const sideTheme = computed(() => settingsStore.sideTheme)
-const theme = computed(() => settingsStore.theme)
-const isCollapse = computed(() => !appStore.sidebar.opened)
-
-// 鑾峰彇鑿滃崟鑳屾櫙鑹�
-const getMenuBackground = computed(() => {
-  if (settingsStore.isDark) {
-    return 'var(--sidebar-bg)'
-  }
-  // 娴呰壊涓婚鏃讹紝鐩存帴鐢ㄤ富棰樿壊
-  return sideTheme.value === 'theme-dark' ? variables.menuBg : settingsStore.theme
-})
-
-// 鑾峰彇鑿滃崟鏂囧瓧棰滆壊
-const getMenuTextColor = computed(() => {
-  if (settingsStore.isDark) {
-    return 'var(--sidebar-text)'
-  }
-  return sideTheme.value === 'theme-dark' ? variables.menuText : variables.menuLightText
-})
-
-const activeMenu = computed(() => {
-  const { meta, path } = route
-  if (meta.activeMenu) {
-    return meta.activeMenu
-  }
-  return path
-})
-</script>
-
-<style lang="scss" scoped>
-.sidebar-container {
-  background-color: v-bind(getMenuBackground);
-
-  .scrollbar-wrapper {
-    background-color: v-bind(getMenuBackground);
-  }
-
-  .el-menu {
-    border: none;
-    height: 100%;
-    width: 100% !important;
-
-    .el-menu-item,
-    .el-sub-menu__title {
-      &:hover {
-        background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;
-      }
-    }
-
-    .el-menu-item {
-      color: v-bind(getMenuTextColor);
-
-      &.is-active {
-        color: var(--menu-active-text, #409eff);
-        background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;
-      }
-    }
-
-    .el-sub-menu__title {
-      color: v-bind(getMenuTextColor);
-    }
-  }
-}
-</style>
+<template>
+  <div :class="{ 'has-logo': showLogo }"
+       class="sidebar-container">
+    <logo v-if="showLogo"
+          :collapse="isCollapse" />
+    <el-scrollbar wrap-class="scrollbar-wrapper">
+      <el-menu :default-active="activeMenu"
+               :collapse="isCollapse"
+               :background-color="getMenuBackground"
+               :text-color="getMenuTextColor"
+               :unique-opened="true"
+               :active-text-color="theme"
+               :collapse-transition="false"
+               mode="vertical"
+               :class="sideTheme">
+        <sidebar-item v-for="(route, index) in sidebarRouters"
+                      :key="route.path + index"
+                      :item="route"
+                      :base-path="route.path" />
+      </el-menu>
+    </el-scrollbar>
+  </div>
+</template>
+
+<script setup>
+  import Logo from "./Logo";
+  import SidebarItem from "./SidebarItem";
+  import variables from "@/assets/styles/variables.module.scss";
+  import useAppStore from "@/store/modules/app";
+  import useSettingsStore from "@/store/modules/settings";
+  import usePermissionStore from "@/store/modules/permission";
+
+  const route = useRoute();
+  const appStore = useAppStore();
+  const settingsStore = useSettingsStore();
+  const permissionStore = usePermissionStore();
+
+  const sidebarRouters = computed(() => permissionStore.sidebarRouters);
+  const showLogo = computed(() => settingsStore.sidebarLogo);
+  const sideTheme = computed(() => settingsStore.sideTheme);
+  const theme = computed(() => settingsStore.theme);
+  const isCollapse = computed(() => !appStore.sidebar.opened);
+
+  const getMenuBackground = computed(() => "var(--sidebar-bg)");
+
+  const getMenuTextColor = computed(() => {
+    if (settingsStore.isDark) {
+      return "var(--sidebar-text)";
+    }
+    return sideTheme.value === "theme-dark"
+      ? variables.menuText
+      : variables.menuLightText;
+  });
+
+  const activeMenu = computed(() => {
+    const { meta, path } = route;
+    if (meta.activeMenu) {
+      return meta.activeMenu;
+    }
+    return path;
+  });
+</script>
+
+<style lang="scss" scoped>
+  .sidebar-container {
+    background-color: v-bind(getMenuBackground);
+    border-radius: 22px;
+    overflow: hidden;
+
+    .scrollbar-wrapper {
+      background-color: v-bind(getMenuBackground);
+    }
+
+    .el-menu {
+      border: none;
+      height: 100%;
+      width: 100% !important;
+      border-radius: 22px;
+
+      .el-menu-item,
+      .el-sub-menu__title {
+        margin-bottom: 6px;
+        border-radius: 14px;
+        color: v-bind(getMenuTextColor);
+
+        &:hover {
+          background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;
+          border-radius: 14px;
+        }
+      }
+
+      .el-menu-item {
+        &.is-active {
+          color: v-bind(theme);
+          background-color: var(--menu-active-bg, rgba(0, 0, 0, 0.06)) !important;
+          font-weight: 600;
+        }
+      }
+
+      .el-sub-menu__title {
+        color: v-bind(getMenuTextColor);
+      }
+
+      :deep(.el-sub-menu.is-active > .el-sub-menu__title) {
+        color: v-bind(theme) !important;
+        font-weight: 600;
+        background-color: var(--menu-active-bg, rgba(0, 0, 0, 0.06)) !important;
+        border-radius: 14px;
+        margin: 0 10px 6px !important;
+        // width: calc(100% - 20px) !important;
+        padding-left: 10px !important;
+        padding-right: 10px !important;
+        box-sizing: border-box;
+        overflow: hidden;
+        background-clip: padding-box;
+      }
+
+      :deep(.el-menu-item.is-active) {
+        margin: 0 10px 6px !important;
+        width: calc(100% - 20px) !important;
+        padding-left: 10px !important;
+        padding-right: 10px !important;
+        box-sizing: border-box;
+        overflow: hidden;
+        background-clip: padding-box;
+        border-radius: 14px;
+      }
+
+      :deep(.el-sub-menu.is-active > .el-sub-menu__title .menu-title),
+      :deep(.el-sub-menu.is-active > .el-sub-menu__title .svg-icon),
+      :deep(.el-menu-item.is-active .menu-title),
+      :deep(.el-menu-item.is-active .svg-icon) {
+        color: v-bind(theme) !important;
+      }
+
+      :deep(.el-sub-menu__title:hover),
+      :deep(.el-menu-item:hover) {
+        border-radius: 14px;
+      }
+    }
+  }
+</style>
diff --git a/src/layout/components/TagsView/ScrollPane.vue b/src/layout/components/TagsView/ScrollPane.vue
index 0189a17..9067108 100644
--- a/src/layout/components/TagsView/ScrollPane.vue
+++ b/src/layout/components/TagsView/ScrollPane.vue
@@ -101,7 +101,7 @@
     bottom: 0px;
   }
   :deep(.el-scrollbar__wrap) {
-    height: 39px;
+    height: 42px;
   }
 }
 </style>
\ No newline at end of file
diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue
index d3aa736..8fe064a 100644
--- a/src/layout/components/TagsView/index.vue
+++ b/src/layout/components/TagsView/index.vue
@@ -13,9 +13,9 @@
         @contextmenu.prevent="openMenu(tag, $event)"
       >
         {{ tag.title }}
-        <span v-if="!isAffix(tag)" @click.prevent.stop="closeSelectedTag(tag)">
-          <close class="el-icon-close" style="width: 1em; height: 1em;vertical-align: middle;" />
-        </span>
+        <span v-if="!isAffix(tag)" class="tags-view-close" @click.prevent.stop="closeSelectedTag(tag)">
+          <close class="el-icon-close" />
+        </span>
       </router-link>
     </scroll-pane>
     <ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu">
@@ -258,44 +258,54 @@
 </script>
 
 <style lang="scss" scoped>
-.tags-view-container {
-  height: 34px;
-  width: 100%;
-  background: transparent;
-
-  .tags-view-wrapper {
-    .tags-view-item {
-      display: inline-block;
-      position: relative;
-      cursor: pointer;
-      height: 32px;
-      line-height: 32px;
-      //border: 1px solid var(--tags-item-border, #d8dce5);
-      color: #4E5463;
-      background: #E5E7EA;
-      padding: 0 16px;
-      font-size: 12px;
-      //margin-left: 5px;
-      //margin-top: 4px;
-
-      //&:first-of-type {
-      //  margin-left: 8px;
-      //}
-      //
-      //&:last-of-type {
-      //  margin-right: 15px;
-      //}
-
-      &.active {
-        background-color: #FFFFFF !important;
-        color: #2C51D9;
-      }
-    }
-    //.tags-view-item div {
-    //  transform: skew(12deg);
-    //  display: inline-block;
-    //}
-  }
+.tags-view-container {
+  height: 42px;
+  width: 100%;
+  margin-top: 10px;
+  padding: 4px 10px;
+  background: rgba(255, 255, 255, 0.9);
+  border: 1px solid rgba(216, 225, 219, 0.92);
+  border-radius: 20px;
+  backdrop-filter: blur(18px);
+  box-shadow: var(--shadow-sm);
+
+  .tags-view-wrapper {
+    display: flex;
+    align-items: center;
+    min-height: 42px;
+
+    .tags-view-item {
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+      position: relative;
+      cursor: pointer;
+      height: 34px;
+      line-height: 1;
+      color: var(--tags-item-text, #4E5463);
+      background: var(--tags-item-bg, #E5E7EA);
+      border: 1px solid var(--tags-item-border, #d8dce5);
+      border-radius: 999px;
+      padding: 0 16px;
+      font-size: 12px;
+      margin-right: 8px;
+      flex-shrink: 0;
+      gap: 6px;
+      transition: all 0.24s ease;
+
+      &:hover {
+        background: var(--tags-item-hover, #eee);
+        border-color: rgba(31, 122, 114, 0.18);
+      }
+
+      &.active {
+        background-color: #FFFFFF !important;
+        color: var(--el-color-primary);
+        box-shadow: 0 10px 24px rgba(31, 122, 114, 0.12);
+        border-color: rgba(31, 122, 114, 0.2) !important;
+      }
+    }
+  }
 
   .contextmenu {
     margin: 0;
@@ -304,12 +314,12 @@
     position: absolute;
     list-style-type: none;
     padding: 5px 0;
-    border-radius: 4px;
+    border-radius: 16px;
     font-size: 12px;
     font-weight: 400;
     color: var(--tags-item-text, #333);
-    box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
-    border: 1px solid var(--el-border-color-light, #e4e7ed);
+    box-shadow: var(--shadow-md);
+    border: 1px solid var(--surface-border, #e4e7ed);
 
     li {
       margin: 0;
@@ -326,30 +336,56 @@
 
 <style lang="scss">
 //reset element css of el-icon-close
-.tags-view-wrapper {
-  .tags-view-item {
-    .el-icon-close {
-      width: 16px;
-      height: 16px;
-      vertical-align: 2px;
-      border-radius: 50%;
-      text-align: center;
-      transition: all .3s cubic-bezier(.645, .045, .355, 1);
-      transform-origin: 100% 50%;
-
-      &:before {
-        transform: scale(.6);
-        display: inline-block;
-        vertical-align: -3px;
-      }
-
-      &:hover {
-        background-color: var(--tags-close-hover, #b4bccc);
-        color: #fff;
-        width: 12px !important;
-        height: 12px !important;
-      }
-    }
-  }
-}
-</style>
\ No newline at end of file
+.tags-view-wrapper {
+  .el-scrollbar__view {
+    display: flex;
+    align-items: center;
+  }
+
+  .tags-view-item {
+    .tags-view-close {
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+      width: 12px;
+      height: 12px;
+      line-height: 1;
+      align-self: center;
+      transform: translateY(1px);
+    }
+
+    .el-icon-close {
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+      width: 12px;
+      height: 12px;
+      line-height: 1;
+      vertical-align: initial !important;
+      border-radius: 50%;
+      text-align: center;
+      transition: all .3s cubic-bezier(.645, .045, .355, 1);
+      transform-origin: 100% 50%;
+      align-self: center;
+
+      &:before {
+        transform: scale(.6);
+        display: inline-flex;
+        align-items: center;
+        justify-content: center;
+      }
+
+      svg {
+        display: block;
+        width: 10px;
+        height: 10px;
+      }
+
+      &:hover {
+        background-color: var(--tags-close-hover, #b4bccc);
+        color: #fff;
+      }
+    }
+  }
+}
+</style>
diff --git a/src/layout/index.vue b/src/layout/index.vue
index 67ac803..03c13ba 100644
--- a/src/layout/index.vue
+++ b/src/layout/index.vue
@@ -1,114 +1,136 @@
-<template>
-  <div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
-    <div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
-    <sidebar v-if="!sidebar.hide" class="sidebar-container" />
-    <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
-      <div :class="{ 'fixed-header': fixedHeader }">
-        <navbar @setLayout="setLayout" />
-        <tags-view v-if="needTagsView" />
-      </div>
-      <app-main />
-      <settings ref="settingRef" />
-    </div>
-  </div>
-</template>
-
-<script setup>
-import { useWindowSize } from '@vueuse/core'
-import Sidebar from './components/Sidebar/index.vue'
-import { AppMain, Navbar, Settings, TagsView } from './components'
-import defaultSettings from '@/settings'
-
-import useAppStore from '@/store/modules/app'
-import useSettingsStore from '@/store/modules/settings'
-
-const settingsStore = useSettingsStore()
-const theme = computed(() => settingsStore.theme)
-const sideTheme = computed(() => settingsStore.sideTheme)
-const sidebar = computed(() => useAppStore().sidebar)
-const device = computed(() => useAppStore().device)
-const needTagsView = computed(() => settingsStore.tagsView)
-const fixedHeader = computed(() => settingsStore.fixedHeader)
-
-const classObj = computed(() => ({
-  hideSidebar: !sidebar.value.opened,
-  openSidebar: sidebar.value.opened,
-  withoutAnimation: sidebar.value.withoutAnimation,
-  mobile: device.value === 'mobile'
-}))
-
-const { width, height } = useWindowSize()
-const WIDTH = 992 // refer to Bootstrap's responsive design
-
-watch(() => device.value, () => {
-  if (device.value === 'mobile' && sidebar.value.opened) {
-    useAppStore().closeSideBar({ withoutAnimation: false })
-  }
-})
-
-watchEffect(() => {
-  if (width.value - 1 < WIDTH) {
-    useAppStore().toggleDevice('mobile')
-    useAppStore().closeSideBar({ withoutAnimation: true })
-  } else {
-    useAppStore().toggleDevice('desktop')
-  }
-})
-
-function handleClickOutside() {
-  useAppStore().closeSideBar({ withoutAnimation: false })
-}
-
-const settingRef = ref(null)
-function setLayout() {
-  settingRef.value.openSetting()
-}
-</script>
-
-<style lang="scss" scoped>
-  @import "@/assets/styles/mixin.scss";
-  @import "@/assets/styles/variables.module.scss";
-
-.app-wrapper {
-  @include clearfix;
-  position: relative;
-  height: 100%;
-  width: 100%;
-
-  &.mobile.openSidebar {
-    position: fixed;
-    top: 0;
-  }
-}
-
-.drawer-bg {
-  background: #000;
-  opacity: 0.3;
-  width: 100%;
-  top: 0;
-  height: 100%;
-  position: absolute;
-  z-index: 999;
-}
-
-.fixed-header {
-  position: fixed;
-  top: 0;
-  right: 0;
-  z-index: 9;
-  width: calc(100% - #{$base-sidebar-width});
-  transition: width 0.28s;
-}
-
-.hideSidebar .fixed-header {
-  width: calc(100% - 54px);
-}
-
-.sidebarHide .fixed-header {
-  width: 100%;
-}
-
-.mobile .fixed-header {
-  width: 100%;
-}
-</style>
\ No newline at end of file
+<template>
+  <div :class="classObj"
+       class="app-wrapper"
+       :style="{ '--current-color': theme }">
+    <div v-if="device === 'mobile' && sidebar.opened"
+         class="drawer-bg"
+         @click="handleClickOutside" />
+    <sidebar v-if="!sidebar.hide"
+             class="sidebar-container" />
+    <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }"
+         class="main-container">
+      <div :class="{ 'fixed-header': fixedHeader }">
+        <navbar @setLayout="setLayout" />
+        <tags-view v-if="needTagsView" />
+      </div>
+      <app-main />
+      <settings ref="settingRef" />
+    </div>
+    <AIChatSidebar v-if="aiEnabled" />
+  </div>
+</template>
+
+<script setup>
+  import { useWindowSize } from "@vueuse/core";
+  import Sidebar from "./components/Sidebar/index.vue";
+  import { AppMain, Navbar, Settings, TagsView } from "./components";
+  import AIChatSidebar from "@/components/AIChatSidebar/index.vue";
+  import defaultSettings from "@/settings";
+
+  import useAppStore from "@/store/modules/app";
+  import useUserStore from "@/store/modules/user";
+  import useSettingsStore from "@/store/modules/settings";
+
+  const settingsStore = useSettingsStore();
+  const userStore = useUserStore();
+  const theme = computed(() => settingsStore.theme);
+  const sideTheme = computed(() => settingsStore.sideTheme);
+  const sidebar = computed(() => useAppStore().sidebar);
+  const device = computed(() => useAppStore().device);
+  const needTagsView = computed(() => settingsStore.tagsView);
+  const fixedHeader = computed(() => settingsStore.fixedHeader);
+  const aiEnabled = computed(() => Number(userStore.aiEnabled) === 1);
+
+  const classObj = computed(() => ({
+    hideSidebar: !sidebar.value.opened,
+    openSidebar: sidebar.value.opened,
+    withoutAnimation: sidebar.value.withoutAnimation,
+    mobile: device.value === "mobile",
+  }));
+
+  const { width, height } = useWindowSize();
+  const WIDTH = 992; // refer to Bootstrap's responsive design
+
+  watch(
+    () => device.value,
+    () => {
+      if (device.value === "mobile" && sidebar.value.opened) {
+        useAppStore().closeSideBar({ withoutAnimation: false });
+      }
+    }
+  );
+
+  watchEffect(() => {
+    if (width.value - 1 < WIDTH) {
+      useAppStore().toggleDevice("mobile");
+      useAppStore().closeSideBar({ withoutAnimation: true });
+    } else {
+      useAppStore().toggleDevice("desktop");
+    }
+  });
+
+  function handleClickOutside() {
+    useAppStore().closeSideBar({ withoutAnimation: false });
+  }
+
+  const settingRef = ref(null);
+  function setLayout() {
+    settingRef.value.openSetting();
+  }
+</script>
+
+<style lang="scss" scoped>
+  @import "@/assets/styles/mixin.scss";
+  @import "@/assets/styles/variables.module.scss";
+
+  .app-wrapper {
+    @include clearfix;
+    position: relative;
+    height: 100%;
+    width: 100%;
+    background: radial-gradient(
+        circle at top,
+        rgba(223, 232, 226, 0.95),
+        transparent 32%
+      ),
+      linear-gradient(180deg, #f7faf8 0%, var(--app-bg) 100%);
+
+    &.mobile.openSidebar {
+      position: fixed;
+      top: 0;
+    }
+  }
+
+  .drawer-bg {
+    background: #000;
+    opacity: 0.3;
+    width: 100%;
+    top: 0;
+    height: 100%;
+    position: absolute;
+    z-index: 999;
+  }
+
+  .fixed-header {
+    position: fixed;
+    top: 0px;
+    padding-top: 12px;
+    right: 16px;
+    z-index: 9;
+    width: calc(100% - #{$base-sidebar-width} - 32px);
+    transition: width 0.28s, right 0.28s;
+    padding-bottom: 8px;
+    background-color: #f3f6f4;
+  }
+  .hideSidebar .fixed-header {
+    width: calc(100% - 100px);
+  }
+
+  .sidebarHide .fixed-header {
+    width: calc(100% - 32px);
+  }
+
+  .mobile .fixed-header {
+    width: 100%;
+  }
+</style>
diff --git a/src/main.js b/src/main.js
index 0b3f714..025ff14 100644
--- a/src/main.js
+++ b/src/main.js
@@ -43,11 +43,13 @@
 // 瀵屾枃鏈粍浠�
 import Editor from "@/components/Editor";
 // 鏂囦欢涓婁紶缁勪欢
-import FileUpload from "@/components/FileUpload";
+import FileUpload from "@/components/AttachmentUpload/file";
 // 鍥剧墖涓婁紶缁勪欢
-import ImageUpload from "@/components/ImageUpload";
+import ImageUpload from "@/components/AttachmentUpload/image";
 // 鍥剧墖棰勮缁勪欢
-import ImagePreview from "@/components/ImagePreview";
+import ImagePreview from "@/components/AttachmentPreview/image";
+// 闄勪欢寮圭獥缁勪欢
+import FileListDialog from "@/components/Dialog/FileList.vue";
 // 瀛楀吀鏍囩缁勪欢
 import DictTag from "@/components/DictTag";
 // 琛ㄦ牸缁勪欢
@@ -92,6 +94,7 @@
 app.component("FileUpload", FileUpload);
 app.component("ImageUpload", ImageUpload);
 app.component("ImagePreview", ImagePreview);
+app.component("FileListDialog", FileListDialog);
 app.component("RightToolbar", RightToolbar);
 app.component("Editor", Editor);
 app.component("PIMTable", PIMTable);
diff --git a/src/router/index.js b/src/router/index.js
index f342004..2bb5586 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -47,6 +47,20 @@
     component: () => import("@/views/register"),
     hidden: true,
   },
+  // 绯荤粺鏋舵瀯鍥�
+  // {
+  //   path: "/system-architecture",
+  //   component: Layout,
+  //   redirect: "/system-architecture/index",
+  //   children: [
+  //     {
+  //       path: "index",
+  //       component: () => import("@/views/systemArchitecture/index.vue"),
+  //       name: "SystemArchitecture",
+  //       meta: { title: "绯荤粺鏋舵瀯鍥�", icon: "tree" },
+  //     },
+  //   ],
+  // },
   {
     path: "/:pathMatch(.*)*",
     component: () => import("@/views/error/404"),
@@ -119,6 +133,119 @@
       },
     ],
   },
+  // 璐㈠姟绠$悊妯″潡璺敱
+  // {
+  //   path: "/financial",
+  //   component: Layout,
+  //   hidden: false,
+  //   redirect: "/financial/general-ledger",
+  //   alwaysShow: true,
+  //   meta: { title: "璐㈠姟绠$悊", icon: "money" },
+  //   children: [
+  //     {
+  //       path: "general-ledger",
+  //       component: () => import("@/views/financialManagement/generalLedger/index.vue"),
+  //       name: "GeneralLedger",
+  //       meta: { title: "鎬诲笎绉戠洰" },
+  //     },
+  //     {
+  //       path: "sales-out",
+  //       component: () => import("@/views/financialManagement/receivable/salesOut.vue"),
+  //       name: "SalesOut",
+  //       meta: { title: "閿�鍞嚭搴�" },
+  //     },
+  //     {
+  //       path: "sales-return",
+  //       component: () => import("@/views/financialManagement/receivable/salesReturn.vue"),
+  //       name: "SalesReturn",
+  //       meta: { title: "閿�鍞��璐�" },
+  //     },
+  //     {
+  //       path: "receivable-reconciliation",
+  //       component: () => import("@/views/financialManagement/receivable/reconciliation.vue"),
+  //       name: "ReceivableReconciliation",
+  //       meta: { title: "搴旀敹瀵硅处" },
+  //     },
+  //     {
+  //       path: "invoice-apply",
+  //       component: () => import("@/views/financialManagement/receivable/invoiceApply.vue"),
+  //       name: "InvoiceApply",
+  //       meta: { title: "寮�绁ㄧ敵璇�" },
+  //     },
+  //     {
+  //       path: "output-invoice",
+  //       component: () => import("@/views/financialManagement/receivable/outputInvoice.vue"),
+  //       name: "OutputInvoice",
+  //       meta: { title: "閿�椤瑰彂绁�" },
+  //     },
+  //     {
+  //       path: "receipt",
+  //       component: () => import("@/views/financialManagement/receivable/receipt.vue"),
+  //       name: "Receipt",
+  //       meta: { title: "鏀舵鍗�" },
+  //     },
+  //     {
+  //       path: "purchase-in",
+  //       component: () => import("@/views/financialManagement/payable/purchaseIn.vue"),
+  //       name: "PurchaseIn",
+  //       meta: { title: "閲囪喘鍏ュ簱" },
+  //     },
+  //     {
+  //       path: "payable-reconciliation",
+  //       component: () => import("@/views/financialManagement/payable/reconciliation.vue"),
+  //       name: "PayableReconciliation",
+  //       meta: { title: "搴斾粯瀵硅处" },
+  //     },
+  //     {
+  //       path: "input-invoice",
+  //       component: () => import("@/views/financialManagement/payable/input-invoice.vue"),
+  //       name: "InputInvoice",
+  //       meta: { title: "杩涢」鍙戠エ" },
+  //     },
+  //     {
+  //       path: "payment-apply",
+  //       component: () => import("@/views/financialManagement/payable/paymentApply.vue"),
+  //       name: "PaymentApply",
+  //       meta: { title: "浠樻鐢宠" },
+  //     },
+  //     {
+  //       path: "payment",
+  //       component: () => import("@/views/financialManagement/payable/payment.vue"),
+  //       name: "Payment",
+  //       meta: { title: "浠樻鍗�" },
+  //     },
+  //     {
+  //       path: "fixed-assets",
+  //       component: () => import("@/views/financialManagement/assets/fixedAssets.vue"),
+  //       name: "FixedAssets",
+  //       meta: { title: "鍥哄畾璧勪骇" },
+  //     },
+  //     {
+  //       path: "intangible-assets",
+  //       component: () => import("@/views/financialManagement/assets/intangibleAssets.vue"),
+  //       name: "IntangibleAssets",
+  //       meta: { title: "鏃犲舰璧勪骇" },
+  //     },
+  //     {
+  //       path: "voucher",
+  //       component: () => import("@/views/financialManagement/voucher/index.vue"),
+  //       name: "Voucher",
+  //       meta: { title: "鍑瘉" },
+  //     },
+  //     {
+  //       path: "voucher-general-ledger",
+  //       component: () => import("@/views/financialManagement/voucher/generalLedger.vue"),
+  //       name: "VoucherGeneralLedger",
+  //       meta: { title: "绉戠洰鎬诲笎" },
+  //     },
+  //     {
+  //       path: "voucher-detail-ledger",
+  //       component: () => import("@/views/financialManagement/voucher/detailLedger.vue"),
+  //       name: "VoucherDetailLedger",
+  //       meta: { title: "绉戠洰鏄庣粏甯�" },
+  //     },
+  //   ],
+  // },
 ];
 
 // 鍔ㄦ�佽矾鐢憋紝鍩轰簬鐢ㄦ埛鏉冮檺鍔ㄦ�佸幓鍔犺浇
diff --git a/src/store/modules/permission.js b/src/store/modules/permission.js
index 6a16d9d..d3f0594 100644
--- a/src/store/modules/permission.js
+++ b/src/store/modules/permission.js
@@ -1,9 +1,10 @@
 import auth from '@/plugins/auth'
 import router, { constantRoutes, dynamicRoutes } from '@/router'
 import { getRouters } from '@/api/menu'
-import Layout from '@/layout/index'
-import ParentView from '@/components/ParentView'
-import InnerLink from '@/layout/components/InnerLink'
+import Layout from '@/layout/index'
+import ParentView from '@/components/ParentView'
+import InnerLink from '@/layout/components/InnerLink'
+import useUserStore from '@/store/modules/user'
 
 // 鍖归厤views閲岄潰鎵�鏈夌殑.vue鏂囦欢
 const modules = import.meta.glob('./../../views/**/*.vue')
@@ -36,15 +37,18 @@
         return new Promise(resolve => {
           // 鍚戝悗绔姹傝矾鐢辨暟鎹�
           getRouters().then(res => {
-            const sdata = JSON.parse(JSON.stringify(res.data))
-            const rdata = JSON.parse(JSON.stringify(res.data))
-            const defaultData = JSON.parse(JSON.stringify(res.data))
+            const aiEnabled = Number(useUserStore().aiEnabled) === 1
+            const rawRoutes = filterAiFeatureRoutes(res.data, aiEnabled)
+            const sdata = JSON.parse(JSON.stringify(rawRoutes))
+            const rdata = JSON.parse(JSON.stringify(rawRoutes))
+            const defaultData = JSON.parse(JSON.stringify(rawRoutes))
             const sidebarRoutes = filterAsyncRouter(sdata)
             const rewriteRoutes = filterAsyncRouter(rdata, false, true)
             const defaultRoutes = filterAsyncRouter(defaultData)
             const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
             asyncRoutes.forEach(route => { router.addRoute(route) })
             this.setRoutes(rewriteRoutes)
+            // 灏嗚储鍔$鐞嗚矾鐢卞悎骞跺埌渚ц竟鏍�
             this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
             this.setDefaultRoutes(sidebarRoutes)
             this.setTopbarRoutes(defaultRoutes)
@@ -56,7 +60,38 @@
   })
 
 // 閬嶅巻鍚庡彴浼犳潵鐨勮矾鐢卞瓧绗︿覆锛岃浆鎹负缁勪欢瀵硅薄
-function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
+function filterAiFeatureRoutes(routes = [], aiEnabled = false) {
+  if (aiEnabled) {
+    return routes
+  }
+  return routes.reduce((acc, route) => {
+    if (!route || isAiFeatureRoute(route)) {
+      return acc
+    }
+    const nextRoute = { ...route }
+    if (Array.isArray(nextRoute.children) && nextRoute.children.length > 0) {
+      nextRoute.children = filterAiFeatureRoutes(nextRoute.children, aiEnabled)
+    }
+    acc.push(nextRoute)
+    return acc
+  }, [])
+}
+
+function isAiFeatureRoute(route = {}) {
+  const path = String(route.path || '').toLowerCase()
+  const component = String(route.component || '').toLowerCase()
+  const name = String(route.name || '').toLowerCase()
+  const title = String(route?.meta?.title ?? route?.title ?? '')
+
+  return (
+    path.includes('chathome') ||
+    component.includes('chathome') ||
+    name.includes('chathome') ||
+    title.includes('AI')
+  )
+}
+
+function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
   return asyncRouterMap.filter(route => {
     if (type && route.children) {
       route.children = filterChildren(route.children)
diff --git a/src/store/modules/user.js b/src/store/modules/user.js
index 4f3eab4..ea358d1 100644
--- a/src/store/modules/user.js
+++ b/src/store/modules/user.js
@@ -7,13 +7,14 @@
 const useUserStore = defineStore(
   'user',
   {
-    state: () => ({
-      token: getToken(),
-      id: '',
-      name: '',
-      avatar: '',
-      roles: [],
-      permissions: []
+    state: () => ({
+      token: getToken(),
+      id: '',
+      name: '',
+      avatar: '',
+      roles: [],
+      permissions: [],
+      aiEnabled: 0
     }),
     actions: {
       // 鐧诲綍
@@ -58,29 +59,31 @@
             this.id = user.userId
             this.name = user.userName
             this.avatar = avatar
-            this.currentFactoryName = user.currentFactoryName
-            this.nickName = user.nickName
-            this.roleName = user.roles[0].roleName
-            this.currentDeptId = user.tenantId
-            this.currentLoginTime = this.getCurrentTime()
-            resolve(res)
-          }).catch(error => {
-            reject(error)
-          })
-        })
+            this.currentFactoryName = user.currentFactoryName
+            this.nickName = user.nickName
+            this.roleName = user.roles[0].roleName
+            this.currentDeptId = user.tenantId
+            this.currentLoginTime = this.getCurrentTime()
+            this.aiEnabled = Number(res.aiEnabled) === 1 ? 1 : 0
+            resolve(res)
+          }).catch(error => {
+            reject(error)
+          })
+        })
       },
       // 閫�鍑虹郴缁�
       logOut() {
         return new Promise((resolve, reject) => {
           logout(this.token).then(() => {
-            this.token = ''
-            this.roles = []
-            this.permissions = []
-            removeToken()
-            resolve()
-          }).catch(error => {
-            reject(error)
-          })
+            this.token = ''
+            this.roles = []
+            this.permissions = []
+            this.aiEnabled = 0
+            removeToken()
+            resolve()
+          }).catch(error => {
+            reject(error)
+          })
         })
       },
       // 鐧诲綍鏍¢獙
diff --git a/src/utils/generator/html.js b/src/utils/generator/html.js
index 4b29841..eacb48e 100644
--- a/src/utils/generator/html.js
+++ b/src/utils/generator/html.js
@@ -8,8 +8,8 @@
   return `<el-dialog v-model="dialogVisible"  @open="onOpen" @close="onClose" title="Dialog Titile">
     ${str}
     <template #footer>
-      <el-button @click="close">鍙栨秷</el-button>
-	  <el-button type="primary" @click="handelConfirm">纭畾</el-button>
+     <el-button type="primary" @click="handelConfirm">纭畾</el-button>
+     <el-button @click="close">鍙栨秷</el-button>
     </template>
   </el-dialog>`
 }
diff --git a/src/views/basicData/customerFile/index.vue b/src/views/basicData/customerFile/index.vue
index ad1c5bb..a080bd9 100644
--- a/src/views/basicData/customerFile/index.vue
+++ b/src/views/basicData/customerFile/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form" style="margin-bottom: 20px;">
       <div>
         <span class="search_title">瀹㈡埛鍚嶇О锛�</span>
         <el-input v-model="searchForm.customerName"
@@ -211,7 +211,8 @@
                  :limit="1"
                  accept=".xlsx, .xls"
                  :headers="upload.headers"
-                 :action="upload.url + '?updateSupport=' + upload.updateSupport"
+                 :action="upload.url"
+                 :data="upload.data"
                  :disabled="upload.isUploading"
                  :before-upload="upload.beforeUpload"
                  :on-progress="upload.onProgress"
@@ -276,9 +277,8 @@
       </el-form>
       <template #footer>
         <div class="dialog-footer">
+          <el-button type="primary" @click="submitReminderForm">纭</el-button>
           <el-button @click="closeReminderDialog">鍙栨秷</el-button>
-          <el-button type="primary"
-                     @click="submitReminderForm">鎻愪氦</el-button>
         </div>
       </template>
     </el-dialog>
@@ -360,9 +360,8 @@
       </el-form>
       <template #footer>
         <div class="dialog-footer">
+          <el-button type="primary" @click="submitNegotiationForm">纭</el-button>
           <el-button @click="closeNegotiationDialog">鍙栨秷</el-button>
-          <el-button type="primary"
-                     @click="submitNegotiationForm">鎻愪氦</el-button>
         </div>
       </template>
     </el-dialog>
@@ -494,7 +493,6 @@
             <template #default="{ row }">
               <el-button type="info"
                          link
-                         size="small"
                          @click="openAttachmentDialog(row)">
                 <el-icon>
                   <Paperclip />
@@ -510,13 +508,11 @@
             <template #default="{ row, $index }">
               <el-button type="primary"
                          link
-                         size="small"
                          @click="editNegotiationRecord(row, $index)">
                 淇敼
               </el-button>
               <el-button type="danger"
                          link
-                         size="small"
                          @click="deleteNegotiationRecord(row, $index)">
                 鍒犻櫎
               </el-button>
@@ -587,13 +583,11 @@
               <template #default="{ row, $index }">
                 <el-button type="primary"
                            link
-                           size="small"
                            @click="downloadAttachment(row)">
                   涓嬭浇
                 </el-button>
                 <el-button type="danger"
                            link
-                           size="small"
                            @click="deleteAttachment(row, $index)">
                   鍒犻櫎
                 </el-button>
@@ -619,17 +613,13 @@
   import { onMounted, ref, reactive, getCurrentInstance, toRefs } from "vue";
   import { Search, Paperclip, Upload } from "@element-plus/icons-vue";
   import {
-    addCustomer,
-    delCustomer,
-    getCustomer,
-    listCustomer,
-    updateCustomer,
     addCustomerFollow,
     updateCustomerFollow,
     delCustomerFollow,
     addReturnVisit,
     getReturnVisit,
   } from "@/api/basicData/customerFile.js";
+  import {listCustomer, getCustomer, addCustomer, updateCustomer, delCustomer} from "@/api/basicData/customer.js";
   import { ElMessageBox } from "element-plus";
   import { userListNoPage } from "@/api/system/user.js";
   import useUserStore from "@/store/modules/user";
@@ -661,7 +651,7 @@
   const negotiationFormRef = ref();
   const negotiationForm = reactive({
     customerName: "",
-    customerId: "",
+		customerId: "",
     followUpMethod: "",
     followUpLevel: "",
     followUpTime: "",
@@ -733,7 +723,7 @@
     },
     {
       label: "鍦板潃鍙婅仈绯绘柟寮�",
-      prop: "addressPhone",
+      prop: "companyAddress",
       width: 250,
     },
     {
@@ -775,6 +765,24 @@
       prop: "maintainer",
     },
     {
+      label: "瀹㈡埛鏉ユ簮",
+      prop: "type",
+      dataType: "tag",
+      width: 100,
+      formatData: value => {
+        if (value === 1 || value === "1") {
+          return "鍏捣";
+        }
+        return "绉佹捣";
+      },
+      formatType: value => {
+        if (value === 1 || value === "1") {
+          return "warning";
+        }
+        return "success";
+      },
+    },
+    {
       label: "缁存姢鏃堕棿",
       prop: "maintenanceTime",
       width: 100,
@@ -784,7 +792,7 @@
       label: "鎿嶄綔",
       align: "center",
       fixed: "right",
-      width: 250,
+      width: 290,
       operation: [
         {
           name: "缂栬緫",
@@ -793,13 +801,13 @@
             openForm("edit", row);
           },
         },
-        {
-          name: "璇︽儏",
-          type: "text",
-          clickFun: row => {
-            openDetailDialog(row);
-          },
-        },
+				{
+					name: "娣诲姞娲借皥杩涘害",
+					type: "text",
+					clickFun: row => {
+						openNegotiationDialog(row);
+					},
+				},
         {
           name: "鍥炶鎻愰啋",
           type: "text",
@@ -807,13 +815,13 @@
             openReminderDialog(row);
           },
         },
-        {
-          name: "娣诲姞娲借皥杩涘害",
-          type: "text",
-          clickFun: row => {
-            openNegotiationDialog(row);
-          },
-        },
+				{
+					name: "璇︽儏",
+					type: "text",
+					clickFun: row => {
+						openDetailDialog(row);
+					},
+				},
       ],
     },
   ]);
@@ -844,6 +852,7 @@
     searchForm: {
       customerName: "",
       customerType: "",
+      type: 0
     },
     form: {
       customerName: "",
@@ -858,6 +867,7 @@
       bankAccount: "",
       bankCode: "",
       customerType: "",
+      type: 0
     },
     rules: {
       customerName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
@@ -889,6 +899,9 @@
     headers: { Authorization: "Bearer " + getToken() },
     // 涓婁紶鐨勫湴鍧�
     url: import.meta.env.VITE_APP_BASE_API + "/basic/customer/importData",
+    data: {
+      type: 0
+    },
     // 鏂囦欢涓婁紶鍓嶇殑鍥炶皟
     beforeUpload: file => {
       console.log("鏂囦欢鍗冲皢涓婁紶", file);
@@ -961,8 +974,8 @@
     tableLoading.value = true;
     listCustomer({ ...searchForm.value, ...page }).then(res => {
       tableLoading.value = false;
-      tableData.value = res.records;
-      page.total = res.total;
+      tableData.value = res.data.records;
+      page.total = res.data.total;
     });
   };
   // 琛ㄦ牸閫夋嫨鏁版嵁
@@ -994,6 +1007,7 @@
         contactPhone: "",
       },
     ];
+    form.value.type = 0;
     form.value.maintenanceTime = getCurrentDate();
     userListNoPage().then(res => {
       userList.value = res.data;
@@ -1069,7 +1083,7 @@
       type: "warning",
     })
       .then(() => {
-        proxy.download("/basic/customer/export", {}, "瀹㈡埛妗f.xlsx");
+        proxy.download("/basic/customer/export", {type: 0}, "瀹㈡埛妗f.xlsx");
       })
       .catch(() => {
         proxy.$modal.msg("宸插彇娑�");
@@ -1079,12 +1093,11 @@
   const handleDelete = () => {
     let ids = [];
     if (selectedRows.value.length > 0) {
-      // 妫�鏌ユ槸鍚︽湁浠栦汉缁存姢鐨勬暟鎹�
       const unauthorizedData = selectedRows.value.filter(
-        item => item.maintainer !== userStore.nickName
+        item => item.type === 1 || item.type === "1"
       );
       if (unauthorizedData.length > 0) {
-        proxy.$modal.msgWarning("涓嶅彲鍒犻櫎浠栦汉缁存姢鐨勬暟鎹�");
+        proxy.$modal.msgWarning("鍏捣鍒嗛厤鐨勫鎴蜂笉鑳藉垹闄�");
         return;
       }
       ids = selectedRows.value.map(item => item.id);
@@ -1100,7 +1113,7 @@
       .then(() => {
         tableLoading.value = true;
         delCustomer(ids)
-          .then(res => {
+          .then(() => {
             proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
             getList();
           })
@@ -1153,7 +1166,7 @@
         if (reminderForm.id) {
           submitvalue.value = {
             id: reminderForm.id,
-            customerId: currentCustomerId.value,
+						customerId: currentCustomerId.value,
             isEnabled: reminderForm.reminderSwitch ? 1 : 0,
             content: reminderForm.reminderContent,
             reminderTime: reminderForm.reminderTime,
@@ -1168,8 +1181,6 @@
             remindUserId: userStore.id,
           };
         }
-
-        console.log("鎻愪氦鍥炶鎻愰啋鏁版嵁:", submitvalue.value);
 
         // 璋冪敤鎺ュ彛
         addReturnVisit(submitvalue.value)
@@ -1198,14 +1209,6 @@
     negotiationForm.followUpTime = "";
     negotiationForm.followerUserName = userStore.nickName; // 榛樿褰撳墠鐧诲綍浜�
     negotiationForm.content = "";
-    // {
-    // 	"customerId": 152,
-    // 	"followUpMethod": "鐢佃瘽娌熼��",
-    // 	"followUpLevel": "娌℃湁鎰忓悜",
-    // 	"followUpTime": "2026-03-04T15:30:00",
-    // 	"followerUserName": "绠$悊鍛樿处鍙�",
-    // 	"content": "111"
-    // }
     negotiationDialogVisible.value = true;
   };
 
@@ -1227,23 +1230,6 @@
 
         if (isEdit) {
           // 淇敼鎿嶄綔
-          console.log("淇敼娲借皥杩涘害鏁版嵁:", negotiationForm);
-          // 杩欓噷鍙互璋冪敤鏇存柊鎺ュ彛
-          // 瀹為檯椤圭洰涓渶瑕佹牴鎹悗绔帴鍙h繘琛岃皟鏁�
-          // 绀轰緥锛歶pdateCustomerFollow(negotiationForm).then(res => {
-          //   // 鏇存柊鏈湴鏁版嵁
-          //   const index = negotiationForm.editIndex;
-          //   negotiationRecords.value[index] = {
-          //     followUpTime: negotiationForm.followUpTime,
-          //     followUpMethod: negotiationForm.followUpMethod,
-          //     followUpLevel: negotiationForm.followUpLevel,
-          //     followerUserName: negotiationForm.followerUserName,
-          //     content: negotiationForm.content,
-          //     id: negotiationForm.id,
-          //   };
-          //   proxy.$modal.msgSuccess("淇敼鎴愬姛");
-          //   closeNegotiationDialog();
-          // });
           updateCustomerFollow(negotiationForm).then(res => {
             // 鏇存柊鏈湴鏁版嵁
             getCustomer(negotiationForm.customerId).then(res => {
@@ -1278,7 +1264,6 @@
 
   // 鎵撳紑璇︽儏寮圭獥
   const openDetailDialog = row => {
-    // 璋冪敤getCustomer鎺ュ彛鑾峰彇瀹㈡埛璇︽儏
     getCustomer(row.id).then(res => {
       // 濉厖瀹㈡埛鍩烘湰淇℃伅
       Object.assign(detailForm, res.data);
@@ -1300,7 +1285,7 @@
     // 灏嗗綋鍓嶈褰曟暟鎹~鍏呭埌琛ㄥ崟
     Object.assign(negotiationForm, {
       customerName: row.customerName,
-      customerId: row.customerId,
+			customerId: row.customerId,
       followUpMethod: row.followUpMethod,
       followUpLevel: row.followUpLevel,
       followUpTime: row.followUpTime,
diff --git a/src/views/basicData/customerFileOpenSea/index.vue b/src/views/basicData/customerFileOpenSea/index.vue
new file mode 100644
index 0000000..2598f48
--- /dev/null
+++ b/src/views/basicData/customerFileOpenSea/index.vue
@@ -0,0 +1,1803 @@
+<template>
+  <div class="app-container">
+    <div class="search_form" style="margin-bottom: 20px;">
+      <div>
+        <span class="search_title">瀹㈡埛鍚嶇О锛�</span>
+        <el-input v-model="searchForm.customerName"
+                  style="width: 240px;margin-right: 10px"
+                  placeholder="璇疯緭鍏�"
+                  @change="handleQuery"
+                  clearable
+                  :prefix-icon="Search" />
+        <span class="search_title">瀹㈡埛鍒嗙被锛�</span>
+        <el-select v-model="searchForm.customerType"
+                   placeholder="璇烽�夋嫨"
+                   style="width: 240px"
+                   clearable
+                   @change="handleQuery">
+          <el-option label="闆跺敭瀹㈡埛"
+                     value="闆跺敭瀹㈡埛" />
+          <el-option label="杩涢攢鍟嗗鎴�"
+                     value="杩涢攢鍟嗗鎴�" />
+        </el-select>
+        <el-button type="primary"
+                   @click="handleQuery"
+                   style="margin-left: 10px">鎼滅储</el-button>
+      </div>
+      <div>
+        <el-button type="primary"
+                   @click="openForm('add')">鏂板瀹㈡埛</el-button>
+        <el-button @click="handleOut">瀵煎嚭</el-button>
+        <el-button type="info"
+                   plain
+                   icon="Upload"
+                   @click="handleImport">瀵煎叆</el-button>
+        <el-button type="danger"
+                   plain
+                   @click="handleDelete">鍒犻櫎</el-button>
+      </div>
+    </div>
+    <div class="table_list">
+      <PIMTable rowKey="id"
+                :column="tableColumn"
+                :tableData="tableData"
+                :page="page"
+                :isSelection="true"
+                @selection-change="handleSelectionChange"
+                :tableLoading="tableLoading"
+                @pagination="pagination"></PIMTable>
+    </div>
+    <el-dialog v-model="dialogFormVisible"
+               :title="operationType === 'add' ? '鏂板瀹㈡埛淇℃伅' : '缂栬緫瀹㈡埛淇℃伅'"
+               width="70%"
+               @close="closeDia">
+      <el-form :model="form"
+               label-width="140px"
+               label-position="top"
+               :rules="rules"
+               ref="formRef">
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="瀹㈡埛鍚嶇О锛�"
+                          prop="customerName">
+              <el-input v-model="form.customerName"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="绾崇◣浜鸿瘑鍒彿锛�"
+                          prop="taxpayerIdentificationNumber">
+              <el-input v-model="form.taxpayerIdentificationNumber"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鍏徃鍦板潃锛�"
+                          prop="companyAddress">
+              <el-input v-model="form.companyAddress"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鍏徃鐢佃瘽锛�"
+                          prop="companyPhone">
+              <el-input v-model="form.companyPhone"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="閾惰鍩烘湰鎴凤細"
+                          prop="basicBankAccount">
+              <el-input v-model="form.basicBankAccount"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="閾惰璐﹀彿锛�"
+                          prop="bankAccount">
+              <el-input v-model="form.bankAccount"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="寮�鎴疯鍙凤細"
+                          prop="bankCode">
+              <el-input v-model="form.bankCode"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="瀹㈡埛鍒嗙被锛�"
+                          prop="customerType">
+              <el-select v-model="form.customerType"
+                         placeholder="璇烽�夋嫨"
+                         clearable>
+                <el-option label="闆跺敭瀹㈡埛"
+                           value="闆跺敭瀹㈡埛" />
+                <el-option label="杩涢攢鍟嗗鎴�"
+                           value="杩涢攢鍟嗗鎴�" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30"
+                v-for="(contact, index) in formYYs.contactList"
+                :key="index">
+          <el-col :span="12">
+            <el-form-item label="鑱旂郴浜猴細"
+                          prop="contactPerson">
+              <el-input v-model="contact.contactPerson"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鑱旂郴鐢佃瘽锛�"
+                          prop="contactPhone">
+              <div style="display: flex; align-items: center;width: 100%;">
+                <el-input v-model="contact.contactPhone"
+                          placeholder="璇疯緭鍏�"
+                          clearable />
+                <el-button @click="removeContact(index)"
+                           type="danger"
+                           circle
+                           style="margin-left: 5px;">
+                  <el-icon>
+                    <Close />
+                  </el-icon>
+                </el-button>
+              </div>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-button @click="addNewContact"
+                   style="margin-bottom: 10px;">+ 鏂板鑱旂郴浜�</el-button>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="缁存姢浜猴細"
+                          prop="maintainer">
+              <el-select v-model="form.maintainer"
+                         placeholder="璇烽�夋嫨"
+                         clearable
+                         disabled>
+                <el-option v-for="item in userList"
+                           :key="item.nickName"
+                           :label="item.nickName"
+                           :value="item.nickName" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="缁存姢鏃堕棿锛�"
+                          prop="maintenanceTime">
+              <el-date-picker style="width: 100%"
+                              v-model="form.maintenanceTime"
+                              value-format="YYYY-MM-DD"
+                              format="YYYY-MM-DD"
+                              type="date"
+                              placeholder="璇烽�夋嫨"
+                              clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="submitForm">纭</el-button>
+          <el-button @click="closeDia">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <el-dialog v-model="assignDialogVisible"
+               title="鍒嗛厤瀹㈡埛"
+               width="500px"
+               @close="closeAssignDialog">
+      <el-form :model="assignForm"
+               :rules="assignRules"
+               ref="assignFormRef"
+               label-width="100px">
+        <el-form-item label="瀹㈡埛鍚嶇О">
+          <el-input v-model="assignForm.customerName"
+                    disabled />
+        </el-form-item>
+        <el-form-item label="鍒嗛厤浜哄憳"
+                      prop="boundId">
+          <el-select v-model="assignForm.boundId"
+                     placeholder="璇烽�夋嫨鍒嗛厤浜哄憳"
+                     style="width: 100%"
+                     filterable>
+            <el-option v-for="item in userList"
+                       :key="item.userId || item.nickName"
+                       :label="item.nickName"
+                       :value="item.userId" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="submitAssignForm">纭</el-button>
+          <el-button @click="closeAssignDialog">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <el-dialog v-model="shareDialogVisible"
+               title="鍏变韩瀹㈡埛"
+               width="500px"
+               @close="closeShareDialog">
+      <el-form :model="shareForm"
+               :rules="shareRules"
+               ref="shareFormRef"
+               label-width="100px">
+        <el-form-item label="瀹㈡埛鍚嶇О">
+          <el-input v-model="shareForm.customerName"
+                    disabled />
+        </el-form-item>
+        <el-form-item label="鍏变韩浜哄憳"
+                      prop="boundIds">
+          <el-select v-model="shareForm.boundIds"
+                     placeholder="璇烽�夋嫨鍏变韩浜哄憳"
+                     style="width: 100%"
+                     filterable
+                     multiple
+                     collapse-tags
+                     collapse-tags-tooltip>
+            <el-option v-for="item in userList"
+                       :key="item.userId || item.nickName"
+                       :label="item.nickName"
+                       :value="item.userId" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="submitShareForm">纭</el-button>
+          <el-button @click="closeShareDialog">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 鐢ㄦ埛瀵煎叆瀵硅瘽妗� -->
+    <el-dialog :title="upload.title"
+               v-model="upload.open"
+               width="400px"
+               append-to-body>
+      <el-upload ref="uploadRef"
+                 :limit="1"
+                 accept=".xlsx, .xls"
+                 :headers="upload.headers"
+                 :action="upload.url"
+                 :data="upload.data"
+                 :disabled="upload.isUploading"
+                 :before-upload="upload.beforeUpload"
+                 :on-progress="upload.onProgress"
+                 :on-success="upload.onSuccess"
+                 :on-error="upload.onError"
+                 :on-change="upload.onChange"
+                 :auto-upload="false"
+                 drag>
+        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+        <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+        <template #tip>
+          <div class="el-upload__tip text-center">
+            <span>浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�</span>
+            <el-link type="primary"
+                     :underline="false"
+                     style="font-size: 12px; vertical-align: baseline"
+                     @click="importTemplate">涓嬭浇妯℃澘</el-link>
+          </div>
+        </template>
+      </el-upload>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="submitFileForm">纭� 瀹�</el-button>
+          <el-button @click="upload.open = false">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 鍥炶鎻愰啋瀵硅瘽妗� -->
+    <el-dialog title="鍥炶鎻愰啋"
+               v-model="reminderDialogVisible"
+               width="500px"
+               @close="closeReminderDialog">
+      <el-form :model="reminderForm"
+               label-width="100px"
+               :rules="reminderRules"
+               ref="reminderFormRef">
+        <el-form-item label="瀹㈡埛鍚嶇О锛�">
+          <el-input v-model="reminderForm.customerName"
+                    disabled />
+        </el-form-item>
+        <el-form-item label="鎻愰啋寮�鍏筹細">
+          <el-switch v-model="reminderForm.reminderSwitch" />
+        </el-form-item>
+        <el-form-item label="鎻愰啋鍐呭锛�"
+                      prop="reminderContent">
+          <el-input v-model="reminderForm.reminderContent"
+                    type="textarea"
+                    :maxlength="100"
+                    show-word-limit
+                    placeholder="璇疯緭鍏ユ彁閱掑唴瀹�" />
+        </el-form-item>
+        <el-form-item label="鎻愰啋鏃堕棿锛�"
+                      prop="reminderTime">
+          <el-date-picker v-model="reminderForm.reminderTime"
+                          type="datetime"
+                          value-format="YYYY-MM-DD HH:mm:ss"
+                          format="YYYY-MM-DD HH:mm:ss"
+                          placeholder="璇烽�夋嫨鎻愰啋鏃堕棿"
+                          style="width: 100%" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitReminderForm">纭</el-button>
+          <el-button @click="closeReminderDialog">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 娣诲姞/淇敼娲借皥杩涘害瀵硅瘽妗� -->
+    <el-dialog :title="negotiationForm.editIndex !== undefined ? '淇敼杩涘害' : '娣诲姞杩涘害'"
+               v-model="negotiationDialogVisible"
+               width="600px"
+               @close="closeNegotiationDialog">
+      <el-form :model="negotiationForm"
+               label-width="100px"
+               :rules="negotiationRules"
+               ref="negotiationFormRef">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="璺熻繘鏂瑰紡锛�"
+                          prop="followUpMethod">
+              <el-select v-model="negotiationForm.followUpMethod"
+                         placeholder="璇烽�夋嫨"
+                         style="width: 100%">
+                <el-option label="鐢佃瘽"
+                           value="鐢佃瘽" />
+                <el-option label="閭欢"
+                           value="閭欢" />
+                <el-option label="涓婇棬"
+                           value="涓婇棬" />
+                <el-option label="寰俊"
+                           value="寰俊" />
+                <el-option label="鍏朵粬"
+                           value="鍏朵粬" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="璺熻繘绋嬪害锛�"
+                          prop="followUpLevel">
+              <el-select v-model="negotiationForm.followUpLevel"
+                         placeholder="璇烽�夋嫨"
+                         style="width: 100%">
+                <el-option label="娼滃湪瀹㈡埛"
+                           value="娼滃湪瀹㈡埛" />
+                <el-option label="鍒濇鎷滆"
+                           value="鍒濇鎷滆" />
+                <el-option label="澶氭鎷滆"
+                           value="澶氭鎷滆" />
+                <el-option label="鎰忓悜瀹㈡埛"
+                           value="鎰忓悜瀹㈡埛" />
+                <el-option label="宸茬绾﹀鎴�"
+                           value="宸茬绾﹀鎴�" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="璺熻繘鏃堕棿锛�"
+                          prop="followUpTime">
+              <el-date-picker v-model="negotiationForm.followUpTime"
+                              type="datetime"
+                              value-format="YYYY-MM-DD HH:mm:ss"
+                              format="YYYY-MM-DD HH:mm:ss"
+                              placeholder="璇烽�夋嫨"
+                              style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="璺熻繘浜猴細">
+              <el-input v-model="negotiationForm.followerUserName"
+                        disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="鍐呭锛�"
+                      prop="content">
+          <el-input v-model="negotiationForm.content"
+                    type="textarea"
+                    :rows="4"
+                    placeholder="璇疯緭鍏�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitNegotiationForm">纭</el-button>
+          <el-button @click="closeNegotiationDialog">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 瀹㈡埛璇︽儏瀵硅瘽妗� -->
+    <el-dialog title="瀹㈡埛璇︽儏"
+               v-model="detailDialogVisible"
+               width="1000px"
+               @close="closeDetailDialog">
+      <!-- 瀹㈡埛鍩烘湰淇℃伅 -->
+      <div class="detail-section">
+        <h3 class="section-title">瀹㈡埛鍩烘湰淇℃伅</h3>
+        <div class="info-display">
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">瀹㈡埛鍚嶇О锛�</span>
+                <span class="info-value">{{ detailForm.customerName }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">瀹㈡埛鍒嗙被锛�</span>
+                <span class="info-value">{{ detailForm.customerType }}</span>
+              </div>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">绾崇◣浜鸿瘑鍒彿锛�</span>
+                <span class="info-value">{{ detailForm.taxpayerIdentificationNumber }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">鍏徃鐢佃瘽锛�</span>
+                <span class="info-value">{{ detailForm.companyPhone }}</span>
+              </div>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">鍏徃鍦板潃锛�</span>
+                <span class="info-value">{{ detailForm.companyAddress }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">閾惰鍩烘湰鎴凤細</span>
+                <span class="info-value">{{ detailForm.basicBankAccount }}</span>
+              </div>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">閾惰璐﹀彿锛�</span>
+                <span class="info-value">{{ detailForm.bankAccount }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">寮�鎴疯鍙凤細</span>
+                <span class="info-value">{{ detailForm.bankCode }}</span>
+              </div>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">鑱旂郴浜猴細</span>
+                <span class="info-value">{{ detailForm.contactPerson }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">鑱旂郴鐢佃瘽锛�</span>
+                <span class="info-value">{{ detailForm.contactPhone }}</span>
+              </div>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">缁存姢浜猴細</span>
+                <span class="info-value">{{ detailForm.maintainer }}</span>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="info-item">
+                <span class="info-label">缁存姢鏃堕棿锛�</span>
+                <span class="info-value">{{ detailForm.maintenanceTime }}</span>
+              </div>
+            </el-col>
+          </el-row>
+        </div>
+      </div>
+      <!-- 娲借皥杩涘害璁板綍 -->
+      <div class="detail-section">
+        <div class="section-header">
+          <h3 class="section-title">娲借皥杩涘害璁板綍</h3>
+          <el-button type="primary"
+                     size="small"
+                     @click="openNegotiationDialog(detailForm)">
+            娣诲姞杩涘害
+          </el-button>
+        </div>
+        <el-table :data="negotiationRecords"
+                  border
+                  style="width: 100%">
+          <el-table-column prop="followUpTime"
+                           label="璺熻繘鏃堕棿"
+                           width="160" />
+          <el-table-column prop="followUpMethod"
+                           label="璺熻繘鏂瑰紡"
+                           width="100" />
+          <el-table-column prop="followUpLevel"
+                           label="璺熻繘绋嬪害" />
+          <el-table-column prop="followerUserName"
+                           label="璺熻繘浜�"
+                           width="100" />
+          <el-table-column prop="content"
+                           label="鍐呭"
+                           show-overflow-tooltip />
+          <el-table-column label="闄勪欢"
+                           width="100"
+                           align="center">
+            <template #default="{ row }">
+              <el-button type="info"
+                         link
+                         @click="openAttachmentDialog(row)">
+                <el-icon>
+                  <Paperclip />
+                </el-icon>
+                闄勪欢
+                <!-- {{ row.fileList && row.fileList.length > 0 ? row.fileList.length : '涓婁紶' }} -->
+              </el-button>
+            </template>
+          </el-table-column>
+          <el-table-column label="鎿嶄綔"
+                           width="150"
+                           align="center">
+            <template #default="{ row, $index }">
+              <el-button type="primary"
+                         link
+                         @click="editNegotiationRecord(row, $index)">
+                淇敼
+              </el-button>
+              <el-button type="danger"
+                         link
+                         @click="deleteNegotiationRecord(row, $index)">
+                鍒犻櫎
+              </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div v-if="negotiationRecords.length === 0"
+             class="no-records">
+          鏆傛棤娲借皥杩涘害璁板綍
+        </div>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="closeDetailDialog">鍏抽棴</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 闄勪欢涓婁紶寮圭獥 -->
+    <el-dialog title="闄勪欢绠$悊"
+               v-model="attachmentDialogVisible"
+               width="600px"
+               @close="closeAttachmentDialog">
+      <div class="attachment-section">
+        <div class="upload-area">
+          <el-upload ref="attachmentUploadRef"
+                     :action="getAttachmentUploadUrl()"
+                     :headers="attachmentUploadHeaders"
+                     :file-list="currentAttachmentList"
+                     :on-success="handleAttachmentSuccess"
+                     :on-error="handleAttachmentError"
+                     :on-remove="handleAttachmentRemove"
+                     :before-upload="beforeAttachmentUpload"
+                     multiple
+                     :limit="10"
+                     name="files">
+            <el-button type="primary">
+              <el-icon>
+                <Upload />
+              </el-icon>
+              涓婁紶闄勪欢
+            </el-button>
+            <template #tip>
+              <div class="el-upload__tip">
+                鏀寔涓婁紶鍥剧墖銆佹枃妗g瓑鏂囦欢锛屽崟涓枃浠朵笉瓒呰繃50MB
+              </div>
+            </template>
+          </el-upload>
+        </div>
+        <div v-if="currentAttachmentList.length > 0"
+             class="attachment-list">
+          <h4>宸蹭笂浼犻檮浠讹細</h4>
+          <el-table :data="currentAttachmentList"
+                    border
+                    size="small">
+            <el-table-column prop="name"
+                             label="鏂囦欢鍚�"
+                             show-overflow-tooltip />
+            <el-table-column prop="size"
+                             label="澶у皬"
+                             width="100">
+              <template #default="{ row }">
+                {{ formatFileSize(row.size) }}
+              </template>
+            </el-table-column>
+            <el-table-column label="鎿嶄綔"
+                             width="120"
+                             align="center">
+              <template #default="{ row, $index }">
+                <el-button type="primary"
+                           link
+                           @click="downloadAttachment(row)">
+                  涓嬭浇
+                </el-button>
+                <el-button type="danger"
+                           link
+                           @click="deleteAttachment(row, $index)">
+                  鍒犻櫎
+                </el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+        <div v-else
+             class="no-attachment">
+          鏆傛棤闄勪欢
+        </div>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="closeAttachmentDialog">鍏抽棴</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+  import { onMounted, ref, reactive, getCurrentInstance, toRefs } from "vue";
+  import { Search, Paperclip, Upload } from "@element-plus/icons-vue";
+  import {
+    addCustomerFollow,
+    updateCustomerFollow,
+    delCustomerFollow,
+    addReturnVisit,
+    getReturnVisit,
+  } from "@/api/basicData/customerFile.js";
+  import {
+    listCustomer,
+    addCustomer,
+    delCustomer,
+    updateCustomer,
+    getCustomer,
+    assignCustomer,
+    recycleCustomer,
+    shareCustomer,
+  } from "@/api/basicData/customer.js";
+
+  import { ElMessageBox } from "element-plus";
+  import { userListNoPage } from "@/api/system/user.js";
+  import useUserStore from "@/store/modules/user";
+  import { getToken } from "@/utils/auth.js";
+  const { proxy } = getCurrentInstance();
+  const userStore = useUserStore();
+  const assignDialogVisible = ref(false);
+  const assignFormRef = ref();
+  const assignForm = reactive({
+    id: undefined,
+    customerName: "",
+    boundId: undefined,
+  });
+  const assignRules = {
+    boundId: [{ required: true, message: "璇烽�夋嫨鍒嗛厤浜哄憳", trigger: "change" }],
+  };
+  const shareDialogVisible = ref(false);
+  const shareFormRef = ref();
+  const shareForm = reactive({
+    id: undefined,
+    customerName: "",
+    boundIds: [],
+  });
+  const shareRules = {
+    boundIds: [{ required: true, message: "璇烽�夋嫨鍏变韩浜哄憳", trigger: "change" }],
+  };
+
+  // 鍥炶鎻愰啋鐩稿叧
+  const reminderDialogVisible = ref(false);
+  const reminderFormRef = ref();
+  const currentCustomerId = ref();
+  const reminderForm = reactive({
+    customerName: "",
+    reminderSwitch: false,
+    reminderContent: "",
+    reminderTime: "",
+  });
+  const reminderRules = {
+    reminderContent: [
+      { required: true, message: "璇疯緭鍏ユ彁閱掑唴瀹�", trigger: "blur" },
+    ],
+    reminderTime: [
+      { required: true, message: "璇烽�夋嫨鎻愰啋鏃堕棿", trigger: "change" },
+    ],
+  };
+
+  // 娲借皥杩涘害鐩稿叧
+  const negotiationDialogVisible = ref(false);
+  const negotiationFormRef = ref();
+  const negotiationForm = reactive({
+    customerName: "",
+    customerId: "",
+    followUpMethod: "",
+    followUpLevel: "",
+    followUpTime: "",
+    followerUserName: "",
+    content: "",
+  });
+  const negotiationRules = {
+    followUpMethod: [
+      { required: true, message: "璇烽�夋嫨璺熻繘鏂瑰紡", trigger: "change" },
+    ],
+    followUpLevel: [
+      { required: true, message: "璇烽�夋嫨璺熻繘绋嬪害", trigger: "change" },
+    ],
+    followUpTime: [
+      { required: true, message: "璇烽�夋嫨璺熻繘鏃堕棿", trigger: "change" },
+    ],
+    content: [{ required: true, message: "璇疯緭鍏ュ唴瀹�", trigger: "blur" }],
+  };
+
+  // 璇︽儏鐩稿叧
+  const detailDialogVisible = ref(false);
+  const detailForm = reactive({
+    customerName: "",
+    customerType: "",
+    taxpayerIdentificationNumber: "",
+    companyPhone: "",
+    companyAddress: "",
+    basicBankAccount: "",
+    bankAccount: "",
+    bankCode: "",
+    contactPerson: "",
+    contactPhone: "",
+    maintainer: "",
+    maintenanceTime: "",
+  });
+  const negotiationRecords = ref([]);
+
+  // 闄勪欢鐩稿叧
+  const attachmentDialogVisible = ref(false);
+  const attachmentUploadRef = ref();
+  const currentAttachmentList = ref([]);
+  const currentFollowRecord = ref({});
+  const attachmentUploadHeaders = { Authorization: "Bearer " + getToken() };
+
+  // 鍔ㄦ�佹瀯寤轰笂浼燯RL
+  const getAttachmentUploadUrl = () => {
+    const baseUrl =
+      import.meta.env.VITE_APP_BASE_API + "/basic/customer-follow/upload";
+    return currentFollowRecord.value.id
+      ? `${baseUrl}/${currentFollowRecord.value.id}`
+      : baseUrl;
+  };
+
+  const tableColumn = ref([
+    {
+      label: "瀹㈡埛鍒嗙被",
+      prop: "customerType",
+      width: 120,
+    },
+    {
+      label: "瀹㈡埛鍚嶇О",
+      prop: "customerName",
+      width: 220,
+    },
+    {
+      label: "绾崇◣浜鸿瘑鍒爜",
+      prop: "taxpayerIdentificationNumber",
+      width: 220,
+    },
+    {
+      label: "鍦板潃鍙婅仈绯绘柟寮�",
+      prop: "addressPhone",
+      width: 250,
+    },
+    {
+      label: "鑱旂郴浜�",
+      prop: "contactPerson",
+    },
+    {
+      label: "鑱旂郴鐢佃瘽",
+      prop: "contactPhone",
+      width: 150,
+    },
+    // {
+    //   label: "璺熻繘杩涘害",
+    //   prop: "followUpLevel",
+    //   width: 120,
+    // },
+    // {
+    //   label: "璺熻繘鏃堕棿",
+    //   prop: "followUpTime",
+    //   width: 120,
+    // },
+    {
+      label: "閾惰鍩烘湰鎴�",
+      prop: "basicBankAccount",
+      width: 220,
+    },
+    {
+      label: "閾惰璐﹀彿",
+      prop: "bankAccount",
+      width: 220,
+    },
+    {
+      label: "寮�鎴疯鍙�",
+      prop: "bankCode",
+      width: 220,
+    },
+    {
+      label: "缁存姢浜�",
+      prop: "maintainer",
+    },
+		{
+			label: "缁存姢鏃堕棿",
+			prop: "maintenanceTime",
+			width: 100,
+		},
+    {
+      label: "棰嗙敤浜�",
+      prop: "usageUserName",
+      width: 120,
+			fixed: "right",
+    },
+    {
+      label: "棰嗙敤鐘舵��",
+      prop: "usageStatus",
+      dataType: "tag",
+      width: 100,
+			fixed: "right",
+      formatData: value => {
+        if (value === 1 || value === "1") {
+          return "宸查鐢�";
+        }
+        return "鏈鐢�";
+      },
+      formatType: value => {
+        if (value === 1 || value === "1") {
+          return "success";
+        }
+        return "info";
+      },
+    },
+		{
+			label: "鍏变韩浜�",
+			prop: "togetherUserNames",
+			width: 120,
+			fixed: "right",
+		},
+    {
+      dataType: "action",
+      label: "鎿嶄綔",
+      align: "center",
+      fixed: "right",
+      width: 200,
+      operation: [
+        {
+          name: "鍒嗛厤",
+          type: "text",
+          showHide: row => row.usageStatus != 1,
+          clickFun: row => {
+            openAssignDialog(row);
+          },
+        },
+        {
+          name: "鍥炴敹",
+          type: "text",
+          showHide: row => row.usageStatus == 1,
+          clickFun: row => {
+            recycle(row);
+          },
+        },
+				{
+					name: "鍏变韩",
+					type: "text",
+					showHide: row => row.usageStatus == 1,
+					clickFun: row => {
+						openShareDialog(row);
+					},
+				},
+				{
+					name: "缂栬緫",
+					type: "text",
+					clickFun: row => {
+						openForm("edit", row);
+					},
+				},
+        // {
+        //   name: "璇︽儏",
+        //   type: "text",
+        //   clickFun: row => {
+        //     openDetailDialog(row);
+        //   },
+        // },
+      ],
+    },
+  ]);
+  const tableData = ref([]);
+  const selectedRows = ref([]);
+  const userList = ref([]);
+  const tableLoading = ref(false);
+  const page = reactive({
+    current: 1,
+    size: 100,
+    total: 0,
+  });
+  const total = ref(0);
+
+  // 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
+  const operationType = ref("");
+  const dialogFormVisible = ref(false);
+  const formYYs = ref({
+    // 鍏朵粬瀛楁...
+    contactList: [
+      {
+        contactPerson: "",
+        contactPhone: "",
+      },
+    ],
+  });
+  const data = reactive({
+    searchForm: {
+      customerName: "",
+      customerType: "",
+      type: 1
+    },
+    form: {
+      customerName: "",
+      taxpayerIdentificationNumber: "",
+      companyAddress: "",
+      companyPhone: "",
+      contactPerson: "",
+      contactPhone: "",
+      maintainer: "",
+      maintenanceTime: "",
+      basicBankAccount: "",
+      bankAccount: "",
+      bankCode: "",
+      customerType: "",
+      type: 1
+    },
+    rules: {
+      customerName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      taxpayerIdentificationNumber: [
+        { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+      ],
+      companyAddress: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      companyPhone: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      // contactPerson: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      // contactPhone: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      maintainer: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
+      maintenanceTime: [
+        { required: false, message: "璇烽�夋嫨", trigger: "change" },
+      ],
+      basicBankAccount: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      bankAccount: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      bankCode: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      customerType: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+    },
+  });
+  const upload = reactive({
+    // 鏄惁鏄剧ず寮瑰嚭灞傦紙瀹㈡埛瀵煎叆锛�
+    open: false,
+    // 寮瑰嚭灞傛爣棰橈紙瀹㈡埛瀵煎叆锛�
+    title: "",
+    // 鏄惁绂佺敤涓婁紶
+    isUploading: false,
+    // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+    headers: { Authorization: "Bearer " + getToken() },
+    // 涓婁紶鐨勫湴鍧�
+    url: import.meta.env.VITE_APP_BASE_API + "/basic/customer/importData",
+    data: {
+      type: 1
+    },
+    // 鏂囦欢涓婁紶鍓嶇殑鍥炶皟
+    beforeUpload: file => {
+      console.log("鏂囦欢鍗冲皢涓婁紶", file);
+      // 鍙互鍦ㄦ澶勫仛鏂囦欢绫诲瀷鎴栧ぇ灏忔牎楠�
+      const isValid =
+        file.type ===
+          "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
+        file.name.endsWith(".xlsx") ||
+        file.name.endsWith(".xls");
+      if (!isValid) {
+        proxy.$modal.msgError("鍙兘涓婁紶 Excel 鏂囦欢");
+      }
+      return isValid;
+    },
+    // 鏂囦欢鐘舵�佹敼鍙樻椂鐨勫洖璋�
+    onChange: (file, fileList) => {
+      console.log("鏂囦欢鐘舵�佹敼鍙�", file, fileList);
+    },
+    // 鏂囦欢涓婁紶鎴愬姛鏃剁殑鍥炶皟
+    onSuccess: (response, file, fileList) => {
+      console.log("涓婁紶鎴愬姛", response, file, fileList);
+      upload.isUploading = false;
+      if (response.code === 200) {
+        proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛");
+        upload.open = false;
+        proxy.$refs["uploadRef"].clearFiles();
+        getList();
+      } else if (response.code === 500) {
+        proxy.$modal.msgError(response.msg);
+      } else {
+        proxy.$modal.msgWarning(response.msg);
+      }
+    },
+    // 鏂囦欢涓婁紶澶辫触鏃剁殑鍥炶皟
+    onError: (error, file, fileList) => {
+      console.error("涓婁紶澶辫触", error, file, fileList);
+      upload.isUploading = false;
+      proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触");
+    },
+    // 鏂囦欢涓婁紶杩涘害鍥炶皟
+    onProgress: (event, file, fileList) => {
+      console.log("涓婁紶涓�...", event.percent);
+    },
+  });
+  const { searchForm, form, rules } = toRefs(data);
+  const addNewContact = () => {
+    formYYs.value.contactList.push({
+      contactPerson: "",
+      contactPhone: "",
+    });
+  };
+
+  const removeContact = index => {
+    if (formYYs.value.contactList.length > 1) {
+      formYYs.value.contactList.splice(index, 1);
+    }
+  };
+  // 鏌ヨ鍒楄〃
+  /** 鎼滅储鎸夐挳鎿嶄綔 */
+  const handleQuery = () => {
+    page.current = 1;
+    getList();
+  };
+  const pagination = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    getList();
+  };
+  const getList = () => {
+    tableLoading.value = true;
+    const { total, ...queryPage } = page;
+    listCustomer({ ...searchForm.value, ...queryPage }).then(res => {
+      tableLoading.value = false;
+      tableData.value = res.data.records;
+      page.total = res.data.total;
+    });
+  };
+  // 琛ㄦ牸閫夋嫨鏁版嵁
+  const handleSelectionChange = selection => {
+    selectedRows.value = selection;
+  };
+  /** 鎻愪氦涓婁紶鏂囦欢 */
+  function submitFileForm() {
+    upload.isUploading = true;
+    proxy.$refs["uploadRef"].submit();
+  }
+  /** 瀵煎叆鎸夐挳鎿嶄綔 */
+  function handleImport() {
+    upload.title = "瀹㈡埛瀵煎叆";
+    upload.open = true;
+  }
+  /** 涓嬭浇妯℃澘 */
+  function importTemplate() {
+    proxy.download("/basic/customer/downloadTemplate", {}, "瀹㈡埛瀵煎叆妯℃澘.xlsx");
+  }
+  // 鎵撳紑寮规
+  const openForm = (type, row) => {
+    operationType.value = type;
+    form.value = {};
+    form.value.maintainer = userStore.nickName;
+    formYYs.value.contactList = [
+      {
+        contactPerson: "",
+        contactPhone: "",
+      },
+    ];
+    form.value.maintenanceTime = getCurrentDate();
+    form.value.type = 1;
+    userListNoPage().then(res => {
+      userList.value = res.data;
+    });
+    if (type === "edit") {
+      getCustomer(row.id).then(res => {
+        form.value = { ...res.data };
+        formYYs.value.contactList = res.data.contactPerson
+          .split(",")
+          .map((item, index) => {
+            return {
+              contactPerson: item,
+              contactPhone: res.data.contactPhone.split(",")[index],
+            };
+          });
+      });
+    }
+    dialogFormVisible.value = true;
+  };
+  // 鎻愪氦琛ㄥ崟
+  const submitForm = () => {
+    proxy.$refs["formRef"].validate(valid => {
+      if (valid) {
+        if (operationType.value === "edit") {
+          submitEdit();
+        } else {
+          submitAdd();
+        }
+      }
+    });
+  };
+  // 鎻愪氦鏂板
+  const submitAdd = () => {
+    if (formYYs.value.contactList.length < 1) {
+      return proxy.$modal.msgWarning("璇疯嚦灏戞坊鍔犱竴涓仈绯讳汉");
+    }
+    form.value.contactPerson = formYYs.value.contactList
+      .map(item => item.contactPerson)
+      .join(",");
+    form.value.contactPhone = formYYs.value.contactList
+      .map(item => item.contactPhone)
+      .join(",");
+    addCustomer(form.value).then(res => {
+      proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+      closeDia();
+      getList();
+    });
+  };
+  // 鎻愪氦淇敼
+  const submitEdit = () => {
+    form.value.contactPerson = formYYs.value.contactList
+      .map(item => item.contactPerson)
+      .join(",");
+    form.value.contactPhone = formYYs.value.contactList
+      .map(item => item.contactPhone)
+      .join(",");
+    updateCustomer(form.value).then(res => {
+      proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+      closeDia();
+      getList();
+    });
+  };
+  // 鍏抽棴寮规
+  const closeDia = () => {
+    proxy.resetForm("formRef");
+    dialogFormVisible.value = false;
+  };
+  const ensureUserList = () => {
+    if (userList.value.length) {
+      return Promise.resolve();
+    }
+    return userListNoPage().then(res => {
+      userList.value = res.data || [];
+    });
+  };
+  const openAssignDialog = row => {
+    assignForm.id = row.id;
+    assignForm.customerName = row.customerName;
+    assignForm.boundId = undefined;
+    ensureUserList().then(() => {
+      assignDialogVisible.value = true;
+    });
+  };
+  const closeAssignDialog = () => {
+    proxy.resetForm("assignFormRef");
+    assignForm.id = undefined;
+    assignForm.customerName = "";
+    assignForm.boundId = undefined;
+    assignDialogVisible.value = false;
+  };
+  const openShareDialog = row => {
+    shareForm.id = row.id;
+    shareForm.customerName = row.customerName;
+    shareForm.boundIds = row.userIds || [];
+    ensureUserList().then(() => {
+      shareDialogVisible.value = true;
+    });
+  };
+  const closeShareDialog = () => {
+    proxy.resetForm("shareFormRef");
+    shareForm.id = undefined;
+    shareForm.customerName = "";
+    shareForm.boundIds = [];
+    shareDialogVisible.value = false;
+  };
+  const submitAssignForm = () => {
+    proxy.$refs.assignFormRef.validate(valid => {
+      if (!valid) {
+        return;
+      }
+      assignCustomer({
+        id: assignForm.id,
+        usageUser: assignForm.boundId,
+      }).then(() => {
+        proxy.$modal.msgSuccess("鍒嗛厤鎴愬姛");
+        closeAssignDialog();
+        getList();
+      });
+    });
+  };
+  const submitShareForm = () => {
+    proxy.$refs.shareFormRef.validate(valid => {
+      if (!valid) {
+        return;
+      }
+      shareCustomer({
+        id: shareForm.id,
+        userIds: shareForm.boundIds,
+      }).then(() => {
+        proxy.$modal.msgSuccess("鍏变韩鎴愬姛");
+        closeShareDialog();
+        getList();
+      });
+    });
+  };
+  const recycle = row => {
+    ElMessageBox.confirm("纭鍥炴敹瀹㈡埛鈥�" + row.customerName + "鈥濆悧锛�", "鍥炴敹鎻愮ず", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        return recycleCustomer({id: row.id}).then(() => {
+          proxy.$modal.msgSuccess("鍥炴敹鎴愬姛");
+          getList();
+        })
+      })
+      .catch(error => {
+        if (error === "cancel" || error === "close") {
+          proxy.$modal.msg("宸插彇娑�");
+        }
+      });
+  };
+  // 瀵煎嚭
+  const handleOut = () => {
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        proxy.download("/basic/customer/export", {type: 1}, "瀹㈡埛妗f.xlsx");
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
+  };
+  // 鍒犻櫎
+  const handleDelete = () => {
+    let ids = [];
+    if (selectedRows.value.length > 0) {
+      // 妫�鏌ユ槸鍚︽湁浠栦汉缁存姢鐨勬暟鎹�
+      const unauthorizedData = selectedRows.value.filter(
+        item => item.maintainer !== userStore.nickName
+      );
+      if (unauthorizedData.length > 0) {
+        proxy.$modal.msgWarning("涓嶅彲鍒犻櫎浠栦汉缁存姢鐨勬暟鎹�");
+        return;
+      }
+      ids = selectedRows.value.map(item => item.id);
+    } else {
+      proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+      return;
+    }
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        tableLoading.value = true;
+        delCustomer(ids)
+          .then(res => {
+            proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+            getList();
+          })
+          .finally(() => {
+            tableLoading.value = false;
+          });
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
+  };
+
+  // 鎵撳紑鍥炶鎻愰啋寮圭獥
+  const openReminderDialog = row => {
+    currentCustomerId.value = row.id;
+    reminderForm.customerName = row.customerName;
+    reminderForm.reminderSwitch = false;
+    reminderForm.reminderContent = "";
+    reminderForm.reminderTime = "";
+
+    // 灏濊瘯鑾峰彇宸叉湁鐨勫洖璁挎彁閱�
+    getReturnVisit(row.id)
+      .then(res => {
+        if (res.code === 200 && res.data) {
+          reminderForm.reminderSwitch = res.data.isEnabled === 1;
+          reminderForm.reminderContent = res.data.content;
+          reminderForm.reminderTime = res.data.reminderTime;
+          reminderForm.id = res.data.id;
+        }
+      })
+      .catch(error => {
+        console.error("鑾峰彇鍥炶鎻愰啋澶辫触:", error);
+      });
+
+    reminderDialogVisible.value = true;
+  };
+
+  // 鍏抽棴鍥炶鎻愰啋寮圭獥
+  const closeReminderDialog = () => {
+    proxy.resetForm("reminderFormRef");
+    reminderDialogVisible.value = false;
+  };
+  const submitvalue = ref({});
+
+  // 鎻愪氦鍥炶鎻愰啋
+  const submitReminderForm = () => {
+    console.log("鎻愪氦鍥炶鎻愰啋鏁版嵁:", userStore.id, userStore);
+    proxy.$refs.reminderFormRef.validate(valid => {
+      if (valid) {
+        if (reminderForm.id) {
+          submitvalue.value = {
+            id: reminderForm.id,
+            customerId: currentCustomerId.value,
+            isEnabled: reminderForm.reminderSwitch ? 1 : 0,
+            content: reminderForm.reminderContent,
+            reminderTime: reminderForm.reminderTime,
+            remindUserId: userStore.id,
+          };
+        } else {
+          submitvalue.value = {
+            customerId: currentCustomerId.value,
+            isEnabled: reminderForm.reminderSwitch ? 1 : 0,
+            content: reminderForm.reminderContent,
+            reminderTime: reminderForm.reminderTime,
+            remindUserId: userStore.id,
+          };
+        }
+
+        console.log("鎻愪氦鍥炶鎻愰啋鏁版嵁:", submitvalue.value);
+
+        // 璋冪敤鎺ュ彛
+        addReturnVisit(submitvalue.value)
+          .then(res => {
+            if (res.code === 200) {
+              proxy.$modal.msgSuccess("鍥炶鎻愰啋璁剧疆鎴愬姛");
+              closeReminderDialog();
+            } else {
+              proxy.$modal.msgError(res.msg || "璁剧疆澶辫触");
+            }
+          })
+          .catch(error => {
+            console.error("璁剧疆鍥炶鎻愰啋澶辫触:", error);
+            proxy.$modal.msgError("璁剧疆澶辫触");
+          });
+      }
+    });
+  };
+
+  // 鎵撳紑娲借皥杩涘害寮圭獥
+  const openNegotiationDialog = row => {
+    negotiationForm.customerName = row.customerName;
+    negotiationForm.customerId = row.id;
+    negotiationForm.followUpMethod = "";
+    negotiationForm.followUpLevel = "";
+    negotiationForm.followUpTime = "";
+    negotiationForm.followerUserName = userStore.nickName; // 榛樿褰撳墠鐧诲綍浜�
+    negotiationForm.content = "";
+    // {
+    // 	"customerId": 152,
+    // 	"followUpMethod": "鐢佃瘽娌熼��",
+    // 	"followUpLevel": "娌℃湁鎰忓悜",
+    // 	"followUpTime": "2026-03-04T15:30:00",
+    // 	"followerUserName": "绠$悊鍛樿处鍙�",
+    // 	"content": "111"
+    // }
+    negotiationDialogVisible.value = true;
+  };
+
+  // 鍏抽棴娲借皥杩涘害寮圭獥
+  const closeNegotiationDialog = () => {
+    proxy.resetForm("negotiationFormRef");
+    // 娓呴櫎缂栬緫鐘舵��
+    delete negotiationForm.editIndex;
+    delete negotiationForm.id;
+    negotiationDialogVisible.value = false;
+  };
+
+  // 鎻愪氦娲借皥杩涘害
+  const submitNegotiationForm = () => {
+    proxy.$refs.negotiationFormRef.validate(valid => {
+      if (valid) {
+        // 鍒ゆ柇鏄柊澧炶繕鏄慨鏀�
+        const isEdit = negotiationForm.editIndex !== undefined;
+
+        if (isEdit) {
+          // 淇敼鎿嶄綔
+          console.log("淇敼娲借皥杩涘害鏁版嵁:", negotiationForm);
+          // 杩欓噷鍙互璋冪敤鏇存柊鎺ュ彛
+          // 瀹為檯椤圭洰涓渶瑕佹牴鎹悗绔帴鍙h繘琛岃皟鏁�
+          // 绀轰緥锛歶pdateCustomerFollow(negotiationForm).then(res => {
+          //   // 鏇存柊鏈湴鏁版嵁
+          //   const index = negotiationForm.editIndex;
+          //   negotiationRecords.value[index] = {
+          //     followUpTime: negotiationForm.followUpTime,
+          //     followUpMethod: negotiationForm.followUpMethod,
+          //     followUpLevel: negotiationForm.followUpLevel,
+          //     followerUserName: negotiationForm.followerUserName,
+          //     content: negotiationForm.content,
+          //     id: negotiationForm.id,
+          //   };
+          //   proxy.$modal.msgSuccess("淇敼鎴愬姛");
+          //   closeNegotiationDialog();
+          // });
+          updateCustomerFollow(negotiationForm).then(res => {
+            // 鏇存柊鏈湴鏁版嵁
+            getCustomer(negotiationForm.customerId).then(res => {
+              // 鏇存柊鏈湴鏁版嵁
+              negotiationRecords.value = res.data.followUpList || [];
+            });
+          });
+          proxy.$modal.msgSuccess("淇敼鎴愬姛");
+          closeNegotiationDialog();
+        } else {
+          // 鏂板鎿嶄綔
+          console.log("鎻愪氦娲借皥杩涘害鏁版嵁:", negotiationForm);
+          addCustomerFollow(negotiationForm).then(res => {
+            // 娣诲姞鎴愬姛鍚庢洿鏂拌鎯呴〉闈㈢殑杩涘害璁板綍
+            const newRecord = {
+              followUpTime: negotiationForm.followUpTime,
+              followUpMethod: negotiationForm.followUpMethod,
+              followUpLevel: negotiationForm.followUpLevel,
+              followerUserName: negotiationForm.followerUserName,
+              content: negotiationForm.content,
+            };
+            negotiationRecords.value.unshift(newRecord);
+
+            proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+            closeNegotiationDialog();
+            getList();
+          });
+        }
+      }
+    });
+  };
+
+  // 鎵撳紑璇︽儏寮圭獥
+  const openDetailDialog = row => {
+    // 璋冪敤getCustomer鎺ュ彛鑾峰彇瀹㈡埛璇︽儏
+    getCustomer(row.id).then(res => {
+      // 濉厖瀹㈡埛鍩烘湰淇℃伅
+      Object.assign(detailForm, res.data);
+
+      // 鑾峰彇娲借皥杩涘害璁板綍
+      negotiationRecords.value = res.data.followUpList || [];
+
+      detailDialogVisible.value = true;
+    });
+  };
+
+  // 鍏抽棴璇︽儏寮圭獥
+  const closeDetailDialog = () => {
+    detailDialogVisible.value = false;
+  };
+
+  // 淇敼娲借皥璁板綍
+  const editNegotiationRecord = (row, index) => {
+    // 灏嗗綋鍓嶈褰曟暟鎹~鍏呭埌琛ㄥ崟
+    Object.assign(negotiationForm, {
+      customerName: row.customerName,
+      customerId: row.customerId,
+      followUpMethod: row.followUpMethod,
+      followUpLevel: row.followUpLevel,
+      followUpTime: row.followUpTime,
+      followerUserName: row.followerUserName,
+      content: row.content,
+      id: row.id, // 璁板綍ID鐢ㄤ簬鏇存柊
+      editIndex: index, // 璁板綍绱㈠紩鐢ㄤ簬鏈湴鏇存柊
+    });
+    negotiationDialogVisible.value = true;
+  };
+
+  // 鍒犻櫎娲借皥璁板綍
+  const deleteNegotiationRecord = (row, index) => {
+    ElMessageBox.confirm("纭畾瑕佸垹闄よ繖鏉℃唇璋堣褰曞悧锛�", "鍒犻櫎鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        // 杩欓噷鍙互璋冪敤鍒犻櫎鎺ュ彛
+        // 瀹為檯椤圭洰涓渶瑕佹牴鎹悗绔帴鍙h繘琛岃皟鏁�
+        // 绀轰緥锛歞eleteCustomerFollow(row.id).then(() => {
+        //   negotiationRecords.value.splice(index, 1);
+        //   proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+        // });
+        delCustomerFollow(row.id).then(() => {
+          // 鍒犻櫎鎴愬姛鍚庢洿鏂版湰鍦版暟鎹�
+          getCustomer(row.customerId).then(res => {
+            // 鏇存柊鏈湴鏁版嵁
+            negotiationRecords.value = res.data.followUpList || [];
+          });
+          proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+        });
+        // 鏈湴鍒犻櫎锛堟ā鎷燂級
+        negotiationRecords.value.splice(index, 1);
+        proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑堝垹闄�");
+      });
+  };
+
+  // 鎵撳紑闄勪欢寮圭獥
+  const openAttachmentDialog = row => {
+    currentFollowRecord.value = row;
+    // 杞崲涓虹鍚圗lement Plus fileList鏍煎紡鐨勬暟缁�
+    currentAttachmentList.value = (row.fileList || []).map((file, index) => ({
+      name: file.fileName,
+      url: file.fileUrl,
+      size: file.fileSize,
+      id: file.id,
+      uid: file.id || index,
+      status: "success",
+    }));
+
+    attachmentDialogVisible.value = true;
+  };
+
+  // 鍏抽棴闄勪欢寮圭獥
+  const closeAttachmentDialog = () => {
+    attachmentDialogVisible.value = false;
+    currentFollowRecord.value = {};
+    currentAttachmentList.value = [];
+  };
+
+  // 闄勪欢涓婁紶鎴愬姛
+  const handleAttachmentSuccess = (response, file, fileList) => {
+    if (response.code === 200) {
+      proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
+      // 鏇存柊褰撳墠璁板綍鐨勯檮浠跺垪琛�
+      currentAttachmentList.value = fileList.map(item => ({
+        name: item.name,
+        size: item.size,
+        url: item.response?.data?.url || item.url,
+        id: item.response?.data?.id,
+        uid: item.uid,
+        status: "success",
+      }));
+      // 鏇存柊鍘熻褰曚腑鐨刦iles瀛楁
+      if (currentFollowRecord.value) {
+        currentFollowRecord.value.files = [...currentAttachmentList.value];
+      }
+    } else {
+      proxy.$modal.msgError(response.msg || "涓婁紶澶辫触");
+    }
+  };
+
+  // 闄勪欢涓婁紶澶辫触
+  const handleAttachmentError = (error, file, fileList) => {
+    console.error("涓婁紶澶辫触:", error);
+    proxy.$modal.msgError("涓婁紶澶辫触");
+  };
+
+  // 闄勪欢绉婚櫎
+  const handleAttachmentRemove = (file, fileList) => {
+    currentAttachmentList.value = fileList;
+    // 鏇存柊鍘熻褰曚腑鐨刦iles瀛楁
+    if (currentFollowRecord.value) {
+      currentFollowRecord.value.files = [...fileList];
+    }
+  };
+
+  // 闄勪欢涓婁紶鍓嶆牎楠�
+  const beforeAttachmentUpload = file => {
+    const maxSize = 50 * 1024 * 1024; // 50MB
+    if (file.size > maxSize) {
+      proxy.$modal.msgError("鏂囦欢澶у皬涓嶈兘瓒呰繃50MB");
+      return false;
+    }
+    return true;
+  };
+
+  // 鏍煎紡鍖栨枃浠跺ぇ灏�
+  const formatFileSize = size => {
+    if (size < 1024) {
+      return size + " B";
+    } else if (size < 1024 * 1024) {
+      return (size / 1024).toFixed(2) + " KB";
+    } else {
+      return (size / (1024 * 1024)).toFixed(2) + " MB";
+    }
+  };
+
+  // 涓嬭浇闄勪欢
+  const downloadAttachment = row => {
+    if (row.url) {
+      // proxy.download(row.url, {}, row.name);
+      proxy.$download.name(row.url);
+    } else {
+      proxy.$modal.msgError("涓嬭浇閾炬帴涓嶅瓨鍦�");
+    }
+  };
+
+  // 鍒犻櫎闄勪欢
+  const deleteAttachment = (row, index) => {
+    ElMessageBox.confirm("纭畾瑕佸垹闄よ繖涓檮浠跺悧锛�", "鍒犻櫎鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        // 璋冪敤鍚庣鎺ュ彛鍒犻櫎闄勪欢
+        const deleteUrl =
+          import.meta.env.VITE_APP_BASE_API +
+          "/basic/customer-follow/file/" +
+          row.id;
+        fetch(deleteUrl, {
+          method: "DELETE",
+          headers: {
+            Authorization: "Bearer " + getToken(),
+            "Content-Type": "application/json",
+          },
+        })
+          .then(response => response.json())
+          .then(res => {
+            if (res.code === 200) {
+              // 鍒犻櫎鎴愬姛鍚庢洿鏂版湰鍦版枃浠跺垪琛�
+              currentAttachmentList.value.splice(index, 1);
+              // 鏇存柊鍘熻褰曚腑鐨刦iles瀛楁
+              if (currentFollowRecord.value) {
+                currentFollowRecord.value.files = [
+                  ...currentAttachmentList.value,
+                ];
+              }
+              proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+            } else {
+              proxy.$modal.msgError(res.msg || "鍒犻櫎澶辫触");
+            }
+          })
+          .catch(error => {
+            console.error("鍒犻櫎闄勪欢澶辫触:", error);
+            proxy.$modal.msgError("鍒犻櫎澶辫触");
+          });
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑堝垹闄�");
+      });
+  };
+
+  // 鑾峰彇褰撳墠鏃ユ湡骞舵牸寮忓寲涓� YYYY-MM-DD
+  function getCurrentDate() {
+    const today = new Date();
+    const year = today.getFullYear();
+    const month = String(today.getMonth() + 1).padStart(2, "0"); // 鏈堜唤浠�0寮�濮�
+    const day = String(today.getDate()).padStart(2, "0");
+    return `${year}-${month}-${day}`;
+  }
+
+  onMounted(() => {
+    getList();
+  });
+</script>
+
+<style scoped lang="scss">
+  .detail-section {
+    margin-bottom: 20px;
+    padding: 15px;
+    background-color: #f9f9f9;
+    border-radius: 4px;
+  }
+
+  .section-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 15px;
+  }
+
+  .section-title {
+    font-size: 16px;
+    font-weight: bold;
+    margin: 0 0 15px 0;
+    color: #333;
+  }
+
+  .info-display {
+    background-color: #fff;
+    padding: 15px;
+    border-radius: 4px;
+  }
+
+  .info-item {
+    margin-bottom: 12px;
+    display: flex;
+    align-items: flex-start;
+  }
+
+  .info-label {
+    width: 120px;
+    font-weight: 500;
+    color: #606266;
+    margin-right: 10px;
+  }
+
+  .info-value {
+    flex: 1;
+    color: #303133;
+    word-break: break-word;
+  }
+
+  .no-records {
+    text-align: center;
+    padding: 30px;
+    color: #999;
+    font-size: 14px;
+  }
+
+  .attachment-section {
+    .upload-area {
+      margin-bottom: 20px;
+      padding: 20px;
+      background-color: #f9f9f9;
+      border-radius: 4px;
+      border: 1px dashed #d9d9d9;
+
+      .el-upload__tip {
+        margin-top: 10px;
+        color: #909399;
+      }
+    }
+
+    .attachment-list {
+      h4 {
+        margin: 0 0 10px 0;
+        font-size: 14px;
+        color: #606266;
+      }
+    }
+
+    .no-attachment {
+      text-align: center;
+      padding: 40px;
+      color: #909399;
+      font-size: 14px;
+    }
+  }
+</style>
diff --git a/src/views/basicData/parameterMaintenance/index.vue b/src/views/basicData/parameterMaintenance/index.vue
new file mode 100644
index 0000000..2dbf5df
--- /dev/null
+++ b/src/views/basicData/parameterMaintenance/index.vue
@@ -0,0 +1,793 @@
+<template>
+  <div class="app-container">
+    <div class="search_form">
+      <div>
+        <span class="search_title ml10">鍙傛暟鍚嶇О锛�</span>
+        <el-input v-model="searchForm.paramName"
+                  style="width: 200px"
+                  placeholder="璇疯緭鍏ュ弬鏁板悕绉�"
+                  clearable />
+        <!-- 鍏宠仈浜у搧绫诲瀷鎼滅储 -->
+        <!-- <span class="search_title ml10">鍏宠仈浜у搧绫诲瀷锛�</span>
+        <el-input v-model="searchForm.productName"
+                  style="width: 200px"d
+                  placeholder="璇疯緭鍏ュ叧鑱斾骇鍝佺被鍨�"
+                  clearable /> -->
+        <el-button type="primary"
+                   @click="handleQuery"
+                   style="margin-left: 10px">鎼滅储</el-button>
+        <el-button @click="handleReset">閲嶇疆</el-button>
+        <el-button type="primary"
+                   @click="handleAdd"
+                   style="margin-left: 10px">鏂板鍙傛暟</el-button>
+        <!-- 浜у搧绫诲瀷缁存姢鎸夐挳 -->
+        <!-- <el-button type="primary"
+                   @click="handleProductTypeMaintenance"
+                   style="margin-left: 10px">浜у搧绫诲瀷缁存姢</el-button> -->
+      </div>
+    </div>
+    <div class="table_list">
+      <PIMTable rowKey="paramName"
+                :column="tableColumn"
+                :tableData="tableData"
+                :page="page"
+                height="calc(100vh - 320px)"
+                :tableLoading="tableLoading"
+                :isSelection="false"
+                :isShowPagination="true"
+                @pagination="pagination">
+      </PIMTable>
+    </div>
+    <!-- 鏂板/缂栬緫瀵硅瘽妗� -->
+    <el-dialog v-model="dialogVisible"
+               :title="dialogTitle"
+               width="500px">
+      <el-form :model="formData"
+               :rules="rules"
+               ref="formRef"
+               label-width="120px">
+        <el-form-item label="鍙傛暟缂栫爜"
+                      prop="paramCode">
+          <el-input v-model="formData.paramCode"
+                    disabled
+                    placeholder="鑷姩鐢熸垚" />
+        </el-form-item>
+        <el-form-item label="鍙傛暟鍚嶇О"
+                      prop="paramName">
+          <el-input v-model="formData.paramName"
+                    placeholder="璇疯緭鍏ュ弬鏁板悕绉�" />
+        </el-form-item>
+        <el-form-item label="鍙傛暟绫诲瀷"
+                      prop="paramType">
+          <el-select v-model="formData.paramType"
+                     @change="handleParamTypeChange"
+                     placeholder="璇烽�夋嫨鍙傛暟绫诲瀷">
+            <el-option label="鏁板�兼牸寮�"
+                       :value="1" />
+            <el-option label="鏂囨湰鏍煎紡"
+                       :value="2" />
+            <el-option label="涓嬫媺閫夐」"
+                       :value="3" />
+            <el-option label="鏃堕棿鏍煎紡"
+                       :value="4" />
+          </el-select>
+        </el-form-item>
+        <!-- <el-form-item label="鍙栧�兼ā寮�"
+                      prop="valueMode">
+          <el-select v-model="formData.valueMode"
+                     placeholder="璇烽�夋嫨鍙栧�兼ā寮�">
+            <el-option label="鍗曞��"
+                       value="1" />
+            <el-option label="鍖洪棿"
+                       value="2" />
+          </el-select>
+        </el-form-item> -->
+        <el-form-item label="鍗曚綅"
+                      prop="unit">
+          <el-input v-model="formData.unit"
+                    placeholder="璇疯緭鍏ュ崟浣�" />
+        </el-form-item>
+        <el-form-item label="鍙栧�兼牸寮�"
+                      v-if="formData.paramType == 1 || formData.paramType == 2"
+                      prop="paramFormat">
+          <el-input v-model="formData.paramFormat"
+                    placeholder="璇疯緭鍏ュ彇鍊兼牸寮�" />
+          <!-- <el-select v-model="formData.paramFormat"
+                     placeholder="璇烽�夋嫨鍙栧�兼ā寮�">
+            <el-option label="#.00000"
+                       value="#.00000" />
+            <el-option label="#.0000"
+                       value="#.0000" />
+            <el-option label="#.000"
+                       value="#.000" />
+            <el-option label="#.00"
+                       value="#.00" />
+          </el-select> -->
+        </el-form-item>
+        <el-form-item label="涓嬫媺瀛楀吀"
+                      v-else-if="formData.paramType == 3"
+                      prop="paramFormat">
+          <el-select v-model="formData.paramFormat"
+                     placeholder="璇烽�夋嫨鍙栧�兼ā寮�">
+            <el-option v-for="item in dictTypes"
+                       :key="item.dictType"
+                       :label="item.dictName"
+                       :value="item.dictType" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鏃堕棿鏍煎紡"
+                      v-else-if="formData.paramType == 4"
+                      prop="paramFormat">
+          <el-select v-model="formData.paramFormat"
+                     placeholder="璇烽�夋嫨鍙栧�兼ā寮�">
+            <el-option label="YYYY-MM-DD"
+                       value="YYYY-MM-DD" />
+            <el-option label="YYYY-MM-DD HH:mm:ss"
+                       value="YYYY-MM-DD HH:mm:ss" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鏄惁蹇呭~"
+                      prop="isRequired">
+          <el-switch v-model="formData.isRequired"
+                     :active-value="1"
+                     :inactive-value="0" />
+        </el-form-item>
+        <el-form-item label="澶囨敞"
+                      prop="remark">
+          <el-input v-model="formData.remark"
+                    type="textarea"
+                    :rows="3"
+                    placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary" @click="handleSubmit">纭畾</el-button>
+          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <!-- 浜у搧绫诲瀷缁存姢瀵硅瘽妗� -->
+    <!-- <el-dialog v-model="productTypeDialogVisible"
+               title="浜у搧绫诲瀷缁存姢"
+               width="600px">
+      <div class="product-type-header">
+        <el-button type="primary"
+                   @click="handleAddProductType">鏂板浜у搧绫诲瀷</el-button>
+      </div>
+      <el-table :data="productTypeList"
+                border
+                style="width: 100%; margin-top: 10px; margin-bottom: 20px">
+        <el-table-column prop="typeCode"
+                         label="绫诲瀷缂栫爜"
+                         width="150" />
+        <el-table-column prop="typeName"
+                         label="绫诲瀷鍚嶇О" />
+        <el-table-column label="鎿嶄綔"
+                         width="150">
+          <template #default="scope">
+            <el-button link
+                       type="primary"
+                       @click="handleEditProductType(scope.row)">缂栬緫</el-button>
+            <el-button link
+                       type="danger"
+                       @click="handleDeleteProductType(scope.row)">鍒犻櫎</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-dialog> -->
+    <!-- 鏂板/缂栬緫浜у搧绫诲瀷瀵硅瘽妗� -->
+    <!-- <el-dialog v-model="productTypeFormVisible"
+               :title="productTypeDialogTitle"
+               width="400px">
+      <el-form :model="productTypeForm"
+               :rules="productTypeRules"
+               ref="productTypeFormRef"
+               label-width="100px">
+        <el-form-item label="绫诲瀷缂栫爜"
+                      prop="typeCode">
+          <el-input v-model="productTypeForm.typeCode"
+                    placeholder="璇疯緭鍏ョ被鍨嬬紪鐮�" />
+        </el-form-item>
+        <el-form-item label="绫诲瀷鍚嶇О"
+                      prop="typeName">
+          <el-input v-model="productTypeForm.typeName"
+                    placeholder="璇疯緭鍏ョ被鍨嬪悕绉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="productTypeFormVisible = false">鍙栨秷</el-button>
+          <el-button type="primary"
+                     @click="handleProductTypeSubmit">纭畾</el-button>
+        </span>
+      </template>
+    </el-dialog> -->
+  </div>
+</template>
+
+<script setup>
+  import { onMounted, ref, reactive } from "vue";
+  import {
+    parameterListPage,
+    addParameter,
+    updateParameter,
+    delParameter,
+    addBaseParam,
+    editBaseParam,
+    getBaseParamList,
+    removeBaseParam,
+    // getProductTypes as getProductTypesApi,
+  } from "@/api/basicData/parameterMaintenance.js";
+  import { listType } from "@/api/system/dict/type";
+  import { deptTreeSelect } from "@/api/system/user.js";
+  import PIMTable from "@/components/PIMTable/PIMTable.vue";
+  import { ElMessage, ElMessageBox } from "element-plus";
+
+  const tableColumn = ref([
+    {
+      label: "鍙傛暟缂栫爜",
+      prop: "paramCode",
+    },
+    {
+      label: "鍙傛暟鍚嶇О",
+      prop: "paramName",
+    },
+    {
+      label: "鍙傛暟绫诲瀷",
+      prop: "paramType",
+      dataType: "tag",
+      formatType: params => {
+        const typeMap = {
+          1: "primary",
+          2: "info",
+          3: "warning",
+          4: "success",
+        };
+        return typeMap[params] || "default";
+      },
+      formatData: val => {
+        const labelMap = {
+          1: "鏁板�兼牸寮�",
+          2: "鏂囨湰鏍煎紡",
+          3: "涓嬫媺閫夐」",
+          4: "鏃堕棿鏍煎紡",
+        };
+        return labelMap[val] || val;
+      },
+    },
+    // {
+    //   label: "鍙栧�兼ā寮�",
+    //   prop: "valueMode",
+    //   dataType: "tag",
+    //   formatType: params => {
+    //     return params === 2 ? "warning" : "success";
+    //   },
+    //   formatData: val => {
+    //     return val === 2 ? "鍖洪棿" : "鍗曞��";
+    //   },
+    // },
+    {
+      label: "鍗曚綅",
+      prop: "unit",
+    },
+    {
+      label: "鍙栧�兼牸寮�",
+      prop: "paramFormat",
+    },
+    {
+      label: "鏄惁蹇呭~",
+      prop: "isRequired",
+      dataType: "tag",
+      formatType: val => {
+        return val === 1 ? "success" : "info";
+      },
+      formatData: val => {
+        return val === 1 ? "鏄�" : "鍚�";
+      },
+    },
+    {
+      label: "澶囨敞",
+      prop: "remark",
+    },
+    {
+      label: "鍒涘缓鏃堕棿",
+      prop: "createTime",
+    },
+    {
+      label: "鎿嶄綔",
+      dataType: "action",
+      width: "150",
+      operation: [
+        {
+          name: "缂栬緫",
+          clickFun: row => {
+            handleEdit(row);
+          },
+        },
+        {
+          name: "鍒犻櫎",
+          clickFun: row => {
+            handleDelete(row);
+          },
+        },
+      ],
+    },
+  ]);
+  const tableData = ref([]);
+  const tableLoading = ref(false);
+  const page = reactive({
+    current: 1,
+    size: 10,
+    total: 0,
+  });
+  // 鎼滅储琛ㄥ崟
+  const searchForm = reactive({
+    paramName: "",
+    productName: "",
+  });
+
+  // 瀵硅瘽妗嗙浉鍏�
+  const dialogVisible = ref(false);
+  const dialogTitle = ref("");
+  const formRef = ref(null);
+  const formData = reactive({
+    id: null,
+    paramCode: "",
+    paramName: "",
+    paramType: "",
+    // valueMode: "1",
+    unit: "",
+    remark: "",
+    isRequired: 0,
+    paramFormat: "",
+  });
+  const rules = reactive({
+    paramName: [{ required: true, message: "璇疯緭鍏ュ弬鏁板悕绉�", trigger: "blur" }],
+    paramType: [{ required: true, message: "璇烽�夋嫨鍙傛暟绫诲瀷", trigger: "change" }],
+    // valueMode: [{ required: true, message: "璇烽�夋嫨鍙栧�兼ā寮�", trigger: "change" }],
+    unit: [
+      {
+        required: false,
+        message: "璇疯緭鍏ュ崟浣�",
+        trigger: "blur",
+        validator: (rule, value, callback) => {
+          if (formData.paramType === 1 && !value) {
+            callback(new Error("鏁板�肩被鍨嬪繀椤诲~鍐欏崟浣�"));
+          } else {
+            callback();
+          }
+        },
+      },
+    ],
+  });
+  // const productTypes = ref([]);
+  const isEdit = ref(false);
+
+  // 浜у搧绫诲瀷缁存姢鐩稿叧 - 宸叉敞閲�
+  // const productTypeDialogVisible = ref(false);
+  // const productTypeFormVisible = ref(false);
+  // const productTypeDialogTitle = ref("");
+  // const productTypeFormRef = ref(null);
+  // const productTypeList = ref([]);
+  // const productTypeForm = reactive({
+  //   id: null,
+  //   typeCode: "",
+  //   typeName: "",
+  // });
+  // const productTypeRules = reactive({
+  //   typeCode: [{ required: true, message: "璇疯緭鍏ョ被鍨嬬紪鐮�", trigger: "blur" }],
+  //   typeName: [{ required: true, message: "璇疯緭鍏ョ被鍨嬪悕绉�", trigger: "blur" }],
+  // });
+  // const isProductTypeEdit = ref(false);
+  const handleParamTypeChange = () => {
+    if (formData.paramType === 1) {
+      formData.paramFormat = "#.00000";
+    } else if (formData.paramType === 4) {
+      formData.paramFormat = "YYYY-MM-DD HH:mm:ss";
+    } else {
+      formData.paramFormat = "";
+    }
+    // 瑙﹀彂鍗曚綅瀛楁楠岃瘉
+    if (formRef.value) {
+      formRef.value.validateField("unit");
+    }
+  };
+  // 浜у搧绫诲瀷缁存姢鎸夐挳鐐瑰嚮浜嬩欢 - 宸叉敞閲�
+  // const handleProductTypeMaintenance = () => {
+  //   productTypeDialogVisible.value = true;
+  //   getProductTypeList();
+  // };
+
+  // 鑾峰彇浜у搧绫诲瀷鍒楄〃 - 宸叉敞閲�
+  // const getProductTypeList = () => {
+  //   productTypeList.value = [
+  //     { id: 1, typeCode: "TYPE001", typeName: "3.5鐮屽潡" },
+  //     { id: 2, typeCode: "TYPE002", typeName: "5.0鐮屽潡" },
+  //     { id: 3, typeCode: "TYPE003", typeName: "鏉挎潗" },
+  //   ];
+  // };
+
+  // 鏂板浜у搧绫诲瀷 - 宸叉敞閲�
+  // const handleAddProductType = () => {
+  //   isProductTypeEdit.value = false;
+  //   productTypeDialogTitle.value = "鏂板浜у搧绫诲瀷";
+  //   productTypeForm.id = null;
+  //   productTypeForm.typeCode = "";
+  //   productTypeForm.typeName = "";
+  //   productTypeFormVisible.value = true;
+  // };
+
+  // 缂栬緫浜у搧绫诲瀷 - 宸叉敞閲�
+  // const handleEditProductType = row => {
+  //   isProductTypeEdit.value = true;
+  //   productTypeDialogTitle.value = "缂栬緫浜у搧绫诲瀷";
+  //   productTypeForm.id = row.id;
+  //   productTypeForm.typeCode = row.typeCode;
+  //   productTypeForm.typeName = row.typeName;
+  //   productTypeFormVisible.value = true;
+  // };
+
+  // 鍒犻櫎浜у搧绫诲瀷 - 宸叉敞閲�
+  // const handleDeleteProductType = row => {
+  //   ElMessageBox.confirm("纭畾瑕佸垹闄よ浜у搧绫诲瀷鍚楋紵", "鎻愮ず", {
+  //     confirmButtonText: "纭畾",
+  //     cancelButtonText: "鍙栨秷",
+  //     type: "warning",
+  //   })
+  //     .then(() => {
+  //       ElMessage.success("鍒犻櫎鎴愬姛");
+  //       getProductTypeList();
+  //     })
+  //     .catch(() => {});
+  // };
+
+  // 鎻愪氦浜у搧绫诲瀷琛ㄥ崟 - 宸叉敞閲�
+  // const handleProductTypeSubmit = () => {
+  //   productTypeFormRef.value.validate(valid => {
+  //     if (valid) {
+  //       ElMessage.success(isProductTypeEdit.value ? "缂栬緫鎴愬姛" : "鏂板鎴愬姛");
+  //       productTypeFormVisible.value = false;
+  //       getProductTypeList();
+  //     }
+  //   });
+  // };
+
+  // 鏌ヨ鍒楄〃
+  /** 鎼滅储鎸夐挳鎿嶄綔 */
+  const handleQuery = () => {
+    page.current = 1;
+    getList();
+  };
+
+  /** 閲嶇疆鎸夐挳鎿嶄綔 */
+  const handleReset = () => {
+    searchForm.paramName = "";
+    searchForm.productName = "";
+    page.current = 1;
+    getList();
+  };
+  const pagination = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    getList();
+  };
+
+  const getList = () => {
+    tableLoading.value = true;
+    // 璋冪敤鏂版帴鍙� /baseParam/list
+    getBaseParamList({
+      paramName: searchForm.paramName,
+      current: page.current,
+      size: page.size,
+    })
+      .then(res => {
+        tableLoading.value = false;
+        if (res.code === 200) {
+          tableData.value = res.data.records || [];
+          page.total = res.data.total || 0;
+          console.log(tableData.value, "tableData.value");
+        } else {
+          ElMessage.error(res.msg || "鏌ヨ澶辫触");
+        }
+      })
+      .catch(() => {
+        tableLoading.value = false;
+        ElMessage.error("鏌ヨ澶辫触");
+      });
+  };
+
+  // 鑾峰彇浜у搧绫诲瀷鍒楄〃 - 宸叉敞閲�
+  // const getProductTypes = () => {
+  //   productTypes.value = [
+  //     { label: "3.5鐮屽潡", value: "type1" },
+  //     { label: "5.0鐮屽潡", value: "type2" },
+  //     { label: "鏉挎潗", value: "type3" },
+  //   ];
+  // };
+
+  // 鏂板鎸夐挳鐐瑰嚮浜嬩欢
+  const handleAdd = () => {
+    isEdit.value = false;
+    dialogTitle.value = "鏂板鍙傛暟";
+    // 閲嶇疆琛ㄥ崟
+    formData.id = null;
+    formData.paramCode = "";
+    formData.paramName = "";
+    formData.paramType = "";
+    // formData.valueMode = "1";
+    formData.unit = "";
+    formData.remark = "";
+    formData.isRequired = 0;
+    dialogVisible.value = true;
+  };
+
+  // 缂栬緫鎸夐挳鐐瑰嚮浜嬩欢
+  const handleEdit = row => {
+    isEdit.value = true;
+    dialogTitle.value = "缂栬緫鍙傛暟";
+    // 濉厖琛ㄥ崟鏁版嵁
+    formData.id = row.id;
+    formData.paramCode = row.paramCode || "";
+    formData.paramName = row.paramName || "";
+    formData.paramType = row.paramType !== undefined ? row.paramType : null;
+    // formData.valueMode =
+    //   row.valueMode !== undefined ? String(row.valueMode) : "1";
+    formData.unit = row.unit || "";
+    formData.remark = row.remark || "";
+    formData.paramFormat = row.paramFormat || "";
+    formData.isRequired = row.isRequired || 0;
+    dialogVisible.value = true;
+  };
+
+  // 鍒犻櫎鎸夐挳鐐瑰嚮浜嬩欢
+  const handleDelete = row => {
+    ElMessageBox.confirm("纭畾瑕佸垹闄よ繖鏉℃暟鎹悧锛�", "鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        // 璋冪敤鏂版帴鍙� /baseParam/remove/{id}
+        removeBaseParam([row.id])
+          .then(res => {
+            ElMessage.success("鍒犻櫎鎴愬姛");
+            getList();
+          })
+          .catch(() => {
+            ElMessage.error("鍒犻櫎澶辫触");
+          });
+      })
+      .catch(() => {
+        // 鍙栨秷鍒犻櫎
+      });
+  };
+
+  // 鎻愪氦琛ㄥ崟
+  const handleSubmit = () => {
+    if (formData.paramType == 3 && !formData.paramFormat) {
+      ElMessage.warning("涓嬫媺瀛楀吀涓嶈兘涓虹┖锛�");
+      return;
+    }
+    formRef.value.validate(valid => {
+      if (valid) {
+        if (formData.id) {
+          // 缂栬緫浣跨敤鏂版帴鍙� /technologyParam/edit
+          editBaseParam(formData)
+            .then(res => {
+              ElMessage.success("缂栬緫鎴愬姛");
+              dialogVisible.value = false;
+              getList();
+            })
+            .catch(() => {
+              // ElMessage.error("缂栬緫澶辫触");
+            });
+        } else {
+          // 鏂板浣跨敤鏂版帴鍙� /technologyParam/add
+          addBaseParam(formData)
+            .then(res => {
+              ElMessage.success("鏂板鎴愬姛");
+              dialogVisible.value = false;
+              getList();
+            })
+            .catch(() => {
+              ElMessage.error("鏂板澶辫触");
+            });
+        }
+      } else {
+        return false;
+      }
+    });
+  };
+  const dictTypes = ref([]);
+  const getDictTypes = () => {
+    listType({ pageNum: 1, pageSize: 1000 }).then(res => {
+      dictTypes.value = res.rows || [];
+    });
+  };
+
+  onMounted(() => {
+    getDictTypes();
+    getList();
+    // getProductTypes();
+  });
+</script>
+
+<style scoped lang="scss">
+  .app-container {
+    padding: 24px;
+    background-color: #f0f2f5;
+    min-height: calc(100vh - 48px);
+  }
+
+  .search_form {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 24px;
+    padding: 20px;
+    background-color: #ffffff;
+    border-radius: 6px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+    transition: all 0.3s ease;
+
+    &:hover {
+      box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
+    }
+
+    .search_title {
+      color: #606266;
+      font-size: 14px;
+      font-weight: 500;
+    }
+
+    .ml10 {
+      margin-left: 10px;
+    }
+  }
+
+  .table_list {
+    background-color: #ffffff;
+    border-radius: 6px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+    overflow: hidden;
+    height: calc(100vh - 230px);
+  }
+
+  :deep(.el-table) {
+    border: none;
+    border-radius: 6px;
+    overflow: hidden;
+    box-shadow: 0 4px 16px rgba(102, 126, 234, 0.1);
+
+    .el-table__header-wrapper {
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+
+      th {
+        background: transparent;
+        font-weight: 600;
+        // color: #ffffff;
+        border-bottom: none;
+        padding: 16px 0;
+        letter-spacing: 0.5px;
+      }
+    }
+
+    .el-table__body-wrapper {
+      tr {
+        transition: all 0.3s ease;
+
+        &:hover {
+          background: linear-gradient(
+            90deg,
+            rgba(102, 126, 234, 0.05) 0%,
+            rgba(118, 75, 162, 0.05) 100%
+          );
+          transform: scale(1.002);
+          box-shadow: 0 2px 8px rgba(102, 126, 234, 0.1);
+        }
+
+        td {
+          border-bottom: 1px solid #f0f0f0;
+          padding: 14px 0;
+          color: #303133;
+        }
+      }
+
+      tr.current-row {
+        background: linear-gradient(
+          90deg,
+          rgba(102, 126, 234, 0.08) 0%,
+          rgba(118, 75, 162, 0.08) 100%
+        );
+      }
+
+      // 鏁板�煎瓧娈垫牱寮�
+      .quantity-cell,
+      .volume-cell,
+      .dimension-cell {
+        font-weight: 600;
+        color: #409eff;
+        font-family: "Courier New", monospace;
+        text-shadow: 0 1px 2px rgba(64, 158, 255, 0.2);
+      }
+
+      // 瑙勬牸瀛楁鏍峰紡
+      .spec-cell {
+        color: #67c23a;
+        font-weight: 500;
+        padding: 4px 8px;
+        border-radius: 4px;
+      }
+
+      // 缂栫爜瀛楁鏍峰紡
+      .code-cell {
+        color: #e6a23c;
+        font-family: "Courier New", monospace;
+        font-weight: 500;
+        padding: 4px 8px;
+        border-radius: 4px;
+      }
+
+      // 鏃ユ湡瀛楁鏍峰紡
+      .date-cell {
+        color: #909399;
+        font-style: italic;
+      }
+    }
+
+    .el-table__empty-block {
+      padding: 60px 0;
+      background-color: #fafafa;
+    }
+  }
+
+  .pagination-container {
+    display: flex;
+    justify-content: flex-end;
+    padding: 16px 20px;
+    background-color: #ffffff;
+    border-top: 1px solid #ebeef5;
+    border-radius: 0 0 12px 12px;
+  }
+
+  :deep(.el-button) {
+    transition: all 0.3s ease;
+
+    &:hover {
+      transform: translateY(-1px);
+    }
+  }
+
+  @media (max-width: 768px) {
+    .app-container {
+      padding: 16px;
+    }
+
+    .search_form {
+      flex-direction: column;
+      align-items: flex-start;
+      gap: 12px;
+
+      .el-form {
+        width: 100%;
+
+        .el-form-item {
+          width: 100%;
+        }
+      }
+
+      .el-button {
+        margin-right: 12px;
+      }
+    }
+
+    :deep(.el-table) {
+      th,
+      td {
+        padding: 10px 0;
+        font-size: 12px;
+      }
+    }
+  }
+</style>
diff --git a/src/views/basicData/product/ProductSelectDialog.vue b/src/views/basicData/product/ProductSelectDialog.vue
index ded23cc..ad27baa 100644
--- a/src/views/basicData/product/ProductSelectDialog.vue
+++ b/src/views/basicData/product/ProductSelectDialog.vue
@@ -1,12 +1,12 @@
 <template>
   <el-dialog v-model="visible" title="閫夋嫨浜у搧" width="900px" destroy-on-close :close-on-click-modal="false">
     <el-form :inline="true" :model="query" class="mb-2">
-      <el-form-item label="浜у搧澶х被">
-        <el-input v-model="query.productName" placeholder="杈撳叆浜у搧澶х被" clearable @keyup.enter="onSearch" />
+      <el-form-item label="浜у搧鍚嶇О">
+        <el-input v-model="query.productName" placeholder="杈撳叆浜у搧鍚嶇О" clearable @keyup.enter="onSearch" />
       </el-form-item>
 
-      <el-form-item label="鍨嬪彿鍚嶇О">
-        <el-input v-model="query.model" placeholder="杈撳叆鍨嬪彿鍚嶇О" clearable @keyup.enter="onSearch" />
+      <el-form-item label="浜у搧鍨嬪彿">
+        <el-input v-model="query.model" placeholder="杈撳叆浜у搧鍨嬪彿" clearable @keyup.enter="onSearch" />
       </el-form-item>
 
       <el-form-item>
@@ -20,8 +20,8 @@
       @selection-change="handleSelectionChange" @select="handleSelect">
       <el-table-column type="selection" width="55" />
       <el-table-column type="index" label="搴忓彿" width="60" />
-      <el-table-column prop="productName" label="浜у搧澶х被" min-width="160" />
-      <el-table-column prop="model" label="鍨嬪彿鍚嶇О" min-width="200" />
+      <el-table-column prop="productName" label="浜у搧鍚嶇О" min-width="160" />
+      <el-table-column prop="model" label="浜у搧鍨嬪彿" min-width="200" />
       <el-table-column prop="unit" label="鍗曚綅" min-width="160" />
     </el-table>
 
@@ -43,7 +43,7 @@
 <script setup lang="ts">
 import { computed, onMounted, reactive, ref, watch, nextTick } from "vue";
 import { ElMessage } from "element-plus";
-import { productModelList } from '@/api/basicData/productModel'
+import { productModelList, productModelListByUrl } from '@/api/basicData/productModel'
 
 export type ProductRow = {
   id: number;
@@ -55,6 +55,8 @@
 const props = defineProps<{
   modelValue: boolean;
   single?: boolean; // 鏄惁鍙兘閫夋嫨涓�涓紝榛樿false锛堝彲閫夋嫨澶氫釜锛�
+  topProductParentId?: number; // 涓�绾т骇鍝乮d
+  requestUrl?: string; // 鑷畾涔夋煡璇㈡帴鍙�
 }>();
 
 const emit = defineEmits(['update:modelValue', 'confirm']);
@@ -154,14 +156,19 @@
   loading.value = true;
   try {
     multipleSelection.value = []; // 缈婚〉/鎼滅储鍚庢竻绌洪�夋嫨鏇寸鍚堥鏈�
-    const res: any = await productModelList({
+    const params = {
       productName: query.productName.trim(),
       model: query.model.trim(),
       current: page.pageNum,
       size: page.pageSize,
-    });
-    tableData.value = res.records;
-    total.value = res.total;
+      topProductParentId: props.topProductParentId,
+    };
+    const res: any = props.requestUrl
+      ? await productModelListByUrl(props.requestUrl, params)
+      : await productModelList(params);
+    const records = res?.records || res?.data?.records || res?.data || [];
+    tableData.value = Array.isArray(records) ? records : [];
+    total.value = Number(res?.total ?? res?.data?.total ?? tableData.value.length);
   } finally {
     loading.value = false;
   }
diff --git a/src/views/basicData/product/index.vue b/src/views/basicData/product/index.vue
index 7b2a819..99ab028 100644
--- a/src/views/basicData/product/index.vue
+++ b/src/views/basicData/product/index.vue
@@ -2,37 +2,34 @@
   <div class="app-container product-view">
     <div class="left">
       <div>
-        <el-input
-          v-model="search"
-          style="width: 210px"
-          placeholder="杈撳叆鍏抽敭瀛楄繘琛屾悳绱�"
-          @change="searchFilter"
-          @clear="searchFilter"
-          clearable
-          prefix-icon="Search"
-        />
-        <el-button
-          type="primary"
-          @click="openProDia('addOne')"
-          style="margin-left: 10px"
-          >鏂板浜у搧澶х被</el-button
-        >
+        <el-input v-model="search"
+                  style="width: 210px"
+                  placeholder="杈撳叆鍏抽敭瀛楄繘琛屾悳绱�"
+                  @change="searchFilter"
+                  @clear="searchFilter"
+                  clearable
+                  prefix-icon="Search" />
+        <el-button v-if="false"
+                   type="primary"
+                   @click="openProDia('addOne')"
+                   style="margin-left: 10px">鏂板浜у搧澶х被</el-button>
       </div>
       <div ref="containerRef">
-        <el-tree
-          ref="tree"
-          v-loading="treeLoad"
-          :data="list"
-          @node-click="handleNodeClick"
-          :expand-on-click-node="false"
-          :default-expanded-keys="expandedKeys"
-          :filter-node-method="filterNode"
-          :props="{ children: 'children', label: 'label' }"
-          highlight-current
-          node-key="id"
-          class="product-tree-scroll"
-          style="height: calc(100vh - 190px); overflow-y: auto"
-        >
+        <el-tree ref="tree"
+                 v-loading="treeLoad"
+                 :data="list"
+                 @node-click="handleNodeClick"
+                 :expand-on-click-node="false"
+                 @node-expand="handleNodeExpand"
+                 @node-collapse="handleNodeCollapse"
+                 :key="treeKey"
+                 :default-expanded-keys="expandedKeys"
+                 :filter-node-method="filterNode"
+                 :props="{ children: 'children', label: 'label' }"
+                 highlight-current
+                 node-key="id"
+                 class="product-tree-scroll"
+                 style="height: calc(100vh - 190px); overflow-y: auto">
           <template #default="{ node, data }">
             <div class="custom-tree-node">
               <span class="tree-node-content">
@@ -43,23 +40,23 @@
                 <span class="tree-node-label">{{ data.label }}</span>
               </span>
               <div>
-                <el-button
-                  type="primary"
-                  link
-                  @click="openProDia('edit', data)"
-                >
+                <el-button type="primary"
+                           link
+                           :disabled="isTopLevelNode(data, node)"
+                           @click="openProDia('edit', data)">
                   缂栬緫
                 </el-button>
-                <el-button type="primary" link @click="openProDia('add', data)" :disabled="node.level >= 3">
+                <el-button type="primary"
+                           link
+                           @click="openProDia('add', data)">
                   娣诲姞浜у搧
                 </el-button>
-                <el-button
-                  v-if="!node.childNodes.length"
-                  style="margin-left: 4px"
-                  type="danger"
-                  link
-                  @click="remove(node, data)"
-                >
+                <el-button v-if="!node.childNodes.length"
+                           style="margin-left: 4px"
+                           type="danger"
+                           link
+                           :disabled="isTopLevelNode(data, node)"
+                           @click="remove(node, data)">
                   鍒犻櫎
                 </el-button>
               </div>
@@ -69,103 +66,109 @@
       </div>
     </div>
     <div class="right">
-      <div style="margin-bottom: 10px" v-if="isShowButton">
-        <el-button type="primary" @click="openModelDia('add')">
+      <div style="margin-bottom: 10px"
+           v-if="isShowButton">
+        <el-button type="primary"
+                   @click="openModelDia('add')">
           鏂板瑙勬牸鍨嬪彿
         </el-button>
-        <ImportExcel :product-id="currentId" @uploadSuccess="getModelList" />
-        <el-button
-          type="danger"
-          @click="handleDelete"
-          style="margin-left: 10px"
-          plain
-        >
+        <ImportExcel :product-id="currentId"
+                     @uploadSuccess="getModelList" />
+        <el-button type="danger"
+                   @click="handleDelete"
+                   style="margin-left: 10px"
+                   plain>
           鍒犻櫎
         </el-button>
       </div>
-      <PIMTable
-        rowKey="id"
-        :column="tableColumn"
-        :tableData="tableData"
-        :page="page"
-        :isSelection="true"
-        @selection-change="handleSelectionChange"
-        :tableLoading="tableLoading"
-        @pagination="pagination"
-      ></PIMTable>
+      <PIMTable rowKey="id"
+                :column="tableColumn"
+                :tableData="tableData"
+                :page="page"
+                :isSelection="true"
+                @selection-change="handleSelectionChange"
+                :tableLoading="tableLoading"
+                @pagination="pagination"></PIMTable>
     </div>
-    <el-dialog v-model="productDia" title="浜у搧" width="400px" @keydown.enter.prevent>
-      <el-form
-        :model="form"
-        label-width="140px"
-        label-position="top"
-        :rules="rules"
-        ref="formRef"
-      >
+    <el-dialog v-model="productDia"
+               title="浜у搧"
+               width="400px"
+               @keydown.enter.prevent>
+      <el-form :model="form"
+               label-width="140px"
+               label-position="top"
+               :rules="rules"
+               ref="formRef">
         <el-row :gutter="30">
           <el-col :span="24">
-            <el-form-item label="浜у搧鍚嶇О锛�" prop="productName">
-              <el-input
-                v-model="form.productName"
-                placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
-                maxlength="20"
-                show-word-limit
-                clearable
-                @keydown.enter.prevent
-              />
+            <el-form-item label="浜у搧鍚嶇О锛�"
+                          prop="productName">
+              <el-input v-model="form.productName"
+                        placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
+                        maxlength="20"
+                        show-word-limit
+                        clearable
+                        @keydown.enter.prevent />
             </el-form-item>
           </el-col>
         </el-row>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button type="primary" @click="submitForm">纭</el-button>
+          <el-button type="primary"
+                     @click="submitForm">纭</el-button>
           <el-button @click="closeProDia">鍙栨秷</el-button>
         </div>
       </template>
     </el-dialog>
-    <el-dialog
-      v-model="modelDia"
-      title="瑙勬牸鍨嬪彿"
-      width="400px"
-      @close="closeModelDia"
-      @keydown.enter.prevent
-    >
-      <el-form
-        :model="modelForm"
-        label-width="140px"
-        label-position="top"
-        :rules="modelRules"
-        ref="modelFormRef"
-      >
+    <el-dialog v-model="modelDia"
+               title="瑙勬牸鍨嬪彿"
+               width="400px"
+               @close="closeModelDia"
+               @keydown.enter.prevent>
+      <el-form :model="modelForm"
+               label-width="140px"
+               label-position="top"
+               :rules="modelRules"
+               ref="modelFormRef">
         <el-row>
+          <el-row>
+            <el-col :span="24">
+              <el-form-item label="浜у搧缂栧彿锛�"
+                            prop="productCode">
+                <el-input v-model="modelForm.productCode"
+                          placeholder="璇疯緭鍏ヤ骇鍝佺紪鍙�"
+                          clearable
+                          @keydown.enter.prevent />
+              </el-form-item>
+            </el-col>
+          </el-row>
           <el-col :span="24">
-            <el-form-item label="瑙勬牸鍨嬪彿锛�" prop="model">
-              <el-input
-                v-model="modelForm.model"
-                placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
-                clearable
-                @keydown.enter.prevent
-              />
+            <el-form-item label="瑙勬牸鍨嬪彿锛�"
+                          prop="model">
+              <el-input v-model="modelForm.model"
+                        placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
+                        clearable
+                        @keydown.enter.prevent />
             </el-form-item>
           </el-col>
         </el-row>
         <el-row>
           <el-col :span="24">
-            <el-form-item label="鍗曚綅锛�" prop="unit">
-              <el-input
-                v-model="modelForm.unit"
-                placeholder="璇疯緭鍏ュ崟浣�"
-                clearable
-                @keydown.enter.prevent
-              />
+            <el-form-item label="鍗曚綅锛�"
+                          prop="unit">
+              <el-input v-model="modelForm.unit"
+                        placeholder="璇疯緭鍏ュ崟浣�"
+                        clearable
+                        @keydown.enter.prevent />
             </el-form-item>
           </el-col>
         </el-row>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button type="primary" @click="submitModelForm">纭</el-button>
+          <el-button type="primary"
+                     @click="submitModelForm">纭</el-button>
           <el-button @click="closeModelDia">鍙栨秷</el-button>
         </div>
       </template>
@@ -174,359 +177,480 @@
 </template>
 
 <script setup>
-import { ref } from "vue";
-import { ElMessageBox } from "element-plus";
-import {
-  addOrEditProduct,
-  addOrEditProductModel,
-  delProduct,
-  delProductModel,
-  modelListPage,
-  productTreeList,
-} from "@/api/basicData/product.js";
-import ImportExcel from "./ImportExcel/index.vue";
+  import { nextTick, ref } from "vue";
+  import { ElMessageBox } from "element-plus";
+  import {
+    addOrEditProduct,
+    addOrEditProductModel,
+    delProduct,
+    delProductModel,
+    modelListPage,
+    productTreeList,
+  } from "@/api/basicData/product.js";
+  import ImportExcel from "./ImportExcel/index.vue";
 
-const { proxy } = getCurrentInstance();
-const tree = ref(null);
-const containerRef = ref(null);
+  const { proxy } = getCurrentInstance();
+  const tree = ref(null);
+  const containerRef = ref(null);
+  const treeKey = ref(0);
+  const expandedKeySet = new Set();
+  const EXPANDED_STORAGE_KEY = "basicData_product_tree_expanded_keys_v2";
 
-const productDia = ref(false);
-const modelDia = ref(false);
-const modelOperationType = ref("");
-const search = ref("");
-const currentId = ref("");
-const currentParentId = ref("");
-const operationType = ref("");
-const treeLoad = ref(false);
-const list = ref([]);
-const expandedKeys = ref([]);
-const tableColumn = ref([
-  {
-    label: "瑙勬牸鍨嬪彿",
-    prop: "model",
-  },
-  {
-    label: "鍗曚綅",
-    prop: "unit",
-  },
-  {
-    dataType: "action",
-    label: "鎿嶄綔",
-    align: "center",
-    operation: [
-      {
-        name: "缂栬緫",
-        type: "text",
-        clickFun: (row) => {
-          openModelDia("edit", row);
-        },
-      },
-    ],
-  },
-]);
-const tableData = ref([]);
-const tableLoading = ref(false);
-const isShowButton = ref(false);
-const selectedRows = ref([]);
-const page = reactive({
-  current: 1,
-  size: 10,
-  total: 0,
-});
-const data = reactive({
-  form: {
-    productName: "",
-  },
-  rules: {
-    productName: [
-      { required: true, message: "璇疯緭鍏�", trigger: "blur" },
-      { max: 20, message: "浜у搧鍚嶇О涓嶈兘瓒呰繃20涓瓧绗�", trigger: "blur" },
-    ],
-  },
-  modelForm: {
-    model: "",
-    unit: "",
-  },
-  modelRules: {
-    model: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-    unit: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-  },
-});
-const { form, rules, modelForm, modelRules } = toRefs(data);
-// 鏌ヨ浜у搧鏍�
-const getProductTreeList = () => {
-  treeLoad.value = true;
-  productTreeList()
-    .then((res) => {
-      list.value = res;
-      list.value.forEach((a) => {
-        expandedKeys.value.push(a.label);
+  const loadExpandedKeys = () => {
+    if (typeof window === "undefined") {
+      return [];
+    }
+    try {
+      const saved = localStorage.getItem(EXPANDED_STORAGE_KEY);
+      return saved ? JSON.parse(saved) : [];
+    } catch (error) {
+      console.error(error);
+      return [];
+    }
+  };
+
+  const saveExpandedKeys = () => {
+    if (typeof window === "undefined") {
+      return;
+    }
+    localStorage.setItem(
+      EXPANDED_STORAGE_KEY,
+      JSON.stringify(Array.from(expandedKeySet))
+    );
+  };
+
+  loadExpandedKeys().forEach(key => expandedKeySet.add(key));
+
+  const syncExpandedKeysFromTree = () => {
+    const keys = [];
+    const walk = nodes => {
+      (nodes || []).forEach(item => {
+        if (item.expanded && item.data?.id !== undefined) {
+          keys.push(item.data.id);
+        }
+        if (item.childNodes && item.childNodes.length) {
+          walk(item.childNodes);
+        }
       });
-      treeLoad.value = false;
-    })
-    .catch((err) => {
-      treeLoad.value = false;
-    });
-};
-// 杩囨护浜у搧鏍�
-const searchFilter = () => {
-  proxy.$refs.tree.filter(search.value);
-};
-// 鎵撳紑浜у搧寮规
-const openProDia = (type, data) => {
-  operationType.value = type;
-  productDia.value = true;
-  form.value.productName = "";
-  if (type === "edit") {
-    form.value.productName = data.productName;
-  }
-};
-// 鎵撳紑瑙勬牸鍨嬪彿寮规
-const openModelDia = (type, data) => {
-  modelOperationType.value = type;
-  modelDia.value = true;
-  modelForm.value.model = "";
-  modelForm.value.model = "";
-  modelForm.value.id = "";
-  if (type === "edit") {
-    modelForm.value = { ...data };
-  }
-};
-// 鎻愪氦浜у搧鍚嶇О淇敼
-const submitForm = () => {
-  proxy.$refs.formRef.validate((valid) => {
-    if (valid) {
-      if (operationType.value === "add") {
-        form.value.parentId = currentId.value;
-        form.value.id = "";
-      } else if (operationType.value === "addOne") {
-        form.value.id = "";
-        form.value.parentId = "";
-      } else {
-        form.value.id = currentId.value;
-        form.value.parentId = "";
+    };
+
+    walk(tree.value?.root?.childNodes);
+    expandedKeySet.clear();
+    keys.forEach(key => expandedKeySet.add(key));
+    expandedKeys.value = keys;
+    saveExpandedKeys();
+  };
+
+  const normalizeExpandedKeys = treeData => {
+    const parentMap = new Map();
+    const walk = (nodes, parentId = null) => {
+      (nodes || []).forEach(item => {
+        parentMap.set(item.id, parentId);
+        if (item.children && item.children.length) {
+          walk(item.children, item.id);
+        }
+      });
+    };
+
+    walk(treeData);
+
+    const normalizedKeys = Array.from(expandedKeySet).filter(key => {
+      if (!parentMap.has(key)) {
+        return false;
       }
-      addOrEditProduct(form.value).then((res) => {
-        proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-        closeProDia();
-        getProductTreeList();
-      });
-    }
-  });
-};
-// 鍏抽棴浜у搧寮规
-const closeProDia = () => {
-  proxy.$refs.formRef.resetFields();
-  productDia.value = false;
-};
+      let currentId = key;
+      while (parentMap.has(currentId)) {
+        const parentId = parentMap.get(currentId);
+        if (!parentId) {
+          return true;
+        }
+        if (!expandedKeySet.has(parentId)) {
+          return false;
+        }
+        currentId = parentId;
+      }
+      return true;
+    });
 
-// 鍒犻櫎浜у搧
-const remove = (node, data) => {
-  let ids = [];
-  ids.push(data.id);
-  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
-    confirmButtonText: "纭",
-    cancelButtonText: "鍙栨秷",
-    type: "warning",
-  })
-    .then(() => {
-      tableLoading.value = true;
-      delProduct(ids)
-        .then((res) => {
-          proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+    if (normalizedKeys.length !== expandedKeySet.size) {
+      expandedKeySet.clear();
+      normalizedKeys.forEach(key => expandedKeySet.add(key));
+      saveExpandedKeys();
+    }
+  };
+
+  const productDia = ref(false);
+  const modelDia = ref(false);
+  const modelOperationType = ref("");
+  const search = ref("");
+  const currentId = ref("");
+  const currentParentId = ref("");
+  const operationType = ref("");
+  const treeLoad = ref(false);
+  const list = ref([]);
+  const expandedKeys = ref([]);
+  const tableColumn = ref([
+    {
+      label: "浜у搧缂栧彿",
+      prop: "productCode",
+    },
+    {
+      label: "瑙勬牸鍨嬪彿",
+      prop: "model",
+    },
+    {
+      label: "鍗曚綅",
+      prop: "unit",
+    },
+    {
+      dataType: "action",
+      label: "鎿嶄綔",
+      align: "center",
+      operation: [
+        {
+          name: "缂栬緫",
+          type: "text",
+          clickFun: row => {
+            openModelDia("edit", row);
+          },
+        },
+      ],
+    },
+  ]);
+  const tableData = ref([]);
+  const tableLoading = ref(false);
+  const isShowButton = ref(false);
+  const selectedRows = ref([]);
+  const page = reactive({
+    current: 1,
+    size: 10,
+    total: 0,
+  });
+  const data = reactive({
+    form: {
+      productName: "",
+    },
+    rules: {
+      productName: [
+        { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+        { max: 20, message: "浜у搧鍚嶇О涓嶈兘瓒呰繃20涓瓧绗�", trigger: "blur" },
+      ],
+    },
+    modelForm: {
+      model: "",
+      unit: "",
+      productCode: "",
+    },
+    modelRules: {
+      model: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      unit: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      productCode: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+    },
+  });
+  const { form, rules, modelForm, modelRules } = toRefs(data);
+  // 鏌ヨ浜у搧鏍�
+  const getProductTreeList = () => {
+    treeLoad.value = true;
+    productTreeList()
+      .then(res => {
+        list.value = res || [];
+        normalizeExpandedKeys(list.value);
+        expandedKeys.value = Array.from(expandedKeySet);
+        treeKey.value += 1;
+        nextTick(() => {
+          tree.value?.setDefaultExpandedKeys?.(expandedKeys.value);
+        });
+      })
+      .catch(err => {
+        console.error(err);
+      })
+      .finally(() => {
+        treeLoad.value = false;
+      });
+  };
+  const handleNodeExpand = data => {
+    nextTick(syncExpandedKeysFromTree);
+  };
+  const handleNodeCollapse = (data, node) => {
+    node?.eachNode?.(item => {
+      item.collapse();
+    });
+    nextTick(syncExpandedKeysFromTree);
+  };
+  // 杩囨护浜у搧鏍�
+  const searchFilter = () => {
+    proxy.$refs.tree.filter(search.value);
+  };
+  const isTopLevelNode = (data, node) => {
+    if (node?.level !== undefined) {
+      return node.level === 1;
+    }
+    return [null, undefined, "", 0, "0"].includes(data?.parentId);
+  };
+  // 鎵撳紑浜у搧寮规
+  const openProDia = (type, data) => {
+    if (data && type === "edit" && isTopLevelNode(data)) {
+      proxy.$modal.msgWarning("涓�绾ц妭鐐逛笉鑳界紪杈戞垨鍒犻櫎");
+      return;
+    }
+    operationType.value = type;
+    productDia.value = true;
+    form.value.productName = "";
+    if (type === "edit") {
+      form.value.productName = data.productName;
+    }
+  };
+  // 鎵撳紑瑙勬牸鍨嬪彿寮规
+  const openModelDia = (type, data) => {
+    modelOperationType.value = type;
+    modelDia.value = true;
+    modelForm.value.model = "";
+    modelForm.value.unit = "";
+    modelForm.value.productCode = "";
+    modelForm.value.id = "";
+    if (type === "edit") {
+      modelForm.value = { ...data };
+    }
+  };
+  // 鎻愪氦浜у搧鍚嶇О淇敼
+  const submitForm = () => {
+    proxy.$refs.formRef.validate(valid => {
+      if (valid) {
+        if (operationType.value === "add") {
+          form.value.parentId = currentId.value;
+          form.value.id = "";
+        } else if (operationType.value === "addOne") {
+          form.value.id = "";
+          form.value.parentId = "";
+        } else {
+          form.value.id = currentId.value;
+          form.value.parentId = "";
+        }
+        addOrEditProduct(form.value).then(res => {
+          proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+          closeProDia();
           getProductTreeList();
-        })
-        .finally(() => {
-          tableLoading.value = false;
         });
-    })
-    .catch(() => {
-      proxy.$modal.msg("宸插彇娑�");
+      }
     });
-};
-// 閫夋嫨浜у搧
-const handleNodeClick = (val, node, el) => {
-  // 鍒ゆ柇鏄惁涓哄彾瀛愯妭鐐�
-  isShowButton.value = !(val.children && val.children.length > 0);
-  // 鍙湁鍙跺瓙鑺傜偣鎵嶆墽琛屼互涓嬮�昏緫
-  currentId.value = val.id;
-  currentParentId.value = val.parentId;
-  getModelList();
-};
+  };
+  // 鍏抽棴浜у搧寮规
+  const closeProDia = () => {
+    proxy.$refs.formRef.resetFields();
+    productDia.value = false;
+  };
 
-// 鎻愪氦瑙勬牸鍨嬪彿淇敼
-const submitModelForm = () => {
-  proxy.$refs.modelFormRef.validate((valid) => {
-    if (valid) {
-      modelForm.value.productId = currentId.value;
-      addOrEditProductModel(modelForm.value).then((res) => {
-        proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-        closeModelDia();
-        getModelList();
-      });
+  // 鍒犻櫎浜у搧
+  const remove = (node, data) => {
+    if (isTopLevelNode(data, node)) {
+      proxy.$modal.msgWarning("涓�绾ц妭鐐逛笉鑳界紪杈戞垨鍒犻櫎");
+      return;
     }
-  });
-};
-// 鍏抽棴鍨嬪彿寮规
-const closeModelDia = () => {
-  proxy.$refs.modelFormRef.resetFields();
-  modelDia.value = false;
-};
-// 琛ㄦ牸閫夋嫨鏁版嵁
-const handleSelectionChange = (selection) => {
-  selectedRows.value = selection;
-};
-
-// 鏌ヨ瑙勬牸鍨嬪彿
-const pagination = (obj) => {
-  page.current = obj.page;
-  page.size = obj.limit;
-  getModelList();
-};
-const getModelList = () => {
-  tableLoading.value = true;
-  modelListPage({
-    id: currentId.value,
-    current: page.current,
-    size: page.size,
-  }).then((res) => {
-    console.log("res", res);
-    tableData.value = res.records;
-    page.total = res.total;
-    tableLoading.value = false;
-  });
-};
-// 鍒犻櫎瑙勬牸鍨嬪彿
-const handleDelete = () => {
-  let ids = [];
-  if (selectedRows.value.length > 0) {
-    ids = selectedRows.value.map((item) => item.id);
-  } else {
-    proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
-    return;
-  }
-  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
-    confirmButtonText: "纭",
-    cancelButtonText: "鍙栨秷",
-    type: "warning",
-  })
-    .then(() => {
-      tableLoading.value = true;
-      delProductModel(ids)
-        .then((res) => {
-          proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-          getModelList();
-        })
-        .finally(() => {
-          tableLoading.value = false;
-        });
+    let ids = [];
+    ids.push(data.id);
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
     })
-    .catch(() => {
-      proxy.$modal.msg("宸插彇娑�");
+      .then(() => {
+        tableLoading.value = true;
+        delProduct(ids)
+          .then(res => {
+            proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+            getProductTreeList();
+          })
+          .finally(() => {
+            tableLoading.value = false;
+          });
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
+  };
+  // 閫夋嫨浜у搧
+  const handleNodeClick = (val, node, el) => {
+    // 鍒ゆ柇鏄惁涓哄彾瀛愯妭鐐�
+    isShowButton.value = !(val.children && val.children.length > 0);
+    // 鍙湁鍙跺瓙鑺傜偣鎵嶆墽琛屼互涓嬮�昏緫
+    currentId.value = val.id;
+    currentParentId.value = val.parentId;
+    getModelList();
+  };
+
+  // 鎻愪氦瑙勬牸鍨嬪彿淇敼
+  const submitModelForm = () => {
+    proxy.$refs.modelFormRef.validate(valid => {
+      if (valid) {
+        modelForm.value.productId = currentId.value;
+        addOrEditProductModel(modelForm.value).then(res => {
+          proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+          closeModelDia();
+          getModelList();
+        });
+      }
     });
-};
-// 璋冪敤tree杩囨护鏂规硶 涓枃鑻辫繃婊�
-const filterNode = (value, data, node) => {
-  if (!value) {
-    //濡傛灉鏁版嵁涓虹┖锛屽垯杩斿洖true,鏄剧ず鎵�鏈夌殑鏁版嵁椤�
-    return true;
-  }
-  // 鏌ヨ鍒楄〃鏄惁鏈夊尮閰嶆暟鎹紝灏嗗�煎皬鍐欙紝鍖归厤鑻辨枃鏁版嵁
-  let val = value.toLowerCase();
-  return chooseNode(val, data, node); // 璋冪敤杩囨护浜屽眰鏂规硶
-};
-// 杩囨护鐖惰妭鐐� / 瀛愯妭鐐� (濡傛灉杈撳叆鐨勫弬鏁版槸鐖惰妭鐐逛笖鑳藉尮閰嶏紝鍒欒繑鍥炶鑺傜偣浠ュ強鍏朵笅鐨勬墍鏈夊瓙鑺傜偣锛涘鏋滃弬鏁版槸瀛愯妭鐐癸紝鍒欒繑鍥炶鑺傜偣鐨勭埗鑺傜偣銆俷ame鏄腑鏂囧瓧绗︼紝enName鏄嫳鏂囧瓧绗�.
-const chooseNode = (value, data, node) => {
-  if (data.label.indexOf(value) !== -1) {
-    return true;
-  }
-  const level = node.level;
-  // 濡傛灉浼犲叆鐨勮妭鐐规湰韬氨鏄竴绾ц妭鐐瑰氨涓嶇敤鏍¢獙浜�
-  if (level === 1) {
-    return false;
-  }
-  // 鍏堝彇褰撳墠鑺傜偣鐨勭埗鑺傜偣
-  let parentData = node.parent;
-  // 閬嶅巻褰撳墠鑺傜偣鐨勭埗鑺傜偣
-  let index = 0;
-  while (index < level - 1) {
-    // 濡傛灉鍖归厤鍒扮洿鎺ヨ繑鍥烇紝姝ゅname鍊兼槸涓枃瀛楃锛宔nName鏄嫳鏂囧瓧绗︺�傚垽鏂尮閰嶄腑鑻辨枃杩囨护
-    if (parentData.data.label.indexOf(value) !== -1) {
+  };
+  // 鍏抽棴鍨嬪彿寮规
+  const closeModelDia = () => {
+    proxy.$refs.modelFormRef.resetFields();
+    modelDia.value = false;
+  };
+  // 琛ㄦ牸閫夋嫨鏁版嵁
+  const handleSelectionChange = selection => {
+    selectedRows.value = selection;
+  };
+
+  // 鏌ヨ瑙勬牸鍨嬪彿
+  const pagination = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    getModelList();
+  };
+  const getModelList = () => {
+    tableLoading.value = true;
+    modelListPage({
+      id: currentId.value,
+      current: page.current,
+      size: page.size,
+    }).then(res => {
+      console.log("res", res);
+      tableData.value = res.records;
+      page.total = res.total;
+      tableLoading.value = false;
+    });
+  };
+  // 鍒犻櫎瑙勬牸鍨嬪彿
+  const handleDelete = () => {
+    let ids = [];
+    if (selectedRows.value.length > 0) {
+      ids = selectedRows.value.map(item => item.id);
+    } else {
+      proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+      return;
+    }
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        tableLoading.value = true;
+        delProductModel(ids)
+          .then(res => {
+            proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+            getModelList();
+          })
+          .finally(() => {
+            tableLoading.value = false;
+          });
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
+  };
+  // 璋冪敤tree杩囨护鏂规硶 涓枃鑻辫繃婊�
+  const filterNode = (value, data, node) => {
+    if (!value) {
+      //濡傛灉鏁版嵁涓虹┖锛屽垯杩斿洖true,鏄剧ず鎵�鏈夌殑鏁版嵁椤�
       return true;
     }
-    // 鍚﹀垯鐨勮瘽鍐嶅線涓婁竴灞傚仛鍖归厤
-    parentData = parentData.parent;
-    index++;
-  }
-  // 娌″尮閰嶅埌杩斿洖false
-  return false;
-};
-getProductTreeList();
+    // 鏌ヨ鍒楄〃鏄惁鏈夊尮閰嶆暟鎹紝灏嗗�煎皬鍐欙紝鍖归厤鑻辨枃鏁版嵁
+    let val = value.toLowerCase();
+    return chooseNode(val, data, node); // 璋冪敤杩囨护浜屽眰鏂规硶
+  };
+  // 杩囨护鐖惰妭鐐� / 瀛愯妭鐐� (濡傛灉杈撳叆鐨勫弬鏁版槸鐖惰妭鐐逛笖鑳藉尮閰嶏紝鍒欒繑鍥炶鑺傜偣浠ュ強鍏朵笅鐨勬墍鏈夊瓙鑺傜偣锛涘鏋滃弬鏁版槸瀛愯妭鐐癸紝鍒欒繑鍥炶鑺傜偣鐨勭埗鑺傜偣銆俷ame鏄腑鏂囧瓧绗︼紝enName鏄嫳鏂囧瓧绗�.
+  const chooseNode = (value, data, node) => {
+    if (data.label.indexOf(value) !== -1) {
+      return true;
+    }
+    const level = node.level;
+    // 濡傛灉浼犲叆鐨勮妭鐐规湰韬氨鏄竴绾ц妭鐐瑰氨涓嶇敤鏍¢獙浜�
+    if (level === 1) {
+      return false;
+    }
+    // 鍏堝彇褰撳墠鑺傜偣鐨勭埗鑺傜偣
+    let parentData = node.parent;
+    // 閬嶅巻褰撳墠鑺傜偣鐨勭埗鑺傜偣
+    let index = 0;
+    while (index < level - 1) {
+      // 濡傛灉鍖归厤鍒扮洿鎺ヨ繑鍥烇紝姝ゅname鍊兼槸涓枃瀛楃锛宔nName鏄嫳鏂囧瓧绗︺�傚垽鏂尮閰嶄腑鑻辨枃杩囨护
+      if (parentData.data.label.indexOf(value) !== -1) {
+        return true;
+      }
+      // 鍚﹀垯鐨勮瘽鍐嶅線涓婁竴灞傚仛鍖归厤
+      parentData = parentData.parent;
+      index++;
+    }
+    // 娌″尮閰嶅埌杩斿洖false
+    return false;
+  };
+  getProductTreeList();
 </script>
 
 <style scoped>
-.product-view {
-  display: flex;
-}
-.left {
-  width: 450px;
-  min-width: 450px;
-  padding: 16px;
-  background: #ffffff;
-}
-.right {
-  flex: 1;
-  min-width: 0;
-  padding: 16px;
-  margin-left: 20px;
-  background: #ffffff;
-}
-.custom-tree-node {
-  flex: 1;
-  min-width: 0;
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  font-size: 14px;
-  padding-right: 8px;
-}
-.tree-node-content {
-  flex: 1;
-  min-width: 0;
-  display: flex;
-  align-items: center;
-  height: 100%;
-  overflow: hidden;
-}
-.tree-node-content .orange-icon {
-  flex-shrink: 0;
-}
-.tree-node-label {
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-.orange-icon {
-  color: orange;
-  font-size: 18px;
-  margin-right: 8px; /* 鍥炬爣涓庢枃瀛椾箣闂村姞鐐归棿璺� */
-}
-.product-tree-scroll {
-  scrollbar-width: thin;
-  scrollbar-color: #c0c4cc #f5f7fa;
-}
-.product-tree-scroll::-webkit-scrollbar {
-  width: 8px;
-}
-.product-tree-scroll::-webkit-scrollbar-track {
-  background: #f5f7fa;
-  border-radius: 4px;
-}
-.product-tree-scroll::-webkit-scrollbar-thumb {
-  background: #c0c4cc;
-  border-radius: 4px;
-}
-.product-tree-scroll::-webkit-scrollbar-thumb:hover {
-  background: #909399;
-}
+  .product-view {
+    display: flex;
+  }
+  .left {
+    width: 450px;
+    min-width: 450px;
+    padding: 16px;
+    background: #ffffff;
+  }
+  .right {
+    flex: 1;
+    min-width: 0;
+    padding: 16px;
+    margin-left: 20px;
+    background: #ffffff;
+  }
+  .custom-tree-node {
+    flex: 1;
+    min-width: 0;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    font-size: 14px;
+    padding-right: 8px;
+  }
+  .tree-node-content {
+    flex: 1;
+    min-width: 0;
+    display: flex;
+    align-items: center;
+    height: 100%;
+    overflow: hidden;
+  }
+  .tree-node-content .orange-icon {
+    flex-shrink: 0;
+  }
+  .tree-node-label {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+  .orange-icon {
+    color: orange;
+    font-size: 18px;
+    margin-right: 8px; /* 鍥炬爣涓庢枃瀛椾箣闂村姞鐐归棿璺� */
+  }
+  .product-tree-scroll {
+    scrollbar-width: thin;
+    scrollbar-color: #c0c4cc #f5f7fa;
+  }
+  .product-tree-scroll::-webkit-scrollbar {
+    width: 8px;
+  }
+  .product-tree-scroll::-webkit-scrollbar-track {
+    background: #f5f7fa;
+    border-radius: 4px;
+  }
+  .product-tree-scroll::-webkit-scrollbar-thumb {
+    background: #c0c4cc;
+    border-radius: 4px;
+  }
+  .product-tree-scroll::-webkit-scrollbar-thumb:hover {
+    background: #909399;
+  }
 </style>
diff --git a/src/views/basicData/supplierManage/components/HomeTab.vue b/src/views/basicData/supplierManage/components/HomeTab.vue
index 85c3265..47dce00 100644
--- a/src/views/basicData/supplierManage/components/HomeTab.vue
+++ b/src/views/basicData/supplierManage/components/HomeTab.vue
@@ -1,7 +1,7 @@
 <template>
-  <div class="app-container">
+  <div>
     <div class="search_form">
-      <div>
+      <div style="margin-bottom: 10px;">
         <span class="search_title">渚涘簲鍟嗘。妗堬細</span>
         <el-input
             v-model="searchForm.supplierName"
@@ -15,7 +15,7 @@
         >鎼滅储</el-button
         >
       </div>
-      <div>
+      <div style="margin-bottom: 10px;">
         <el-button type="primary" @click="openForm('add')"
         >鏂板渚涘簲鍟�</el-button
         >
diff --git a/src/views/collaborativeApproval/approvalManagement/index.vue b/src/views/collaborativeApproval/approvalManagement/index.vue
new file mode 100644
index 0000000..4638392
--- /dev/null
+++ b/src/views/collaborativeApproval/approvalManagement/index.vue
@@ -0,0 +1,881 @@
+<template>
+  <div class="app-container">
+    <!-- 椤甸潰鏍囬 -->
+    <div class="page-header">
+      <div class="header-title">
+        <el-icon class="title-icon"><Setting /></el-icon>
+        <span>瀹℃壒娴佺▼閰嶇疆</span>
+      </div>
+      <div class="header-desc">涓轰笉鍚屽鎵圭被鍨嬮厤缃鎵规祦绋嬪拰瀹℃壒浜�</div>
+    </div>
+
+    <!-- 瀹℃壒绫诲瀷鍒囨崲 - 绱у噾鏍囩寮� -->
+    <div class="type-tabs">
+      <div
+        v-for="type in approveTypes"
+        :key="type.value"
+        class="type-tab"
+        :class="{ active: activeTab === type.value }"
+        @click="activeTab = type.value; handleTabChange()"
+      >
+        <el-icon :size="14" :style="{ color: activeTab === type.value ? type.color : '#909399' }">
+          <component :is="type.icon" />
+        </el-icon>
+        <span class="tab-name">{{ type.label }}</span>
+      </div>
+    </div>
+
+    <!-- 涓昏鍐呭鍖哄煙 -->
+    <el-card class="config-card" shadow="hover" v-loading="loading">
+      <template #header>
+        <div class="card-header">
+          <div class="header-left">
+            <div class="type-icon" :style="{ backgroundColor: getTypeColor(currentApproveType) }">
+              <el-icon :size="20" color="#fff"><component :is="getTypeIcon(currentApproveType)" /></el-icon>
+            </div>
+            <div class="header-info">
+              <span class="type-name">{{ currentApproveTypeName }}</span>
+              <el-tag :type="approverList.length > 0 ? 'success' : 'warning'" size="small" effect="light">
+                {{ approverList.length > 0 ? `宸查厤缃� ${approverList.length} 涓鎵逛汉` : '鏈厤缃鎵逛汉' }}
+              </el-tag>
+            </div>
+          </div>
+          <div class="header-actions" v-if="approverList.length > 0">
+            <el-button @click="handleReset" size="default">
+              <el-icon><RefreshLeft /></el-icon>
+              閲嶇疆
+            </el-button>
+            <el-button type="primary" @click="handleSave" :loading="saveLoading" size="default">
+              <el-icon><Check /></el-icon>
+              淇濆瓨閰嶇疆
+            </el-button>
+          </div>
+        </div>
+      </template>
+
+      <!-- 瀹℃壒娴佺▼灞曠ず -->
+      <div class="flow-wrapper" v-if="approverList.length > 0">
+        <div class="flow-container">
+          <div
+            v-for="(item, index) in approverList"
+            :key="index"
+            class="flow-item"
+          >
+            <!-- 瀹℃壒鑺傜偣鍗$墖 -->
+            <div class="node-card" :class="{ 'empty': !item.approverId }">
+              <!-- 椤堕儴搴忓彿鍜岀骇鍒� -->
+              <div class="node-badge">{{ index + 1 }}</div>
+              
+              <!-- 澶村儚鍖哄煙 -->
+              <div class="node-avatar-section">
+                <div 
+                  class="node-avatar" 
+                  :class="{ 'has-user': item.approverId }"
+                  :style="item.approverId ? { backgroundColor: getAvatarColor(item.approverName) } : {}"
+                >
+                  <span v-if="item.approverId">{{ item.approverName.charAt(0) }}</span>
+                  <el-icon v-else :size="24"><User /></el-icon>
+                </div>
+                <div class="node-level">{{ getLevelText(index) }}</div>
+              </div>
+
+              <!-- 閫夋嫨鍖哄煙 -->
+              <div class="node-select-section">
+                <el-select
+                  v-model="item.approverId"
+                  placeholder="閫夋嫨瀹℃壒浜�"
+                  filterable
+                  size="default"
+                  @change="(val) => handleApproverChange(val, item)"
+                >
+                  <el-option
+                    v-for="user in userList"
+                    :key="user.userId"
+                    :label="user.nickName"
+                    :value="user.userId"
+                  />
+                </el-select>
+              </div>
+
+              <!-- 鎿嶄綔鎸夐挳 -->
+              <div class="node-actions">
+                <el-button
+                  type="primary"
+                  circle
+                  :disabled="index === 0"
+                  @click="moveLeft(index)"
+                  size="small"
+                  class="action-btn"
+                  title="鍓嶇Щ"
+                >
+                  <el-icon><ArrowLeft /></el-icon>
+                </el-button>
+                <el-button
+                  type="primary"
+                  circle
+                  :disabled="index === approverList.length - 1"
+                  @click="moveRight(index)"
+                  size="small"
+                  class="action-btn"
+                  title="鍚庣Щ"
+                >
+                  <el-icon><ArrowRight /></el-icon>
+                </el-button>
+                <el-button
+                  type="danger"
+                  circle
+                  @click="handleDelete(index)"
+                  size="small"
+                  class="action-btn"
+                >
+                  <el-icon><Delete /></el-icon>
+                </el-button>
+              </div>
+            </div>
+
+            <!-- 杩炴帴绠ご -->
+            <div class="arrow-connector" v-if="index < approverList.length - 1">
+              <div class="arrow-line"></div>
+              <el-icon class="arrow-icon"><ArrowRight /></el-icon>
+            </div>
+          </div>
+
+          <!-- 鏂板鑺傜偣鎸夐挳 - 鏀惧湪娴佺▼鏈�鍚� -->
+          <div class="add-node-item">
+            <div class="arrow-connector" v-if="approverList.length > 0">
+              <div class="arrow-line"></div>
+              <el-icon class="arrow-icon"><ArrowRight /></el-icon>
+            </div>
+            <div class="add-node-card" @click="handleAdd">
+              <div class="add-icon-wrapper">
+                <el-icon :size="28"><Plus /></el-icon>
+              </div>
+              <span class="add-text">鏂板瀹℃壒浜�</span>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 绌虹姸鎬� -->
+      <div class="empty-state" v-else>
+        <div class="empty-content">
+          <div class="empty-icon-wrapper">
+            <el-icon :size="48" color="#c0c4cc"><User /></el-icon>
+          </div>
+          <div class="empty-text">鏆傛棤瀹℃壒浜洪厤缃�</div>
+          <div class="empty-subtext">鐐瑰嚮涓嬫柟鎸夐挳娣诲姞绗竴涓鎵逛汉</div>
+          <el-button type="primary" size="large" @click="handleAdd" class="empty-add-btn">
+            <el-icon><Plus /></el-icon>
+            鏂板瀹℃壒浜�
+          </el-button>
+        </div>
+      </div>
+    </el-card>
+
+    <!-- 搴曢儴鎻愮ず -->
+    <div class="bottom-tips">
+      <el-icon><InfoFilled /></el-icon>
+      <span>鎻愮ず锛氭瘡涓祦绋嬭嚦灏戦厤缃竴涓鎵逛汉锛屽鎵规寜椤哄簭娴佽浆锛屽彲閫氳繃绠ご璋冩暣椤哄簭</span>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import {
+  Plus, ArrowLeft, Delete, Check, RefreshLeft, Setting,
+  Suitcase, Calendar, Location, Money, ShoppingCart, DocumentChecked,
+  Van, ArrowRight, User, InfoFilled
+} from '@element-plus/icons-vue';
+import { getApproveProcessConfigNodeList, addApproveProcessConfigNode } from '@/api/collaborativeApproval/approvalManagement';
+import { userListNoPage } from '@/api/system/user';
+
+// 褰撳墠閫変腑鐨勬爣绛鹃〉
+const activeTab = ref('1');
+
+// 瀹℃壒绫诲瀷閰嶇疆鏁扮粍
+const approveTypes = [
+  { value: '1', label: '鍏嚭绠$悊', icon: 'Suitcase', color: '#409EFF' },
+  { value: '2', label: '璇峰亣绠$悊', icon: 'Calendar', color: '#67C23A' },
+  { value: '3', label: '鍑哄樊绠$悊', icon: 'Location', color: '#E6A23C' },
+  { value: '4', label: '鎶ラ攢绠$悊', icon: 'Money', color: '#F56C6C' },
+  { value: '5', label: '閲囪喘瀹℃壒', icon: 'ShoppingCart', color: '#909399' },
+  { value: '6', label: '鎶ヤ环瀹℃壒', icon: 'DocumentChecked', color: '#9B59B6' },
+  { value: '7', label: '鍙戣揣瀹℃壒', icon: 'Van', color: '#1ABC9C' },
+];
+
+// 瀹℃壒绫诲瀷鍚嶇О鏄犲皠
+const approveTypeNameMap = {
+  1: '鍏嚭绠$悊',
+  2: '璇峰亣绠$悊',
+  3: '鍑哄樊绠$悊',
+  4: '鎶ラ攢绠$悊',
+  5: '閲囪喘瀹℃壒',
+  6: '鎶ヤ环瀹℃壒',
+  7: '鍙戣揣瀹℃壒',
+};
+
+// 瀹℃壒绫诲瀷鍥炬爣鏄犲皠
+const typeIconMap = {
+  1: 'Suitcase',
+  2: 'Calendar',
+  3: 'Location',
+  4: 'Money',
+  5: 'ShoppingCart',
+  6: 'DocumentChecked',
+  7: 'Van',
+};
+
+// 瀹℃壒绫诲瀷棰滆壊鏄犲皠
+const typeColorMap = {
+  1: '#409EFF',
+  2: '#67C23A',
+  3: '#E6A23C',
+  4: '#F56C6C',
+  5: '#909399',
+  6: '#9B59B6',
+  7: '#1ABC9C',
+};
+
+// 澶村儚棰滆壊姹�
+const avatarColors = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#9B59B6', '#1ABC9C', '#FF6B6B', '#4ECDC4'];
+
+// 褰撳墠瀹℃壒绫诲瀷鍚嶇О
+const currentApproveTypeName = computed(() => {
+  return approveTypeNameMap[activeTab.value] || '鏈煡绫诲瀷';
+});
+
+// 褰撳墠瀹℃壒绫诲瀷
+const currentApproveType = computed(() => {
+  return Number(activeTab.value);
+});
+
+// 鑾峰彇绫诲瀷鍥炬爣
+const getTypeIcon = (type) => typeIconMap[type] || 'Setting';
+
+// 鑾峰彇绫诲瀷棰滆壊
+const getTypeColor = (type) => typeColorMap[type] || '#409EFF';
+
+// 鑾峰彇澶村儚棰滆壊
+const getAvatarColor = (name) => {
+  if (!name) return '#C0C4CC';
+  let hash = 0;
+  for (let i = 0; i < name.length; i++) {
+    hash = name.charCodeAt(i) + ((hash << 5) - hash);
+  }
+  return avatarColors[Math.abs(hash) % avatarColors.length];
+};
+
+// 鑾峰彇绾у埆鏂囨湰
+const getLevelText = (index) => {
+  const texts = ['绗竴绾�', '绗簩绾�', '绗笁绾�', '绗洓绾�', '绗簲绾�', '绗叚绾�', '绗竷绾�', '绗叓绾�'];
+  return texts[index] || `绗�${index + 1}绾;
+};
+
+// 瀹℃壒浜哄垪琛紙鐪熷疄鎺ュ彛锛�
+const userList = ref([]);
+
+// 瀹℃壒浜哄垪琛�
+const approverList = ref([]);
+
+// 鍘熷鏁版嵁锛岀敤浜庨噸缃�
+const originalList = ref([]);
+
+// 鍔犺浇鐘舵��
+const loading = ref(false);
+const saveLoading = ref(false);
+
+// 鏍囩椤靛垏鎹㈠鐞�
+const handleTabChange = () => {
+  loadData();
+};
+
+// 鍔犺浇瀹℃壒閰嶇疆鏁版嵁锛堟ā鎷燂級
+const loadData = async () => {
+  loading.value = true;
+  try {
+    const res = await getApproveProcessConfigNodeList(currentApproveType.value);
+    const source = Array.isArray(res?.data)
+      ? res.data
+      : Array.isArray(res?.rows)
+        ? res.rows
+        : Array.isArray(res?.data?.records)
+          ? res.data.records
+          : [];
+    const data = source.map((item, index) => ({
+      ...item,
+      sortOrder: item.nodeOrder ?? item.sortOrder ?? index + 1,
+    }));
+    approverList.value = data.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
+    originalList.value = JSON.parse(JSON.stringify(approverList.value));
+  } catch (error) {
+    approverList.value = [];
+    originalList.value = [];
+    ElMessage.error('鍔犺浇瀹℃壒閰嶇疆澶辫触');
+  } finally {
+    loading.value = false;
+  }
+};
+
+const loadUserList = async () => {
+  try {
+    const res = await userListNoPage();
+    userList.value = Array.isArray(res?.data) ? res.data : [];
+  } catch (error) {
+    userList.value = [];
+    ElMessage.error('鍔犺浇浜哄憳鍒楄〃澶辫触');
+  }
+};
+
+// 瀹℃壒浜洪�夋嫨鍙樺寲
+const handleApproverChange = (userId, row) => {
+  const user = userList.value.find((u) => u.userId === userId);
+  if (user) {
+    row.approverName = user.nickName;
+  }
+};
+
+// 鏂板瀹℃壒浜�
+const handleAdd = () => {
+  const newOrder = approverList.value.length + 1;
+  approverList.value.push({
+    id: null,
+    approveType: currentApproveType.value,
+    approverId: null,
+    approverName: '',
+    sortOrder: newOrder,
+  });
+};
+
+// 鍒犻櫎瀹℃壒浜�
+const handleDelete = (index) => {
+  ElMessageBox.confirm('纭畾鍒犻櫎璇ュ鎵逛汉鍚楋紵', '鎻愮ず', {
+    confirmButtonText: '纭畾',
+    cancelButtonText: '鍙栨秷',
+    type: 'warning',
+  })
+    .then(() => {
+      approverList.value.splice(index, 1);
+      approverList.value.forEach((item, idx) => {
+        item.sortOrder = idx + 1;
+      });
+      ElMessage.success('鍒犻櫎鎴愬姛');
+    })
+    .catch(() => {});
+};
+
+// 鍓嶇Щ
+const moveLeft = (index) => {
+  if (index === 0) return;
+  const temp = approverList.value[index];
+  approverList.value[index] = approverList.value[index - 1];
+  approverList.value[index - 1] = temp;
+  approverList.value[index].sortOrder = index + 1;
+  approverList.value[index - 1].sortOrder = index;
+};
+
+// 鍚庣Щ
+const moveRight = (index) => {
+  if (index === approverList.value.length - 1) return;
+  const temp = approverList.value[index];
+  approverList.value[index] = approverList.value[index + 1];
+  approverList.value[index + 1] = temp;
+  approverList.value[index].sortOrder = index + 1;
+  approverList.value[index + 1].sortOrder = index + 2;
+};
+
+// 淇濆瓨閰嶇疆
+const handleSave = async () => {
+  if (approverList.value.length === 0) {
+    ElMessage.warning('璇疯嚦灏戦厤缃竴涓鎵逛汉');
+    return;
+  }
+
+  const hasEmptyApprover = approverList.value.some((item) => !item.approverId);
+  if (hasEmptyApprover) {
+    ElMessage.warning('璇烽�夋嫨鎵�鏈夊鎵逛汉');
+    return;
+  }
+
+  const approverIds = approverList.value.map((item) => item.approverId);
+  const uniqueIds = [...new Set(approverIds)];
+  if (uniqueIds.length !== approverIds.length) {
+    ElMessage.warning('瀹℃壒浜轰笉鑳介噸澶�');
+    return;
+  }
+
+  saveLoading.value = true;
+  try {
+    const payload = approverList.value.map((item, index) => ({
+      approveType: currentApproveType.value,
+      nodeOrder: index + 1,
+      approverId: item.approverId,
+      approverName: item.approverName,
+    }));
+    await addApproveProcessConfigNode(payload);
+    ElMessage.success('淇濆瓨鎴愬姛');
+    await loadData();
+  } catch (error) {
+    ElMessage.error('淇濆瓨澶辫触');
+  } finally {
+    saveLoading.value = false;
+  }
+};
+
+// 閲嶇疆
+const handleReset = () => {
+  if (originalList.value.length === 0) {
+    approverList.value = [];
+    return;
+  }
+  ElMessageBox.confirm('纭畾瑕侀噸缃綋鍓嶉厤缃悧锛熸湭淇濆瓨鐨勬洿鏀瑰皢涓㈠け銆�', '鎻愮ず', {
+    confirmButtonText: '纭畾',
+    cancelButtonText: '鍙栨秷',
+    type: 'warning',
+  })
+    .then(() => {
+      approverList.value = JSON.parse(JSON.stringify(originalList.value));
+      ElMessage.success('宸查噸缃�');
+    })
+    .catch(() => {});
+};
+
+onMounted(async () => {
+  await loadUserList();
+  await loadData();
+});
+</script>
+
+<style scoped>
+.page-header {
+  margin-bottom: 20px;
+}
+
+.header-title {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  font-size: 20px;
+  font-weight: 600;
+  color: var(--el-text-color-primary, #303133);
+  margin-bottom: 6px;
+}
+
+.title-icon {
+  font-size: 24px;
+  color: var(--el-color-primary, #409EFF);
+}
+
+.header-desc {
+  font-size: 13px;
+  color: var(--el-text-color-secondary, #909399);
+  margin-left: 34px;
+}
+
+/* 瀹℃壒绫诲瀷鍒囨崲 - 绱у噾鏍囩寮� */
+.type-tabs {
+  display: flex;
+  gap: 4px;
+  margin-bottom: 16px;
+  padding: 4px;
+  background: var(--el-fill-color-light, #f5f7fa);
+  border-radius: 8px;
+  overflow-x: auto;
+}
+
+.type-tab {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  padding: 8px 14px;
+  border-radius: 6px;
+  cursor: pointer;
+  transition: all 0.2s ease;
+  white-space: nowrap;
+  font-size: 13px;
+  color: var(--el-text-color-regular, #606266);
+}
+
+.type-tab:hover {
+  background: var(--el-color-primary-light-9, rgba(64, 158, 255, 0.1));
+  color: var(--el-color-primary, #409EFF);
+}
+
+.type-tab.active {
+  background: var(--el-bg-color, #fff);
+  color: var(--el-color-primary, #409EFF);
+  font-weight: 600;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+}
+
+.tab-name {
+  font-size: 13px;
+}
+
+.tab-count {
+  min-width: 16px;
+  height: 16px;
+  padding: 0 5px;
+  background: var(--el-color-success, #67C23A);
+  color: #fff;
+  border-radius: 8px;
+  font-size: 11px;
+  font-weight: 600;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.config-card {
+  margin-bottom: 16px;
+  border-radius: 12px;
+}
+
+:deep(.el-card__header) {
+  padding: 16px 20px;
+  border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.header-left {
+  display: flex;
+  align-items: center;
+  gap: 14px;
+}
+
+.type-icon {
+  width: 44px;
+  height: 44px;
+  border-radius: 10px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.header-info {
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+}
+
+.type-name {
+  font-size: 16px;
+  font-weight: 600;
+  color: var(--el-text-color-primary, #303133);
+}
+
+.header-actions {
+  display: flex;
+  gap: 10px;
+}
+
+.flow-wrapper {
+  overflow-x: auto;
+  padding: 8px 4px;
+}
+
+.flow-container {
+  display: flex;
+  align-items: center;
+  gap: 0;
+  min-width: min-content;
+}
+
+.flow-item {
+  display: flex;
+  align-items: center;
+}
+
+.node-card {
+  width: 200px;
+  background: var(--el-bg-color, #fff);
+  border: 2px solid var(--el-border-color, #e4e7ed);
+  border-radius: 12px;
+  padding: 16px;
+  position: relative;
+  transition: all 0.3s ease;
+  flex-shrink: 0;
+}
+
+.node-card:hover {
+  border-color: var(--el-color-primary, #409EFF);
+  box-shadow: 0 4px 16px rgba(64, 158, 255, 0.15);
+  transform: translateY(-2px);
+}
+
+.node-card.empty {
+  border-style: dashed;
+  border-color: var(--el-border-color, #c0c4cc);
+  background: var(--el-fill-color-light, #fafbfc);
+}
+
+.node-card.empty:hover {
+  border-color: var(--el-color-primary, #409EFF);
+  background: var(--el-fill-color-light, #f5f7fa);
+}
+
+.node-badge {
+  position: absolute;
+  top: -10px;
+  left: 16px;
+  width: 24px;
+  height: 24px;
+  background: var(--el-color-primary, #409EFF);
+  color: #fff;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 13px;
+  font-weight: 700;
+  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.4);
+}
+
+.node-avatar-section {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  margin-bottom: 12px;
+  margin-top: 4px;
+}
+
+.node-avatar {
+  width: 56px;
+  height: 56px;
+  border-radius: 50%;
+  background: var(--el-fill-color, #f0f2f5);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 8px;
+  color: var(--el-text-color-placeholder, #c0c4cc);
+  transition: all 0.3s ease;
+}
+
+.node-avatar.has-user {
+  color: #fff;
+  font-size: 22px;
+  font-weight: 600;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+}
+
+.node-level {
+  font-size: 12px;
+  color: var(--el-text-color-secondary, #909399);
+  font-weight: 500;
+}
+
+.node-select-section {
+  margin-bottom: 12px;
+}
+
+.node-select-section :deep(.el-select) {
+  width: 100%;
+}
+
+.node-actions {
+  display: flex;
+  justify-content: center;
+  gap: 8px;
+  padding-top: 12px;
+  border-top: 1px solid var(--el-border-color-light, #ebeef5);
+}
+
+.action-btn {
+  transition: all 0.2s;
+}
+
+.action-btn:hover:not(:disabled) {
+  transform: scale(1.1);
+}
+
+.arrow-connector {
+  display: flex;
+  align-items: center;
+  width: 50px;
+  position: relative;
+}
+
+.arrow-line {
+  flex: 1;
+  height: 2px;
+  background: var(--el-border-color, #c0c4cc);
+}
+
+.arrow-icon {
+  color: var(--el-text-color-placeholder, #c0c4cc);
+  font-size: 14px;
+  margin-left: -2px;
+}
+
+/* 鏂板鑺傜偣鏍峰紡 */
+.add-node-item {
+  display: flex;
+  align-items: center;
+}
+
+.add-node-card {
+  width: 140px;
+  height: 200px;
+  border: 2px dashed var(--el-border-color, #c0c4cc);
+  border-radius: 12px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  gap: 12px;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  background: var(--el-fill-color-light, #fafbfc);
+  flex-shrink: 0;
+  margin-left: 0;
+}
+
+.add-node-card:hover {
+  border-color: var(--el-color-primary, #409EFF);
+  background: var(--el-color-primary-light-9, #f0f7ff);
+  transform: translateY(-2px);
+  box-shadow: 0 4px 16px rgba(64, 158, 255, 0.15);
+}
+
+.add-icon-wrapper {
+  width: 48px;
+  height: 48px;
+  border-radius: 50%;
+  background: var(--el-color-primary, #409EFF);
+  color: #fff;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
+  transition: all 0.3s ease;
+}
+
+.add-node-card:hover .add-icon-wrapper {
+  transform: scale(1.1);
+  box-shadow: 0 6px 16px rgba(64, 158, 255, 0.4);
+}
+
+.add-text {
+  font-size: 14px;
+  color: var(--el-text-color-regular, #606266);
+  font-weight: 500;
+}
+
+.add-node-card:hover .add-text {
+  color: var(--el-color-primary, #409EFF);
+}
+
+/* 绌虹姸鎬� */
+.empty-state {
+  padding: 50px 20px;
+}
+
+.empty-content {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 16px;
+}
+
+.empty-icon-wrapper {
+  width: 80px;
+  height: 80px;
+  border-radius: 50%;
+  background: var(--el-fill-color-light, #f5f7fa);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 8px;
+}
+
+.empty-text {
+  font-size: 16px;
+  font-weight: 600;
+  color: var(--el-text-color-regular, #606266);
+}
+
+.empty-subtext {
+  font-size: 13px;
+  color: var(--el-text-color-secondary, #909399);
+}
+
+.empty-add-btn {
+  margin-top: 8px;
+  padding: 12px 28px;
+}
+
+/* 搴曢儴鎻愮ず */
+.bottom-tips {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 8px;
+  padding: 12px 20px;
+  background: var(--el-fill-color-light, #f5f7fa);
+  border-radius: 8px;
+  color: var(--el-text-color-regular, #606266);
+  font-size: 13px;
+}
+
+.bottom-tips .el-icon {
+  color: var(--el-color-primary, #409EFF);
+  font-size: 16px;
+}
+
+@media (max-width: 768px) {
+  .type-tabs {
+    padding: 3px;
+  }
+
+  .type-tab {
+    padding: 6px 10px;
+    font-size: 12px;
+  }
+
+  .tab-name {
+    font-size: 12px;
+  }
+
+  .flow-container {
+    flex-wrap: wrap;
+    justify-content: center;
+  }
+
+  .arrow-connector {
+    width: 100%;
+    height: 30px;
+    flex-direction: row;
+    justify-content: center;
+  }
+
+  .arrow-line {
+    width: 2px;
+    height: 30px;
+  }
+
+  .arrow-icon {
+    right: auto;
+    top: auto;
+    bottom: -5px;
+    transform: rotate(90deg);
+  }
+
+  .add-node-item {
+    width: 100%;
+    justify-content: center;
+    margin-top: 10px;
+  }
+
+  .add-node-item .arrow-connector {
+    display: none;
+  }
+}
+</style>
diff --git a/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue b/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
index 20a4ee6..1269621 100644
--- a/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
+++ b/src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
@@ -39,39 +39,6 @@
 						</el-form-item>
 					</el-col>
 				</el-row>
-				<!-- 瀹℃壒浜洪�夋嫨锛堝姩鎬佽妭鐐癸級 -->
-				<el-row :gutter="30">
-					<el-col :span="12">
-						<el-form-item label="鐢宠浜猴細" prop="approveUser">
-							<el-select
-								v-model="form.approveUser"
-								placeholder="閫夋嫨浜哄憳"
-								disabled
-							>
-								<el-option
-									v-for="user in userList"
-									:key="user.userId"
-									:label="user.nickName"
-									:value="user.userId"
-								/>
-							</el-select>
-						</el-form-item>
-					</el-col>
-					<el-col :span="12">
-						<el-form-item label="鐢宠鏃ユ湡锛�" prop="approveTime">
-							<el-date-picker
-								v-model="form.approveTime"
-								type="date"
-								placeholder="璇烽�夋嫨鏃ユ湡"
-								value-format="YYYY-MM-DD"
-								format="YYYY-MM-DD"
-								clearable
-								style="width: 100%"
-								disabled
-							/>
-						</el-form-item>
-					</el-col>
-				</el-row>
 			</el-form>
 
       <!-- 鎶ヤ环瀹℃壒锛氬睍绀烘姤浠疯鎯咃紙澶嶇敤閿�鍞姤浠�"鏌ョ湅璇︽儏瀵硅瘽妗�"鍐呭缁撴瀯锛� -->
@@ -228,7 +195,6 @@
 	updateApproveNode
 } from "@/api/collaborativeApproval/approvalProcess.js";
 import useUserStore from "@/store/modules/user.js";
-import {userListNoPageByTenantId} from "@/api/system/user.js";
 import { WarningFilled, Edit, Check, MoreFilled } from '@element-plus/icons-vue'
 import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
 import { getPurchaseByCode } from "@/api/procurementManagement/procurementLedger.js";
@@ -248,7 +214,6 @@
 const formRef = ref(null);
 const userStore = useUserStore()
 const productOptions = ref([]);
-const userList = ref([])
 const quotationLoading = ref(false)
 const currentQuotation = ref({})
 const purchaseLoading = ref(false)
@@ -258,9 +223,7 @@
 
 const data = reactive({
 	form: {
-		approveTime: "",
 		approveId: "",
-		approveUser: "",
 		approveDeptId: "",
 		approveReason: "",
 		checkResult: "",
@@ -295,9 +258,6 @@
   dialogFormVisible.value = true;
   currentQuotation.value = {}
   currentPurchase.value = {}
-	userListNoPageByTenantId().then((res) => {
-		userList.value = res.data;
-	});
 	form.value = {...row}
 	// 绔嬪嵆娓呴櫎琛ㄥ崟楠岃瘉鐘舵�侊紙鍥犱负瀛楁鏄痙isabled鐨勶紝涓嶉渶瑕侀獙璇侊級
 	nextTick(() => {
diff --git a/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue b/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
index 998fa33..6461b2d 100644
--- a/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
+++ b/src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
@@ -97,96 +97,11 @@
             </el-form-item>
           </el-col>
         </el-row>
-        <!-- 瀹℃壒浜洪�夋嫨锛堝姩鎬佽妭鐐癸級 -->
-        <el-row>
-          <el-col :span="24">
-            <el-form-item>
-              <template #label>
-                <span>瀹℃壒浜洪�夋嫨锛�</span>
-                <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">鏂板鑺傜偣</el-button>
-              </template>
-              <div style="display: flex; align-items: flex-end; flex-wrap: wrap;">
-                <div
-                  v-for="(node, index) in approverNodes"
-                  :key="node.id"
-                  style="margin-right: 30px; text-align: center; margin-bottom: 10px;"
-                >
-                  <div>
-                    <span>瀹℃壒浜�</span>
-                    鈫�
-                  </div>
-                  <el-select
-                    v-model="node.userId"
-                    placeholder="閫夋嫨浜哄憳"
-                    style="width: 120px; margin-bottom: 8px;"
-                  >
-                    <el-option
-                      v-for="user in userList"
-                      :key="user.userId"
-                      :label="user.nickName"
-                      :value="user.userId"
-                    />
-                  </el-select>
-                  <div>
-                    <el-button
-                      type="danger"
-                      size="small"
-                      @click="removeApproverNode(index)"
-                      v-if="approverNodes.length > 1"
-                    >鍒犻櫎</el-button>
-                  </div>
-                </div>
-              </div>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="鐢宠浜猴細" prop="approveUser">
-							<el-select
-								v-model="form.approveUser"
-								placeholder="閫夋嫨浜哄憳"
-                filterable
-                default-first-option
-                :reserve-keyword="false"
-							>
-								<el-option
-									v-for="user in userList"
-									:key="user.userId"
-									:label="user.nickName"
-									:value="user.userId"
-								/>
-							</el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="鐢宠鏃ユ湡锛�" prop="approveTime">
-              <el-date-picker
-                  v-model="form.approveTime"
-                  type="date"
-                  placeholder="璇烽�夋嫨鏃ユ湡"
-                  value-format="YYYY-MM-DD"
-                  format="YYYY-MM-DD"
-                  clearable
-                  style="width: 100%"
-              />
-            </el-form-item>
-          </el-col>
-        </el-row>
+
         <el-row :gutter="30">
           <el-col :span="24">
             <el-form-item label="闄勪欢鏉愭枡锛�" prop="remark">
-              <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
-                         :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
-                         :on-success="handleUploadSuccess" :on-remove="handleRemove">
-                <el-button type="primary" v-if="operationType !== 'view'">涓婁紶</el-button>
-                <template #tip v-if="operationType !== 'view'">
-                  <div class="el-upload__tip">
-                    鏂囦欢鏍煎紡鏀寔
-                    doc锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�7z
-                  </div>
-                </template>
-              </el-upload>
+              <FileUpload v-model:file-list="fileList" />
             </el-form-item>
           </el-col>
         </el-row>
@@ -211,13 +126,11 @@
 import {
   delLedgerFile,
 } from "@/api/salesManagement/salesLedger.js";
-import {userListNoPageByTenantId} from "@/api/system/user.js";
 import { getToken } from "@/utils/auth";
 const { proxy } = getCurrentInstance()
 const emit = defineEmits(['close'])
 import useUserStore from "@/store/modules/user";
-import { getCurrentDate } from "@/utils/index.js";
-import log from "@/views/monitor/job/log.vue";
+import FileUpload from "@/components/AttachmentUpload/file/index.vue";
 const userStore = useUserStore();
 
 const dialogFormVisible = ref(false);
@@ -231,24 +144,18 @@
 });
 const data = reactive({
   form: {
-    approveTime: "",
     approveId: "",
-    approveUser: "",
-		approveDeptId: "",
+    approveDeptId: "",
     approveDeptName: "",
     approveReason: "",
     checkResult: "",
-    tempFileIds: [],
-    approverList: [], // 鏂板瀛楁锛屽瓨鍌ㄦ墍鏈夎妭鐐圭殑瀹℃壒浜篿d
     startDate: "", // 璇峰亣寮�濮嬫椂闂�
     endDate: "", // 璇峰亣缁撴潫鏃堕棿
     price: null, // 鎶ラ攢閲戦
     location: "" // 鍑哄樊鍦扮偣
   },
   rules: {
-    approveTime: [{ required: false, message: "璇疯緭鍏�", trigger: "change" },],
     approveId: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
-    approveUser: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
     approveDeptName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
     approveReason: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
     checkResult: [{ required: false, message: "璇疯緭鍏�", trigger: "blur" }],
@@ -268,18 +175,7 @@
   }
 })
 
-// 瀹℃壒浜鸿妭鐐圭浉鍏�
-const approverNodes = ref([
-  { id: 1, userId: null }
-])
-let nextApproverId = 2
-const userList = ref([])
-function addApproverNode() {
-  approverNodes.value.push({ id: nextApproverId++, userId: null })
-}
-function removeApproverNode(index) {
-  approverNodes.value.splice(index, 1)
-}
+
 // 澶勭悊閮ㄩ棬閫夋嫨鍙樺寲
 const handleDeptChange = (deptId) => {
   if (deptId) {
@@ -295,39 +191,20 @@
 const openDialog = (type, row) => {
   operationType.value = type;
   dialogFormVisible.value = true;
-	userListNoPageByTenantId().then((res) => {
-    userList.value = res.data;
-  });
-	form.value = {}
-	approverNodes.value = [
-		{ id: 1, userId: null }
-	]
-  form.value.approveUser = userStore.id;
-  form.value.approveTime = getCurrentDate();
-  
+  form.value = {}
+
   // 鑾峰彇褰撳墠鐢ㄦ埛淇℃伅骞惰缃儴闂↖D
   form.value.approveDeptId = userStore.currentDeptId
-  
+
   // 鍔犺浇閮ㄩ棬閫夐」锛屽苟鍦ㄥ姞杞藉畬鎴愬悗璁剧疆閮ㄩ棬鍚嶇О
   getProductOptions();
   if (operationType.value === 'edit') {
     fileList.value = row.commonFileList
     form.value.tempFileIds = fileList.value.map(file => file.id)
-		currentApproveStatus.value = row.approveStatus
+    currentApproveStatus.value = row.approveStatus
     approveProcessGetInfo({id: row.approveId,approveReason: '1'}).then(res => {
-			form.value = {...res.data}
-      // 鍙嶆樉瀹℃壒浜�
-      if (res.data && res.data.approveUserIds) {
-        const userIds = res.data.approveUserIds.split(',')
-        approverNodes.value = userIds.map((userId, idx) => ({
-          id: idx + 1, 
-          userId: parseInt(userId.trim())
-        }))
-        nextApproverId = userIds.length + 1
-      } else {
-        approverNodes.value = [{ id: 1, userId: null }]
-        nextApproverId = 2
-      }
+      form.value = {...res.data}
+      fileList.value = res.data.storageBlobVOS
     })
   }
 }
@@ -336,8 +213,8 @@
     productOptions.value = res.data;
     // 濡傛灉宸叉湁閮ㄩ棬ID锛岃嚜鍔ㄨ缃儴闂ㄥ悕绉帮紙鐢ㄤ簬楠岃瘉锛�
     if (form.value.approveDeptId && productOptions.value.length > 0) {
-      const matchedDept = productOptions.value.find(dept => 
-        dept.deptId == form.value.approveDeptId || 
+      const matchedDept = productOptions.value.find(dept =>
+        dept.deptId == form.value.approveDeptId ||
         String(dept.deptId) === String(form.value.approveDeptId)
       );
       if (matchedDept) {
@@ -356,21 +233,13 @@
     if (children && children.length > 0) {
       newItem.children = convertIdToValue(children);
     }
-    
+
     return newItem;
   });
 }
 // 鎻愪氦浜у搧琛ㄥ崟
 const submitForm = () => {
-  // 鏀堕泦鎵�鏈夎妭鐐圭殑瀹℃壒浜篿d
-  form.value.approveUserIds = approverNodes.value.map(node => node.userId).join(',')
   form.value.approveType = props.approveType
-  // 瀹℃壒浜哄繀濉牎楠�
-  const hasEmptyApprover = approverNodes.value.some(node => !node.userId)
-  if (hasEmptyApprover) {
-    proxy.$modal.msgError("璇蜂负鎵�鏈夊鎵硅妭鐐归�夋嫨瀹℃壒浜猴紒")
-    return
-  }
   // 褰� approveType 涓� 2 鏃讹紝鏍¢獙璇峰亣鏃堕棿
   if (props.approveType == 2) {
     if (!form.value.startDate) {
@@ -401,6 +270,8 @@
       return
     }
   }
+  form.value.storageBlobDTOList = fileList.value
+
   proxy.$refs.formRef.validate(valid => {
     if (valid) {
       if (operationType.value === "add" || currentApproveStatus.value == 3) {
@@ -424,47 +295,6 @@
   dialogFormVisible.value = false;
   emit('close')
 };
-
-// 涓婁紶鍓嶆牎妫�
-function handleBeforeUpload(file) {
-  // 鏍℃鏂囦欢澶у皬
-  // if (file.size > 1024 * 1024 * 10) {
-  //   proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃10MB!");
-  //   return false;
-  // }
-  proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
-  return true;
-}
-// 涓婁紶澶辫触
-function handleUploadError(err) {
-  proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
-  proxy.$modal.closeLoading();
-}
-// 涓婁紶鎴愬姛鍥炶皟
-function handleUploadSuccess(res, file, uploadFiles) {
-  proxy.$modal.closeLoading();
-  if (res.code === 200) {
-    // 纭繚 tempFileIds 瀛樺湪涓斾负鏁扮粍
-    if (!form.value.tempFileIds) {
-      form.value.tempFileIds = [];
-    }
-    form.value.tempFileIds.push(res.data.tempId);
-    proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
-  } else {
-    proxy.$modal.msgError(res.msg);
-    proxy.$refs.fileUpload.handleRemove(file);
-  }
-}
-// 绉婚櫎鏂囦欢
-function handleRemove(file) {
-  if (operationType.value === "edit") {
-    let ids = [];
-    ids.push(file.id);
-    delLedgerFile(ids).then((res) => {
-      proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-    });
-  }
-}
 
 defineExpose({
   openDialog,
diff --git a/src/views/collaborativeApproval/approvalProcess/fileList.vue b/src/views/collaborativeApproval/approvalProcess/fileList.vue
index 5cc65f1..498f474 100644
--- a/src/views/collaborativeApproval/approvalProcess/fileList.vue
+++ b/src/views/collaborativeApproval/approvalProcess/fileList.vue
@@ -4,9 +4,9 @@
       <el-table-column label="闄勪欢鍚嶇О" prop="name" min-width="400" show-overflow-tooltip />
       <el-table-column fixed="right" label="鎿嶄綔" width="150" align="center">
         <template #default="scope">
-          <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">涓嬭浇</el-button>
-          <el-button link type="primary" size="small" @click="lookFile(scope.row)">棰勮</el-button>
-          <el-button link type="danger" size="small" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+          <el-button link type="primary" @click="downLoadFile(scope.row)">涓嬭浇</el-button>
+          <el-button link type="primary" @click="lookFile(scope.row)">棰勮</el-button>
+          <el-button link type="danger" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
         </template>
       </el-table-column>
     </el-table>
diff --git a/src/views/collaborativeApproval/approvalProcess/index.vue b/src/views/collaborativeApproval/approvalProcess/index.vue
index 33bde47..dba6bc1 100644
--- a/src/views/collaborativeApproval/approvalProcess/index.vue
+++ b/src/views/collaborativeApproval/approvalProcess/index.vue
@@ -1,67 +1,129 @@
 <template>
   <div class="app-container">
-    <!-- 鏍囩椤靛垏鎹笉鍚岀殑瀹℃壒绫诲瀷 -->
-    <el-tabs v-model="activeTab" @tab-change="handleTabChange" class="approval-tabs">
-      <el-tab-pane label="鍏嚭绠$悊" name="1"></el-tab-pane>
-      <el-tab-pane label="璇峰亣绠$悊" name="2"></el-tab-pane>
-      <el-tab-pane label="鍑哄樊绠$悊" name="3"></el-tab-pane>
-      <el-tab-pane label="鎶ラ攢绠$悊" name="4"></el-tab-pane>
-      <el-tab-pane label="閲囪喘瀹℃壒" name="5"></el-tab-pane>
-      <el-tab-pane label="鎶ヤ环瀹℃壒" name="6"></el-tab-pane>
-      <el-tab-pane label="鍙戣揣瀹℃壒" name="7"></el-tab-pane>
-    </el-tabs>
-    
-    <div class="search_form">
-      <div>
-        <span class="search_title">娴佺▼缂栧彿锛�</span>
-        <el-input
-            v-model="searchForm.approveId"
-            style="width: 240px"
-            placeholder="璇疯緭鍏ユ祦绋嬬紪鍙锋悳绱�"
-            @change="handleQuery"
-            clearable
-            :prefix-icon="Search"
-        />
-        <span class="search_title ml10">瀹℃壒鐘舵�侊細</span>
-				<el-select v-model="searchForm.approveStatus" clearable @change="handleQuery" style="width: 240px">
-					<el-option label="寰呭鏍�" :value="0" />
-					<el-option label="瀹℃牳涓�" :value="1" />
-					<el-option label="瀹℃牳瀹屾垚" :value="2" />
-					<el-option label="瀹℃牳鏈�氳繃" :value="3" />
-					<el-option label="宸查噸鏂版彁浜�" :value="4" />
-				</el-select>
-        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
-        >鎼滅储</el-button
-        >
-      </div>
-      <div>
-        <el-button
-          type="primary"
-          @click="openForm('add')"
-          v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7"
-        >鏂板</el-button>
-        <el-button @click="handleOut">瀵煎嚭</el-button>
-        <el-button
-          type="danger"
-          plain
-          @click="handleDelete"
-          v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7"
-        >鍒犻櫎</el-button>
+    <!-- 瀹℃壒绫诲瀷鍒囨崲 - 绱у噾鏍囩寮� -->
+    <div class="type-tabs">
+      <div
+        v-for="type in approveTypes"
+        :key="type.value"
+        class="type-tab"
+        :class="{ active: activeTab === type.value }"
+        @click="activeTab = type.value; handleTabChange()"
+      >
+        <el-icon :size="14" :style="{ color: activeTab === type.value ? type.color : '#909399' }">
+          <component :is="type.icon" />
+        </el-icon>
+        <span class="tab-name">{{ type.label }}</span>
       </div>
     </div>
-    <div class="table_list">
+
+    <!-- 鎼滅储鍜屾搷浣滃尯鍩� -->
+    <el-card class="search-card" shadow="never">
+      <div class="search-content">
+        <div class="search-filters">
+          <div class="filter-item">
+            <span class="filter-label">娴佺▼缂栧彿</span>
+            <el-input
+              v-model="searchForm.approveId"
+              placeholder="璇疯緭鍏ユ祦绋嬬紪鍙�"
+              clearable
+              :prefix-icon="Search"
+              @keyup.enter="handleQuery"
+              class="search-input"
+            />
+          </div>
+          <div class="filter-item">
+            <span class="filter-label">瀹℃壒鐘舵��</span>
+            <el-select 
+              v-model="searchForm.approveStatus" 
+              clearable 
+              @change="handleQuery"
+              placeholder="璇烽�夋嫨鐘舵��"
+              class="search-select"
+            >
+              <el-option label="寰呭鏍�" :value="0">
+                <el-tag size="small" type="warning">寰呭鏍�</el-tag>
+              </el-option>
+              <el-option label="瀹℃牳涓�" :value="1">
+                <el-tag size="small" type="primary">瀹℃牳涓�</el-tag>
+              </el-option>
+              <el-option label="瀹℃牳瀹屾垚" :value="2">
+                <el-tag size="small" type="success">瀹℃牳瀹屾垚</el-tag>
+              </el-option>
+              <el-option label="瀹℃牳鏈�氳繃" :value="3">
+                <el-tag size="small" type="danger">瀹℃牳鏈�氳繃</el-tag>
+              </el-option>
+              <el-option label="宸查噸鏂版彁浜�" :value="4">
+                <el-tag size="small" type="info">宸查噸鏂版彁浜�</el-tag>
+              </el-option>
+            </el-select>
+          </div>
+          <el-button type="primary" @click="handleQuery" class="search-btn">
+            <el-icon><Search /></el-icon>
+            鎼滅储
+          </el-button>
+          <el-button @click="resetQuery" class="reset-btn">
+            <el-icon><RefreshRight /></el-icon>
+            閲嶇疆
+          </el-button>
+        </div>
+        <div class="search-actions">
+          <el-button
+            type="primary"
+            @click="openForm('add')"
+            v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7"
+            class="action-btn primary"
+          >
+            <el-icon><Plus /></el-icon>
+            鏂板
+          </el-button>
+          <el-button @click="handleOut" class="action-btn">
+            <el-icon><Download /></el-icon>
+            瀵煎嚭
+          </el-button>
+          <el-button
+            type="danger"
+            plain
+            @click="handleDelete"
+            v-if="currentApproveType !== 5 && currentApproveType !== 6 && currentApproveType !== 7"
+            class="action-btn danger"
+          >
+            <el-icon><Delete /></el-icon>
+            鍒犻櫎
+          </el-button>
+        </div>
+      </div>
+    </el-card>
+
+    <!-- 鏁版嵁琛ㄦ牸 -->
+    <el-card class="table-card" shadow="never" v-loading="tableLoading">
+      <template #header>
+        <div class="table-header">
+          <div class="table-title">
+            <div class="type-tag" :style="{ backgroundColor: currentTypeInfo.color }">
+              <el-icon color="#fff" :size="16"><component :is="currentTypeInfo.icon" /></el-icon>
+            </div>
+            <span>{{ currentTypeInfo.label }}鍒楄〃</span>
+            <el-tag type="info" size="small" effect="plain" class="count-tag">
+              鍏� {{ page.total }} 鏉�
+            </el-tag>
+          </div>
+        </div>
+      </template>
       <PIMTable
-          rowKey="id"
-          :column="tableColumnCopy"
-          :tableData="tableData"
-          :page="page"
-          :isSelection="true"
-          @selection-change="handleSelectionChange"
-          :tableLoading="tableLoading"
-          @pagination="pagination"
-          :total="page.total"
+        rowKey="id"
+        :column="tableColumnCopy"
+        :tableData="tableData"
+        :page="page"
+        :isSelection="true"
+        @selection-change="handleSelectionChange"
+        :tableLoading="tableLoading"
+        @pagination="pagination"
+        :total="page.total"
+        class="custom-table"
       ></PIMTable>
-    </div>
+    </el-card>
+
+    <!-- 寮圭獥缁勪欢 -->
     <info-form-dia ref="infoFormDia" @close="handleQuery" :approveType="currentApproveType"></info-form-dia>
     <approval-dia ref="approvalDia" @close="handleQuery" :approveType="currentApproveType"></approval-dia>
     <FileList ref="fileListRef" />
@@ -70,7 +132,7 @@
 
 <script setup>
 import FileList from "./fileList.vue";
-import { Search } from "@element-plus/icons-vue";
+import { Search, Plus, Delete, Download, RefreshRight, DocumentChecked } from "@element-plus/icons-vue";
 import {onMounted, ref, computed, reactive, toRefs, nextTick, getCurrentInstance} from "vue";
 import {ElMessageBox} from "element-plus";
 import { useRoute } from 'vue-router';
@@ -85,13 +147,37 @@
 // 褰撳墠閫変腑鐨勬爣绛鹃〉锛岄粯璁や负鍏嚭绠$悊
 const activeTab = ref('1');
 
+// 鍚勭被鍨嬫暟閲忕粺璁�
+const typeCounts = ref({});
+
+// 瀹℃壒绫诲瀷閰嶇疆
+const approveTypes = [
+  { value: '1', label: '鍏嚭绠$悊', icon: 'Suitcase', color: '#409EFF' },
+  { value: '2', label: '璇峰亣绠$悊', icon: 'Calendar', color: '#67C23A' },
+  { value: '3', label: '鍑哄樊绠$悊', icon: 'Location', color: '#E6A23C' },
+  { value: '4', label: '鎶ラ攢绠$悊', icon: 'Money', color: '#F56C6C' },
+  { value: '5', label: '閲囪喘瀹℃壒', icon: 'ShoppingCart', color: '#909399' },
+  { value: '6', label: '鎶ヤ环瀹℃壒', icon: 'DocumentChecked', color: '#9B59B6' },
+  { value: '7', label: '鍙戣揣瀹℃壒', icon: 'Van', color: '#1ABC9C' },
+];
+
+// 褰撳墠瀹℃壒绫诲瀷淇℃伅
+const currentTypeInfo = computed(() => {
+  return approveTypes.find(t => t.value === activeTab.value) || approveTypes[0];
+});
+
+// 鑾峰彇绫诲瀷鏁伴噺
+const getTypeCount = (value) => {
+  return typeCounts.value[value] || 0;
+};
+
 // 褰撳墠瀹℃壒绫诲瀷锛屾牴鎹�変腑鐨勬爣绛鹃〉璁$畻
 const currentApproveType = computed(() => {
   return Number(activeTab.value);
 });
 
 // 鏍囩椤靛垏鎹㈠鐞�
-const handleTabChange = (tabName) => {
+const handleTabChange = () => {
   // 鍒囨崲鏍囩椤垫椂閲嶇疆鎼滅储鏉′欢鍜屽垎椤碉紝骞堕噸鏂板姞杞芥暟鎹�
   searchForm.value.approveId = '';
   searchForm.value.approveStatus = '';
@@ -102,11 +188,18 @@
 
 const data = reactive({
   searchForm: {
-		approveId: "",
-		approveStatus: "",
+    approveId: "",
+    approveStatus: "",
   },
 });
 const { searchForm } = toRefs(data);
+
+// 閲嶇疆鎼滅储
+const resetQuery = () => {
+  searchForm.value.approveId = '';
+  searchForm.value.approveStatus = '';
+  handleQuery();
+};
 
 // 鍔ㄦ�佽〃鏍煎垪閰嶇疆锛屾牴鎹鎵圭被鍨嬬敓鎴愬垪
 const tableColumnCopy = computed(() => {
@@ -238,7 +331,7 @@
     },
   ];
 
-  // 鎶ヤ环瀹℃壒锛堢被鍨� 6锛変笉灞曠ず鈥滈檮浠垛�濇搷浣�
+  // 鎶ヤ环瀹℃壒锛堢被鍨� 6锛変笉灞曠ず"闄勪欢"鎿嶄綔
   if (!isQuotationType) {
     actionOperations.push({
       name: "闄勪欢",
@@ -294,6 +387,8 @@
     tableLoading.value = false;
     tableData.value = res.data.records
     page.total = res.data.total;
+    // 鏇存柊褰撳墠绫诲瀷鏁伴噺
+    typeCounts.value[activeTab.value] = res.data.total;
   }).catch(err => {
     tableLoading.value = false;
   })
@@ -388,7 +483,256 @@
 </script>
 
 <style scoped>
-.approval-tabs {
-  margin-bottom: 10px;
+.page-header {
+  margin-bottom: 20px;
+}
+
+.header-title {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+}
+
+.title-icon {
+  font-size: 28px;
+  color: var(--el-color-primary, #409EFF);
+}
+
+.header-text {
+  display: flex;
+  flex-direction: column;
+  gap: 4px;
+}
+
+.main-title {
+  font-size: 20px;
+  font-weight: 600;
+  color: var(--el-text-color-primary, #303133);
+}
+
+.sub-title {
+  font-size: 13px;
+  color: var(--el-text-color-secondary, #909399);
+}
+
+/* 瀹℃壒绫诲瀷鍒囨崲 - 绱у噾鏍囩寮� */
+.type-tabs {
+  display: flex;
+  gap: 4px;
+  margin-bottom: 16px;
+  padding: 4px;
+  background: var(--el-fill-color-light, #f5f7fa);
+  border-radius: 8px;
+  overflow-x: auto;
+}
+
+.type-tab {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  padding: 8px 14px;
+  border-radius: 6px;
+  cursor: pointer;
+  transition: all 0.2s ease;
+  white-space: nowrap;
+  font-size: 13px;
+  color: var(--el-text-color-regular, #606266);
+}
+
+.type-tab:hover {
+  background: var(--el-color-primary-light-9, rgba(64, 158, 255, 0.1));
+  color: var(--el-color-primary, #409EFF);
+}
+
+.type-tab.active {
+  background: var(--el-bg-color, #fff);
+  color: var(--el-color-primary, #409EFF);
+  font-weight: 600;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+}
+
+.tab-name {
+  font-size: 13px;
+}
+
+.tab-count {
+  min-width: 16px;
+  height: 16px;
+  padding: 0 5px;
+  background: var(--el-color-success, #67C23A);
+  color: #fff;
+  border-radius: 8px;
+  font-size: 11px;
+  font-weight: 600;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+/* 鎼滅储鍗$墖 */
+.search-card {
+  margin-bottom: 16px;
+  border-radius: 12px;
+}
+
+:deep(.el-card__body) {
+  padding: 20px;
+}
+
+.search-content {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  flex-wrap: wrap;
+  gap: 16px;
+}
+
+.search-filters {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+  flex-wrap: wrap;
+}
+
+.filter-item {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.filter-label {
+  font-size: 14px;
+  color: var(--el-text-color-regular, #606266);
+  font-weight: 500;
+  white-space: nowrap;
+}
+
+.search-input,
+.search-select {
+  width: 200px;
+}
+
+.search-btn {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+}
+
+.reset-btn {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+}
+
+.search-actions {
+  display: flex;
+  gap: 10px;
+}
+
+.action-btn {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+}
+
+.action-btn.primary {
+  background: var(--el-color-primary, #409EFF);
+  border: none;
+}
+
+.action-btn.danger {
+  transition: all 0.3s;
+}
+
+.action-btn.danger:hover {
+  background: #f56c6c;
+  color: #fff;
+}
+
+/* 琛ㄦ牸鍗$墖 */
+.table-card {
+  border-radius: 12px;
+}
+
+:deep(.el-card__header) {
+  padding: 16px 20px;
+  border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
+}
+
+.table-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.table-title {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  font-size: 16px;
+  font-weight: 600;
+  color: var(--el-text-color-primary, #303133);
+}
+
+.type-tag {
+  width: 32px;
+  height: 32px;
+  border-radius: 8px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.count-tag {
+  margin-left: 8px;
+}
+
+.custom-table {
+  margin-top: 8px;
+}
+
+/* 鍝嶅簲寮� */
+@media (max-width: 1200px) {
+  .search-content {
+    flex-direction: column;
+    align-items: stretch;
+  }
+
+  .search-filters {
+    justify-content: flex-start;
+  }
+
+  .search-actions {
+    justify-content: flex-end;
+  }
+}
+
+@media (max-width: 768px) {
+  .type-tabs {
+    padding: 3px;
+  }
+
+  .type-tab {
+    padding: 6px 10px;
+    font-size: 12px;
+  }
+
+  .tab-name {
+    font-size: 12px;
+  }
+
+  .search-filters {
+    flex-direction: column;
+    align-items: stretch;
+  }
+
+  .filter-item {
+    width: 100%;
+  }
+
+  .search-input,
+  .search-select {
+    width: 100%;
+  }
 }
 </style>
diff --git a/src/views/collaborativeApproval/attendanceManagement/index.vue b/src/views/collaborativeApproval/attendanceManagement/index.vue
index f6a3e3c..1f5f956 100644
--- a/src/views/collaborativeApproval/attendanceManagement/index.vue
+++ b/src/views/collaborativeApproval/attendanceManagement/index.vue
@@ -303,8 +303,8 @@
 
       <template #footer>
         <span class="dialog-footer">
-          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
           <el-button type="primary" @click="submitForm">纭畾</el-button>
+          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
         </span>
       </template>
     </el-dialog>
diff --git a/src/views/collaborativeApproval/customerVisit/index.vue b/src/views/collaborativeApproval/customerVisit/index.vue
index 3122f3f..68396eb 100644
--- a/src/views/collaborativeApproval/customerVisit/index.vue
+++ b/src/views/collaborativeApproval/customerVisit/index.vue
@@ -45,7 +45,7 @@
         <el-table-column label="鎷滆浜�" prop="visitingPeople" width="120" show-overflow-tooltip />
         <el-table-column fixed="right" label="鎿嶄綔" width="100" align="center">
           <template #default="scope">
-            <el-button link type="primary" size="small" @click="viewDetail(scope.row)">鏌ョ湅</el-button>
+            <el-button link type="primary" size="small" @click="viewDetail(scope.row)" style="color: #67C23A">鏌ョ湅</el-button>
           </template>
         </el-table-column>
       </el-table>
diff --git a/src/views/collaborativeApproval/enterpriseBook/index.vue b/src/views/collaborativeApproval/enterpriseBook/index.vue
index fa9fa5c..8239811 100644
--- a/src/views/collaborativeApproval/enterpriseBook/index.vue
+++ b/src/views/collaborativeApproval/enterpriseBook/index.vue
@@ -275,8 +275,8 @@
         </el-form-item>
       </el-form>
       <template #footer>
-        <el-button @click="showAddContactDialog = false">鍙栨秷</el-button>
         <el-button type="primary" @click="addContact">纭畾</el-button>
+        <el-button @click="showAddContactDialog = false">鍙栨秷</el-button>
       </template>
     </el-dialog>
   </div>
diff --git a/src/views/collaborativeApproval/knowledgeBase/index.vue b/src/views/collaborativeApproval/knowledgeBase/index.vue
index bdaf0b4..43fee33 100644
--- a/src/views/collaborativeApproval/knowledgeBase/index.vue
+++ b/src/views/collaborativeApproval/knowledgeBase/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form" style="margin-bottom: 20px;">
       <div>
         <span class="search_title">鐭ヨ瘑鏍囬锛�</span>
         <el-input
@@ -382,7 +382,7 @@
         }
       },
       {
-        name: "鏌ョ湅",
+        name: "璇︽儏",
         type: "text",
         clickFun: (row) => {
           viewKnowledge(row);
diff --git a/src/views/collaborativeApproval/meetingBoard/index.vue b/src/views/collaborativeApproval/meetingBoard/index.vue
index dfbb922..ebedd1f 100644
--- a/src/views/collaborativeApproval/meetingBoard/index.vue
+++ b/src/views/collaborativeApproval/meetingBoard/index.vue
@@ -83,9 +83,7 @@
 
 <script setup>
 import { ref, reactive, onMounted } from 'vue'
-import { ElMessage } from 'element-plus'
 import { Clock, Location, User, UserFilled } from '@element-plus/icons-vue'
-import Editor from "@/components/Editor/index.vue";
 import {getMeetSummaryItems,getMeetSummary} from '@/api/collaborativeApproval/meeting.js'
 import dayjs from "dayjs";
 
diff --git a/src/views/collaborativeApproval/noticeManagement/index.vue b/src/views/collaborativeApproval/noticeManagement/index.vue
index 0957882..a36b950 100644
--- a/src/views/collaborativeApproval/noticeManagement/index.vue
+++ b/src/views/collaborativeApproval/noticeManagement/index.vue
@@ -210,7 +210,6 @@
                   v-if="scope.row.editing"
                   link
                   type="primary"
-                  size="small"
                   @click="handleSaveNoticeType(scope.row)"
               >
                 淇濆瓨
@@ -219,7 +218,6 @@
                   v-if="scope.row.editing"
                   link
                   type="info"
-                  size="small"
                   @click="handleCancelEdit(scope.row)"
               >
                 鍙栨秷
@@ -228,7 +226,6 @@
                   v-if="!scope.row.editing"
                   link
                   type="primary"
-                  size="small"
                   @click="handleEditNoticeType(scope.row)"
               >
                 缂栬緫
@@ -237,7 +234,6 @@
                   v-if="!scope.row.editing"
                   link
                   type="danger"
-                  size="small"
                   @click="handleDeleteNoticeType(scope.row)"
               >
                 鍒犻櫎
@@ -933,7 +929,7 @@
 }
 
 .dialog-footer {
-  text-align: right;
+  text-align: center;
 }
 
 .notice-type-container {
diff --git a/src/views/collaborativeApproval/notificationManagement/index.vue b/src/views/collaborativeApproval/notificationManagement/index.vue
index fb3f7a8..fa02f47 100644
--- a/src/views/collaborativeApproval/notificationManagement/index.vue
+++ b/src/views/collaborativeApproval/notificationManagement/index.vue
@@ -131,8 +131,8 @@
       </el-form>
       <template #footer>
         <span class="dialog-footer">
-          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
           <el-button type="primary" @click="submitForm">纭畾</el-button>
+          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
         </span>
       </template>
     </el-dialog>
diff --git a/src/views/collaborativeApproval/notificationManagement/meetSetting/index.vue b/src/views/collaborativeApproval/notificationManagement/meetSetting/index.vue
index ad4931a..636fd79 100644
--- a/src/views/collaborativeApproval/notificationManagement/meetSetting/index.vue
+++ b/src/views/collaborativeApproval/notificationManagement/meetSetting/index.vue
@@ -93,8 +93,8 @@
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button @click="cancel">鍙� 娑�</el-button>
           <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
         </div>
       </template>
     </el-dialog>
@@ -313,8 +313,6 @@
 }
 
 .dialog-footer {
-  display: flex;
-  justify-content: flex-end;
-  gap: 10px;
+	text-align: center;
 }
 </style>
diff --git a/src/views/collaborativeApproval/notificationManagement/summary/index.vue b/src/views/collaborativeApproval/notificationManagement/summary/index.vue
index 2b26723..bf6e230 100644
--- a/src/views/collaborativeApproval/notificationManagement/summary/index.vue
+++ b/src/views/collaborativeApproval/notificationManagement/summary/index.vue
@@ -146,8 +146,8 @@
 
       <template #footer>
         <div class="dialog-footer">
-          <el-button @click="minutesDialogVisible = false">鍙� 娑�</el-button>
           <el-button type="primary" @click="submitMinutes">淇� 瀛�</el-button>
+          <el-button @click="minutesDialogVisible = false">鍙� 娑�</el-button>
         </div>
       </template>
     </el-dialog>
@@ -367,9 +367,7 @@
 }
 
 .dialog-footer {
-  display: flex;
-  justify-content: flex-end;
-  gap: 10px;
+  text-align: center;
 }
 
 .content-section h4 {
diff --git a/src/views/collaborativeApproval/planTemplate/index.vue b/src/views/collaborativeApproval/planTemplate/index.vue
index 0af6d8b..fa2bfd9 100644
--- a/src/views/collaborativeApproval/planTemplate/index.vue
+++ b/src/views/collaborativeApproval/planTemplate/index.vue
@@ -123,7 +123,7 @@
               </div>
               <div class="plan-actions">
                 <el-button size="small" @click="handleEditPlan(plan)">缂栬緫</el-button>
-                <el-button size="small" @click="handleViewDetail(plan)">璇︽儏</el-button>
+                <el-button size="small" @click="handleViewDetail(plan)" style="color: #67C23A">璇︽儏</el-button>
                 <el-dropdown @command="(command) => handleMoreAction(plan, command)">
                   <el-button size="small">
                     鏇村<el-icon class="el-icon--right"><ArrowDown /></el-icon>
diff --git a/src/views/collaborativeApproval/processTracking/index.vue b/src/views/collaborativeApproval/processTracking/index.vue
index 67f5106..197a438 100644
--- a/src/views/collaborativeApproval/processTracking/index.vue
+++ b/src/views/collaborativeApproval/processTracking/index.vue
@@ -59,7 +59,7 @@
           <el-table-column label="鎿嶄綔" width="150">
             <template #default="{ row }">
               <el-button type="text" @click="updateStatus(row)">鏇存柊鐘舵��</el-button>
-              <el-button type="text" @click="viewDetails(row)">璇︽儏</el-button>
+              <el-button type="text" @click="viewDetails(row)" style="color: #67C23A">璇︽儏</el-button>
             </template>
           </el-table-column>
         </el-table>
diff --git a/src/views/collaborativeApproval/purchaseApproval/index.vue b/src/views/collaborativeApproval/purchaseApproval/index.vue
index af17bbe..fe90686 100644
--- a/src/views/collaborativeApproval/purchaseApproval/index.vue
+++ b/src/views/collaborativeApproval/purchaseApproval/index.vue
@@ -2,678 +2,771 @@
   <div class="app-container">
     <div class="search_form">
       <div>
-        <el-form :model="searchForm" :inline="true">
+        <el-form :model="searchForm"
+                 :inline="true">
           <el-form-item label="渚涘簲鍟嗗悕绉帮細">
-            <el-input v-model="searchForm.supplierName" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
+            <el-input v-model="searchForm.supplierName"
+                      placeholder="璇疯緭鍏�"
+                      clearable
+                      prefix-icon="Search"
                       @change="handleQuery" />
           </el-form-item>
           <el-form-item label="閲囪喘鍚堝悓鍙凤細">
-            <el-input
-                v-model="searchForm.purchaseContractNumber"
-                style="width: 240px"
-                placeholder="璇疯緭鍏�"
-                @change="handleQuery"
-                clearable
-                :prefix-icon="Search"
-            />
+            <el-input v-model="searchForm.purchaseContractNumber"
+                      style="width: 240px"
+                      placeholder="璇疯緭鍏�"
+                      @change="handleQuery"
+                      clearable
+                      :prefix-icon="Search" />
           </el-form-item>
           <el-form-item label="閿�鍞悎鍚屽彿锛�">
-            <el-input v-model="searchForm.salesContractNo" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
+            <el-input v-model="searchForm.salesContractNo"
+                      placeholder="璇疯緭鍏�"
+                      clearable
+                      prefix-icon="Search"
                       @change="handleQuery" />
           </el-form-item>
           <el-form-item label="椤圭洰鍚嶇О锛�">
-            <el-input v-model="searchForm.projectName" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
+            <el-input v-model="searchForm.projectName"
+                      placeholder="璇疯緭鍏�"
+                      clearable
+                      prefix-icon="Search"
                       @change="handleQuery" />
           </el-form-item>
           <el-form-item>
-            <el-button type="primary" @click="handleQuery"> 鎼滅储 </el-button>
+            <el-button type="primary"
+                       @click="handleQuery"> 鎼滅储 </el-button>
           </el-form-item>
         </el-form>
       </div>
-
     </div>
     <div class="table_list">
       <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
         <el-button @click="handleOut">瀵煎嚭</el-button>
-        <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+        <el-button type="danger"
+                   plain
+                   @click="handleDelete">鍒犻櫎</el-button>
       </div>
-      <el-table
-        :data="tableData"
-        border
-        v-loading="tableLoading"
-        @selection-change="handleSelectionChange"
-        :expand-row-keys="expandedRowKeys"
-        :row-key="(row) => row.id"
-        show-summary
-        :summary-method="summarizeMainTable"
-        @expand-change="expandChange"
-        height="calc(100vh - 18.5em)"
-        :row-class-name="tableRowClassName"
-      >
-        <el-table-column align="center" type="selection" width="55" />
+      <el-table :data="tableData"
+                border
+                v-loading="tableLoading"
+                @selection-change="handleSelectionChange"
+                :expand-row-keys="expandedRowKeys"
+                :row-key="(row) => row.id"
+                show-summary
+                :summary-method="summarizeMainTable"
+                @expand-change="expandChange"
+                height="calc(100vh - 18.5em)"
+                :row-class-name="tableRowClassName">
+        <el-table-column align="center"
+                         type="selection"
+                         width="55" />
         <el-table-column type="expand">
           <template #default="props">
-            <el-table
-              :data="props.row.children"
-              border
-              show-summary
-              :summary-method="summarizeChildrenTable"
-            >
-              <el-table-column
-                align="center"
-                label="搴忓彿"
-                type="index"
-                width="60"
-              />
-              <el-table-column label="浜у搧澶х被" prop="productCategory" />
-              <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
-              <el-table-column label="鍗曚綅" prop="unit" />
-              <el-table-column label="鏁伴噺" prop="quantity" />
-              <el-table-column label="绋庣巼(%)" prop="taxRate" />
-              <el-table-column
-                label="鍚◣鍗曚环(鍏�)"
-                prop="taxInclusiveUnitPrice"
-                :formatter="formattedNumber"
-              />
-              <el-table-column
-                label="鍚◣鎬讳环(鍏�)"
-                prop="taxInclusiveTotalPrice"
-                :formatter="formattedNumber"
-              />
-              <el-table-column
-                label="涓嶅惈绋庢�讳环(鍏�)"
-                prop="taxExclusiveTotalPrice"
-                :formatter="formattedNumber"
-              />
+            <el-table :data="props.row.children"
+                      border
+                      show-summary
+                      :summary-method="summarizeChildrenTable">
+              <el-table-column align="center"
+                               label="搴忓彿"
+                               type="index"
+                               width="60" />
+              <el-table-column label="浜у搧澶х被"
+                               prop="productCategory" />
+              <el-table-column label="瑙勬牸鍨嬪彿"
+                               prop="specificationModel" />
+              <el-table-column label="鍗曚綅"
+                               prop="unit" />
+              <el-table-column label="鏁伴噺"
+                               prop="quantity" />
+              <el-table-column label="绋庣巼(%)"
+                               prop="taxRate" />
+              <el-table-column label="鍚◣鍗曚环(鍏�)"
+                               prop="taxInclusiveUnitPrice"
+                               :formatter="formattedNumber" />
+              <el-table-column label="鍚◣鎬讳环(鍏�)"
+                               prop="taxInclusiveTotalPrice"
+                               :formatter="formattedNumber" />
+              <el-table-column label="涓嶅惈绋庢�讳环(鍏�)"
+                               prop="taxExclusiveTotalPrice"
+                               :formatter="formattedNumber" />
             </el-table>
           </template>
         </el-table-column>
-        <el-table-column align="center" label="搴忓彿" type="index" width="60" />
-        <el-table-column
-          label="閲囪喘鍚堝悓鍙�"
-          prop="purchaseContractNumber"
-          width="200"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="閿�鍞悎鍚屽彿"
-          prop="salesContractNo"
-          width="200"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="渚涘簲鍟嗗悕绉�"
-          width="240"
-          prop="supplierName"
-          show-overflow-tooltip
-        />
-        <el-table-column label="璁㈠崟鐘舵��" width="100" align="center">
+        <el-table-column align="center"
+                         label="搴忓彿"
+                         type="index"
+                         width="60" />
+        <el-table-column label="閲囪喘鍚堝悓鍙�"
+                         prop="purchaseContractNumber"
+                         width="200"
+                         show-overflow-tooltip />
+        <el-table-column label="閿�鍞悎鍚屽彿"
+                         prop="salesContractNo"
+                         width="200"
+                         show-overflow-tooltip />
+        <el-table-column label="渚涘簲鍟嗗悕绉�"
+                         width="240"
+                         prop="supplierName"
+                         show-overflow-tooltip />
+        <el-table-column label="璁㈠崟鐘舵��"
+                         width="100"
+                         align="center">
           <template #default="scope">
-            <el-tag v-if="scope.row.isInvalid" type="danger" size="small">澶辨晥</el-tag>
-            <el-tag v-else type="success" size="small">姝e父</el-tag>
+            <el-tag v-if="scope.row.isInvalid"
+                    type="danger"
+                    size="small">澶辨晥</el-tag>
+            <el-tag v-else
+                    type="success"
+                    size="small">姝e父</el-tag>
           </template>
         </el-table-column>
-        <el-table-column
-          label="椤圭洰鍚嶇О"
-          prop="projectName"
-          width="420"
-          show-overflow-tooltip
-        />
-        <el-table-column
-            label="瀹℃壒鐘舵��"
-            prop="approvalStatus"
-            width="200"
-            show-overflow-tooltip
-        >
+        <el-table-column label="椤圭洰鍚嶇О"
+                         prop="projectName"
+                         width="420"
+                         show-overflow-tooltip />
+        <el-table-column label="瀹℃壒鐘舵��"
+                         prop="approvalStatus"
+                         width="200"
+                         show-overflow-tooltip>
           <template #default="scope">
-            <el-tag
-                size="small"
-            >
+            <el-tag size="small">
               {{ approvalStatusText[scope.row.approvalStatus] || '鏈煡鐘舵��' }}
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column
-          label="浠樻鏂瑰紡"
-          width="100"
-          prop="paymentMethod"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="鍚堝悓閲戦(鍏�)"
-          prop="contractAmount"
-           width="200"
-          show-overflow-tooltip
-          :formatter="formattedNumber"
-        />
-        <el-table-column
-          label="褰曞叆浜�"
-          prop="recorderName"
-           width="100"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="褰曞叆鏃ユ湡"
-          prop="entryDate"
-           width="100"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          fixed="right"
-          label="鎿嶄綔"
-          min-width="150"
-          align="center"
-        >
+        <el-table-column label="浠樻鏂瑰紡"
+                         width="100"
+                         prop="paymentMethod"
+                         show-overflow-tooltip />
+        <el-table-column label="鍚堝悓閲戦(鍏�)"
+                         prop="contractAmount"
+                         width="200"
+                         show-overflow-tooltip
+                         :formatter="formattedNumber" />
+        <el-table-column label="褰曞叆浜�"
+                         prop="recorderName"
+                         width="100"
+                         show-overflow-tooltip />
+        <el-table-column label="褰曞叆鏃ユ湡"
+                         prop="entryDate"
+                         width="100"
+                         show-overflow-tooltip />
+        <el-table-column fixed="right"
+                         label="鎿嶄綔"
+                         min-width="150"
+                         align="center">
           <template #default="scope">
-            <el-button
-              link
-              type="primary"
-              size="small"
-              @click="approvePurchase(scope.row)"
-              :disabled="scope.row.approvalStatus !== 0"
-              >瀹℃壒</el-button
-            >
-            <el-button
-                link
-                type="primary"
-                size="small"
-                @click="rejectPurchase(scope.row)"
-                :disabled="scope.row.approvalStatus !== 0"
-            >鎷掔粷瀹℃壒</el-button
-            >
+            <el-button link
+                       type="primary"
+                       size="small"
+                       @click="approvePurchase(scope.row)"
+                       :disabled="scope.row.approvalStatus !== 0">瀹℃壒</el-button>
+            <el-button link
+                       type="primary"
+                       size="small"
+                       @click="rejectPurchase(scope.row)"
+                       :disabled="scope.row.approvalStatus !== 0">鎷掔粷瀹℃壒</el-button>
           </template>
         </el-table-column>
       </el-table>
-      <pagination
-        v-show="total > 0"
-        :total="total"
-        layout="total, sizes, prev, pager, next, jumper"
-        :page="page.current"
-        :limit="page.size"
-        @pagination="paginationChange"
-      />
+      <pagination v-show="total > 0"
+                  :total="total"
+                  layout="total, sizes, prev, pager, next, jumper"
+                  :page="page.current"
+                  :limit="page.size"
+                  @pagination="paginationChange" />
     </div>
   </div>
 </template>
 
 <script setup>
-import { getToken } from "@/utils/auth";
-import pagination from "@/components/PIMTable/Pagination.vue";
-import { ref, onMounted, reactive, toRefs, getCurrentInstance, nextTick } from "vue";
-import { Search } from "@element-plus/icons-vue";
-import { ElMessageBox } from "element-plus";
-import { userListNoPage } from "@/api/system/user.js";
-import {
-  getSalesLedgerWithProducts,
-  addOrUpdateSalesLedgerProduct,
-  delProduct,
-  delLedgerFile,
-  getProductInfoByContractNo,
-} from "@/api/salesManagement/salesLedger.js";
-import {
-  addOrEditPurchase,
-  delPurchase,
-  getSalesNo,
-  purchaseListPage,
-  productList,
-  getPurchaseById,
-  getOptions,
-  createPurchaseNo, updateApprovalStatus,
-} from "@/api/procurementManagement/procurementLedger.js";
-import useFormData from "@/hooks/useFormData.js";
-import QRCode from "qrcode";
+  import { getToken } from "@/utils/auth";
+  import pagination from "@/components/PIMTable/Pagination.vue";
+  import {
+    ref,
+    onMounted,
+    reactive,
+    toRefs,
+    getCurrentInstance,
+    nextTick,
+  } from "vue";
+  import { Search } from "@element-plus/icons-vue";
+  import { ElMessageBox } from "element-plus";
+  import { userListNoPage } from "@/api/system/user.js";
+  import {
+    getSalesLedgerWithProducts,
+    addOrUpdateSalesLedgerProduct,
+    delProduct,
+    delLedgerFile,
+    getProductInfoByContractNo,
+  } from "@/api/salesManagement/salesLedger.js";
+  import {
+    addOrEditPurchase,
+    delPurchase,
+    getSalesNo,
+    purchaseListPage,
+    productList,
+    getPurchaseById,
+    getOptions,
+    createPurchaseNo,
+    updateApprovalStatus,
+  } from "@/api/procurementManagement/procurementLedger.js";
+  import useFormData from "@/hooks/useFormData.js";
+  import QRCode from "qrcode";
 
+  const { proxy } = getCurrentInstance();
+  const tableData = ref([]);
+  const productData = ref([]);
+  const selectedRows = ref([]);
+  const productSelectedRows = ref([]);
+  const modelOptions = ref([]);
+  const userList = ref([]);
+  const productOptions = ref([]);
+  const salesContractList = ref([]);
+  const supplierList = ref([]);
+  const tableLoading = ref(false);
+  const page = reactive({
+    current: 1,
+    size: 100,
+  });
+  const total = ref(0);
+  const fileList = ref([]);
+  import useUserStore from "@/store/modules/user";
+  import { modelList, productTreeList } from "@/api/basicData/product.js";
+  import dayjs from "dayjs";
+  import { getCurrentDate } from "@/utils/index.js";
 
-const { proxy } = getCurrentInstance();
-const tableData = ref([]);
-const productData = ref([]);
-const selectedRows = ref([]);
-const productSelectedRows = ref([]);
-const modelOptions = ref([]);
-const userList = ref([]);
-const productOptions = ref([]);
-const salesContractList = ref([]);
-const supplierList = ref([]);
-const tableLoading = ref(false);
-const page = reactive({
-  current: 1,
-  size: 100,
-});
-const total = ref(0);
-const fileList = ref([]);
-import useUserStore from "@/store/modules/user";
-import { modelList, productTreeList } from "@/api/basicData/product.js";
-import dayjs from "dayjs";
-import { getCurrentDate } from "@/utils/index.js";
+  const userStore = useUserStore();
 
-const userStore = useUserStore();
+  // 浜岀淮鐮佺浉鍏冲彉閲�
+  const qrCodeDialogVisible = ref(false);
+  const qrCodeUrl = ref("");
 
-// 浜岀淮鐮佺浉鍏冲彉閲�
-const qrCodeDialogVisible = ref(false);
-const qrCodeUrl = ref("");
+  // 璁㈠崟瀹℃壒鐘舵�佹樉绀烘枃鏈�
+  const approvalStatusText = {
+    0: "寰呭鎵�",
+    1: "瀹℃壒閫氳繃",
+    2: "瀹℃壒澶辫触",
+  };
 
-// 璁㈠崟瀹℃壒鐘舵�佹樉绀烘枃鏈�
-const approvalStatusText = {
-  0: '寰呭鎵�',
-  1: '瀹℃壒閫氳繃',
-  2: '瀹℃壒澶辫触'
-};
+  // 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
+  const operationType = ref("");
+  const dialogFormVisible = ref(false);
+  const data = reactive({
+    searchForm: {
+      supplierName: "", // 渚涘簲鍟嗗悕绉�
+      purchaseContractNumber: "", // 閲囪喘鍚堝悓缂栧彿
+      salesContractNo: "", // 閿�鍞悎鍚岀紪鍙�
+      projectName: "", // 椤圭洰鍚嶇О
+      entryDate: null, // 褰曞叆鏃ユ湡
+      entryDateStart: undefined,
+      entryDateEnd: undefined,
+    },
+    form: {
+      purchaseContractNumber: "",
+      salesLedgerId: "",
+      projectName: "",
+      recorderId: "",
+      entryDate: "",
+      productData: [],
+      supplierName: "",
+      supplierId: "",
+      paymentMethod: "",
+      executionDate: "",
+      approvalStatus: "0",
+    },
+    rules: {
+      purchaseContractNumber: [
+        { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+      ],
+      projectName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      supplierId: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      entryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+      executionDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+    },
+  });
+  const { form, rules } = toRefs(data);
+  const { form: searchForm } = useFormData(data.searchForm);
 
-// 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
-const operationType = ref("");
-const dialogFormVisible = ref(false);
-const data = reactive({
-  searchForm: {
-    supplierName: "", // 渚涘簲鍟嗗悕绉�
-    purchaseContractNumber: "", // 閲囪喘鍚堝悓缂栧彿
-    salesContractNo: "", // 閿�鍞悎鍚岀紪鍙�
-    projectName: "", // 椤圭洰鍚嶇О
-    entryDate: null, // 褰曞叆鏃ユ湡
-    entryDateStart: undefined,
-    entryDateEnd: undefined,
-  },
-  form: {
-    purchaseContractNumber: "",
-    salesLedgerId: "",
-    projectName: "",
-    recorderId: "",
-    entryDate: "",
-    productData: [],
-    supplierName: "",
-    supplierId: "",
-    paymentMethod: "",
-		executionDate: "",
-    approvalStatus: "0",
-  },
-  rules: {
-    purchaseContractNumber: [
-      { required: true, message: "璇疯緭鍏�", trigger: "blur" },
-    ],
-    projectName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-    supplierId: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		entryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-		executionDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-  },
-});
-const {  form, rules } = toRefs(data);
-const { form: searchForm } = useFormData(data.searchForm);
+  // 浜у搧琛ㄥ崟寮规鏁版嵁
+  const productFormVisible = ref(false);
+  const productOperationType = ref("");
+  const productOperationIndex = ref("");
+  const currentId = ref("");
+  const productFormData = reactive({
+    productForm: {
+      productId: "",
+      productCategory: "",
+      productModelId: "",
+      specificationModel: "",
+      unit: "",
+      quantity: "",
+      taxInclusiveUnitPrice: "",
+      taxRate: "",
+      taxInclusiveTotalPrice: "",
+      taxExclusiveTotalPrice: "",
+      invoiceType: "",
+      warnNum: "",
+    },
+    productRules: {
+      productId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+      productModelId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+      unit: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      quantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      taxInclusiveUnitPrice: [
+        { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+      ],
+      taxRate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+      warnNum: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
+      taxInclusiveTotalPrice: [
+        { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+      ],
+      taxExclusiveTotalPrice: [
+        { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+      ],
+      invoiceType: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+    },
+  });
+  const { productForm, productRules } = toRefs(productFormData);
+  // const upload = reactive({
+  //   // 涓婁紶鐨勫湴鍧�
+  //   url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
+  //   // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+  //   headers: { Authorization: "Bearer " + getToken() },
+  // });
 
-// 浜у搧琛ㄥ崟寮规鏁版嵁
-const productFormVisible = ref(false);
-const productOperationType = ref("");
-const productOperationIndex = ref("");
-const currentId = ref("");
-const productFormData = reactive({
-  productForm: {
-    productId: "",
-    productCategory: "",
-    productModelId: "",
-    specificationModel: "",
-    unit: "",
-    quantity: "",
-    taxInclusiveUnitPrice: "",
-    taxRate: "",
-    taxInclusiveTotalPrice: "",
-    taxExclusiveTotalPrice: "",
-    invoiceType: "",
-		warnNum: "",
-  },
-  productRules: {
-    productId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-    productModelId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-    unit: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-    quantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-    taxInclusiveUnitPrice: [
-      { required: true, message: "璇疯緭鍏�", trigger: "blur" },
-    ],
-    taxRate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-		warnNum: [{ required: false, message: "璇烽�夋嫨", trigger: "change" }],
-    taxInclusiveTotalPrice: [
-      { required: true, message: "璇疯緭鍏�", trigger: "blur" },
-    ],
-    taxExclusiveTotalPrice: [
-      { required: true, message: "璇疯緭鍏�", trigger: "blur" },
-    ],
-    invoiceType: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-  },
-});
-const { productForm, productRules } = toRefs(productFormData);
-const upload = reactive({
-  // 涓婁紶鐨勫湴鍧�
-  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
-  // 璁剧疆涓婁紶鐨勮姹傚ご閮�
-  headers: { Authorization: "Bearer " + getToken() },
-});
+  const changeDaterange = value => {
+    if (value) {
+      searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
+      searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
+    } else {
+      searchForm.entryDateStart = undefined;
+      searchForm.entryDateEnd = undefined;
+    }
+    handleQuery();
+  };
 
-const changeDaterange = (value) => {
-  if (value) {
-    searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
-    searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
-  } else {
-    searchForm.entryDateStart = undefined;
-    searchForm.entryDateEnd = undefined;
-  }
-  handleQuery();
-};
-
-const formattedNumber = (row, column, cellValue) => {
-  return parseFloat(cellValue).toFixed(2);
-};
-// 鏌ヨ鍒楄〃
-/** 鎼滅储鎸夐挳鎿嶄綔 */
-const handleQuery = () => {
-  page.current = 1;
-  getList();
-};
-// 瀛愯〃鍚堣鏂规硶
-const summarizeChildrenTable = (param) => {
-  return proxy.summarizeTable(
-    param,
-    [
+  const formattedNumber = (row, column, cellValue) => {
+    return parseFloat(cellValue).toFixed(2);
+  };
+  // 鏌ヨ鍒楄〃
+  /** 鎼滅储鎸夐挳鎿嶄綔 */
+  const handleQuery = () => {
+    page.current = 1;
+    getList();
+  };
+  // 瀛愯〃鍚堣鏂规硶
+  const summarizeChildrenTable = param => {
+    return proxy.summarizeTable(
+      param,
+      [
+        "taxInclusiveUnitPrice",
+        "taxInclusiveTotalPrice",
+        "taxExclusiveTotalPrice",
+        "ticketsNum",
+        "ticketsAmount",
+        "futureTickets",
+        "futureTicketsAmount",
+      ],
+      {
+        ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+        futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+      }
+    );
+  };
+  const paginationChange = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    getList();
+  };
+  const getList = () => {
+    tableLoading.value = true;
+    const { entryDate, ...rest } = searchForm;
+    purchaseListPage({ ...rest, ...page })
+      .then(res => {
+        tableLoading.value = false;
+        // tableData.value = res.data.records;
+        // 澶勭悊鏁版嵁锛屾坊鍔犲け鏁堢姸鎬佹爣璁�
+        tableData.value = res.data.records.map(record => ({
+          ...record,
+          isInvalid: record.isWhite === 1,
+        }));
+        tableData.value.map(item => {
+          item.children = [];
+        });
+        total.value = res.data.total;
+        expandedRowKeys.value = [];
+      })
+      .catch(() => {
+        tableLoading.value = false;
+      });
+  };
+  // 琛ㄦ牸閫夋嫨鏁版嵁
+  const handleSelectionChange = selection => {
+    selectedRows.value = selection;
+  };
+  const productSelected = selectedRows => {
+    productSelectedRows.value = selectedRows;
+  };
+  const expandedRowKeys = ref([]);
+  // 灞曞紑琛�
+  const expandChange = (row, expandedRows) => {
+    if (expandedRows.length > 0) {
+      expandedRowKeys.value = [];
+      try {
+        productList({ salesLedgerId: row.id, type: 2 }).then(res => {
+          const index = tableData.value.findIndex(item => item.id === row.id);
+          if (index > -1) {
+            tableData.value[index].children = res.data;
+          }
+          expandedRowKeys.value.push(row.id);
+        });
+      } catch (error) {
+        console.log(error);
+      }
+    } else {
+      expandedRowKeys.value = [];
+    }
+  };
+  // 涓昏〃鍚堣鏂规硶
+  const summarizeMainTable = param => {
+    return proxy.summarizeTable(param, ["contractAmount"]);
+  };
+  // 瀛愯〃鍚堣鏂规硶
+  const summarizeProTable = param => {
+    return proxy.summarizeTable(param, [
       "taxInclusiveUnitPrice",
       "taxInclusiveTotalPrice",
       "taxExclusiveTotalPrice",
-      "ticketsNum",
-      "ticketsAmount",
-      "futureTickets",
-      "futureTicketsAmount",
-    ],
-    {
-      ticketsNum: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
-      futureTickets: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
-    }
-  );
-};
-const paginationChange = (obj) => {
-  page.current = obj.page;
-  page.size = obj.limit;
-  getList();
-};
-const getList = () => {
-  tableLoading.value = true;
-  const { entryDate, ...rest } = searchForm;
-  purchaseListPage({ ...rest, ...page })
-    .then((res) => {
-      tableLoading.value = false;
-      // tableData.value = res.data.records;
-      // 澶勭悊鏁版嵁锛屾坊鍔犲け鏁堢姸鎬佹爣璁�
-      tableData.value = res.data.records.map(record => ({
-        ...record,
-        isInvalid: record.isWhite === 1
-      }));
-      tableData.value.map((item) => {
-        item.children = [];
+    ]);
+  };
+  // 鎵撳紑寮规
+  const openForm = (type, row) => {
+    operationType.value = type;
+    form.value = {};
+    productData.value = [];
+    fileList.value = [];
+    if (operationType.value == "add") {
+      createPurchaseNo().then(res => {
+        form.value.purchaseContractNumber = res.data;
       });
-      total.value = res.data.total;
-      expandedRowKeys.value = [];
-    })
-    .catch(() => {
-      tableLoading.value = false;
+    }
+    userListNoPage().then(res => {
+      userList.value = res.data;
     });
-};
-// 琛ㄦ牸閫夋嫨鏁版嵁
-const handleSelectionChange = (selection) => {
-  selectedRows.value = selection;
-};
-const productSelected = (selectedRows) => {
-  productSelectedRows.value = selectedRows;
-};
-const expandedRowKeys = ref([]);
-// 灞曞紑琛�
-const expandChange = (row, expandedRows) => {
-  if (expandedRows.length > 0) {
-    expandedRowKeys.value = [];
-    try {
-      productList({ salesLedgerId: row.id, type: 2 }).then((res) => {
-        const index = tableData.value.findIndex((item) => item.id === row.id);
-        if (index > -1) {
-          tableData.value[index].children = res.data;
+    getSalesNo().then(res => {
+      salesContractList.value = res;
+    });
+    getOptions().then(res => {
+      // 渚涘簲鍟嗚繃婊ゅ嚭isWhite=0 鐨勬暟鎹�
+      supplierList.value = res.data.filter(item => item.isWhite == 0);
+    });
+    form.value.recorderId = userStore.id;
+    form.value.entryDate = getCurrentDate();
+    if (type === "edit") {
+      currentId.value = row.id;
+      getPurchaseById({ id: row.id, type: 2 }).then(res => {
+        form.value = { ...res };
+        productData.value = form.value.productData;
+        if (form.value.salesLedgerFiles) {
+          fileList.value = form.value.salesLedgerFiles;
+        } else {
+          fileList.value = [];
         }
-        expandedRowKeys.value.push(row.id);
       });
-    } catch (error) {
-      console.log(error);
     }
-  } else {
-    expandedRowKeys.value = [];
+    dialogFormVisible.value = true;
+  };
+  // 涓婁紶鍓嶆牎妫�
+  function handleBeforeUpload(file) {
+    // 鏍℃鏂囦欢澶у皬
+    if (file.size > 1024 * 1024 * 10) {
+      proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃10MB!");
+      return false;
+    }
+    proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
+    return true;
   }
-};
-// 涓昏〃鍚堣鏂规硶
-const summarizeMainTable = (param) => {
-  return proxy.summarizeTable(param, ["contractAmount"]);
-};
-// 瀛愯〃鍚堣鏂规硶
-const summarizeProTable = (param) => {
-  return proxy.summarizeTable(param, [
-    "taxInclusiveUnitPrice",
-    "taxInclusiveTotalPrice",
-    "taxExclusiveTotalPrice",
-  ]);
-};
-// 鎵撳紑寮规
-const openForm = (type, row) => {
-  operationType.value = type;
-  form.value = {};
-  productData.value = [];
-  fileList.value = [];
-  if (operationType.value == "add") {
-    createPurchaseNo().then((res) => {
-      form.value.purchaseContractNumber = res.data;
-    });
+  // 涓婁紶澶辫触
+  function handleUploadError(err) {
+    proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
+    proxy.$modal.closeLoading();
   }
-  userListNoPage().then((res) => {
-    userList.value = res.data;
-  });
-  getSalesNo().then((res) => {
-    salesContractList.value = res;
-  });
-  getOptions().then((res) => {
-    // 渚涘簲鍟嗚繃婊ゅ嚭isWhite=0 鐨勬暟鎹�
-    supplierList.value = res.data.filter((item) => item.isWhite == 0);
-  });
-  form.value.recorderId = userStore.id;
-  form.value.entryDate = getCurrentDate();
-  if (type === "edit") {
-    currentId.value = row.id;
-    getPurchaseById({ id: row.id, type: 2 }).then((res) => {
-      form.value = { ...res };
-      productData.value = form.value.productData;
-      if (form.value.salesLedgerFiles) {
-        fileList.value = form.value.salesLedgerFiles;
-      } else {
-        fileList.value = [];
+  // 涓婁紶鎴愬姛鍥炶皟
+  function handleUploadSuccess(res, file, uploadFiles) {
+    proxy.$modal.closeLoading();
+    if (res.code === 200) {
+      file.tempId = res.data.tempId;
+      proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
+    } else {
+      proxy.$modal.msgError(res.msg);
+      proxy.$refs.fileUpload.handleRemove(file);
+    }
+  }
+  // 绉婚櫎鏂囦欢
+  function handleRemove(file) {
+    console.log("handleRemove", file.id);
+    if (file.size > 1024 * 1024 * 10) {
+      // 浠呭墠绔竻鐞嗭紝涓嶈皟鐢ㄥ垹闄ゆ帴鍙e拰鎻愮ず
+      return;
+    }
+    if (operationType.value === "edit") {
+      let ids = [];
+      ids.push(file.id);
+      delLedgerFile(ids).then(res => {
+        proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+      });
+    }
+  }
+  // 鎻愪氦琛ㄥ崟
+  const submitForm = n => {
+    proxy.$refs["formRef"].validate(valid => {
+      if (valid) {
+        if (productData.value.length > 0) {
+          form.value.productData = proxy.HaveJson(productData.value);
+        } else {
+          proxy.$modal.msgWarning("璇锋坊鍔犱骇鍝佷俊鎭�");
+          return;
+        }
+        let tempFileIds = [];
+        if (fileList.value.length > 0) {
+          tempFileIds = fileList.value.map(item => item.tempId);
+        }
+        form.value.tempFileIds = tempFileIds;
+        form.value.type = 2;
+        form.value.approvalStatus = n;
+        addOrEditPurchase(form.value).then(res => {
+          proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+          closeDia();
+          getList();
+        });
       }
     });
-  }
-  dialogFormVisible.value = true;
-};
-// 涓婁紶鍓嶆牎妫�
-function handleBeforeUpload(file) {
-  // 鏍℃鏂囦欢澶у皬
-  if (file.size > 1024 * 1024 * 10) {
-    proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃10MB!");
-    return false;
-  }
-  proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
-  return true;
-}
-// 涓婁紶澶辫触
-function handleUploadError(err) {
-  proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
-  proxy.$modal.closeLoading();
-}
-// 涓婁紶鎴愬姛鍥炶皟
-function handleUploadSuccess(res, file, uploadFiles) {
-  proxy.$modal.closeLoading();
-  if (res.code === 200) {
-    file.tempId = res.data.tempId;
-    proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
-  } else {
-    proxy.$modal.msgError(res.msg);
-    proxy.$refs.fileUpload.handleRemove(file);
-  }
-}
-// 绉婚櫎鏂囦欢
-function handleRemove(file) {
-  console.log("handleRemove", file.id);
-  if (file.size > 1024 * 1024 * 10) { 
-    // 浠呭墠绔竻鐞嗭紝涓嶈皟鐢ㄥ垹闄ゆ帴鍙e拰鎻愮ず
-    return; 
-  }
-  if (operationType.value === "edit") {
-    let ids = [];
-    ids.push(file.id);
-    delLedgerFile(ids).then((res) => {
-      proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+  };
+  // 鍏抽棴寮规
+  const closeDia = () => {
+    proxy.resetForm("formRef");
+    dialogFormVisible.value = false;
+  };
+  // 鎵撳紑浜у搧寮规
+  const openProductForm = (type, row, index) => {
+    productOperationType.value = type;
+    productOperationIndex.value = index;
+    productForm.value = {};
+    proxy.resetForm("productFormRef");
+    if (type === "edit") {
+      productForm.value = { ...row };
+    }
+    productFormVisible.value = true;
+    getProductOptions();
+  };
+  const getProductOptions = () => {
+    productTreeList().then(res => {
+      productOptions.value = convertIdToValue(res);
+    });
+  };
+  const getModels = value => {
+    if (value) {
+      productForm.value.productCategory =
+        findNodeById(productOptions.value, value) || "";
+      modelList({ id: value }).then(res => {
+        modelOptions.value = res;
+      });
+    } else {
+      productForm.value.productCategory = "";
+      modelOptions.value = [];
+    }
+  };
+  const getProductModel = value => {
+    const index = modelOptions.value.findIndex(item => item.id === value);
+    if (index !== -1) {
+      productForm.value.specificationModel = modelOptions.value[index].model;
+      productForm.value.unit = modelOptions.value[index].unit;
+    } else {
+      productForm.value.specificationModel = null;
+      productForm.value.unit = null;
+    }
+  };
+  const findNodeById = (nodes, productId) => {
+    for (let i = 0; i < nodes.length; i++) {
+      if (nodes[i].value === productId) {
+        return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥炶鑺傜偣鐨刲abel
+      }
+      if (nodes[i].children && nodes[i].children.length > 0) {
+        const foundNode = findNodeById(nodes[i].children, productId);
+        if (foundNode) {
+          return foundNode; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝鐩存帴杩斿洖锛堝凡缁忔槸label瀛楃涓诧級
+        }
+      }
+    }
+    return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
+  };
+  function convertIdToValue(data) {
+    return data.map(item => {
+      const { id, children, ...rest } = item;
+      const newItem = {
+        ...rest,
+        value: id, // 灏� id 鏀逛负 value
+      };
+      if (children && children.length > 0) {
+        newItem.children = convertIdToValue(children);
+      }
+
+      return newItem;
     });
   }
-}
-// 鎻愪氦琛ㄥ崟
-const submitForm = (n) => {
-  proxy.$refs["formRef"].validate((valid) => {
-    if (valid) {
-      if (productData.value.length > 0) {
-        form.value.productData = proxy.HaveJson(productData.value);
-      } else {
-        proxy.$modal.msgWarning("璇锋坊鍔犱骇鍝佷俊鎭�");
+  // 鎻愪氦浜у搧琛ㄥ崟
+  const submitProduct = () => {
+    proxy.$refs["productFormRef"].validate(valid => {
+      if (valid) {
+        if (operationType.value === "edit") {
+          submitProductEdit();
+        } else {
+          if (productOperationType.value === "add") {
+            productData.value.push({ ...productForm.value });
+            console.log("productData.value---", productData.value);
+          } else {
+            productData.value[productOperationIndex.value] = {
+              ...productForm.value,
+            };
+          }
+          closeProductDia();
+        }
+      }
+    });
+  };
+  const submitProductEdit = () => {
+    productForm.value.salesLedgerId = currentId.value;
+    productForm.value.type = 2;
+    addOrUpdateSalesLedgerProduct(productForm.value).then(res => {
+      proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+      closeProductDia();
+      getPurchaseById({ id: currentId.value, type: 2 }).then(res => {
+        productData.value = res.productData;
+      });
+    });
+  };
+  // 鍒犻櫎浜у搧
+  const deleteProduct = () => {
+    if (productSelectedRows.value.length === 0) {
+      proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+      return;
+    }
+    if (operationType.value === "add") {
+      productSelectedRows.value.forEach(selectedRow => {
+        const index = productData.value.findIndex(
+          product => product.id === selectedRow.id
+        );
+        if (index !== -1) {
+          productData.value.splice(index, 1);
+        }
+      });
+    } else {
+      let ids = [];
+      if (productSelectedRows.value.length > 0) {
+        ids = productSelectedRows.value.map(item => item.id);
+      }
+      ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
+        confirmButtonText: "纭",
+        cancelButtonText: "鍙栨秷",
+        type: "warning",
+      })
+        .then(() => {
+          delProduct(ids).then(res => {
+            proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+            closeProductDia();
+            getSalesLedgerWithProducts({ id: currentId.value, type: 2 }).then(
+              res => {
+                productData.value = res.productData;
+              }
+            );
+          });
+        })
+        .catch(() => {
+          proxy.$modal.msg("宸插彇娑�");
+        });
+    }
+  };
+  // 鍏抽棴浜у搧寮规
+  const closeProductDia = () => {
+    proxy.resetForm("productFormRef");
+    productFormVisible.value = false;
+  };
+  // 瀹℃壒閫氳繃鏂规硶
+  const approvePurchase = row => {
+    ElMessageBox.confirm(
+      `纭閫氳繃閲囪喘鍚堝悓鍙蜂负 ${row.purchaseContractNumber} 鐨勫鎵癸紵`,
+      "瀹℃壒纭",
+      {
+        confirmButtonText: "纭",
+        cancelButtonText: "鍙栨秷",
+        type: "warning",
+      }
+    )
+      .then(() => {
+        updateApprovalStatus({ id: row.id, approvalStatus: 1 }).then(res => {
+          proxy.$modal.msgSuccess("瀹℃壒鎴愬姛");
+          getList();
+        });
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑堝鎵�");
+      });
+  };
+
+  // 瀹℃壒鎷掔粷鏂规硶
+  const rejectPurchase = row => {
+    ElMessageBox.confirm(
+      `纭鎷掔粷閲囪喘鍚堝悓鍙蜂负 ${row.purchaseContractNumber} 鐨勫鎵癸紵`,
+      "瀹℃壒纭",
+      {
+        confirmButtonText: "纭",
+        cancelButtonText: "鍙栨秷",
+        type: "warning",
+      }
+    )
+      .then(() => {
+        updateApprovalStatus({ id: row.id, approvalStatus: 2 }).then(res => {
+          proxy.$modal.msgSuccess("瀹℃壒鎴愬姛");
+          getList();
+        });
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑堝鎵�");
+      });
+  };
+
+  // 瀵煎嚭
+  const handleOut = () => {
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        proxy.download("/purchase/ledger/export", {}, "閲囪喘鍙拌处.xlsx");
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
+  };
+  // 鍒犻櫎
+  const handleDelete = () => {
+    let ids = [];
+    if (selectedRows.value.length > 0) {
+      // 妫�鏌ユ槸鍚︽湁浠栦汉缁存姢鐨勬暟鎹�
+      const unauthorizedData = selectedRows.value.filter(
+        item => item.recorderName !== userStore.nickName
+      );
+      if (unauthorizedData.length > 0) {
+        proxy.$modal.msgWarning("涓嶅彲鍒犻櫎浠栦汉缁存姢鐨勬暟鎹�");
         return;
       }
-      let tempFileIds = [];
-      if (fileList.value.length > 0) {
-        tempFileIds = fileList.value.map((item) => item.tempId);
-      }
-      form.value.tempFileIds = tempFileIds;
-      form.value.type = 2;
-      form.value.approvalStatus = n;
-      addOrEditPurchase(form.value).then((res) => {
-        proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-        closeDia();
-        getList();
-      });
-    }
-  });
-};
-// 鍏抽棴寮规
-const closeDia = () => {
-  proxy.resetForm("formRef");
-  dialogFormVisible.value = false;
-};
-// 鎵撳紑浜у搧寮规
-const openProductForm = (type, row, index) => {
-  productOperationType.value = type;
-  productOperationIndex.value = index;
-  productForm.value = {};
-  proxy.resetForm("productFormRef");
-  if (type === "edit") {
-    productForm.value = { ...row };
-  }
-  productFormVisible.value = true;
-  getProductOptions();
-};
-const getProductOptions = () => {
-  productTreeList().then((res) => {
-    productOptions.value = convertIdToValue(res);
-  });
-};
-const getModels = (value) => {
-  if (value) {
-    productForm.value.productCategory = findNodeById(productOptions.value, value) || "";
-    modelList({ id: value }).then((res) => {
-      modelOptions.value = res;
-    });
-  } else {
-    productForm.value.productCategory = "";
-    modelOptions.value = [];
-  }
-};
-const getProductModel = (value) => {
-  const index = modelOptions.value.findIndex((item) => item.id === value);
-  if (index !== -1) {
-    productForm.value.specificationModel = modelOptions.value[index].model;
-    productForm.value.unit = modelOptions.value[index].unit;
-  } else {
-    productForm.value.specificationModel = null;
-    productForm.value.unit = null;
-  }
-};
-const findNodeById = (nodes, productId) => {
-  for (let i = 0; i < nodes.length; i++) {
-    if (nodes[i].value === productId) {
-      return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥炶鑺傜偣鐨刲abel
-    }
-    if (nodes[i].children && nodes[i].children.length > 0) {
-      const foundNode = findNodeById(nodes[i].children, productId);
-      if (foundNode) {
-        return foundNode; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝鐩存帴杩斿洖锛堝凡缁忔槸label瀛楃涓诧級
-      }
-    }
-  }
-  return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
-};
-function convertIdToValue(data) {
-  return data.map((item) => {
-    const { id, children, ...rest } = item;
-    const newItem = {
-      ...rest,
-      value: id, // 灏� id 鏀逛负 value
-    };
-    if (children && children.length > 0) {
-      newItem.children = convertIdToValue(children);
-    }
-
-    return newItem;
-  });
-}
-// 鎻愪氦浜у搧琛ㄥ崟
-const submitProduct = () => {
-  proxy.$refs["productFormRef"].validate((valid) => {
-    if (valid) {
-      if (operationType.value === "edit") {
-        submitProductEdit();
-      } else {
-        if (productOperationType.value === "add") {
-          productData.value.push({ ...productForm.value });
-          console.log("productData.value---", productData.value);
-        } else {
-          productData.value[productOperationIndex.value] = {
-            ...productForm.value,
-          };
-        }
-        closeProductDia();
-      }
-    }
-  });
-};
-const submitProductEdit = () => {
-  productForm.value.salesLedgerId = currentId.value;
-  productForm.value.type = 2;
-  addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
-    proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-    closeProductDia();
-    getPurchaseById({ id: currentId.value, type: 2 }).then((res) => {
-      productData.value = res.productData;
-    });
-  });
-};
-// 鍒犻櫎浜у搧
-const deleteProduct = () => {
-  if (productSelectedRows.value.length === 0) {
-    proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
-    return;
-  }
-  if (operationType.value === "add") {
-    productSelectedRows.value.forEach((selectedRow) => {
-      const index = productData.value.findIndex(
-        (product) => product.id === selectedRow.id
-      );
-      if (index !== -1) {
-        productData.value.splice(index, 1);
-      }
-    });
-  } else {
-    let ids = [];
-    if (productSelectedRows.value.length > 0) {
-      ids = productSelectedRows.value.map((item) => item.id);
+      ids = selectedRows.value.map(item => item.id);
+    } else {
+      proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+      return;
     }
     ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
       confirmButtonText: "纭",
@@ -681,384 +774,313 @@
       type: "warning",
     })
       .then(() => {
-        delProduct(ids).then((res) => {
+        delPurchase(ids).then(res => {
           proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-          closeProductDia();
-          getSalesLedgerWithProducts({ id: currentId.value, type: 2 }).then(
-            (res) => {
-              productData.value = res.productData;
-            }
-          );
+          getList();
         });
       })
       .catch(() => {
         proxy.$modal.msg("宸插彇娑�");
       });
-  }
-};
-// 鍏抽棴浜у搧寮规
-const closeProductDia = () => {
-  proxy.resetForm("productFormRef");
-  productFormVisible.value = false;
-};
-// 瀹℃壒閫氳繃鏂规硶
-const approvePurchase = (row) => {
-  ElMessageBox.confirm(`纭閫氳繃閲囪喘鍚堝悓鍙蜂负 ${row.purchaseContractNumber} 鐨勫鎵癸紵`, '瀹℃壒纭', {
-    confirmButtonText: '纭',
-    cancelButtonText: '鍙栨秷',
-    type: 'warning',
-  }).then(() => {
-    updateApprovalStatus({ id: row.id, approvalStatus: 1}).then((res)=>{
-      proxy.$modal.msgSuccess('瀹℃壒鎴愬姛');
-      getList();
-    })
-  }).catch(() => {
-    proxy.$modal.msg('宸插彇娑堝鎵�');
-  });
-};
-
-// 瀹℃壒鎷掔粷鏂规硶
-const rejectPurchase = (row) => {
-  ElMessageBox.confirm(`纭鎷掔粷閲囪喘鍚堝悓鍙蜂负 ${row.purchaseContractNumber} 鐨勫鎵癸紵`, '瀹℃壒纭', {
-    confirmButtonText: '纭',
-    cancelButtonText: '鍙栨秷',
-    type: 'warning',
-  }).then(() => {
-    updateApprovalStatus({ id: row.id, approvalStatus: 2}).then((res)=>{
-      proxy.$modal.msgSuccess('瀹℃壒鎴愬姛');
-      getList();
-    })
-  }).catch(() => {
-    proxy.$modal.msg('宸插彇娑堝鎵�');
-  });
-};
-
-// 瀵煎嚭
-const handleOut = () => {
-  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
-    confirmButtonText: "纭",
-    cancelButtonText: "鍙栨秷",
-    type: "warning",
-  })
-    .then(() => {
-      proxy.download("/purchase/ledger/export", {}, "閲囪喘鍙拌处.xlsx");
-    })
-    .catch(() => {
-      proxy.$modal.msg("宸插彇娑�");
-    });
-};
-// 鍒犻櫎
-const handleDelete = () => {
-  let ids = [];
-  if (selectedRows.value.length > 0) {
-		// 妫�鏌ユ槸鍚︽湁浠栦汉缁存姢鐨勬暟鎹�
-		const unauthorizedData = selectedRows.value.filter(item => item.recorderName !== userStore.nickName);
-		if (unauthorizedData.length > 0) {
-			proxy.$modal.msgWarning("涓嶅彲鍒犻櫎浠栦汉缁存姢鐨勬暟鎹�");
-			return;
-		}
-    ids = selectedRows.value.map((item) => item.id);
-  } else {
-    proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
-    return;
-  }
-  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
-    confirmButtonText: "纭",
-    cancelButtonText: "鍙栨秷",
-    type: "warning",
-  })
-    .then(() => {
-      delPurchase(ids).then((res) => {
-        proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-        getList();
-      });
-    })
-    .catch(() => {
-      proxy.$modal.msg("宸插彇娑�");
-    });
-};
-const mathNum = () => {
-	if (!productForm.value.taxRate) {
-		proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
-		return;
-	}
-  if (!productForm.value.taxInclusiveUnitPrice) {
-    return;
-  }
-  if (!productForm.value.quantity) {
-    return;
-  }
-  // 鍚◣鎬讳环璁$畻
-  productForm.value.taxInclusiveTotalPrice =
-    proxy.calculateTaxIncludeTotalPrice(
-      productForm.value.taxInclusiveUnitPrice,
-      productForm.value.quantity
-    );
-  if (productForm.value.taxRate) {
-    // 涓嶅惈绋庢�讳环璁$畻
-    productForm.value.taxExclusiveTotalPrice =
-      proxy.calculateTaxExclusiveTotalPrice(
-        productForm.value.taxInclusiveTotalPrice,
-        productForm.value.taxRate
-      );
-  }
-};
-const reverseMathNum = (field) => {
-	if (!productForm.value.taxRate) {
-		proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
-		return;
-	}
-  const taxRate = Number(productForm.value.taxRate);
-  if (!taxRate) return;
-  if (field === 'taxInclusiveTotalPrice') {
-    // 宸茬煡鍚◣鎬讳环鍜屾暟閲忥紝鍙嶇畻鍚◣鍗曚环
-    if (productForm.value.quantity) {
-      productForm.value.taxInclusiveUnitPrice = 
-        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
-    }
-    // 宸茬煡鍚◣鎬讳环鍜屽惈绋庡崟浠凤紝鍙嶇畻鏁伴噺
-    else if (productForm.value.taxInclusiveUnitPrice) {
-      productForm.value.quantity = 
-        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
-    }
-    // 鍙嶇畻涓嶅惈绋庢�讳环
-    productForm.value.taxExclusiveTotalPrice = 
-      (Number(productForm.value.taxInclusiveTotalPrice) / (1 + taxRate / 100)).toFixed(2);
-  } else if (field === 'taxExclusiveTotalPrice') {
-    // 鍙嶇畻鍚◣鎬讳环
-    productForm.value.taxInclusiveTotalPrice = 
-      (Number(productForm.value.taxExclusiveTotalPrice) * (1 + taxRate / 100)).toFixed(2);
-    // 宸茬煡鏁伴噺锛屽弽绠楀惈绋庡崟浠�
-    if (productForm.value.quantity) {
-      productForm.value.taxInclusiveUnitPrice = 
-        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.quantity)).toFixed(2);
-    }
-    // 宸茬煡鍚◣鍗曚环锛屽弽绠楁暟閲�
-    else if (productForm.value.taxInclusiveUnitPrice) {
-      productForm.value.quantity = 
-        (Number(productForm.value.taxInclusiveTotalPrice) / Number(productForm.value.taxInclusiveUnitPrice)).toFixed(2);
-    }
-  }
-};
-// 閿�鍞悎鍚岄�夋嫨鏀瑰彉鏂规硶
-const salesLedgerChange = async (row) => {
-  console.log("row", row);
-  var index = salesContractList.value.findIndex((item) => item.id == row);
-  console.log("index", index);
-  if (index > -1) {
-    form.value.projectName = salesContractList.value[index].projectName;
-    await querygProductInfoByContractNo();
-  }
-};
-
-const querygProductInfoByContractNo = async () => {
-  const { code, data } = await getProductInfoByContractNo({
-    contractNo: form.value.salesLedgerId,
-  });
-  if (code == 200) {
-    productData.value = data;
-  }
-};
-
-// 鏄剧ず浜岀淮鐮�
-const showQRCode = async (row) => {
-  try {
-    // 鏋勫缓浜岀淮鐮佸唴瀹癸紝鍙寘鍚噰璐悎鍚屽彿锛堢函鏂囨湰锛�
-    const qrContent = row.purchaseContractNumber || '';
-    // 妫�鏌ュ唴瀹规槸鍚︿负绌�
-    if (!qrContent || qrContent.trim() === '') {
-      proxy.$modal.msgWarning("璇ヨ娌℃湁閲囪喘鍚堝悓鍙凤紝鏃犳硶鐢熸垚浜岀淮鐮�");
+  };
+  const mathNum = () => {
+    if (!productForm.value.taxRate) {
+      proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
       return;
     }
-    qrCodeUrl.value = await QRCode.toDataURL(qrContent, {
-      width: 200,
-      margin: 2,
-      color: {
-        dark: '#000000',
-        light: '#FFFFFF'
+    if (!productForm.value.taxInclusiveUnitPrice) {
+      return;
+    }
+    if (!productForm.value.quantity) {
+      return;
+    }
+    // 鍚◣鎬讳环璁$畻
+    productForm.value.taxInclusiveTotalPrice =
+      proxy.calculateTaxIncludeTotalPrice(
+        productForm.value.taxInclusiveUnitPrice,
+        productForm.value.quantity
+      );
+    if (productForm.value.taxRate) {
+      // 涓嶅惈绋庢�讳环璁$畻
+      productForm.value.taxExclusiveTotalPrice =
+        proxy.calculateTaxExclusiveTotalPrice(
+          productForm.value.taxInclusiveTotalPrice,
+          productForm.value.taxRate
+        );
+    }
+  };
+  const reverseMathNum = field => {
+    if (!productForm.value.taxRate) {
+      proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+      return;
+    }
+    const taxRate = Number(productForm.value.taxRate);
+    if (!taxRate) return;
+    if (field === "taxInclusiveTotalPrice") {
+      // 宸茬煡鍚◣鎬讳环鍜屾暟閲忥紝鍙嶇畻鍚◣鍗曚环
+      if (productForm.value.quantity) {
+        productForm.value.taxInclusiveUnitPrice = (
+          Number(productForm.value.taxInclusiveTotalPrice) /
+          Number(productForm.value.quantity)
+        ).toFixed(2);
+      }
+      // 宸茬煡鍚◣鎬讳环鍜屽惈绋庡崟浠凤紝鍙嶇畻鏁伴噺
+      else if (productForm.value.taxInclusiveUnitPrice) {
+        productForm.value.quantity = (
+          Number(productForm.value.taxInclusiveTotalPrice) /
+          Number(productForm.value.taxInclusiveUnitPrice)
+        ).toFixed(2);
+      }
+      // 鍙嶇畻涓嶅惈绋庢�讳环
+      productForm.value.taxExclusiveTotalPrice = (
+        Number(productForm.value.taxInclusiveTotalPrice) /
+        (1 + taxRate / 100)
+      ).toFixed(2);
+    } else if (field === "taxExclusiveTotalPrice") {
+      // 鍙嶇畻鍚◣鎬讳环
+      productForm.value.taxInclusiveTotalPrice = (
+        Number(productForm.value.taxExclusiveTotalPrice) *
+        (1 + taxRate / 100)
+      ).toFixed(2);
+      // 宸茬煡鏁伴噺锛屽弽绠楀惈绋庡崟浠�
+      if (productForm.value.quantity) {
+        productForm.value.taxInclusiveUnitPrice = (
+          Number(productForm.value.taxInclusiveTotalPrice) /
+          Number(productForm.value.quantity)
+        ).toFixed(2);
+      }
+      // 宸茬煡鍚◣鍗曚环锛屽弽绠楁暟閲�
+      else if (productForm.value.taxInclusiveUnitPrice) {
+        productForm.value.quantity = (
+          Number(productForm.value.taxInclusiveTotalPrice) /
+          Number(productForm.value.taxInclusiveUnitPrice)
+        ).toFixed(2);
+      }
+    }
+  };
+  // 閿�鍞悎鍚岄�夋嫨鏀瑰彉鏂规硶
+  const salesLedgerChange = async row => {
+    console.log("row", row);
+    var index = salesContractList.value.findIndex(item => item.id == row);
+    console.log("index", index);
+    if (index > -1) {
+      form.value.projectName = salesContractList.value[index].projectName;
+      await querygProductInfoByContractNo();
+    }
+  };
+
+  const querygProductInfoByContractNo = async () => {
+    const { code, data } = await getProductInfoByContractNo({
+      contractNo: form.value.salesLedgerId,
+    });
+    if (code == 200) {
+      productData.value = data;
+    }
+  };
+
+  // 鏄剧ず浜岀淮鐮�
+  const showQRCode = async row => {
+    try {
+      // 鏋勫缓浜岀淮鐮佸唴瀹癸紝鍙寘鍚噰璐悎鍚屽彿锛堢函鏂囨湰锛�
+      const qrContent = row.purchaseContractNumber || "";
+      // 妫�鏌ュ唴瀹规槸鍚︿负绌�
+      if (!qrContent || qrContent.trim() === "") {
+        proxy.$modal.msgWarning("璇ヨ娌℃湁閲囪喘鍚堝悓鍙凤紝鏃犳硶鐢熸垚浜岀淮鐮�");
+        return;
+      }
+      qrCodeUrl.value = await QRCode.toDataURL(qrContent, {
+        width: 200,
+        margin: 2,
+        color: {
+          dark: "#000000",
+          light: "#FFFFFF",
+        },
+      });
+      qrCodeDialogVisible.value = true;
+    } catch (error) {
+      console.error("鐢熸垚浜岀淮鐮佸け璐�:", error);
+      proxy.$modal.msgError("鐢熸垚浜岀淮鐮佸け璐ワ細" + error.message);
+    }
+  };
+
+  // 涓嬭浇浜岀淮鐮�
+  const downloadQRCode = () => {
+    if (!qrCodeUrl.value) {
+      proxy.$modal.msgWarning("浜岀淮鐮佹湭鐢熸垚");
+      return;
+    }
+
+    const a = document.createElement("a");
+    a.href = qrCodeUrl.value;
+    a.download = `閲囪喘鍚堝悓鍙蜂簩缁寸爜_${new Date().getTime()}.png`;
+    document.body.appendChild(a);
+    a.click();
+    document.body.removeChild(a);
+    proxy.$modal.msgSuccess("涓嬭浇鎴愬姛");
+  };
+
+  // 鎵爜鏂板瀵硅瘽妗嗙浉鍏冲彉閲�
+  const scanAddDialogVisible = ref(false);
+  const scanAddForm = reactive({
+    scanContent: "",
+    purchaseContractNumber: "",
+    supplierName: "",
+    projectName: "",
+    contractAmount: "",
+    paymentMethod: "",
+    recorderName: "",
+    scanRemark: "",
+  });
+  const scanAddRules = {
+    purchaseContractNumber: [
+      { required: true, message: "璇疯緭鍏ラ噰璐悎鍚屽彿", trigger: "blur" },
+    ],
+    supplierName: [
+      { required: true, message: "璇疯緭鍏ヤ緵搴斿晢鍚嶇О", trigger: "blur" },
+    ],
+    projectName: [{ required: true, message: "璇疯緭鍏ラ」鐩悕绉�", trigger: "blur" }],
+  };
+
+  // 鎵爜鐧昏瀵硅瘽妗嗙浉鍏冲彉閲�
+  const scanDialogVisible = ref(false);
+  const scanForm = reactive({
+    purchaseContractNumber: "",
+    supplierName: "",
+    projectName: "",
+    scanTime: "",
+    scannerName: "",
+    scanStatus: "鏈壂鐮�",
+    scanRemark: "",
+  });
+  const scanRules = {
+    scanRemark: [{ required: true, message: "璇疯緭鍏ユ壂鐮佸娉�", trigger: "blur" }],
+  };
+  const scanRecords = ref([]);
+
+  // 鎵撳紑鎵爜鏂板瀵硅瘽妗�
+  const openScanAddDialog = () => {
+    scanAddForm.scanContent = "";
+    scanAddForm.purchaseContractNumber = "";
+    scanAddForm.supplierName = "";
+    scanAddForm.projectName = "";
+    scanAddForm.contractAmount = "";
+    scanAddForm.paymentMethod = "";
+    scanAddForm.recorderName = userStore.nickName;
+    scanAddForm.scanRemark = "";
+    scanAddDialogVisible.value = true;
+  };
+
+  // 瑙f瀽鎵爜鍐呭锛堟ā鎷熻В鏋愪簩缁寸爜鏁版嵁锛�
+  const parseScanContent = content => {
+    if (!content) return;
+
+    // 妯℃嫙瑙f瀽浜岀淮鐮佸唴瀹癸紝杩欓噷鍙互鏍规嵁瀹為檯闇�姹傝皟鏁磋В鏋愰�昏緫
+    // 鍋囪鎵爜鍐呭鏍煎紡涓猴細鍚堝悓鍙穦渚涘簲鍟唡椤圭洰|閲戦|浠樻鏂瑰紡
+    const parts = content.split("|");
+    if (parts.length >= 3) {
+      scanAddForm.purchaseContractNumber = parts[0] || "";
+      scanAddForm.supplierName = parts[1] || "";
+      scanAddForm.projectName = parts[2] || "";
+      scanAddForm.contractAmount = parts[3] || "";
+      scanAddForm.paymentMethod = parts[4] || "";
+    }
+  };
+
+  // 鍏抽棴鎵爜鏂板瀵硅瘽妗�
+  const closeScanAddDialog = () => {
+    scanAddDialogVisible.value = false;
+    proxy.resetForm("scanAddFormRef");
+  };
+
+  // 鎻愪氦鎵爜鏂板
+  const submitScanAdd = () => {
+    proxy.$refs["scanAddFormRef"].validate(valid => {
+      if (valid) {
+        // 鏋勫缓鏂板鏁版嵁
+        const newData = {
+          purchaseContractNumber: scanAddForm.purchaseContractNumber,
+          supplierName: scanAddForm.supplierName,
+          projectName: scanAddForm.projectName,
+          contractAmount: scanAddForm.contractAmount,
+          paymentMethod: scanAddForm.paymentMethod,
+          recorderName: scanAddForm.recorderName,
+          entryDate: getCurrentDate(),
+          remark: scanAddForm.scanRemark,
+          type: 2,
+        };
+
+        // 妯℃嫙鏂板鎴愬姛
+        proxy.$modal.msgSuccess("鎵爜鏂板鎴愬姛锛�");
+        closeScanAddDialog();
+
+        // 鍙互閫夋嫨鏄惁鍒锋柊鍒楄〃
+        // getList();
       }
     });
-    qrCodeDialogVisible.value = true;
-  } catch (error) {
-    console.error('鐢熸垚浜岀淮鐮佸け璐�:', error);
-    proxy.$modal.msgError("鐢熸垚浜岀淮鐮佸け璐ワ細" + error.message);
+  };
+
+  // 鎵撳紑鎵爜鐧昏瀵硅瘽妗�
+  const openScanDialog = row => {
+    scanForm.purchaseContractNumber = row.purchaseContractNumber;
+    scanForm.supplierName = row.supplierName;
+    scanForm.projectName = row.projectName;
+    scanForm.scanTime = getCurrentDateTime();
+    scanForm.scannerName = userStore.nickName;
+    scanForm.scanStatus = "鏈壂鐮�";
+    scanForm.scanRemark = "";
+    scanRecords.value = [];
+    scanDialogVisible.value = true;
+  };
+
+  // 鍏抽棴鎵爜鐧昏瀵硅瘽妗�
+  const closeScanDialog = () => {
+    scanDialogVisible.value = false;
+    proxy.resetForm("scanFormRef");
+  };
+
+  // 鎻愪氦鎵爜鐧昏
+  const submitScan = () => {
+    proxy.$refs["scanFormRef"].validate(valid => {
+      if (valid) {
+        // 娣诲姞鎵爜璁板綍
+        scanRecords.value.push({
+          ...scanForm,
+          id: Date.now(), // 妯℃嫙ID
+          scanTime: getCurrentDateTime(),
+        });
+        scanForm.scanStatus = "宸叉壂鐮�";
+        scanForm.scanRemark = scanForm.scanRemark || "鏃�";
+        proxy.$modal.msgSuccess("鎵爜鐧昏鎴愬姛锛�");
+        closeScanDialog();
+      }
+    });
+  };
+
+  // 鑾峰彇褰撳墠鏃ユ湡鏃堕棿
+  function getCurrentDateTime() {
+    const now = new Date();
+    const year = now.getFullYear();
+    const month = String(now.getMonth() + 1).padStart(2, "0");
+    const day = String(now.getDate()).padStart(2, "0");
+    const hours = String(now.getHours()).padStart(2, "0");
+    const minutes = String(now.getMinutes()).padStart(2, "0");
+    const seconds = String(now.getSeconds()).padStart(2, "0");
+    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
   }
-};
 
-// 涓嬭浇浜岀淮鐮�
-const downloadQRCode = () => {
-  if (!qrCodeUrl.value) {
-    proxy.$modal.msgWarning("浜岀淮鐮佹湭鐢熸垚");
-    return;
-  }
-  
-  const a = document.createElement('a');
-  a.href = qrCodeUrl.value;
-  a.download = `閲囪喘鍚堝悓鍙蜂簩缁寸爜_${new Date().getTime()}.png`;
-  document.body.appendChild(a);
-  a.click();
-  document.body.removeChild(a);
-  proxy.$modal.msgSuccess("涓嬭浇鎴愬姛");
-};
+  // 娣诲姞琛岀被鍚嶆柟娉�
+  const tableRowClassName = ({ row }) => {
+    return row.isInvalid ? "invalid-row" : "";
+  };
 
-// 鎵爜鏂板瀵硅瘽妗嗙浉鍏冲彉閲�
-const scanAddDialogVisible = ref(false);
-const scanAddForm = reactive({
-  scanContent: "",
-  purchaseContractNumber: "",
-  supplierName: "",
-  projectName: "",
-  contractAmount: "",
-  paymentMethod: "",
-  recorderName: "",
-  scanRemark: "",
-});
-const scanAddRules = {
-  purchaseContractNumber: [{ required: true, message: "璇疯緭鍏ラ噰璐悎鍚屽彿", trigger: "blur" }],
-  supplierName: [{ required: true, message: "璇疯緭鍏ヤ緵搴斿晢鍚嶇О", trigger: "blur" }],
-  projectName: [{ required: true, message: "璇疯緭鍏ラ」鐩悕绉�", trigger: "blur" }],
-};
-
-// 鎵爜鐧昏瀵硅瘽妗嗙浉鍏冲彉閲�
-const scanDialogVisible = ref(false);
-const scanForm = reactive({
-  purchaseContractNumber: "",
-  supplierName: "",
-  projectName: "",
-  scanTime: "",
-  scannerName: "",
-  scanStatus: "鏈壂鐮�",
-  scanRemark: "",
-});
-const scanRules = {
-  scanRemark: [{ required: true, message: "璇疯緭鍏ユ壂鐮佸娉�", trigger: "blur" }],
-};
-const scanRecords = ref([]);
-
-// 鎵撳紑鎵爜鏂板瀵硅瘽妗�
-const openScanAddDialog = () => {
-  scanAddForm.scanContent = "";
-  scanAddForm.purchaseContractNumber = "";
-  scanAddForm.supplierName = "";
-  scanAddForm.projectName = "";
-  scanAddForm.contractAmount = "";
-  scanAddForm.paymentMethod = "";
-  scanAddForm.recorderName = userStore.nickName;
-  scanAddForm.scanRemark = "";
-  scanAddDialogVisible.value = true;
-};
-
-// 瑙f瀽鎵爜鍐呭锛堟ā鎷熻В鏋愪簩缁寸爜鏁版嵁锛�
-const parseScanContent = (content) => {
-  if (!content) return;
-  
-  // 妯℃嫙瑙f瀽浜岀淮鐮佸唴瀹癸紝杩欓噷鍙互鏍规嵁瀹為檯闇�姹傝皟鏁磋В鏋愰�昏緫
-  // 鍋囪鎵爜鍐呭鏍煎紡涓猴細鍚堝悓鍙穦渚涘簲鍟唡椤圭洰|閲戦|浠樻鏂瑰紡
-  const parts = content.split('|');
-  if (parts.length >= 3) {
-    scanAddForm.purchaseContractNumber = parts[0] || "";
-    scanAddForm.supplierName = parts[1] || "";
-    scanAddForm.projectName = parts[2] || "";
-    scanAddForm.contractAmount = parts[3] || "";
-    scanAddForm.paymentMethod = parts[4] || "";
-  }
-};
-
-// 鍏抽棴鎵爜鏂板瀵硅瘽妗�
-const closeScanAddDialog = () => {
-  scanAddDialogVisible.value = false;
-  proxy.resetForm("scanAddFormRef");
-};
-
-// 鎻愪氦鎵爜鏂板
-const submitScanAdd = () => {
-  proxy.$refs["scanAddFormRef"].validate((valid) => {
-    if (valid) {
-      // 鏋勫缓鏂板鏁版嵁
-      const newData = {
-        purchaseContractNumber: scanAddForm.purchaseContractNumber,
-        supplierName: scanAddForm.supplierName,
-        projectName: scanAddForm.projectName,
-        contractAmount: scanAddForm.contractAmount,
-        paymentMethod: scanAddForm.paymentMethod,
-        recorderName: scanAddForm.recorderName,
-        entryDate: getCurrentDate(),
-        remark: scanAddForm.scanRemark,
-        type: 2
-      };
-      
-      // 妯℃嫙鏂板鎴愬姛
-      proxy.$modal.msgSuccess("鎵爜鏂板鎴愬姛锛�");
-      closeScanAddDialog();
-      
-      // 鍙互閫夋嫨鏄惁鍒锋柊鍒楄〃
-      // getList();
-    }
+  onMounted(() => {
+    getList();
   });
-};
-
-// 鎵撳紑鎵爜鐧昏瀵硅瘽妗�
-const openScanDialog = (row) => {
-  scanForm.purchaseContractNumber = row.purchaseContractNumber;
-  scanForm.supplierName = row.supplierName;
-  scanForm.projectName = row.projectName;
-  scanForm.scanTime = getCurrentDateTime();
-  scanForm.scannerName = userStore.nickName;
-  scanForm.scanStatus = "鏈壂鐮�";
-  scanForm.scanRemark = "";
-  scanRecords.value = [];
-  scanDialogVisible.value = true;
-};
-
-// 鍏抽棴鎵爜鐧昏瀵硅瘽妗�
-const closeScanDialog = () => {
-  scanDialogVisible.value = false;
-  proxy.resetForm("scanFormRef");
-};
-
-// 鎻愪氦鎵爜鐧昏
-const submitScan = () => {
-  proxy.$refs["scanFormRef"].validate((valid) => {
-    if (valid) {
-      // 娣诲姞鎵爜璁板綍
-      scanRecords.value.push({
-        ...scanForm,
-        id: Date.now(), // 妯℃嫙ID
-        scanTime: getCurrentDateTime(),
-      });
-      scanForm.scanStatus = "宸叉壂鐮�";
-      scanForm.scanRemark = scanForm.scanRemark || "鏃�";
-      proxy.$modal.msgSuccess("鎵爜鐧昏鎴愬姛锛�");
-      closeScanDialog();
-    }
-  });
-};
-
-// 鑾峰彇褰撳墠鏃ユ湡鏃堕棿
-function getCurrentDateTime() {
-  const now = new Date();
-  const year = now.getFullYear();
-  const month = String(now.getMonth() + 1).padStart(2, "0");
-  const day = String(now.getDate()).padStart(2, "0");
-  const hours = String(now.getHours()).padStart(2, "0");
-  const minutes = String(now.getMinutes()).padStart(2, "0");
-  const seconds = String(now.getSeconds()).padStart(2, "0");
-  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
-}
-
-// 娣诲姞琛岀被鍚嶆柟娉�
-const tableRowClassName = ({ row }) => {
-  return row.isInvalid ? 'invalid-row' : '';
-};
-
-onMounted(() => {
-  getList();
-});
 </script>
 
 <style scoped lang="scss">
-.invalid-row {
-  opacity: 0.6;
-  background-color: #f5f7fa;
-}
+  .invalid-row {
+    opacity: 0.6;
+    background-color: #f5f7fa;
+  }
 </style>
diff --git a/src/views/collaborativeApproval/rpaManagement/index.vue b/src/views/collaborativeApproval/rpaManagement/index.vue
index 9e5b504..c734b28 100644
--- a/src/views/collaborativeApproval/rpaManagement/index.vue
+++ b/src/views/collaborativeApproval/rpaManagement/index.vue
@@ -80,8 +80,8 @@
       </el-form>
       <template #footer>
         <span class="dialog-footer">
-          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
           <el-button type="primary" @click="submitForm">纭畾</el-button>
+          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
         </span>
       </template>
     </el-dialog>
diff --git a/src/views/collaborativeApproval/rulesRegulationsManagement/index.vue b/src/views/collaborativeApproval/rulesRegulationsManagement/index.vue
index ba8e46d..f7ba9d9 100644
--- a/src/views/collaborativeApproval/rulesRegulationsManagement/index.vue
+++ b/src/views/collaborativeApproval/rulesRegulationsManagement/index.vue
@@ -126,9 +126,8 @@
       </el-form>
       <template #footer>
         <span class="dialog-footer">
+          <el-button type="primary" @click="submitRegulation">鍙戝竷鍒跺害</el-button>
           <el-button @click="showRegulationDialog = false">鍙栨秷</el-button>
-          <el-button type="primary"
-                     @click="submitRegulation">鍙戝竷鍒跺害</el-button>
         </span>
       </template>
     </el-dialog>
@@ -213,14 +212,7 @@
         </el-table-column>
       </el-table>
     </el-dialog>
-    <FileListDialog ref="fileListDialogRef"
-                    v-model="fileDialogVisible"
-                    :show-upload-button="true"
-                    :show-delete-button="true"
-                    :delete-method="handleAttachmentDelete"
-                    :rules-regulations-management-id="currentFileRuleId"
-                    :name-column-label="'闄勪欢鍚嶇О'"
-                    @upload="handleAttachmentUpload"/>
+    <FileList v-if="fileDialogVisible"  v-model:visible="fileDialogVisible" record-type="rules_regulations_management" :record-id="recordId"  />
   </div>
 </template>
 
@@ -236,7 +228,7 @@
     addReadingStatus,
     updateReadingStatus,
   } from "@/api/collaborativeApproval/sealManagement.js";
-  import FileListDialog from "@/components/Dialog/FileListDialog.vue";
+  const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
   import {
     listRuleFiles,
     delRuleFile,
@@ -255,14 +247,7 @@
     total: 0,
   });
   // 闄勪欢寮圭獥
-  const fileDialogVisible = ref(false);
-  const fileListDialogRef = ref(null);
   const currentFileRuleId = ref(null);
-  const filePage = reactive({
-    current: 1,
-    size: 1000,
-    total: 0,
-  });
   // 瑙勭珷鍒跺害鐩稿叧
   const showRegulationDialog = ref(false);
   const showRegulationDetailDialog = ref(false);
@@ -341,10 +326,10 @@
       fixed: "right",
       align: "center",
       operation: [
-        { name: "鏌ョ湅", clickFun: (row) => viewRegulation(row) },
         { name: "缂栬緫", clickFun: (row) => handleEdit(row) },
         { name: "搴熷純", clickFun: (row) => repealEdit(row) },
         { name: "鐗堟湰鍘嗗彶", clickFun: (row) => viewVersionHistory(row) },
+        { name: "璇︽儏", clickFun: (row) => viewRegulation(row) },
         { name: "闄勪欢", clickFun: (row) => openFileDialog(row) },
       ],
     },
@@ -565,63 +550,15 @@
     );
   };
 
-  // 闄勪欢锛氭煡璇�
-  const fetchRuleFiles = async rulesRegulationsManagementId => {
-    const params = {
-      current: filePage.current,
-      size: filePage.size,
-      rulesRegulationsManagementId,
-    };
-    const res = await listRuleFiles(params);
-    const records = res?.data?.records || [];
-    filePage.total = res?.data?.total || records.length;
-    const mapped = records.map(item => ({
-      id: item.id,
-      name: item.fileName || item.name,
-      url: item.fileUrl || item.url,
-      raw: item,
-    }));
-    fileListDialogRef.value?.setList(mapped);
-  };
-
   // 鎵撳紑闄勪欢寮圭獥
-  const openFileDialog = async row => {
-    currentFileRuleId.value = row.id;
-    fileDialogVisible.value = true;
-    await fetchRuleFiles(row.id);
-  };
+  const recordId =ref(0)
+  const fileDialogVisible = ref(false)
 
-  // 鍒锋柊闄勪欢鍒楄〃
-  const refreshFileList = async () => {
-    if (!currentFileRuleId.value) return;
-    await fetchRuleFiles(currentFileRuleId.value);
-  };
-
-  // 涓婁紶闄勪欢锛堢敱瀛愮粍浠惰Е鍙戯級
-  const handleAttachmentUpload = async filePayload => {
-    if (!currentFileRuleId.value) return;
-    const payload = {
-      name: filePayload?.fileName || filePayload?.name,
-      url: filePayload?.fileUrl || filePayload?.url,
-      rulesRegulationsManagementId: currentFileRuleId.value,
-    };
-    await addRuleFile(payload);
-    ElMessage.success("鏂囦欢涓婁紶鎴愬姛");
-    await refreshFileList();
-  };
-
-  // 鍒犻櫎闄勪欢
-  const handleAttachmentDelete = async row => {
-    if (!row?.id) return false;
-    try {
-      await ElMessageBox.confirm("纭鍒犻櫎璇ラ檮浠讹紵", "鎻愮ず", { type: "warning" });
-    } catch {
-      return false;
-    }
-    await delRuleFile([row.id]);
-    ElMessage.success("鍒犻櫎鎴愬姛");
-    await refreshFileList();
-  };
+  // 鎵撳紑闄勪欢寮规
+  const openFileDialog = async (row) => {
+    recordId.value = row.id
+    fileDialogVisible.value = true
+  }
 
   // 鑾峰彇瑙勭珷鍒跺害鍒楄〃鏁版嵁
   const getRegulationList = async () => {
@@ -687,8 +624,6 @@
   }
 
   .dialog-footer {
-    display: flex;
-    justify-content: flex-end;
-    gap: 10px;
+    text-align: center;
   }
 </style>
diff --git a/src/views/collaborativeApproval/sealManagement/index.vue b/src/views/collaborativeApproval/sealManagement/index.vue
index c0d13f2..a6232c2 100644
--- a/src/views/collaborativeApproval/sealManagement/index.vue
+++ b/src/views/collaborativeApproval/sealManagement/index.vue
@@ -236,7 +236,6 @@
     fixed: 'right',
     align: 'center',
     operation: [
-      { name: '鏌ョ湅', clickFun: (row) => viewSealDetail(row) },
       {
         name: '瀹℃壒',
         clickFun: (row) => approveSeal(row),
@@ -246,7 +245,8 @@
         name: '鎷掔粷',
         clickFun: (row) => rejectSeal(row),
         showHide: (row) => row.status === 'pending'
-      }
+      },
+			{ name: '璇︽儏', clickFun: (row) => viewSealDetail(row) }
     ]
   }
 ])
diff --git a/src/views/customerService/afterSalesHandling/index.vue b/src/views/customerService/afterSalesHandling/index.vue
index 57cc2eb..a42337d 100644
--- a/src/views/customerService/afterSalesHandling/index.vue
+++ b/src/views/customerService/afterSalesHandling/index.vue
@@ -1,6 +1,6 @@
 <template>
 	<div class="app-container">
-		<div class="search-wrapper">
+		<div class="search-wrapper mb20">
       <el-form
           :model="searchForm"
           class="demo-form-inline"
@@ -102,33 +102,19 @@
 			></PIMTable>
 		</div>
 		<form-dia ref="formDia" @close="handleQuery"></form-dia>
-		<FileListDialog
-			ref="fileListRef"
-			v-model="fileListDialogVisible"
-			title="鍞悗闄勪欢"
-			:show-upload-button="true"
-			:show-delete-button="true"
-			:upload-method="handleFileUpload"
-			:delete-method="handleFileDelete"
-		/>
-	</div>
+    <FileList v-if="fileDialogVisible"  v-model:visible="fileDialogVisible" record-type="after_sales_service" :record-id="recordId"  />
+  </div>
 </template>
 
 <script setup>
-import { onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick } from "vue";
+import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick, defineAsyncComponent} from "vue";
 import FormDia from "@/views/customerService/afterSalesHandling/components/formDia.vue";
-import FileListDialog from "@/components/Dialog/FileListDialog.vue";
 import { ElMessageBox } from "element-plus";
-import request from "@/utils/request";
-import { getToken } from "@/utils/auth";
 import {
 	afterSalesServiceListPage,
-	afterSalesServiceFileListPage,
-	afterSalesServiceFileDel,
 } from "@/api/customerService/index.js";
-import useUserStore from "@/store/modules/user.js";
 const { proxy } = getCurrentInstance();
-const userStore = useUserStore()
+const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
 
 const data = reactive({
 	searchForm: {
@@ -303,144 +289,15 @@
   })
 }
 
+
+// 鎵撳紑闄勪欢寮圭獥
+const recordId =ref(0)
+const fileDialogVisible = ref(false)
+
 // 鎵撳紑闄勪欢寮规
 const openFilesFormDia = async (row) => {
-	currentFileRow.value = row
-	try {
-		const res = await afterSalesServiceFileListPage({
-			afterSalesServiceId: row.id,
-			current: 1,
-			size: 100,
-		})
-		if (res.code === 200 && fileListRef.value) {
-			const fileList = (res.data?.records || []).map((item) => ({
-				name: item.name || item.fileName,
-				url: item.url || item.fileUrl,
-				id: item.id,
-				...item,
-			}))
-			fileListRef.value.open(fileList)
-			fileListDialogVisible.value = true
-		} else {
-			fileListRef.value?.open([])
-			fileListDialogVisible.value = true
-		}
-	} catch (error) {
-		proxy.$modal.msgError("鑾峰彇闄勪欢鍒楄〃澶辫触")
-		fileListRef.value?.open([])
-		fileListDialogVisible.value = true
-	}
-}
-
-// 涓婁紶闄勪欢
-const handleFileUpload = async () => {
-	if (!currentFileRow.value) {
-		proxy.$modal.msgWarning("璇峰厛閫夋嫨鏁版嵁")
-		return
-	}
-	return new Promise((resolve) => {
-		const input = document.createElement("input")
-		input.type = "file"
-		input.style.display = "none"
-		input.onchange = async (e) => {
-			const file = e.target.files[0]
-			if (!file) {
-				resolve(null)
-				return
-			}
-			try {
-				const formData = new FormData()
-				formData.append("file", file)
-				formData.append("id", currentFileRow.value.id)
-				const uploadRes = await request({
-					url: "/afterSalesService/file/upload",
-					method: "post",
-					data: formData,
-					headers: {
-						"Content-Type": "multipart/form-data",
-						Authorization: `Bearer ${getToken()}`,
-					},
-				})
-				if (uploadRes.code === 200) {
-					proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛")
-					// 閲嶆柊鑾峰彇鏂囦欢鍒楄〃
-					const listRes = await afterSalesServiceFileListPage({
-						afterSalesServiceId: currentFileRow.value.id,
-						current: 1,
-						size: 100,
-					})
-					if (listRes.code === 200 && fileListRef.value) {
-						const fileList = (listRes.data?.records || []).map((item) => ({
-							name: item.fileName,
-							url: item.fileUrl,
-							id: item.id,
-							...item,
-						}))
-						fileListRef.value.setList(fileList)
-					}
-					resolve({ name: file.name, url: "", id: null })
-				} else {
-					proxy.$modal.msgError(uploadRes.msg || "鏂囦欢涓婁紶澶辫触")
-					resolve(null)
-				}
-			} catch (err) {
-				proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触")
-				resolve(null)
-			} finally {
-				document.body.removeChild(input)
-			}
-		}
-		document.body.appendChild(input)
-		input.click()
-	})
-}
-
-// 鍒犻櫎闄勪欢
-const handleFileDelete = async (row) => {
-	try {
-		// 娣诲姞纭瀵硅瘽妗�
-		const confirmResult = await ElMessageBox.confirm(
-			'纭畾瑕佸垹闄よ繖涓檮浠跺悧锛�',
-			'鍒犻櫎纭',
-			{
-				confirmButtonText: '纭畾',
-				cancelButtonText: '鍙栨秷',
-				type: 'warning'
-			}
-		)
-
-		if (confirmResult === 'confirm') {
-			const res = await afterSalesServiceFileDel(row.id)
-			if (res.code === 200) {
-				proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛")
-				if (currentFileRow.value && fileListRef.value) {
-					const listRes = await afterSalesServiceFileListPage({
-						afterSalesServiceId: currentFileRow.value.id,
-						current: 1,
-						size: 100,
-					})
-					if (listRes.code === 200) {
-						const fileList = (listRes.data?.records || []).map((item) => ({
-							name: item.fileName,
-							url: item.fileUrl,
-							id: item.id,
-							...item,
-						}))
-						fileListRef.value.setList(fileList)
-					}
-				}
-			} else {
-				proxy.$modal.msgError(res.msg || "鍒犻櫎澶辫触")
-				return false
-			}
-		}
-	} catch (error) {
-		// 濡傛灉鐢ㄦ埛鍙栨秷鍒犻櫎锛屼笉鏄剧ず閿欒淇℃伅
-		if (error !== 'cancel') {
-			proxy.$modal.msgError("鍒犻櫎澶辫触")
-		}
-		return false
-	}
+  recordId.value = row.id
+  fileDialogVisible.value = true
 }
 
 // 鏌ヨ鍒楄〃
diff --git a/src/views/customerService/expiryAfterSales/index.vue b/src/views/customerService/expiryAfterSales/index.vue
index ee5395d..c94e3dd 100644
--- a/src/views/customerService/expiryAfterSales/index.vue
+++ b/src/views/customerService/expiryAfterSales/index.vue
@@ -1,6 +1,6 @@
 <template>
 	<div class="app-container">
-		<div class="search_form">
+		<div class="search_form mb20">
 			<div>
 				<span class="search_title">涓存湡鏃ユ湡锛�</span>
 				<el-date-picker
@@ -34,11 +34,12 @@
 				>閲嶇疆</el-button
 				>
 			</div>
+			<div class="table_actions">
+				<el-button type="primary" @click="openForm('add')">鏂板</el-button>
+				<el-button type="danger" @click="handleDelete">鍒犻櫎</el-button>
+			</div>
 		</div>
-		<div class="table_actions" style="margin-bottom: 10px;">
-			<el-button type="primary" @click="openForm('add')">鏂板</el-button>
-			<el-button type="danger" @click="handleDelete">鍒犻櫎</el-button>
-		</div>
+		
 		<div class="table_list">
 			<PIMTable
 				rowKey="id"
diff --git a/src/views/customerService/feedbackRegistration/components/formDia.vue b/src/views/customerService/feedbackRegistration/components/formDia.vue
index 71cd167..5c9e565 100644
--- a/src/views/customerService/feedbackRegistration/components/formDia.vue
+++ b/src/views/customerService/feedbackRegistration/components/formDia.vue
@@ -79,9 +79,9 @@
               </el-form-item>
             </el-col>
             <el-col :span="4">
-              <el-form-item label="闂鎻忚堪锛�" prop="disRes">
+              <el-form-item label="闂鎻忚堪锛�" prop="proDesc">
                 <el-input
-                    v-model="form.disRes"
+                    v-model="form.proDesc"
                     placeholder="璇疯緭鍏ラ棶棰樻弿杩�"
                 />
               </el-form-item>
@@ -106,6 +106,11 @@
                 :column="tableColumn"
                 :tableData="tableData"
             >
+              <template #approveStatus="{ row }">
+                <el-tag :type="getApproveStatusType(row)" size="small">
+                  {{ getApproveStatusText(row) }}
+                </el-tag>
+              </template>
               <template #shippingStatus="{ row }">
                 <el-tag :type="getShippingStatusType(row)" size="small">
                   {{ getShippingStatusText(row) }}
@@ -155,7 +160,7 @@
     productModelIds: "",
     customerId: null,
     salesContractNo: "",
-    disRes: "",
+    proDesc: "",
     customerName: ""
 	},
 	rules: {
@@ -207,6 +212,7 @@
     taxInclusiveUnitPrice: row?.taxInclusiveUnitPrice ?? 0,
     taxInclusiveTotalPrice: row?.taxInclusiveTotalPrice ?? 0,
     taxExclusiveTotalPrice: row?.taxExclusiveTotalPrice ?? 0,
+    noQuantity: row?.noQuantity ?? 0,
   }
 }
 
@@ -219,9 +225,8 @@
     prop: "approveStatus",
     width: 100,
     align: "center",
-    dataType: "tag",
-    formatData: (v) => (v === 1 ? "鍏呰冻" : "涓嶈冻"),
-    formatType: (v) => (v === 1 ? "success" : "danger"),
+    dataType: "slot",
+    slot: "approveStatus",
   },
   {
     label: "鍙戣揣鐘舵��",
@@ -304,9 +309,15 @@
 })
 
 const customerNameChange = (val) => {
+  form.value.salesContractNo = "";
+  form.value.salesLedgerId = null;
+  tableData.value = [];
+  associatedSalesOrderNumberOptions.value = [];
   const opt = customerNameOptions.value.find(item => item.value === val);
   if (opt) {
     form.value.customerId = opt.id;
+  } else {
+    form.value.customerId = null;
   }
   getSalesLedger({
     customerName: form.value.customerName
@@ -320,6 +331,22 @@
       }))
     }
   })
+}
+
+const getApproveStatusText = (row) => {
+  if (!row) return '涓嶈冻'
+  if (row.approveStatus === 1 && (!row.shippingDate || !row.shippingCarNumber)) {
+    return '鍏呰冻'
+  }
+  if (row.approveStatus === 0 && (row.shippingDate || row.shippingCarNumber)) {
+    return '宸插嚭搴�'
+  }
+  return '涓嶈冻'
+}
+
+const getApproveStatusType = (row) => {
+  const statusText = getApproveStatusText(row)
+  return statusText === '涓嶈冻' ? 'danger' : 'success'
 }
 
 const getShippingStatusText = (row) => {
@@ -365,7 +392,11 @@
 // 鎵撳紑寮规
 const openDialog =async (type, row) => {
   // 璇锋眰澶氫釜鎺ュ彛锛岃幏鍙栨暟鎹�
-  let res = await getAllCustomerList();
+  let res = await getAllCustomerList({
+    current: 1,
+  size: 1000,
+  total: 0,
+  });
   if(res.records){
     customerNameOptions.value = res.records.map(item => ({
       label: item.customerName,
diff --git a/src/views/customerService/feedbackRegistration/index.vue b/src/views/customerService/feedbackRegistration/index.vue
index 3a2d362..40d99be 100644
--- a/src/views/customerService/feedbackRegistration/index.vue
+++ b/src/views/customerService/feedbackRegistration/index.vue
@@ -255,7 +255,7 @@
   },
   {
     label: "闂鎻忚堪",
-    prop: "disRes",
+    prop: "proDesc",
     width:300,
   },
   {
@@ -267,7 +267,6 @@
   {
     dataType: "action",
     label: "鎿嶄綔",
-    align: "center",
     fixed: 'right',
     operation: [
       {
@@ -404,15 +403,19 @@
       });
 };
 
+const getStatsCountByStatus = (list, status) => {
+  if (!Array.isArray(list)) return 0;
+  return list.find((item) => item?.status === status)?.count || 0;
+};
+
   // 鑾峰彇缁熻鏁版嵁骞跺埛鏂伴《閮ㄥ崱鐗�
   const getSalesLedgerDetails = () => {
     getSalesLedgerDetail({}).then((res) => {
       if (res.code === 200) {
-        statsList.value[0].count = res.data.filter((item) => item.status === 3)[0].count;
-        statsList.value[1].count = res.data.filter((item) => item.status === 2)[0].count;
-        statsList.value[2].count = res.data.filter((item) => item.status === 1)[0].count;
-
-        // });
+        const statsData = Array.isArray(res.data) ? res.data : [];
+        statsList.value[0].count = getStatsCountByStatus(statsData, 3);
+        statsList.value[1].count = getStatsCountByStatus(statsData, 2);
+        statsList.value[2].count = getStatsCountByStatus(statsData, 1);
       }
     });
   }
@@ -491,7 +494,6 @@
 
 .table_list {
   height: calc(100vh - 380px);
-  min-height: 360px;
   background: #fff;
   margin-top: 20px;
   display: flex;
diff --git a/src/views/energyManagement/dynamicEnergySaving/index.vue b/src/views/energyManagement/dynamicEnergySaving/index.vue
index b641276..8976b22 100644
--- a/src/views/energyManagement/dynamicEnergySaving/index.vue
+++ b/src/views/energyManagement/dynamicEnergySaving/index.vue
@@ -157,14 +157,12 @@
         <el-table-column prop="lastUpdate" label="鏈�鍚庢洿鏂�" />
         <el-table-column label="鎿嶄綔">
           <template #default="scope">
-            <el-button 
-              size="small" 
+            <el-button
               @click="updateModel(scope.row)"
             >
               鏇存柊妯″瀷
             </el-button>
-            <el-button 
-              size="small" 
+            <el-button
               type="danger" 
               @click="deleteModel(scope.row)"
             >
diff --git a/src/views/equipmentManagement/brand/index.vue b/src/views/equipmentManagement/brand/index.vue
index 6607cc8..f93518a 100644
--- a/src/views/equipmentManagement/brand/index.vue
+++ b/src/views/equipmentManagement/brand/index.vue
@@ -60,8 +60,8 @@
         </el-form-item>
       </el-form>
       <template #footer>
-        <el-button @click="visible = false">鍙栨秷</el-button>
         <el-button type="primary" @click="handleSubmit">纭畾</el-button>
+        <el-button @click="visible = false">鍙栨秷</el-button>
       </template>
     </el-dialog>
   </div>
diff --git a/src/views/equipmentManagement/calibration/index.vue b/src/views/equipmentManagement/calibration/index.vue
index e3eaef6..b04da2e 100644
--- a/src/views/equipmentManagement/calibration/index.vue
+++ b/src/views/equipmentManagement/calibration/index.vue
@@ -1,6 +1,6 @@
 <template>
 	<div class="app-container">
-		<div class="search_form">
+		<div class="search_form mb20">
 			<div>
 				<span class="search_title">妫�瀹氭棩鏈燂細</span>
 				<el-date-picker
diff --git a/src/views/equipmentManagement/defectManagement/index.vue b/src/views/equipmentManagement/defectManagement/index.vue
index f35454f..8673000 100644
--- a/src/views/equipmentManagement/defectManagement/index.vue
+++ b/src/views/equipmentManagement/defectManagement/index.vue
@@ -65,8 +65,8 @@
       </el-form>
       <template #footer>
         <span class="dialog-footer">
-          <el-button @click="showRegisterDialog = false">鍙栨秷</el-button>
           <el-button type="primary" @click="submitDefectForm">纭畾</el-button>
+          <el-button @click="showRegisterDialog = false">鍙栨秷</el-button>
         </span>
       </template>
     </el-dialog>
diff --git a/src/views/equipmentManagement/inspectionManagement/components/formDia.vue b/src/views/equipmentManagement/inspectionManagement/components/formDia.vue
index b048a9c..9f509b1 100644
--- a/src/views/equipmentManagement/inspectionManagement/components/formDia.vue
+++ b/src/views/equipmentManagement/inspectionManagement/components/formDia.vue
@@ -91,8 +91,8 @@
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button @click="cancel">鍙栨秷</el-button>
           <el-button type="primary" @click="submitForm">淇濆瓨</el-button>
+          <el-button @click="cancel">鍙栨秷</el-button>
         </div>
       </template>
     </el-dialog>
diff --git a/src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue b/src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue
index 27b4a59..66867e3 100644
--- a/src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue
+++ b/src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue
@@ -134,40 +134,6 @@
 const currentMediaIndex = ref(0);
 const mediaList = ref([]); // 瀛樺偍褰撳墠瑕佹煡鐪嬬殑濯掍綋鍒楄〃锛堝惈鍥剧墖鍜岃棰戝璞★級
 const mediaType = ref('image'); // image | video
-const javaApi = proxy.javaApi;
-
-// 澶勭悊 URL锛氬皢 Windows 璺緞杞崲涓哄彲璁块棶鐨� URL
-function processFileUrl(fileUrl) {
-  if (!fileUrl) return '';
-  
-  // 濡傛灉 URL 鏄� Windows 璺緞鏍煎紡锛堝寘鍚弽鏂滄潬锛夛紝闇�瑕佽浆鎹�
-  if (fileUrl && fileUrl.indexOf('\\') > -1) {
-    // 鏌ユ壘 uploads 鍏抽敭瀛楃殑浣嶇疆锛屼粠閭i噷寮�濮嬫彁鍙栫浉瀵硅矾寰�
-    const uploadsIndex = fileUrl.toLowerCase().indexOf('uploads');
-    if (uploadsIndex > -1) {
-      // 浠� uploads 寮�濮嬫彁鍙栬矾寰勶紝骞跺皢鍙嶆枩鏉犳浛鎹负姝f枩鏉�
-      const relativePath = fileUrl.substring(uploadsIndex).replace(/\\/g, '/');
-      fileUrl = '/' + relativePath;
-    } else {
-      // 濡傛灉娌℃湁鎵惧埌 uploads锛屾彁鍙栨渶鍚庝竴涓洰褰曞拰鏂囦欢鍚�
-      const parts = fileUrl.split('\\');
-      const fileName = parts[parts.length - 1];
-      fileUrl = '/uploads/' + fileName;
-    }
-  }
-  
-  // 纭繚鎵�鏈夐潪 http 寮�澶寸殑 URL 閮芥嫾鎺� baseUrl
-  if (fileUrl && !fileUrl.startsWith('http')) {
-    // 纭繚璺緞浠� / 寮�澶�
-    if (!fileUrl.startsWith('/')) {
-      fileUrl = '/' + fileUrl;
-    }
-    // 鎷兼帴 baseUrl
-    fileUrl = javaApi + fileUrl;
-  }
-  
-  return fileUrl;
-}
 
 // 澶勭悊姣忎竴绫绘暟鎹細鍒嗙鍥剧墖鍜岃棰�
 function processItems(items) {
@@ -180,24 +146,18 @@
   }
   
   items.forEach(item => {
-    if (!item || !item.url) return;
+    if (!item || !item.previewURL || !item.contentType) return;
+
     
     // 澶勭悊鏂囦欢 URL
-    const fileUrl = processFileUrl(item.url);
-    
-    // 鏍规嵁鏂囦欢鎵╁睍鍚嶅垽鏂槸鍥剧墖杩樻槸瑙嗛
-    const urlLower = fileUrl.toLowerCase();
-    if (urlLower.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/)) {
+    const fileUrl = item.previewURL;
+    const contentType = String(item.contentType).toLowerCase();
+
+    // 鏍规嵁 contentType 鍒ゆ柇鏄浘鐗囪繕鏄棰�
+    if (contentType.startsWith('image/')) {
       images.push(fileUrl);
-    } else if (urlLower.match(/\.(mp4|avi|mov|wmv|flv|mkv|webm)$/)) {
+    } else if (contentType.startsWith('video/')) {
       videos.push(fileUrl);
-    } else if (item.contentType) {
-      // 濡傛灉鏈� contentType锛屼娇鐢� contentType 鍒ゆ柇
-      if (item.contentType.startsWith('image/')) {
-        images.push(fileUrl);
-      } else if (item.contentType.startsWith('video/')) {
-        videos.push(fileUrl);
-      }
     }
   });
   
@@ -207,10 +167,9 @@
 // 鎵撳紑寮圭獥骞跺姞杞芥暟鎹�
 const openDialog = async (row) => {
   // 浣跨敤姝g‘鐨勫瓧娈靛悕锛歝ommonFileListBefore, commonFileListAfter
-  // productionIssues 鍙兘涓嶅瓨鍦紝浣跨敤绌烘暟缁�
-  const { images: beforeImgs, videos: beforeVids } = processItems(row.commonFileListBefore || []);
-  const { images: afterImgs, videos: afterVids } = processItems(row.commonFileListAfter || []);
-  const { images: issueImgs, videos: issueVids } = processItems(row.productionIssues || []);
+  const { images: beforeImgs, videos: beforeVids } = processItems(row.commonFileListBeforeVO || []);
+  const { images: afterImgs, videos: afterVids } = processItems(row.commonFileListAfterVO || []);
+  const { images: issueImgs, videos: issueVids } = processItems(row.commonFileListVO || []);
   
   beforeProductionImgs.value = beforeImgs;
   beforeProductionVideos.value = beforeVids;
diff --git a/src/views/equipmentManagement/ledger/Form.vue b/src/views/equipmentManagement/ledger/Form.vue
index 72d594e..45ca248 100644
--- a/src/views/equipmentManagement/ledger/Form.vue
+++ b/src/views/equipmentManagement/ledger/Form.vue
@@ -100,22 +100,18 @@
       </el-col>
       <el-col :span="12">
         <el-form-item label="绋庣巼(%)" prop="taxRate">
-          <!-- <el-input
-            v-model="form.taxRate"
-            placeholder="璇疯緭鍏ョ◣鐜�"
-            type="number"
-          >
-            <template #append> % </template>
-          </el-input> -->
           <el-select
             v-model="form.taxRate"
             placeholder="璇烽�夋嫨"
             clearable
             @change="mathNum"
           >
-            <el-option label="1" :value="1" />
-            <el-option label="6" :value="6" />
-            <el-option label="13" :value="13" />
+            <el-option
+              v-for="dict in tax_rate"
+              :key="dict.value"
+              :label="dict.label"
+              :value="Number(dict.value)"
+            />
           </el-select>
         </el-form-item>
       </el-col>
@@ -174,7 +170,10 @@
   calculateTaxExclusiveTotalPrice,
 } from "@/utils/summarizeTable";
 import { ElMessage } from "element-plus";
-import {ref} from "vue";
+import {ref, getCurrentInstance} from "vue";
+
+const { proxy } = getCurrentInstance();
+const { tax_rate } = proxy.useDict("tax_rate");
 
 defineOptions({
   name: "璁惧鍙拌处琛ㄥ崟",
diff --git a/src/views/equipmentManagement/ledger/index.vue b/src/views/equipmentManagement/ledger/index.vue
index 62f0c6a..7ba9401 100644
--- a/src/views/equipmentManagement/ledger/index.vue
+++ b/src/views/equipmentManagement/ledger/index.vue
@@ -4,7 +4,7 @@
       <el-form-item label="璁惧鍚嶇О">
         <el-input
           v-model="filters.deviceName"
-          style="width: 240px"
+          style="width: 200px"
           placeholder="璇疯緭鍏ヨ澶囧悕绉�"
           clearable
           @change="getTableData"
@@ -13,7 +13,7 @@
       <el-form-item label="瑙勬牸鍨嬪彿">
         <el-input
             v-model="filters.deviceModel"
-            style="width: 240px"
+            style="width: 200px"
             placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
             clearable
             @change="getTableData"
@@ -22,7 +22,7 @@
       <el-form-item label="渚涘簲鍟�">
         <el-input
             v-model="filters.supplierName"
-            style="width: 240px"
+            style="width: 200px"
             placeholder="璇疯緭鍏ヤ緵搴斿晢"
             clearable
             @change="getTableData"
@@ -42,6 +42,7 @@
         <div></div>
         <div>
           <el-button type="primary" @click="add" icon="Plus"> 鏂板 </el-button>
+          <el-button type="info" @click="handleImport" icon="Upload">瀵煎叆</el-button>
           <el-button @click="handleOut" icon="download">瀵煎嚭</el-button>
           <el-button
             type="danger"
@@ -77,6 +78,37 @@
         </div>
       </div>
     </el-dialog>
+    
+    <!-- 瀵煎叆瀵硅瘽妗� -->
+    <el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
+      <el-upload
+        ref="uploadRef"
+        :limit="1"
+        accept=".xlsx, .xls"
+        :headers="upload.headers"
+        :action="upload.url"
+        :disabled="upload.isUploading"
+        :on-progress="handleFileUploadProgress"
+        :on-success="handleFileSuccess"
+        :auto-upload="false"
+        drag
+      >
+        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+        <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+        <template #tip>
+          <div class="el-upload__tip text-center">
+            <span>浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�</span>
+            <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline; margin-left: 5px;" @click="importTemplate">涓嬭浇妯℃澘</el-link>
+          </div>
+        </template>
+      </el-upload>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button>
+          <el-button @click="upload.open = false">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -84,12 +116,13 @@
 import { usePaginationApi } from "@/hooks/usePaginationApi";
 // import { Search } from "@element-plus/icons-vue";
 import { getLedgerPage, delLedger } from "@/api/equipmentManagement/ledger";
-import { onMounted, getCurrentInstance } from "vue";
+import { onMounted, getCurrentInstance, ref, reactive } from "vue";
 import Modal from "./Modal.vue";
 import { ElMessageBox, ElMessage } from "element-plus";
+import { UploadFilled } from "@element-plus/icons-vue";
+import { getToken } from "@/utils/auth";
 import dayjs from "dayjs";
 import QRCode from "qrcode";
-import { ref } from "vue";
 
 defineOptions({
   name: "璁惧鍙拌处",
@@ -102,6 +135,21 @@
 const qrDialogVisible = ref(false);
 const qrCodeUrl = ref("");
 const qrRowData = ref(null);
+
+// 瀵煎叆鐩稿叧
+const uploadRef = ref(null)
+const upload = reactive({
+  // 鏄惁鏄剧ず寮瑰嚭灞�
+  open: false,
+  // 寮瑰嚭灞傛爣棰�
+  title: "",
+  // 鏄惁绂佺敤涓婁紶
+  isUploading: false,
+  // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+  headers: { Authorization: "Bearer " + getToken() },
+  // 涓婁紶鐨勫湴鍧�
+  url: import.meta.env.VITE_APP_BASE_API + "/device/ledger/import"
+})
 
 const {
   filters,
@@ -262,6 +310,36 @@
   a.click();
 };
 
+// 瀵煎叆鎸夐挳鎿嶄綔
+const handleImport = () => {
+  upload.title = "璁惧鍙拌处瀵煎叆"
+  upload.open = true
+}
+
+// 涓嬭浇妯℃澘鎿嶄綔
+const importTemplate = () => {
+  proxy.download("/device/ledger/downloadTemplate", {}, `璁惧鍙拌处瀵煎叆妯℃澘_${new Date().getTime()}.xlsx`)
+}
+
+// 鏂囦欢涓婁紶涓鐞�
+const handleFileUploadProgress = (event, file, fileList) => {
+  upload.isUploading = true
+}
+
+// 鏂囦欢涓婁紶鎴愬姛澶勭悊
+const handleFileSuccess = (response, file, fileList) => {
+  upload.open = false
+  upload.isUploading = false
+  proxy.$refs["uploadRef"].handleRemove(file)
+  proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "瀵煎叆缁撴灉", { dangerouslyUseHTMLString: true })
+  getTableData()
+}
+
+// 鎻愪氦涓婁紶鏂囦欢
+const submitFileForm = () => {
+  proxy.$refs["uploadRef"].submit()
+}
+
 onMounted(() => {
   getTableData();
 });
diff --git a/src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue b/src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
index b7fa07e..923dd2c 100644
--- a/src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
+++ b/src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
@@ -228,39 +228,6 @@
 	form.value.entryDate = getCurrentDate();
 }
 
-// 涓婁紶鍓嶆牎妫�
-function handleBeforeUpload(file) {
-	proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
-	return true;
-}
-// 涓婁紶澶辫触
-function handleUploadError(err) {
-	proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
-	proxy.$modal.closeLoading();
-}
-// 涓婁紶鎴愬姛鍥炶皟
-function handleUploadSuccess(res, file, uploadFiles) {
-	proxy.$modal.closeLoading();
-	if (res.code === 200) {
-		file.tempId = res.data.tempId;
-		form.value.tempFileIds.push(file.tempId);
-		proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
-	} else {
-		proxy.$modal.msgError(res.msg);
-		proxy.$refs.fileUpload.handleRemove(file);
-	}
-}
-// 绉婚櫎鏂囦欢
-function handleRemove(file) {
-	if (operationType.value === "edit") {
-		let ids = [];
-		ids.push(file.id);
-		delLedgerFile(ids).then((res) => {
-			proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-		});
-	}
-}
-
 // 澶勭悊鏈夋晥鏃ユ湡杈撳叆锛屽彧鍏佽姝f暣鏁�
 const handleValidInput = (value) => {
 	if (value === '' || value === null || value === undefined) {
diff --git a/src/views/equipmentManagement/measurementEquipment/index.vue b/src/views/equipmentManagement/measurementEquipment/index.vue
index 46ca100..c1d5379 100644
--- a/src/views/equipmentManagement/measurementEquipment/index.vue
+++ b/src/views/equipmentManagement/measurementEquipment/index.vue
@@ -1,6 +1,6 @@
 <template>
 	<div class="app-container">
-		<div class="search_form">
+		<div class="search_form mb20">
 			<div>
 				<span class="search_title">褰曞叆鏃ユ湡锛�</span>
 				<el-date-picker
diff --git a/src/views/equipmentManagement/repair/Modal/MaintainModal.vue b/src/views/equipmentManagement/repair/Modal/MaintainModal.vue
index 496b072..b0b09f0 100644
--- a/src/views/equipmentManagement/repair/Modal/MaintainModal.vue
+++ b/src/views/equipmentManagement/repair/Modal/MaintainModal.vue
@@ -32,23 +32,61 @@
           style="width: 100%"
         />
       </el-form-item>
+      <el-form-item label="璁惧澶囦欢">
+        <el-select v-model="form.sparePartsIds" :loading="loadingSparePartOptions" placeholder="璇烽�夋嫨璁惧澶囦欢" multiple filterable>
+          <el-option
+              v-for="item in sparePartOptions"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+
+      <el-form-item v-if="selectedSpareParts.length" label="棰嗙敤鏁伴噺">
+        <div style="width: 100%">
+          <div
+            v-for="item in selectedSpareParts"
+            :key="item.id"
+            style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;"
+          >
+            <div style="flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
+              {{ item.name }}
+              <span v-if="item.quantity !== null && item.quantity !== undefined" style="color: #909399;">
+                锛堝簱瀛橈細{{ item.quantity }}锛�
+              </span>
+            </div>
+            <el-input-number
+              v-model="sparePartQtyMap[item.id]"
+              :min="1"
+              :max="item.quantity !== null && item.quantity !== undefined ? Number(item.quantity) : undefined"
+              :step="1"
+              controls-position="right"
+              style="width: 180px"
+            />
+          </div>
+        </div>
+      </el-form-item>
     </el-form>
   </FormDialog>
 </template>
 
 <script setup>
+import { computed, getCurrentInstance, nextTick, ref } from "vue";
 import FormDialog from "@/components/Dialog/FormDialog.vue";
 import { addMaintain } from "@/api/equipmentManagement/repair";
 import useFormData from "@/hooks/useFormData";
 import useUserStore from "@/store/modules/user";
 import dayjs from "dayjs";
 import { ElMessage } from "element-plus";
+import { getSparePartsList } from "@/api/equipmentManagement/spareParts";
 
 defineOptions({
   name: "缁翠慨妯℃�佹",
 });
 
 const emits = defineEmits(["ok"]);
+const { proxy } = getCurrentInstance();
 
 // 淇濆瓨鎶ヤ慨璁板綍鐨刬d
 const repairId = ref();
@@ -61,6 +99,16 @@
   maintenanceResult: undefined, // 缁翠慨缁撴灉
   maintenanceTime: undefined, // 缁翠慨鏃ユ湡
   status: 0,
+  sparePartsIds: [],
+});
+const sparePartOptions = ref([])
+const loadingSparePartOptions = ref(true)
+const sparePartQtyMap = ref({})
+
+const selectedSpareParts = computed(() => {
+  const ids = Array.isArray(form.sparePartsIds) ? form.sparePartsIds : [];
+  const set = new Set(ids.map((i) => String(i)));
+  return (sparePartOptions.value || []).filter((p) => set.has(String(p.id)));
 });
 
 const setForm = (data) => {
@@ -71,16 +119,59 @@
       ? dayjs(data.maintenanceTime).format("YYYY-MM-DD HH:mm:ss")
       : dayjs().format("YYYY-MM-DD HH:mm:ss");
   form.status = 1; // 榛樿鐘舵�佷负瀹岀粨
+  // multiple 閫夋嫨鍣ㄨ姹傛暟缁勶紱鍚庣甯歌繑鍥� "1,2,3"
+  if (Array.isArray(data?.sparePartsIds)) {
+    form.sparePartsIds = data.sparePartsIds.map((v) => Number(v)).filter((v) => Number.isFinite(v));
+  } else if (typeof data?.sparePartsIds === "string") {
+    form.sparePartsIds = data.sparePartsIds
+      .split(",")
+      .map((s) => Number(String(s).trim()))
+      .filter((v) => Number.isFinite(v));
+  } else if (typeof data?.sparePartsIds === "number") {
+    form.sparePartsIds = [data.sparePartsIds];
+  } else {
+    form.sparePartsIds = [];
+  }
 };
 
 const sendForm = async () => {
   loading.value = true;
   try {
-    const { code } = await addMaintain({ id: repairId.value, ...form });
+    // 棰嗙敤鏁伴噺鏍¢獙
+    if (Array.isArray(form.sparePartsIds) && form.sparePartsIds.length > 0) {
+      for (const partId of form.sparePartsIds) {
+        const qty = Number(sparePartQtyMap.value?.[partId]);
+        if (!Number.isFinite(qty) || qty <= 0) {
+          proxy?.$modal?.msgError?.("璇峰~鍐欏浠堕鐢ㄦ暟閲�");
+          return;
+        }
+        const part = sparePartOptions.value.find((p) => String(p.id) === String(partId));
+        const stock = part?.quantity;
+        if (stock !== null && stock !== undefined && Number.isFinite(Number(stock))) {
+          if (qty > Number(stock)) {
+            proxy?.$modal?.msgError?.(`澶囦欢銆�${part?.name || ""}銆嶉鐢ㄦ暟閲忎笉鑳借秴杩囧簱瀛橈紙${stock}锛塦);
+            return;
+          }
+        }
+      }
+    }
+    const data = {
+      id: repairId.value,
+      ...form,
+      sparePartsIds: form.sparePartsIds ? form.sparePartsIds.join(",") : "",
+      sparePartsQty: form.sparePartsIds
+        ? form.sparePartsIds.map((id) => sparePartQtyMap.value?.[id] ?? 1).join(",")
+        : "",
+      sparePartsUseList: form.sparePartsIds
+        ? form.sparePartsIds.map((id) => ({ id, quantity: sparePartQtyMap.value?.[id] ?? 1 }))
+        : [],
+    }
+    const { code } = await addMaintain(data);
     if (code == 200) {
       ElMessage.success("缁翠慨鎴愬姛");
       emits("ok");
       resetForm();
+      sparePartQtyMap.value = {};
       visible.value = false;
     }
   } finally {
@@ -88,13 +179,34 @@
   }
 };
 
+const fetchSparePartOptions = () => {
+  loadingSparePartOptions.value = true;
+  // 鍜屽浠剁鐞嗛〉涓�鑷达細/spareParts/listPage 鈫� res.data.records
+  getSparePartsList({ current: 1, size: 1000 })
+    .then((res) => {
+      if (res.code === 200) {
+        sparePartOptions.value = res?.data?.records || [];
+      } else {
+        sparePartOptions.value = [];
+      }
+    })
+    .catch(() => {
+      sparePartOptions.value = [];
+    })
+    .finally(() => {
+      loadingSparePartOptions.value = false;
+    });
+}
+
 const handleCancel = () => {
   resetForm();
+  sparePartQtyMap.value = {};
   visible.value = false;
 };
 
 const handleClose = () => {
   resetForm();
+  sparePartQtyMap.value = {};
   visible.value = false;
 };
 
@@ -103,6 +215,7 @@
   visible.value = true;
   await nextTick();
   setForm(row);
+  fetchSparePartOptions()
 };
 
 defineExpose({
diff --git a/src/views/equipmentManagement/repair/Modal/RepairModal.vue b/src/views/equipmentManagement/repair/Modal/RepairModal.vue
index 1aa82ec..5e31943 100644
--- a/src/views/equipmentManagement/repair/Modal/RepairModal.vue
+++ b/src/views/equipmentManagement/repair/Modal/RepairModal.vue
@@ -48,6 +48,11 @@
             <el-input v-model="form.repairName" placeholder="璇疯緭鍏ユ姤淇汉" />
           </el-form-item>
         </el-col>
+        <el-col :span="12">
+          <el-form-item label="椤圭洰">
+            <el-input v-model="form.machineryCategory" placeholder="璇疯緭鍏ラ」鐩�" />
+          </el-form-item>
+        </el-col>
       </el-row>
       <el-row v-if="id">
         <el-col :span="12">
@@ -72,12 +77,20 @@
           </el-form-item>
         </el-col>
       </el-row>
+      <el-row :gutter="30">
+        <el-col :span="24">
+          <el-form-item label="闄勪欢" prop="attachmentIds">
+            <FileUpload v-model:file-list="form.storageBlobDTOs" />
+          </el-form-item>
+        </el-col>
+      </el-row>
     </el-form>
   </FormDialog>
 </template>
 
 <script setup>
 import FormDialog from "@/components/Dialog/FormDialog.vue";
+import FileUpload from "@/components/AttachmentUpload/file/index.vue";
 import {
   addRepair,
   editRepair,
@@ -101,6 +114,7 @@
 
 const userStore = useUserStore();
 const deviceOptions = ref([]);
+const fileList = ref([]);
 
 const loadDeviceName = async () => {
   const { data } = await getDeviceLedger();
@@ -115,6 +129,8 @@
   repairName: userStore.nickName, // 鎶ヤ慨浜�
   remark: undefined, // 鏁呴殰鐜拌薄
   status: 0, // 鎶ヤ慨鐘舵��
+  machineryCategory: undefined,
+  storageBlobDTOs: [],
 });
 
 const setDeviceModel = (deviceId) => {
@@ -130,6 +146,8 @@
   form.repairName = data.repairName;
   form.remark = data.remark;
   form.status = data.status;
+  form.machineryCategory = data.machineryCategory;
+  form.storageBlobDTOs = data.storageBlobVOs || [];
 };
 
 const sendForm = async () => {
@@ -161,6 +179,7 @@
 const openAdd = async () => {
   id.value = undefined;
   visible.value = true;
+  fileList.value = [];
   await nextTick();
   await loadDeviceName();
 };
diff --git a/src/views/equipmentManagement/repair/index.vue b/src/views/equipmentManagement/repair/index.vue
index 1e7af53..f3a4330 100644
--- a/src/views/equipmentManagement/repair/index.vue
+++ b/src/views/equipmentManagement/repair/index.vue
@@ -127,22 +127,31 @@
           >
             鍒犻櫎
           </el-button>
+          <el-button
+              type="primary"
+              link
+              @click="openFileDialog(row)"
+          >
+            闄勪欢
+          </el-button>
         </template>
       </PIMTable>
     </div>
     <RepairModal ref="repairModalRef" @ok="getTableData"/>
     <MaintainModal ref="maintainModalRef" @ok="getTableData"/>
+    <FileList v-if="fileDialogVisible"  v-model:visible="fileDialogVisible" :record-type="'device_repair'" :record-id="recordId"  />
   </div>
 </template>
 
 <script setup>
-import { onMounted, getCurrentInstance, computed } from "vue";
+import {onMounted, getCurrentInstance, computed, ref, defineAsyncComponent} from "vue";
 import {usePaginationApi} from "@/hooks/usePaginationApi";
 import {getRepairPage, delRepair} from "@/api/equipmentManagement/repair";
 import RepairModal from "./Modal/RepairModal.vue";
 import {ElMessageBox, ElMessage} from "element-plus";
 import dayjs from "dayjs";
 import MaintainModal from "./Modal/MaintainModal.vue";
+const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
 
 defineOptions({
   name: "璁惧鎶ヤ慨",
@@ -186,6 +195,11 @@
         label: "瑙勬牸鍨嬪彿",
         align: "center",
         prop: "deviceModel",
+      },
+      {
+        label: "椤圭洰",
+        align: "center",
+        prop: "machineryCategory",
       },
       {
         label: "鎶ヤ慨鏃ユ湡",
@@ -253,6 +267,15 @@
   getTableData();
 };
 
+// 鎵撳紑闄勪欢寮圭獥
+const recordId =ref(0)
+const fileDialogVisible = ref(false)
+
+const openFileDialog = async (row) => {
+  recordId.value = row.id
+  fileDialogVisible.value = true
+}
+
 // 澶氶�夊悗鍋氫粈涔�
 const handleSelectionChange = (selectionList) => {
   multipleList.value = selectionList;
diff --git a/src/views/equipmentManagement/spareParts/index.vue b/src/views/equipmentManagement/spareParts/index.vue
index 4a48d28..8abe35d 100644
--- a/src/views/equipmentManagement/spareParts/index.vue
+++ b/src/views/equipmentManagement/spareParts/index.vue
@@ -1,107 +1,146 @@
 <template>
   <div class="spare-part-category">
-		<div class="search_form">
-			<el-form :inline="true" :model="queryParams" class="search-form">
-				<el-form-item label="澶囦欢鍚嶇О">
-					<el-input
-						v-model="queryParams.name"
-						placeholder="璇疯緭鍏ュ浠跺悕绉�"
-						clearable
-						style="width: 240px"
-					/>
-				</el-form-item>
-				<el-form-item>
-					<el-button type="primary" @click="handleQuery">鏌ヨ</el-button>
-					<el-button @click="resetQuery">閲嶇疆</el-button>
-				</el-form-item>
-			</el-form>
-			<div>
-				<el-button type="primary" @click="addCategory" >鏂板</el-button>
-			</div>
-		</div>
+    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
+      <el-tab-pane label="澶囦欢鍒楄〃" name="list">
+        <div class="search_form">
+          <el-form :inline="true" :model="queryParams" class="search-form">
+            <el-form-item label="澶囦欢鍚嶇О">
+              <el-input
+                v-model="queryParams.name"
+                placeholder="璇疯緭鍏ュ浠跺悕绉�"
+                clearable
+                style="width: 240px"
+              />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" @click="handleQuery">鏌ヨ</el-button>
+              <el-button @click="resetQuery">閲嶇疆</el-button>
+            </el-form-item>
+          </el-form>
+          <div>
+            <el-button type="primary" @click="addCategory">鏂板</el-button>
+          </div>
+        </div>
+				<div class="table_list">
+					<PIMTable
+						rowKey="id"
+						:column="columns"
+						:tableData="renderTableData"
+						:tableLoading="loading"
+						:page="pagination"
+						:isShowPagination="true"
+						@pagination="handleSizeChange"
+					>
+						<template #status="{ row }">
+							<el-tag type="success" size="small">{{ row.status }}</el-tag>
+						</template>
+					</PIMTable>
+				</div>
 
-    <PIMTable
-        rowKey="id"
-        :column="columns"
-        :tableData="renderTableData"
-        :tableLoading="loading"
-        :page="pagination"
-        :isShowPagination="true"
-        @pagination="handleSizeChange"
-    >
-      <template #status="{ row }">
-        <el-tag type="success" size="small">{{ row.status }}</el-tag>
-      </template>
-    </PIMTable>
-    
-    <el-dialog title="鍒嗙被绠$悊" v-model="dialogVisible" width="60%">
-      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
-        <el-form-item label="璁惧" prop="deviceLedgerIds">
-          <el-select
-            v-model="form.deviceLedgerIds"
-            placeholder="璇烽�夋嫨璁惧"
-            filterable
-            default-first-option
-            :reserve-keyword="false"
-            multiple
-            style="width: 100%"
-          >
-            <el-option
-              v-for="(item, index) in deviceOptions"
-              :key="index"
-              :label="item.deviceName"
-              :value="item.id"
-            ></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="澶囦欢鍚嶇О" prop="name">
-          <el-input v-model="form.name"></el-input>
-        </el-form-item>
-        <el-form-item label="澶囦欢缂栧彿" prop="sparePartsNo">
-          <el-input v-model="form.sparePartsNo"></el-input>
-        </el-form-item>
-        <el-form-item label="鏁伴噺" prop="quantity">
-          <el-input type="number" v-model="form.quantity"></el-input>
-        </el-form-item>
-        <el-form-item label="鐘舵��" prop="status">
-          <el-select v-model="form.status" placeholder="璇烽�夋嫨鐘舵��">
-            <el-option label="姝e父" value="姝e父"></el-option>
-            <el-option label="绂佺敤" value="绂佺敤"></el-option>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="鎻忚堪" prop="description">
-          <el-input v-model="form.description"></el-input>
-        </el-form-item>
-        <el-form-item label="浠锋牸" prop="price">
-          <el-input-number
-            v-model="form.price"
-            placeholder="璇疯緭鍏ヤ环鏍�"
-            :min="0"
-            :step="0.01"
-            :precision="2"
-            style="width: 100%"
-          ></el-input-number>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="dialogVisible = false" :disabled="formLoading">鍙栨秷</el-button>
-          <el-button type="primary" @click="submitForm" :loading="formLoading">纭畾</el-button>
-        </span>
-      </template>
-    </el-dialog>
+        <el-dialog title="鍒嗙被绠$悊" v-model="dialogVisible" width="60%">
+          <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
+            <el-form-item label="璁惧" prop="deviceLedgerIds">
+              <el-select
+                v-model="form.deviceLedgerIds"
+                placeholder="璇烽�夋嫨璁惧"
+                filterable
+                default-first-option
+                :reserve-keyword="false"
+                multiple
+                style="width: 100%"
+              >
+                <el-option
+                  v-for="(item, index) in deviceOptions"
+                  :key="index"
+                  :label="item.deviceName"
+                  :value="item.id"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="澶囦欢鍚嶇О" prop="name">
+              <el-input v-model="form.name"></el-input>
+            </el-form-item>
+            <el-form-item label="澶囦欢缂栧彿" prop="sparePartsNo">
+              <el-input v-model="form.sparePartsNo"></el-input>
+            </el-form-item>
+            <el-form-item label="鏁伴噺" prop="quantity">
+              <el-input type="number" v-model="form.quantity"></el-input>
+            </el-form-item>
+            <el-form-item label="鐘舵��" prop="status">
+              <el-select v-model="form.status" placeholder="璇烽�夋嫨鐘舵��">
+                <el-option label="姝e父" value="姝e父"></el-option>
+                <el-option label="绂佺敤" value="绂佺敤"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="鎻忚堪" prop="description">
+              <el-input v-model="form.description"></el-input>
+            </el-form-item>
+            <el-form-item label="浠锋牸" prop="price">
+              <el-input-number
+                v-model="form.price"
+                placeholder="璇疯緭鍏ヤ环鏍�"
+                :min="0"
+                :step="0.01"
+                :precision="2"
+                style="width: 100%"
+              ></el-input-number>
+            </el-form-item>
+          </el-form>
+          <template #footer>
+            <span class="dialog-footer">
+              <el-button type="primary" @click="submitForm" :loading="formLoading">纭畾</el-button>
+              <el-button @click="dialogVisible = false" :disabled="formLoading">鍙栨秷</el-button>
+            </span>
+          </template>
+        </el-dialog>
+      </el-tab-pane>
+
+      <el-tab-pane label="澶囦欢棰嗙敤璁板綍" name="usage">
+        <div class="search_form">
+          <el-form :inline="true" :model="usageQuery" class="search-form">
+            <el-form-item label="澶囦欢鍚嶇О">
+              <el-input v-model="usageQuery.sparePartsName" placeholder="璇疯緭鍏ュ浠跺悕绉�" clearable style="width: 240px" />
+            </el-form-item>
+            <el-form-item label="鏉ユ簮">
+              <el-select v-model="usageQuery.sourceType" placeholder="璇烽�夋嫨" clearable style="width: 200px">
+                <el-option label="缁翠慨" :value="0" />
+                <el-option label="淇濆吇" :value="1" />
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" @click="handleUsageQuery">鏌ヨ</el-button>
+              <el-button @click="resetUsageQuery">閲嶇疆</el-button>
+            </el-form-item>
+          </el-form>
+        </div>
+				<div class="table_list">
+					<PIMTable
+						rowKey="rowKey"
+						:column="usageColumns"
+						:tableData="usageTableData"
+						:tableLoading="usageLoading"
+						:page="usagePagination"
+						:isShowPagination="true"
+						@pagination="handleUsagePageChange"
+					/>
+				</div>
+      </el-tab-pane>
+    </el-tabs>
   </div>
 </template>
 
 <script setup>
-import { ref, computed, onMounted, reactive, watch } from 'vue';
+import { ref, computed, onMounted, reactive } from 'vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
 import { getSparePartsList, addSparePart, editSparePart, delSparePart } from "@/api/equipmentManagement/spareParts";
 import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
+import { getSparePartsUsagePage } from "@/api/equipmentManagement/sparePartsUsage";
 
 // 鍔犺浇鐘舵��
 const loading = ref(false);
 const formLoading = ref(false);
+const activeTab = ref("list");
 // 瀵硅瘽妗嗘樉绀虹姸鎬�
 const dialogVisible = ref(false);
 // 缂栬緫 ID
@@ -126,6 +165,35 @@
   size: 10,
   total: 0
 });
+
+// 澶囦欢棰嗙敤璁板綍
+const usageLoading = ref(false);
+const usageQuery = reactive({
+  sparePartsName: "",
+  sourceType: "",
+});
+const usagePagination = reactive({
+  current: 1,
+  size: 10,
+  total: 0,
+});
+const usageTableData = ref([]);
+const usageColumns = ref([
+  { label: "鏉ユ簮", prop: "sourceText" },
+  { label: "鍗曟嵁/璁板綍ID", prop: "sourceId" },
+  { label: "璁惧鍚嶇О", prop: "deviceName" },
+  { label: "澶囦欢鍚嶇О", prop: "sparePartsName" },
+  { label: "棰嗙敤鏁伴噺", prop: "quantity" },
+  { label: "鎿嶄綔浜�", prop: "operator" },
+  { label: "鏃堕棿", prop: "createTime" },
+]);
+
+const handleTabChange = async (name) => {
+  if (name === "usage") {
+    usagePagination.current = 1;
+    await fetchUsageData();
+  }
+};
 const columns = ref([
   {
     label: "璁惧鍚嶇О",
@@ -267,6 +335,48 @@
     loading.value = false;
   }
 }
+
+const fetchUsageData = async () => {
+  usageLoading.value = true;
+  try {
+    const res = await getSparePartsUsagePage({
+      current: usagePagination.current,
+      size: usagePagination.size,
+      sparePartsName: usageQuery.sparePartsName || undefined,
+      sourceType: usageQuery.sourceType || undefined,
+    });
+    if (res?.code === 200) {
+      const records = res?.data?.records || [];
+      usagePagination.total = res?.data?.total || 0;
+      usageTableData.value = records.map((r, idx) => ({
+        rowKey: r.id ?? `${usagePagination.current}-${idx}`,
+        ...r,
+        sourceText: r.sourceText === "" ? "-" : r.sourceText,
+      }));
+    } else {
+      usagePagination.total = 0;
+      usageTableData.value = [];
+    }
+  } finally {
+    usageLoading.value = false;
+  }
+};
+
+const handleUsageQuery = () => {
+  usagePagination.current = 1;
+  fetchUsageData();
+};
+const resetUsageQuery = () => {
+  usageQuery.sparePartsName = "";
+  usageQuery.sourceType = "";
+  usagePagination.current = 1;
+  fetchUsageData();
+};
+const handleUsagePageChange = (obj) => {
+  usagePagination.current = obj.page;
+  usagePagination.size = obj.limit;
+  fetchUsageData();
+};
 
 // 鏌ヨ
 const handleQuery = () => {
@@ -430,7 +540,6 @@
   margin-top: 20px;
   display: flex;
   justify-content: flex-end;
-  padding: 16px 0;
 }
 
 .el-table__header-wrapper th {
diff --git a/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue b/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
index c660840..e86b64a 100644
--- a/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
+++ b/src/views/equipmentManagement/upkeep/Form/MaintenanceModal.vue
@@ -38,6 +38,41 @@
           placeholder="璇疯緭鍏ヤ繚鍏荤粨鏋�"
           type="text" />
       </el-form-item>
+      <el-form-item label="璁惧澶囦欢">
+        <el-select v-model="form.sparePartsIds" :loading="loadingSparePartOptions" placeholder="璇烽�夋嫨璁惧澶囦欢" multiple filterable>
+          <el-option
+              v-for="item in sparePartOptions"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+
+      <el-form-item v-if="selectedSpareParts.length" label="棰嗙敤鏁伴噺">
+        <div style="width: 100%">
+          <div
+              v-for="item in selectedSpareParts"
+              :key="item.id"
+              style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;"
+          >
+            <div style="flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
+              {{ item.name }}
+              <span v-if="item.quantity !== null && item.quantity !== undefined" style="color: #909399;">
+                锛堝簱瀛橈細{{ item.quantity }}锛�
+              </span>
+            </div>
+            <el-input-number
+                v-model="sparePartQtyMap[item.id]"
+                :min="1"
+                :max="item.quantity !== null && item.quantity !== undefined ? Number(item.quantity) : undefined"
+                :step="1"
+                controls-position="right"
+                style="width: 180px"
+            />
+          </div>
+        </div>
+      </el-form-item>
     </el-form>
   </FormDialog>
 </template>
@@ -49,6 +84,8 @@
 import dayjs from "dayjs";
 import useUserStore from "@/store/modules/user";
 import { ElMessage } from "element-plus";
+import {computed, ref} from "vue";
+import {getSparePartsList} from "@/api/equipmentManagement/spareParts.js";
 
 defineOptions({
   name: "淇濆吇妯℃�佹",
@@ -67,6 +104,17 @@
   maintenanceActuallyTime: undefined, // 瀹為檯淇濆吇鏃ユ湡
   maintenanceResult: undefined, // 淇濆吇缁撴灉
   status: 0, // 淇濆吇鐘舵��
+  sparePartsIds: [],
+});
+
+const sparePartOptions = ref([])
+const loadingSparePartOptions = ref(true)
+const sparePartQtyMap = ref({})
+
+const selectedSpareParts = computed(() => {
+  const ids = Array.isArray(form.sparePartsIds) ? form.sparePartsIds : [];
+  const set = new Set(ids.map((i) => String(i)));
+  return (sparePartOptions.value || []).filter((p) => set.has(String(p.id)));
 });
 
 const setForm = (data) => {
@@ -78,6 +126,19 @@
       : dayjs().format("YYYY-MM-DD HH:mm:ss");
   form.maintenanceResult = data.maintenanceResult;
   form.status = 1; // 榛樿鐘舵�佷负瀹岀粨
+  // multiple 閫夋嫨鍣ㄨ姹傛暟缁勶紱鍚庣甯歌繑鍥� "1,2,3"
+  if (Array.isArray(data?.sparePartsIds)) {
+    form.sparePartsIds = data.sparePartsIds.map((v) => Number(v)).filter((v) => Number.isFinite(v));
+  } else if (typeof data?.sparePartsIds === "string") {
+    form.sparePartsIds = data.sparePartsIds
+        .split(",")
+        .map((s) => Number(String(s).trim()))
+        .filter((v) => Number.isFinite(v));
+  } else if (typeof data?.sparePartsIds === "number") {
+    form.sparePartsIds = [data.sparePartsIds];
+  } else {
+    form.sparePartsIds = [];
+  }
 };
 
 /**
@@ -86,11 +147,41 @@
 const sendForm = async () => {
   loading.value = true;
   try {
-    const { code } = await addMaintenance({ id: planId.value, ...form });
+    // 棰嗙敤鏁伴噺鏍¢獙
+    if (Array.isArray(form.sparePartsIds) && form.sparePartsIds.length > 0) {
+      for (const partId of form.sparePartsIds) {
+        const qty = Number(sparePartQtyMap.value?.[partId]);
+        if (!Number.isFinite(qty) || qty <= 0) {
+          proxy?.$modal?.msgError?.("璇峰~鍐欏浠堕鐢ㄦ暟閲�");
+          return;
+        }
+        const part = sparePartOptions.value.find((p) => String(p.id) === String(partId));
+        const stock = part?.quantity;
+        if (stock !== null && stock !== undefined && Number.isFinite(Number(stock))) {
+          if (qty > Number(stock)) {
+            proxy?.$modal?.msgError?.(`澶囦欢銆�${part?.name || ""}銆嶉鐢ㄦ暟閲忎笉鑳借秴杩囧簱瀛橈紙${stock}锛塦);
+            return;
+          }
+        }
+      }
+    }
+    const data = {
+      id: planId.value,
+      ...form,
+      sparePartsIds: form.sparePartsIds ? form.sparePartsIds.join(",") : "",
+      sparePartsQty: form.sparePartsIds
+          ? form.sparePartsIds.map((id) => sparePartQtyMap.value?.[id] ?? 1).join(",")
+          : "",
+      sparePartsUseList: form.sparePartsIds
+          ? form.sparePartsIds.map((id) => ({ id, quantity: sparePartQtyMap.value?.[id] ?? 1 }))
+          : [],
+    }
+    const { code } = await addMaintenance(data);
     if (code == 200) {
       ElMessage.success("淇濆吇鎴愬姛");
       emits("ok");
       resetForm();
+      sparePartQtyMap.value = {};
       visible.value = false;
     }
   } finally {
@@ -98,13 +189,34 @@
   }
 };
 
+const fetchSparePartOptions = () => {
+  loadingSparePartOptions.value = true;
+  // 鍜屽浠剁鐞嗛〉涓�鑷达細/spareParts/listPage 鈫� res.data.records
+  getSparePartsList({ current: 1, size: 1000 })
+      .then((res) => {
+        if (res.code === 200) {
+          sparePartOptions.value = res?.data?.records || [];
+        } else {
+          sparePartOptions.value = [];
+        }
+      })
+      .catch(() => {
+        sparePartOptions.value = [];
+      })
+      .finally(() => {
+        loadingSparePartOptions.value = false;
+      });
+}
+
 const handleCancel = () => {
   resetForm();
+  sparePartQtyMap.value = {};
   visible.value = false;
 };
 
 const handleClose = () => {
   resetForm();
+  sparePartQtyMap.value = {};
   visible.value = false;
 };
 
@@ -112,6 +224,7 @@
   planId.value = id; // 淇濆瓨璁″垝淇濆吇璁板綍鐨刬d
   visible.value = true;
   await nextTick();
+  fetchSparePartOptions()
   setForm(row);
 };
 
diff --git a/src/views/equipmentManagement/upkeep/Form/PlanModal.vue b/src/views/equipmentManagement/upkeep/Form/PlanModal.vue
index 19095b9..ee59ce2 100644
--- a/src/views/equipmentManagement/upkeep/Form/PlanModal.vue
+++ b/src/views/equipmentManagement/upkeep/Form/PlanModal.vue
@@ -32,6 +32,12 @@
           disabled
         />
       </el-form-item>
+      <el-form-item label="椤圭洰">
+        <el-input
+            v-model="form.machineryCategory"
+            placeholder="璇疯緭鍏ラ」鐩�"
+        />
+      </el-form-item>
       <el-form-item label="褰曞叆浜�">
         <el-select
           v-model="form.createUser"
@@ -67,6 +73,13 @@
           clearable
         />
       </el-form-item>
+      <el-row :gutter="30">
+        <el-col :span="24">
+          <el-form-item label="闄勪欢" prop="attachmentIds">
+            <FileUpload v-model:file-list="form.storageBlobDTOs" />
+          </el-form-item>
+        </el-col>
+      </el-row>
     </el-form>
   </FormDialog>
 </template>
@@ -84,6 +97,7 @@
 import { onMounted } from "vue";
 import dayjs from "dayjs";
 import { userListNoPage } from "@/api/system/user.js";
+import FileUpload from "@/components/AttachmentUpload/file/index.vue";
 
 defineOptions({
   name: "璁惧淇濆吇鏂板璁″垝",
@@ -108,6 +122,8 @@
   maintenancePlanTime: undefined, // 璁″垝淇濆吇鏃ユ湡
   createUser: undefined, // 褰曞叆浜�
   status: 0, //淇濅慨鐘舵��
+  machineryCategory: undefined,
+  storageBlobDTOs: [],
 });
 
 const setDeviceModel = (deviceId) => {
@@ -125,9 +141,13 @@
   form.deviceModel = data.deviceModel;
   form.createUser = Number(data.createUser);
   form.status = data.status;
-  form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format(
-    "YYYY-MM-DD HH:mm:ss"
-  );
+  form.machineryCategory = data.machineryCategory;
+  if (data.maintenancePlanTime) {
+    form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format(
+      "YYYY-MM-DD HH:mm:ss"
+    );
+  }
+  form.storageBlobDTOs = data.storageBlobVOs || [];
 };
 
 // 鐢ㄦ埛鍒楄〃
diff --git a/src/views/equipmentManagement/upkeep/index.vue b/src/views/equipmentManagement/upkeep/index.vue
index 543e37b..0ee99eb 100644
--- a/src/views/equipmentManagement/upkeep/index.vue
+++ b/src/views/equipmentManagement/upkeep/index.vue
@@ -1,694 +1,636 @@
 <template>
   <div class="app-container">
-    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
+    <el-tabs v-model="activeTab"
+             @tab-change="handleTabChange">
       <!-- 瀹氭椂浠诲姟绠$悊tab -->
-      <el-tab-pane label="瀹氭椂浠诲姟绠$悊" name="scheduled">
+      <el-tab-pane label="瀹氭椂浠诲姟绠$悊"
+                   name="scheduled">
         <div class="search_form">
-          <el-form :model="scheduledFilters" :inline="true">
+          <el-form :model="scheduledFilters"
+                   :inline="true">
             <el-form-item label="浠诲姟鍚嶇О">
-              <el-input
-                  v-model="scheduledFilters.taskName"
-                  style="width: 240px"
-                  placeholder="璇疯緭鍏ヤ换鍔″悕绉�"
-                  clearable
-                  :prefix-icon="Search"
-                  @change="getScheduledTableData"
-              />
+              <el-input v-model="scheduledFilters.taskName"
+                        style="width: 240px"
+                        placeholder="璇疯緭鍏ヤ换鍔″悕绉�"
+                        clearable
+                        :prefix-icon="Search"
+                        @change="getScheduledTableData" />
             </el-form-item>
             <el-form-item label="浠诲姟鐘舵��">
-              <el-select v-model="scheduledFilters.status" placeholder="璇烽�夋嫨浠诲姟鐘舵��" clearable style="width: 200px">
-                <el-option label="鍚敤" value="1" />
-                <el-option label="鍋滅敤" value="0" />
+              <el-select v-model="scheduledFilters.status"
+                         placeholder="璇烽�夋嫨浠诲姟鐘舵��"
+                         clearable
+                         style="width: 200px">
+                <el-option label="鍚敤"
+                           value="1" />
+                <el-option label="鍋滅敤"
+                           value="0" />
               </el-select>
             </el-form-item>
             <el-form-item>
-              <el-button type="primary" @click="getScheduledTableData">鎼滅储</el-button>
+              <el-button type="primary"
+                         @click="getScheduledTableData">鎼滅储</el-button>
               <el-button @click="resetScheduledFilters">閲嶇疆</el-button>
             </el-form-item>
           </el-form>
         </div>
         <div class="table_list">
           <div class="actions">
-            <el-text class="mx-1" size="large">瀹氭椂浠诲姟绠$悊</el-text>
+            <el-text class="mx-1"
+                     size="large">瀹氭椂浠诲姟绠$悊</el-text>
             <div>
-              <el-button type="primary" icon="Plus" @click="addScheduledTask">
+              <el-button type="primary"
+                         icon="Plus"
+                         @click="addScheduledTask">
                 鏂板浠诲姟
               </el-button>
-              <el-button
-                type="danger"
-                icon="Delete"
-                :disabled="scheduledMultipleList.length <= 0"
-                @click="delScheduledTaskByIds(scheduledMultipleList.map((item) => item.id))"
-              >
+              <el-button type="danger"
+                         icon="Delete"
+                         :disabled="scheduledMultipleList.length <= 0"
+                         @click="delScheduledTaskByIds(scheduledMultipleList.map((item) => item.id))">
                 鎵归噺鍒犻櫎
               </el-button>
             </div>
           </div>
-          <PIMTable
-            rowKey="id"
-            isSelection
-            :column="scheduledColumns"
-            :tableData="scheduledDataList"
-            :page="{
+          <PIMTable rowKey="id"
+                    isSelection
+                    :column="scheduledColumns"
+                    :tableData="scheduledDataList"
+                    :page="{
               current: scheduledPagination.currentPage,
               size: scheduledPagination.pageSize,
               total: scheduledPagination.total,
             }"
-            @selection-change="handleScheduledSelectionChange"
-            @pagination="changeScheduledPage"
-          >
+                    @selection-change="handleScheduledSelectionChange"
+                    @pagination="changeScheduledPage">
             <template #statusRef="{ row }">
-              <el-tag v-if="row.status === 1" type="success">鍚敤</el-tag>
-              <el-tag v-if="row.status === 0" type="danger">鍋滅敤</el-tag>
+              <el-tag v-if="row.status === 1"
+                      type="success">鍚敤</el-tag>
+              <el-tag v-if="row.status === 0"
+                      type="danger">鍋滅敤</el-tag>
             </template>
             <template #operation="{ row }">
-              <el-button
-                type="primary"
-                link
-                @click="editScheduledTask(row)"
-              >
+              <el-button type="primary"
+                         link
+                         @click="editScheduledTask(row)">
                 缂栬緫
               </el-button>
-              <el-button
-                type="danger"
-                link
-                @click="delScheduledTaskByIds(row.id)"
-              >
+              <el-button type="danger"
+                         link
+                         @click="delScheduledTaskByIds(row.id)">
                 鍒犻櫎
               </el-button>
             </template>
           </PIMTable>
         </div>
       </el-tab-pane>
-
       <!-- 浠诲姟璁板綍tab锛堝師璁惧淇濆吇椤甸潰锛� -->
-      <el-tab-pane label="浠诲姟璁板綍" name="record">
+      <el-tab-pane label="浠诲姟璁板綍"
+                   name="record">
         <div class="search_form">
-          <el-form :model="filters" :inline="true">
+          <el-form :model="filters"
+                   :inline="true">
             <el-form-item label="璁惧鍚嶇О">
-              <el-input
-                  v-model="filters.deviceName"
-                  style="width: 240px"
-                  placeholder="璇疯緭鍏ヨ澶囧悕绉�"
-                  clearable
-                  :prefix-icon="Search"
-                  @change="getTableData"
-              />
+              <el-input v-model="filters.deviceName"
+                        style="width: 240px"
+                        placeholder="璇疯緭鍏ヨ澶囧悕绉�"
+                        clearable
+                        :prefix-icon="Search"
+                        @change="getTableData" />
             </el-form-item>
             <el-form-item label="璁″垝淇濆吇鏃ユ湡">
-              <el-date-picker
-                  v-model="filters.maintenancePlanTime"
-                  type="date"
-                  placeholder="璇烽�夋嫨璁″垝淇濆吇鏃ユ湡"
-                  size="default"
-                  @change="(date) => handleDateChange(date,2)"
-              />
+              <el-date-picker v-model="filters.maintenancePlanTime"
+                              type="date"
+                              placeholder="璇烽�夋嫨璁″垝淇濆吇鏃ユ湡"
+                              size="default"
+                              @change="(date) => handleDateChange(date,2)" />
             </el-form-item>
             <el-form-item label="瀹為檯淇濆吇鏃ユ湡">
-              <el-date-picker
-                  v-model="filters.maintenanceActuallyTime"
-                  type="date"
-                  placeholder="璇烽�夋嫨瀹為檯淇濆吇鏃ユ湡"
-                  size="default"
-                  @change="(date) => handleDateChange(date,1)"
-              />
+              <el-date-picker v-model="filters.maintenanceActuallyTime"
+                              type="date"
+                              placeholder="璇烽�夋嫨瀹為檯淇濆吇鏃ユ湡"
+                              size="default"
+                              @change="(date) => handleDateChange(date,1)" />
             </el-form-item>
             <el-form-item label="瀹為檯淇濆吇浜�">
-              <el-input
-                  v-model="filters.maintenanceActuallyName"
-                  style="width: 240px"
-                  placeholder="璇疯緭鍏ュ疄闄呬繚鍏讳汉"
-                  clearable
-                  :prefix-icon="Search"
-                  @change="getTableData"
-              />
+              <el-input v-model="filters.maintenanceActuallyName"
+                        style="width: 240px"
+                        placeholder="璇疯緭鍏ュ疄闄呬繚鍏讳汉"
+                        clearable
+                        :prefix-icon="Search"
+                        @change="getTableData" />
             </el-form-item>
             <el-form-item>
-              <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+              <el-button type="primary"
+                         @click="getTableData">鎼滅储</el-button>
               <el-button @click="resetFilters">閲嶇疆</el-button>
             </el-form-item>
           </el-form>
         </div>
         <div class="table_list">
           <div class="actions">
-            <el-text class="mx-1" size="large">浠诲姟璁板綍</el-text>
+            <el-text class="mx-1"
+                     size="large">浠诲姟璁板綍</el-text>
             <div>
-              <el-button type="success" icon="Van" @click="addPlan">
+              <el-button type="success"
+                         icon="Van"
+                         @click="addPlan">
                 鏂板璁″垝
               </el-button>
               <el-button @click="handleOut">
                 瀵煎嚭
               </el-button>
-              <el-button
-                type="danger"
-                icon="Delete"
-                :disabled="multipleList.length <= 0 || hasFinishedStatus"
-                @click="delRepairByIds(multipleList.map((item) => item.id))"
-              >
+              <el-button type="danger"
+                         icon="Delete"
+                         :disabled="multipleList.length <= 0 || hasFinishedStatus"
+                         @click="delRepairByIds(multipleList.map((item) => item.id))">
                 鎵归噺鍒犻櫎
               </el-button>
             </div>
           </div>
-         <PIMTable
-        rowKey="id"
-        isSelection
-        :column="columns"
-        :tableData="dataList"
-        :page="{
+          <PIMTable rowKey="id"
+                    isSelection
+                    :column="columns"
+                    :tableData="dataList"
+                    :page="{
           current: pagination.currentPage,
           size: pagination.pageSize,
           total: pagination.total,
         }"
-        @selection-change="handleSelectionChange"
-        @pagination="changePage"
-      >
-        <template #maintenanceResultRef="{ row }">
-          <div>{{ row.maintenanceResult || '-' }}</div>
-        </template>
-        <template #statusRef="{ row }">
-          <el-tag v-if="row.status === 2" type="danger">澶辫触</el-tag>
-          <el-tag v-if="row.status === 1" type="success">瀹岀粨</el-tag>
-          <el-tag v-if="row.status === 0" type="warning">寰呬繚鍏�</el-tag>
-        </template>
-        <template #operation="{ row }">
-          <!-- 杩欎釜鍔熻兘璺熸柊澧炰繚鍏诲姛鑳戒竴妯′竴鏍凤紝鏈夊暐鎰忎箟锛� -->
-          <!-- <el-button
+                    @selection-change="handleSelectionChange"
+                    @pagination="changePage">
+            <template #maintenanceResultRef="{ row }">
+              <div>{{ row.maintenanceResult || '-' }}</div>
+            </template>
+            <template #statusRef="{ row }">
+              <el-tag v-if="row.status === 2"
+                      type="danger">澶辫触</el-tag>
+              <el-tag v-if="row.status === 1"
+                      type="success">瀹岀粨</el-tag>
+              <el-tag v-if="row.status === 0"
+                      type="warning">寰呬繚鍏�</el-tag>
+            </template>
+            <template #operation="{ row }">
+              <!-- 杩欎釜鍔熻兘璺熸柊澧炰繚鍏诲姛鑳戒竴妯′竴鏍凤紝鏈夊暐鎰忎箟锛� -->
+              <!-- <el-button
               type="primary"
               text
               @click="addMaintain(row)"
           >
             鏂板淇濆吇
           </el-button> -->
-          <el-button
-            type="primary"
-            link
-            :disabled="row.status === 1"
-            @click="editPlan(row.id)"
-          >
-            缂栬緫
-          </el-button>
-          <el-button
-            type="success"
-            link
-            :disabled="row.status === 1"
-            @click="addMaintain(row)"
-          >
-            淇濆吇
-          </el-button>
-          <el-button
-            type="danger"
-            link
-            :disabled="row.status === 1"
-            @click="delRepairByIds(row.id)"
-          >
-            鍒犻櫎
-          </el-button>
-          <el-button
-            type="primary"
-            link
-            @click="openFileDialog(row)"
-          >
-            闄勪欢
-          </el-button>
-        </template>
-      </PIMTable>
+              <el-button type="primary"
+                         link
+                         :disabled="row.status === 1"
+                         @click="editPlan(row.id)">
+                缂栬緫
+              </el-button>
+              <el-button type="success"
+                         link
+                         :disabled="row.status === 1"
+                         @click="addMaintain(row)">
+                淇濆吇
+              </el-button>
+              <el-button type="danger"
+                         link
+                         :disabled="row.status === 1"
+                         @click="delRepairByIds(row.id)">
+                鍒犻櫎
+              </el-button>
+              <el-button type="primary"
+                         link
+                         @click="openFileDialog(row)">
+                闄勪欢
+              </el-button>
+            </template>
+          </PIMTable>
         </div>
       </el-tab-pane>
     </el-tabs>
-    <PlanModal ref="planModalRef" @ok="getTableData" />
-        <MaintenanceModal ref="maintainModalRef" @ok="getTableData" />
-        <FormDia ref="formDiaRef" @closeDia="getScheduledTableData" />
-    <FileListDialog 
-      ref="fileListDialogRef"
-      v-model="fileDialogVisible"
-      :show-upload-button="true"
-      :show-delete-button="true"
-      :delete-method="handleAttachmentDelete"
-      :name-column-label="'闄勪欢鍚嶇О'"
-      :rulesRegulationsManagementId="currentMaintenanceTaskId"
-      @upload="handleAttachmentUpload" />
+    <PlanModal ref="planModalRef"
+               @ok="getTableData" />
+    <MaintenanceModal ref="maintainModalRef"
+                      @ok="getTableData" />
+    <FormDia ref="formDiaRef"
+             @closeDia="getScheduledTableData" />
+    <FileList v-if="fileDialogVisible"
+              v-model:visible="fileDialogVisible"
+              :record-type="'device_maintenance'"
+              :record-id="currentMaintenanceTaskId" />
   </div>
 </template>
 
 <script setup>
-import { ref, onMounted, reactive, getCurrentInstance, nextTick, computed } from 'vue'
-import { Search } from '@element-plus/icons-vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import PlanModal from './Form/PlanModal.vue'
-import MaintenanceModal from './Form/MaintenanceModal.vue'
-import FormDia from './Form/formDia.vue'
-import FileListDialog from '@/components/Dialog/FileListDialog.vue'
-import {
-  getUpkeepPage,
-  delUpkeep,
-  deviceMaintenanceTaskList,
-  deviceMaintenanceTaskDel,
-} from '@/api/equipmentManagement/upkeep'
-import {
-  listMaintenanceTaskFiles,
-  addMaintenanceTaskFile,
-  delMaintenanceTaskFile,
-} from '@/api/equipmentManagement/maintenanceTaskFile'
-import dayjs from 'dayjs'
+  import {
+    ref,
+    onMounted,
+    reactive,
+    getCurrentInstance,
+    nextTick,
+    computed,
+    defineAsyncComponent,
+  } from "vue";
+  import { Search } from "@element-plus/icons-vue";
+  import { ElMessage, ElMessageBox } from "element-plus";
+  import PlanModal from "./Form/PlanModal.vue";
+  import MaintenanceModal from "./Form/MaintenanceModal.vue";
+  import FormDia from "./Form/formDia.vue";
+  import {
+    getUpkeepPage,
+    delUpkeep,
+    deviceMaintenanceTaskList,
+    deviceMaintenanceTaskDel,
+  } from "@/api/equipmentManagement/upkeep";
+  import dayjs from "dayjs";
 
-const { proxy } = getCurrentInstance()
+  const { proxy } = getCurrentInstance();
+  const FileList = defineAsyncComponent(() =>
+    import("@/components/Dialog/FileList.vue")
+  );
 
-// Tab鐩稿叧
-const activeTab = ref('scheduled')
+  // Tab鐩稿叧
+  const activeTab = ref("scheduled");
 
-// 璁″垝寮圭獥鎺у埗鍣�
-const planModalRef = ref()
-// 淇濆吇寮圭獥鎺у埗鍣�
-const maintainModalRef = ref()
-// 瀹氭椂浠诲姟寮圭獥鎺у埗鍣�
-const formDiaRef = ref()
-// 闄勪欢寮圭獥
-const fileListDialogRef = ref(null)
-const fileDialogVisible = ref(false)
-const currentMaintenanceTaskId = ref(null)
+  // 璁″垝寮圭獥鎺у埗鍣�
+  const planModalRef = ref();
+  // 淇濆吇寮圭獥鎺у埗鍣�
+  const maintainModalRef = ref();
+  // 瀹氭椂浠诲姟寮圭獥鎺у埗鍣�
+  const formDiaRef = ref();
+  // 闄勪欢寮圭獥
+  const fileListDialogRef = ref(null);
+  const fileDialogVisible = ref(false);
+  const currentMaintenanceTaskId = ref(null);
 
-// 浠诲姟璁板綍tab锛堝師璁惧淇濆吇椤甸潰锛夌浉鍏冲彉閲�
-const filters = reactive({
-  deviceName: '',
-  maintenancePlanTime: '',
-  maintenanceActuallyTime: '',
-  maintenanceActuallyName: '',
-})
+  // 浠诲姟璁板綍tab锛堝師璁惧淇濆吇椤甸潰锛夌浉鍏冲彉閲�
+  const filters = reactive({
+    deviceName: "",
+    maintenancePlanTime: "",
+    maintenanceActuallyTime: "",
+    maintenanceActuallyName: "",
+  });
 
-const dataList = ref([])
-const pagination = ref({
-  currentPage: 1,
-  pageSize: 10,
-  total: 0,
-})
-const multipleList = ref([])
+  const dataList = ref([]);
+  const pagination = ref({
+    currentPage: 1,
+    pageSize: 10,
+    total: 0,
+  });
+  const multipleList = ref([]);
 
-// 瀹氭椂浠诲姟绠$悊tab鐩稿叧鍙橀噺
-const scheduledFilters = reactive({
-  taskName: '',
-  status: '',
-})
+  // 瀹氭椂浠诲姟绠$悊tab鐩稿叧鍙橀噺
+  const scheduledFilters = reactive({
+    taskName: "",
+    status: "",
+  });
 
-const scheduledDataList = ref([])
-const scheduledPagination = reactive({
-  currentPage: 1,
-  pageSize: 10,
-  total: 0,
-})
-const scheduledMultipleList = ref([])
+  const scheduledDataList = ref([]);
+  const scheduledPagination = reactive({
+    currentPage: 1,
+    pageSize: 10,
+    total: 0,
+  });
+  const scheduledMultipleList = ref([]);
 
-// 瀹氭椂浠诲姟绠$悊琛ㄦ牸鍒楅厤缃�
-const scheduledColumns = ref([
-	{ prop: "taskName", label: "璁惧鍚嶇О"},
-	{
-		label: "瑙勬牸鍨嬪彿",
-		prop: "deviceModel",
-	},
-	{
-		prop: "frequencyType",
-		label: "棰戞",
-		minWidth: 150,
-		// PIMTable 浣跨敤鐨勬槸 formatData锛岃�屼笉鏄� Element-Plus 鐨� formatter
-		formatData: (cell) => ({
-			DAILY: "姣忔棩",
-			WEEKLY: "姣忓懆",
-			MONTHLY: "姣忔湀",
-			QUARTERLY: "瀛e害"
-		}[cell] || "")
-	},
-	{
-		prop: "frequencyDetail",
-		label: "寮�濮嬫棩鏈熶笌鏃堕棿",
-		minWidth: 150,
-		// 鍚屾牱鏀圭敤 formatData锛孭IMTable 鍐呴儴浼氭妸鍗曞厓鏍煎�间紶杩涙潵
-		formatData: (cell) => {
-			if (typeof cell !== 'string') return '';
-			let val = cell;
-			const replacements = {
-				MON: '鍛ㄤ竴',
-				TUE: '鍛ㄤ簩',
-				WED: '鍛ㄤ笁',
-				THU: '鍛ㄥ洓',
-				FRI: '鍛ㄤ簲',
-				SAT: '鍛ㄥ叚',
-				SUN: '鍛ㄦ棩'
-			};
-			// 浣跨敤姝e垯涓�娆℃�ф浛鎹㈡墍鏈夊尮閰嶉」
-			return val.replace(/MON|TUE|WED|THU|FRI|SAT|SUN/g, match => replacements[match]);
-		}
-	},
-	{ prop: "registrant", label: "鐧昏浜�", minWidth: 100 },
-	{ prop: "registrationDate", label: "鐧昏鏃ユ湡", minWidth: 100 },
-	{
-		fixed: "right",
-		label: "鎿嶄綔",
-		dataType: "slot",
-		slot: "operation",
-		align: "center",
-		width: "200px",
-	},
-])
+  // 瀹氭椂浠诲姟绠$悊琛ㄦ牸鍒楅厤缃�
+  const scheduledColumns = ref([
+    { prop: "taskName", label: "璁惧鍚嶇О" },
+    {
+      label: "瑙勬牸鍨嬪彿",
+      prop: "deviceModel",
+    },
+    {
+      prop: "frequencyType",
+      label: "棰戞",
+      minWidth: 150,
+      // PIMTable 浣跨敤鐨勬槸 formatData锛岃�屼笉鏄� Element-Plus 鐨� formatter
+      formatData: cell =>
+        ({
+          DAILY: "姣忔棩",
+          WEEKLY: "姣忓懆",
+          MONTHLY: "姣忔湀",
+          QUARTERLY: "瀛e害",
+        }[cell] || ""),
+    },
+    {
+      prop: "frequencyDetail",
+      label: "寮�濮嬫棩鏈熶笌鏃堕棿",
+      minWidth: 150,
+      // 鍚屾牱鏀圭敤 formatData锛孭IMTable 鍐呴儴浼氭妸鍗曞厓鏍煎�间紶杩涙潵
+      formatData: cell => {
+        if (typeof cell !== "string") return "";
+        let val = cell;
+        const replacements = {
+          MON: "鍛ㄤ竴",
+          TUE: "鍛ㄤ簩",
+          WED: "鍛ㄤ笁",
+          THU: "鍛ㄥ洓",
+          FRI: "鍛ㄤ簲",
+          SAT: "鍛ㄥ叚",
+          SUN: "鍛ㄦ棩",
+        };
+        // 浣跨敤姝e垯涓�娆℃�ф浛鎹㈡墍鏈夊尮閰嶉」
+        return val.replace(
+          /MON|TUE|WED|THU|FRI|SAT|SUN/g,
+          match => replacements[match]
+        );
+      },
+    },
+    { prop: "registrant", label: "鐧昏浜�", minWidth: 100 },
+    { prop: "registrationDate", label: "鐧昏鏃ユ湡", minWidth: 100 },
+    {
+      fixed: "right",
+      label: "鎿嶄綔",
+      dataType: "slot",
+      slot: "operation",
+      align: "center",
+      width: "200px",
+    },
+  ]);
 
-// 浠诲姟璁板綍琛ㄦ牸鍒楅厤缃紙鍘熻澶囦繚鍏昏〃鏍煎垪锛�
-const columns = ref([
-	{
-		label: "璁惧鍚嶇О",
-		align: "center",
-		prop: "deviceName",
-	},
-	{
-		label: "瑙勬牸鍨嬪彿",
-		align: "center",
-		prop: "deviceModel",
-	},
-	{
-		label: "璁″垝淇濆吇鏃ユ湡",
-		align: "center",
-		prop: "maintenancePlanTime",
-		formatData: (cell) => dayjs(cell).format("YYYY-MM-DD"),
-	},
-	{
-		label: "褰曞叆浜�",
-		align: "center",
-		prop: "createUserName",
-	},
-	// {
-	//   label: "褰曞叆鏃ユ湡",
-	//   align: "center",
-	//   prop: "createTime",
-	//   formatData: (cell) => dayjs(cell).format("YYYY-MM-DD HH:mm:ss"),
-	//   width: 200,
-	// },
-	{
-		label: "瀹為檯淇濆吇浜�",
-		align: "center",
-		prop: "maintenanceActuallyName",
-	},
-	{
-		label: "瀹為檯淇濆吇鏃ユ湡",
-		align: "center",
-		prop: "maintenanceActuallyTime",
-		formatData: (cell) =>
-			cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-",
-	},
-	{
-		label: "淇濆吇缁撴灉",
-		align: "center",
-		prop: "maintenanceResult",
-		dataType: "slot",
-		slot: "maintenanceResultRef",
-	},
-	{
-		label: "鐘舵��",
-		align: "center",
-		prop: "status",
-		dataType: "slot",
-		slot: "statusRef",
-	},
-	{
-		fixed: "right",
-		label: "鎿嶄綔",
-		dataType: "slot",
-		slot: "operation",
-		align: "center",
-		width: "350px",
-	},
-])
+  // 浠诲姟璁板綍琛ㄦ牸鍒楅厤缃紙鍘熻澶囦繚鍏昏〃鏍煎垪锛�
+  const columns = ref([
+    {
+      label: "璁惧鍚嶇О",
+      align: "center",
+      prop: "deviceName",
+    },
+    {
+      label: "瑙勬牸鍨嬪彿",
+      align: "center",
+      prop: "deviceModel",
+    },
+    {
+      label: "璁″垝淇濆吇鏃ユ湡",
+      align: "center",
+      prop: "maintenancePlanTime",
+      formatData: cell => dayjs(cell).format("YYYY-MM-DD"),
+    },
+    {
+      label: "褰曞叆浜�",
+      align: "center",
+      prop: "createUserName",
+    },
+    {
+      label: "椤圭洰",
+      align: "center",
+      prop: "machineryCategory",
+    },
+    // {
+    //   label: "褰曞叆鏃ユ湡",
+    //   align: "center",
+    //   prop: "createTime",
+    //   formatData: (cell) => dayjs(cell).format("YYYY-MM-DD HH:mm:ss"),
+    //   width: 200,
+    // },
+    {
+      label: "瀹為檯淇濆吇浜�",
+      align: "center",
+      prop: "maintenanceActuallyName",
+    },
+    {
+      label: "瀹為檯淇濆吇鏃ユ湡",
+      align: "center",
+      prop: "maintenanceActuallyTime",
+      formatData: cell =>
+        cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-",
+    },
+    {
+      label: "淇濆吇缁撴灉",
+      align: "center",
+      prop: "maintenanceResult",
+      dataType: "slot",
+      slot: "maintenanceResultRef",
+    },
+    {
+      label: "鐘舵��",
+      align: "center",
+      prop: "status",
+      dataType: "slot",
+      slot: "statusRef",
+    },
+    {
+      fixed: "right",
+      label: "鎿嶄綔",
+      dataType: "slot",
+      slot: "operation",
+      align: "center",
+      width: "350px",
+    },
+  ]);
 
-// Tab鍒囨崲澶勭悊
-const handleTabChange = (tabName) => {
-  if (tabName === 'record') {
-    getTableData()
-  } else if (tabName === 'scheduled') {
-    getScheduledTableData()
-  }
-}
-
-// 瀹氭椂浠诲姟绠$悊鐩稿叧鏂规硶
-const getScheduledTableData = async () => {
-  try {
-    const params = {
-      current: scheduledPagination.currentPage,
-      size: scheduledPagination.pageSize,
-      taskName: scheduledFilters.taskName || undefined,
-      status: scheduledFilters.status || undefined,
+  // Tab鍒囨崲澶勭悊
+  const handleTabChange = tabName => {
+    if (tabName === "record") {
+      getTableData();
+    } else if (tabName === "scheduled") {
+      getScheduledTableData();
     }
-    const { code, data } = await deviceMaintenanceTaskList(params)
-    if (code === 200) {
-      scheduledDataList.value = data?.records || []
-      scheduledPagination.total = data?.total || 0
+  };
+
+  // 瀹氭椂浠诲姟绠$悊鐩稿叧鏂规硶
+  const getScheduledTableData = async () => {
+    try {
+      const params = {
+        current: scheduledPagination.currentPage,
+        size: scheduledPagination.pageSize,
+        taskName: scheduledFilters.taskName || undefined,
+        status: scheduledFilters.status || undefined,
+      };
+      const { code, data } = await deviceMaintenanceTaskList(params);
+      if (code === 200) {
+        scheduledDataList.value = data?.records || [];
+        scheduledPagination.total = data?.total || 0;
+      }
+    } catch (error) {
+      ElMessage.error("鑾峰彇瀹氭椂浠诲姟鍒楄〃澶辫触");
     }
-  } catch (error) {
-    ElMessage.error('鑾峰彇瀹氭椂浠诲姟鍒楄〃澶辫触')
-  }
-}
+  };
 
-const resetScheduledFilters = () => {
-  scheduledFilters.taskName = ''
-  scheduledFilters.status = ''
-  getScheduledTableData()
-}
+  const resetScheduledFilters = () => {
+    scheduledFilters.taskName = "";
+    scheduledFilters.status = "";
+    getScheduledTableData();
+  };
 
-const handleScheduledSelectionChange = (selection) => {
-  scheduledMultipleList.value = selection
-}
+  const handleScheduledSelectionChange = selection => {
+    scheduledMultipleList.value = selection;
+  };
 
-const changeScheduledPage = (page) => {
-  scheduledPagination.currentPage = page.page
-  scheduledPagination.pageSize = page.limit
-  getScheduledTableData()
-}
+  const changeScheduledPage = page => {
+    scheduledPagination.currentPage = page.page;
+    scheduledPagination.pageSize = page.limit;
+    getScheduledTableData();
+  };
 
-const addScheduledTask = () => {
-  nextTick(() => {
-		formDiaRef.value?.openDialog('add');
-	});
-}
+  const addScheduledTask = () => {
+    nextTick(() => {
+      formDiaRef.value?.openDialog("add");
+    });
+  };
 
-const editScheduledTask = (row) => {
-  if (row) {
-		nextTick(() => {
-			formDiaRef.value?.openDialog('edit', row);
-		});
-  }
-}
+  const editScheduledTask = row => {
+    if (row) {
+      nextTick(() => {
+        formDiaRef.value?.openDialog("edit", row);
+      });
+    }
+  };
 
-const delScheduledTaskByIds = async (ids) => {
-  try {
-    await ElMessageBox.confirm('纭畾鍒犻櫎閫変腑鐨勫畾鏃朵换鍔″悧锛�', '鎻愮ず', {
-      type: 'warning',
+  const delScheduledTaskByIds = async ids => {
+    try {
+      await ElMessageBox.confirm("纭畾鍒犻櫎閫変腑鐨勫畾鏃朵换鍔″悧锛�", "鎻愮ず", {
+        type: "warning",
+      });
+      const payload = Array.isArray(ids) ? ids : [ids];
+      await deviceMaintenanceTaskDel(payload);
+      ElMessage.success("鍒犻櫎瀹氭椂浠诲姟鎴愬姛");
+      getScheduledTableData();
+    } catch (error) {
+      // 鐢ㄦ埛鍙栨秷鍒犻櫎
+    }
+  };
+
+  const handleScheduledOut = () => {
+    ElMessage.info("瀵煎嚭瀹氭椂浠诲姟鍔熻兘寰呭疄鐜�");
+  };
+
+  // 浠诲姟璁板綍鐩稿叧鏂规硶锛堝師璁惧淇濆吇椤甸潰鏂规硶锛�
+  const getTableData = async () => {
+    try {
+      const params = {
+        current: pagination.value.currentPage,
+        size: pagination.value.pageSize,
+        deviceName: filters.deviceName || undefined,
+        maintenancePlanTime: filters.maintenancePlanTime
+          ? dayjs(filters.maintenancePlanTime).format("YYYY-MM-DD")
+          : undefined,
+        maintenanceActuallyTime: filters.maintenanceActuallyTime
+          ? dayjs(filters.maintenanceActuallyTime).format("YYYY-MM-DD")
+          : undefined,
+        maintenanceActuallyName: filters.maintenanceActuallyName || undefined,
+      };
+
+      const { code, data } = await getUpkeepPage(params);
+      if (code === 200) {
+        dataList.value = data.records;
+        pagination.value.total = data.total;
+      }
+    } catch (error) {
+      console.log(error);
+    }
+  };
+
+  const resetFilters = () => {
+    filters.deviceName = "";
+    filters.maintenancePlanTime = "";
+    filters.maintenanceActuallyTime = "";
+    filters.maintenanceActuallyName = "";
+    getTableData();
+  };
+
+  const handleSelectionChange = selection => {
+    multipleList.value = selection;
+  };
+
+  // 妫�鏌ラ�変腑鐨勮褰曚腑鏄惁鏈夊畬缁撶姸鎬佺殑
+  const hasFinishedStatus = computed(() => {
+    return multipleList.value.some(item => item.status === 1);
+  });
+
+  const changePage = page => {
+    pagination.value.currentPage = page.page;
+    pagination.value.pageSize = page.limit;
+    getTableData();
+  };
+
+  const addMaintain = row => {
+    maintainModalRef.value.open(row.id, row);
+  };
+
+  const addPlan = () => {
+    planModalRef.value.openModal();
+  };
+
+  const editPlan = id => {
+    planModalRef.value.openEdit(id);
+  };
+
+  const delRepairByIds = async ids => {
+    // 妫�鏌ユ槸鍚︽湁瀹岀粨鐘舵�佺殑璁板綍
+    const hasFinished = multipleList.value.some(item => item.status === 1);
+    if (hasFinished) {
+      ElMessage.warning("涓嶈兘鍒犻櫎鐘舵�佷负瀹岀粨鐨勮褰�");
+      return;
+    }
+
+    try {
+      await ElMessageBox.confirm("纭鍒犻櫎淇濆吇鏁版嵁, 姝ゆ搷浣滀笉鍙��?", "璀﹀憡", {
+        confirmButtonText: "纭畾",
+        cancelButtonText: "鍙栨秷",
+        type: "warning",
+      });
+
+      const { code } = await delUpkeep(ids);
+      if (code === 200) {
+        ElMessage.success("鍒犻櫎鎴愬姛");
+        getTableData();
+      }
+    } catch (error) {
+      // 鐢ㄦ埛鍙栨秷鍒犻櫎
+    }
+  };
+
+  const handleOut = () => {
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
     })
-    const payload = Array.isArray(ids) ? ids : [ids]
-    await deviceMaintenanceTaskDel(payload)
-    ElMessage.success('鍒犻櫎瀹氭椂浠诲姟鎴愬姛')
-    getScheduledTableData()
-  } catch (error) {
-    // 鐢ㄦ埛鍙栨秷鍒犻櫎
-  }
-}
+      .then(() => {
+        proxy.download("/device/maintenance/export", {}, "璁惧淇濆吇.xlsx");
+      })
+      .catch(() => {
+        ElMessage.info("宸插彇娑�");
+      });
+  };
 
-const handleScheduledOut = () => {
-  ElMessage.info('瀵煎嚭瀹氭椂浠诲姟鍔熻兘寰呭疄鐜�')
-}
-
-// 浠诲姟璁板綍鐩稿叧鏂规硶锛堝師璁惧淇濆吇椤甸潰鏂规硶锛�
-const getTableData = async () => {
-  try {
-    const params = {
-      current: pagination.value.currentPage,
-      size: pagination.value.pageSize,
-      deviceName: filters.deviceName || undefined,
-      maintenancePlanTime: filters.maintenancePlanTime ? dayjs(filters.maintenancePlanTime).format('YYYY-MM-DD') : undefined,
-      maintenanceActuallyTime: filters.maintenanceActuallyTime ? dayjs(filters.maintenanceActuallyTime).format('YYYY-MM-DD') : undefined,
-      maintenanceActuallyName: filters.maintenanceActuallyName || undefined,
+  const handleDateChange = (date, type) => {
+    if (type === 1) {
+      filters.maintenanceActuallyTime = date
+        ? dayjs(date).format("YYYY-MM-DD")
+        : "";
+    } else {
+      filters.maintenancePlanTime = date ? dayjs(date).format("YYYY-MM-DD") : "";
     }
+    getTableData();
+  };
 
-    const { code, data } = await getUpkeepPage(params)
-    if (code === 200) {
-      dataList.value = data.records
-      pagination.value.total = data.total
+  // 鎵撳紑闄勪欢寮圭獥
+  const openFileDialog = async row => {
+    currentMaintenanceTaskId.value = row.id;
+    fileDialogVisible.value = true;
+  };
+
+  onMounted(() => {
+    // 鏍规嵁榛樿婵�娲荤殑 Tab 璋冪敤瀵瑰簲鐨勬煡璇㈡帴鍙�
+    if (activeTab.value === "scheduled") {
+      getScheduledTableData();
+    } else {
+      getTableData();
     }
-  } catch (error) {
-    console.log(error);
-    
-  }
-}
-
-const resetFilters = () => {
-  filters.deviceName = ''
-  filters.maintenancePlanTime = ''
-  filters.maintenanceActuallyTime = ''
-  filters.maintenanceActuallyName = ''
-  getTableData()
-}
-
-const handleSelectionChange = (selection) => {
-  multipleList.value = selection
-}
-
-// 妫�鏌ラ�変腑鐨勮褰曚腑鏄惁鏈夊畬缁撶姸鎬佺殑
-const hasFinishedStatus = computed(() => {
-  return multipleList.value.some(item => item.status === 1)
-})
-
-const changePage = (page) => {
-  pagination.value.currentPage = page.page
-  pagination.value.pageSize = page.limit
-  getTableData()
-}
-
-const addMaintain = (row) => {
-  maintainModalRef.value.open(row.id, row)
-}
-
-const addPlan = () => {
-  planModalRef.value.openModal()
-}
-
-const editPlan = (id) => {
-  planModalRef.value.openEdit(id)
-}
-
-const delRepairByIds = async (ids) => {
-  // 妫�鏌ユ槸鍚︽湁瀹岀粨鐘舵�佺殑璁板綍
-  const hasFinished = multipleList.value.some(item => item.status === 1)
-  if (hasFinished) {
-    ElMessage.warning('涓嶈兘鍒犻櫎鐘舵�佷负瀹岀粨鐨勮褰�')
-    return
-  }
-  
-  try {
-    await ElMessageBox.confirm('纭鍒犻櫎淇濆吇鏁版嵁, 姝ゆ搷浣滀笉鍙��?', '璀﹀憡', {
-      confirmButtonText: '纭畾',
-      cancelButtonText: '鍙栨秷',
-      type: 'warning',
-    })
-    
-    const { code } = await delUpkeep(ids)
-    if (code === 200) {
-      ElMessage.success('鍒犻櫎鎴愬姛')
-      getTableData()
-    }
-  } catch (error) {
-    // 鐢ㄦ埛鍙栨秷鍒犻櫎
-  }
-}
-
-const handleOut = () => {
-  ElMessageBox.confirm('閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�', '瀵煎嚭', {
-    confirmButtonText: '纭',
-    cancelButtonText: '鍙栨秷',
-    type: 'warning',
-  })
-    .then(() => {
-      proxy.download('/device/maintenance/export', {}, '璁惧淇濆吇.xlsx')
-    })
-    .catch(() => {
-      ElMessage.info('宸插彇娑�')
-    })
-}
-
-const handleDateChange = (date, type) => {
-  if (type === 1) {
-    filters.maintenanceActuallyTime = date ? dayjs(date).format('YYYY-MM-DD') : ''
-  } else {
-    filters.maintenancePlanTime = date ? dayjs(date).format('YYYY-MM-DD') : ''
-  }
-  getTableData()
-}
-
-// 闄勪欢鐩稿叧鏂规硶
-// 鏌ヨ闄勪欢鍒楄〃
-const fetchMaintenanceTaskFiles = async (deviceMaintenanceId) => {
-  try {
-    const params = {
-      current: 1,
-      size: 100,
-      deviceMaintenanceId,
-      rulesRegulationsManagementId:deviceMaintenanceId
-    }
-    const res = await listMaintenanceTaskFiles(params)
-    const records = res?.data?.records || []
-    const mapped = records.map(item => ({
-      id: item.id,
-      name: item.fileName || item.name,
-      url: item.fileUrl || item.url,
-      raw: item,
-    }))
-    fileListDialogRef.value?.setList(mapped)
-  } catch (error) {
-    ElMessage.error('鑾峰彇闄勪欢鍒楄〃澶辫触')
-  }
-}
-
-// 鎵撳紑闄勪欢寮圭獥
-const openFileDialog = async (row) => {
-  currentMaintenanceTaskId.value = row.id
-  fileDialogVisible.value = true
-  await fetchMaintenanceTaskFiles(row.id)
-}
-
-// 鍒锋柊闄勪欢鍒楄〃
-const refreshFileList = async () => {
-  if (!currentMaintenanceTaskId.value) return
-  await fetchMaintenanceTaskFiles(currentMaintenanceTaskId.value)
-}
-
-// 涓婁紶闄勪欢
-const handleAttachmentUpload = async (filePayload) => {
-  if (!currentMaintenanceTaskId.value) return
-  try {
-    const payload = {
-      name: filePayload?.fileName || filePayload?.name,
-      url: filePayload?.fileUrl || filePayload?.url,
-      deviceMaintenanceId: currentMaintenanceTaskId.value,
-    }
-    await addMaintenanceTaskFile(payload)
-    ElMessage.success('鏂囦欢涓婁紶鎴愬姛')
-    await refreshFileList()
-  } catch (error) {
-    ElMessage.error('鏂囦欢涓婁紶澶辫触')
-  }
-}
-
-// 鍒犻櫎闄勪欢
-const handleAttachmentDelete = async (row) => {
-  if (!row?.id) return false
-  try {
-    await ElMessageBox.confirm('纭鍒犻櫎璇ラ檮浠讹紵', '鎻愮ず', { type: 'warning' })
-  } catch {
-    return false
-  }
-  try {
-    await delMaintenanceTaskFile(row.id)
-    ElMessage.success('鍒犻櫎鎴愬姛')
-    await refreshFileList()
-    return true
-  } catch (error) {
-    ElMessage.error('鍒犻櫎澶辫触')
-    return false
-  }
-}
-
-onMounted(() => {
-  // 鏍规嵁榛樿婵�娲荤殑 Tab 璋冪敤瀵瑰簲鐨勬煡璇㈡帴鍙�
-  if (activeTab.value === 'scheduled') {
-    getScheduledTableData()
-  } else {
-    getTableData()
-  }
-})
+  });
 </script>
 
 <style lang="scss" scoped>
-.table_list {
-  margin-top: unset;
-}
-.actions {
-  display: flex;
-  justify-content: space-between;
-  margin-bottom: 10px;
-}
+  .table_list {
+    margin-top: unset;
+  }
+  .actions {
+    display: flex;
+    justify-content: space-between;
+    margin-bottom: 10px;
+  }
 </style>
 
 
diff --git a/src/views/example/DynamicTableExample.vue b/src/views/example/DynamicTableExample.vue
index 038cd43..14dfc92 100644
--- a/src/views/example/DynamicTableExample.vue
+++ b/src/views/example/DynamicTableExample.vue
@@ -94,8 +94,8 @@
       
       <template #footer>
         <div class="dialog-footer">
-          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
           <el-button type="primary" @click="handleSubmit">纭畾</el-button>
+          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
         </div>
       </template>
     </el-dialog>
diff --git a/src/views/fileManagement/bookshelf/index.vue b/src/views/fileManagement/bookshelf/index.vue
index a084900..c73ae1c 100644
--- a/src/views/fileManagement/bookshelf/index.vue
+++ b/src/views/fileManagement/bookshelf/index.vue
@@ -97,8 +97,8 @@
       </el-tree>
       <template #footer>
         <span class="dialog-footer">
-          <el-button @click="keepVisible = false">鍙� 娑�</el-button>
           <el-button type="primary" @click="keepVisible = false" >纭� 瀹�</el-button>
+          <el-button @click="keepVisible = false">鍙� 娑�</el-button>
         </span>
       </template>
     </el-dialog>
@@ -115,8 +115,8 @@
       </el-row>
       <template #footer>
         <span class="dialog-footer">
-          <el-button @click="warehouseVisible = false">鍙� 娑�</el-button>
           <el-button type="primary" @click="confirmWarehouse" :loading="upLoadWarehouse">纭� 瀹�</el-button>
+          <el-button @click="warehouseVisible = false">鍙� 娑�</el-button>
         </span>
       </template>
     </el-dialog>
@@ -149,8 +149,8 @@
       </el-row>
       <template #footer>
         <span class="dialog-footer">
-          <el-button @click="shelvesVisible = false">鍙� 娑�</el-button>
           <el-button type="primary" @click="confirmShelves" :loading="upLoadShelves">纭� 瀹�</el-button>
+          <el-button @click="shelvesVisible = false">鍙� 娑�</el-button>
         </span>
       </template>
     </el-dialog>
diff --git a/src/views/fileManagement/borrow/index.vue b/src/views/fileManagement/borrow/index.vue
index 705a0f8..c7d8e0d 100644
--- a/src/views/fileManagement/borrow/index.vue
+++ b/src/views/fileManagement/borrow/index.vue
@@ -100,16 +100,14 @@
            </el-col>
            <el-col :span="12">
              <el-form-item label="鍊熼槄涔︾睄锛�" prop="documentationId">
-               <!-- <el-select v-model="borrowForm.documentationId" placeholder="璇烽�夋嫨鍊熼槄涔︾睄" style="width: 100%" @change="handleScanContent">
-                 <el-option 
-                   v-for="item in documentList" 
-                   :key="item.id" 
-                   :label="item.docName || item.name" 
-                   :value="item.id"
-                 />
-               </el-select> -->
                <div style="display: flex; gap: 10px;">
-                <el-select v-model="borrowForm.documentationId" placeholder="璇烽�夋嫨鍊熼槄涔︾睄" style="flex: 1;width: 100px;" @change="handleSelectChange">
+                <el-select
+                  v-if="borrowOperationType !== 'edit'"
+                  v-model="borrowForm.documentationId"
+                  placeholder="璇烽�夋嫨鍊熼槄涔︾睄"
+                  style="flex: 1;width: 100px;"
+                  @change="handleSelectChange"
+                >
                   <el-option 
                     v-for="item in documentList" 
                     :key="item.id" 
@@ -118,6 +116,13 @@
                   />
                 </el-select>
                 <el-input
+                  v-else
+                  v-model="currentEditDocName"
+                  style="flex: 1;width: 100px;"
+                  disabled
+                />
+                <el-input
+                  v-if="borrowOperationType !== 'edit'"
                   v-model="scanContent"
                   placeholder="鎵爜杈撳叆"
                   style="width: 100px;"
@@ -205,6 +210,7 @@
 const selectedRows = ref([]);
 const documentList = ref([]); // 鏂囨。鍒楄〃锛岀敤浜庡�熼槄涔︾睄閫夋嫨
 const scanContent = ref() // 鎵爜鍐呭
+const currentEditDocName = ref(''); // 缂栬緫鏃跺瓨鍌ㄧ殑鏂囨。鍚嶇О
 // 鍒嗛〉鐩稿叧
 const pagination = reactive({
   currentPage: 1,
@@ -282,6 +288,7 @@
       {
         name: "缂栬緫",
         type: "text",
+        disabled: (row) => row.borrowStatus === '褰掕繕',
         clickFun: (row) => {
           openBorrowDia('edit', row)
         },
@@ -428,13 +435,16 @@
   if (type === "edit") {
     // 缂栬緫妯″紡锛屽姞杞界幇鏈夋暟鎹�
     Object.assign(borrowForm, data);
+    // 瀛樺偍鏂囨。鍚嶇О鐢ㄤ簬鏄剧ず
+    currentEditDocName.value = data.docName || '';
   } else {
     // 鏂板妯″紡锛屾竻绌鸿〃鍗�
     Object.keys(borrowForm).forEach(key => {
       borrowForm[key] = "";
     });
-         // 璁剧疆榛樿鐘舵��
-     borrowForm.borrowStatus = "鍊熼槄";
+    currentEditDocName.value = ''; // 娓呯┖缂栬緫鏃剁殑鏂囨。鍚嶇О
+    // 璁剧疆榛樿鐘舵��
+    borrowForm.borrowStatus = "鍊熼槄";
     // 璁剧疆褰撳墠鏃ユ湡涓哄�熼槄鏃ユ湡
     borrowForm.borrowDate = new Date().toISOString().split('T')[0];
   }
@@ -445,6 +455,7 @@
   proxy.$refs.borrowFormRef.resetFields();
   borrowDia.value = false;
   scanContent.value = ''; // 娓呯┖鎵爜鍐呭
+  currentEditDocName.value = ''; // 娓呯┖缂栬緫鏃剁殑鏂囨。鍚嶇О
 };
 
 // 鎻愪氦鍊熼槄琛ㄥ崟
@@ -625,7 +636,7 @@
 }
 
 .dialog-footer {
-  text-align: right;
+  text-align: center;
 }
 
 :deep(.el-form-item__label) {
diff --git a/src/views/fileManagement/document/index.vue b/src/views/fileManagement/document/index.vue
index aa182b0..f4eac2d 100644
--- a/src/views/fileManagement/document/index.vue
+++ b/src/views/fileManagement/document/index.vue
@@ -862,12 +862,14 @@
       documentForm[key] = "";
     });
     documentForm.attachments = []; // 鏂板妯″紡涓嬩篃娓呯┖闄勪欢
-    // 璁剧疆榛樿鍊� - 浣跨敤瀛楀吀鏁版嵁鐨勭涓�涓�夐」浣滀负榛樿鍊�
+    // 璁剧疆榛樿鍊� - 鏂囨。鐘舵�侀粯璁よ缃负"姝e父"
     if (document_status.value && document_status.value.length > 0) {
-      documentForm.docStatus = document_status.value[0].value;
+      const normalStatus = document_status.value.find(item => item.label === '姝e父');
+      documentForm.docStatus = normalStatus ? normalStatus.value : document_status.value[0].value;
     }
     if (document_urgency.value && document_urgency.value.length > 0) {
-      documentForm.urgencyLevel = document_urgency.value[0].value;
+      const normalUrgency = document_urgency.value.find(item => item.label === '鏅��');
+      documentForm.urgencyLevel = normalUrgency ? normalUrgency.value : document_urgency.value[0].value;
     }
   }
 };
@@ -1298,7 +1300,7 @@
 }
 
 .dialog-footer {
-  text-align: right;
+  text-align: center;
 }
 
 .operation-column {
diff --git a/src/views/fileManagement/return/index.vue b/src/views/fileManagement/return/index.vue
index 34e733f..097ab29 100644
--- a/src/views/fileManagement/return/index.vue
+++ b/src/views/fileManagement/return/index.vue
@@ -103,16 +103,14 @@
                  <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="鏂囨。锛�" prop="borrowId">
-               <!-- <el-select v-model="returnForm.borrowId" placeholder="璇烽�夋嫨鏂囨。" style="flex: 1;" @change="handleDocumentChange">
-                 <el-option 
-                   v-for="item in documentList" 
-                   :key="item.id" 
-                   :label="item.docName || item.name" 
-                   :value="item.id"
-                 />
-               </el-select> -->
                <div style="display: flex; gap: 10px;">
-                <el-select v-model="returnForm.borrowId" placeholder="璇烽�夋嫨鏂囨。" style="width: 120px;" @change="handleDocumentChange">
+                <el-select 
+                  v-if="returnOperationType !== 'edit'"
+                  v-model="returnForm.borrowId" 
+                  placeholder="璇烽�夋嫨鏂囨。" 
+                  style="width: 120px;" 
+                  @change="handleDocumentChange"
+                >
                   <el-option 
                     v-for="item in documentList" 
                     :key="item.id" 
@@ -121,6 +119,13 @@
                   />
                 </el-select>
                 <el-input
+                  v-else
+                  v-model="currentEditDocName"
+                  style="width: 120px;"
+                  disabled
+                />
+                <el-input
+                  v-if="returnOperationType !== 'edit'"
                   v-model="scanContent"
                   placeholder="鎵爜杈撳叆"
                   style="flex: 1;"
@@ -215,6 +220,7 @@
 const documentList = ref([]); // 鏂囨。鍒楄〃
 const borrowInfoList = ref([]); // 鍊熼槄淇℃伅鍒楄〃
 const scanContent = ref(); // 鎵爜鍐呭
+const currentEditDocName = ref(''); // 缂栬緫鏃跺瓨鍌ㄧ殑鏂囨。鍚嶇О
 
 // 鍒嗛〉鐩稿叧
 const pagination = reactive({
@@ -286,6 +292,7 @@
       {
         name: "缂栬緫",
         type: "text",
+        disabled: (row) => row.borrowStatus === '褰掕繕',
         clickFun: (row) => {
           openReturnDia('edit', row)
         },
@@ -396,15 +403,14 @@
   if (type === "edit") {
     // 缂栬緫妯″紡锛屽姞杞界幇鏈夋暟鎹�
     Object.assign(returnForm, data);
-    // 缂栬緫妯″紡涓嬶紝鏂囨。閫夋嫨鍚庤嚜鍔ㄥ~鍏呭�熼槄浜哄拰搴斿綊杩樻棩鏈�
-    if (returnForm.borrowId) {
-      handleDocumentChange(returnForm.borrowId);
-    }
+    // 瀛樺偍鏂囨。鍚嶇О鐢ㄤ簬鏄剧ず
+    currentEditDocName.value = data.docName || '';
   } else {
     // 鏂板妯″紡锛屾竻绌鸿〃鍗�
     Object.keys(returnForm).forEach(key => {
       returnForm[key] = "";
     });
+    currentEditDocName.value = ''; // 娓呯┖缂栬緫鏃剁殑鏂囨。鍚嶇О
     // 璁剧疆榛樿鐘舵��
     returnForm.borrowStatus = "褰掕繕";
     // 璁剧疆褰撳墠鏃ユ湡涓哄綊杩樻棩鏈�
@@ -418,6 +424,7 @@
   returnDia.value = false;
   scanContent.value = ''; // 娓呯┖鎵爜鍐呭
   borrowInfoList.value = []; // 娓呯┖鍊熼槄淇℃伅鍒楄〃
+  currentEditDocName.value = ''; // 娓呯┖缂栬緫鏃剁殑鏂囨。鍚嶇О
 };
 
 // 鎻愪氦褰掕繕琛ㄥ崟
@@ -677,7 +684,7 @@
 }
 
 .dialog-footer {
-  text-align: right;
+  text-align: center;
 }
 
 :deep(.el-form-item__label) {
diff --git a/src/views/financialManagement/accounting/index.vue b/src/views/financialManagement/accounting/index.vue
index 830fe1a..ea858e1 100644
--- a/src/views/financialManagement/accounting/index.vue
+++ b/src/views/financialManagement/accounting/index.vue
@@ -526,7 +526,6 @@
 
 /* 椤甸潰鑳屾櫙 */
 main {
-  background: #f5f5f5;
   padding: 0;
   margin: 0 -20px;
   padding: 0 20px 20px;
diff --git a/src/views/financialManagement/assets/fixedAssets.vue b/src/views/financialManagement/assets/fixedAssets.vue
new file mode 100644
index 0000000..61eb245
--- /dev/null
+++ b/src/views/financialManagement/assets/fixedAssets.vue
@@ -0,0 +1,462 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="璧勪骇缂栧彿:">
+        <el-input v-model="filters.assetCode" placeholder="璇疯緭鍏ヨ祫浜х紪鍙�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="璧勪骇鍚嶇О:">
+        <el-input v-model="filters.assetName" placeholder="璇疯緭鍏ヨ祫浜у悕绉�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="璧勪骇绫诲埆:">
+        <el-select v-model="filters.category" placeholder="璇烽�夋嫨绫诲埆" clearable style="width: 150px;">
+          <el-option label="鎴垮眿寤虹瓚" value="building" />
+          <el-option label="鏈哄櫒璁惧" value="machine" />
+          <el-option label="杩愯緭宸ュ叿" value="vehicle" />
+          <el-option label="鐢靛瓙璁惧" value="electronic" />
+          <el-option label="鍔炲叕瀹跺叿" value="furniture" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鐘舵��:">
+        <el-select v-model="filters.status" placeholder="璇烽�夋嫨鐘舵��" clearable style="width: 150px;">
+          <el-option label="鍦ㄧ敤" value="in_use" />
+          <el-option label="闂茬疆" value="idle" />
+          <el-option label="鎶ュ簾" value="scrapped" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div>
+          <el-statistic title="璧勪骇鍘熷�煎悎璁�" :value="totalOriginalValue" precision="2" prefix="楼" />
+          <el-statistic title="绱鎶樻棫鍚堣" :value="totalDepreciation" precision="2" prefix="楼" style="margin-left: 30px;" />
+          <el-statistic title="鍑�鍊煎悎璁�" :value="totalNetValue" precision="2" prefix="楼" style="margin-left: 30px;" />
+        </div>
+        <div>
+          <el-button type="primary" @click="add" icon="Plus">鏂板璧勪骇</el-button>
+          <el-button type="warning" @click="handleDepreciation" icon="Money">鎶樻棫璁℃彁</el-button>
+          <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @pagination="changePage"
+      >
+        <template #originalValue="{ row }">
+          <span class="text-primary">楼{{ formatMoney(row.originalValue) }}</span>
+        </template>
+        <template #accumulatedDepreciation="{ row }">
+          <span class="text-warning">楼{{ formatMoney(row.accumulatedDepreciation) }}</span>
+        </template>
+        <template #netValue="{ row }">
+          <span class="text-success">楼{{ formatMoney(row.netValue) }}</span>
+        </template>
+        <template #category="{ row }">
+          <el-tag>{{ getCategoryLabel(row.category) }}</el-tag>
+        </template>
+        <template #status="{ row }">
+          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
+        </template>
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="view(row)">鏌ョ湅</el-button>
+          <el-button type="primary" link @click="edit(row)">缂栬緫</el-button>
+          <el-button type="danger" link @click="handleDelete(row)">鍒犻櫎</el-button>
+        </template>
+      </PIMTable>
+    </div>
+
+    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
+      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="璧勪骇缂栧彿" prop="assetCode">
+              <el-input v-model="form.assetCode" placeholder="绯荤粺鑷姩鐢熸垚" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="璧勪骇鍚嶇О" prop="assetName">
+              <el-input v-model="form.assetName" placeholder="璇疯緭鍏ヨ祫浜у悕绉�" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="璧勪骇绫诲埆" prop="category">
+              <el-select v-model="form.category" placeholder="璇烽�夋嫨璧勪骇绫诲埆" style="width: 100%;">
+                <el-option label="鎴垮眿寤虹瓚" value="building" />
+                <el-option label="鏈哄櫒璁惧" value="machine" />
+                <el-option label="杩愯緭宸ュ叿" value="vehicle" />
+                <el-option label="鐢靛瓙璁惧" value="electronic" />
+                <el-option label="鍔炲叕瀹跺叿" value="furniture" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="瑙勬牸鍨嬪彿" prop="specification">
+              <el-input v-model="form.specification" placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="璐疆鏃ユ湡" prop="purchaseDate">
+              <el-date-picker v-model="form.purchaseDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="璧勪骇鍘熷��" prop="originalValue">
+              <el-input-number v-model="form.originalValue" :min="0" :precision="2" style="width: 100%;" @change="calculateNetValue" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="浣跨敤骞撮檺" prop="usefulLife">
+              <el-input-number v-model="form.usefulLife" :min="1" :max="50" style="width: 100%;" />
+              <span style="margin-left: 10px;">骞�</span>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="娈嬪�肩巼" prop="residualRate">
+              <el-input-number v-model="form.residualRate" :min="0" :max="10" :precision="2" style="width: 100%;" />
+              <span style="margin-left: 10px;">%</span>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="绱鎶樻棫">
+              <el-input v-model="form.accumulatedDepreciation" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="璧勪骇鍑�鍊�">
+              <el-input v-model="form.netValue" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="瀛樻斁鍦扮偣" prop="location">
+              <el-input v-model="form.location" placeholder="璇疯緭鍏ュ瓨鏀惧湴鐐�" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="浣跨敤閮ㄩ棬" prop="department">
+              <el-input v-model="form.department" placeholder="璇疯緭鍏ヤ娇鐢ㄩ儴闂�" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="淇濈浜�" prop="keeper">
+              <el-input v-model="form.keeper" placeholder="璇疯緭鍏ヤ繚绠′汉" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鐘舵��" prop="status">
+              <el-select v-model="form.status" placeholder="璇烽�夋嫨鐘舵��" style="width: 100%;">
+                <el-option label="鍦ㄧ敤" value="in_use" />
+                <el-option label="闂茬疆" value="idle" />
+                <el-option label="鎶ュ簾" value="scrapped" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitForm">纭畾</el-button>
+        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, computed } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+
+defineOptions({
+  name: "鍥哄畾璧勪骇",
+});
+
+const filters = reactive({
+  assetCode: "",
+  assetName: "",
+  category: "",
+  status: "",
+});
+
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const columns = [
+  { label: "璧勪骇缂栧彿", prop: "assetCode", width: "130" },
+  { label: "璧勪骇鍚嶇О", prop: "assetName", width: "150" },
+  { label: "璧勪骇绫诲埆", prop: "category", slot: "category" },
+  { label: "瑙勬牸鍨嬪彿", prop: "specification", width: "120" },
+  { label: "璧勪骇鍘熷��", prop: "originalValue", slot: "originalValue" },
+  { label: "绱鎶樻棫", prop: "accumulatedDepreciation", slot: "accumulatedDepreciation" },
+  { label: "璧勪骇鍑�鍊�", prop: "netValue", slot: "netValue" },
+  { label: "鐘舵��", prop: "status", slot: "status" },
+  { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "180", fixed: "right" },
+];
+
+const dataList = ref([]);
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const isEdit = ref(false);
+const currentId = ref(null);
+
+const form = reactive({
+  assetCode: "",
+  assetName: "",
+  category: "",
+  specification: "",
+  purchaseDate: "",
+  originalValue: 0,
+  usefulLife: 5,
+  residualRate: 5,
+  accumulatedDepreciation: 0,
+  netValue: 0,
+  location: "",
+  department: "",
+  keeper: "",
+  status: "in_use",
+  remark: "",
+});
+
+const rules = {
+  assetName: [{ required: true, message: "璇疯緭鍏ヨ祫浜у悕绉�", trigger: "blur" }],
+  category: [{ required: true, message: "璇烽�夋嫨璧勪骇绫诲埆", trigger: "change" }],
+  purchaseDate: [{ required: true, message: "璇烽�夋嫨璐疆鏃ユ湡", trigger: "change" }],
+  originalValue: [{ required: true, message: "璇疯緭鍏ヨ祫浜у師鍊�", trigger: "blur" }],
+  usefulLife: [{ required: true, message: "璇疯緭鍏ヤ娇鐢ㄥ勾闄�", trigger: "blur" }],
+};
+
+const mockData = [
+  { id: 1, assetCode: "GD2024001", assetName: "鍔炲叕鐢佃剳", category: "electronic", specification: "鑱旀兂ThinkPad X1", purchaseDate: "2023-01-15", originalValue: 8000, usefulLife: 5, residualRate: 5, accumulatedDepreciation: 1520, netValue: 6480, location: "鍔炲叕瀹�", department: "璐㈠姟閮�", keeper: "寮犱笁", status: "in_use", remark: "" },
+  { id: 2, assetCode: "GD2024002", assetName: "鎵撳嵃鏈�", category: "electronic", specification: "鎯犳櫘M479fdw", purchaseDate: "2023-03-20", originalValue: 3500, usefulLife: 5, residualRate: 5, accumulatedDepreciation: 532, netValue: 2968, location: "鏂囧嵃瀹�", department: "琛屾斂閮�", keeper: "鏉庡洓", status: "in_use", remark: "" },
+  { id: 3, assetCode: "GD2024003", assetName: "鍔炲叕妗屾", category: "furniture", specification: "瀹炴湪鍔炲叕妗�", purchaseDate: "2023-06-10", originalValue: 2500, usefulLife: 10, residualRate: 5, accumulatedDepreciation: 118.75, netValue: 2381.25, location: "鍔炲叕瀹�", department: "閿�鍞儴", keeper: "鐜嬩簲", status: "in_use", remark: "" },
+  { id: 4, assetCode: "GD2024004", assetName: "鍟嗗姟杞�", category: "vehicle", specification: "鍒厠GL8", purchaseDate: "2022-08-01", originalValue: 280000, usefulLife: 10, residualRate: 5, accumulatedDepreciation: 53200, netValue: 226800, location: "鍋滆溅鍦�", department: "琛屾斂閮�", keeper: "璧靛叚", status: "in_use", remark: "" },
+];
+
+const totalOriginalValue = computed(() => {
+  return dataList.value.reduce((sum, item) => sum + Number(item.originalValue), 0);
+});
+
+const totalDepreciation = computed(() => {
+  return dataList.value.reduce((sum, item) => sum + Number(item.accumulatedDepreciation), 0);
+});
+
+const totalNetValue = computed(() => {
+  return dataList.value.reduce((sum, item) => sum + Number(item.netValue), 0);
+});
+
+const formatMoney = (value) => {
+  if (value === undefined || value === null) return "0.00";
+  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+const getCategoryLabel = (category) => {
+  const map = {
+    building: "鎴垮眿寤虹瓚",
+    machine: "鏈哄櫒璁惧",
+    vehicle: "杩愯緭宸ュ叿",
+    electronic: "鐢靛瓙璁惧",
+    furniture: "鍔炲叕瀹跺叿",
+  };
+  return map[category] || category;
+};
+
+const getStatusLabel = (status) => {
+  const map = { in_use: "鍦ㄧ敤", idle: "闂茬疆", scrapped: "鎶ュ簾" };
+  return map[status] || status;
+};
+
+const getStatusType = (status) => {
+  const map = { in_use: "success", idle: "warning", scrapped: "info" };
+  return map[status] || "";
+};
+
+const calculateNetValue = () => {
+  form.netValue = Number((form.originalValue - form.accumulatedDepreciation).toFixed(2));
+};
+
+const getTableData = () => {
+  let result = [...mockData];
+  if (filters.assetCode) {
+    result = result.filter(item => item.assetCode.includes(filters.assetCode));
+  }
+  if (filters.assetName) {
+    result = result.filter(item => item.assetName.includes(filters.assetName));
+  }
+  if (filters.category) {
+    result = result.filter(item => item.category === filters.category);
+  }
+  if (filters.status) {
+    result = result.filter(item => item.status === filters.status);
+  }
+  pagination.total = result.length;
+  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
+};
+
+const resetFilters = () => {
+  filters.assetCode = "";
+  filters.assetName = "";
+  filters.category = "";
+  filters.status = "";
+  pagination.currentPage = 1;
+  getTableData();
+};
+
+const changePage = ({ current, size }) => {
+  pagination.currentPage = current;
+  pagination.pageSize = size;
+  getTableData();
+};
+
+const add = () => {
+  isEdit.value = false;
+  dialogTitle.value = "鏂板鍥哄畾璧勪骇";
+  Object.assign(form, {
+    assetCode: "GD" + Date.now().toString().slice(-8),
+    assetName: "",
+    category: "",
+    specification: "",
+    purchaseDate: new Date().toISOString().split('T')[0],
+    originalValue: 0,
+    usefulLife: 5,
+    residualRate: 5,
+    accumulatedDepreciation: 0,
+    netValue: 0,
+    location: "",
+    department: "",
+    keeper: "",
+    status: "in_use",
+    remark: "",
+  });
+  dialogVisible.value = true;
+};
+
+const edit = (row) => {
+  isEdit.value = true;
+  currentId.value = row.id;
+  dialogTitle.value = "缂栬緫鍥哄畾璧勪骇";
+  Object.assign(form, row);
+  dialogVisible.value = true;
+};
+
+const view = (row) => {
+  ElMessage.info(`鏌ョ湅璧勪骇: ${row.assetName}`);
+};
+
+const handleDelete = (row) => {
+  ElMessageBox.confirm("纭鍒犻櫎璇ュ浐瀹氳祫浜у悧锛�", "鎻愮ず", {
+    confirmButtonText: "纭畾",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData.splice(index, 1);
+    }
+    ElMessage.success("鍒犻櫎鎴愬姛");
+    getTableData();
+  });
+};
+
+const handleDepreciation = () => {
+  ElMessageBox.confirm("纭杩涜鏈湀鎶樻棫璁℃彁鍚楋紵", "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "info",
+  }).then(() => {
+    mockData.forEach(item => {
+      if (item.status === "in_use") {
+        const monthlyDepreciation = (item.originalValue * (1 - item.residualRate / 100)) / (item.usefulLife * 12);
+        item.accumulatedDepreciation = Number((item.accumulatedDepreciation + monthlyDepreciation).toFixed(2));
+        item.netValue = Number((item.originalValue - item.accumulatedDepreciation).toFixed(2));
+      }
+    });
+    ElMessage.success("鎶樻棫璁℃彁瀹屾垚");
+    getTableData();
+  });
+};
+
+const handleOut = () => {
+  ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+const submitForm = () => {
+  formRef.value.validate((valid) => {
+    if (valid) {
+      calculateNetValue();
+      if (isEdit.value) {
+        const index = mockData.findIndex(item => item.id === currentId.value);
+        if (index !== -1) {
+          mockData[index] = { ...mockData[index], ...form };
+        }
+        ElMessage.success("缂栬緫鎴愬姛");
+      } else {
+        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
+        mockData.push({ id: newId, ...form });
+        ElMessage.success("鏂板鎴愬姛");
+      }
+      dialogVisible.value = false;
+      getTableData();
+    }
+  });
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 15px;
+
+  > div:first-child {
+    display: flex;
+    align-items: center;
+  }
+}
+
+.text-primary {
+  color: #409eff;
+  font-weight: bold;
+}
+
+.text-warning {
+  color: #e6a23c;
+  font-weight: bold;
+}
+
+.text-success {
+  color: #67c23a;
+  font-weight: bold;
+}
+</style>
diff --git a/src/views/financialManagement/assets/intangibleAssets.vue b/src/views/financialManagement/assets/intangibleAssets.vue
new file mode 100644
index 0000000..14dae55
--- /dev/null
+++ b/src/views/financialManagement/assets/intangibleAssets.vue
@@ -0,0 +1,458 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="璧勪骇缂栧彿:">
+        <el-input v-model="filters.assetCode" placeholder="璇疯緭鍏ヨ祫浜х紪鍙�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="璧勪骇鍚嶇О:">
+        <el-input v-model="filters.assetName" placeholder="璇疯緭鍏ヨ祫浜у悕绉�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="璧勪骇绫诲埆:">
+        <el-select v-model="filters.category" placeholder="璇烽�夋嫨绫诲埆" clearable style="width: 150px;">
+          <el-option label="涓撳埄鏉�" value="patent" />
+          <el-option label="鍟嗘爣鏉�" value="trademark" />
+          <el-option label="钁椾綔鏉�" value="copyright" />
+          <el-option label="杞欢" value="software" />
+          <el-option label="鍦熷湴浣跨敤鏉�" value="land" />
+          <el-option label="鍏朵粬" value="other" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鐘舵��:">
+        <el-select v-model="filters.status" placeholder="璇烽�夋嫨鐘舵��" clearable style="width: 150px;">
+          <el-option label="鍦ㄧ敤" value="in_use" />
+          <el-option label="闂茬疆" value="idle" />
+          <el-option label="宸叉憡閿�瀹屾瘯" value="amortized" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div>
+          <el-statistic title="璧勪骇鍘熷�煎悎璁�" :value="totalOriginalValue" precision="2" prefix="楼" />
+          <el-statistic title="绱鎽婇攢鍚堣" :value="totalAmortization" precision="2" prefix="楼" style="margin-left: 30px;" />
+          <el-statistic title="鍑�鍊煎悎璁�" :value="totalNetValue" precision="2" prefix="楼" style="margin-left: 30px;" />
+        </div>
+        <div>
+          <el-button type="primary" @click="add" icon="Plus">鏂板璧勪骇</el-button>
+          <el-button type="warning" @click="handleAmortization" icon="Money">鎽婇攢璁℃彁</el-button>
+          <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @pagination="changePage"
+      >
+        <template #originalValue="{ row }">
+          <span class="text-primary">楼{{ formatMoney(row.originalValue) }}</span>
+        </template>
+        <template #accumulatedAmortization="{ row }">
+          <span class="text-warning">楼{{ formatMoney(row.accumulatedAmortization) }}</span>
+        </template>
+        <template #netValue="{ row }">
+          <span class="text-success">楼{{ formatMoney(row.netValue) }}</span>
+        </template>
+        <template #category="{ row }">
+          <el-tag>{{ getCategoryLabel(row.category) }}</el-tag>
+        </template>
+        <template #status="{ row }">
+          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
+        </template>
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="view(row)">鏌ョ湅</el-button>
+          <el-button type="primary" link @click="edit(row)">缂栬緫</el-button>
+          <el-button type="danger" link @click="handleDelete(row)">鍒犻櫎</el-button>
+        </template>
+      </PIMTable>
+    </div>
+
+    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
+      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="璧勪骇缂栧彿" prop="assetCode">
+              <el-input v-model="form.assetCode" placeholder="绯荤粺鑷姩鐢熸垚" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="璧勪骇鍚嶇О" prop="assetName">
+              <el-input v-model="form.assetName" placeholder="璇疯緭鍏ヨ祫浜у悕绉�" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="璧勪骇绫诲埆" prop="category">
+              <el-select v-model="form.category" placeholder="璇烽�夋嫨璧勪骇绫诲埆" style="width: 100%;">
+                <el-option label="涓撳埄鏉�" value="patent" />
+                <el-option label="鍟嗘爣鏉�" value="trademark" />
+                <el-option label="钁椾綔鏉�" value="copyright" />
+                <el-option label="杞欢" value="software" />
+                <el-option label="鍦熷湴浣跨敤鏉�" value="land" />
+                <el-option label="鍏朵粬" value="other" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="璇佷功缂栧彿" prop="certificateNo">
+              <el-input v-model="form.certificateNo" placeholder="璇疯緭鍏ヨ瘉涔︾紪鍙�" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鍙栧緱鏃ユ湡" prop="acquisitionDate">
+              <el-date-picker v-model="form.acquisitionDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="璧勪骇鍘熷��" prop="originalValue">
+              <el-input-number v-model="form.originalValue" :min="0" :precision="2" style="width: 100%;" @change="calculateNetValue" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鎽婇攢骞撮檺" prop="amortizationPeriod">
+              <el-input-number v-model="form.amortizationPeriod" :min="1" :max="50" style="width: 100%;" />
+              <span style="margin-left: 10px;">骞�</span>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="娈嬪�肩巼" prop="residualRate">
+              <el-input-number v-model="form.residualRate" :min="0" :max="10" :precision="2" style="width: 100%;" />
+              <span style="margin-left: 10px;">%</span>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="绱鎽婇攢">
+              <el-input v-model="form.accumulatedAmortization" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="璧勪骇鍑�鍊�">
+              <el-input v-model="form.netValue" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鏈夋晥鏈熻嚦" prop="validityDate">
+              <el-date-picker v-model="form.validityDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鐘舵��" prop="status">
+              <el-select v-model="form.status" placeholder="璇烽�夋嫨鐘舵��" style="width: 100%;">
+                <el-option label="鍦ㄧ敤" value="in_use" />
+                <el-option label="闂茬疆" value="idle" />
+                <el-option label="宸叉憡閿�瀹屾瘯" value="amortized" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="璧勪骇鎻忚堪" prop="description">
+          <el-input v-model="form.description" type="textarea" :rows="3" placeholder="璇疯緭鍏ヨ祫浜ф弿杩�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitForm">纭畾</el-button>
+        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, computed } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+
+defineOptions({
+  name: "鏃犲舰璧勪骇",
+});
+
+const filters = reactive({
+  assetCode: "",
+  assetName: "",
+  category: "",
+  status: "",
+});
+
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const columns = [
+  { label: "璧勪骇缂栧彿", prop: "assetCode", width: "130" },
+  { label: "璧勪骇鍚嶇О", prop: "assetName", width: "150" },
+  { label: "璧勪骇绫诲埆", prop: "category", slot: "category" },
+  { label: "璇佷功缂栧彿", prop: "certificateNo", width: "150" },
+  { label: "璧勪骇鍘熷��", prop: "originalValue", slot: "originalValue" },
+  { label: "绱鎽婇攢", prop: "accumulatedAmortization", slot: "accumulatedAmortization" },
+  { label: "璧勪骇鍑�鍊�", prop: "netValue", slot: "netValue" },
+  { label: "鐘舵��", prop: "status", slot: "status" },
+  { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "180", fixed: "right" },
+];
+
+const dataList = ref([]);
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const isEdit = ref(false);
+const currentId = ref(null);
+
+const form = reactive({
+  assetCode: "",
+  assetName: "",
+  category: "",
+  certificateNo: "",
+  acquisitionDate: "",
+  originalValue: 0,
+  amortizationPeriod: 10,
+  residualRate: 0,
+  accumulatedAmortization: 0,
+  netValue: 0,
+  validityDate: "",
+  status: "in_use",
+  description: "",
+  remark: "",
+});
+
+const rules = {
+  assetName: [{ required: true, message: "璇疯緭鍏ヨ祫浜у悕绉�", trigger: "blur" }],
+  category: [{ required: true, message: "璇烽�夋嫨璧勪骇绫诲埆", trigger: "change" }],
+  acquisitionDate: [{ required: true, message: "璇烽�夋嫨鍙栧緱鏃ユ湡", trigger: "change" }],
+  originalValue: [{ required: true, message: "璇疯緭鍏ヨ祫浜у師鍊�", trigger: "blur" }],
+  amortizationPeriod: [{ required: true, message: "璇疯緭鍏ユ憡閿�骞撮檺", trigger: "blur" }],
+};
+
+const mockData = [
+  { id: 1, assetCode: "WX2024001", assetName: "ERP杞欢璁稿彲", category: "software", certificateNo: "SW-2023-001", acquisitionDate: "2023-01-01", originalValue: 50000, amortizationPeriod: 10, residualRate: 0, accumulatedAmortization: 5000, netValue: 45000, validityDate: "2033-01-01", status: "in_use", description: "浼佷笟璧勬簮璁″垝绠$悊绯荤粺", remark: "" },
+  { id: 2, assetCode: "WX2024002", assetName: "鍙戞槑涓撳埄", category: "patent", certificateNo: "ZL202210123456.7", acquisitionDate: "2022-06-15", originalValue: 100000, amortizationPeriod: 20, residualRate: 0, accumulatedAmortization: 3750, netValue: 96250, validityDate: "2042-06-15", status: "in_use", description: "涓�绉嶆柊鍨嬬敓浜у伐鑹�", remark: "" },
+  { id: 3, assetCode: "WX2024003", assetName: "鍟嗘爣鏉�", category: "trademark", certificateNo: "TM-2023-008", acquisitionDate: "2023-03-10", originalValue: 20000, amortizationPeriod: 10, residualRate: 0, accumulatedAmortization: 1500, netValue: 18500, validityDate: "2033-03-10", status: "in_use", description: "鍏徃鍝佺墝鍟嗘爣", remark: "" },
+  { id: 4, assetCode: "WX2024004", assetName: "鍦熷湴浣跨敤鏉�", category: "land", certificateNo: "鍦熷浗鐢�(2023)绗�001鍙�", acquisitionDate: "2023-07-01", originalValue: 500000, amortizationPeriod: 50, residualRate: 0, accumulatedAmortization: 5000, netValue: 495000, validityDate: "2073-07-01", status: "in_use", description: "宸ヤ笟鐢ㄥ湴浣跨敤鏉�", remark: "" },
+];
+
+const totalOriginalValue = computed(() => {
+  return dataList.value.reduce((sum, item) => sum + Number(item.originalValue), 0);
+});
+
+const totalAmortization = computed(() => {
+  return dataList.value.reduce((sum, item) => sum + Number(item.accumulatedAmortization), 0);
+});
+
+const totalNetValue = computed(() => {
+  return dataList.value.reduce((sum, item) => sum + Number(item.netValue), 0);
+});
+
+const formatMoney = (value) => {
+  if (value === undefined || value === null) return "0.00";
+  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+const getCategoryLabel = (category) => {
+  const map = {
+    patent: "涓撳埄鏉�",
+    trademark: "鍟嗘爣鏉�",
+    copyright: "钁椾綔鏉�",
+    software: "杞欢",
+    land: "鍦熷湴浣跨敤鏉�",
+    other: "鍏朵粬",
+  };
+  return map[category] || category;
+};
+
+const getStatusLabel = (status) => {
+  const map = { in_use: "鍦ㄧ敤", idle: "闂茬疆", amortized: "宸叉憡閿�瀹屾瘯" };
+  return map[status] || status;
+};
+
+const getStatusType = (status) => {
+  const map = { in_use: "success", idle: "warning", amortized: "info" };
+  return map[status] || "";
+};
+
+const calculateNetValue = () => {
+  form.netValue = Number((form.originalValue - form.accumulatedAmortization).toFixed(2));
+};
+
+const getTableData = () => {
+  let result = [...mockData];
+  if (filters.assetCode) {
+    result = result.filter(item => item.assetCode.includes(filters.assetCode));
+  }
+  if (filters.assetName) {
+    result = result.filter(item => item.assetName.includes(filters.assetName));
+  }
+  if (filters.category) {
+    result = result.filter(item => item.category === filters.category);
+  }
+  if (filters.status) {
+    result = result.filter(item => item.status === filters.status);
+  }
+  pagination.total = result.length;
+  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
+};
+
+const resetFilters = () => {
+  filters.assetCode = "";
+  filters.assetName = "";
+  filters.category = "";
+  filters.status = "";
+  pagination.currentPage = 1;
+  getTableData();
+};
+
+const changePage = ({ current, size }) => {
+  pagination.currentPage = current;
+  pagination.pageSize = size;
+  getTableData();
+};
+
+const add = () => {
+  isEdit.value = false;
+  dialogTitle.value = "鏂板鏃犲舰璧勪骇";
+  Object.assign(form, {
+    assetCode: "WX" + Date.now().toString().slice(-8),
+    assetName: "",
+    category: "",
+    certificateNo: "",
+    acquisitionDate: new Date().toISOString().split('T')[0],
+    originalValue: 0,
+    amortizationPeriod: 10,
+    residualRate: 0,
+    accumulatedAmortization: 0,
+    netValue: 0,
+    validityDate: "",
+    status: "in_use",
+    description: "",
+    remark: "",
+  });
+  dialogVisible.value = true;
+};
+
+const edit = (row) => {
+  isEdit.value = true;
+  currentId.value = row.id;
+  dialogTitle.value = "缂栬緫鏃犲舰璧勪骇";
+  Object.assign(form, row);
+  dialogVisible.value = true;
+};
+
+const view = (row) => {
+  ElMessage.info(`鏌ョ湅璧勪骇: ${row.assetName}`);
+};
+
+const handleDelete = (row) => {
+  ElMessageBox.confirm("纭鍒犻櫎璇ユ棤褰㈣祫浜у悧锛�", "鎻愮ず", {
+    confirmButtonText: "纭畾",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData.splice(index, 1);
+    }
+    ElMessage.success("鍒犻櫎鎴愬姛");
+    getTableData();
+  });
+};
+
+const handleAmortization = () => {
+  ElMessageBox.confirm("纭杩涜鏈湀鎽婇攢璁℃彁鍚楋紵", "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "info",
+  }).then(() => {
+    mockData.forEach(item => {
+      if (item.status === "in_use") {
+        const monthlyAmortization = (item.originalValue * (1 - item.residualRate / 100)) / (item.amortizationPeriod * 12);
+        item.accumulatedAmortization = Number((item.accumulatedAmortization + monthlyAmortization).toFixed(2));
+        item.netValue = Number((item.originalValue - item.accumulatedAmortization).toFixed(2));
+        if (item.netValue <= 0) {
+          item.status = "amortized";
+          item.netValue = 0;
+        }
+      }
+    });
+    ElMessage.success("鎽婇攢璁℃彁瀹屾垚");
+    getTableData();
+  });
+};
+
+const handleOut = () => {
+  ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+const submitForm = () => {
+  formRef.value.validate((valid) => {
+    if (valid) {
+      calculateNetValue();
+      if (isEdit.value) {
+        const index = mockData.findIndex(item => item.id === currentId.value);
+        if (index !== -1) {
+          mockData[index] = { ...mockData[index], ...form };
+        }
+        ElMessage.success("缂栬緫鎴愬姛");
+      } else {
+        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
+        mockData.push({ id: newId, ...form });
+        ElMessage.success("鏂板鎴愬姛");
+      }
+      dialogVisible.value = false;
+      getTableData();
+    }
+  });
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 15px;
+
+  > div:first-child {
+    display: flex;
+    align-items: center;
+  }
+}
+
+.text-primary {
+  color: #409eff;
+  font-weight: bold;
+}
+
+.text-warning {
+  color: #e6a23c;
+  font-weight: bold;
+}
+
+.text-success {
+  color: #67c23a;
+  font-weight: bold;
+}
+</style>
diff --git a/src/views/financialManagement/expenseManagement/index.vue b/src/views/financialManagement/expenseManagement/index.vue
index ac55d01..173f8e1 100644
--- a/src/views/financialManagement/expenseManagement/index.vue
+++ b/src/views/financialManagement/expenseManagement/index.vue
@@ -76,27 +76,18 @@
       </PIMTable>
     </div>
     <Modal ref="modalRef" @success="getTableData"></Modal>
-    <FileListDialog 
-      ref="fileListRef" 
-      v-model="fileListDialogVisible"
-      :show-upload-button="true"
-      :show-delete-button="true"
-      :upload-method="handleUpload"
-      :delete-method="handleFileDelete"
-    />
+    <FileList v-if="fileDialogVisible"  v-model:visible="fileDialogVisible" record-type="account_expense" :record-id="recordId"  />
   </div>
 </template>
 
 <script setup>
 import { usePaginationApi } from "@/hooks/usePaginationApi";
-import { listPage, delAccountExpense, fileListPage, fileAdd, fileDel } from "@/api/financialManagement/expenseManagement";
-import { onMounted, getCurrentInstance, ref, computed } from "vue";
+import { listPage, delAccountExpense } from "@/api/financialManagement/expenseManagement";
+import {onMounted, getCurrentInstance, ref, computed, defineAsyncComponent} from "vue";
 import Modal from "./Modal.vue";
 import { ElMessageBox, ElMessage } from "element-plus";
 import dayjs from "dayjs";
-import FileListDialog from "@/components/Dialog/FileListDialog.vue";
-import request from "@/utils/request";
-import { getToken } from "@/utils/auth";
+const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
 
 defineOptions({
   name: "鏀嚭绠$悊",
@@ -108,9 +99,6 @@
 const modalRef = ref();
 const { checkout_payment } = proxy.useDict("checkout_payment");
 const { expense_types } = proxy.useDict("expense_types");
-const fileListRef = ref(null);
-const fileListDialogVisible = ref(false);
-const currentFileRow = ref(null);
 const accountType = ref('鏀嚭');
 
 const {
@@ -315,156 +303,16 @@
       proxy.$modal.msg("宸插彇娑�");
     });
 };
+
+// 鎵撳紑闄勪欢寮圭獥
+const recordId =ref(0)
+const fileDialogVisible = ref(false)
+
 // 鎵撳紑闄勪欢寮规
 const openFilesFormDia = async (row) => {
-  currentFileRow.value = row;
-  accountType.value = '鏀嚭';
-  try {
-    const res = await fileListPage({
-      accountId: row.id,
-      accountType: accountType.value,
-      current: 1,
-      size: 100
-    });
-    if (res.code === 200 && fileListRef.value) {
-      // 灏嗘暟鎹浆鎹负 FileListDialog 闇�瑕佺殑鏍煎紡
-      const fileList = (res.data?.records || []).map(item => ({
-        name: item.name,
-        url: item.url,
-        id: item.id,
-        ...item
-      }));
-      fileListRef.value.open(fileList);
-      fileListDialogVisible.value = true;
-    }
-  } catch (error) {
-    proxy.$modal.msgError("鑾峰彇闄勪欢鍒楄〃澶辫触");
-  }
-};
-
-// 涓婁紶闄勪欢
-const handleUpload = async () => {
-  if (!currentFileRow.value) {
-    proxy.$modal.msgWarning("璇峰厛閫夋嫨鏁版嵁");
-    return null;
-  }
-  
-  return new Promise((resolve) => {
-    // 鍒涘缓涓�涓殣钘忕殑鏂囦欢杈撳叆鍏冪礌
-    const input = document.createElement('input');
-    input.type = 'file';
-    input.style.display = 'none';
-    input.onchange = async (e) => {
-      const file = e.target.files[0];
-      if (!file) {
-        resolve(null);
-        return;
-      }
-      
-      try {
-        // 浣跨敤 FormData 涓婁紶鏂囦欢
-        const formData = new FormData();
-        formData.append('file', file);
-        
-        const uploadRes = await request({
-          url: '/file/upload',
-          method: 'post',
-          data: formData,
-          headers: {
-            'Content-Type': 'multipart/form-data',
-            Authorization: `Bearer ${getToken()}`
-          }
-        });
-        
-        if (uploadRes.code === 200) {
-          // 淇濆瓨闄勪欢淇℃伅
-          const fileData = {
-            accountId: currentFileRow.value.id,
-            accountType: accountType.value,
-            name: uploadRes.data.originalName || file.name,
-            url: uploadRes.data.tempPath || uploadRes.data.url
-          };
-          
-          const saveRes = await fileAdd(fileData);
-          if (saveRes.code === 200) {
-            proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛");
-            // 閲嶆柊鍔犺浇鏂囦欢鍒楄〃
-            const listRes = await fileListPage({
-              accountId: currentFileRow.value.id,
-              accountType: accountType.value,
-              current: 1,
-              size: 100
-            });
-            if (listRes.code === 200 && fileListRef.value) {
-              const fileList = (listRes.data?.records || []).map(item => ({
-                name: item.name,
-                url: item.url,
-                id: item.id,
-                ...item
-              }));
-              fileListRef.value.setList(fileList);
-            }
-            // 杩斿洖鏂版枃浠朵俊鎭�
-            resolve({
-              name: fileData.name,
-              url: fileData.url,
-              id: saveRes.data?.id
-            });
-          } else {
-            proxy.$modal.msgError(saveRes.msg || "鏂囦欢淇濆瓨澶辫触");
-            resolve(null);
-          }
-        } else {
-          proxy.$modal.msgError(uploadRes.msg || "鏂囦欢涓婁紶澶辫触");
-          resolve(null);
-        }
-      } catch (error) {
-        proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触");
-        resolve(null);
-      } finally {
-        document.body.removeChild(input);
-      }
-    };
-    
-    document.body.appendChild(input);
-    input.click();
-  });
-};
-
-// 鍒犻櫎闄勪欢
-const handleFileDelete = async (row) => {
-  try {
-    const res = await fileDel([row.id]);
-    if (res.code === 200) {
-      proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-      // 閲嶆柊鍔犺浇鏂囦欢鍒楄〃
-      if (currentFileRow.value && fileListRef.value) {
-        const listRes = await fileListPage({
-          accountId: currentFileRow.value.id,
-          accountType: accountType.value,
-          current: 1,
-          size: 100
-        });
-        if (listRes.code === 200) {
-          const fileList = (listRes.data?.records || []).map(item => ({
-            name: item.name,
-            url: item.url,
-            id: item.id,
-            ...item
-          }));
-          fileListRef.value.setList(fileList);
-        }
-      }
-      return true; // 杩斿洖 true 琛ㄧず鍒犻櫎鎴愬姛锛岀粍浠朵細鏇存柊鍒楄〃
-    } else {
-      proxy.$modal.msgError(res.msg || "鍒犻櫎澶辫触");
-      return false;
-    }
-  } catch (error) {
-    proxy.$modal.msgError("鍒犻櫎澶辫触");
-    return false;
-  }
-};
+  recordId.value = row.id
+  fileDialogVisible.value = true
+}
 
 onMounted(() => {
   getTableData();
diff --git a/src/views/financialManagement/generalLedger/index.vue b/src/views/financialManagement/generalLedger/index.vue
new file mode 100644
index 0000000..0570627
--- /dev/null
+++ b/src/views/financialManagement/generalLedger/index.vue
@@ -0,0 +1,291 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="绉戠洰缂栫爜:">
+        <el-input v-model="filters.subjectCode" placeholder="璇疯緭鍏ョ鐩紪鐮�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="绉戠洰鍚嶇О:">
+        <el-input v-model="filters.subjectName" placeholder="璇疯緭鍏ョ鐩悕绉�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="绉戠洰绫诲瀷:">
+        <el-select v-model="filters.subjectType" placeholder="璇烽�夋嫨" clearable style="width: 200px;">
+          <el-option label="璧勪骇绫�" value="asset" />
+          <el-option label="璐熷�虹被" value="liability" />
+          <el-option label="鏉冪泭绫�" value="equity" />
+          <el-option label="鎴愭湰绫�" value="cost" />
+          <el-option label="鎹熺泭绫�" value="profit_loss" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div></div>
+        <div>
+          <el-button type="primary" @click="add" icon="Plus">鏂板</el-button>
+          <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @pagination="changePage"
+      >
+        <template #subjectType="{ row }">
+          <el-tag :type="getSubjectTypeType(row.subjectType)">{{ getSubjectTypeLabel(row.subjectType) }}</el-tag>
+        </template>
+        <template #balanceDirection="{ row }">
+          <el-tag :type="row.balanceDirection === 'debit' ? 'success' : 'danger'">
+            {{ row.balanceDirection === 'debit' ? '鍊熸柟' : '璐锋柟' }}
+          </el-tag>
+        </template>
+        <template #status="{ row }">
+          <el-tag :type="row.status === 'active' ? 'success' : 'info'">
+            {{ row.status === 'active' ? '鍚敤' : '绂佺敤' }}
+          </el-tag>
+        </template>
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="edit(row)">缂栬緫</el-button>
+          <el-button type="danger" link @click="handleDelete(row)">鍒犻櫎</el-button>
+        </template>
+      </PIMTable>
+    </div>
+
+    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="600px" @confirm="submitForm" @cancel="dialogVisible = false">
+      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
+        <el-form-item label="绉戠洰缂栫爜" prop="subjectCode">
+          <el-input v-model="form.subjectCode" placeholder="璇疯緭鍏ョ鐩紪鐮�" />
+        </el-form-item>
+        <el-form-item label="绉戠洰鍚嶇О" prop="subjectName">
+          <el-input v-model="form.subjectName" placeholder="璇疯緭鍏ョ鐩悕绉�" />
+        </el-form-item>
+        <el-form-item label="绉戠洰绫诲瀷" prop="subjectType">
+          <el-select v-model="form.subjectType" placeholder="璇烽�夋嫨绉戠洰绫诲瀷" style="width: 100%;">
+            <el-option label="璧勪骇绫�" value="asset" />
+            <el-option label="璐熷�虹被" value="liability" />
+            <el-option label="鏉冪泭绫�" value="equity" />
+            <el-option label="鎴愭湰绫�" value="cost" />
+            <el-option label="鎹熺泭绫�" value="profit_loss" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="浣欓鏂瑰悜" prop="balanceDirection">
+          <el-radio-group v-model="form.balanceDirection">
+            <el-radio label="debit">鍊熸柟</el-radio>
+            <el-radio label="credit">璐锋柟</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="鐘舵��" prop="status">
+          <el-radio-group v-model="form.status">
+            <el-radio label="active">鍚敤</el-radio>
+            <el-radio label="inactive">绂佺敤</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitForm">纭畾</el-button>
+        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+
+defineOptions({
+  name: "鎬诲笎绉戠洰",
+});
+
+const filters = reactive({
+  subjectCode: "",
+  subjectName: "",
+  subjectType: "",
+});
+
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const columns = [
+  { label: "绉戠洰缂栫爜", prop: "subjectCode", width: "120" },
+  { label: "绉戠洰鍚嶇О", prop: "subjectName", width: "150" },
+  { label: "绉戠洰绫诲瀷", prop: "subjectType", slot: "subjectType" },
+  { label: "浣欓鏂瑰悜", prop: "balanceDirection", slot: "balanceDirection" },
+  { label: "鐘舵��", prop: "status", slot: "status" },
+  { label: "澶囨敞", prop: "remark", showOverflowTooltip: true },
+  { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "150", fixed: "right" },
+];
+
+const dataList = ref([]);
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const isEdit = ref(false);
+const currentId = ref(null);
+
+const form = reactive({
+  subjectCode: "",
+  subjectName: "",
+  subjectType: "",
+  balanceDirection: "debit",
+  status: "active",
+  remark: "",
+});
+
+const rules = {
+  subjectCode: [{ required: true, message: "璇疯緭鍏ョ鐩紪鐮�", trigger: "blur" }],
+  subjectName: [{ required: true, message: "璇疯緭鍏ョ鐩悕绉�", trigger: "blur" }],
+  subjectType: [{ required: true, message: "璇烽�夋嫨绉戠洰绫诲瀷", trigger: "change" }],
+};
+
+const mockData = [
+  { id: 1, subjectCode: "1001", subjectName: "搴撳瓨鐜伴噾", subjectType: "asset", balanceDirection: "debit", status: "active", remark: "" },
+  { id: 2, subjectCode: "1002", subjectName: "閾惰瀛樻", subjectType: "asset", balanceDirection: "debit", status: "active", remark: "" },
+  { id: 3, subjectCode: "1122", subjectName: "搴旀敹璐︽", subjectType: "asset", balanceDirection: "debit", status: "active", remark: "" },
+  { id: 4, subjectCode: "2202", subjectName: "搴斾粯璐︽", subjectType: "liability", balanceDirection: "credit", status: "active", remark: "" },
+  { id: 5, subjectCode: "4001", subjectName: "瀹炴敹璧勬湰", subjectType: "equity", balanceDirection: "credit", status: "active", remark: "" },
+  { id: 6, subjectCode: "5001", subjectName: "鐢熶骇鎴愭湰", subjectType: "cost", balanceDirection: "debit", status: "active", remark: "" },
+  { id: 7, subjectCode: "6001", subjectName: "涓昏惀涓氬姟鏀跺叆", subjectType: "profit_loss", balanceDirection: "credit", status: "active", remark: "" },
+  { id: 8, subjectCode: "6401", subjectName: "涓昏惀涓氬姟鎴愭湰", subjectType: "profit_loss", balanceDirection: "debit", status: "active", remark: "" },
+];
+
+const getSubjectTypeLabel = (type) => {
+  const map = {
+    asset: "璧勪骇绫�",
+    liability: "璐熷�虹被",
+    equity: "鏉冪泭绫�",
+    cost: "鎴愭湰绫�",
+    profit_loss: "鎹熺泭绫�",
+  };
+  return map[type] || type;
+};
+
+const getSubjectTypeType = (type) => {
+  const map = {
+    asset: "success",
+    liability: "danger",
+    equity: "warning",
+    cost: "info",
+    profit_loss: "primary",
+  };
+  return map[type] || "";
+};
+
+const getTableData = () => {
+  let result = [...mockData];
+  if (filters.subjectCode) {
+    result = result.filter(item => item.subjectCode.includes(filters.subjectCode));
+  }
+  if (filters.subjectName) {
+    result = result.filter(item => item.subjectName.includes(filters.subjectName));
+  }
+  if (filters.subjectType) {
+    result = result.filter(item => item.subjectType === filters.subjectType);
+  }
+  pagination.total = result.length;
+  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
+};
+
+const resetFilters = () => {
+  filters.subjectCode = "";
+  filters.subjectName = "";
+  filters.subjectType = "";
+  pagination.currentPage = 1;
+  getTableData();
+};
+
+const changePage = ({ current, size }) => {
+  pagination.currentPage = current;
+  pagination.pageSize = size;
+  getTableData();
+};
+
+const add = () => {
+  isEdit.value = false;
+  dialogTitle.value = "鏂板绉戠洰";
+  Object.assign(form, {
+    subjectCode: "",
+    subjectName: "",
+    subjectType: "",
+    balanceDirection: "debit",
+    status: "active",
+    remark: "",
+  });
+  dialogVisible.value = true;
+};
+
+const edit = (row) => {
+  isEdit.value = true;
+  currentId.value = row.id;
+  dialogTitle.value = "缂栬緫绉戠洰";
+  Object.assign(form, row);
+  dialogVisible.value = true;
+};
+
+const submitForm = () => {
+  formRef.value.validate((valid) => {
+    if (valid) {
+      if (isEdit.value) {
+        const index = mockData.findIndex(item => item.id === currentId.value);
+        if (index !== -1) {
+          mockData[index] = { ...mockData[index], ...form };
+        }
+        ElMessage.success("缂栬緫鎴愬姛");
+      } else {
+        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
+        mockData.push({ id: newId, ...form });
+        ElMessage.success("鏂板鎴愬姛");
+      }
+      dialogVisible.value = false;
+      getTableData();
+    }
+  });
+};
+
+const handleDelete = (row) => {
+  ElMessageBox.confirm("纭鍒犻櫎璇ョ鐩悧锛�", "鎻愮ず", {
+    confirmButtonText: "纭畾",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData.splice(index, 1);
+    }
+    ElMessage.success("鍒犻櫎鎴愬姛");
+    getTableData();
+  });
+};
+
+const handleOut = () => {
+  ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 15px;
+}
+</style>
diff --git a/src/views/financialManagement/payable/input-invoice.vue b/src/views/financialManagement/payable/input-invoice.vue
new file mode 100644
index 0000000..660d0dd
--- /dev/null
+++ b/src/views/financialManagement/payable/input-invoice.vue
@@ -0,0 +1,409 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="鍙戠エ浠g爜:">
+        <el-input v-model="filters.invoiceCode" placeholder="璇疯緭鍏ュ彂绁ㄤ唬鐮�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="鍙戠エ鍙风爜:">
+        <el-input v-model="filters.invoiceNo" placeholder="璇疯緭鍏ュ彂绁ㄥ彿鐮�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="渚涘簲鍟�:">
+        <el-select v-model="filters.supplierId" placeholder="璇烽�夋嫨渚涘簲鍟�" clearable style="width: 200px;">
+          <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="璁よ瘉鐘舵��:">
+        <el-select v-model="filters.certifyStatus" placeholder="璇烽�夋嫨璁よ瘉鐘舵��" clearable style="width: 150px;">
+          <el-option label="鏈璇�" value="uncertified" />
+          <el-option label="宸茶璇�" value="certified" />
+          <el-option label="璁よ瘉澶辫触" value="failed" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div>
+          <el-button type="success" @click="handleBatchCertify" icon="Check" :disabled="selectedRows.length === 0">鎵归噺璁よ瘉</el-button>
+        </div>
+        <div>
+          <el-button type="primary" @click="add" icon="Plus">褰曞叆鍙戠エ</el-button>
+          <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        isSelection
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @selection-change="handleSelectionChange"
+        @pagination="changePage"
+      >
+        <template #amount="{ row }">
+          <span class="text-primary">楼{{ formatMoney(row.amount) }}</span>
+        </template>
+        <template #taxAmount="{ row }">
+          <span class="text-danger">楼{{ formatMoney(row.taxAmount) }}</span>
+        </template>
+        <template #totalAmount="{ row }">
+          <span class="text-success">楼{{ formatMoney(row.totalAmount) }}</span>
+        </template>
+        <template #certifyStatus="{ row }">
+          <el-tag :type="getCertifyStatusType(row.certifyStatus)">{{ getCertifyStatusLabel(row.certifyStatus) }}</el-tag>
+        </template>
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="view(row)">鏌ョ湅</el-button>
+          <el-button type="primary" link @click="edit(row)">缂栬緫</el-button>
+          <el-button type="success" link @click="handleCertify(row)" v-if="row.certifyStatus === 'uncertified'">璁よ瘉</el-button>
+        </template>
+      </PIMTable>
+    </div>
+
+    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
+      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鍙戠エ浠g爜" prop="invoiceCode">
+              <el-input v-model="form.invoiceCode" placeholder="璇疯緭鍏ュ彂绁ㄤ唬鐮�" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鍙戠エ鍙风爜" prop="invoiceNo">
+              <el-input v-model="form.invoiceNo" placeholder="璇疯緭鍏ュ彂绁ㄥ彿鐮�" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="渚涘簲鍟�" prop="supplierId">
+              <el-select v-model="form.supplierId" placeholder="璇烽�夋嫨渚涘簲鍟�" style="width: 100%;">
+                <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="寮�绁ㄦ棩鏈�" prop="invoiceDate">
+              <el-date-picker v-model="form.invoiceDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="閲戦(涓嶅惈绋�)" prop="amount">
+              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" @change="calculateTax" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="绋庣巼" prop="taxRate">
+              <el-select v-model="form.taxRate" placeholder="璇烽�夋嫨绋庣巼" style="width: 100%;" @change="calculateTax">
+                <el-option
+                  v-for="dict in tax_rate"
+                  :key="dict.value"
+                  :label="dict.label"
+                  :value="Number(dict.value)"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="绋庨">
+              <el-input v-model="form.taxAmount" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="璁よ瘉鐘舵��" prop="certifyStatus">
+              <el-select v-model="form.certifyStatus" placeholder="璇烽�夋嫨璁よ瘉鐘舵��" style="width: 100%;" disabled>
+                <el-option label="鏈璇�" value="uncertified" />
+                <el-option label="宸茶璇�" value="certified" />
+                <el-option label="璁よ瘉澶辫触" value="failed" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="璁よ瘉鏃ユ湡" prop="certifyDate">
+              <el-date-picker v-model="form.certifyDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="鍙戠エ鍐呭" prop="content">
+          <el-input v-model="form.content" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ彂绁ㄥ唴瀹�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitForm">纭畾</el-button>
+        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, getCurrentInstance } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+
+defineOptions({
+  name: "杩涢」鍙戠エ",
+});
+
+const { proxy } = getCurrentInstance();
+const { tax_rate } = proxy.useDict("tax_rate");
+
+const filters = reactive({
+  invoiceCode: "",
+  invoiceNo: "",
+  supplierId: "",
+  certifyStatus: "",
+});
+
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const columns = [
+  { label: "鍙戠エ浠g爜", prop: "invoiceCode", width: "130" },
+  { label: "鍙戠エ鍙风爜", prop: "invoiceNo", width: "120" },
+  { label: "渚涘簲鍟�", prop: "supplierName", width: "180" },
+  { label: "寮�绁ㄦ棩鏈�", prop: "invoiceDate", width: "120" },
+  { label: "閲戦", prop: "amount", slot: "amount" },
+  { label: "绋庨", prop: "taxAmount", slot: "taxAmount" },
+  { label: "浠风◣鍚堣", prop: "totalAmount", slot: "totalAmount" },
+  { label: "璁よ瘉鐘舵��", prop: "certifyStatus", slot: "certifyStatus" },
+  { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "180", fixed: "right" },
+];
+
+const dataList = ref([]);
+const selectedRows = ref([]);
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const isEdit = ref(false);
+const currentId = ref(null);
+
+const supplierList = [
+  { id: 1, name: "鍖椾含鍘熸潗鏂欎緵搴斿晢" },
+  { id: 2, name: "涓婃捣鐢靛瓙鍏冨櫒浠跺叕鍙�" },
+  { id: 3, name: "骞垮窞鍖呰鏉愭枡鍘�" },
+  { id: 4, name: "娣卞湷浜旈噾閰嶄欢鍏徃" },
+];
+
+const form = reactive({
+  invoiceCode: "",
+  invoiceNo: "",
+  supplierId: "",
+  invoiceDate: "",
+  amount: 0,
+  taxRate: 13,
+  taxAmount: 0,
+  totalAmount: 0,
+  certifyStatus: "uncertified",
+  certifyDate: "",
+  content: "",
+  remark: "",
+});
+
+const rules = {
+  invoiceCode: [{ required: true, message: "璇疯緭鍏ュ彂绁ㄤ唬鐮�", trigger: "blur" }],
+  invoiceNo: [{ required: true, message: "璇疯緭鍏ュ彂绁ㄥ彿鐮�", trigger: "blur" }],
+  supplierId: [{ required: true, message: "璇烽�夋嫨渚涘簲鍟�", trigger: "change" }],
+  invoiceDate: [{ required: true, message: "璇烽�夋嫨寮�绁ㄦ棩鏈�", trigger: "change" }],
+  amount: [{ required: true, message: "璇疯緭鍏ラ噾棰�", trigger: "blur" }],
+  taxRate: [{ required: true, message: "璇烽�夋嫨绋庣巼", trigger: "change" }],
+};
+
+const mockData = [
+  { id: 1, invoiceCode: "0440021001", invoiceNo: "12345678", supplierId: 1, supplierName: "鍖椾含鍘熸潗鏂欎緵搴斿晢", invoiceDate: "2024-01-08", amount: 8000, taxRate: 13, taxAmount: 1040, totalAmount: 9040, certifyStatus: "certified", certifyDate: "2024-01-15", content: "鍘熸潗鏂欓噰璐�", remark: "" },
+  { id: 2, invoiceCode: "0440021002", invoiceNo: "87654321", supplierId: 2, supplierName: "涓婃捣鐢靛瓙鍏冨櫒浠跺叕鍙�", invoiceDate: "2024-01-10", amount: 12000, taxRate: 13, taxAmount: 1560, totalAmount: 13560, certifyStatus: "uncertified", certifyDate: "", content: "鐢靛瓙鍏冨櫒浠�", remark: "" },
+  { id: 3, invoiceCode: "0440021003", invoiceNo: "11112222", supplierId: 3, supplierName: "骞垮窞鍖呰鏉愭枡鍘�", invoiceDate: "2024-01-12", amount: 3500, taxRate: 13, taxAmount: 455, totalAmount: 3955, certifyStatus: "certified", certifyDate: "2024-01-18", content: "鍖呰鏉愭枡", remark: "" },
+];
+
+const formatMoney = (value) => {
+  if (value === undefined || value === null) return "0.00";
+  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+const calculateTax = () => {
+  form.taxAmount = Number((form.amount * form.taxRate / 100).toFixed(2));
+  form.totalAmount = Number((form.amount + form.taxAmount).toFixed(2));
+};
+
+const getCertifyStatusLabel = (status) => {
+  const map = { uncertified: "鏈璇�", certified: "宸茶璇�", failed: "璁よ瘉澶辫触" };
+  return map[status] || status;
+};
+
+const getCertifyStatusType = (status) => {
+  const map = { uncertified: "info", certified: "success", failed: "danger" };
+  return map[status] || "";
+};
+
+const getTableData = () => {
+  let result = [...mockData];
+  if (filters.invoiceCode) {
+    result = result.filter(item => item.invoiceCode.includes(filters.invoiceCode));
+  }
+  if (filters.invoiceNo) {
+    result = result.filter(item => item.invoiceNo.includes(filters.invoiceNo));
+  }
+  if (filters.supplierId) {
+    result = result.filter(item => item.supplierId === filters.supplierId);
+  }
+  if (filters.certifyStatus) {
+    result = result.filter(item => item.certifyStatus === filters.certifyStatus);
+  }
+  pagination.total = result.length;
+  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
+};
+
+const resetFilters = () => {
+  filters.invoiceCode = "";
+  filters.invoiceNo = "";
+  filters.supplierId = "";
+  filters.certifyStatus = "";
+  pagination.currentPage = 1;
+  getTableData();
+};
+
+const changePage = ({ current, size }) => {
+  pagination.currentPage = current;
+  pagination.pageSize = size;
+  getTableData();
+};
+
+const handleSelectionChange = (selection) => {
+  selectedRows.value = selection;
+};
+
+const add = () => {
+  isEdit.value = false;
+  dialogTitle.value = "褰曞叆鍙戠エ";
+  Object.assign(form, {
+    invoiceCode: "",
+    invoiceNo: "",
+    supplierId: "",
+    invoiceDate: new Date().toISOString().split('T')[0],
+    amount: 0,
+    taxRate: 13,
+    taxAmount: 0,
+    totalAmount: 0,
+    certifyStatus: "uncertified",
+    certifyDate: "",
+    content: "",
+    remark: "",
+  });
+  dialogVisible.value = true;
+};
+
+const edit = (row) => {
+  isEdit.value = true;
+  currentId.value = row.id;
+  dialogTitle.value = "缂栬緫鍙戠エ";
+  Object.assign(form, row);
+  dialogVisible.value = true;
+};
+
+const view = (row) => {
+  ElMessage.info(`鏌ョ湅鍙戠エ: ${row.invoiceCode}-${row.invoiceNo}`);
+};
+
+const handleCertify = (row) => {
+  ElMessageBox.confirm("纭璁よ瘉璇ュ彂绁ㄥ悧锛�", "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "info",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData[index].certifyStatus = "certified";
+      mockData[index].certifyDate = new Date().toISOString().split('T')[0];
+    }
+    ElMessage.success("璁よ瘉鎴愬姛");
+    getTableData();
+  });
+};
+
+const handleBatchCertify = () => {
+  ElMessageBox.confirm(`纭鎵归噺璁よ瘉閫変腑鐨� ${selectedRows.value.length} 寮犲彂绁ㄥ悧锛焋, "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "info",
+  }).then(() => {
+    selectedRows.value.forEach(row => {
+      const index = mockData.findIndex(item => item.id === row.id);
+      if (index !== -1 && mockData[index].certifyStatus === "uncertified") {
+        mockData[index].certifyStatus = "certified";
+        mockData[index].certifyDate = new Date().toISOString().split('T')[0];
+      }
+    });
+    ElMessage.success("鎵归噺璁よ瘉鎴愬姛");
+    getTableData();
+  });
+};
+
+const handleOut = () => {
+  ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+const submitForm = () => {
+  formRef.value.validate((valid) => {
+    if (valid) {
+      const supplier = supplierList.find(item => item.id === form.supplierId);
+      if (isEdit.value) {
+        const index = mockData.findIndex(item => item.id === currentId.value);
+        if (index !== -1) {
+          mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name };
+        }
+        ElMessage.success("缂栬緫鎴愬姛");
+      } else {
+        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
+        mockData.push({ id: newId, ...form, supplierName: supplier?.name });
+        ElMessage.success("褰曞叆鎴愬姛");
+      }
+      dialogVisible.value = false;
+      getTableData();
+    }
+  });
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 15px;
+}
+
+.text-primary {
+  color: #409eff;
+  font-weight: bold;
+}
+
+.text-danger {
+  color: #f56c6c;
+  font-weight: bold;
+}
+
+.text-success {
+  color: #67c23a;
+  font-weight: bold;
+}
+</style>
diff --git a/src/views/financialManagement/payable/payment.vue b/src/views/financialManagement/payable/payment.vue
new file mode 100644
index 0000000..d4774fe
--- /dev/null
+++ b/src/views/financialManagement/payable/payment.vue
@@ -0,0 +1,377 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="浠樻鍗曞彿:">
+        <el-input v-model="filters.paymentCode" placeholder="璇疯緭鍏ヤ粯娆惧崟鍙�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="渚涘簲鍟�:">
+        <el-select v-model="filters.supplierId" placeholder="璇烽�夋嫨渚涘簲鍟�" clearable style="width: 200px;">
+          <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="浠樻鏂瑰紡:">
+        <el-select v-model="filters.paymentMethod" placeholder="璇烽�夋嫨浠樻鏂瑰紡" clearable style="width: 150px;">
+          <el-option label="閾惰杞处" value="bank_transfer" />
+          <el-option label="鐜伴噾" value="cash" />
+          <el-option label="鏀エ" value="check" />
+          <el-option label="姹囩エ" value="draft" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鐘舵��:">
+        <el-select v-model="filters.status" placeholder="璇烽�夋嫨鐘舵��" clearable style="width: 150px;">
+          <el-option label="寰呬粯娆�" value="pending" />
+          <el-option label="宸插畬鎴�" value="completed" />
+          <el-option label="宸插彇娑�" value="cancelled" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div>
+          <el-statistic title="鏈湡浠樻鍚堣" :value="totalPaymentAmount" precision="2" prefix="楼" />
+        </div>
+        <div>
+          <el-button type="primary" @click="add" icon="Plus">鏂板浠樻</el-button>
+          <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @pagination="changePage"
+      >
+        <template #amount="{ row }">
+          <span class="text-danger">楼{{ formatMoney(row.amount) }}</span>
+        </template>
+        <template #paymentMethod="{ row }">
+          <el-tag>{{ getPaymentMethodLabel(row.paymentMethod) }}</el-tag>
+        </template>
+        <template #status="{ row }">
+          <el-tag :type="row.status === 'completed' ? 'success' : row.status === 'pending' ? 'warning' : 'info'">
+            {{ row.status === 'completed' ? '宸插畬鎴�' : row.status === 'pending' ? '寰呬粯娆�' : '宸插彇娑�' }}
+          </el-tag>
+        </template>
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="view(row)">鏌ョ湅</el-button>
+          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">缂栬緫</el-button>
+          <el-button type="success" link @click="handleComplete(row)" v-if="row.status === 'pending'">瀹屾垚</el-button>
+          <el-button type="danger" link @click="handleCancel(row)" v-if="row.status === 'pending'">鍙栨秷</el-button>
+        </template>
+      </PIMTable>
+    </div>
+
+    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
+      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="浠樻鍗曞彿" prop="paymentCode">
+              <el-input v-model="form.paymentCode" placeholder="绯荤粺鑷姩鐢熸垚" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鍏宠仈鐢宠鍗�" prop="applyCode">
+              <el-select v-model="form.applyCode" placeholder="璇烽�夋嫨鍏宠仈鐢宠鍗�" style="width: 100%;" :disabled="isEdit">
+                <el-option v-for="item in applyList" :key="item.applyCode" :label="item.applyCode" :value="item.applyCode" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="渚涘簲鍟�" prop="supplierId">
+              <el-select v-model="form.supplierId" placeholder="璇烽�夋嫨渚涘簲鍟�" style="width: 100%;" :disabled="isEdit">
+                <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="浠樻鏃ユ湡" prop="paymentDate">
+              <el-date-picker v-model="form.paymentDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="浠樻閲戦" prop="amount">
+              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="浠樻鏂瑰紡" prop="paymentMethod">
+              <el-select v-model="form.paymentMethod" placeholder="璇烽�夋嫨浠樻鏂瑰紡" style="width: 100%;">
+                <el-option label="閾惰杞处" value="bank_transfer" />
+                <el-option label="鐜伴噾" value="cash" />
+                <el-option label="鏀エ" value="check" />
+                <el-option label="姹囩エ" value="draft" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="閾惰璐﹀彿" prop="bankAccount" v-if="form.paymentMethod === 'bank_transfer'">
+              <el-input v-model="form.bankAccount" placeholder="璇疯緭鍏ラ摱琛岃处鍙�" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="寮�鎴疯" prop="bankName" v-if="form.paymentMethod === 'bank_transfer'">
+              <el-input v-model="form.bankName" placeholder="璇疯緭鍏ュ紑鎴疯" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitForm">纭畾</el-button>
+        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, computed } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+
+defineOptions({
+  name: "浠樻鍗�",
+});
+
+const filters = reactive({
+  paymentCode: "",
+  supplierId: "",
+  paymentMethod: "",
+  status: "",
+});
+
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const columns = [
+  { label: "浠樻鍗曞彿", prop: "paymentCode", width: "150" },
+  { label: "鍏宠仈鐢宠鍗�", prop: "applyCode", width: "150" },
+  { label: "渚涘簲鍟�", prop: "supplierName", width: "180" },
+  { label: "浠樻鏃ユ湡", prop: "paymentDate", width: "120" },
+  { label: "浠樻閲戦", prop: "amount", slot: "amount" },
+  { label: "浠樻鏂瑰紡", prop: "paymentMethod", slot: "paymentMethod" },
+  { label: "鐘舵��", prop: "status", slot: "status" },
+  { label: "澶囨敞", prop: "remark", showOverflowTooltip: true },
+  { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "220", fixed: "right" },
+];
+
+const dataList = ref([]);
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const isEdit = ref(false);
+const currentId = ref(null);
+
+const supplierList = [
+  { id: 1, name: "鍖椾含鍘熸潗鏂欎緵搴斿晢" },
+  { id: 2, name: "涓婃捣鐢靛瓙鍏冨櫒浠跺叕鍙�" },
+  { id: 3, name: "骞垮窞鍖呰鏉愭枡鍘�" },
+  { id: 4, name: "娣卞湷浜旈噾閰嶄欢鍏徃" },
+];
+
+const applyList = [
+  { applyCode: "FK2024001", supplierId: 1, amount: 5000 },
+  { applyCode: "FK2024002", supplierId: 2, amount: 8000 },
+  { applyCode: "FK2024003", supplierId: 3, amount: 3000 },
+];
+
+const form = reactive({
+  paymentCode: "",
+  applyCode: "",
+  supplierId: "",
+  paymentDate: "",
+  amount: 0,
+  paymentMethod: "bank_transfer",
+  bankAccount: "",
+  bankName: "",
+  remark: "",
+});
+
+const rules = {
+  applyCode: [{ required: true, message: "璇烽�夋嫨鍏宠仈鐢宠鍗�", trigger: "change" }],
+  supplierId: [{ required: true, message: "璇烽�夋嫨渚涘簲鍟�", trigger: "change" }],
+  paymentDate: [{ required: true, message: "璇烽�夋嫨浠樻鏃ユ湡", trigger: "change" }],
+  amount: [{ required: true, message: "璇疯緭鍏ヤ粯娆鹃噾棰�", trigger: "blur" }],
+  paymentMethod: [{ required: true, message: "璇烽�夋嫨浠樻鏂瑰紡", trigger: "change" }],
+};
+
+const mockData = [
+  { id: 1, paymentCode: "FKD2024001", applyCode: "FK2024001", supplierId: 1, supplierName: "鍖椾含鍘熸潗鏂欎緵搴斿晢", paymentDate: "2024-01-15", amount: 5000, paymentMethod: "bank_transfer", status: "completed", bankAccount: "6222021234567890123", bankName: "宸ュ晢閾惰", remark: "" },
+  { id: 2, paymentCode: "FKD2024002", applyCode: "FK2024002", supplierId: 2, supplierName: "涓婃捣鐢靛瓙鍏冨櫒浠跺叕鍙�", paymentDate: "2024-01-18", amount: 8000, paymentMethod: "bank_transfer", status: "pending", bankAccount: "6222029876543210987", bankName: "寤鸿閾惰", remark: "" },
+  { id: 3, paymentCode: "FKD2024003", applyCode: "FK2024003", supplierId: 3, supplierName: "骞垮窞鍖呰鏉愭枡鍘�", paymentDate: "2024-01-20", amount: 3000, paymentMethod: "cash", status: "completed", remark: "" },
+];
+
+const totalPaymentAmount = computed(() => {
+  return dataList.value.reduce((sum, item) => sum + Number(item.amount), 0);
+});
+
+const formatMoney = (value) => {
+  if (value === undefined || value === null) return "0.00";
+  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+const getPaymentMethodLabel = (method) => {
+  const map = {
+    bank_transfer: "閾惰杞处",
+    cash: "鐜伴噾",
+    check: "鏀エ",
+    draft: "姹囩エ",
+  };
+  return map[method] || method;
+};
+
+const getTableData = () => {
+  let result = [...mockData];
+  if (filters.paymentCode) {
+    result = result.filter(item => item.paymentCode.includes(filters.paymentCode));
+  }
+  if (filters.supplierId) {
+    result = result.filter(item => item.supplierId === filters.supplierId);
+  }
+  if (filters.paymentMethod) {
+    result = result.filter(item => item.paymentMethod === filters.paymentMethod);
+  }
+  if (filters.status) {
+    result = result.filter(item => item.status === filters.status);
+  }
+  pagination.total = result.length;
+  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
+};
+
+const resetFilters = () => {
+  filters.paymentCode = "";
+  filters.supplierId = "";
+  filters.paymentMethod = "";
+  filters.status = "";
+  pagination.currentPage = 1;
+  getTableData();
+};
+
+const changePage = ({ current, size }) => {
+  pagination.currentPage = current;
+  pagination.pageSize = size;
+  getTableData();
+};
+
+const add = () => {
+  isEdit.value = false;
+  dialogTitle.value = "鏂板浠樻";
+  Object.assign(form, {
+    paymentCode: "FKD" + Date.now().toString().slice(-8),
+    applyCode: "",
+    supplierId: "",
+    paymentDate: new Date().toISOString().split('T')[0],
+    amount: 0,
+    paymentMethod: "bank_transfer",
+    bankAccount: "",
+    bankName: "",
+    remark: "",
+  });
+  dialogVisible.value = true;
+};
+
+const edit = (row) => {
+  isEdit.value = true;
+  currentId.value = row.id;
+  dialogTitle.value = "缂栬緫浠樻";
+  Object.assign(form, row);
+  dialogVisible.value = true;
+};
+
+const view = (row) => {
+  ElMessage.info(`鏌ョ湅浠樻鍗�: ${row.paymentCode}`);
+};
+
+const handleComplete = (row) => {
+  ElMessageBox.confirm("纭璇ヤ粯娆惧崟宸插畬鎴愬悧锛�", "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "info",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData[index].status = "completed";
+    }
+    ElMessage.success("浠樻瀹屾垚");
+    getTableData();
+  });
+};
+
+const handleCancel = (row) => {
+  ElMessageBox.confirm("纭鍙栨秷璇ヤ粯娆惧崟鍚楋紵", "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData[index].status = "cancelled";
+    }
+    ElMessage.success("宸插彇娑�");
+    getTableData();
+  });
+};
+
+const handleOut = () => {
+  ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+const submitForm = () => {
+  formRef.value.validate((valid) => {
+    if (valid) {
+      const supplier = supplierList.find(item => item.id === form.supplierId);
+      if (isEdit.value) {
+        const index = mockData.findIndex(item => item.id === currentId.value);
+        if (index !== -1) {
+          mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name };
+        }
+        ElMessage.success("缂栬緫鎴愬姛");
+      } else {
+        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
+        mockData.push({ id: newId, ...form, supplierName: supplier?.name, status: "pending" });
+        ElMessage.success("鏂板鎴愬姛");
+      }
+      dialogVisible.value = false;
+      getTableData();
+    }
+  });
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 15px;
+}
+
+.text-danger {
+  color: #f56c6c;
+  font-weight: bold;
+}
+</style>
diff --git a/src/views/financialManagement/payable/paymentApply.vue b/src/views/financialManagement/payable/paymentApply.vue
new file mode 100644
index 0000000..fb23db3
--- /dev/null
+++ b/src/views/financialManagement/payable/paymentApply.vue
@@ -0,0 +1,360 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="鐢宠鍗曞彿:">
+        <el-input v-model="filters.applyCode" placeholder="璇疯緭鍏ョ敵璇峰崟鍙�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="渚涘簲鍟�:">
+        <el-select v-model="filters.supplierId" placeholder="璇烽�夋嫨渚涘簲鍟�" clearable style="width: 200px;">
+          <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鐘舵��:">
+        <el-select v-model="filters.status" placeholder="璇烽�夋嫨鐘舵��" clearable style="width: 150px;">
+          <el-option label="寰呭鎵�" value="pending" />
+          <el-option label="宸插鎵�" value="approved" />
+          <el-option label="宸查┏鍥�" value="rejected" />
+          <el-option label="宸蹭粯娆�" value="paid" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div></div>
+        <div>
+          <el-button type="primary" @click="add" icon="Plus">鏂板鐢宠</el-button>
+          <el-button @click="handleBatchApply" icon="Document" :disabled="selectedRows.length === 0">鎵归噺鐢宠</el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        isSelection
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @selection-change="handleSelectionChange"
+        @pagination="changePage"
+      >
+        <template #amount="{ row }">
+          <span class="text-danger">楼{{ formatMoney(row.amount) }}</span>
+        </template>
+        <template #paymentMethod="{ row }">
+          <el-tag>{{ getPaymentMethodLabel(row.paymentMethod) }}</el-tag>
+        </template>
+        <template #status="{ row }">
+          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
+        </template>
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="view(row)">鏌ョ湅</el-button>
+          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">缂栬緫</el-button>
+          <el-button type="success" link @click="handleAudit(row)" v-if="row.status === 'pending'">瀹℃壒</el-button>
+        </template>
+      </PIMTable>
+    </div>
+
+    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
+      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鐢宠鍗曞彿" prop="applyCode">
+              <el-input v-model="form.applyCode" placeholder="绯荤粺鑷姩鐢熸垚" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="渚涘簲鍟�" prop="supplierId">
+              <el-select v-model="form.supplierId" placeholder="璇烽�夋嫨渚涘簲鍟�" style="width: 100%;" :disabled="isEdit">
+                <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="浠樻閲戦" prop="amount">
+              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="浠樻鏂瑰紡" prop="paymentMethod">
+              <el-select v-model="form.paymentMethod" placeholder="璇烽�夋嫨浠樻鏂瑰紡" style="width: 100%;">
+                <el-option label="閾惰杞处" value="bank_transfer" />
+                <el-option label="鐜伴噾" value="cash" />
+                <el-option label="鏀エ" value="check" />
+                <el-option label="姹囩エ" value="draft" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鐢宠鏃ユ湡" prop="applyDate">
+              <el-date-picker v-model="form.applyDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鏈熸湜浠樻鏃ユ湡" prop="expectedDate">
+              <el-date-picker v-model="form.expectedDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="鍏宠仈鍏ュ簱鍗�" prop="relatedDocs">
+          <el-select v-model="form.relatedDocs" multiple placeholder="璇烽�夋嫨鍏宠仈鍏ュ簱鍗�" style="width: 100%;">
+            <el-option v-for="item in inList" :key="item.inCode" :label="item.inCode" :value="item.inCode" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="浠樻浜嬬敱" prop="reason">
+          <el-input v-model="form.reason" type="textarea" :rows="3" placeholder="璇疯緭鍏ヤ粯娆句簨鐢�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitForm">纭畾</el-button>
+        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+
+defineOptions({
+  name: "浠樻鐢宠",
+});
+
+const filters = reactive({
+  applyCode: "",
+  supplierId: "",
+  status: "",
+});
+
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const columns = [
+  { label: "鐢宠鍗曞彿", prop: "applyCode", width: "150" },
+  { label: "渚涘簲鍟�", prop: "supplierName", width: "180" },
+  { label: "浠樻閲戦", prop: "amount", slot: "amount" },
+  { label: "浠樻鏂瑰紡", prop: "paymentMethod", slot: "paymentMethod" },
+  { label: "鐢宠鏃ユ湡", prop: "applyDate", width: "120" },
+  { label: "鏈熸湜浠樻鏃�", prop: "expectedDate", width: "120" },
+  { label: "鐘舵��", prop: "status", slot: "status" },
+  { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "200", fixed: "right" },
+];
+
+const dataList = ref([]);
+const selectedRows = ref([]);
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const isEdit = ref(false);
+const currentId = ref(null);
+
+const supplierList = [
+  { id: 1, name: "鍖椾含鍘熸潗鏂欎緵搴斿晢" },
+  { id: 2, name: "涓婃捣鐢靛瓙鍏冨櫒浠跺叕鍙�" },
+  { id: 3, name: "骞垮窞鍖呰鏉愭枡鍘�" },
+  { id: 4, name: "娣卞湷浜旈噾閰嶄欢鍏徃" },
+];
+
+const inList = [
+  { inCode: "RK2024001", supplierId: 1 },
+  { inCode: "RK2024002", supplierId: 2 },
+  { inCode: "RK2024003", supplierId: 3 },
+];
+
+const form = reactive({
+  applyCode: "",
+  supplierId: "",
+  amount: 0,
+  paymentMethod: "bank_transfer",
+  applyDate: "",
+  expectedDate: "",
+  relatedDocs: [],
+  reason: "",
+  remark: "",
+});
+
+const rules = {
+  supplierId: [{ required: true, message: "璇烽�夋嫨渚涘簲鍟�", trigger: "change" }],
+  amount: [{ required: true, message: "璇疯緭鍏ヤ粯娆鹃噾棰�", trigger: "blur" }],
+  paymentMethod: [{ required: true, message: "璇烽�夋嫨浠樻鏂瑰紡", trigger: "change" }],
+  applyDate: [{ required: true, message: "璇烽�夋嫨鐢宠鏃ユ湡", trigger: "change" }],
+  expectedDate: [{ required: true, message: "璇烽�夋嫨鏈熸湜浠樻鏃ユ湡", trigger: "change" }],
+};
+
+const mockData = [
+  { id: 1, applyCode: "FK2024001", supplierId: 1, supplierName: "鍖椾含鍘熸潗鏂欎緵搴斿晢", amount: 5000, paymentMethod: "bank_transfer", applyDate: "2024-01-12", expectedDate: "2024-01-15", status: "pending", relatedDocs: ["RK2024001"], reason: "鏀粯鍘熸潗鏂欒揣娆�", remark: "" },
+  { id: 2, applyCode: "FK2024002", supplierId: 2, supplierName: "涓婃捣鐢靛瓙鍏冨櫒浠跺叕鍙�", amount: 8000, paymentMethod: "bank_transfer", applyDate: "2024-01-14", expectedDate: "2024-01-18", status: "approved", relatedDocs: ["RK2024002"], reason: "鏀粯鐢靛瓙鍏冨櫒浠惰揣娆�", remark: "" },
+  { id: 3, applyCode: "FK2024003", supplierId: 3, supplierName: "骞垮窞鍖呰鏉愭枡鍘�", amount: 3000, paymentMethod: "cash", applyDate: "2024-01-16", expectedDate: "2024-01-20", status: "paid", relatedDocs: ["RK2024003"], reason: "鏀粯鍖呰鏉愭枡璐ф", remark: "" },
+];
+
+const formatMoney = (value) => {
+  if (value === undefined || value === null) return "0.00";
+  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+const getPaymentMethodLabel = (method) => {
+  const map = {
+    bank_transfer: "閾惰杞处",
+    cash: "鐜伴噾",
+    check: "鏀エ",
+    draft: "姹囩エ",
+  };
+  return map[method] || method;
+};
+
+const getStatusLabel = (status) => {
+  const map = { pending: "寰呭鎵�", approved: "宸插鎵�", rejected: "宸查┏鍥�", paid: "宸蹭粯娆�" };
+  return map[status] || status;
+};
+
+const getStatusType = (status) => {
+  const map = { pending: "warning", approved: "success", rejected: "danger", paid: "primary" };
+  return map[status] || "";
+};
+
+const getTableData = () => {
+  let result = [...mockData];
+  if (filters.applyCode) {
+    result = result.filter(item => item.applyCode.includes(filters.applyCode));
+  }
+  if (filters.supplierId) {
+    result = result.filter(item => item.supplierId === filters.supplierId);
+  }
+  if (filters.status) {
+    result = result.filter(item => item.status === filters.status);
+  }
+  pagination.total = result.length;
+  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
+};
+
+const resetFilters = () => {
+  filters.applyCode = "";
+  filters.supplierId = "";
+  filters.status = "";
+  pagination.currentPage = 1;
+  getTableData();
+};
+
+const changePage = ({ current, size }) => {
+  pagination.currentPage = current;
+  pagination.pageSize = size;
+  getTableData();
+};
+
+const handleSelectionChange = (selection) => {
+  selectedRows.value = selection;
+};
+
+const add = () => {
+  isEdit.value = false;
+  dialogTitle.value = "鏂板浠樻鐢宠";
+  Object.assign(form, {
+    applyCode: "FK" + Date.now().toString().slice(-8),
+    supplierId: "",
+    amount: 0,
+    paymentMethod: "bank_transfer",
+    applyDate: new Date().toISOString().split('T')[0],
+    expectedDate: "",
+    relatedDocs: [],
+    reason: "",
+    remark: "",
+  });
+  dialogVisible.value = true;
+};
+
+const edit = (row) => {
+  isEdit.value = true;
+  currentId.value = row.id;
+  dialogTitle.value = "缂栬緫浠樻鐢宠";
+  Object.assign(form, row);
+  dialogVisible.value = true;
+};
+
+const view = (row) => {
+  ElMessage.info(`鏌ョ湅鐢宠鍗�: ${row.applyCode}`);
+};
+
+const handleAudit = (row) => {
+  ElMessageBox.confirm("纭瀹℃壒閫氳繃璇ヤ粯娆剧敵璇峰悧锛�", "鎻愮ず", {
+    confirmButtonText: "閫氳繃",
+    cancelButtonText: "椹冲洖",
+    distinguishCancelAndClose: true,
+    type: "warning",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData[index].status = "approved";
+    }
+    ElMessage.success("瀹℃壒閫氳繃");
+    getTableData();
+  }).catch((action) => {
+    if (action === "cancel") {
+      const index = mockData.findIndex(item => item.id === row.id);
+      if (index !== -1) {
+        mockData[index].status = "rejected";
+      }
+      ElMessage.warning("宸查┏鍥�");
+      getTableData();
+    }
+  });
+};
+
+const handleBatchApply = () => {
+  ElMessage.success(`鎵归噺鐢宠 ${selectedRows.value.length} 鏉¤褰昤);
+};
+
+const submitForm = () => {
+  formRef.value.validate((valid) => {
+    if (valid) {
+      const supplier = supplierList.find(item => item.id === form.supplierId);
+      if (isEdit.value) {
+        const index = mockData.findIndex(item => item.id === currentId.value);
+        if (index !== -1) {
+          mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name };
+        }
+        ElMessage.success("缂栬緫鎴愬姛");
+      } else {
+        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
+        mockData.push({ id: newId, ...form, supplierName: supplier?.name, status: "pending" });
+        ElMessage.success("鏂板鎴愬姛");
+      }
+      dialogVisible.value = false;
+      getTableData();
+    }
+  });
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 15px;
+}
+
+.text-danger {
+  color: #f56c6c;
+  font-weight: bold;
+}
+</style>
diff --git a/src/views/financialManagement/payable/purchaseIn.vue b/src/views/financialManagement/payable/purchaseIn.vue
new file mode 100644
index 0000000..4813159
--- /dev/null
+++ b/src/views/financialManagement/payable/purchaseIn.vue
@@ -0,0 +1,331 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="鍏ュ簱鍗曞彿:">
+        <el-input v-model="filters.inCode" placeholder="璇疯緭鍏ュ叆搴撳崟鍙�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="渚涘簲鍟�:">
+        <el-select v-model="filters.supplierId" placeholder="璇烽�夋嫨渚涘簲鍟�" clearable style="width: 200px;">
+          <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鍏ュ簱鏃ユ湡:">
+        <el-date-picker v-model="filters.dateRange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" range-separator="鑷�" start-placeholder="寮�濮嬫棩鏈�" end-placeholder="缁撴潫鏃ユ湡" clearable />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div></div>
+        <div>
+          <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @pagination="changePage"
+      >
+        <template #amount="{ row }">
+          <span class="text-primary">楼{{ formatMoney(row.amount) }}</span>
+        </template>
+        <template #status="{ row }">
+          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
+        </template>
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="view(row)">鏌ョ湅</el-button>
+          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">缂栬緫</el-button>
+          <el-button type="danger" link @click="handleDelete(row)" v-if="row.status === 'pending'">鍒犻櫎</el-button>
+        </template>
+      </PIMTable>
+    </div>
+
+    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
+      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鍏ュ簱鍗曞彿" prop="inCode">
+              <el-input v-model="form.inCode" placeholder="璇疯緭鍏ュ叆搴撳崟鍙�" :disabled="isEdit" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="渚涘簲鍟�" prop="supplierId">
+              <el-select v-model="form.supplierId" placeholder="璇烽�夋嫨渚涘簲鍟�" style="width: 100%;" :disabled="isEdit">
+                <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鍏ュ簱鏃ユ湡" prop="inDate">
+              <el-date-picker v-model="form.inDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鍏ュ簱閲戦" prop="amount">
+              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="鍏ュ簱鏄庣粏" prop="details">
+          <el-table :data="form.details" border style="width: 100%">
+            <el-table-column prop="materialName" label="鐗╂枡鍚嶇О" width="150">
+              <template #default="{ $index }">
+                <el-input v-model="form.details[$index].materialName" placeholder="鐗╂枡鍚嶇О" />
+              </template>
+            </el-table-column>
+            <el-table-column prop="spec" label="瑙勬牸" width="120">
+              <template #default="{ $index }">
+                <el-input v-model="form.details[$index].spec" placeholder="瑙勬牸" />
+              </template>
+            </el-table-column>
+            <el-table-column prop="quantity" label="鏁伴噺" width="100">
+              <template #default="{ $index }">
+                <el-input-number v-model="form.details[$index].quantity" :min="0" style="width: 100%;" />
+              </template>
+            </el-table-column>
+            <el-table-column prop="unitPrice" label="鍗曚环" width="120">
+              <template #default="{ $index }">
+                <el-input-number v-model="form.details[$index].unitPrice" :min="0" :precision="2" style="width: 100%;" />
+              </template>
+            </el-table-column>
+            <el-table-column prop="total" label="閲戦" width="120">
+              <template #default="{ row }">
+                <span>楼{{ formatMoney(row.quantity * row.unitPrice) }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="鎿嶄綔" width="80">
+              <template #default="{ $index }">
+                <el-button type="danger" link @click="removeDetail($index)">鍒犻櫎</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+          <el-button type="primary" link @click="addDetail" style="margin-top: 10px;">+ 娣诲姞鏄庣粏</el-button>
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitForm">纭畾</el-button>
+        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+
+defineOptions({
+  name: "閲囪喘鍏ュ簱",
+});
+
+const filters = reactive({
+  inCode: "",
+  supplierId: "",
+  dateRange: [],
+});
+
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const columns = [
+  { label: "鍏ュ簱鍗曞彿", prop: "inCode", width: "150" },
+  { label: "渚涘簲鍟�", prop: "supplierName", width: "180" },
+  { label: "鍏ュ簱鏃ユ湡", prop: "inDate", width: "120" },
+  { label: "鍏ュ簱閲戦", prop: "amount", slot: "amount" },
+  { label: "鐘舵��", prop: "status", slot: "status" },
+  { label: "澶囨敞", prop: "remark", showOverflowTooltip: true },
+  { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "200", fixed: "right" },
+];
+
+const dataList = ref([]);
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const isEdit = ref(false);
+const currentId = ref(null);
+
+const supplierList = [
+  { id: 1, name: "鍖椾含鍘熸潗鏂欎緵搴斿晢" },
+  { id: 2, name: "涓婃捣鐢靛瓙鍏冨櫒浠跺叕鍙�" },
+  { id: 3, name: "骞垮窞鍖呰鏉愭枡鍘�" },
+  { id: 4, name: "娣卞湷浜旈噾閰嶄欢鍏徃" },
+];
+
+const form = reactive({
+  inCode: "",
+  supplierId: "",
+  inDate: "",
+  amount: 0,
+  details: [],
+  remark: "",
+});
+
+const rules = {
+  inCode: [{ required: true, message: "璇疯緭鍏ュ叆搴撳崟鍙�", trigger: "blur" }],
+  supplierId: [{ required: true, message: "璇烽�夋嫨渚涘簲鍟�", trigger: "change" }],
+  inDate: [{ required: true, message: "璇烽�夋嫨鍏ュ簱鏃ユ湡", trigger: "change" }],
+  amount: [{ required: true, message: "璇疯緭鍏ュ叆搴撻噾棰�", trigger: "blur" }],
+};
+
+const mockData = [
+  { id: 1, inCode: "RK2024001", supplierId: 1, supplierName: "鍖椾含鍘熸潗鏂欎緵搴斿晢", inDate: "2024-01-10", amount: 8000, status: "approved", details: [{ materialName: "閽㈡潗", spec: "Q235", quantity: 10, unitPrice: 500 }], remark: "" },
+  { id: 2, inCode: "RK2024002", supplierId: 2, supplierName: "涓婃捣鐢靛瓙鍏冨櫒浠跺叕鍙�", inDate: "2024-01-12", amount: 12000, status: "pending", details: [{ materialName: "鑺墖", spec: "STM32", quantity: 100, unitPrice: 80 }], remark: "" },
+  { id: 3, inCode: "RK2024003", supplierId: 3, supplierName: "骞垮窞鍖呰鏉愭枡鍘�", inDate: "2024-01-15", amount: 3500, status: "approved", details: [{ materialName: "绾哥", spec: "50*40*30", quantity: 500, unitPrice: 5 }], remark: "" },
+];
+
+const formatMoney = (value) => {
+  if (value === undefined || value === null) return "0.00";
+  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+const getStatusLabel = (status) => {
+  const map = { pending: "寰呭鏍�", approved: "宸插鏍�", rejected: "宸查┏鍥�" };
+  return map[status] || status;
+};
+
+const getStatusType = (status) => {
+  const map = { pending: "warning", approved: "success", rejected: "danger" };
+  return map[status] || "";
+};
+
+const getTableData = () => {
+  let result = [...mockData];
+  if (filters.inCode) {
+    result = result.filter(item => item.inCode.includes(filters.inCode));
+  }
+  if (filters.supplierId) {
+    result = result.filter(item => item.supplierId === filters.supplierId);
+  }
+  if (filters.dateRange && filters.dateRange.length === 2) {
+    result = result.filter(item => item.inDate >= filters.dateRange[0] && item.inDate <= filters.dateRange[1]);
+  }
+  pagination.total = result.length;
+  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
+};
+
+const resetFilters = () => {
+  filters.inCode = "";
+  filters.supplierId = "";
+  filters.dateRange = [];
+  pagination.currentPage = 1;
+  getTableData();
+};
+
+const changePage = ({ current, size }) => {
+  pagination.currentPage = current;
+  pagination.pageSize = size;
+  getTableData();
+};
+
+const addDetail = () => {
+  form.details.push({ materialName: "", spec: "", quantity: 0, unitPrice: 0 });
+};
+
+const removeDetail = (index) => {
+  form.details.splice(index, 1);
+};
+
+const add = () => {
+  isEdit.value = false;
+  dialogTitle.value = "鏂板鍏ュ簱";
+  Object.assign(form, {
+    inCode: "RK" + Date.now().toString().slice(-8),
+    supplierId: "",
+    inDate: new Date().toISOString().split('T')[0],
+    amount: 0,
+    details: [{ materialName: "", spec: "", quantity: 0, unitPrice: 0 }],
+    remark: "",
+  });
+  dialogVisible.value = true;
+};
+
+const edit = (row) => {
+  isEdit.value = true;
+  currentId.value = row.id;
+  dialogTitle.value = "缂栬緫鍏ュ簱";
+  Object.assign(form, row);
+  if (!form.details || form.details.length === 0) {
+    form.details = [{ materialName: "", spec: "", quantity: 0, unitPrice: 0 }];
+  }
+  dialogVisible.value = true;
+};
+
+const view = (row) => {
+  ElMessage.info(`鏌ョ湅鍏ュ簱鍗�: ${row.inCode}`);
+};
+
+const handleDelete = (row) => {
+  ElMessageBox.confirm("纭鍒犻櫎璇ュ叆搴撳崟鍚楋紵", "鎻愮ず", {
+    confirmButtonText: "纭畾",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData.splice(index, 1);
+    }
+    ElMessage.success("鍒犻櫎鎴愬姛");
+    getTableData();
+  });
+};
+
+const handleOut = () => {
+  ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+const submitForm = () => {
+  formRef.value.validate((valid) => {
+    if (valid) {
+      const supplier = supplierList.find(item => item.id === form.supplierId);
+      if (isEdit.value) {
+        const index = mockData.findIndex(item => item.id === currentId.value);
+        if (index !== -1) {
+          mockData[index] = { ...mockData[index], ...form, supplierName: supplier?.name };
+        }
+        ElMessage.success("缂栬緫鎴愬姛");
+      } else {
+        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
+        mockData.push({ id: newId, ...form, supplierName: supplier?.name, status: "pending" });
+        ElMessage.success("鏂板鎴愬姛");
+      }
+      dialogVisible.value = false;
+      getTableData();
+    }
+  });
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 15px;
+}
+
+.text-primary {
+  color: #409eff;
+  font-weight: bold;
+}
+</style>
diff --git a/src/views/financialManagement/payable/reconciliation.vue b/src/views/financialManagement/payable/reconciliation.vue
new file mode 100644
index 0000000..3aa23cd
--- /dev/null
+++ b/src/views/financialManagement/payable/reconciliation.vue
@@ -0,0 +1,469 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="渚涘簲鍟�:">
+        <el-select v-model="filters.supplierId" placeholder="璇烽�夋嫨渚涘簲鍟�" clearable style="width: 200px;">
+          <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="瀵硅处鏈熼棿:">
+        <el-date-picker v-model="filters.startMonth" type="month" placeholder="寮�濮嬫湀浠�" value-format="YYYY-MM" style="width: 140px;" />
+        <span style="margin: 0 10px;">鑷�</span>
+        <el-date-picker v-model="filters.endMonth" type="month" placeholder="缁撴潫鏈堜唤" value-format="YYYY-MM" style="width: 140px;" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div>
+          <el-button type="primary" @click="generateStatement" icon="Document">鐢熸垚瀵硅处鍗�</el-button>
+        </div>
+        <div>
+          <el-button @click="handleOut" icon="Download">瀵煎嚭瀵硅处鍗�</el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @pagination="changePage"
+      >
+        <template #beginBalance="{ row }">
+          <span :class="row.beginBalance >= 0 ? 'text-success' : 'text-danger'">楼{{ formatMoney(row.beginBalance) }}</span>
+        </template>
+        <template #currentPayable="{ row }">
+          <span class="text-danger">楼{{ formatMoney(row.currentPayable) }}</span>
+        </template>
+        <template #currentPayment="{ row }">
+          <span class="text-success">楼{{ formatMoney(row.currentPayment) }}</span>
+        </template>
+        <template #endBalance="{ row }">
+          <span :class="row.endBalance >= 0 ? 'text-success' : 'text-danger'">楼{{ formatMoney(row.endBalance) }}</span>
+        </template>
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="viewDetail(row)">鏌ョ湅鏄庣粏</el-button>
+          <el-button type="primary" link @click="printStatement(row)">鎵撳嵃</el-button>
+        </template>
+      </PIMTable>
+    </div>
+
+    <FormDialog title="瀵硅处鏄庣粏" v-model="detailDialogVisible" width="900px" @confirm="printDetail" @cancel="detailDialogVisible = false" operationType="detail">
+      <div class="statement-header">
+        <h3>{{ currentSupplier }} 搴斾粯瀵硅处鍗�</h3>
+        <p>瀵硅处鏈熼棿: {{ currentPeriod }}</p>
+      </div>
+      <el-table :data="detailData" border style="width: 100%">
+        <el-table-column prop="date" label="鏃ユ湡" width="120" />
+        <el-table-column prop="type" label="绫诲瀷" width="100">
+          <template #default="{ row }">
+            <el-tag :type="row.type === '鍏ュ簱' ? 'success' : row.type === '閫�璐�' ? 'danger' : 'primary'">{{ row.type }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="code" label="鍗曟嵁缂栧彿" width="150" />
+        <el-table-column prop="debit" label="鍊熸柟(浠樻)" width="120">
+          <template #default="{ row }">
+            <span v-if="row.debit > 0" class="text-success">楼{{ formatMoney(row.debit) }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="credit" label="璐锋柟(搴斾粯)" width="120">
+          <template #default="{ row }">
+            <span v-if="row.credit > 0" class="text-danger">楼{{ formatMoney(row.credit) }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="balance" label="浣欓" width="120">
+          <template #default="{ row }">
+            <span :class="row.balance >= 0 ? 'text-success' : 'text-danger'">楼{{ formatMoney(row.balance) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="remark" label="澶囨敞" show-overflow-tooltip />
+      </el-table>
+      <template #footer>
+        <el-button type="primary" @click="printDetail">鎵撳嵃</el-button>
+        <el-button @click="detailDialogVisible = false">鍏抽棴</el-button>
+      </template>
+    </FormDialog>
+
+    <FormDialog title="鐢熸垚瀵硅处鍗�" v-model="generateDialogVisible" width="1000px" @confirm="confirmGenerate" @cancel="generateDialogVisible = false">
+      <el-form :model="generateForm" label-width="100px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="閫夋嫨渚涘簲鍟�" prop="supplierId">
+              <el-select v-model="generateForm.supplierId" placeholder="璇烽�夋嫨渚涘簲鍟�" style="width: 100%;" @change="onSupplierChange">
+                <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="瀵硅处鏈堜唤" prop="period">
+              <el-date-picker v-model="generateForm.period" type="month" placeholder="閫夋嫨鏈堜唤" value-format="YYYY-MM" style="width: 100%;" @change="onPeriodChange" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+
+      <div v-if="purchaseData.length > 0" class="purchase-section">
+        <div class="section-title">鏈湀閲囪喘鏁版嵁</div>
+        <el-table :data="purchaseData" border style="width: 100%; margin-bottom: 15px;" v-loading="purchaseLoading" @selection-change="handlePurchaseSelectionChange">
+          <el-table-column type="selection" width="55" align="center" />
+          <el-table-column prop="date" label="鏃ユ湡" width="120" />
+          <el-table-column prop="code" label="鍗曟嵁缂栧彿" width="150" />
+          <el-table-column prop="type" label="绫诲瀷" width="100">
+            <template #default="{ row }">
+              <el-tag :type="row.type === '鍏ュ簱' ? 'success' : 'danger'">{{ row.type }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column prop="amount" label="閲戦" width="120">
+            <template #default="{ row }">
+              <span :class="row.type === '鍏ュ簱' ? 'text-danger' : 'text-success'">楼{{ formatMoney(row.amount) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="remark" label="澶囨敞" />
+        </el-table>
+
+        <div class="summary-row">
+          <span>鏈熷垵浣欓: <strong class="text-primary">楼{{ formatMoney(generateForm.beginBalance) }}</strong></span>
+          <span>鏈湡搴斾粯: <strong class="text-danger">楼{{ formatMoney(generateForm.currentPayable) }}</strong></span>
+          <span>鏈湡浠樻: <strong class="text-success">楼{{ formatMoney(generateForm.currentPayment) }}</strong></span>
+          <span>鏈熸湯浣欓: <strong class="text-primary">楼{{ formatMoney(calculateEndBalance(generateForm.beginBalance, generateForm.currentPayable, generateForm.currentPayment)) }}</strong></span>
+        </div>
+      </div>
+
+      <div v-else-if="generateForm.supplierId && !purchaseLoading" class="empty-tip">
+        <el-empty description="璇ヤ緵搴斿晢鏈湀鏆傛棤閲囪喘鏁版嵁" />
+      </div>
+
+      <template #footer>
+        <el-button type="primary" @click="confirmGenerate" :disabled="!canGenerate">纭鐢熸垚</el-button>
+        <el-button @click="generateDialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, computed } from "vue";
+import { ElMessage } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+
+defineOptions({
+  name: "搴斾粯瀵硅处",
+});
+
+const filters = reactive({
+  supplierId: "",
+  startMonth: "",
+  endMonth: "",
+});
+
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const columns = [
+  { label: "瀵硅处鍗曞彿", prop: "statementCode", width: "150" },
+  { label: "渚涘簲鍟�", prop: "supplierName", width: "180" },
+  { label: "瀵硅处鏈熼棿", prop: "period", width: "150" },
+  { label: "鏈熷垵浣欓", prop: "beginBalance", slot: "beginBalance" },
+  { label: "鏈湡搴斾粯", prop: "currentPayable", slot: "currentPayable" },
+  { label: "鏈湡浠樻", prop: "currentPayment", slot: "currentPayment" },
+  { label: "鏈熸湯浣欓", prop: "endBalance", slot: "endBalance" },
+  { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "150", fixed: "right" },
+];
+
+const dataList = ref([]);
+const detailDialogVisible = ref(false);
+const currentSupplier = ref("");
+const currentPeriod = ref("");
+const detailData = ref([]);
+
+const generateDialogVisible = ref(false);
+const purchaseLoading = ref(false);
+const purchaseData = ref([]);
+const selectedPurchases = ref([]);
+
+const generateForm = reactive({
+  supplierId: "",
+  supplierName: "",
+  period: "",
+  beginBalance: 0,
+  currentPayable: 0,
+  currentPayment: 0,
+});
+
+const canGenerate = computed(() => {
+  return generateForm.supplierId && generateForm.period && selectedPurchases.value.length > 0;
+});
+
+const supplierList = [
+  { id: 1, name: "鍖椾含鍘熸潗鏂欎緵搴斿晢" },
+  { id: 2, name: "涓婃捣鐢靛瓙鍏冨櫒浠跺叕鍙�" },
+  { id: 3, name: "骞垮窞鍖呰鏉愭枡鍘�" },
+  { id: 4, name: "娣卞湷浜旈噾閰嶄欢鍏徃" },
+];
+
+const mockData = [
+  { id: 1, statementCode: "DZ202401001", supplierId: 1, supplierName: "鍖椾含鍘熸潗鏂欎緵搴斿晢", period: "2024-01", beginBalance: 20000, currentPayable: 15000, currentPayment: 10000, endBalance: 25000 },
+  { id: 2, statementCode: "DZ202401002", supplierId: 2, supplierName: "涓婃捣鐢靛瓙鍏冨櫒浠跺叕鍙�", period: "2024-01", beginBalance: 10000, currentPayable: 20000, currentPayment: 15000, endBalance: 15000 },
+  { id: 3, statementCode: "DZ202402001", supplierId: 1, supplierName: "鍖椾含鍘熸潗鏂欎緵搴斿晢", period: "2024-02", beginBalance: 25000, currentPayable: 18000, currentPayment: 20000, endBalance: 23000 },
+];
+
+const calculateEndBalance = (beginBalance, currentPayable, currentPayment) => {
+  return beginBalance + currentPayable - currentPayment;
+};
+
+const formatMoney = (value) => {
+  if (value === undefined || value === null) return "0.00";
+  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+const getTableData = () => {
+  let result = [...mockData];
+  if (filters.supplierId) {
+    result = result.filter(item => item.supplierId === filters.supplierId);
+  }
+  if (filters.startMonth && filters.endMonth) {
+    result = result.filter(item => item.period >= filters.startMonth && item.period <= filters.endMonth);
+  }
+  pagination.total = result.length;
+  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
+};
+
+const resetFilters = () => {
+  filters.supplierId = "";
+  filters.startMonth = "";
+  filters.endMonth = "";
+  pagination.currentPage = 1;
+  getTableData();
+};
+
+const changePage = ({ current, size }) => {
+  pagination.currentPage = current;
+  pagination.pageSize = size;
+  getTableData();
+};
+
+const generateStatement = () => {
+  generateForm.supplierId = "";
+  generateForm.supplierName = "";
+  generateForm.period = "";
+  generateForm.beginBalance = 0;
+  generateForm.currentPayable = 0;
+  generateForm.currentPayment = 0;
+  purchaseData.value = [];
+  selectedPurchases.value = [];
+  generateDialogVisible.value = true;
+};
+
+const onSupplierChange = (supplierId) => {
+  const supplier = supplierList.find(item => item.id === supplierId);
+  if (supplier) {
+    generateForm.supplierName = supplier.name;
+  }
+  loadPurchaseData();
+};
+
+const onPeriodChange = () => {
+  loadPurchaseData();
+};
+
+const loadPurchaseData = () => {
+  if (!generateForm.supplierId || !generateForm.period) {
+    purchaseData.value = [];
+    return;
+  }
+
+  purchaseLoading.value = true;
+
+  setTimeout(() => {
+    const mockPurchaseData = [
+      { id: 1, date: generateForm.period + "-05", code: "RK2024001", type: "鍏ュ簱", amount: 8000, remark: "鍘熸潗鏂欓噰璐�" },
+      { id: 2, date: generateForm.period + "-10", code: "FK2024001", type: "浠樻", amount: 5000, remark: "鏀粯璐ф" },
+      { id: 3, date: generateForm.period + "-15", code: "RK2024002", type: "鍏ュ簱", amount: 12000, remark: "鐢靛瓙鍏冨櫒浠�" },
+      { id: 4, date: generateForm.period + "-18", code: "TH2024001", type: "閫�璐�", amount: 2000, remark: "璐ㄩ噺闂閫�璐�" },
+      { id: 5, date: generateForm.period + "-22", code: "RK2024003", type: "鍏ュ簱", amount: 6000, remark: "鍖呰鏉愭枡" },
+      { id: 6, date: generateForm.period + "-25", code: "FK2024002", type: "浠樻", amount: 8000, remark: "鏀粯璐ф" },
+    ];
+
+    purchaseData.value = mockPurchaseData;
+
+    const lastPeriod = getLastPeriod(generateForm.period);
+    const lastStatement = mockData.find(item =>
+      item.supplierId === generateForm.supplierId && item.period === lastPeriod
+    );
+    generateForm.beginBalance = lastStatement ? lastStatement.endBalance : 0;
+
+    calculateSummary();
+
+    purchaseLoading.value = false;
+  }, 500);
+};
+
+const getLastPeriod = (period) => {
+  const [year, month] = period.split("-").map(Number);
+  if (month === 1) {
+    return `${year - 1}-12`;
+  }
+  return `${year}-${String(month - 1).padStart(2, "0")}`;
+};
+
+const calculateSummary = () => {
+  let payable = 0;
+  let payment = 0;
+
+  selectedPurchases.value.forEach(item => {
+    if (item.type === "鍏ュ簱") {
+      payable += item.amount;
+    } else if (item.type === "閫�璐�") {
+      payable -= item.amount;
+    } else if (item.type === "浠樻") {
+      payment += item.amount;
+    }
+  });
+
+  generateForm.currentPayable = payable;
+  generateForm.currentPayment = payment;
+};
+
+const handlePurchaseSelectionChange = (selection) => {
+  selectedPurchases.value = selection;
+  calculateSummary();
+};
+
+const confirmGenerate = () => {
+  const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
+  const endBalance = calculateEndBalance(generateForm.beginBalance, generateForm.currentPayable, generateForm.currentPayment);
+
+  mockData.unshift({
+    id: newId,
+    statementCode: "DZ" + Date.now(),
+    supplierId: generateForm.supplierId,
+    supplierName: generateForm.supplierName,
+    period: generateForm.period,
+    beginBalance: generateForm.beginBalance,
+    currentPayable: generateForm.currentPayable,
+    currentPayment: generateForm.currentPayment,
+    endBalance,
+  });
+
+  generateDialogVisible.value = false;
+  ElMessage.success("瀵硅处鍗曠敓鎴愭垚鍔�");
+  getTableData();
+};
+
+const viewDetail = (row) => {
+  currentSupplier.value = row.supplierName;
+  currentPeriod.value = row.period;
+
+  const purchaseInAmount = Math.floor(row.currentPayable * 0.7);
+  const returnAmount = Math.floor(row.currentPayable * 0.1);
+  const firstPayment = Math.floor(row.currentPayment * 0.5);
+  const secondPayment = row.currentPayment - firstPayment;
+
+  let runningBalance = row.beginBalance;
+
+  detailData.value = [
+    { date: row.period + "-01", type: "鏈熷垵", code: "-", debit: 0, credit: 0, balance: runningBalance, remark: "鏈熷垵浣欓" },
+    { date: row.period + "-05", type: "鍏ュ簱", code: "RK2024001", debit: 0, credit: purchaseInAmount, balance: runningBalance += purchaseInAmount, remark: "閲囪喘鍏ュ簱" },
+    { date: row.period + "-10", type: "浠樻", code: "FK2024001", debit: firstPayment, credit: 0, balance: runningBalance -= firstPayment, remark: "鏀粯璐ф" },
+    { date: row.period + "-15", type: "鍏ュ簱", code: "RK2024002", debit: 0, credit: row.currentPayable - purchaseInAmount - returnAmount, balance: runningBalance += (row.currentPayable - purchaseInAmount - returnAmount), remark: "閲囪喘鍏ュ簱" },
+    { date: row.period + "-20", type: "閫�璐�", code: "TH2024001", debit: 0, credit: -returnAmount, balance: runningBalance -= returnAmount, remark: "閲囪喘閫�璐�" },
+    { date: row.period + "-25", type: "浠樻", code: "FK2024002", debit: secondPayment, credit: 0, balance: runningBalance -= secondPayment, remark: "鏀粯璐ф" },
+  ];
+
+  detailDialogVisible.value = true;
+};
+
+const printStatement = (row) => {
+  ElMessage.info(`鎵撳嵃瀵硅处鍗�: ${row.statementCode}`);
+};
+
+const printDetail = () => {
+  ElMessage.info("鎵撳嵃鏄庣粏");
+};
+
+const handleOut = () => {
+  ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 15px;
+}
+
+.text-success {
+  color: #67c23a;
+}
+
+.text-danger {
+  color: #f56c6c;
+}
+
+.statement-header {
+  text-align: center;
+  margin-bottom: 20px;
+  h3 {
+    margin: 0 0 10px 0;
+  }
+  p {
+    color: #909399;
+    margin: 0;
+  }
+}
+
+.purchase-section {
+  margin-top: 20px;
+
+  .section-title {
+    font-size: 16px;
+    font-weight: bold;
+    margin-bottom: 15px;
+    padding-left: 10px;
+    border-left: 4px solid #409eff;
+  }
+}
+
+.summary-row {
+  display: flex;
+  justify-content: space-around;
+  padding: 15px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+  margin-top: 15px;
+
+  span {
+    font-size: 14px;
+
+    strong {
+      font-size: 16px;
+      margin-left: 5px;
+    }
+  }
+}
+
+.empty-tip {
+  margin-top: 30px;
+}
+
+.text-primary {
+  color: #409eff;
+}
+</style>
diff --git a/src/views/financialManagement/receivable/invoiceApply.vue b/src/views/financialManagement/receivable/invoiceApply.vue
new file mode 100644
index 0000000..2a4bd81
--- /dev/null
+++ b/src/views/financialManagement/receivable/invoiceApply.vue
@@ -0,0 +1,363 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="鐢宠鍗曞彿:">
+        <el-input v-model="filters.applyCode" placeholder="璇疯緭鍏ョ敵璇峰崟鍙�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="瀹㈡埛:">
+        <el-select v-model="filters.customerId" placeholder="璇烽�夋嫨瀹㈡埛" clearable style="width: 200px;">
+          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鐘舵��:">
+        <el-select v-model="filters.status" placeholder="璇烽�夋嫨鐘舵��" clearable style="width: 150px;">
+          <el-option label="寰呭鏍�" value="pending" />
+          <el-option label="宸插鏍�" value="approved" />
+          <el-option label="宸查┏鍥�" value="rejected" />
+          <el-option label="宸插紑绁�" value="invoiced" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div></div>
+        <div>
+          <el-button type="primary" @click="add" icon="Plus">鏂板鐢宠</el-button>
+          <el-button @click="handleBatchApply" icon="Document" :disabled="selectedRows.length === 0">鎵归噺鐢宠</el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        isSelection
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @selection-change="handleSelectionChange"
+        @pagination="changePage"
+      >
+        <template #amount="{ row }">
+          <span class="text-primary">楼{{ formatMoney(row.amount) }}</span>
+        </template>
+        <template #taxRate="{ row }">
+          <span>{{ row.taxRate }}%</span>
+        </template>
+        <template #status="{ row }">
+          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
+        </template>
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="view(row)">鏌ョ湅</el-button>
+          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">缂栬緫</el-button>
+          <el-button type="success" link @click="handleAudit(row)" v-if="row.status === 'pending'">瀹℃牳</el-button>
+          <el-button type="warning" link @click="handleInvoice(row)" v-if="row.status === 'approved'">寮�绁�</el-button>
+        </template>
+      </PIMTable>
+    </div>
+
+    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
+      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鐢宠鍗曞彿" prop="applyCode">
+              <el-input v-model="form.applyCode" placeholder="绯荤粺鑷姩鐢熸垚" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="瀹㈡埛" prop="customerId">
+              <el-select v-model="form.customerId" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%;" :disabled="isEdit">
+                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="寮�绁ㄩ噾棰�" prop="amount">
+              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="绋庣巼" prop="taxRate">
+              <el-select v-model="form.taxRate" placeholder="璇烽�夋嫨绋庣巼" style="width: 100%;">
+                <el-option
+                  v-for="dict in tax_rate"
+                  :key="dict.value"
+                  :label="dict.label"
+                  :value="Number(dict.value)"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鍙戠エ绫诲瀷" prop="invoiceType">
+              <el-select v-model="form.invoiceType" placeholder="璇烽�夋嫨鍙戠エ绫诲瀷" style="width: 100%;">
+                <el-option label="澧炲�肩◣涓撶敤鍙戠エ" value="special" />
+                <el-option label="澧炲�肩◣鏅�氬彂绁�" value="normal" />
+                <el-option label="鐢靛瓙鍙戠エ" value="electronic" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鐢宠鏃ユ湡" prop="applyDate">
+              <el-date-picker v-model="form.applyDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="鍙戠エ鍐呭" prop="content">
+          <el-input v-model="form.content" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ彂绁ㄥ唴瀹�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitForm">纭畾</el-button>
+        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, getCurrentInstance } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+
+defineOptions({
+  name: "寮�绁ㄧ敵璇�",
+});
+
+const { proxy } = getCurrentInstance();
+const { tax_rate } = proxy.useDict("tax_rate");
+
+const filters = reactive({
+  applyCode: "",
+  customerId: "",
+  status: "",
+});
+
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const columns = [
+  { label: "鐢宠鍗曞彿", prop: "applyCode", width: "150" },
+  { label: "瀹㈡埛鍚嶇О", prop: "customerName", width: "180" },
+  { label: "寮�绁ㄩ噾棰�", prop: "amount", slot: "amount" },
+  { label: "绋庣巼", prop: "taxRate", slot: "taxRate" },
+  { label: "鍙戠エ绫诲瀷", prop: "invoiceTypeLabel", width: "130" },
+  { label: "鐢宠鏃ユ湡", prop: "applyDate", width: "120" },
+  { label: "鐘舵��", prop: "status", slot: "status" },
+  { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "200", fixed: "right" },
+];
+
+const dataList = ref([]);
+const selectedRows = ref([]);
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const isEdit = ref(false);
+const currentId = ref(null);
+
+const customerList = [
+  { id: 1, name: "鍖椾含绉戞妧鏈夐檺鍏徃" },
+  { id: 2, name: "涓婃捣璐告槗鍏徃" },
+  { id: 3, name: "骞垮窞瀹炰笟鏈夐檺鍏徃" },
+  { id: 4, name: "娣卞湷鐢靛瓙鍏徃" },
+];
+
+const form = reactive({
+  applyCode: "",
+  customerId: "",
+  amount: 0,
+  taxRate: 13,
+  invoiceType: "special",
+  applyDate: "",
+  content: "",
+  remark: "",
+});
+
+const rules = {
+  customerId: [{ required: true, message: "璇烽�夋嫨瀹㈡埛", trigger: "change" }],
+  amount: [{ required: true, message: "璇疯緭鍏ュ紑绁ㄩ噾棰�", trigger: "blur" }],
+  taxRate: [{ required: true, message: "璇烽�夋嫨绋庣巼", trigger: "change" }],
+  invoiceType: [{ required: true, message: "璇烽�夋嫨鍙戠エ绫诲瀷", trigger: "change" }],
+  applyDate: [{ required: true, message: "璇烽�夋嫨鐢宠鏃ユ湡", trigger: "change" }],
+};
+
+const mockData = [
+  { id: 1, applyCode: "KP2024001", customerId: 1, customerName: "鍖椾含绉戞妧鏈夐檺鍏徃", amount: 5000, taxRate: 13, invoiceType: "special", invoiceTypeLabel: "澧炲�肩◣涓撶敤鍙戠エ", applyDate: "2024-01-15", status: "pending", content: "杞欢鏈嶅姟璐�", remark: "" },
+  { id: 2, applyCode: "KP2024002", customerId: 2, customerName: "涓婃捣璐告槗鍏徃", amount: 8000, taxRate: 13, invoiceType: "normal", invoiceTypeLabel: "澧炲�肩◣鏅�氬彂绁�", applyDate: "2024-01-16", status: "approved", content: "鍟嗗搧閿�鍞�", remark: "" },
+  { id: 3, applyCode: "KP2024003", customerId: 3, customerName: "骞垮窞瀹炰笟鏈夐檺鍏徃", amount: 12000, taxRate: 6, invoiceType: "electronic", invoiceTypeLabel: "鐢靛瓙鍙戠エ", applyDate: "2024-01-18", status: "invoiced", content: "鎶�鏈湇鍔¤垂", remark: "" },
+];
+
+const formatMoney = (value) => {
+  if (value === undefined || value === null) return "0.00";
+  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+const getStatusLabel = (status) => {
+  const map = { pending: "寰呭鏍�", approved: "宸插鏍�", rejected: "宸查┏鍥�", invoiced: "宸插紑绁�" };
+  return map[status] || status;
+};
+
+const getStatusType = (status) => {
+  const map = { pending: "warning", approved: "success", rejected: "danger", invoiced: "primary" };
+  return map[status] || "";
+};
+
+const getTableData = () => {
+  let result = [...mockData];
+  if (filters.applyCode) {
+    result = result.filter(item => item.applyCode.includes(filters.applyCode));
+  }
+  if (filters.customerId) {
+    result = result.filter(item => item.customerId === filters.customerId);
+  }
+  if (filters.status) {
+    result = result.filter(item => item.status === filters.status);
+  }
+  pagination.total = result.length;
+  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
+};
+
+const resetFilters = () => {
+  filters.applyCode = "";
+  filters.customerId = "";
+  filters.status = "";
+  pagination.currentPage = 1;
+  getTableData();
+};
+
+const changePage = ({ current, size }) => {
+  pagination.currentPage = current;
+  pagination.pageSize = size;
+  getTableData();
+};
+
+const handleSelectionChange = (selection) => {
+  selectedRows.value = selection;
+};
+
+const add = () => {
+  isEdit.value = false;
+  dialogTitle.value = "鏂板寮�绁ㄧ敵璇�";
+  Object.assign(form, {
+    applyCode: "KP" + Date.now().toString().slice(-8),
+    customerId: "",
+    amount: 0,
+    taxRate: 13,
+    invoiceType: "special",
+    applyDate: new Date().toISOString().split('T')[0],
+    content: "",
+    remark: "",
+  });
+  dialogVisible.value = true;
+};
+
+const edit = (row) => {
+  isEdit.value = true;
+  currentId.value = row.id;
+  dialogTitle.value = "缂栬緫寮�绁ㄧ敵璇�";
+  Object.assign(form, row);
+  dialogVisible.value = true;
+};
+
+const view = (row) => {
+  ElMessage.info(`鏌ョ湅鐢宠鍗�: ${row.applyCode}`);
+};
+
+const handleAudit = (row) => {
+  ElMessageBox.confirm("纭瀹℃牳閫氳繃璇ュ紑绁ㄧ敵璇峰悧锛�", "鎻愮ず", {
+    confirmButtonText: "閫氳繃",
+    cancelButtonText: "椹冲洖",
+    distinguishCancelAndClose: true,
+    type: "warning",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData[index].status = "approved";
+    }
+    ElMessage.success("瀹℃牳閫氳繃");
+    getTableData();
+  }).catch((action) => {
+    if (action === "cancel") {
+      const index = mockData.findIndex(item => item.id === row.id);
+      if (index !== -1) {
+        mockData[index].status = "rejected";
+      }
+      ElMessage.warning("宸查┏鍥�");
+      getTableData();
+    }
+  });
+};
+
+const handleInvoice = (row) => {
+  ElMessageBox.confirm("纭宸插紑鍏峰彂绁紵", "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "info",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData[index].status = "invoiced";
+    }
+    ElMessage.success("寮�绁ㄥ畬鎴�");
+    getTableData();
+  });
+};
+
+const handleBatchApply = () => {
+  ElMessage.success(`鎵归噺鐢宠 ${selectedRows.value.length} 鏉¤褰昤);
+};
+
+const submitForm = () => {
+  formRef.value.validate((valid) => {
+    if (valid) {
+      const customer = customerList.find(item => item.id === form.customerId);
+      const invoiceTypeMap = { special: "澧炲�肩◣涓撶敤鍙戠エ", normal: "澧炲�肩◣鏅�氬彂绁�", electronic: "鐢靛瓙鍙戠エ" };
+      if (isEdit.value) {
+        const index = mockData.findIndex(item => item.id === currentId.value);
+        if (index !== -1) {
+          mockData[index] = { ...mockData[index], ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType] };
+        }
+        ElMessage.success("缂栬緫鎴愬姛");
+      } else {
+        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
+        mockData.push({ id: newId, ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType], status: "pending" });
+        ElMessage.success("鏂板鎴愬姛");
+      }
+      dialogVisible.value = false;
+      getTableData();
+    }
+  });
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 15px;
+}
+
+.text-primary {
+  color: #409eff;
+  font-weight: bold;
+}
+</style>
diff --git a/src/views/financialManagement/receivable/outputInvoice.vue b/src/views/financialManagement/receivable/outputInvoice.vue
new file mode 100644
index 0000000..3e597db
--- /dev/null
+++ b/src/views/financialManagement/receivable/outputInvoice.vue
@@ -0,0 +1,373 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="鍙戠エ浠g爜:">
+        <el-input v-model="filters.invoiceCode" placeholder="璇疯緭鍏ュ彂绁ㄤ唬鐮�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="鍙戠エ鍙风爜:">
+        <el-input v-model="filters.invoiceNo" placeholder="璇疯緭鍏ュ彂绁ㄥ彿鐮�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="瀹㈡埛:">
+        <el-select v-model="filters.customerId" placeholder="璇烽�夋嫨瀹㈡埛" clearable style="width: 200px;">
+          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div></div>
+        <div>
+          <el-button type="primary" @click="add" icon="Plus">褰曞叆鍙戠エ</el-button>
+          <el-button @click="handleImport" icon="Upload">瀵煎叆</el-button>
+          <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @pagination="changePage"
+      >
+        <template #amount="{ row }">
+          <span class="text-primary">楼{{ formatMoney(row.amount) }}</span>
+        </template>
+        <template #taxAmount="{ row }">
+          <span class="text-danger">楼{{ formatMoney(row.taxAmount) }}</span>
+        </template>
+        <template #totalAmount="{ row }">
+          <span class="text-success">楼{{ formatMoney(row.totalAmount) }}</span>
+        </template>
+        <template #invoiceType="{ row }">
+          <el-tag :type="row.invoiceType === 'special' ? 'danger' : 'primary'">{{ row.invoiceTypeLabel }}</el-tag>
+        </template>
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="view(row)">鏌ョ湅</el-button>
+          <el-button type="primary" link @click="edit(row)">缂栬緫</el-button>
+          <el-button type="danger" link @click="handleDelete(row)">浣滃簾</el-button>
+        </template>
+      </PIMTable>
+    </div>
+
+    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
+      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鍙戠エ浠g爜" prop="invoiceCode">
+              <el-input v-model="form.invoiceCode" placeholder="璇疯緭鍏ュ彂绁ㄤ唬鐮�" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鍙戠エ鍙风爜" prop="invoiceNo">
+              <el-input v-model="form.invoiceNo" placeholder="璇疯緭鍏ュ彂绁ㄥ彿鐮�" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="瀹㈡埛" prop="customerId">
+              <el-select v-model="form.customerId" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%;">
+                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="寮�绁ㄦ棩鏈�" prop="invoiceDate">
+              <el-date-picker v-model="form.invoiceDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鍙戠エ绫诲瀷" prop="invoiceType">
+              <el-select v-model="form.invoiceType" placeholder="璇烽�夋嫨鍙戠エ绫诲瀷" style="width: 100%;" @change="handleInvoiceTypeChange">
+                <el-option label="澧炲�肩◣涓撶敤鍙戠エ" value="special" />
+                <el-option label="澧炲�肩◣鏅�氬彂绁�" value="normal" />
+                <el-option label="鐢靛瓙鍙戠エ" value="electronic" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="绋庣巼" prop="taxRate">
+              <el-select v-model="form.taxRate" placeholder="璇烽�夋嫨绋庣巼" style="width: 100%;" @change="calculateTax">
+                <el-option
+                  v-for="dict in tax_rate"
+                  :key="dict.value"
+                  :label="dict.label"
+                  :value="Number(dict.value)"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="閲戦(涓嶅惈绋�)" prop="amount">
+              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" @change="calculateTax" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="绋庨">
+              <el-input v-model="form.taxAmount" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="浠风◣鍚堣">
+              <el-input v-model="form.totalAmount" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="鍙戠エ鍐呭" prop="content">
+          <el-input v-model="form.content" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ彂绁ㄥ唴瀹�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitForm">纭畾</el-button>
+        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, computed, getCurrentInstance } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+
+defineOptions({
+  name: "閿�椤瑰彂绁�",
+});
+
+const { proxy } = getCurrentInstance();
+const { tax_rate } = proxy.useDict("tax_rate");
+
+const filters = reactive({
+  invoiceCode: "",
+  invoiceNo: "",
+  customerId: "",
+});
+
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const columns = [
+  { label: "鍙戠エ浠g爜", prop: "invoiceCode", width: "130" },
+  { label: "鍙戠エ鍙风爜", prop: "invoiceNo", width: "120" },
+  { label: "瀹㈡埛鍚嶇О", prop: "customerName", width: "180" },
+  { label: "寮�绁ㄦ棩鏈�", prop: "invoiceDate", width: "120" },
+  { label: "閲戦", prop: "amount", slot: "amount" },
+  { label: "绋庨", prop: "taxAmount", slot: "taxAmount" },
+  { label: "浠风◣鍚堣", prop: "totalAmount", slot: "totalAmount" },
+  { label: "鍙戠エ绫诲瀷", prop: "invoiceType", slot: "invoiceType" },
+  { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "180", fixed: "right" },
+];
+
+const dataList = ref([]);
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const isEdit = ref(false);
+const currentId = ref(null);
+
+const customerList = [
+  { id: 1, name: "鍖椾含绉戞妧鏈夐檺鍏徃" },
+  { id: 2, name: "涓婃捣璐告槗鍏徃" },
+  { id: 3, name: "骞垮窞瀹炰笟鏈夐檺鍏徃" },
+  { id: 4, name: "娣卞湷鐢靛瓙鍏徃" },
+];
+
+const form = reactive({
+  invoiceCode: "",
+  invoiceNo: "",
+  customerId: "",
+  invoiceDate: "",
+  invoiceType: "special",
+  taxRate: 13,
+  amount: 0,
+  taxAmount: 0,
+  totalAmount: 0,
+  content: "",
+  remark: "",
+});
+
+const rules = {
+  invoiceCode: [{ required: true, message: "璇疯緭鍏ュ彂绁ㄤ唬鐮�", trigger: "blur" }],
+  invoiceNo: [{ required: true, message: "璇疯緭鍏ュ彂绁ㄥ彿鐮�", trigger: "blur" }],
+  customerId: [{ required: true, message: "璇烽�夋嫨瀹㈡埛", trigger: "change" }],
+  invoiceDate: [{ required: true, message: "璇烽�夋嫨寮�绁ㄦ棩鏈�", trigger: "change" }],
+  invoiceType: [{ required: true, message: "璇烽�夋嫨鍙戠エ绫诲瀷", trigger: "change" }],
+  taxRate: [{ required: true, message: "璇烽�夋嫨绋庣巼", trigger: "change" }],
+  amount: [{ required: true, message: "璇疯緭鍏ラ噾棰�", trigger: "blur" }],
+};
+
+const mockData = [
+  { id: 1, invoiceCode: "0440021001", invoiceNo: "12345678", customerId: 1, customerName: "鍖椾含绉戞妧鏈夐檺鍏徃", invoiceDate: "2024-01-15", amount: 5000, taxRate: 13, taxAmount: 650, totalAmount: 5650, invoiceType: "special", invoiceTypeLabel: "澧炲�肩◣涓撶敤鍙戠エ", content: "杞欢鏈嶅姟璐�", remark: "" },
+  { id: 2, invoiceCode: "0440021002", invoiceNo: "87654321", customerId: 2, customerName: "涓婃捣璐告槗鍏徃", invoiceDate: "2024-01-16", amount: 8000, taxRate: 13, taxAmount: 1040, totalAmount: 9040, invoiceType: "normal", invoiceTypeLabel: "澧炲�肩◣鏅�氬彂绁�", content: "鍟嗗搧閿�鍞�", remark: "" },
+  { id: 3, invoiceCode: "0440021003", invoiceNo: "11112222", customerId: 3, customerName: "骞垮窞瀹炰笟鏈夐檺鍏徃", invoiceDate: "2024-01-18", amount: 12000, taxRate: 6, taxAmount: 720, totalAmount: 12720, invoiceType: "electronic", invoiceTypeLabel: "鐢靛瓙鍙戠エ", content: "鎶�鏈湇鍔¤垂", remark: "" },
+];
+
+const formatMoney = (value) => {
+  if (value === undefined || value === null) return "0.00";
+  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+const calculateTax = () => {
+  form.taxAmount = Number((form.amount * form.taxRate / 100).toFixed(2));
+  form.totalAmount = Number((form.amount + form.taxAmount).toFixed(2));
+};
+
+const handleInvoiceTypeChange = () => {
+  if (form.invoiceType === "special") {
+    form.taxRate = 13;
+  } else {
+    form.taxRate = 13;
+  }
+  calculateTax();
+};
+
+const getTableData = () => {
+  let result = [...mockData];
+  if (filters.invoiceCode) {
+    result = result.filter(item => item.invoiceCode.includes(filters.invoiceCode));
+  }
+  if (filters.invoiceNo) {
+    result = result.filter(item => item.invoiceNo.includes(filters.invoiceNo));
+  }
+  if (filters.customerId) {
+    result = result.filter(item => item.customerId === filters.customerId);
+  }
+  pagination.total = result.length;
+  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
+};
+
+const resetFilters = () => {
+  filters.invoiceCode = "";
+  filters.invoiceNo = "";
+  filters.customerId = "";
+  pagination.currentPage = 1;
+  getTableData();
+};
+
+const changePage = ({ current, size }) => {
+  pagination.currentPage = current;
+  pagination.pageSize = size;
+  getTableData();
+};
+
+const add = () => {
+  isEdit.value = false;
+  dialogTitle.value = "褰曞叆鍙戠エ";
+  Object.assign(form, {
+    invoiceCode: "",
+    invoiceNo: "",
+    customerId: "",
+    invoiceDate: new Date().toISOString().split('T')[0],
+    invoiceType: "special",
+    taxRate: 13,
+    amount: 0,
+    taxAmount: 0,
+    totalAmount: 0,
+    content: "",
+    remark: "",
+  });
+  dialogVisible.value = true;
+};
+
+const edit = (row) => {
+  isEdit.value = true;
+  currentId.value = row.id;
+  dialogTitle.value = "缂栬緫鍙戠エ";
+  Object.assign(form, row);
+  dialogVisible.value = true;
+};
+
+const view = (row) => {
+  ElMessage.info(`鏌ョ湅鍙戠エ: ${row.invoiceCode}-${row.invoiceNo}`);
+};
+
+const handleDelete = (row) => {
+  ElMessageBox.confirm("纭浣滃簾璇ュ彂绁ㄥ悧锛�", "鎻愮ず", {
+    confirmButtonText: "纭畾",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData.splice(index, 1);
+    }
+    ElMessage.success("浣滃簾鎴愬姛");
+    getTableData();
+  });
+};
+
+const handleImport = () => {
+  ElMessage.info("瀵煎叆鍔熻兘");
+};
+
+const handleOut = () => {
+  ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+const submitForm = () => {
+  formRef.value.validate((valid) => {
+    if (valid) {
+      const customer = customerList.find(item => item.id === form.customerId);
+      const invoiceTypeMap = { special: "澧炲�肩◣涓撶敤鍙戠エ", normal: "澧炲�肩◣鏅�氬彂绁�", electronic: "鐢靛瓙鍙戠エ" };
+      if (isEdit.value) {
+        const index = mockData.findIndex(item => item.id === currentId.value);
+        if (index !== -1) {
+          mockData[index] = { ...mockData[index], ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType] };
+        }
+        ElMessage.success("缂栬緫鎴愬姛");
+      } else {
+        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
+        mockData.push({ id: newId, ...form, customerName: customer?.name, invoiceTypeLabel: invoiceTypeMap[form.invoiceType] });
+        ElMessage.success("褰曞叆鎴愬姛");
+      }
+      dialogVisible.value = false;
+      getTableData();
+    }
+  });
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 15px;
+}
+
+.text-primary {
+  color: #409eff;
+  font-weight: bold;
+}
+
+.text-danger {
+  color: #f56c6c;
+  font-weight: bold;
+}
+
+.text-success {
+  color: #67c23a;
+  font-weight: bold;
+}
+</style>
diff --git a/src/views/financialManagement/receivable/receipt.vue b/src/views/financialManagement/receivable/receipt.vue
new file mode 100644
index 0000000..2bbbb96
--- /dev/null
+++ b/src/views/financialManagement/receivable/receipt.vue
@@ -0,0 +1,356 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="鏀舵鍗曞彿:">
+        <el-input v-model="filters.receiptCode" placeholder="璇疯緭鍏ユ敹娆惧崟鍙�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="瀹㈡埛:">
+        <el-select v-model="filters.customerId" placeholder="璇烽�夋嫨瀹㈡埛" clearable style="width: 200px;">
+          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鏀舵鏂瑰紡:">
+        <el-select v-model="filters.receiptMethod" placeholder="璇烽�夋嫨鏀舵鏂瑰紡" clearable style="width: 150px;">
+          <el-option label="閾惰杞处" value="bank_transfer" />
+          <el-option label="鐜伴噾" value="cash" />
+          <el-option label="鏀エ" value="check" />
+          <el-option label="姹囩エ" value="draft" />
+          <el-option label="鏀粯瀹�" value="alipay" />
+          <el-option label="寰俊" value="wechat" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div>
+          <el-statistic title="鏈湡鏀舵鍚堣" :value="totalReceiptAmount" precision="2" prefix="楼" />
+        </div>
+        <div>
+          <el-button type="primary" @click="add" icon="Plus">鏂板鏀舵</el-button>
+          <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @pagination="changePage"
+      >
+        <template #amount="{ row }">
+          <span class="text-success">楼{{ formatMoney(row.amount) }}</span>
+        </template>
+        <template #receiptMethod="{ row }">
+          <el-tag>{{ getReceiptMethodLabel(row.receiptMethod) }}</el-tag>
+        </template>
+        <template #status="{ row }">
+          <el-tag :type="row.status === 'confirmed' ? 'success' : 'warning'">{{ row.status === 'confirmed' ? '宸茬‘璁�' : '寰呯‘璁�' }}</el-tag>
+        </template>
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="view(row)">鏌ョ湅</el-button>
+          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">缂栬緫</el-button>
+          <el-button type="success" link @click="handleConfirm(row)" v-if="row.status === 'pending'">纭</el-button>
+          <el-button type="danger" link @click="handleDelete(row)" v-if="row.status === 'pending'">鍒犻櫎</el-button>
+        </template>
+      </PIMTable>
+    </div>
+
+    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
+      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鏀舵鍗曞彿" prop="receiptCode">
+              <el-input v-model="form.receiptCode" placeholder="绯荤粺鑷姩鐢熸垚" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="瀹㈡埛" prop="customerId">
+              <el-select v-model="form.customerId" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%;" :disabled="isEdit">
+                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鏀舵鏃ユ湡" prop="receiptDate">
+              <el-date-picker v-model="form.receiptDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鏀舵閲戦" prop="amount">
+              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鏀舵鏂瑰紡" prop="receiptMethod">
+              <el-select v-model="form.receiptMethod" placeholder="璇烽�夋嫨鏀舵鏂瑰紡" style="width: 100%;">
+                <el-option label="閾惰杞处" value="bank_transfer" />
+                <el-option label="鐜伴噾" value="cash" />
+                <el-option label="鏀エ" value="check" />
+                <el-option label="姹囩エ" value="draft" />
+                <el-option label="鏀粯瀹�" value="alipay" />
+                <el-option label="寰俊" value="wechat" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="閾惰璐﹀彿" prop="bankAccount" v-if="form.receiptMethod === 'bank_transfer'">
+              <el-input v-model="form.bankAccount" placeholder="璇疯緭鍏ラ摱琛岃处鍙�" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="鍏宠仈鍗曟嵁" prop="relatedDocs">
+          <el-select v-model="form.relatedDocs" multiple placeholder="璇烽�夋嫨鍏宠仈鍗曟嵁" style="width: 100%;">
+            <el-option v-for="item in outList" :key="item.outCode" :label="item.outCode" :value="item.outCode" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitForm">纭畾</el-button>
+        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, computed } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+
+defineOptions({
+  name: "鏀舵鍗�",
+});
+
+const filters = reactive({
+  receiptCode: "",
+  customerId: "",
+  receiptMethod: "",
+});
+
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const columns = [
+  { label: "鏀舵鍗曞彿", prop: "receiptCode", width: "150" },
+  { label: "瀹㈡埛鍚嶇О", prop: "customerName", width: "180" },
+  { label: "鏀舵鏃ユ湡", prop: "receiptDate", width: "120" },
+  { label: "鏀舵閲戦", prop: "amount", slot: "amount" },
+  { label: "鏀舵鏂瑰紡", prop: "receiptMethod", slot: "receiptMethod" },
+  { label: "鐘舵��", prop: "status", slot: "status" },
+  { label: "澶囨敞", prop: "remark", showOverflowTooltip: true },
+  { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "220", fixed: "right" },
+];
+
+const dataList = ref([]);
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const isEdit = ref(false);
+const currentId = ref(null);
+
+const customerList = [
+  { id: 1, name: "鍖椾含绉戞妧鏈夐檺鍏徃" },
+  { id: 2, name: "涓婃捣璐告槗鍏徃" },
+  { id: 3, name: "骞垮窞瀹炰笟鏈夐檺鍏徃" },
+  { id: 4, name: "娣卞湷鐢靛瓙鍏徃" },
+];
+
+const outList = [
+  { outCode: "CK2024001", customerId: 1 },
+  { outCode: "CK2024002", customerId: 2 },
+  { outCode: "CK2024003", customerId: 3 },
+];
+
+const form = reactive({
+  receiptCode: "",
+  customerId: "",
+  receiptDate: "",
+  amount: 0,
+  receiptMethod: "bank_transfer",
+  bankAccount: "",
+  relatedDocs: [],
+  remark: "",
+});
+
+const rules = {
+  customerId: [{ required: true, message: "璇烽�夋嫨瀹㈡埛", trigger: "change" }],
+  receiptDate: [{ required: true, message: "璇烽�夋嫨鏀舵鏃ユ湡", trigger: "change" }],
+  amount: [{ required: true, message: "璇疯緭鍏ユ敹娆鹃噾棰�", trigger: "blur" }],
+  receiptMethod: [{ required: true, message: "璇烽�夋嫨鏀舵鏂瑰紡", trigger: "change" }],
+};
+
+const mockData = [
+  { id: 1, receiptCode: "SK2024001", customerId: 1, customerName: "鍖椾含绉戞妧鏈夐檺鍏徃", receiptDate: "2024-01-16", amount: 3000, receiptMethod: "bank_transfer", status: "confirmed", relatedDocs: ["CK2024001"], remark: "" },
+  { id: 2, receiptCode: "SK2024002", customerId: 2, customerName: "涓婃捣璐告槗鍏徃", receiptDate: "2024-01-18", amount: 5000, receiptMethod: "cash", status: "pending", relatedDocs: ["CK2024002"], remark: "" },
+  { id: 3, receiptCode: "SK2024003", customerId: 3, customerName: "骞垮窞瀹炰笟鏈夐檺鍏徃", receiptDate: "2024-01-20", amount: 8000, receiptMethod: "alipay", status: "confirmed", relatedDocs: ["CK2024003"], remark: "" },
+];
+
+const totalReceiptAmount = computed(() => {
+  return dataList.value.reduce((sum, item) => sum + Number(item.amount), 0);
+});
+
+const formatMoney = (value) => {
+  if (value === undefined || value === null) return "0.00";
+  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+const getReceiptMethodLabel = (method) => {
+  const map = {
+    bank_transfer: "閾惰杞处",
+    cash: "鐜伴噾",
+    check: "鏀エ",
+    draft: "姹囩エ",
+    alipay: "鏀粯瀹�",
+    wechat: "寰俊",
+  };
+  return map[method] || method;
+};
+
+const getTableData = () => {
+  let result = [...mockData];
+  if (filters.receiptCode) {
+    result = result.filter(item => item.receiptCode.includes(filters.receiptCode));
+  }
+  if (filters.customerId) {
+    result = result.filter(item => item.customerId === filters.customerId);
+  }
+  if (filters.receiptMethod) {
+    result = result.filter(item => item.receiptMethod === filters.receiptMethod);
+  }
+  pagination.total = result.length;
+  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
+};
+
+const resetFilters = () => {
+  filters.receiptCode = "";
+  filters.customerId = "";
+  filters.receiptMethod = "";
+  pagination.currentPage = 1;
+  getTableData();
+};
+
+const changePage = ({ current, size }) => {
+  pagination.currentPage = current;
+  pagination.pageSize = size;
+  getTableData();
+};
+
+const add = () => {
+  isEdit.value = false;
+  dialogTitle.value = "鏂板鏀舵";
+  Object.assign(form, {
+    receiptCode: "SK" + Date.now().toString().slice(-8),
+    customerId: "",
+    receiptDate: new Date().toISOString().split('T')[0],
+    amount: 0,
+    receiptMethod: "bank_transfer",
+    bankAccount: "",
+    relatedDocs: [],
+    remark: "",
+  });
+  dialogVisible.value = true;
+};
+
+const edit = (row) => {
+  isEdit.value = true;
+  currentId.value = row.id;
+  dialogTitle.value = "缂栬緫鏀舵";
+  Object.assign(form, row);
+  dialogVisible.value = true;
+};
+
+const view = (row) => {
+  ElMessage.info(`鏌ョ湅鏀舵鍗�: ${row.receiptCode}`);
+};
+
+const handleConfirm = (row) => {
+  ElMessageBox.confirm("纭璇ユ敹娆惧崟鍚楋紵", "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "info",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData[index].status = "confirmed";
+    }
+    ElMessage.success("纭鎴愬姛");
+    getTableData();
+  });
+};
+
+const handleDelete = (row) => {
+  ElMessageBox.confirm("纭鍒犻櫎璇ユ敹娆惧崟鍚楋紵", "鎻愮ず", {
+    confirmButtonText: "纭畾",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData.splice(index, 1);
+    }
+    ElMessage.success("鍒犻櫎鎴愬姛");
+    getTableData();
+  });
+};
+
+const handleOut = () => {
+  ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+const submitForm = () => {
+  formRef.value.validate((valid) => {
+    if (valid) {
+      const customer = customerList.find(item => item.id === form.customerId);
+      if (isEdit.value) {
+        const index = mockData.findIndex(item => item.id === currentId.value);
+        if (index !== -1) {
+          mockData[index] = { ...mockData[index], ...form, customerName: customer?.name };
+        }
+        ElMessage.success("缂栬緫鎴愬姛");
+      } else {
+        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
+        mockData.push({ id: newId, ...form, customerName: customer?.name, status: "pending" });
+        ElMessage.success("鏂板鎴愬姛");
+      }
+      dialogVisible.value = false;
+      getTableData();
+    }
+  });
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 15px;
+}
+
+.text-success {
+  color: #67c23a;
+  font-weight: bold;
+}
+</style>
diff --git a/src/views/financialManagement/receivable/reconciliation.vue b/src/views/financialManagement/receivable/reconciliation.vue
new file mode 100644
index 0000000..883e12e
--- /dev/null
+++ b/src/views/financialManagement/receivable/reconciliation.vue
@@ -0,0 +1,469 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="瀹㈡埛:">
+        <el-select v-model="filters.customerId" placeholder="璇烽�夋嫨瀹㈡埛" clearable style="width: 200px;">
+          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="瀵硅处鏈熼棿:">
+        <el-date-picker v-model="filters.startMonth" type="month" placeholder="寮�濮嬫湀浠�" value-format="YYYY-MM" style="width: 140px;" />
+        <span style="margin: 0 10px;">鑷�</span>
+        <el-date-picker v-model="filters.endMonth" type="month" placeholder="缁撴潫鏈堜唤" value-format="YYYY-MM" style="width: 140px;" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div>
+          <el-button type="primary" @click="generateStatement" icon="Document">鐢熸垚瀵硅处鍗�</el-button>
+        </div>
+        <div>
+          <el-button @click="handleOut" icon="Download">瀵煎嚭瀵硅处鍗�</el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @pagination="changePage"
+      >
+        <template #beginBalance="{ row }">
+          <span :class="row.beginBalance >= 0 ? 'text-success' : 'text-danger'">楼{{ formatMoney(row.beginBalance) }}</span>
+        </template>
+        <template #currentReceivable="{ row }">
+          <span class="text-primary">楼{{ formatMoney(row.currentReceivable) }}</span>
+        </template>
+        <template #currentReceipt="{ row }">
+          <span class="text-success">楼{{ formatMoney(row.currentReceipt) }}</span>
+        </template>
+        <template #endBalance="{ row }">
+          <span :class="row.endBalance >= 0 ? 'text-success' : 'text-danger'">楼{{ formatMoney(row.endBalance) }}</span>
+        </template>
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="viewDetail(row)">鏌ョ湅鏄庣粏</el-button>
+          <el-button type="primary" link @click="printStatement(row)">鎵撳嵃</el-button>
+        </template>
+      </PIMTable>
+    </div>
+
+    <FormDialog title="瀵硅处鏄庣粏" v-model="detailDialogVisible" width="900px" @confirm="printDetail" @cancel="detailDialogVisible = false" operationType="detail">
+      <div class="statement-header">
+        <h3>{{ currentCustomer }} 搴旀敹瀵硅处鍗�</h3>
+        <p>瀵硅处鏈熼棿: {{ currentPeriod }}</p>
+      </div>
+      <el-table :data="detailData" border style="width: 100%">
+        <el-table-column prop="date" label="鏃ユ湡" width="120" />
+        <el-table-column prop="type" label="绫诲瀷" width="100">
+          <template #default="{ row }">
+            <el-tag :type="row.type === '鍑哄簱' ? 'success' : row.type === '閫�璐�' ? 'danger' : 'primary'">{{ row.type }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="code" label="鍗曟嵁缂栧彿" width="150" />
+        <el-table-column prop="debit" label="鍊熸柟(搴旀敹)" width="120">
+          <template #default="{ row }">
+            <span v-if="row.debit > 0" class="text-danger">楼{{ formatMoney(row.debit) }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="credit" label="璐锋柟(鏀舵)" width="120">
+          <template #default="{ row }">
+            <span v-if="row.credit > 0" class="text-success">楼{{ formatMoney(row.credit) }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="balance" label="浣欓" width="120">
+          <template #default="{ row }">
+            <span :class="row.balance >= 0 ? 'text-success' : 'text-danger'">楼{{ formatMoney(row.balance) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="remark" label="澶囨敞" show-overflow-tooltip />
+      </el-table>
+      <template #footer>
+        <el-button type="primary" @click="printDetail">鎵撳嵃</el-button>
+        <el-button @click="detailDialogVisible = false">鍏抽棴</el-button>
+      </template>
+    </FormDialog>
+
+    <FormDialog title="鐢熸垚瀵硅处鍗�" v-model="generateDialogVisible" width="1000px" @confirm="confirmGenerate" @cancel="generateDialogVisible = false">
+      <el-form :model="generateForm" label-width="100px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="閫夋嫨瀹㈡埛" prop="customerId">
+              <el-select v-model="generateForm.customerId" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%;" @change="onCustomerChange">
+                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="瀵硅处鏈堜唤" prop="period">
+              <el-date-picker v-model="generateForm.period" type="month" placeholder="閫夋嫨鏈堜唤" value-format="YYYY-MM" style="width: 100%;" @change="onPeriodChange" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+
+      <div v-if="salesData.length > 0" class="sales-section">
+        <div class="section-title">鏈湀閿�鍞暟鎹�</div>
+        <el-table :data="salesData" border style="width: 100%; margin-bottom: 15px;" v-loading="salesLoading" @selection-change="handleSalesSelectionChange">
+          <el-table-column type="selection" width="55" align="center" />
+          <el-table-column prop="date" label="鏃ユ湡" width="120" />
+          <el-table-column prop="code" label="鍗曟嵁缂栧彿" width="150" />
+          <el-table-column prop="type" label="绫诲瀷" width="100">
+            <template #default="{ row }">
+              <el-tag :type="row.type === '鍑哄簱' ? 'success' : row.type === '鏀舵' ? 'primary' : 'danger'">{{ row.type }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column prop="amount" label="閲戦" width="120">
+            <template #default="{ row }">
+              <span :class="row.type === '鍑哄簱' ? 'text-primary' : row.type === '鏀舵' ? 'text-success' : 'text-danger'">楼{{ formatMoney(row.amount) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="remark" label="澶囨敞" />
+        </el-table>
+
+        <div class="summary-row">
+          <span>鏈熷垵浣欓: <strong class="text-primary">楼{{ formatMoney(generateForm.beginBalance) }}</strong></span>
+          <span>鏈湡搴旀敹: <strong class="text-primary">楼{{ formatMoney(generateForm.currentReceivable) }}</strong></span>
+          <span>鏈湡鏀舵: <strong class="text-success">楼{{ formatMoney(generateForm.currentReceipt) }}</strong></span>
+          <span>鏈熸湯浣欓: <strong :class="calculateEndBalance(generateForm.beginBalance, generateForm.currentReceivable, generateForm.currentReceipt) >= 0 ? 'text-success' : 'text-danger'">楼{{ formatMoney(calculateEndBalance(generateForm.beginBalance, generateForm.currentReceivable, generateForm.currentReceipt)) }}</strong></span>
+        </div>
+      </div>
+
+      <div v-else-if="generateForm.customerId && !salesLoading" class="empty-tip">
+        <el-empty description="璇ュ鎴锋湰鏈堟殏鏃犻攢鍞暟鎹�" />
+      </div>
+
+      <template #footer>
+        <el-button type="primary" @click="confirmGenerate" :disabled="!canGenerate">纭鐢熸垚</el-button>
+        <el-button @click="generateDialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, computed } from "vue";
+import { ElMessage } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+
+defineOptions({
+  name: "搴旀敹瀵硅处",
+});
+
+const filters = reactive({
+  customerId: "",
+  startMonth: "",
+  endMonth: "",
+});
+
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const columns = [
+  { label: "瀵硅处鍗曞彿", prop: "statementCode", width: "150" },
+  { label: "瀹㈡埛鍚嶇О", prop: "customerName", width: "180" },
+  { label: "瀵硅处鏈熼棿", prop: "period", width: "150" },
+  { label: "鏈熷垵浣欓", prop: "beginBalance", slot: "beginBalance" },
+  { label: "鏈湡搴旀敹", prop: "currentReceivable", slot: "currentReceivable" },
+  { label: "鏈湡鏀舵", prop: "currentReceipt", slot: "currentReceipt" },
+  { label: "鏈熸湯浣欓", prop: "endBalance", slot: "endBalance" },
+  { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "150", fixed: "right" },
+];
+
+const dataList = ref([]);
+const detailDialogVisible = ref(false);
+const currentCustomer = ref("");
+const currentPeriod = ref("");
+const detailData = ref([]);
+
+const generateDialogVisible = ref(false);
+const salesLoading = ref(false);
+const salesData = ref([]);
+const selectedSales = ref([]);
+
+const generateForm = reactive({
+  customerId: "",
+  customerName: "",
+  period: "",
+  beginBalance: 0,
+  currentReceivable: 0,
+  currentReceipt: 0,
+});
+
+const canGenerate = computed(() => {
+  return generateForm.customerId && generateForm.period && selectedSales.value.length > 0;
+});
+
+const customerList = [
+  { id: 1, name: "鍖椾含绉戞妧鏈夐檺鍏徃" },
+  { id: 2, name: "涓婃捣璐告槗鍏徃" },
+  { id: 3, name: "骞垮窞瀹炰笟鏈夐檺鍏徃" },
+  { id: 4, name: "娣卞湷鐢靛瓙鍏徃" },
+];
+
+const mockData = [
+  { id: 1, statementCode: "DZ202401001", customerId: 1, customerName: "鍖椾含绉戞妧鏈夐檺鍏徃", period: "2024-01", beginBalance: 10000, currentReceivable: 15000, currentReceipt: 8000, endBalance: 17000 },
+  { id: 2, statementCode: "DZ202401002", customerId: 2, customerName: "涓婃捣璐告槗鍏徃", period: "2024-01", beginBalance: 5000, currentReceivable: 12000, currentReceipt: 10000, endBalance: 7000 },
+  { id: 3, statementCode: "DZ202402001", customerId: 1, customerName: "鍖椾含绉戞妧鏈夐檺鍏徃", period: "2024-02", beginBalance: 17000, currentReceivable: 20000, currentReceipt: 15000, endBalance: 22000 },
+];
+
+const calculateEndBalance = (beginBalance, currentReceivable, currentReceipt) => {
+  return beginBalance + currentReceivable - currentReceipt;
+};
+
+const formatMoney = (value) => {
+  if (value === undefined || value === null) return "0.00";
+  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+const getTableData = () => {
+  let result = [...mockData];
+  if (filters.customerId) {
+    result = result.filter(item => item.customerId === filters.customerId);
+  }
+  if (filters.startMonth && filters.endMonth) {
+    result = result.filter(item => item.period >= filters.startMonth && item.period <= filters.endMonth);
+  }
+  pagination.total = result.length;
+  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
+};
+
+const resetFilters = () => {
+  filters.customerId = "";
+  filters.startMonth = "";
+  filters.endMonth = "";
+  pagination.currentPage = 1;
+  getTableData();
+};
+
+const changePage = ({ current, size }) => {
+  pagination.currentPage = current;
+  pagination.pageSize = size;
+  getTableData();
+};
+
+const generateStatement = () => {
+  generateForm.customerId = "";
+  generateForm.customerName = "";
+  generateForm.period = "";
+  generateForm.beginBalance = 0;
+  generateForm.currentReceivable = 0;
+  generateForm.currentReceipt = 0;
+  salesData.value = [];
+  selectedSales.value = [];
+  generateDialogVisible.value = true;
+};
+
+const onCustomerChange = (customerId) => {
+  const customer = customerList.find(item => item.id === customerId);
+  if (customer) {
+    generateForm.customerName = customer.name;
+  }
+  loadSalesData();
+};
+
+const onPeriodChange = () => {
+  loadSalesData();
+};
+
+const loadSalesData = () => {
+  if (!generateForm.customerId || !generateForm.period) {
+    salesData.value = [];
+    return;
+  }
+
+  salesLoading.value = true;
+
+  setTimeout(() => {
+    const mockSalesData = [
+      { id: 1, date: generateForm.period + "-03", code: "CK2024001", type: "鍑哄簱", amount: 8000, remark: "浜у搧A閿�鍞�" },
+      { id: 2, date: generateForm.period + "-08", code: "SK2024001", type: "鏀舵", amount: 5000, remark: "瀹㈡埛鍥炴" },
+      { id: 3, date: generateForm.period + "-12", code: "CK2024002", type: "鍑哄簱", amount: 12000, remark: "浜у搧B閿�鍞�" },
+      { id: 4, date: generateForm.period + "-15", code: "TH2024001", type: "閫�璐�", amount: 2000, remark: "璐ㄩ噺闂閫�璐�" },
+      { id: 5, date: generateForm.period + "-20", code: "CK2024003", type: "鍑哄簱", amount: 5000, remark: "浜у搧C閿�鍞�" },
+      { id: 6, date: generateForm.period + "-25", code: "SK2024002", type: "鏀舵", amount: 8000, remark: "瀹㈡埛鍥炴" },
+    ];
+
+    salesData.value = mockSalesData;
+
+    const lastPeriod = getLastPeriod(generateForm.period);
+    const lastStatement = mockData.find(item =>
+      item.customerId === generateForm.customerId && item.period === lastPeriod
+    );
+    generateForm.beginBalance = lastStatement ? lastStatement.endBalance : 0;
+
+    calculateSummary();
+
+    salesLoading.value = false;
+  }, 500);
+};
+
+const getLastPeriod = (period) => {
+  const [year, month] = period.split("-").map(Number);
+  if (month === 1) {
+    return `${year - 1}-12`;
+  }
+  return `${year}-${String(month - 1).padStart(2, "0")}`;
+};
+
+const calculateSummary = () => {
+  let receivable = 0;
+  let receipt = 0;
+
+  selectedSales.value.forEach(item => {
+    if (item.type === "鍑哄簱") {
+      receivable += item.amount;
+    } else if (item.type === "閫�璐�") {
+      receivable -= item.amount;
+    } else if (item.type === "鏀舵") {
+      receipt += item.amount;
+    }
+  });
+
+  generateForm.currentReceivable = receivable;
+  generateForm.currentReceipt = receipt;
+};
+
+const handleSalesSelectionChange = (selection) => {
+  selectedSales.value = selection;
+  calculateSummary();
+};
+
+const confirmGenerate = () => {
+  const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
+  const endBalance = calculateEndBalance(generateForm.beginBalance, generateForm.currentReceivable, generateForm.currentReceipt);
+
+  mockData.unshift({
+    id: newId,
+    statementCode: "DZ" + Date.now(),
+    customerId: generateForm.customerId,
+    customerName: generateForm.customerName,
+    period: generateForm.period,
+    beginBalance: generateForm.beginBalance,
+    currentReceivable: generateForm.currentReceivable,
+    currentReceipt: generateForm.currentReceipt,
+    endBalance,
+  });
+
+  generateDialogVisible.value = false;
+  ElMessage.success("瀵硅处鍗曠敓鎴愭垚鍔�");
+  getTableData();
+};
+
+const viewDetail = (row) => {
+  currentCustomer.value = row.customerName;
+  currentPeriod.value = row.period;
+
+  const saleOutAmount = Math.floor(row.currentReceivable * 0.6);
+  const returnAmount = Math.floor(row.currentReceivable * 0.1);
+  const firstReceipt = Math.floor(row.currentReceipt * 0.4);
+  const secondReceipt = row.currentReceipt - firstReceipt;
+
+  let runningBalance = row.beginBalance;
+
+  detailData.value = [
+    { date: row.period + "-01", type: "鏈熷垵", code: "-", debit: 0, credit: 0, balance: runningBalance, remark: "鏈熷垵浣欓" },
+    { date: row.period + "-05", type: "鍑哄簱", code: "CK2024001", debit: saleOutAmount, credit: 0, balance: runningBalance += saleOutAmount, remark: "閿�鍞嚭搴�" },
+    { date: row.period + "-10", type: "鏀舵", code: "SK2024001", debit: 0, credit: firstReceipt, balance: runningBalance -= firstReceipt, remark: "瀹㈡埛鍥炴" },
+    { date: row.period + "-15", type: "鍑哄簱", code: "CK2024002", debit: row.currentReceivable - saleOutAmount - returnAmount, credit: 0, balance: runningBalance += (row.currentReceivable - saleOutAmount - returnAmount), remark: "閿�鍞嚭搴�" },
+    { date: row.period + "-20", type: "閫�璐�", code: "TH2024001", debit: 0, credit: returnAmount, balance: runningBalance -= returnAmount, remark: "閿�鍞��璐�" },
+    { date: row.period + "-25", type: "鏀舵", code: "SK2024002", debit: 0, credit: secondReceipt, balance: runningBalance -= secondReceipt, remark: "瀹㈡埛鍥炴" },
+  ];
+
+  detailDialogVisible.value = true;
+};
+
+const printStatement = (row) => {
+  ElMessage.info(`鎵撳嵃瀵硅处鍗�: ${row.statementCode}`);
+};
+
+const printDetail = () => {
+  ElMessage.info("鎵撳嵃鏄庣粏");
+};
+
+const handleOut = () => {
+  ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 15px;
+}
+
+.text-success {
+  color: #67c23a;
+}
+
+.text-danger {
+  color: #f56c6c;
+}
+
+.text-primary {
+  color: #409eff;
+}
+
+.statement-header {
+  text-align: center;
+  margin-bottom: 20px;
+  h3 {
+    margin: 0 0 10px 0;
+  }
+  p {
+    color: #909399;
+    margin: 0;
+  }
+}
+
+.sales-section {
+  margin-top: 20px;
+
+  .section-title {
+    font-size: 16px;
+    font-weight: bold;
+    margin-bottom: 15px;
+    padding-left: 10px;
+    border-left: 4px solid #409eff;
+  }
+}
+
+.summary-row {
+  display: flex;
+  justify-content: space-around;
+  padding: 15px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+  margin-top: 15px;
+
+  span {
+    font-size: 14px;
+
+    strong {
+      font-size: 16px;
+      margin-left: 5px;
+    }
+  }
+}
+
+.empty-tip {
+  margin-top: 30px;
+}
+</style>
diff --git a/src/views/financialManagement/receivable/salesOut.vue b/src/views/financialManagement/receivable/salesOut.vue
new file mode 100644
index 0000000..fce0c20
--- /dev/null
+++ b/src/views/financialManagement/receivable/salesOut.vue
@@ -0,0 +1,271 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="鍑哄簱鍗曞彿:">
+        <el-input v-model="filters.outCode" placeholder="璇疯緭鍏ュ嚭搴撳崟鍙�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="瀹㈡埛:">
+        <el-select v-model="filters.customerId" placeholder="璇烽�夋嫨瀹㈡埛" clearable style="width: 200px;">
+          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鍑哄簱鏃ユ湡:">
+        <el-date-picker v-model="filters.dateRange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" range-separator="鑷�" start-placeholder="寮�濮嬫棩鏈�" end-placeholder="缁撴潫鏃ユ湡" clearable />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div></div>
+        <div>
+          <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @pagination="changePage"
+      >
+        <template #status="{ row }">
+          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
+        </template>
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="view(row)">鏌ョ湅</el-button>
+          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">缂栬緫</el-button>
+          <el-button type="danger" link @click="handleDelete(row)" v-if="row.status === 'pending'">鍒犻櫎</el-button>
+        </template>
+      </PIMTable>
+    </div>
+
+    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
+      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鍑哄簱鍗曞彿" prop="outCode">
+              <el-input v-model="form.outCode" placeholder="璇疯緭鍏ュ嚭搴撳崟鍙�" :disabled="isEdit" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="瀹㈡埛" prop="customerId">
+              <el-select v-model="form.customerId" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%;" :disabled="isEdit">
+                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鍑哄簱鏃ユ湡" prop="outDate">
+              <el-date-picker v-model="form.outDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="閲戦" prop="amount">
+              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitForm">纭畾</el-button>
+        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+
+defineOptions({
+  name: "閿�鍞嚭搴�",
+});
+
+const filters = reactive({
+  outCode: "",
+  customerId: "",
+  dateRange: [],
+});
+
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const columns = [
+  { label: "鍑哄簱鍗曞彿", prop: "outCode", width: "150" },
+  { label: "瀹㈡埛鍚嶇О", prop: "customerName", width: "180" },
+  { label: "鍑哄簱鏃ユ湡", prop: "outDate", width: "120" },
+  { label: "閲戦", prop: "amount", width: "120" },
+  { label: "鐘舵��", prop: "status", slot: "status" },
+  { label: "澶囨敞", prop: "remark", showOverflowTooltip: true },
+  { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "200", fixed: "right" },
+];
+
+const dataList = ref([]);
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const isEdit = ref(false);
+const currentId = ref(null);
+
+const customerList = [
+  { id: 1, name: "鍖椾含绉戞妧鏈夐檺鍏徃" },
+  { id: 2, name: "涓婃捣璐告槗鍏徃" },
+  { id: 3, name: "骞垮窞瀹炰笟鏈夐檺鍏徃" },
+  { id: 4, name: "娣卞湷鐢靛瓙鍏徃" },
+];
+
+const form = reactive({
+  outCode: "",
+  customerId: "",
+  outDate: "",
+  amount: 0,
+  remark: "",
+});
+
+const rules = {
+  outCode: [{ required: true, message: "璇疯緭鍏ュ嚭搴撳崟鍙�", trigger: "blur" }],
+  customerId: [{ required: true, message: "璇烽�夋嫨瀹㈡埛", trigger: "change" }],
+  outDate: [{ required: true, message: "璇烽�夋嫨鍑哄簱鏃ユ湡", trigger: "change" }],
+  amount: [{ required: true, message: "璇疯緭鍏ラ噾棰�", trigger: "blur" }],
+};
+
+const mockData = [
+  { id: 1, outCode: "CK2024001", customerId: 1, customerName: "鍖椾含绉戞妧鏈夐檺鍏徃", outDate: "2024-01-15", amount: 5000, status: "approved", remark: "" },
+  { id: 2, outCode: "CK2024002", customerId: 2, customerName: "涓婃捣璐告槗鍏徃", outDate: "2024-01-16", amount: 8000, status: "pending", remark: "" },
+  { id: 3, outCode: "CK2024003", customerId: 3, customerName: "骞垮窞瀹炰笟鏈夐檺鍏徃", outDate: "2024-01-18", amount: 12000, status: "approved", remark: "" },
+  { id: 4, outCode: "CK2024004", customerId: 4, customerName: "娣卞湷鐢靛瓙鍏徃", outDate: "2024-01-20", amount: 3500, status: "pending", remark: "" },
+];
+
+const getStatusLabel = (status) => {
+  const map = { pending: "寰呭鏍�", approved: "宸插鏍�", rejected: "宸查┏鍥�" };
+  return map[status] || status;
+};
+
+const getStatusType = (status) => {
+  const map = { pending: "warning", approved: "success", rejected: "danger" };
+  return map[status] || "";
+};
+
+const getTableData = () => {
+  let result = [...mockData];
+  if (filters.outCode) {
+    result = result.filter(item => item.outCode.includes(filters.outCode));
+  }
+  if (filters.customerId) {
+    result = result.filter(item => item.customerId === filters.customerId);
+  }
+  if (filters.dateRange && filters.dateRange.length === 2) {
+    result = result.filter(item => item.outDate >= filters.dateRange[0] && item.outDate <= filters.dateRange[1]);
+  }
+  pagination.total = result.length;
+  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
+};
+
+const resetFilters = () => {
+  filters.outCode = "";
+  filters.customerId = "";
+  filters.dateRange = [];
+  pagination.currentPage = 1;
+  getTableData();
+};
+
+const changePage = ({ current, size }) => {
+  pagination.currentPage = current;
+  pagination.pageSize = size;
+  getTableData();
+};
+
+const add = () => {
+  isEdit.value = false;
+  dialogTitle.value = "鏂板鍑哄簱";
+  Object.assign(form, {
+    outCode: "CK" + Date.now(),
+    customerId: "",
+    outDate: "",
+    amount: 0,
+    remark: "",
+  });
+  dialogVisible.value = true;
+};
+
+const edit = (row) => {
+  isEdit.value = true;
+  currentId.value = row.id;
+  dialogTitle.value = "缂栬緫鍑哄簱";
+  Object.assign(form, row);
+  dialogVisible.value = true;
+};
+
+const view = (row) => {
+  ElMessage.info(`鏌ョ湅鍑哄簱鍗�: ${row.outCode}`);
+};
+
+const submitForm = () => {
+  formRef.value.validate((valid) => {
+    if (valid) {
+      const customer = customerList.find(item => item.id === form.customerId);
+      if (isEdit.value) {
+        const index = mockData.findIndex(item => item.id === currentId.value);
+        if (index !== -1) {
+          mockData[index] = { ...mockData[index], ...form, customerName: customer?.name };
+        }
+        ElMessage.success("缂栬緫鎴愬姛");
+      } else {
+        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
+        mockData.push({ id: newId, ...form, customerName: customer?.name, status: "pending" });
+        ElMessage.success("鏂板鎴愬姛");
+      }
+      dialogVisible.value = false;
+      getTableData();
+    }
+  });
+};
+
+const handleDelete = (row) => {
+  ElMessageBox.confirm("纭鍒犻櫎璇ュ嚭搴撳崟鍚楋紵", "鎻愮ず", {
+    confirmButtonText: "纭畾",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData.splice(index, 1);
+    }
+    ElMessage.success("鍒犻櫎鎴愬姛");
+    getTableData();
+  });
+};
+
+const handleOut = () => {
+  ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 15px;
+}
+</style>
diff --git a/src/views/financialManagement/receivable/salesReturn.vue b/src/views/financialManagement/receivable/salesReturn.vue
new file mode 100644
index 0000000..4cf54d6
--- /dev/null
+++ b/src/views/financialManagement/receivable/salesReturn.vue
@@ -0,0 +1,305 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="閫�璐у崟鍙�:">
+        <el-input v-model="filters.returnCode" placeholder="璇疯緭鍏ラ��璐у崟鍙�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="瀹㈡埛:">
+        <el-select v-model="filters.customerId" placeholder="璇烽�夋嫨瀹㈡埛" clearable style="width: 200px;">
+          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="閫�璐ф棩鏈�:">
+        <el-date-picker v-model="filters.dateRange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" range-separator="鑷�" start-placeholder="寮�濮嬫棩鏈�" end-placeholder="缁撴潫鏃ユ湡" clearable />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div></div>
+        <div>
+          <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @pagination="changePage"
+      >
+        <template #status="{ row }">
+          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
+        </template>
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="view(row)">鏌ョ湅</el-button>
+          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'pending'">缂栬緫</el-button>
+          <el-button type="success" link @click="handleAudit(row)" v-if="row.status === 'pending'">瀹℃牳</el-button>
+        </template>
+      </PIMTable>
+    </div>
+
+    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="800px" @confirm="submitForm" @cancel="dialogVisible = false">
+      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="閫�璐у崟鍙�" prop="returnCode">
+              <el-input v-model="form.returnCode" placeholder="璇疯緭鍏ラ��璐у崟鍙�" :disabled="isEdit" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鍏宠仈鍑哄簱鍗�" prop="outCode">
+              <el-select v-model="form.outCode" placeholder="璇烽�夋嫨鍑哄簱鍗�" style="width: 100%;" :disabled="isEdit">
+                <el-option v-for="item in outList" :key="item.outCode" :label="item.outCode" :value="item.outCode" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="瀹㈡埛" prop="customerId">
+              <el-select v-model="form.customerId" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%;" :disabled="isEdit">
+                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="閫�璐ф棩鏈�" prop="returnDate">
+              <el-date-picker v-model="form.returnDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="閫�璐ч噾棰�" prop="amount">
+              <el-input-number v-model="form.amount" :min="0" :precision="2" style="width: 100%;" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="閫�璐у師鍥�" prop="reason">
+              <el-input v-model="form.reason" placeholder="璇疯緭鍏ラ��璐у師鍥�" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitForm">纭畾</el-button>
+        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+      </template>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+
+defineOptions({
+  name: "閿�鍞��璐�",
+});
+
+const filters = reactive({
+  returnCode: "",
+  customerId: "",
+  dateRange: [],
+});
+
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const columns = [
+  { label: "閫�璐у崟鍙�", prop: "returnCode", width: "150" },
+  { label: "瀹㈡埛鍚嶇О", prop: "customerName", width: "180" },
+  { label: "鍏宠仈鍑哄簱鍗�", prop: "outCode", width: "150" },
+  { label: "閫�璐ф棩鏈�", prop: "returnDate", width: "120" },
+  { label: "閫�璐ч噾棰�", prop: "amount", width: "120" },
+  { label: "閫�璐у師鍥�", prop: "reason", width: "150", showOverflowTooltip: true },
+  { label: "鐘舵��", prop: "status", slot: "status" },
+  { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "200", fixed: "right" },
+];
+
+const dataList = ref([]);
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const isEdit = ref(false);
+const currentId = ref(null);
+
+const customerList = [
+  { id: 1, name: "鍖椾含绉戞妧鏈夐檺鍏徃" },
+  { id: 2, name: "涓婃捣璐告槗鍏徃" },
+  { id: 3, name: "骞垮窞瀹炰笟鏈夐檺鍏徃" },
+  { id: 4, name: "娣卞湷鐢靛瓙鍏徃" },
+];
+
+const outList = [
+  { outCode: "CK2024001", customerId: 1 },
+  { outCode: "CK2024002", customerId: 2 },
+  { outCode: "CK2024003", customerId: 3 },
+];
+
+const form = reactive({
+  returnCode: "",
+  outCode: "",
+  customerId: "",
+  returnDate: "",
+  amount: 0,
+  reason: "",
+  remark: "",
+});
+
+const rules = {
+  returnCode: [{ required: true, message: "璇疯緭鍏ラ��璐у崟鍙�", trigger: "blur" }],
+  outCode: [{ required: true, message: "璇烽�夋嫨鍏宠仈鍑哄簱鍗�", trigger: "change" }],
+  customerId: [{ required: true, message: "璇烽�夋嫨瀹㈡埛", trigger: "change" }],
+  returnDate: [{ required: true, message: "璇烽�夋嫨閫�璐ф棩鏈�", trigger: "change" }],
+  amount: [{ required: true, message: "璇疯緭鍏ラ��璐ч噾棰�", trigger: "blur" }],
+};
+
+const mockData = [
+  { id: 1, returnCode: "TH2024001", outCode: "CK2024001", customerId: 1, customerName: "鍖椾含绉戞妧鏈夐檺鍏徃", returnDate: "2024-01-20", amount: 1000, reason: "璐ㄩ噺闂", status: "approved", remark: "" },
+  { id: 2, returnCode: "TH2024002", outCode: "CK2024002", customerId: 2, customerName: "涓婃捣璐告槗鍏徃", returnDate: "2024-01-22", amount: 500, reason: "瑙勬牸涓嶇", status: "pending", remark: "" },
+];
+
+const getStatusLabel = (status) => {
+  const map = { pending: "寰呭鏍�", approved: "宸插鏍�", rejected: "宸查┏鍥�" };
+  return map[status] || status;
+};
+
+const getStatusType = (status) => {
+  const map = { pending: "warning", approved: "success", rejected: "danger" };
+  return map[status] || "";
+};
+
+const getTableData = () => {
+  let result = [...mockData];
+  if (filters.returnCode) {
+    result = result.filter(item => item.returnCode.includes(filters.returnCode));
+  }
+  if (filters.customerId) {
+    result = result.filter(item => item.customerId === filters.customerId);
+  }
+  if (filters.dateRange && filters.dateRange.length === 2) {
+    result = result.filter(item => item.returnDate >= filters.dateRange[0] && item.returnDate <= filters.dateRange[1]);
+  }
+  pagination.total = result.length;
+  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
+};
+
+const resetFilters = () => {
+  filters.returnCode = "";
+  filters.customerId = "";
+  filters.dateRange = [];
+  pagination.currentPage = 1;
+  getTableData();
+};
+
+const changePage = ({ current, size }) => {
+  pagination.currentPage = current;
+  pagination.pageSize = size;
+  getTableData();
+};
+
+const add = () => {
+  isEdit.value = false;
+  dialogTitle.value = "鏂板閫�璐�";
+  Object.assign(form, {
+    returnCode: "TH" + Date.now(),
+    outCode: "",
+    customerId: "",
+    returnDate: "",
+    amount: 0,
+    reason: "",
+    remark: "",
+  });
+  dialogVisible.value = true;
+};
+
+const edit = (row) => {
+  isEdit.value = true;
+  currentId.value = row.id;
+  dialogTitle.value = "缂栬緫閫�璐�";
+  Object.assign(form, row);
+  dialogVisible.value = true;
+};
+
+const view = (row) => {
+  ElMessage.info(`鏌ョ湅閫�璐у崟: ${row.returnCode}`);
+};
+
+const handleAudit = (row) => {
+  ElMessageBox.confirm("纭瀹℃牳閫氳繃璇ラ��璐у崟鍚楋紵", "鎻愮ず", {
+    confirmButtonText: "閫氳繃",
+    cancelButtonText: "椹冲洖",
+    distinguishCancelAndClose: true,
+    type: "warning",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData[index].status = "approved";
+    }
+    ElMessage.success("瀹℃牳閫氳繃");
+    getTableData();
+  }).catch((action) => {
+    if (action === "cancel") {
+      const index = mockData.findIndex(item => item.id === row.id);
+      if (index !== -1) {
+        mockData[index].status = "rejected";
+      }
+      ElMessage.warning("宸查┏鍥�");
+      getTableData();
+    }
+  });
+};
+
+const submitForm = () => {
+  formRef.value.validate((valid) => {
+    if (valid) {
+      const customer = customerList.find(item => item.id === form.customerId);
+      if (isEdit.value) {
+        const index = mockData.findIndex(item => item.id === currentId.value);
+        if (index !== -1) {
+          mockData[index] = { ...mockData[index], ...form, customerName: customer?.name };
+        }
+        ElMessage.success("缂栬緫鎴愬姛");
+      } else {
+        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
+        mockData.push({ id: newId, ...form, customerName: customer?.name, status: "pending" });
+        ElMessage.success("鏂板鎴愬姛");
+      }
+      dialogVisible.value = false;
+      getTableData();
+    }
+  });
+};
+
+const handleOut = () => {
+  ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 15px;
+}
+</style>
diff --git a/src/views/financialManagement/revenueManagement/index.vue b/src/views/financialManagement/revenueManagement/index.vue
index 12abfe3..bcef5b6 100644
--- a/src/views/financialManagement/revenueManagement/index.vue
+++ b/src/views/financialManagement/revenueManagement/index.vue
@@ -3,22 +3,22 @@
     <el-form :model="filters" :inline="true">
       <el-form-item label="鏀跺叆鏃ユ湡:">
         <el-date-picker v-model="filters.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
-                        placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
+                        placeholder="璇烽�夋嫨" clearable @change="changeDaterange"/>
       </el-form-item>
       <el-form-item label="鏀舵鏂瑰紡:">
         <el-select
-                v-model="filters.incomeMethod"
-                placeholder="璇烽�夋嫨"
-                clearable
-                style="width: 200px;"
-              >
-                <el-option
-                  v-for="item in payment_methods"
-                  :key="item.value"
-                  :label="item.label"
-                  :value="item.value"
-                />
-              </el-select>
+            v-model="filters.incomeMethodLabel"
+            placeholder="璇烽�夋嫨"
+            clearable
+            style="width: 200px;"
+        >
+          <el-option
+              v-for="item in incomeMethodOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+          />
+        </el-select>
       </el-form-item>
       <el-form-item>
         <el-button type="primary" @click="getTableData">鎼滅储</el-button>
@@ -29,46 +29,51 @@
       <div class="actions">
         <div></div>
         <div>
-          <el-button type="primary" @click="add" icon="Plus"> 鏂板 </el-button>
+          <el-button type="primary" @click="add" icon="Plus"> 鏂板</el-button>
           <el-button @click="handleOut" icon="download">瀵煎嚭</el-button>
           <el-button
-            type="danger"
-            icon="Delete"
-            :disabled="multipleList.length <= 0 || hasBusinessIdInSelection"
-            @click="handleBatchDelete"
+              type="danger"
+              icon="Delete"
+              :disabled="multipleList.length <= 0 || hasBusinessIdInSelection"
+              @click="handleBatchDelete"
           >
             鎵归噺鍒犻櫎
           </el-button>
         </div>
       </div>
       <PIMTable
-        rowKey="id"
-        isSelection
-        :column="columns"
-        :tableData="dataList"
-        :page="{
+          rowKey="id"
+          isSelection
+          :column="columns"
+          :tableData="dataList"
+          :page="{
           current: pagination.currentPage,
           size: pagination.pageSize,
           total: pagination.total,
         }"
-        :isShowSummary="true"
-        :summaryMethod="summarizeMainTable"
-        @selection-change="handleSelectionChange"
-        @pagination="changePage"
+          :isShowSummary="true"
+          :summaryMethod="summarizeMainTable"
+          @selection-change="handleSelectionChange"
+          @pagination="changePage"
       >
+        <template #incomeMethodSlot="{ row }">
+          <el-tag>
+            {{ getIncomeMethodLabel(row) }}
+          </el-tag>
+        </template>
         <template #operation="{ row }">
-          <el-button 
-            type="primary" 
-            link 
-            :disabled="!!row.businessId"
-            @click="edit(row.id)"
+          <el-button
+              type="primary"
+              link
+              :disabled="!!row.businessId"
+              @click="edit(row.id)"
           >
             缂栬緫
           </el-button>
           <el-button
-            type="primary"
-						link
-            @click="openFilesFormDia(row)"
+              type="primary"
+              link
+              @click="openFilesFormDia(row)"
           >
             闄勪欢
           </el-button>
@@ -76,27 +81,16 @@
       </PIMTable>
     </div>
     <Modal ref="modalRef" @success="getTableData"></Modal>
-    <FileListDialog 
-      ref="fileListRef" 
-      v-model="fileListDialogVisible"
-      :show-upload-button="true"
-      :show-delete-button="true"
-      :upload-method="handleUpload"
-      :delete-method="handleFileDelete"
-    />
+    <FileListDialog v-if="fileListDialogVisible" :record-id="currentRecordId" record-type="account_income" v-model:visible="fileListDialogVisible"/>
   </div>
 </template>
 
 <script setup>
-import { usePaginationApi } from "@/hooks/usePaginationApi";
-import { listPage, delAccountIncome, fileListPage, fileAdd, fileDel } from "@/api/financialManagement/revenueManagement";
-import { onMounted, getCurrentInstance, ref, computed } from "vue";
-import Modal from "./Modal.vue";
-import { ElMessageBox, ElMessage } from "element-plus";
+import {usePaginationApi} from "@/hooks/usePaginationApi";
+import {listPage, delAccountIncome} from "@/api/financialManagement/revenueManagement";
+import {onMounted, getCurrentInstance, ref, computed} from "vue";
+import {ElMessageBox, ElMessage} from "element-plus";
 import dayjs from "dayjs";
-import FileListDialog from "@/components/Dialog/FileListDialog.vue";
-import request from "@/utils/request";
-import { getToken } from "@/utils/auth";
 
 defineOptions({
   name: "鏀跺叆绠$悊",
@@ -104,14 +98,27 @@
 
 // 琛ㄦ牸澶氶�夋閫変腑椤�
 const multipleList = ref([]);
-const { proxy } = getCurrentInstance();
+const {proxy} = getCurrentInstance();
 const modalRef = ref();
-const { payment_methods } = proxy.useDict("payment_methods");
-const { income_types } = proxy.useDict("income_types");
+const {payment_methods} = proxy.useDict("payment_methods");
+const {receipt_payment_type} = proxy.useDict("receipt_payment_type");
+const {income_types} = proxy.useDict("income_types");
 const fileListRef = ref(null);
 const fileListDialogVisible = ref(false);
-const currentFileRow = ref(null);
-const accountType = ref('鏀跺叆');
+const currentRecordId = ref(0);
+
+const incomeMethodOptions = computed(() => {
+  const merged = [...(payment_methods.value || []), ...(receipt_payment_type.value || [])];
+  const uniqueMap = new Map();
+  merged.forEach((item) => {
+    const label = item?.label;
+    if (!label) return;
+    if (!uniqueMap.has(label)) {
+      uniqueMap.set(label, {label, value: label});
+    }
+  });
+  return Array.from(uniqueMap.values());
+});
 
 const {
   filters,
@@ -122,91 +129,99 @@
   resetFilters,
   onCurrentChange,
 } = usePaginationApi(
-  listPage,
-  {
-    incomeMethod: undefined,
-    entryDate: undefined,
-  },
-  [
+    listPage,
     {
-      label: "鏀跺叆鏃ユ湡",
-      prop: "incomeDate",
+      incomeMethodLabel: undefined,
+      entryDate: undefined,
     },
-    {
-      label: "鏀跺叆绫诲瀷",
-      prop: "incomeType",
-      dataType: "tag",
-      formatData: (params) => {
-        if (income_types.value.find((m) => m.value == params)) {
-          return income_types.value.find((m) => m.value == params).label;
-        } else {
-          return null
-        }
+    [
+      {
+        label: "鏀跺叆鏃ユ湡",
+        prop: "incomeDate",
       },
-    },
-    {
-      label: "瀹㈡埛鍚嶇О",
-      prop: "customerName",
-			width: '200'
-
-    },
-    {
-      label: "鏀跺叆閲戦",
-      prop: "incomeMoney",
-
-    },
-    {
-      label: "鏀跺叆鎻忚堪",
-      prop: "incomeDescribed",
-
-    },
-    {
-      label: "鏀舵鏂瑰紡",
-      prop: "incomeMethod",
-			align: 'center',
-			width: '100',
-      dataType: "tag",
-      formatData: (params) => {
-        if (payment_methods.value.find((m) => m.value == params)) {
-          return payment_methods.value.find((m) => m.value == params).label;
-        } else {
-          return null
-        }
+      {
+        label: "鏀跺叆绫诲瀷",
+        prop: "incomeType",
+        dataType: "tag",
+        formatData: (params) => {
+          if (income_types.value.find((m) => m.value == params)) {
+            return income_types.value.find((m) => m.value == params).label;
+          } else {
+            return null
+          }
+        },
       },
-    },
-    {
-      label: "鍙戠エ鍙风爜",
-      prop: "invoiceNumber",
+      {
+        label: "瀹㈡埛鍚嶇О",
+        prop: "customerName",
+        width: '200'
 
-    },
-    {
-      label: "澶囨敞",
-      prop: "note",
+      },
+      {
+        label: "鏀跺叆閲戦",
+        prop: "incomeMoney",
 
-    },
-    {
-      label: "褰曞叆浜�",
-      prop: "inputUser",
-    },
-    {
-      label: "褰曞叆鏃ユ湡",
-      prop: "inputTime",
+      },
+      {
+        label: "鏀跺叆鎻忚堪",
+        prop: "incomeDescribed",
 
-    },
+      },
+      {
+        label: "鏀舵鏂瑰紡",
+        prop: "incomeMethodLabel",
+        align: 'center',
+        width: '100',
+        dataType: "slot",
+        slot: "incomeMethodSlot",
+      },
+      {
+        label: "鍙戠エ鍙风爜",
+        prop: "invoiceNumber",
+
+      },
+      {
+        label: "澶囨敞",
+        prop: "note",
+
+      },
+      {
+        label: "褰曞叆浜�",
+        prop: "inputUser",
+      },
+      {
+        label: "褰曞叆鏃ユ湡",
+        prop: "inputTime",
+
+      },
+      {
+        fixed: "right",
+        label: "鎿嶄綔",
+        dataType: "slot",
+        slot: "operation",
+        align: "center",
+        width: "160px",
+      },
+    ],
+    undefined,
     {
-      fixed: "right",
-      label: "鎿嶄綔",
-      dataType: "slot",
-      slot: "operation",
-      align: "center",
-      width: "160px",
-    },
-  ]
+      incomeMethodLabel: (value) => ({
+        incomeMethodLabel: value || undefined,
+      }),
+    }
 );
 
 // 琛ㄦ牸鍚堣锛氭敹鍏ラ噾棰�
 const summarizeMainTable = (param) => {
   return proxy.summarizeTable(param, ["incomeMoney"]);
+};
+
+const getIncomeMethodLabel = (row) => {
+  const methodValue = row?.incomeMethod;
+  const dictList = String(row?.businessType) === "1"
+      ? receipt_payment_type.value
+      : payment_methods.value;
+  return dictList.find((item) => item.value == methodValue)?.label || "--";
 };
 
 // 澶氶�夊悗鍋氫粈涔�
@@ -231,9 +246,9 @@
   }
   modalRef.value.loadForm(id);
 };
-const changePage = ({ page, limit }) => {
+const changePage = ({page, limit}) => {
   pagination.currentPage = page;
-	pagination.pageSize = limit;
+  pagination.pageSize = limit;
   onCurrentChange(page);
 };
 const deleteRow = (id) => {
@@ -255,13 +270,13 @@
       return;
     }
   }
-  
+
   ElMessageBox.confirm("姝ゆ搷浣滃皢姘镐箙鍒犻櫎璇ユ暟鎹�, 鏄惁缁х画?", "鎻愮ず", {
     confirmButtonText: "纭畾",
     cancelButtonText: "鍙栨秷",
     type: "warning",
   }).then(async () => {
-    const { code } = await delAccountIncome(id);
+    const {code} = await delAccountIncome(id);
     if (code == 200) {
       ElMessage({
         type: "success",
@@ -278,13 +293,13 @@
     proxy.$modal.msgWarning("璇烽�夋嫨瑕佸垹闄ょ殑鏁版嵁");
     return;
   }
-  
+
   // 妫�鏌ユ槸鍚︽湁 businessId
   if (hasBusinessIdInSelection.value) {
     proxy.$modal.msgWarning("閫変腑鐨勮褰曚腑鍖呭惈宸插叧鑱斾笟鍔$殑璁板綍锛屼笉鑳藉垹闄�");
     return;
   }
-  
+
   const ids = multipleList.value.map((item) => item.id);
   deleteRow(ids);
 };
@@ -308,162 +323,17 @@
     cancelButtonText: "鍙栨秷",
     type: "warning",
   })
-    .then(() => {
-      proxy.download(`/account/accountIncome/export`, {}, "鏀跺叆鍙拌处.xlsx");
-    })
-    .catch(() => {
-      proxy.$modal.msg("宸插彇娑�");
-    });
+      .then(() => {
+        proxy.download(`/account/accountIncome/export`, {}, "鏀跺叆鍙拌处.xlsx");
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
 };
 // 鎵撳紑闄勪欢寮规
 const openFilesFormDia = async (row) => {
-  currentFileRow.value = row;
-  accountType.value = '鏀跺叆';
-  try {
-    const res = await fileListPage({
-      accountId: row.id,
-      accountType: accountType.value,
-      current: 1,
-      size: 100
-    });
-    if (res.code === 200 && fileListRef.value) {
-      // 灏嗘暟鎹浆鎹负 FileListDialog 闇�瑕佺殑鏍煎紡
-      const fileList = (res.data?.records || []).map(item => ({
-        name: item.name,
-        url: item.url,
-        id: item.id,
-        ...item
-      }));
-      fileListRef.value.open(fileList);
-      fileListDialogVisible.value = true;
-    }
-  } catch (error) {
-    proxy.$modal.msgError("鑾峰彇闄勪欢鍒楄〃澶辫触");
-  }
-};
-
-// 涓婁紶闄勪欢
-const handleUpload = async () => {
-  if (!currentFileRow.value) {
-    proxy.$modal.msgWarning("璇峰厛閫夋嫨鏁版嵁");
-    return null;
-  }
-  
-  return new Promise((resolve) => {
-    // 鍒涘缓涓�涓殣钘忕殑鏂囦欢杈撳叆鍏冪礌
-    const input = document.createElement('input');
-    input.type = 'file';
-    input.style.display = 'none';
-    input.onchange = async (e) => {
-      const file = e.target.files[0];
-      if (!file) {
-        resolve(null);
-        return;
-      }
-      
-      try {
-        // 浣跨敤 FormData 涓婁紶鏂囦欢
-        const formData = new FormData();
-        formData.append('file', file);
-        
-        const uploadRes = await request({
-          url: '/file/upload',
-          method: 'post',
-          data: formData,
-          headers: {
-            'Content-Type': 'multipart/form-data',
-            Authorization: `Bearer ${getToken()}`
-          }
-        });
-        
-        if (uploadRes.code === 200) {
-          // 淇濆瓨闄勪欢淇℃伅
-          const fileData = {
-            accountId: currentFileRow.value.id,
-            accountType: accountType.value,
-            name: uploadRes.data.originalName || file.name,
-            url: uploadRes.data.tempPath || uploadRes.data.url
-          };
-          
-          const saveRes = await fileAdd(fileData);
-          if (saveRes.code === 200) {
-            proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛");
-            // 閲嶆柊鍔犺浇鏂囦欢鍒楄〃
-            const listRes = await fileListPage({
-              accountId: currentFileRow.value.id,
-              accountType: accountType.value,
-              current: 1,
-              size: 100
-            });
-            if (listRes.code === 200 && fileListRef.value) {
-              const fileList = (listRes.data?.records || []).map(item => ({
-                name: item.name,
-                url: item.url,
-                id: item.id,
-                ...item
-              }));
-              fileListRef.value.setList(fileList);
-            }
-            // 杩斿洖鏂版枃浠朵俊鎭�
-            resolve({
-              name: fileData.name,
-              url: fileData.url,
-              id: saveRes.data?.id
-            });
-          } else {
-            proxy.$modal.msgError(saveRes.msg || "鏂囦欢淇濆瓨澶辫触");
-            resolve(null);
-          }
-        } else {
-          proxy.$modal.msgError(uploadRes.msg || "鏂囦欢涓婁紶澶辫触");
-          resolve(null);
-        }
-      } catch (error) {
-        proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触");
-        resolve(null);
-      } finally {
-        document.body.removeChild(input);
-      }
-    };
-    
-    document.body.appendChild(input);
-    input.click();
-  });
-};
-
-// 鍒犻櫎闄勪欢
-const handleFileDelete = async (row) => {
-  try {
-    const res = await fileDel([row.id]);
-    if (res.code === 200) {
-      proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-      // 閲嶆柊鍔犺浇鏂囦欢鍒楄〃
-      if (currentFileRow.value && fileListRef.value) {
-        const listRes = await fileListPage({
-          accountId: currentFileRow.value.id,
-          accountType: accountType.value,
-          current: 1,
-          size: 100
-        });
-        if (listRes.code === 200) {
-          const fileList = (listRes.data?.records || []).map(item => ({
-            name: item.name,
-            url: item.url,
-            id: item.id,
-            ...item
-          }));
-          fileListRef.value.setList(fileList);
-        }
-      }
-      return true; // 杩斿洖 true 琛ㄧず鍒犻櫎鎴愬姛锛岀粍浠朵細鏇存柊鍒楄〃
-    } else {
-      proxy.$modal.msgError(res.msg || "鍒犻櫎澶辫触");
-      return false;
-    }
-  } catch (error) {
-    proxy.$modal.msgError("鍒犻櫎澶辫触");
-    return false;
-  }
+  currentRecordId.value = row.id;
+  fileListDialogVisible.value = true;
 };
 
 onMounted(() => {
@@ -475,6 +345,7 @@
 .table_list {
   margin-top: unset;
 }
+
 .actions {
   display: flex;
   justify-content: space-between;
diff --git a/src/views/financialManagement/salesRefund/components/ReceiptandRefundPopupWindow.vue b/src/views/financialManagement/salesRefund/components/ReceiptandRefundPopupWindow.vue
index d8218c1..35215a5 100644
--- a/src/views/financialManagement/salesRefund/components/ReceiptandRefundPopupWindow.vue
+++ b/src/views/financialManagement/salesRefund/components/ReceiptandRefundPopupWindow.vue
@@ -1,5 +1,5 @@
 <template>
-  <el-dialog v-model="visible" title="鏀舵/閫�娆�" width="90%" append-to-body>
+  <FormDialog v-model="visible" title="鏀舵/閫�娆�" width="90%" @confirm="submit" @cancel="visible=false">
     <div class="section">
       <div class="section-title descriptions">鍩虹璧勬枡</div>
       <el-form :model="form" label-width="100px">
@@ -140,12 +140,13 @@
       <el-button type="primary" @click="submit">纭</el-button>
       <el-button @click="visible=false">鍙栨秷</el-button>
     </template>
-  </el-dialog>
+  </FormDialog>
 </template>
 
 <script setup>
 import { ref } from 'vue';
 import { getToken } from '@/utils/auth';
+import FormDialog from "@/components/Dialog/FormDialog.vue";
 
 const visible = ref(false);
 const form = ref({
diff --git a/src/views/financialManagement/salesRefund/index.vue b/src/views/financialManagement/salesRefund/index.vue
index b4a792f..ccb775f 100644
--- a/src/views/financialManagement/salesRefund/index.vue
+++ b/src/views/financialManagement/salesRefund/index.vue
@@ -26,7 +26,7 @@
         <el-table-column label="鍒涘缓鏃堕棿" prop="createTime" align="center" />
         <el-table-column label="鎿嶄綔" align="center" width="150">
           <template #default="scope">
-            <el-button link type="primary" @click="openDetail(scope.row)">璇︽儏</el-button>
+            <el-button link type="primary" @click="openDetail(scope.row)" style="color: #67C23A">璇︽儏</el-button>
             <el-button link type="primary" @click="openConfirm(scope.row)">纭</el-button>
           </template>
         </el-table-column>
@@ -83,7 +83,7 @@
 function getList() {
   loading.value = true;
   const { pageNum, pageSize, ...filters } = queryParams;
-  listPlan({
+  listPage({
     current: pageNum,
     size: pageSize,
     ...filters
diff --git a/src/views/financialManagement/voucher/detailLedger.vue b/src/views/financialManagement/voucher/detailLedger.vue
new file mode 100644
index 0000000..7f85790
--- /dev/null
+++ b/src/views/financialManagement/voucher/detailLedger.vue
@@ -0,0 +1,289 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="浼氳绉戠洰:">
+        <el-cascader v-model="filters.subject" :options="subjectOptions" :props="{ label: 'name', value: 'code' }" placeholder="璇烽�夋嫨浼氳绉戠洰" clearable style="width: 250px;" filterable />
+      </el-form-item>
+      <el-form-item label="杈呭姪鏍哥畻:">
+        <el-select v-model="filters.auxiliary" placeholder="璇烽�夋嫨杈呭姪鏍哥畻" clearable style="width: 180px;">
+          <el-option label="瀹㈡埛" value="customer" />
+          <el-option label="渚涘簲鍟�" value="supplier" />
+          <el-option label="閮ㄩ棬" value="department" />
+          <el-option label="鍛樺伐" value="employee" />
+          <el-option label="椤圭洰" value="project" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鏍哥畻瀵硅薄:">
+        <el-select v-model="filters.auxiliaryItem" placeholder="璇烽�夋嫨鏍哥畻瀵硅薄" clearable style="width: 200px;" :disabled="!filters.auxiliary">
+          <el-option v-for="item in auxiliaryItems" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鏈熼棿:">
+        <el-date-picker v-model="filters.startMonth" type="month" placeholder="寮�濮嬫湀浠�" value-format="YYYY-MM" style="width: 140px;" />
+        <span style="margin: 0 10px;">鑷�</span>
+        <el-date-picker v-model="filters.endMonth" type="month" placeholder="缁撴潫鏈堜唤" value-format="YYYY-MM" style="width: 140px;" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鏌ヨ</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+        <el-button @click="handlePrint" icon="Printer">鎵撳嵃</el-button>
+        <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+      </el-form-item>
+    </el-form>
+
+    <div class="ledger-header" v-if="currentSubject">
+      <h2>绉戠洰鏄庣粏璐�</h2>
+      <p>绉戠洰: {{ currentSubject.code }} {{ currentSubject.name }}</p>
+      <p v-if="filters.auxiliary && filters.auxiliaryItem">杈呭姪鏍哥畻: {{ getAuxiliaryLabel() }}</p>
+      <p>鏈熼棿: {{ filters.startMonth }} 鑷� {{ filters.endMonth }}</p>
+    </div>
+
+    <div class="table_list">
+      <el-table :data="dataList" border style="width: 100%" show-summary :summary-method="getSummaries">
+        <el-table-column prop="date" label="鏃ユ湡" width="120" />
+        <el-table-column prop="voucherNo" label="鍑瘉瀛楀彿" width="120" />
+        <el-table-column prop="summary" label="鎽樿" min-width="200" show-overflow-tooltip />
+        <el-table-column label="鍊熸柟" width="150">
+          <template #default="{ row }">
+            <span v-if="row.debit > 0" class="text-danger">楼{{ formatMoney(row.debit) }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="璐锋柟" width="150">
+          <template #default="{ row }">
+            <span v-if="row.credit > 0" class="text-success">楼{{ formatMoney(row.credit) }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="鏂瑰悜" width="80">
+          <template #default="{ row }">
+            <el-tag :type="row.direction === '鍊�' ? 'success' : 'danger'" size="small">{{ row.direction }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="浣欓" width="150">
+          <template #default="{ row }">
+            <span :class="row.balance >= 0 ? 'text-primary' : 'text-warning'">楼{{ formatMoney(Math.abs(row.balance)) }}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <el-empty v-if="!currentSubject" description="璇烽�夋嫨浼氳绉戠洰鏌ヨ" style="margin-top: 50px;" />
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, computed, watch } from "vue";
+import { ElMessage } from "element-plus";
+
+defineOptions({
+  name: "绉戠洰鏄庣粏璐�",
+});
+
+const filters = reactive({
+  subject: [],
+  auxiliary: "",
+  auxiliaryItem: "",
+  startMonth: "2024-01",
+  endMonth: "2024-03",
+});
+
+const dataList = ref([]);
+
+const subjectOptions = [
+  {
+    code: "1122",
+    name: "搴旀敹璐︽",
+    children: [
+      { code: "112201", name: "鍖椾含绉戞妧鏈夐檺鍏徃" },
+      { code: "112202", name: "涓婃捣璐告槗鍏徃" },
+      { code: "112203", name: "骞垮窞瀹炰笟鏈夐檺鍏徃" },
+    ],
+  },
+  {
+    code: "2202",
+    name: "搴斾粯璐︽",
+    children: [
+      { code: "220201", name: "鍖椾含鍘熸潗鏂欎緵搴斿晢" },
+      { code: "220202", name: "涓婃捣鐢靛瓙鍏冨櫒浠跺叕鍙�" },
+      { code: "220203", name: "骞垮窞鍖呰鏉愭枡鍘�" },
+    ],
+  },
+  {
+    code: "6602",
+    name: "绠$悊璐圭敤",
+    children: [
+      { code: "660201", name: "鍔炲叕璐�" },
+      { code: "660202", name: "宸梾璐�" },
+      { code: "660203", name: "涓氬姟鎷涘緟璐�" },
+    ],
+  },
+];
+
+const auxiliaryItems = computed(() => {
+  const map = {
+    customer: [
+      { value: "1", label: "鍖椾含绉戞妧鏈夐檺鍏徃" },
+      { value: "2", label: "涓婃捣璐告槗鍏徃" },
+      { value: "3", label: "骞垮窞瀹炰笟鏈夐檺鍏徃" },
+    ],
+    supplier: [
+      { value: "1", label: "鍖椾含鍘熸潗鏂欎緵搴斿晢" },
+      { value: "2", label: "涓婃捣鐢靛瓙鍏冨櫒浠跺叕鍙�" },
+      { value: "3", label: "骞垮窞鍖呰鏉愭枡鍘�" },
+    ],
+    department: [
+      { value: "1", label: "璐㈠姟閮�" },
+      { value: "2", label: "閿�鍞儴" },
+      { value: "3", label: "閲囪喘閮�" },
+    ],
+    employee: [
+      { value: "1", label: "寮犱笁" },
+      { value: "2", label: "鏉庡洓" },
+      { value: "3", label: "鐜嬩簲" },
+    ],
+    project: [
+      { value: "1", label: "椤圭洰A" },
+      { value: "2", label: "椤圭洰B" },
+      { value: "3", label: "椤圭洰C" },
+    ],
+  };
+  return map[filters.auxiliary] || [];
+});
+
+watch(() => filters.auxiliary, () => {
+  filters.auxiliaryItem = "";
+});
+
+const currentSubject = computed(() => {
+  if (!filters.subject || filters.subject.length === 0) return null;
+  const code = filters.subject[filters.subject.length - 1];
+  return findSubject(subjectOptions, code);
+});
+
+const findSubject = (options, code) => {
+  for (const item of options) {
+    if (item.code === code) return item;
+    if (item.children && item.children.length > 0) {
+      const found = findSubject(item.children, code);
+      if (found) return found;
+    }
+  }
+  return null;
+};
+
+const getAuxiliaryLabel = () => {
+  const item = auxiliaryItems.value.find(i => i.value === filters.auxiliaryItem);
+  return item ? item.label : "";
+};
+
+const formatMoney = (value) => {
+  if (value === undefined || value === null) return "0.00";
+  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+const mockData = [
+  { date: "2024-01-01", voucherNo: "-", summary: "鏈熷垵浣欓", debit: 0, credit: 0, direction: "鍊�", balance: 10000 },
+  { date: "2024-01-05", voucherNo: "璁�-0001", summary: "閿�鍞嚭搴�", debit: 5000, credit: 0, direction: "鍊�", balance: 15000 },
+  { date: "2024-01-10", voucherNo: "璁�-0002", summary: "鏀跺埌璐ф", debit: 0, credit: 3000, direction: "鍊�", balance: 12000 },
+  { date: "2024-01-15", voucherNo: "璁�-0003", summary: "閿�鍞嚭搴�", debit: 8000, credit: 0, direction: "鍊�", balance: 20000 },
+  { date: "2024-01-20", voucherNo: "璁�-0004", summary: "閿�鍞��璐�", debit: 0, credit: 2000, direction: "鍊�", balance: 18000 },
+  { date: "2024-01-25", voucherNo: "璁�-0005", summary: "鏀跺埌璐ф", debit: 0, credit: 5000, direction: "鍊�", balance: 13000 },
+  { date: "2024-01-31", voucherNo: "-", summary: "鏈湀鍚堣", debit: 13000, credit: 10000, direction: "鍊�", balance: 13000 },
+  { date: "2024-02-01", voucherNo: "-", summary: "鏈熷垵浣欓", debit: 0, credit: 0, direction: "鍊�", balance: 13000 },
+  { date: "2024-02-10", voucherNo: "璁�-0006", summary: "閿�鍞嚭搴�", debit: 6000, credit: 0, direction: "鍊�", balance: 19000 },
+  { date: "2024-02-15", voucherNo: "璁�-0007", summary: "鏀跺埌璐ф", debit: 0, credit: 4000, direction: "鍊�", balance: 15000 },
+  { date: "2024-02-28", voucherNo: "-", summary: "鏈湀鍚堣", debit: 6000, credit: 4000, direction: "鍊�", balance: 15000 },
+  { date: "2024-03-01", voucherNo: "-", summary: "鏈熷垵浣欓", debit: 0, credit: 0, direction: "鍊�", balance: 15000 },
+  { date: "2024-03-05", voucherNo: "璁�-0008", summary: "閿�鍞嚭搴�", debit: 7000, credit: 0, direction: "鍊�", balance: 22000 },
+  { date: "2024-03-10", voucherNo: "璁�-0009", summary: "鏀跺埌璐ф", debit: 0, credit: 6000, direction: "鍊�", balance: 16000 },
+  { date: "2024-03-31", voucherNo: "-", summary: "鏈湀鍚堣", debit: 7000, credit: 6000, direction: "鍊�", balance: 16000 },
+  { date: "2024-03-31", voucherNo: "-", summary: "鏈勾绱", debit: 26000, credit: 20000, direction: "鍊�", balance: 16000 },
+];
+
+const getTableData = () => {
+  if (!currentSubject.value) {
+    dataList.value = [];
+    return;
+  }
+  dataList.value = [...mockData];
+};
+
+const resetFilters = () => {
+  filters.subject = [];
+  filters.auxiliary = "";
+  filters.auxiliaryItem = "";
+  filters.startMonth = "2024-01";
+  filters.endMonth = "2024-03";
+  dataList.value = [];
+};
+
+const getSummaries = (param) => {
+  const { columns, data } = param;
+  const sums = [];
+  columns.forEach((column, index) => {
+    if (index === 0) {
+      sums[index] = "鍚堣";
+      return;
+    }
+    if (column.property === "debit") {
+      const values = data.map(item => Number(item.debit));
+      const sum = values.reduce((prev, curr) => prev + curr, 0);
+      sums[index] = "楼" + formatMoney(sum);
+    } else if (column.property === "credit") {
+      const values = data.map(item => Number(item.credit));
+      const sum = values.reduce((prev, curr) => prev + curr, 0);
+      sums[index] = "楼" + formatMoney(sum);
+    } else {
+      sums[index] = "";
+    }
+  });
+  return sums;
+};
+
+const handlePrint = () => {
+  ElMessage.info("鎵撳嵃鍔熻兘");
+};
+
+const handleOut = () => {
+  ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+onMounted(() => {
+  // 榛樿涓嶅姞杞芥暟鎹紝闇�瑕侀�夋嫨绉戠洰
+});
+</script>
+
+<style lang="scss" scoped>
+.ledger-header {
+  text-align: center;
+  margin-bottom: 20px;
+  h2 {
+    margin: 0 0 10px 0;
+  }
+  p {
+    color: #606266;
+    margin: 5px 0;
+  }
+}
+
+.text-primary {
+  color: #409eff;
+  font-weight: bold;
+}
+
+.text-success {
+  color: #67c23a;
+  font-weight: bold;
+}
+
+.text-danger {
+  color: #f56c6c;
+  font-weight: bold;
+}
+
+.text-warning {
+  color: #e6a23c;
+  font-weight: bold;
+}
+</style>
diff --git a/src/views/financialManagement/voucher/generalLedger.vue b/src/views/financialManagement/voucher/generalLedger.vue
new file mode 100644
index 0000000..5da2d70
--- /dev/null
+++ b/src/views/financialManagement/voucher/generalLedger.vue
@@ -0,0 +1,230 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="浼氳绉戠洰:">
+        <el-cascader v-model="filters.subject" :options="subjectOptions" :props="{ label: 'name', value: 'code' }" placeholder="璇烽�夋嫨浼氳绉戠洰" clearable style="width: 250px;" filterable />
+      </el-form-item>
+      <el-form-item label="鏈熼棿:">
+        <el-date-picker v-model="filters.startMonth" type="month" placeholder="寮�濮嬫湀浠�" value-format="YYYY-MM" style="width: 140px;" />
+        <span style="margin: 0 10px;">鑷�</span>
+        <el-date-picker v-model="filters.endMonth" type="month" placeholder="缁撴潫鏈堜唤" value-format="YYYY-MM" style="width: 140px;" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鏌ヨ</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+        <el-button @click="handlePrint" icon="Printer">鎵撳嵃</el-button>
+        <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+      </el-form-item>
+    </el-form>
+
+    <div class="ledger-header" v-if="currentSubject">
+      <h2>绉戠洰鎬昏处</h2>
+      <p>绉戠洰: {{ currentSubject.code }} {{ currentSubject.name }}</p>
+      <p>鏈熼棿: {{ filters.startMonth }} 鑷� {{ filters.endMonth }}</p>
+    </div>
+
+    <div class="table_list">
+      <el-table :data="dataList" border style="width: 100%" show-summary :summary-method="getSummaries">
+        <el-table-column prop="date" label="鏃ユ湡" width="120" />
+        <el-table-column prop="voucherNo" label="鍑瘉瀛楀彿" width="120" />
+        <el-table-column prop="summary" label="鎽樿" min-width="200" show-overflow-tooltip />
+        <el-table-column label="鍊熸柟" width="150">
+          <template #default="{ row }">
+            <span v-if="row.debit > 0" class="text-danger">楼{{ formatMoney(row.debit) }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="璐锋柟" width="150">
+          <template #default="{ row }">
+            <span v-if="row.credit > 0" class="text-success">楼{{ formatMoney(row.credit) }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="鏂瑰悜" width="80">
+          <template #default="{ row }">
+            <el-tag :type="row.direction === '鍊�' ? 'success' : 'danger'" size="small">{{ row.direction }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="浣欓" width="150">
+          <template #default="{ row }">
+            <span :class="row.balance >= 0 ? 'text-primary' : 'text-warning'">楼{{ formatMoney(Math.abs(row.balance)) }}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <el-empty v-if="!currentSubject" description="璇烽�夋嫨浼氳绉戠洰鏌ヨ" style="margin-top: 50px;" />
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, computed } from "vue";
+import { ElMessage } from "element-plus";
+
+defineOptions({
+  name: "绉戠洰鎬昏处",
+});
+
+const filters = reactive({
+  subject: [],
+  startMonth: "2024-01",
+  endMonth: "2024-03",
+});
+
+const dataList = ref([]);
+
+const subjectOptions = [
+  {
+    code: "1001",
+    name: "搴撳瓨鐜伴噾",
+    children: [],
+  },
+  {
+    code: "1002",
+    name: "閾惰瀛樻",
+    children: [
+      { code: "100201", name: "宸ュ晢閾惰" },
+      { code: "100202", name: "寤鸿閾惰" },
+    ],
+  },
+  {
+    code: "1122",
+    name: "搴旀敹璐︽",
+    children: [],
+  },
+  {
+    code: "2202",
+    name: "搴斾粯璐︽",
+    children: [],
+  },
+  {
+    code: "6001",
+    name: "涓昏惀涓氬姟鏀跺叆",
+    children: [],
+  },
+];
+
+const currentSubject = computed(() => {
+  if (!filters.subject || filters.subject.length === 0) return null;
+  const code = filters.subject[filters.subject.length - 1];
+  return findSubject(subjectOptions, code);
+});
+
+const findSubject = (options, code) => {
+  for (const item of options) {
+    if (item.code === code) return item;
+    if (item.children && item.children.length > 0) {
+      const found = findSubject(item.children, code);
+      if (found) return found;
+    }
+  }
+  return null;
+};
+
+const formatMoney = (value) => {
+  if (value === undefined || value === null) return "0.00";
+  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+const mockData = [
+  { date: "2024-01-01", voucherNo: "-", summary: "鏈熷垵浣欓", debit: 0, credit: 0, direction: "鍊�", balance: 100000 },
+  { date: "2024-01-05", voucherNo: "璁�-0001", summary: "閿�鍞敹鍏�", debit: 5650, credit: 0, direction: "鍊�", balance: 105650 },
+  { date: "2024-01-10", voucherNo: "璁�-0002", summary: "閲囪喘鏀嚭", debit: 0, credit: 8000, direction: "鍊�", balance: 97650 },
+  { date: "2024-01-15", voucherNo: "璁�-0003", summary: "鏀跺埌璐ф", debit: 10000, credit: 0, direction: "鍊�", balance: 107650 },
+  { date: "2024-01-20", voucherNo: "璁�-0004", summary: "鏀粯璐圭敤", debit: 0, credit: 5000, direction: "鍊�", balance: 102650 },
+  { date: "2024-01-31", voucherNo: "-", summary: "鏈湀鍚堣", debit: 15650, credit: 13000, direction: "鍊�", balance: 102650 },
+  { date: "2024-02-01", voucherNo: "-", summary: "鏈熷垵浣欓", debit: 0, credit: 0, direction: "鍊�", balance: 102650 },
+  { date: "2024-02-10", voucherNo: "璁�-0005", summary: "閿�鍞敹鍏�", debit: 8000, credit: 0, direction: "鍊�", balance: 110650 },
+  { date: "2024-02-15", voucherNo: "璁�-0006", summary: "閲囪喘鏀嚭", debit: 0, credit: 12000, direction: "鍊�", balance: 98650 },
+  { date: "2024-02-28", voucherNo: "-", summary: "鏈湀鍚堣", debit: 8000, credit: 12000, direction: "鍊�", balance: 98650 },
+  { date: "2024-03-01", voucherNo: "-", summary: "鏈熷垵浣欓", debit: 0, credit: 0, direction: "鍊�", balance: 98650 },
+  { date: "2024-03-05", voucherNo: "璁�-0007", summary: "閿�鍞敹鍏�", debit: 12000, credit: 0, direction: "鍊�", balance: 110650 },
+  { date: "2024-03-10", voucherNo: "璁�-0008", summary: "鏀粯宸ヨ祫", debit: 0, credit: 15000, direction: "鍊�", balance: 95650 },
+  { date: "2024-03-31", voucherNo: "-", summary: "鏈湀鍚堣", debit: 12000, credit: 15000, direction: "鍊�", balance: 95650 },
+  { date: "2024-03-31", voucherNo: "-", summary: "鏈勾绱", debit: 35650, credit: 40000, direction: "鍊�", balance: 95650 },
+];
+
+const getTableData = () => {
+  if (!currentSubject.value) {
+    dataList.value = [];
+    return;
+  }
+  dataList.value = [...mockData];
+};
+
+const resetFilters = () => {
+  filters.subject = [];
+  filters.startMonth = "2024-01";
+  filters.endMonth = "2024-03";
+  dataList.value = [];
+};
+
+const getSummaries = (param) => {
+  const { columns, data } = param;
+  const sums = [];
+  columns.forEach((column, index) => {
+    if (index === 0) {
+      sums[index] = "鍚堣";
+      return;
+    }
+    if (column.property === "debit") {
+      const values = data.map(item => Number(item.debit));
+      const sum = values.reduce((prev, curr) => prev + curr, 0);
+      sums[index] = "楼" + formatMoney(sum);
+    } else if (column.property === "credit") {
+      const values = data.map(item => Number(item.credit));
+      const sum = values.reduce((prev, curr) => prev + curr, 0);
+      sums[index] = "楼" + formatMoney(sum);
+    } else {
+      sums[index] = "";
+    }
+  });
+  return sums;
+};
+
+const handlePrint = () => {
+  ElMessage.info("鎵撳嵃鍔熻兘");
+};
+
+const handleOut = () => {
+  ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+onMounted(() => {
+  // 榛樿涓嶅姞杞芥暟鎹紝闇�瑕侀�夋嫨绉戠洰
+});
+</script>
+
+<style lang="scss" scoped>
+.ledger-header {
+  text-align: center;
+  margin-bottom: 20px;
+  h2 {
+    margin: 0 0 10px 0;
+  }
+  p {
+    color: #606266;
+    margin: 5px 0;
+  }
+}
+
+.text-primary {
+  color: #409eff;
+  font-weight: bold;
+}
+
+.text-success {
+  color: #67c23a;
+  font-weight: bold;
+}
+
+.text-danger {
+  color: #f56c6c;
+  font-weight: bold;
+}
+
+.text-warning {
+  color: #e6a23c;
+  font-weight: bold;
+}
+</style>
diff --git a/src/views/financialManagement/voucher/index.vue b/src/views/financialManagement/voucher/index.vue
new file mode 100644
index 0000000..817185c
--- /dev/null
+++ b/src/views/financialManagement/voucher/index.vue
@@ -0,0 +1,836 @@
+<template>
+  <div class="app-container">
+    <el-form :model="filters" :inline="true">
+      <el-form-item label="鍑瘉瀛楀彿:">
+        <el-input v-model="filters.voucherNo" placeholder="璇疯緭鍏ュ嚟璇佸瓧鍙�" clearable style="width: 200px;" />
+      </el-form-item>
+      <el-form-item label="鍑瘉鏃ユ湡:">
+        <el-date-picker v-model="filters.dateRange" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" range-separator="鑷�" start-placeholder="寮�濮嬫棩鏈�" end-placeholder="缁撴潫鏃ユ湡" clearable />
+      </el-form-item>
+      <el-form-item label="鍒跺崟浜�:">
+        <el-select v-model="filters.creator" placeholder="璇烽�夋嫨鍒跺崟浜�" clearable style="width: 150px;">
+          <el-option label="寮犱笁" value="寮犱笁" />
+          <el-option label="鏉庡洓" value="鏉庡洓" />
+          <el-option label="鐜嬩簲" value="鐜嬩簲" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鐘舵��:">
+        <el-select v-model="filters.status" placeholder="璇烽�夋嫨鐘舵��" clearable style="width: 150px;">
+          <el-option label="鏈繃璐�" value="unposted" />
+          <el-option label="宸茶繃璐�" value="posted" />
+          <el-option label="宸蹭綔搴�" value="cancelled" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="getTableData">鎼滅储</el-button>
+        <el-button @click="resetFilters">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+    <div class="table_list">
+      <div class="actions">
+        <div>
+          <el-statistic title="鍊熸柟鍚堣" :value="totalDebit" precision="2" prefix="楼" />
+          <el-statistic title="璐锋柟鍚堣" :value="totalCredit" precision="2" prefix="楼" style="margin-left: 30px;" />
+        </div>
+        <div>
+          <el-button type="primary" @click="add" icon="Plus">鏂板鍑瘉</el-button>
+          <el-button @click="handleImport" icon="Upload">瀵煎叆</el-button>
+          <el-button @click="handleOut" icon="Download">瀵煎嚭</el-button>
+        </div>
+      </div>
+      <PIMTable
+        rowKey="id"
+        :column="columns"
+        :tableData="dataList"
+        :page="{
+          current: pagination.currentPage,
+          size: pagination.pageSize,
+          total: pagination.total,
+        }"
+        @pagination="changePage"
+      >
+        <template #debit="{ row }">
+          <span class="text-danger" v-if="row.debit > 0">楼{{ formatMoney(row.debit) }}</span>
+          <span v-else>-</span>
+        </template>
+        <template #credit="{ row }">
+          <span class="text-success" v-if="row.credit > 0">楼{{ formatMoney(row.credit) }}</span>
+          <span v-else>-</span>
+        </template>
+        <template #status="{ row }">
+          <el-tag :type="getStatusType(row.status)">{{ getStatusLabel(row.status) }}</el-tag>
+        </template>
+        <template #operation="{ row }">
+          <el-button type="primary" link @click="view(row)">鏌ョ湅</el-button>
+          <el-button type="primary" link @click="edit(row)" v-if="row.status === 'unposted'">缂栬緫</el-button>
+          <el-button type="success" link @click="handlePost(row)" v-if="row.status === 'unposted'">杩囪处</el-button>
+          <el-button type="danger" link @click="handleCancel(row)" v-if="row.status === 'unposted'">浣滃簾</el-button>
+        </template>
+      </PIMTable>
+    </div>
+
+    <FormDialog :title="dialogTitle" v-model="dialogVisible" width="1200px" @confirm="submitForm" @cancel="dialogVisible = false">
+      <div class="voucher-container">
+        <div class="voucher-header">
+          <h2 class="voucher-title">璁拌处鍑瘉</h2>
+          <div class="voucher-period">{{ form.voucherDate ? form.voucherDate.substring(0, 7) + '鏈�' : '' }}</div>
+        </div>
+        <el-form :model="form" :rules="rules" ref="formRef" label-width="0">
+          <div class="voucher-info">
+            <div class="voucher-no-section">
+              <span class="label">鍑瘉瀛楋細</span>
+              <el-select v-model="form.voucherPrefix" style="width: 70px;">
+                <el-option label="璁�" value="璁�" />
+              </el-select>
+              <el-input v-model="form.voucherNum" style="width: 60px;" />
+              <span class="label" style="margin-left: 5px;">鍙�</span>
+            </div>
+            <div class="voucher-date-section">
+              <span class="label">鏃ユ湡锛�</span>
+              <el-date-picker v-model="form.voucherDate" type="date" placeholder="閫夋嫨鏃ユ湡" value-format="YYYY-MM-DD" style="width: 140px;" />
+            </div>
+            <div class="voucher-attachment-section">
+              <span class="label">闄勪欢锛�</span>
+              <el-input-number v-model="form.attachmentCount" :min="0" :controls="false" style="width: 60px;" />
+              <span class="label" style="margin-left: 5px;">寮�</span>
+              <el-button type="primary" link style="margin-left: 10px;">涓婁紶鏂囦欢</el-button>
+            </div>
+          </div>
+          <div class="voucher-table">
+            <table class="accounting-voucher">
+              <thead>
+                <tr>
+                  <th class="col-summary" rowspan="2">鎽樿</th>
+                  <th class="col-subject" rowspan="2">浼氳绉戠洰</th>
+                  <th class="col-debit-header" colspan="11">鍊熸柟</th>
+                  <th class="col-credit-header" colspan="11">璐锋柟</th>
+                  <th class="col-action" rowspan="2">鎿嶄綔</th>
+                </tr>
+                <tr class="amount-header">
+                  <th>浜�</th>
+                  <th>鍗�</th>
+                  <th>鐧�</th>
+                  <th>鍗�</th>
+                  <th>涓�</th>
+                  <th>鍗�</th>
+                  <th>鐧�</th>
+                  <th>鍗�</th>
+                  <th>鍏�</th>
+                  <th>瑙�</th>
+                  <th>鍒�</th>
+                  <th>浜�</th>
+                  <th>鍗�</th>
+                  <th>鐧�</th>
+                  <th>鍗�</th>
+                  <th>涓�</th>
+                  <th>鍗�</th>
+                  <th>鐧�</th>
+                  <th>鍗�</th>
+                  <th>鍏�</th>
+                  <th>瑙�</th>
+                  <th>鍒�</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr v-for="(entry, rowIndex) in form.entries" :key="rowIndex" @click="selectRow(rowIndex)" :class="{ 'selected-row': selectedRowIndex === rowIndex }">
+                  <td class="col-summary">
+                    <el-input v-model="entry.summary" placeholder="璇疯緭鍏ユ憳瑕�" @focus="selectRow(rowIndex)" />
+                  </td>
+                  <td class="col-subject">
+                    <el-select v-model="entry.subjectCode" placeholder="閫夋嫨绉戠洰" filterable @change="(val) => handleSubjectChange(val, rowIndex)" @focus="selectRow(rowIndex)">
+                      <el-option v-for="item in subjectList" :key="item.code" :label="item.code + item.name" :value="item.code" />
+                    </el-select>
+                    <div class="subject-name">{{ entry.subjectName }}</div>
+                  </td>
+                  <!-- 鍊熸柟11鍒� -->
+                  <template v-if="editingCell.row === rowIndex && editingCell.type === 'debit'">
+                    <td colspan="11" class="debit-input-cell">
+                      <el-input-number ref="amountInputRef" v-model="entry.debit" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" />
+                    </td>
+                  </template>
+                  <template v-else>
+                    <td v-for="(digit, dIndex) in getAmountDigits(entry.debit, 11)" :key="'debit-'+dIndex" class="amount-cell debit-cell" @click="openAmountInput(rowIndex, 'debit')">
+                      <span :class="{ 'text-primary': digit !== '', 'zero': digit === '' }">{{ digit || '' }}</span>
+                    </td>
+                  </template>
+                  <!-- 璐锋柟11鍒� -->
+                  <template v-if="editingCell.row === rowIndex && editingCell.type === 'credit'">
+                    <td colspan="11" class="credit-input-cell">
+                      <el-input-number ref="amountInputRef" v-model="entry.credit" :min="0" :precision="2" :controls="false" size="small" @blur="finishEdit" class="full-width-input" />
+                    </td>
+                  </template>
+                  <template v-else>
+                    <td v-for="(digit, dIndex) in getAmountDigits(entry.credit, 11)" :key="'credit-'+dIndex" class="amount-cell credit-cell" @click="openAmountInput(rowIndex, 'credit')">
+                      <span :class="{ 'text-danger': digit !== '', 'zero': digit === '' }">{{ digit || '' }}</span>
+                    </td>
+                  </template>
+                  <td class="col-action">
+                    <el-button type="danger" link size="small" @click="removeEntry(rowIndex)" icon="Delete" :disabled="form.entries.length <= 2">鍒犻櫎</el-button>
+                  </td>
+                </tr>
+                <tr class="total-row">
+                  <td class="col-summary" colspan="2" style="text-align: center; font-weight: bold;">鍚堣锛�</td>
+                  <td v-for="(digit, index) in getAmountDigits(totalDebitEntry, 11)" :key="'total-debit-'+index" class="amount-cell total-debit-cell">
+                    <span :class="{ 'text-primary': digit !== '' }">{{ digit }}</span>
+                  </td>
+                  <td v-for="(digit, index) in getAmountDigits(totalCreditEntry, 11)" :key="'total-credit-'+index" class="amount-cell total-credit-cell">
+                    <span :class="{ 'text-danger': digit !== '' }">{{ digit }}</span>
+                  </td>
+                  <td class="col-action"></td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+          <div class="voucher-toolbar">
+            <el-button type="primary" link @click="addEntry" icon="Plus">鏂板琛�</el-button>
+          </div>
+          <div class="voucher-footer">
+            <div class="creator-section">
+              <span class="label">鍒跺崟浜猴細{{ form.creator }}</span>
+            </div>
+          </div>
+        </el-form>
+      </div>
+      <template #footer>
+        <div>
+          <el-button type="primary" @click="submitForm" :disabled="!isBalanced">淇濆瓨</el-button>
+          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+        </div>
+      </template>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, computed, nextTick } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+
+defineOptions({
+  name: "鍑瘉绠$悊",
+});
+
+const filters = reactive({
+  voucherNo: "",
+  dateRange: [],
+  creator: "",
+  status: "",
+});
+
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+});
+
+const columns = [
+  { label: "鍑瘉瀛楀彿", prop: "voucherNo", width: "120" },
+  { label: "鍑瘉鏃ユ湡", prop: "voucherDate", width: "120" },
+  { label: "鎽樿", prop: "summary", showOverflowTooltip: true },
+  { label: "鍊熸柟閲戦", prop: "debit", slot: "debit" },
+  { label: "璐锋柟閲戦", prop: "credit", slot: "credit" },
+  { label: "鍒跺崟浜�", prop: "creator", width: "100" },
+  { label: "鐘舵��", prop: "status", slot: "status" },
+  { label: "鎿嶄綔", prop: "operation", slot: "operation", width: "220", fixed: "right" },
+];
+
+const dataList = ref([]);
+const dialogVisible = ref(false);
+const dialogTitle = ref("");
+const formRef = ref(null);
+const isEdit = ref(false);
+const currentId = ref(null);
+
+const subjectList = [
+  { code: "1001", name: "搴撳瓨鐜伴噾" },
+  { code: "1002", name: "閾惰瀛樻" },
+  { code: "1122", name: "搴旀敹璐︽" },
+  { code: "2202", name: "搴斾粯璐︽" },
+  { code: "5001", name: "鐢熶骇鎴愭湰" },
+  { code: "6001", name: "涓昏惀涓氬姟鏀跺叆" },
+  { code: "6401", name: "涓昏惀涓氬姟鎴愭湰" },
+];
+
+const form = reactive({
+  voucherNo: "",
+  voucherPrefix: "璁�",
+  voucherNum: "",
+  voucherDate: "",
+  attachmentCount: 0,
+  entries: [],
+  creator: "寮犱笁",
+  remark: "",
+});
+
+const selectedRowIndex = ref(-1);
+const editingCell = reactive({
+  row: -1,
+  type: "",
+});
+const amountInputRef = ref(null);
+
+const isBalanced = computed(() => {
+  return totalDebitEntry.value === totalCreditEntry.value && totalDebitEntry.value > 0;
+});
+
+const rules = {
+  voucherDate: [{ required: true, message: "璇烽�夋嫨鍑瘉鏃ユ湡", trigger: "change" }],
+};
+
+const mockData = [
+  { id: 1, voucherNo: "璁�-0001", voucherDate: "2024-01-15", summary: "閿�鍞敹鍏�", debit: 5650, credit: 5650, creator: "寮犱笁", status: "posted", entries: [{ subjectCode: "1002", subjectName: "閾惰瀛樻", summary: "閿�鍞敹鍏�", debit: 5650, credit: 0 }, { subjectCode: "6001", subjectName: "涓昏惀涓氬姟鏀跺叆", summary: "閿�鍞敹鍏�", debit: 0, credit: 5000 }, { subjectCode: "2221", subjectName: "搴斾氦绋庤垂", summary: "閿�椤圭◣棰�", debit: 0, credit: 650 }] },
+  { id: 2, voucherNo: "璁�-0002", voucherDate: "2024-01-16", summary: "閲囪喘鍘熸潗鏂�", debit: 9040, credit: 9040, creator: "鏉庡洓", status: "unposted", entries: [{ subjectCode: "5001", subjectName: "鐢熶骇鎴愭湰", summary: "閲囪喘鍘熸潗鏂�", debit: 8000, credit: 0 }, { subjectCode: "2221", subjectName: "搴斾氦绋庤垂", summary: "杩涢」绋庨", debit: 1040, credit: 0 }, { subjectCode: "2202", subjectName: "搴斾粯璐︽", summary: "閲囪喘鍘熸潗鏂�", debit: 0, credit: 9040 }] },
+  { id: 3, voucherNo: "璁�-0003", voucherDate: "2024-01-18", summary: "鏀粯璐ф", debit: 5000, credit: 5000, creator: "寮犱笁", status: "posted", entries: [{ subjectCode: "2202", subjectName: "搴斾粯璐︽", summary: "鏀粯璐ф", debit: 5000, credit: 0 }, { subjectCode: "1002", subjectName: "閾惰瀛樻", summary: "鏀粯璐ф", debit: 0, credit: 5000 }] },
+];
+
+const totalDebit = computed(() => {
+  return dataList.value.reduce((sum, item) => sum + Number(item.debit), 0);
+});
+
+const totalCredit = computed(() => {
+  return dataList.value.reduce((sum, item) => sum + Number(item.credit), 0);
+});
+
+const totalDebitEntry = computed(() => {
+  return form.entries.reduce((sum, item) => sum + Number(item.debit || 0), 0);
+});
+
+const totalCreditEntry = computed(() => {
+  return form.entries.reduce((sum, item) => sum + Number(item.credit || 0), 0);
+});
+
+const formatMoney = (value) => {
+  if (value === undefined || value === null) return "0.00";
+  return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+const getStatusLabel = (status) => {
+  const map = { unposted: "鏈繃璐�", posted: "宸茶繃璐�", cancelled: "宸蹭綔搴�" };
+  return map[status] || status;
+};
+
+const getStatusType = (status) => {
+  const map = { unposted: "warning", posted: "success", cancelled: "info" };
+  return map[status] || "";
+};
+
+const getTableData = () => {
+  let result = [...mockData];
+  if (filters.voucherNo) {
+    result = result.filter(item => item.voucherNo.includes(filters.voucherNo));
+  }
+  if (filters.dateRange && filters.dateRange.length === 2) {
+    result = result.filter(item => item.voucherDate >= filters.dateRange[0] && item.voucherDate <= filters.dateRange[1]);
+  }
+  if (filters.creator) {
+    result = result.filter(item => item.creator === filters.creator);
+  }
+  if (filters.status) {
+    result = result.filter(item => item.status === filters.status);
+  }
+  pagination.total = result.length;
+  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
+};
+
+const resetFilters = () => {
+  filters.voucherNo = "";
+  filters.dateRange = [];
+  filters.creator = "";
+  filters.status = "";
+  pagination.currentPage = 1;
+  getTableData();
+};
+
+const changePage = ({ current, size }) => {
+  pagination.currentPage = current;
+  pagination.pageSize = size;
+  getTableData();
+};
+
+const addEntry = () => {
+  form.entries.push({ subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 });
+};
+
+const selectRow = (index) => {
+  selectedRowIndex.value = index;
+};
+
+const openAmountInput = (index, type) => {
+  editingCell.row = index;
+  editingCell.type = type;
+  nextTick(() => {
+    if (amountInputRef.value) {
+      amountInputRef.value.focus();
+    }
+  });
+};
+
+const finishEdit = () => {
+  editingCell.row = -1;
+  editingCell.type = "";
+};
+
+const getAmountDigits = (amount, length) => {
+  if (!amount || amount === 0) {
+    return new Array(length).fill('');
+  }
+
+  const amountStr = Number(amount).toFixed(2);
+  const [intPart, decPart] = amountStr.split('.');
+  const fullAmount = intPart + decPart;
+
+  // 宸﹀~鍏�0鍒版寚瀹氶暱搴�
+  const paddedAmount = fullAmount.padStart(length, '0');
+  const digits = paddedAmount.split('');
+
+  // 鎵惧埌绗竴涓潪闆舵暟瀛楃殑浣嶇疆
+  let firstNonZeroIndex = 0;
+  for (let i = 0; i < digits.length; i++) {
+    if (digits[i] !== '0') {
+      firstNonZeroIndex = i;
+      break;
+    }
+  }
+
+  // 鍙殣钘忓墠瀵奸浂锛堢涓�涓潪闆舵暟瀛椾箣鍓嶇殑闆讹級
+  return digits.map((d, index) => {
+    if (index < firstNonZeroIndex) {
+      return ''; // 鍓嶅闆舵樉绀轰负绌�
+    }
+    return d; // 淇濈暀閲戦涓殑闆�
+  });
+};
+
+const removeEntry = (index) => {
+  form.entries.splice(index, 1);
+  calculateTotal();
+};
+
+const handleSubjectChange = (val, index) => {
+  const subject = subjectList.find(item => item.code === val);
+  if (subject) {
+    form.entries[index].subjectName = subject.name;
+  }
+};
+
+const calculateTotal = () => {
+  // 鑷姩璁$畻锛岀敱computed灞炴�у鐞�
+};
+
+const add = () => {
+  isEdit.value = false;
+  dialogTitle.value = "鏂板鍑瘉";
+  const nextNum = String(mockData.length + 1).padStart(2, "0");
+  Object.assign(form, {
+    voucherNo: "璁�-" + nextNum,
+    voucherPrefix: "璁�",
+    voucherNum: nextNum,
+    voucherDate: new Date().toISOString().split('T')[0],
+    attachmentCount: 0,
+    entries: [
+      { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 },
+      { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 },
+      { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 },
+      { subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 },
+    ],
+    creator: "寮犱笁",
+    remark: "",
+  });
+  selectedRowIndex.value = 0;
+  dialogVisible.value = true;
+};
+
+const edit = (row) => {
+  isEdit.value = true;
+  currentId.value = row.id;
+  dialogTitle.value = "缂栬緫鍑瘉";
+  const parts = row.voucherNo.split('-');
+  Object.assign(form, {
+    ...row,
+    voucherPrefix: parts[0] || '璁�',
+    voucherNum: parts[1] || '',
+  });
+  if (form.entries.length < 4) {
+    while (form.entries.length < 4) {
+      form.entries.push({ subjectCode: "", subjectName: "", summary: "", debit: 0, credit: 0 });
+    }
+  }
+  selectedRowIndex.value = 0;
+  dialogVisible.value = true;
+};
+
+const view = (row) => {
+  ElMessage.info(`鏌ョ湅鍑瘉: ${row.voucherNo}`);
+};
+
+const handlePost = (row) => {
+  ElMessageBox.confirm("纭杩囪处璇ュ嚟璇佸悧锛�", "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "info",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData[index].status = "posted";
+    }
+    ElMessage.success("杩囪处鎴愬姛");
+    getTableData();
+  });
+};
+
+const handleCancel = (row) => {
+  ElMessageBox.confirm("纭浣滃簾璇ュ嚟璇佸悧锛�", "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  }).then(() => {
+    const index = mockData.findIndex(item => item.id === row.id);
+    if (index !== -1) {
+      mockData[index].status = "cancelled";
+    }
+    ElMessage.success("浣滃簾鎴愬姛");
+    getTableData();
+  });
+};
+
+const handleImport = () => {
+  ElMessage.info("瀵煎叆鍔熻兘");
+};
+
+const handleOut = () => {
+  ElMessage.success("瀵煎嚭鎴愬姛");
+};
+
+const submitForm = () => {
+  formRef.value.validate((valid) => {
+    if (valid) {
+      if (!isBalanced.value) {
+        ElMessage.error("鍊熻捶涓嶅钩琛★紝璇锋鏌ュ垎褰�");
+        return;
+      }
+
+      const validEntries = form.entries.filter(e => e.subjectCode && (e.debit > 0 || e.credit > 0));
+      const summary = validEntries.find(e => e.debit > 0)?.summary || "";
+
+      const voucherNo = `${form.voucherPrefix}-${form.voucherNum}`;
+      const dataToSave = {
+        ...form,
+        voucherNo,
+        summary,
+        debit: totalDebitEntry.value,
+        credit: totalCreditEntry.value,
+        entries: validEntries,
+      };
+
+      if (isEdit.value) {
+        const index = mockData.findIndex(item => item.id === currentId.value);
+        if (index !== -1) {
+          mockData[index] = { ...mockData[index], ...dataToSave };
+        }
+        ElMessage.success("缂栬緫鎴愬姛");
+      } else {
+        const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
+        mockData.push({ id: newId, ...dataToSave, status: "unposted" });
+        ElMessage.success("鏂板鎴愬姛");
+      }
+      dialogVisible.value = false;
+      getTableData();
+    }
+  });
+};
+
+onMounted(() => {
+  getTableData();
+});
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 15px;
+
+  > div:first-child {
+    display: flex;
+    align-items: center;
+  }
+}
+
+.text-success {
+  color: #67c23a;
+  font-weight: bold;
+}
+
+.text-danger {
+  color: #f56c6c;
+  font-weight: bold;
+}
+
+.text-primary {
+  color: #409eff;
+}
+
+.voucher-container {
+  background: #fff;
+  padding: 20px;
+}
+
+.voucher-header {
+  text-align: center;
+  margin-bottom: 15px;
+
+  .voucher-title {
+    font-size: 22px;
+    font-weight: bold;
+    margin: 0 0 5px 0;
+    color: #303133;
+  }
+
+  .voucher-period {
+    font-size: 14px;
+    color: #909399;
+  }
+}
+
+.voucher-info {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 15px;
+  padding: 0 10px;
+
+  .label {
+    font-size: 14px;
+    color: #606266;
+  }
+
+  .voucher-no-section,
+  .voucher-date-section,
+  .voucher-attachment-section {
+    display: flex;
+    align-items: center;
+  }
+}
+
+.voucher-table {
+  border: 1px solid #dcdfe6;
+  border-right: none;
+  margin-bottom: 15px;
+}
+
+.accounting-voucher {
+  width: 100%;
+  border-collapse: collapse;
+  font-size: 13px;
+
+  th,
+  td {
+    border: 1px solid #dcdfe6;
+    text-align: center;
+    padding: 0;
+    height: 36px;
+  }
+
+  & th:last-child,
+  & td:last-child {
+    border-right: none !important;
+  }
+
+  thead {
+    background-color: #f5f7fa;
+
+    th {
+      font-weight: normal;
+      color: #606266;
+      font-size: 12px;
+    }
+
+    .col-summary,
+    .col-subject {
+      font-weight: bold;
+      font-size: 13px;
+    }
+
+    .col-debit-header,
+    .col-credit-header {
+      background-color: #ecf5ff;
+      color: #409eff;
+      font-weight: bold;
+    }
+  }
+
+  .amount-header {
+    th {
+      font-size: 11px;
+      padding: 2px 0;
+      background-color: #f5f7fa;
+    }
+  }
+
+  .col-summary {
+    width: 160px;
+    min-width: 160px;
+  }
+
+  .col-subject {
+    width: 180px;
+    min-width: 180px;
+  }
+
+  .col-action {
+    width: 60px;
+    min-width: 60px;
+    text-align: center;
+  }
+
+  .amount-cell {
+    width: 24px;
+    min-width: 24px;
+    max-width: 24px;
+    padding: 0;
+    font-size: 13px;
+    font-family: 'Courier New', monospace;
+    cursor: pointer;
+    text-align: center;
+
+    &:hover {
+      background-color: #f5f7fa;
+    }
+
+    span {
+      display: block;
+      width: 100%;
+      height: 100%;
+      line-height: 36px;
+
+      &.zero {
+        color: #c0c4cc;
+      }
+    }
+  }
+
+  .debit-input-cell,
+  .credit-input-cell {
+    padding: 0;
+    background-color: #ecf5ff;
+
+    .full-width-input {
+      width: 100%;
+
+      :deep(.el-input__wrapper) {
+        padding: 0 10px;
+        box-shadow: none;
+        background-color: transparent;
+      }
+
+      input {
+        text-align: right;
+        font-size: 14px;
+        height: 34px;
+      }
+    }
+  }
+
+  tbody {
+    tr {
+      &:hover {
+        background-color: #f5f7fa;
+      }
+
+      &.selected-row {
+        background-color: #ecf5ff;
+      }
+    }
+
+    td {
+      .el-input {
+        .el-input__wrapper {
+          box-shadow: none;
+          padding: 0 5px;
+        }
+
+        input {
+          text-align: center;
+          height: 34px;
+        }
+      }
+
+      .el-select {
+        width: 100%;
+
+        .el-input__wrapper {
+          box-shadow: none;
+        }
+
+        input {
+          text-align: center;
+          height: 34px;
+        }
+      }
+    }
+
+    .col-summary {
+      .el-input input {
+        text-align: left;
+        padding-left: 10px;
+      }
+    }
+
+    .col-subject {
+      position: relative;
+
+      .el-select {
+        .el-input input {
+          font-size: 12px;
+        }
+      }
+
+      .subject-name {
+        font-size: 11px;
+        color: #909399;
+        margin-top: 2px;
+        line-height: 1.2;
+      }
+    }
+  }
+
+  .total-row {
+    background-color: #fdf6ec;
+
+    td {
+      font-weight: bold;
+    }
+
+    .total-cell {
+      background-color: #fdf6ec;
+      font-weight: bold;
+    }
+  }
+}
+
+.voucher-toolbar {
+  display: flex;
+  justify-content: flex-start;
+  padding: 10px 0;
+  margin-top: 5px;
+}
+
+.voucher-footer {
+  display: flex;
+  justify-content: flex-end;
+  padding: 0 10px;
+  margin-top: 10px;
+
+  .creator-section {
+    .label {
+      font-size: 14px;
+      color: #606266;
+    }
+  }
+}
+
+:deep(.el-dialog__body) {
+  padding: 10px 20px;
+}
+</style>
diff --git a/src/views/index.vue b/src/views/index.vue
index 4ae5fe4..00d4312 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -8,7 +8,7 @@
           <!-- 椤堕儴闂�欐潯 -->
           <div class="welcome-banner">
             <div class="welcome-title">
-              <span class="welcome-user">{{ userStore.roleName || '绯荤粺绠$悊鍛�' }}</span>
+              <span class="welcome-user">{{ userStore.nickName || '绯荤粺绠$悊鍛�' }}</span>
               <span> 鎮ㄥソ锛佺鎮ㄥ紑蹇冩瘡涓�澶�</span>
             </div>
             <div class="welcome-time">鐧诲綍浜�: {{ userStore.currentLoginTime }}</div>
@@ -728,7 +728,6 @@
 
 <style scoped>
 .dashboard {
-  background: #f5f7fa;
   min-height: 100vh;
   padding: 20px;
   box-sizing: border-box;
diff --git a/src/views/inventoryManagement/dispatchLog/Record.vue b/src/views/inventoryManagement/dispatchLog/Record.vue
index 53bb3c3..6060cf5 100644
--- a/src/views/inventoryManagement/dispatchLog/Record.vue
+++ b/src/views/inventoryManagement/dispatchLog/Record.vue
@@ -1,6 +1,6 @@
 <template>
-	<div class="app-container">
-		<div class="search_form">
+	<div>
+		<div class="search_form" style="margin-bottom: 10px;">
 			<div>
 				<span class="search_title ml10">鍑哄簱鏃ユ湡锛�</span>
 				<el-date-picker
@@ -27,6 +27,7 @@
 				>
 			</div>
 			<div>
+				<el-button type="primary" @click="handleBatchApprove">瀹℃壒</el-button>
 				<el-button @click="handleOut">瀵煎嚭</el-button>
 				<el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
 				<el-button type="primary" plain @click="handlePrint">鎵撳嵃</el-button>
@@ -67,6 +68,11 @@
 					show-overflow-tooltip
 				/>
 				<el-table-column
+					label="鎵瑰彿"
+					prop="batchNo"
+					show-overflow-tooltip
+				/>
+				<el-table-column
 					label="鍗曚綅"
 					prop="unit"
 					show-overflow-tooltip
@@ -88,6 +94,11 @@
             {{ getRecordType(scope.row.recordType) }}
           </template>
         </el-table-column>
+				<el-table-column label="瀹℃壒鐘舵��" prop="approvalStatus" show-overflow-tooltip>
+					<template #default="scope">
+						{{ getApprovalStatusLabel(scope.row.approvalStatus) }}
+					</template>
+				</el-table-column>
 			</el-table>
 			<pagination
 				v-show="total > 0"
@@ -109,7 +120,8 @@
 import { getCurrentDate } from "@/utils/index.js";
 import {
 	getStockOutPage,
-	delStockOut,
+	delPendingStockOut,
+	batchApproveStockOutRecords,
 } from "@/api/inventoryManagement/stockOut.js";
 import {
   findAllQualifiedStockOutRecordTypeOptions, findAllUnQualifiedStockOutRecordTypeOptions,
@@ -133,6 +145,10 @@
     type: String,
     required: true,
     default: '0'
+  },
+  topParentProductId: {
+    type: [String, Number],
+    default: undefined
   }
 })
 
@@ -163,7 +179,7 @@
 };
 const getList = () => {
 	tableLoading.value = true;
-	getStockOutPage({ ...searchForm.value, ...page, type: props.type })
+	getStockOutPage({ ...searchForm.value, ...page, type: props.type, topParentProductId: props.topParentProductId })
 		.then((res) => {
 			tableLoading.value = false;
 			tableData.value = res.data.records;
@@ -180,6 +196,25 @@
 const getRecordType = (recordType) => {
   return stockRecordTypeOptions.value.find(item => item.value === recordType)?.label || ''
 }
+
+const approvalStatusLabelMap = {
+	0: "寰呭鎵�",
+	1: "閫氳繃",
+	2: "椹冲洖",
+	pending: "寰呭鎵�",
+	approved: "閫氳繃",
+	rejected: "椹冲洖",
+	PENDING: "寰呭鎵�",
+	APPROVED: "閫氳繃",
+	REJECTED: "椹冲洖",
+};
+
+const getApprovalStatusLabel = (status) => {
+	if (status === null || status === undefined || status === "") {
+		return "寰呭鎵�";
+	}
+	return approvalStatusLabelMap[status] || "寰呭鎵�";
+};
 
 // 鑾峰彇鏉ユ簮绫诲瀷閫夐」
 const fetchStockRecordTypeOptions = () => {
@@ -203,6 +238,44 @@
 	console.log("selection", selectedRows.value);
 };
 const expandedRowKeys = ref([]);
+
+const handleBatchApprove = () => {
+	if (selectedRows.value.length === 0) {
+		proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+		return;
+	}
+	const ids = selectedRows.value.map((item) => item.id);
+	ElMessageBox.confirm("璇烽�夋嫨瀹℃壒缁撴灉", "瀹℃壒", {
+		confirmButtonText: "閫氳繃",
+		cancelButtonText: "椹冲洖",
+		type: "warning",
+		distinguishCancelAndClose: true,
+	})
+		.then(() => {
+			batchApproveStockOutRecords({ ids, approvalStatus: 1 })
+				.then(() => {
+					proxy.$modal.msgSuccess("瀹℃壒閫氳繃鎴愬姛");
+					getList();
+				})
+				.catch(() => {
+					proxy.$modal.msgError("瀹℃壒閫氳繃澶辫触");
+				});
+		})
+		.catch((action) => {
+			if (action === "cancel") {
+				batchApproveStockOutRecords({ ids, approvalStatus: 2 })
+					.then(() => {
+						proxy.$modal.msgSuccess("瀹℃壒椹冲洖鎴愬姛");
+						getList();
+					})
+					.catch(() => {
+						proxy.$modal.msgError("瀹℃壒椹冲洖澶辫触");
+					});
+				return;
+			}
+			proxy.$modal.msg("宸插彇娑�");
+		});
+};
 
 // 瀵煎嚭
 const handleOut = () => {
@@ -234,7 +307,7 @@
 		type: "warning",
 	})
 		.then(() => {
-			delStockOut(ids).then((res) => {
+			delPendingStockOut(ids).then((res) => {
 				proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
 				getList();
 			});
@@ -405,7 +478,6 @@
       <div class="print-page">
         <div class="delivery-note">
           <div class="header">
-            <div class="company-name">鑻辨辰闃查攬鏂版潗鏂欐湁闄愬叕鍙�</div>
             <div class="document-title">闆跺敭鍙戣揣鍗�</div>
           </div>
           
@@ -417,7 +489,7 @@
               </div>
               <div>
                 <span class="label">瀹㈡埛鍚嶇О锛�</span>
-                <span class="value">${item.supplierName || '寮犵埍鏈�'}</span>
+                <span class="value">${item.supplierName}</span>
               </div>
             </div>
             <div class="info-row">
@@ -537,6 +609,14 @@
 	getList();
   fetchStockRecordTypeOptions();
 });
+
+watch(
+  () => props.topParentProductId,
+  () => {
+    page.current = 1;
+    getList();
+  }
+);
 </script>
 
 <style scoped lang="scss">
diff --git a/src/views/inventoryManagement/dispatchLog/index.vue b/src/views/inventoryManagement/dispatchLog/index.vue
index 88d9984..458e9ec 100644
--- a/src/views/inventoryManagement/dispatchLog/index.vue
+++ b/src/views/inventoryManagement/dispatchLog/index.vue
@@ -1,38 +1,55 @@
 <!-- 鍦ㄤ綘鐨勪富椤甸潰涓� -->
 <template>
   <div class="app-container">
-    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
+    <div v-loading="loading" element-loading-text="鍔犺浇涓�..." style="min-height: 80vh;">
+    <el-tabs v-model="activeTab" @tab-change="handleTabChange" v-if="!loading">
       <el-tab-pane v-for="tab in tabs"
-                   :label="tab.label"
-                   :name="tab.name"
-                   :key="tab.name">
-        <record :type="tab.type" v-if="activeTab === tab.name" />
+                   :label="tab.productName"
+                   :name="tab.id"
+                   :key="tab.id">
+        <Record v-bind="{ type: tab.type, topParentProductId: activeTab }" v-if="tab.id === activeTab" />
       </el-tab-pane>
     </el-tabs>
+    </div>
   </div>
 </template>
 
 <script setup>
+import { ref, onMounted } from 'vue';
+import { productTreeList } from "@/api/basicData/product.js";
 import Record from "@/views/inventoryManagement/dispatchLog/Record.vue";
-const activeTab = ref('qualified')
-const type = ref(0)
-const tabs = computed(() => {
-  return [
-    {
-      label: '鍚堟牸鍑哄簱',
-      name: 'qualified',
-      type: '0'
-    },
-    {
-      label: '涓嶅悎鏍煎嚭搴�',
-      name: 'unqualified',
-      type: '1'
-    }
-  ]
-})
+const activeTab = ref(null)
+const tabs = ref([])
+const loading = ref(false)
+
+const resolveTypeByName = (name) => {
+  return String(name || "").includes("涓嶅悎鏍�") ? "1" : "0";
+};
 
 const handleTabChange = (tabName) => {
   activeTab.value = tabName;
-  type.value = tabName === 'qualified' ? 0 : 1
 }
+
+const fetchProducts = async () => {
+  loading.value = true;
+  try {
+    const res = await productTreeList();
+    tabs.value = res
+      .filter((item) => item.parentId === null)
+      .map(({ id, productName }) => ({
+        id,
+        productName,
+        type: resolveTypeByName(productName),
+      }));
+    if (tabs.value.length > 0) {
+      activeTab.value = tabs.value[0].id;
+    }
+  } finally {
+    loading.value = false;
+  }
+}
+
+onMounted(() => {
+  fetchProducts();
+})
 </script>
diff --git a/src/views/inventoryManagement/issueManagement/index.vue b/src/views/inventoryManagement/issueManagement/index.vue
index f5d2ea9..2bba7d0 100644
--- a/src/views/inventoryManagement/issueManagement/index.vue
+++ b/src/views/inventoryManagement/issueManagement/index.vue
@@ -44,7 +44,7 @@
         <el-table-column label="鍏ュ簱浜�" prop="createBy" width="80" show-overflow-tooltip />
         <el-table-column fixed="right" label="鎿嶄綔" min-width="60" align="center">
           <template #default="scope">
-            <el-button link type="primary" size="small" @click="openForm(scope.row);">棰嗙敤</el-button>
+            <el-button link type="primary" @click="openForm(scope.row);">棰嗙敤</el-button>
           </template>
         </el-table-column>
       </el-table>
diff --git a/src/views/inventoryManagement/receiptManagement/Record.vue b/src/views/inventoryManagement/receiptManagement/Record.vue
index 3f90edf..7ca8fea 100644
--- a/src/views/inventoryManagement/receiptManagement/Record.vue
+++ b/src/views/inventoryManagement/receiptManagement/Record.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="app-container">
-    <div class="search_form">
+  <div>
+    <div class="search_form" style="margin-bottom: 10px;">
       <div>
         <span class="search_title ml10">鍏ュ簱鏃ユ湡锛�</span>
         <el-date-picker v-model="searchForm.timeStr"
@@ -31,6 +31,7 @@
         </el-button>
       </div>
       <div>
+        <el-button type="primary" @click="handleBatchApprove">瀹℃壒</el-button>
         <el-button @click="handleOut">瀵煎嚭</el-button>
         <el-button type="danger"
                    plain
@@ -67,6 +68,9 @@
         <el-table-column label="瑙勬牸鍨嬪彿"
                          prop="model"
                          show-overflow-tooltip/>
+        <el-table-column label="鎵瑰彿"
+                         prop="batchNo"
+                         show-overflow-tooltip/>
         <el-table-column label="鍗曚綅"
                          prop="unit"
                          show-overflow-tooltip/>
@@ -81,6 +85,13 @@
                          show-overflow-tooltip>
           <template #default="scope">
             {{ getRecordType(scope.row.recordType) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="瀹℃壒鐘舵��"
+                         prop="approvalStatus"
+                         show-overflow-tooltip>
+          <template #default="scope">
+            {{ getApprovalStatusLabel(scope.row.approvalStatus) }}
           </template>
         </el-table-column>
       </el-table>
@@ -106,7 +117,8 @@
 import {ElMessageBox} from "element-plus";
 import {
   getStockInRecordListPage,
-  batchDeleteStockInRecords,
+  batchDeletePendingStockInRecords,
+  batchApproveStockInRecords,
 } from "@/api/inventoryManagement/stockInRecord.js";
 import {
   findAllQualifiedStockInRecordTypeOptions, findAllUnQualifiedStockInRecordTypeOptions,
@@ -119,6 +131,10 @@
     type: String,
     required: true,
     default: '0'
+  },
+  topParentProductId: {
+    type: [String, Number],
+    default: undefined
   }
 })
 
@@ -152,6 +168,25 @@
   return stockRecordTypeOptions.value.find(item => item.value === recordType)?.label || ''
 }
 
+const approvalStatusLabelMap = {
+  0: "寰呭鎵�",
+  1: "閫氳繃",
+  2: "椹冲洖",
+  pending: "寰呭鎵�",
+  approved: "閫氳繃",
+  rejected: "椹冲洖",
+  PENDING: "寰呭鎵�",
+  APPROVED: "閫氳繃",
+  REJECTED: "椹冲洖",
+};
+
+const getApprovalStatusLabel = (status) => {
+  if (status === null || status === undefined || status === "") {
+    return "寰呭鎵�";
+  }
+  return approvalStatusLabelMap[status] || "寰呭鎵�";
+};
+
 const pageProductChange = obj => {
   page.current = obj.page;
   page.size = obj.limit;
@@ -160,7 +195,7 @@
 
 const getList = () => {
   tableLoading.value = true;
-  const params = {...page, type: props.type};
+  const params = {...page, type: props.type, topParentProductId: props.topParentProductId};
   params.timeStr = searchForm.value.timeStr;
   params.productName = searchForm.value.productName;
   params.recordType = searchForm.value.recordType;
@@ -195,6 +230,44 @@
 
 const expandedRowKeys = ref([]);
 
+const handleBatchApprove = () => {
+  if (selectedRows.value.length === 0) {
+    proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+    return;
+  }
+  const ids = selectedRows.value.map(item => item.id);
+  ElMessageBox.confirm("璇烽�夋嫨瀹℃壒缁撴灉", "瀹℃壒", {
+    confirmButtonText: "閫氳繃",
+    cancelButtonText: "椹冲洖",
+    type: "warning",
+    distinguishCancelAndClose: true,
+  })
+      .then(() => {
+        batchApproveStockInRecords({ids, approvalStatus: 1})
+            .then(() => {
+              proxy.$modal.msgSuccess("瀹℃壒閫氳繃鎴愬姛");
+              getList();
+            })
+            .catch(() => {
+              proxy.$modal.msgError("瀹℃壒閫氳繃澶辫触");
+            });
+      })
+      .catch((action) => {
+        if (action === "cancel") {
+          batchApproveStockInRecords({ids, approvalStatus: 2})
+              .then(() => {
+                proxy.$modal.msgSuccess("瀹℃壒椹冲洖鎴愬姛");
+                getList();
+              })
+              .catch(() => {
+                proxy.$modal.msgError("瀹℃壒椹冲洖澶辫触");
+              });
+          return;
+        }
+        proxy.$modal.msg("宸插彇娑�");
+      });
+};
+
 // 瀵煎嚭
 const handleOut = () => {
   ElMessageBox.confirm("鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
@@ -225,7 +298,7 @@
     type: "warning",
   })
       .then(() => {
-        batchDeleteStockInRecords(ids)
+        batchDeletePendingStockInRecords(ids)
             .then(() => {
               proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
               getList();
@@ -243,6 +316,14 @@
   getList();
   fetchStockRecordTypeOptions();
 });
+
+watch(
+  () => props.topParentProductId,
+  () => {
+    page.current = 1;
+    getList();
+  }
+);
 </script>
 
 <style scoped lang="scss"></style>
diff --git a/src/views/inventoryManagement/receiptManagement/index.vue b/src/views/inventoryManagement/receiptManagement/index.vue
index 8ca110f..17a0823 100644
--- a/src/views/inventoryManagement/receiptManagement/index.vue
+++ b/src/views/inventoryManagement/receiptManagement/index.vue
@@ -1,36 +1,55 @@
 <template>
   <div class="app-container">
-    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
+    <div v-loading="loading" element-loading-text="鍔犺浇涓�..." style="min-height: 80vh;">
+    <el-tabs v-model="activeTab" @tab-change="handleTabChange" v-if="!loading">
       <el-tab-pane v-for="tab in tabs"
-                   :label="tab.label"
-                   :name="tab.name"
-                   :key="tab.name">
-        <record :type="tab.type" v-if="activeTab === tab.name" />
+                   :label="tab.productName"
+                   :name="tab.id"
+                   :key="tab.id">
+        <Record v-bind="{ type: tab.type, topParentProductId: activeTab }" v-if="tab.id === activeTab" />
       </el-tab-pane>
     </el-tabs>
+    </div>
   </div>
 </template>
 
 <script setup>
+import { ref, onMounted } from 'vue';
+import { productTreeList } from "@/api/basicData/product.js";
 import Record from "@/views/inventoryManagement/receiptManagement/Record.vue";
 
-const activeTab = ref('qualified')
-const type = ref(0)
-const tabs = ref([
-  {
-    label: '鍚堟牸鍏ュ簱',
-    name: 'qualified',
-    type: '0'
-  },
-  {
-    label: '涓嶅悎鏍煎叆搴�',
-    name: 'unqualified',
-    type: '1'
-  }
-])
+const activeTab = ref(null)
+const tabs = ref([])
+const loading = ref(false)
+
+const resolveTypeByName = (name) => {
+  return String(name || "").includes("涓嶅悎鏍�") ? "1" : "0";
+};
 
 const handleTabChange = (tabName) => {
   activeTab.value = tabName;
-  type.value = tabName === 'qualified' ? 0 : 1
 }
+
+const fetchProducts = async () => {
+  loading.value = true;
+  try {
+    const res = await productTreeList();
+    tabs.value = res
+      .filter((item) => item.parentId === null)
+      .map(({ id, productName }) => ({
+        id,
+        productName,
+        type: resolveTypeByName(productName),
+      }));
+    if (tabs.value.length > 0) {
+      activeTab.value = tabs.value[0].id;
+    }
+  } finally {
+    loading.value = false;
+  }
+}
+
+onMounted(() => {
+  fetchProducts();
+})
 </script>
diff --git a/src/views/inventoryManagement/stockManagement/FrozenAndThaw.vue b/src/views/inventoryManagement/stockManagement/FrozenAndThaw.vue
index 463cb83..a7a9400 100644
--- a/src/views/inventoryManagement/stockManagement/FrozenAndThaw.vue
+++ b/src/views/inventoryManagement/stockManagement/FrozenAndThaw.vue
@@ -8,10 +8,27 @@
     >
       <el-form label-width="140px" :model="formState" ref="formRef">
         <el-form-item
+            label="搴撳瓨绫诲瀷"
+            prop="type"
+            :rules="[
+                {
+                required: true,
+                message: '璇烽�夋嫨搴撳瓨绫诲瀷',
+                trigger: 'change',
+              }
+            ]"
+        >
+          <el-select v-model="formState.type" placeholder="璇烽�夋嫨搴撳瓨绫诲瀷" @change="handleChangeType">
+            <el-option label="鍚堟牸搴撳瓨" value="qualified" :disabled="(operationType === 'frozen' && props.record.qualifiedUnLockedQuantity <= 0) || (operationType === 'thaw' && props.record.qualifiedLockedQuantity <= 0)" />
+            <el-option label="涓嶅悎鏍煎簱瀛�" value="unqualified" :disabled="(operationType === 'frozen' && props.record.unQualifiedUnLockedQuantity <= 0) || (operationType === 'thaw' && props.record.unQualifiedLockedQuantity <= 0)" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item
             :label="operationType === 'frozen' ? '鍐荤粨鏁伴噺锛�' : '瑙e喕鏁伴噺锛�'"
             prop="lockedQuantity"
         >
-          <el-input-number v-model="formState.lockedQuantity" :step="1" :min="1" precision="0" style="width: 100%" :max="maxCount" />
+          <el-input-number v-model="formState.lockedQuantity" :step="1" :min="maxCount > 0 ? 1 : 0" precision="0" style="width: 100%" :max="maxCount" :disabled="maxCount < 1" />
         </el-form-item>
       </el-form>
 
@@ -26,7 +43,7 @@
 </template>
 
 <script setup>
-import {ref, computed, getCurrentInstance} from "vue";
+import {ref, computed, getCurrentInstance, onMounted} from "vue";
 import {frozenStockInventory, thawStockInventory} from "@/api/inventoryManagement/stockInventory.js";
 import {frozenStockUninventory, thawStockUninventory} from "@/api/inventoryManagement/stockUninventory.js";
 
@@ -42,12 +59,6 @@
     default: 'frozen',
   },
 
-  type: {
-    type: String,
-    required: true,
-    default: 'qualified',
-  },
-
   record: {
     type: Object,
     default: () => {},
@@ -58,7 +69,8 @@
 
 // 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
 const formState = ref({
-  lockedQuantity: 0,
+  type: undefined,
+  lockedQuantity: undefined,
 });
 
 const isShow = computed({
@@ -76,7 +88,8 @@
 const closeModal = () => {
   // 閲嶇疆琛ㄥ崟鏁版嵁
   formState.value = {
-    lockedQuantity: undefined
+    lockedQuantity: undefined,
+    type: undefined,
   };
   isShow.value = false;
 };
@@ -84,17 +97,32 @@
 const maxCount = computed(() => {
   // 鍐荤粨搴撳瓨鏈�澶ф暟閲忎负鏈В鍐绘暟閲�
   if (props.operationType === 'frozen') {
-    return props.record.unLockedQuantity
+    // 鍐荤粨鍚堟牸搴撳瓨鏈�澶ф暟閲忎负鏈В鍐诲悎鏍兼暟閲�
+    if (formState.value.type === 'qualified') {
+      return Math.max(0, props.record.qualifiedUnLockedQuantity || 0)
+    }
+    // 鍐荤粨涓嶅悎鏍煎簱瀛樻渶澶ф暟閲忎负鏈В鍐讳笉鍚堟牸鏁伴噺
+    return Math.max(0, props.record.unQualifiedUnLockedQuantity || 0)
   }
   // 瑙e喕搴撳瓨鏈�澶ф暟閲忎负宸插喕缁撴暟閲�
-  return props.record.lockedQuantity
+  if (formState.value.type === 'qualified') {
+    // 瑙e喕鍚堟牸搴撳瓨鏈�澶ф暟閲忎负宸插喕缁撳悎鏍兼暟閲�
+    return Math.max(0, props.record.qualifiedLockedQuantity || 0)
+  }
+  // 瑙e喕涓嶅悎鏍煎簱瀛樻渶澶ф暟閲忎负宸插喕缁撲笉鍚堟牸鏁伴噺
+  return Math.max(0, props.record.unQualifiedLockedQuantity || 0)
 })
+
+const handleChangeType = (type) => {
+  formState.value.lockedQuantity = maxCount.value;
+}
 
 const handleSubmit = () => {
   proxy.$refs["formRef"].validate(valid => {
     if (valid) {
-      const data = Object.assign({id: props.record.id}, formState.value);
-      if (props.type === 'qualified') {
+      const data = Object.assign({}, formState.value);
+      if (formState.value.type === 'qualified') {
+        data.id = props.record.qualifiedId;
         // 鍐荤粨
         if (props.operationType === 'frozen') {
           frozenStockInventory(data).then(res => {
@@ -122,6 +150,7 @@
           })
         }
       } else {
+        data.id = props.record.unQualifiedId;
         if (props.operationType === 'frozen') {
           frozenStockUninventory(data).then(res => {
             if (res.code === 200) {
@@ -153,7 +182,6 @@
 };
 
 onMounted(() => {
-  formState.value.lockedQuantity = maxCount.value;
 })
 
 defineExpose({
@@ -161,4 +189,4 @@
   handleSubmit,
   isShow,
 });
-</script>
+</script>
\ No newline at end of file
diff --git a/src/views/inventoryManagement/stockManagement/New.vue b/src/views/inventoryManagement/stockManagement/New.vue
index 1f86fd6..2addb95 100644
--- a/src/views/inventoryManagement/stockManagement/New.vue
+++ b/src/views/inventoryManagement/stockManagement/New.vue
@@ -1,71 +1,90 @@
 <template>
   <div>
-    <el-dialog
-        v-model="isShow"
-        title="鏂板搴撳瓨"
-        width="800"
-        @close="closeModal"
-    >
-      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
-        <el-form-item
-            label="浜у搧鍚嶇О"
-            prop="productModelId"
-            :rules="[
+    <el-dialog v-model="isShow"
+               title="鏂板搴撳瓨"
+               width="800"
+               @close="closeModal">
+      <el-form label-width="140px"
+               :model="formState"
+               label-position="top"
+               ref="formRef">
+        <el-form-item label="浜у搧鍚嶇О"
+                      prop="productModelId"
+                      :rules="[
                 {
                 required: true,
                 message: '璇烽�夋嫨浜у搧',
                 trigger: 'change',
               }
-            ]"
-        >
-          <el-button type="primary" @click="showProductSelectDialog = true">
+            ]">
+          <el-button type="primary"
+                     @click="showProductSelectDialog = true">
             {{ formState.productName ? formState.productName : '閫夋嫨浜у搧' }}
           </el-button>
         </el-form-item>
-
-        <el-form-item
-            label="瑙勬牸"
-            prop="productModelName"
-        >
-          <el-input v-model="formState.productModelName"  disabled />
+        <el-form-item label="瑙勬牸"
+                      prop="productModelName">
+          <el-input v-model="formState.productModelName"
+                    disabled />
         </el-form-item>
-
-        <el-form-item
-            label="鍗曚綅"
-            prop="unit"
-        >
-          <el-input v-model="formState.unit"  disabled />
+        <el-form-item label="鍗曚綅"
+                      prop="unit">
+          <el-input v-model="formState.unit"
+                    disabled />
         </el-form-item>
-
-        <el-form-item
-            label="搴撳瓨鏁伴噺"
-            prop="qualitity"
-        >
-          <el-input-number v-model="formState.qualitity" :step="1" :min="1" style="width: 100%" />
+        <el-form-item label="搴撳瓨绫诲瀷"
+                      prop="type"
+                      :rules="[
+                {
+                required: true,
+                message: '璇烽�夋嫨搴撳瓨绫诲瀷',
+                trigger: 'change',
+              }
+            ]">
+          <el-select v-model="formState.type"
+                     placeholder="璇烽�夋嫨搴撳瓨绫诲瀷">
+            <el-option label="鍚堟牸搴撳瓨"
+                       value="qualified" />
+            <el-option label="涓嶅悎鏍煎簱瀛�"
+                       value="unqualified" />
+          </el-select>
         </el-form-item>
-
-        <el-form-item
-            v-if="type === 'qualified'"
-            label="搴撳瓨棰勮鏁伴噺"
-            prop="warnNum"
-        >
-          <el-input-number v-model="formState.warnNum" :step="1" :min="0" :max="formState.qualitity" style="width: 100%" />
+        <el-form-item label="搴撳瓨鏁伴噺"
+                      prop="qualitity">
+          <el-input-number v-model="formState.qualitity"
+                           :step="1"
+                           :min="1"
+                           style="width: 100%" />
         </el-form-item>
-
-        <el-form-item label="澶囨敞" prop="remark">
-          <el-input v-model="formState.remark" type="textarea" />
+        <el-form-item label="鎵瑰彿"
+                      prop="batchNo">
+          <el-input v-model="formState.batchNo"
+                    placeholder="璇疯緭鍏ユ壒鍙�" />
+        </el-form-item>
+        <el-form-item v-if="formState.type === 'qualified'"
+                      label="搴撳瓨棰勮鏁伴噺"
+                      prop="warnNum">
+          <el-input-number v-model="formState.warnNum"
+                           :step="1"
+                           :min="0"
+                           :max="formState.qualitity"
+                           style="width: 100%" />
+        </el-form-item>
+        <el-form-item label="澶囨敞"
+                      prop="remark">
+          <el-input v-model="formState.remark"
+                    type="textarea" />
         </el-form-item>
       </el-form>
-
       <!-- 浜у搧閫夋嫨寮圭獥 -->
-      <ProductSelectDialog
-          v-model="showProductSelectDialog"
-          @confirm="handleProductSelect"
-          single
-      />
+      <ProductSelectDialog v-model="showProductSelectDialog"
+                           @confirm="handleProductSelect"
+                           :top-product-parent-id="props.topProductParentId"
+                           single />
       <template #footer>
         <div class="dialog-footer">
-          <el-button type="primary" @click="handleSubmit">纭</el-button>
+          <el-button type="primary"
+                     @click="handleSubmit">纭</el-button>
           <el-button @click="closeModal">鍙栨秷</el-button>
         </div>
       </template>
@@ -74,116 +93,131 @@
 </template>
 
 <script setup>
-import {ref, computed, getCurrentInstance} from "vue";
-import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
-import {createStockInventory} from "@/api/inventoryManagement/stockInventory.js";
-import {createStockUnInventory} from "@/api/inventoryManagement/stockUninventory.js";
+  import { ref, computed, watch, getCurrentInstance } from "vue";
+  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+  import { addStockInRecordOnly } from "@/api/inventoryManagement/stockInventory.js";
+  import { createStockUnInventory } from "@/api/inventoryManagement/stockUninventory.js";
 
-const props = defineProps({
-  visible: {
-    type: Boolean,
-    required: true,
-  },
+  const props = defineProps({
+    visible: {
+      type: Boolean,
+      required: true,
+    },
+    topProductParentId: {
+      type: Number,
+      default: undefined,
+      required: false,
+    },
+  });
 
-  type: {
-    type: String,
-    required: true,
-    default: 'qualified',
-  },
-});
+  const emit = defineEmits(["update:visible", "completed"]);
 
-const emit = defineEmits(['update:visible', 'completed']);
-
-// 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
-const formState = ref({
-  productId: undefined,
-  productModelId: undefined,
-  productName: "",
-  productModelName: "",
-  unit: "",
-  qualitity: 0,
-  warnNum: 0,
-  remark: '',
-});
-
-const isShow = computed({
-  get() {
-    return props.visible;
-  },
-  set(val) {
-    emit('update:visible', val);
-  },
-});
-
-const showProductSelectDialog = ref(false);
-
-let { proxy } = getCurrentInstance()
-
-const closeModal = () => {
-  // 閲嶇疆琛ㄥ崟鏁版嵁
-  formState.value = {
+  // 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
+  const formState = ref({
     productId: undefined,
     productModelId: undefined,
     productName: "",
     productModelName: "",
-    description: '',
-  };
-  isShow.value = false;
-};
+    unit: "",
+    type: undefined,
+    qualitity: 0,
+    batchNo: null,
+    warnNum: 0,
+    remark: "",
+  });
 
-// 浜у搧閫夋嫨澶勭悊
-const handleProductSelect = async (products) => {
-  if (products && products.length > 0) {
-    const product = products[0];
-    formState.value.productId = product.productId;
-    formState.value.productName = product.productName;
-    formState.value.productModelName = product.model;
-    formState.value.productModelId = product.id;
-    formState.value.unit = product.unit;
-    showProductSelectDialog.value = false;
-    // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
-    proxy.$refs["formRef"]?.validateField('productModelId');
-  }
-};
+  const isShow = computed({
+    get() {
+      return props.visible;
+    },
+    set(val) {
+      emit("update:visible", val);
+    },
+  });
 
-const handleSubmit = () => {
-  proxy.$refs["formRef"].validate(valid => {
-    if (valid) {
-      // 楠岃瘉鏄惁閫夋嫨浜嗕骇鍝佸拰瑙勬牸
-      if (!formState.value.productModelId) {
-        proxy.$modal.msgError("璇烽�夋嫨浜у搧");
-        return;
-      }
-      if (!formState.value.productModelId) {
-        proxy.$modal.msgError("璇烽�夋嫨瑙勬牸");
-        return;
-      }
-      if (props.type === 'qualified') {
-        createStockInventory(formState.value).then(res => {
-          // 鍏抽棴妯℃�佹
-          isShow.value = false;
-          // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
-          emit('completed');
-          proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-        })
-      } else {
-        createStockUnInventory(formState.value).then(res => {
-          // 鍏抽棴妯℃�佹
-          isShow.value = false;
-          // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
-          emit('completed');
-          proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-        })
-      }
+  const showProductSelectDialog = ref(false);
 
+  // 鎵瑰彿涓虹┖鏃惰浆涓� null
+  watch(
+    () => formState.value.batchNo,
+    val => {
+      if (val === "") {
+        formState.value.batchNo = null;
+      }
     }
-  })
-};
+  );
 
+  let { proxy } = getCurrentInstance();
 
-defineExpose({
-  closeModal,
-  handleSubmit,
-  isShow,
-});
+  const closeModal = () => {
+    // 閲嶇疆琛ㄥ崟鏁版嵁
+    formState.value = {
+      productId: undefined,
+      productModelId: undefined,
+      productName: "",
+      productModelName: "",
+      unit: "",
+      type: undefined,
+      qualitity: 0,
+      batchNo: null,
+      warnNum: 0,
+      remark: "",
+    };
+    isShow.value = false;
+  };
+
+  // 浜у搧閫夋嫨澶勭悊
+  const handleProductSelect = async products => {
+    if (products && products.length > 0) {
+      const product = products[0];
+      formState.value.productId = product.productId;
+      formState.value.productName = product.productName;
+      formState.value.productModelName = product.model;
+      formState.value.productModelId = product.id;
+      formState.value.unit = product.unit;
+      showProductSelectDialog.value = false;
+      // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
+      proxy.$refs["formRef"]?.validateField("productModelId");
+    }
+  };
+
+  const handleSubmit = () => {
+    proxy.$refs["formRef"].validate(valid => {
+      if (valid) {
+        // 楠岃瘉鏄惁閫夋嫨浜嗕骇鍝佸拰瑙勬牸
+        if (!formState.value.productModelId) {
+          proxy.$modal.msgError("璇烽�夋嫨浜у搧");
+          return;
+        }
+        if (!formState.value.productModelId) {
+          proxy.$modal.msgError("璇烽�夋嫨瑙勬牸");
+          return;
+        }
+        if (formState.value.type === "qualified") {
+          addStockInRecordOnly(formState.value).then(res => {
+            // 鍏抽棴妯℃�佹
+            isShow.value = false;
+            // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
+            emit("completed");
+            proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+          });
+        } else {
+          formState.value.warnNum = 0;
+          createStockUnInventory(formState.value).then(res => {
+            // 鍏抽棴妯℃�佹
+            isShow.value = false;
+            // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
+            emit("completed");
+            proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+          });
+        }
+      }
+    });
+  };
+
+  defineExpose({
+    closeModal,
+    handleSubmit,
+    isShow,
+  });
 </script>
diff --git a/src/views/inventoryManagement/stockManagement/Qualified.vue b/src/views/inventoryManagement/stockManagement/Qualified.vue
index 8b15db1..4286772 100644
--- a/src/views/inventoryManagement/stockManagement/Qualified.vue
+++ b/src/views/inventoryManagement/stockManagement/Qualified.vue
@@ -25,17 +25,18 @@
         <el-table-column align="center" label="搴忓彿" type="index" width="60" />
         <el-table-column label="浜у搧澶х被" prop="productName" show-overflow-tooltip />
         <el-table-column label="瑙勬牸鍨嬪彿" prop="model" show-overflow-tooltip />
+        <el-table-column label="鎵瑰彿" prop="batchNo" show-overflow-tooltip />
         <el-table-column label="鍗曚綅" prop="unit" show-overflow-tooltip />
         <el-table-column label="搴撳瓨鏁伴噺" prop="qualitity" show-overflow-tooltip />
         <el-table-column label="鍐荤粨鏁伴噺" prop="lockedQuantity" show-overflow-tooltip />
         <el-table-column label="搴撳瓨棰勮鏁伴噺" prop="warnNum"  show-overflow-tooltip />
         <el-table-column label="澶囨敞" prop="remark"  show-overflow-tooltip />
         <el-table-column label="鏈�杩戞洿鏂版椂闂�" prop="updateTime" show-overflow-tooltip />
-        <el-table-column fixed="right" label="鎿嶄綔" min-width="60" align="center">
+        <el-table-column fixed="right" label="鎿嶄綔" min-width="90" align="center">
           <template #default="scope">
-            <el-button link type="primary" size="small" @click="showSubtractModal(scope.row)" :disabled="scope.row.unLockedQuantity === 0">棰嗙敤</el-button>
-            <el-button link type="primary" size="small" v-if="scope.row.unLockedQuantity > 0" @click="showFrozenModal(scope.row)">鍐荤粨</el-button>
-            <el-button link type="primary" size="small" v-if="scope.row.lockedQuantity > 0" @click="showThawModal(scope.row)">瑙e喕</el-button>
+            <el-button link type="primary" @click="showSubtractModal(scope.row)" :disabled="scope.row.unLockedQuantity === 0">棰嗙敤</el-button>
+            <el-button link type="primary" v-if="scope.row.unLockedQuantity > 0" @click="showFrozenModal(scope.row)">鍐荤粨</el-button>
+            <el-button link type="primary" v-if="scope.row.lockedQuantity > 0" @click="showThawModal(scope.row)">瑙e喕</el-button>
           </template>
         </el-table-column>
       </el-table>
diff --git a/src/views/inventoryManagement/stockManagement/Record.vue b/src/views/inventoryManagement/stockManagement/Record.vue
new file mode 100644
index 0000000..7c0a461
--- /dev/null
+++ b/src/views/inventoryManagement/stockManagement/Record.vue
@@ -0,0 +1,223 @@
+<template>
+  <div>
+    <div class="search_form mb10">
+      <div>
+        <span class="search_title ml10">浜у搧澶х被锛�</span>
+        <el-input v-model="searchForm.productName"
+                  style="width: 240px"
+                  placeholder="璇疯緭鍏�"
+                  clearable/>
+        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">鎼滅储</el-button>
+      </div>
+      <div>
+         <el-button type="primary" @click="isShowNewModal = true">鏂板搴撳瓨</el-button>
+        <el-button type="info" plain icon="Upload" @click="isShowImportModal = true">
+          瀵煎叆搴撳瓨
+        </el-button>
+        <el-button @click="handleOut">瀵煎嚭</el-button>
+      </div>
+    </div>
+    <div class="table_list">
+      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
+        :expand-row-keys="expandedRowKeys" :row-key="(row, index) => index" style="width: 100%"
+        :row-class-name="tableRowClassName" height="calc(100vh - 18.5em)">
+        <el-table-column align="center" type="selection" width="55" />
+        <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+        <el-table-column label="浜у搧澶х被" prop="productName" show-overflow-tooltip />
+        <el-table-column label="瑙勬牸鍨嬪彿" prop="model" show-overflow-tooltip />
+        <el-table-column label="鍗曚綅" prop="unit" show-overflow-tooltip />
+        <el-table-column label="鎵瑰彿" prop="batchNo" show-overflow-tooltip />
+        <el-table-column label="鍚堟牸搴撳瓨鏁伴噺" prop="qualifiedQuantity" show-overflow-tooltip />
+        <el-table-column label="涓嶅悎鏍煎簱瀛樻暟閲�" prop="unQualifiedQuantity" show-overflow-tooltip />
+        <el-table-column label="鍚堟牸鍐荤粨鏁伴噺" prop="qualifiedLockedQuantity" show-overflow-tooltip />
+        <el-table-column label="涓嶅悎鏍煎喕缁撴暟閲�" prop="unQualifiedLockedQuantity" show-overflow-tooltip />
+        <el-table-column label="搴撳瓨棰勮鏁伴噺" prop="warnNum"  show-overflow-tooltip />
+        <el-table-column label="澶囨敞" prop="remark"  show-overflow-tooltip />
+        <el-table-column label="鏈�杩戞洿鏂版椂闂�" prop="updateTime" show-overflow-tooltip />
+        <el-table-column fixed="right" label="鎿嶄綔" min-width="90" align="center">
+          <template #default="scope">
+            <el-button link type="primary" @click="showSubtractModal(scope.row)" :disabled="((scope.row.qualifiedUnLockedQuantity || 0) + (scope.row.qualifiedPendingOutQuantity || 0) <= 0) && ((scope.row.unQualifiedUnLockedQuantity || 0) + (scope.row.unQualifiedPendingOutQuantity || 0) <= 0)">棰嗙敤</el-button>
+            <el-button link type="primary" v-if="scope.row.unQualifiedUnLockedQuantity > 0 || scope.row.qualifiedUnLockedQuantity > 0" @click="showFrozenModal(scope.row)">鍐荤粨</el-button>
+            <el-button link type="primary" v-if="scope.row.qualifiedLockedQuantity > 0 || scope.row.unQualifiedLockedQuantity > 0" @click="showThawModal(scope.row)">瑙e喕</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
+        :page="page.current" :limit="page.size" @pagination="paginationChange" />
+    </div>
+    <new-stock-inventory v-if="isShowNewModal"
+                 v-model:visible="isShowNewModal"
+                 :top-product-parent-id="props.productId"
+                 @completed="handleQuery" />
+
+    <subtract-stock-inventory v-if="isShowSubtractModal"
+                 v-model:visible="isShowSubtractModal"
+                 :record="record"
+                 :type="record.stockType"
+                 @completed="handleQuery" />
+    <!-- 瀵煎叆搴撳瓨-->
+    <import-stock-inventory v-if="isShowImportModal"
+                 v-model:visible="isShowImportModal"
+                 type="qualified"
+                 @uploadSuccess="handleQuery" />
+    <!-- 鍐荤粨/瑙e喕搴撳瓨-->
+    <frozen-and-thaw-stock-inventory v-if="isShowFrozenAndThawModal"
+                 v-model:visible="isShowFrozenAndThawModal"
+                 :record="record"
+                 :operation-type="operationType"
+                 :type="record.stockType"
+                 @completed="handleQuery" />
+  </div>
+</template>
+
+<script setup>
+import pagination from '@/components/PIMTable/Pagination.vue'
+import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue'
+import {ElMessage, ElMessageBox} from "element-plus";
+import {
+  getStockInventoryListPageCombined
+} from "@/api/inventoryManagement/stockInventory.js";
+const props = defineProps({
+  productId: {
+    type: Number,
+    required: true,
+    default: 0
+  }
+});
+
+const NewStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/New.vue"));
+const SubtractStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/Subtract.vue"));
+const ImportStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/Import.vue"));
+const FrozenAndThawStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/FrozenAndThaw.vue"));
+const { proxy } = getCurrentInstance()
+const tableData = ref([])
+const selectedRows = ref([])
+const record = ref({})
+const tableLoading = ref(false)
+const page = reactive({
+  current: 1,
+  size: 100,
+})
+const total = ref(0)
+// 鏄惁鏄剧ず鏂板寮规
+const isShowNewModal = ref(false)
+// 鏄惁鏄剧ず棰嗙敤寮规
+const isShowSubtractModal = ref(false)
+// 鏄惁鏄剧ず鍐荤粨/瑙e喕寮规
+const isShowFrozenAndThawModal = ref(false)
+// 鎿嶄綔绫诲瀷
+const operationType = ref('frozen')
+// 鏄惁鏄剧ず瀵煎叆寮规
+const isShowImportModal = ref(false)
+const data = reactive({
+  searchForm: {
+    productName: '',
+    topParentProductId: props.productId,
+  }
+})
+const { searchForm } = toRefs(data)
+
+// 鏌ヨ鍒楄〃
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  page.current = 1
+  getList()
+}
+const paginationChange = (obj) => {
+  page.current = obj.page;
+  page.size = obj.limit;
+  getList()
+}
+const getList = () => {
+  tableLoading.value = true
+  getStockInventoryListPageCombined({ ...searchForm.value, ...page }).then(res => {
+    tableLoading.value = false
+    tableData.value = res.data.records
+    total.value = res.data.total
+    // 鏁版嵁鍔犺浇瀹屾垚鍚庢鏌ュ簱瀛�
+    // checkStockAndCreatePurchase();
+  }).catch(() => {
+    tableLoading.value = false
+  })
+}
+
+const handleFileSuccess = (response) => {
+  const { code, msg } = response;
+  if (code == 200) {
+    ElMessage({ message: "瀵煎叆鎴愬姛", type: "success" });
+    upload.open = false;
+    emits("uploadSuccess");
+  } else {
+    ElMessage({ message: msg, type: "error" });
+  }
+};
+
+// 鐐瑰嚮棰嗙敤
+const showSubtractModal = (row) => {
+  record.value = row
+  isShowSubtractModal.value = true
+}
+
+// 鐐瑰嚮鍐荤粨
+const showFrozenModal = (row) => {
+  record.value = row
+  isShowFrozenAndThawModal.value = true
+  operationType.value = 'frozen'
+}
+
+// 鐐瑰嚮瑙e喕
+const showThawModal = (row) => {
+  record.value = row
+  isShowFrozenAndThawModal.value = true
+  operationType.value = 'thaw'
+}
+
+// 琛ㄦ牸閫夋嫨鏁版嵁
+const handleSelectionChange = (selection) => {
+  // 杩囨护鎺夊瓙鏁版嵁
+  selectedRows.value = selection.filter(item => item.id);
+  console.log('selection', selectedRows.value)
+}
+const expandedRowKeys = ref([])
+
+// 琛ㄦ牸琛岀被鍚�
+const tableRowClassName = ({ row }) => {
+  const stock = Number(row?.qualifiedUnLockedQuantity ?? 0);
+  const warn = Number(row?.warnNum ?? 0);
+  if (!Number.isFinite(stock) || !Number.isFinite(warn)) {
+    return '';
+  }
+  return stock < warn ? 'row-low-stock' : '';
+};
+
+// 瀵煎嚭
+const handleOut = () => {
+  ElMessageBox.confirm(
+    '鏄惁纭瀵煎嚭锛�',
+    '瀵煎嚭', {
+    confirmButtonText: '纭',
+    cancelButtonText: '鍙栨秷',
+    type: 'warning',
+  }
+  ).then(() => {
+    proxy.download("/stockInventory/exportStockInventory", {topParentProductId: props.productId}, '搴撳瓨淇℃伅.xlsx')
+  }).catch(() => {
+    proxy.$modal.msg("宸插彇娑�")
+  })
+}
+
+onMounted(() => {
+  getList()
+})
+</script>
+
+<style scoped lang="scss">
+:deep(.row-low-stock td) {
+  background-color: #fde2e2;
+  color: #c45656;
+}
+
+:deep(.row-low-stock:hover > td) {
+  background-color: #fcd4d4;
+}
+</style>
diff --git a/src/views/inventoryManagement/stockManagement/Subtract.vue b/src/views/inventoryManagement/stockManagement/Subtract.vue
index a277a00..2cdfcb6 100644
--- a/src/views/inventoryManagement/stockManagement/Subtract.vue
+++ b/src/views/inventoryManagement/stockManagement/Subtract.vue
@@ -38,6 +38,23 @@
         </el-form-item>
 
         <el-form-item
+            label="搴撳瓨绫诲瀷"
+            prop="type"
+            :rules="[
+                {
+                required: true,
+                message: '璇烽�夋嫨搴撳瓨绫诲瀷',
+                trigger: 'change',
+              }
+            ]"
+        >
+          <el-select v-model="formState.type" placeholder="璇烽�夋嫨搴撳瓨绫诲瀷" @change="handleTypeChange">
+            <el-option label="鍚堟牸搴撳瓨" value="qualified" :disabled="(props.record.qualifiedUnLockedQuantity || 0) + (props.record.qualifiedPendingOutQuantity || 0) <= 0" />
+            <el-option label="涓嶅悎鏍煎簱瀛�" value="unqualified" :disabled="(props.record.unQualifiedUnLockedQuantity || 0) + (props.record.unQualifiedPendingOutQuantity || 0) <= 0" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item
             label="鏁伴噺"
             prop="qualitity"
         >
@@ -68,8 +85,8 @@
 <script setup>
 import {ref, computed, getCurrentInstance} from "vue";
 import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
-import {subtractStockInventory} from "@/api/inventoryManagement/stockInventory.js";
-import {subtractStockUnInventory} from "@/api/inventoryManagement/stockUninventory.js";
+import {addStockOutRecordOnly} from "@/api/inventoryManagement/stockInventory.js";
+import {addUnqualifiedStockOutRecordOnly} from "@/api/inventoryManagement/stockUninventory.js";
 
 const props = defineProps({
   visible: {
@@ -79,12 +96,7 @@
   record: {
     type: Object,
     default: () => {},
-  },
-  type: {
-    type: String,
-    required: true,
-    default: 'qualified',
-  },
+  }
 });
 
 const emit = defineEmits(['update:visible', 'completed']);
@@ -94,8 +106,17 @@
 })
 
 const maxQuality = computed(() => {
-  return props.record.unLockedQuantity ? props.record.unLockedQuantity :  0;
+  if (formState.value.type === 'qualified') {
+    // 鍚堟牸鍙嚭 = 鏈喕缁撻噺 + 寰呭鏍稿嚭搴撻噺锛堝嵆宸茬敵璇蜂絾灏氭湭瀹℃壒鐨勬暟閲忥級
+    return Math.max(1, Number(props.record.qualifiedUnLockedQuantity || 0) + Number(props.record.qualifiedPendingOutQuantity || 0));
+  } else {
+    return Math.max(1, Number(props.record.unQualifiedUnLockedQuantity || 0) + Number(props.record.unQualifiedPendingOutQuantity || 0));
+  }
 })
+
+const handleTypeChange = () => {
+  formState.value.qualitity = undefined;
+}
 
 const initFormData = () => {
   if (props.record) {
@@ -145,7 +166,6 @@
 const handleProductSelect = async (products) => {
   if (products && products.length > 0) {
     const product = products[0];
-    console.log(product)
     formState.value.productId = product.productId;
     formState.value.productName = product.productName;
     formState.value.productModelName = product.model;
@@ -169,8 +189,8 @@
         proxy.$modal.msgError("璇烽�夋嫨瑙勬牸");
         return;
       }
-      if (props.type === 'qualified') {
-        subtractStockInventory(formState.value).then(res => {
+      if (formState.value.type === 'qualified') {
+        addStockOutRecordOnly(formState.value).then(res => {
           // 鍏抽棴妯℃�佹
           isShow.value = false;
           // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
@@ -178,7 +198,7 @@
           proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
         })
       } else {
-        subtractStockUnInventory(formState.value).then(res => {
+        addUnqualifiedStockOutRecordOnly(formState.value).then(res => {
           // 鍏抽棴妯℃�佹
           isShow.value = false;
           // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
@@ -196,4 +216,4 @@
   handleSubmit,
   isShow,
 });
-</script>
+</script>
\ No newline at end of file
diff --git a/src/views/inventoryManagement/stockManagement/Unqualified.vue b/src/views/inventoryManagement/stockManagement/Unqualified.vue
index 9b5652d..55662be 100644
--- a/src/views/inventoryManagement/stockManagement/Unqualified.vue
+++ b/src/views/inventoryManagement/stockManagement/Unqualified.vue
@@ -27,11 +27,11 @@
         <el-table-column label="鍐荤粨鏁伴噺" prop="lockedQuantity" show-overflow-tooltip />
         <el-table-column label="澶囨敞" prop="remark"  show-overflow-tooltip />
         <el-table-column label="鏈�杩戞洿鏂版椂闂�" prop="updateTime" show-overflow-tooltip />
-        <el-table-column fixed="right" label="鎿嶄綔" min-width="60" align="center">
+        <el-table-column fixed="right" label="鎿嶄綔" min-width="90" align="center">
           <template #default="scope">
-            <el-button link type="primary" size="small" @click="showSubtractModal(scope.row)" :disabled="scope.row.unLockedQuantity === 0">棰嗙敤</el-button>
-            <el-button link type="primary" size="small" v-if="scope.row.unLockedQuantity > 0" @click="showFrozenModal(scope.row)">鍐荤粨</el-button>
-            <el-button link type="primary" size="small" v-if="scope.row.lockedQuantity > 0" @click="showThawModal(scope.row)">瑙e喕</el-button>
+            <el-button link type="primary" @click="showSubtractModal(scope.row)" :disabled="scope.row.unLockedQuantity === 0">棰嗙敤</el-button>
+            <el-button link type="primary" v-if="scope.row.unLockedQuantity > 0" @click="showFrozenModal(scope.row)">鍐荤粨</el-button>
+            <el-button link type="primary" v-if="scope.row.lockedQuantity > 0" @click="showThawModal(scope.row)">瑙e喕</el-button>
           </template>
         </el-table-column>
       </el-table>
diff --git a/src/views/inventoryManagement/stockManagement/index.vue b/src/views/inventoryManagement/stockManagement/index.vue
index 347de38..b3aa7ee 100644
--- a/src/views/inventoryManagement/stockManagement/index.vue
+++ b/src/views/inventoryManagement/stockManagement/index.vue
@@ -1,33 +1,44 @@
 <template>
   <div class="app-container">
-    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
-      <el-tab-pane v-for="tab in tabs"
-                   :label="tab.label"
-                   :name="tab.name"
-                   :key="tab.name">
-        <component :is="tab.name === 'qualified' ? QualifiedRecord : UnqualifiedRecord" />
-      </el-tab-pane>
-    </el-tabs>
+    <div v-loading="loading" element-loading-text="鍔犺浇涓�..." style="min-height: 80vh;">
+      <el-tabs v-model="activeTab" @tab-change="handleTabChange" v-if="!loading">
+        <el-tab-pane v-for="tab in products"
+                     :label="tab.productName"
+                     :name="tab.id"
+                     :key="tab.id">
+          <Record :product-id="tab.id" v-if="tab.id === activeTab" />
+        </el-tab-pane>
+      </el-tabs>
+    </div>
   </div>
 </template>
 
 <script setup>
-import QualifiedRecord from "@/views/inventoryManagement/stockManagement/Qualified.vue";
-import UnqualifiedRecord from "@/views/inventoryManagement/stockManagement/Unqualified.vue";
-
-const activeTab = ref('qualified')
-const tabs = ref([
-  {
-    label: '鍚堟牸搴撳瓨',
-    name: 'qualified'
-  },
-  {
-    label: '涓嶅悎鏍煎簱瀛�',
-    name: 'unqualified'
-  }
-])
+import { ref, onMounted } from 'vue';
+import { productTreeList } from "@/api/basicData/product.js";
+import Record from "@/views/inventoryManagement/stockManagement/Record.vue";
+const products = ref([])
+const activeTab = ref(null)
+const loading = ref(false)
 
 const handleTabChange = (tabName) => {
   activeTab.value = tabName;
 }
-</script>
+
+const fetchProducts = async () => {
+  loading.value = true;
+  try {
+    const res = await productTreeList();
+    products.value = res.filter((item) => item.parentId === null).map(({ id, productName }) => ({ id, productName }));
+    if (products.value.length > 0) {
+      activeTab.value = products.value[0].id;
+    }
+  } finally {
+    loading.value = false;
+  }
+}
+
+onMounted(() => {
+  fetchProducts();
+})
+</script>
\ No newline at end of file
diff --git a/src/views/inventoryManagement/stockWarning/index.vue b/src/views/inventoryManagement/stockWarning/index.vue
index d0d5834..b5216cb 100644
--- a/src/views/inventoryManagement/stockWarning/index.vue
+++ b/src/views/inventoryManagement/stockWarning/index.vue
@@ -119,9 +119,9 @@
         <!-- 鎿嶄綔鍒� -->
         <el-table-column fixed="right" label="鎿嶄綔" width="200" align="center">
           <template #default="scope">
-            <el-button link type="primary" size="small" @click="handleEdit(scope.row)">缂栬緫</el-button>
+            <el-button link type="primary" @click="handleEdit(scope.row)">缂栬緫</el-button>
 <!--            <el-button link type="success" size="small" @click="handleProcess(scope.row)">澶勭悊@</el-button>-->
-            <el-button link type="danger" size="small" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+            <el-button link type="danger" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
           </template>
         </el-table-column>
       </el-table>
diff --git a/src/views/inventoryManagement/transportTaskManagement/index.vue b/src/views/inventoryManagement/transportTaskManagement/index.vue
index 1feb54b..8e73004 100644
--- a/src/views/inventoryManagement/transportTaskManagement/index.vue
+++ b/src/views/inventoryManagement/transportTaskManagement/index.vue
@@ -681,11 +681,11 @@
   text-align: right;
 }
 
-::v-deep(.row-finished) {
+:deep(.row-finished) {
   background-color: #f6ffed;
 }
 
-::v-deep(.row-running) {
+:deep(.row-running) {
   background-color: #fffbe6;
 }
 </style>
diff --git a/src/views/inventoryManagement/vehicleFuelManagement/index.vue b/src/views/inventoryManagement/vehicleFuelManagement/index.vue
index 8579cba..eaf543c 100644
--- a/src/views/inventoryManagement/vehicleFuelManagement/index.vue
+++ b/src/views/inventoryManagement/vehicleFuelManagement/index.vue
@@ -549,7 +549,7 @@
   text-align: right;
 }
 
-::v-deep(.row-abnormal) {
+:deep(.row-abnormal) {
   background-color: #fff5f5;
 }
 </style>
diff --git a/src/views/lavorissue/statistics/index.vue b/src/views/lavorissue/statistics/index.vue
index 2c34f67..54e1f38 100644
--- a/src/views/lavorissue/statistics/index.vue
+++ b/src/views/lavorissue/statistics/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form mb20">
       <div>
         <span class="search_title">鍙戞斁瀛e害锛�</span>
         <el-select
diff --git a/src/views/login.vue b/src/views/login.vue
index 6217877..872537b 100644
--- a/src/views/login.vue
+++ b/src/views/login.vue
@@ -1,232 +1,401 @@
-<template>
-  <div class="login">
-    <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
-      <h3 class="title">{{ title }}</h3>
-      <el-divider />
-      <el-form-item prop="username">
-        <el-input
-          v-model="loginForm.username"
-          type="text"
-          size="large"
-          auto-complete="off"
-          placeholder="璐﹀彿"
-        >
-          <template #prefix><el-icon><User /></el-icon></template>
-        </el-input>
-      </el-form-item>
-      <el-form-item prop="password">
-        <el-input
-          v-model="loginForm.password"
-          type="password"
-          size="large"
-          auto-complete="off"
-          placeholder="瀵嗙爜"
-          show-password
-          @keyup.enter="handleLogin"
-        >
-          <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
-        </el-input>
-      </el-form-item>
-<!--      <el-form-item prop="code" v-if="captchaEnabled">-->
-<!--        <el-input-->
-<!--          v-model="loginForm.code"-->
-<!--          size="large"-->
-<!--          auto-complete="off"-->
-<!--          placeholder="楠岃瘉鐮�"-->
-<!--          style="width: 63%"-->
-<!--          @keyup.enter="handleLogin"-->
-<!--        >-->
-<!--          <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>-->
-<!--        </el-input>-->
-<!--        <div class="login-code">-->
-<!--          <img :src="codeUrl" @click="getCode" class="login-code-img"/>-->
-<!--        </div>-->
-<!--      </el-form-item>-->
-      <el-form-item style="width:100%;">
-        <el-button
-          :loading="loading"
-          size="large"
-          type="primary"
-          style="width:100%;"
-          @click.prevent="handleLogin"
-        >
-          <span v-if="!loading">鐧� 褰�</span>
-          <span v-else>鐧� 褰� 涓�...</span>
-        </el-button>
-        <div style="float: right;" v-if="register">
-          <router-link class="link-type" :to="'/register'">绔嬪嵆娉ㄥ唽</router-link>
-        </div>
-      </el-form-item>
-      <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">璁颁綇瀵嗙爜</el-checkbox>
-    </el-form>
-    <!--  搴曢儴  -->
-<!--    <div class="el-login-footer">-->
-<!--      <span>Copyright 漏 2018-2025 ruoyi.vip All Rights Reserved.</span>-->
-<!--    </div>-->
-  </div>
-</template>
-
-<script setup>
-import {getCodeImg} from "@/api/login"
-import Cookies from "js-cookie"
-import { encrypt, decrypt } from "@/utils/jsencrypt"
-import useUserStore from '@/store/modules/user'
-
-const title = import.meta.env.VITE_APP_TITLE
-const userStore = useUserStore()
-const route = useRoute()
-const router = useRouter()
-const { proxy } = getCurrentInstance()
-
-const loginForm = ref({
-  username: "",
-  password: "",
-  rememberMe: false,
-})
-
-const loginRules = {
-  username: [{ required: true, trigger: "blur", message: "璇疯緭鍏ユ偍鐨勮处鍙�" }],
-  password: [{ required: true, trigger: "blur", message: "璇疯緭鍏ユ偍鐨勫瘑鐮�" }],
-  // code: [{ required: true, trigger: "change", message: "璇疯緭鍏ラ獙璇佺爜" }]
-}
-
-const codeUrl = ref("")
-const loading = ref(false)
-// 楠岃瘉鐮佸紑鍏�
-const captchaEnabled = ref(true)
-// 娉ㄥ唽寮�鍏�
-const register = ref(false)
-const redirect = ref(undefined)
-
-watch(route, (newRoute) => {
-    redirect.value = newRoute.query && newRoute.query.redirect
-}, { immediate: true })
-
-function handleLogin() {
-  proxy.$refs.loginRef.validate(valid => {
-    if (valid) {
-      loading.value = true
-      // 鍕鹃�変簡闇�瑕佽浣忓瘑鐮佽缃湪 cookie 涓缃浣忕敤鎴峰悕鍜屽瘑鐮�
-      Cookies.set("username", loginForm.value.username, { expires: 30 })
-      Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 })
-      Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 })
-      userStore.loginCheckFactory(loginForm.value).then(res => {
-        const query = route.query
-        const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
-          if (cur !== "redirect") {
-            acc[cur] = query[cur]
-          }
-          return acc
-        }, {})
-        router.push({ path: redirect.value || "/", query: otherQueryParams })
-      }).catch(() => {
-        loading.value = false
-        // 閲嶆柊鑾峰彇楠岃瘉鐮�
-        if (captchaEnabled.value) {
-          getCode()
-        }
-      })
-    }
-  })
-}
-
-function getCode() {
-  getCodeImg().then(res => {
-    captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled
-    if (captchaEnabled.value) {
-      codeUrl.value = "data:image/gif;base64," + res.img
-      loginForm.value.uuid = res.uuid
-    }
-  })
-}
-
-function getCookie() {
-  const username = Cookies.get("username")
-  const password = Cookies.get("password")
-  const rememberMe = Cookies.get("rememberMe")
-  loginForm.value = {
-    username: username === undefined ? loginForm.value.username : username,
-    password: password === undefined ? loginForm.value.password : decrypt(password),
-    rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
-  }
-}
-
-getCode()
-getCookie()
-</script>
-
-<style lang='scss' scoped>
-.login {
-  height: 100%;
-  background-image: url("../assets/images/login-background.png");
-  background-size: cover;
-  position: relative;
-}
-.title {
-  margin: 20px auto 14px auto;
-  text-align: center;
-  color: #2C51D9;
-  font-size: 28px;
-  font-weight: 700;
-}
-
-.login-form {
-  position: absolute;
-  top: 50%;
-  right: 15%;
-  transform: translate(0, -50%);
-  border-radius: 6px;
-  background: #ffffff;
-  width: 420px;
-  height: 500px;
-  padding: 40px;
-  z-index: 1;
-	box-shadow: 0 0 5px 1px #ccc;
-  .el-input {
-    height: 40px;
-    input {
-      height: 40px;
-    }
-  }
-  .input-icon {
-    height: 39px;
-    width: 14px;
-    margin-left: 0px;
-  }
-}
-.login-tip {
-  font-size: 13px;
-  text-align: center;
-  color: #bfbfbf;
-}
-.login-code {
-  width: 33%;
-  height: 40px;
-  float: right;
-  img {
-    cursor: pointer;
-    vertical-align: middle;
-  }
-}
-.el-login-footer {
-  height: 40px;
-  line-height: 40px;
-  position: fixed;
-  bottom: 0;
-  width: 100%;
-  text-align: center;
-  color: #fff;
-  font-family: Arial;
-  font-size: 12px;
-  letter-spacing: 1px;
-}
-.login-code-img {
-  height: 40px;
-  padding-left: 12px;
-}
-:deep() {
-  .el-form-item--default {
-    margin-bottom: 36px;
-  }
-}
-</style>
+<template>
+  <div class="login-page">
+    <div class="login-shell">
+      <section class="login-brand">
+        <div class="brand-badge">PRODUCT INVENTORY</div>
+        <img :src="brandLogo" alt="brand logo" class="brand-logo" />
+        <h1 class="brand-title">{{ title }}</h1>
+        <p class="brand-copy">
+          缁熶竴绠$悊搴撳瓨銆佹祦绋嬩笌涓氬姟鏁版嵁锛岃绯荤粺鍏ュ彛鍜屽悗鍙颁富鐣岄潰淇濇寔鍚屼竴濂楃畝绾﹁瑙夎瑷�銆�
+        </p>
+        <div class="brand-points">
+          <div class="brand-point">
+            <span class="point-dot"></span>
+            <span>娓呮櫚鐨勬暟鎹叆鍙�</span>
+          </div>
+          <div class="brand-point">
+            <span class="point-dot"></span>
+            <span>鏇磋交鐨勭晫闈㈠眰娆�</span>
+          </div>
+          <div class="brand-point">
+            <span class="point-dot"></span>
+            <span>绋冲畾鐨勪笟鍔″崗鍚屼綋楠�</span>
+          </div>
+        </div>
+      </section>
+
+      <section class="login-panel">
+        <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
+          <div class="panel-head">
+            <p class="panel-kicker">WELCOME BACK</p>
+            <h2 class="panel-title">鐧诲綍绯荤粺</h2>
+            <p class="panel-subtitle">杈撳叆璐﹀彿鍜屽瘑鐮佽繘鍏ュ伐浣滃彴銆�</p>
+          </div>
+
+          <el-form-item prop="username">
+            <el-input
+              v-model="loginForm.username"
+              type="text"
+              size="large"
+              auto-complete="off"
+              placeholder="璐﹀彿"
+            >
+              <template #prefix><el-icon><User /></el-icon></template>
+            </el-input>
+          </el-form-item>
+
+          <el-form-item prop="password">
+            <el-input
+              v-model="loginForm.password"
+              type="password"
+              size="large"
+              auto-complete="off"
+              placeholder="瀵嗙爜"
+              show-password
+              @keyup.enter="handleLogin"
+            >
+              <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
+            </el-input>
+          </el-form-item>
+
+          <div class="login-options">
+            <el-checkbox v-model="loginForm.rememberMe">璁颁綇瀵嗙爜</el-checkbox>
+            <router-link v-if="register" class="register-link" :to="'/register'">绔嬪嵆娉ㄥ唽</router-link>
+          </div>
+
+          <el-button
+            :loading="loading"
+            size="large"
+            type="primary"
+            class="login-submit"
+            @click.prevent="handleLogin"
+          >
+            <span v-if="!loading">鐧诲綍</span>
+            <span v-else>鐧诲綍涓�...</span>
+          </el-button>
+        </el-form>
+      </section>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { getCodeImg } from "@/api/login"
+import Cookies from "js-cookie"
+import { encrypt, decrypt } from "@/utils/jsencrypt"
+import useUserStore from "@/store/modules/user"
+import brandLogo from "@/assets/logo/logo.png"
+
+const title = import.meta.env.VITE_APP_TITLE
+const userStore = useUserStore()
+const route = useRoute()
+const router = useRouter()
+const { proxy } = getCurrentInstance()
+
+const loginForm = ref({
+  username: "",
+  password: "",
+  rememberMe: false,
+})
+
+const loginRules = {
+  username: [{ required: true, trigger: "blur", message: "璇疯緭鍏ユ偍鐨勮处鍙�" }],
+  password: [{ required: true, trigger: "blur", message: "璇疯緭鍏ユ偍鐨勫瘑鐮�" }],
+}
+
+const codeUrl = ref("")
+const loading = ref(false)
+const captchaEnabled = ref(true)
+const register = ref(false)
+const redirect = ref(undefined)
+
+watch(
+  route,
+  (newRoute) => {
+    redirect.value = newRoute.query && newRoute.query.redirect
+  },
+  { immediate: true }
+)
+
+function handleLogin() {
+  proxy.$refs.loginRef.validate((valid) => {
+    if (valid) {
+      loading.value = true
+      Cookies.set("username", loginForm.value.username, { expires: 30 })
+      Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 })
+      Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 })
+      userStore
+        .loginCheckFactory(loginForm.value)
+        .then(() => {
+          const query = route.query
+          const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
+            if (cur !== "redirect") {
+              acc[cur] = query[cur]
+            }
+            return acc
+          }, {})
+          router.push({ path: redirect.value || "/", query: otherQueryParams })
+        })
+        .catch(() => {
+          loading.value = false
+          if (captchaEnabled.value) {
+            getCode()
+          }
+        })
+    }
+  })
+}
+
+function getCode() {
+  getCodeImg().then((res) => {
+    captchaEnabled.value = res.captchaEnabled === undefined ? true : res.captchaEnabled
+    if (captchaEnabled.value) {
+      codeUrl.value = "data:image/gif;base64," + res.img
+      loginForm.value.uuid = res.uuid
+    }
+  })
+}
+
+function getCookie() {
+  const username = Cookies.get("username")
+  const password = Cookies.get("password")
+  const rememberMe = Cookies.get("rememberMe")
+  loginForm.value = {
+    username: username === undefined ? loginForm.value.username : username,
+    password: password === undefined ? loginForm.value.password : decrypt(password),
+    rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
+  }
+}
+
+getCode()
+getCookie()
+</script>
+
+<style lang="scss" scoped>
+.login-page {
+  min-height: 100vh;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 32px;
+  background:
+    radial-gradient(circle at top left, rgba(207, 223, 214, 0.95), transparent 30%),
+    radial-gradient(circle at bottom right, rgba(222, 232, 227, 0.9), transparent 28%),
+    linear-gradient(180deg, #f7faf8 0%, #eef2ee 100%);
+}
+
+.login-shell {
+  width: min(1120px, 100%);
+  min-height: 680px;
+  display: grid;
+  grid-template-columns: 1.1fr 0.9fr;
+  border: 1px solid rgba(216, 225, 219, 0.9);
+  border-radius: 32px;
+  overflow: hidden;
+  background: rgba(255, 255, 255, 0.76);
+  box-shadow: 0 26px 80px rgba(31, 49, 38, 0.12);
+  backdrop-filter: blur(24px);
+}
+
+.login-brand {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  padding: 56px 64px;
+  background:
+    linear-gradient(180deg, rgba(244, 248, 245, 0.9), rgba(233, 240, 236, 0.9)),
+    linear-gradient(135deg, rgba(31, 122, 114, 0.05), rgba(255, 255, 255, 0));
+
+  &::after {
+    content: "";
+    position: absolute;
+    inset: 28px;
+    border: 1px solid rgba(31, 122, 114, 0.08);
+    border-radius: 28px;
+    pointer-events: none;
+  }
+}
+
+.brand-badge {
+  width: fit-content;
+  padding: 8px 14px;
+  border-radius: 999px;
+  background: rgba(31, 122, 114, 0.1);
+  color: #1f7a72;
+  font-size: 12px;
+  font-weight: 700;
+  letter-spacing: 0.14em;
+}
+
+.brand-logo {
+  width: 160px;
+  height: auto;
+  margin: 30px 0 24px;
+  object-fit: contain;
+}
+
+.brand-title {
+  margin: 0;
+  font-size: 42px;
+  line-height: 1.12;
+  color: #21313f;
+  letter-spacing: -0.03em;
+}
+
+.brand-copy {
+  max-width: 460px;
+  margin: 18px 0 0;
+  font-size: 16px;
+  line-height: 1.75;
+  color: #5f6d7e;
+}
+
+.brand-points {
+  margin-top: 34px;
+  display: flex;
+  flex-direction: column;
+  gap: 14px;
+}
+
+.brand-point {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  color: #3d4b59;
+  font-size: 15px;
+  font-weight: 500;
+}
+
+.point-dot {
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  background: linear-gradient(135deg, #1f7a72, #5ca39c);
+  box-shadow: 0 0 0 6px rgba(31, 122, 114, 0.08);
+}
+
+.login-panel {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 40px;
+  background: rgba(255, 255, 255, 0.7);
+}
+
+.login-form {
+  width: min(420px, 100%);
+  padding: 38px 34px 34px;
+  border: 1px solid rgba(216, 225, 219, 0.92);
+  border-radius: 28px;
+  background: rgba(255, 255, 255, 0.88);
+  box-shadow: 0 18px 52px rgba(31, 49, 38, 0.1);
+}
+
+.panel-head {
+  margin-bottom: 28px;
+}
+
+.panel-kicker {
+  margin: 0 0 10px;
+  color: #8a98a8;
+  font-size: 12px;
+  font-weight: 700;
+  letter-spacing: 0.16em;
+}
+
+.panel-title {
+  margin: 0;
+  color: #21313f;
+  font-size: 30px;
+  font-weight: 700;
+}
+
+.panel-subtitle {
+  margin: 10px 0 0;
+  color: #6b7888;
+  font-size: 14px;
+}
+
+.login-options {
+  margin: -4px 0 22px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  color: #5f6d7e;
+}
+
+.register-link {
+  color: var(--el-color-primary);
+  font-weight: 600;
+}
+
+.login-submit {
+  width: 100%;
+  height: 48px;
+}
+
+.input-icon {
+  width: 14px;
+}
+
+:deep(.el-form-item) {
+  margin-bottom: 22px;
+}
+
+:deep(.el-input__wrapper) {
+  min-height: 42px;
+  height: 42px;
+  padding-top: 0;
+  padding-bottom: 0;
+}
+
+:deep(.el-input__inner) {
+  height: 42px;
+  line-height: 42px;
+}
+
+:deep(.el-checkbox) {
+  color: #5f6d7e;
+}
+
+@media (max-width: 960px) {
+  .login-page {
+    padding: 18px;
+  }
+
+  .login-shell {
+    min-height: auto;
+    grid-template-columns: 1fr;
+  }
+
+  .login-brand {
+    padding: 40px 28px 22px;
+  }
+
+  .login-brand::after {
+    inset: 16px;
+  }
+
+  .brand-title {
+    font-size: 32px;
+  }
+
+  .brand-copy {
+    font-size: 14px;
+  }
+
+  .brand-points {
+    margin-top: 24px;
+  }
+
+  .login-panel {
+    padding: 12px 18px 24px;
+  }
+
+  .login-form {
+    width: 100%;
+    padding: 28px 22px 24px;
+  }
+}
+</style>
diff --git a/src/views/oaSystem/projectManagement/components/milestoneList.vue b/src/views/oaSystem/projectManagement/components/milestoneList.vue
index 47b0027..ed31772 100644
--- a/src/views/oaSystem/projectManagement/components/milestoneList.vue
+++ b/src/views/oaSystem/projectManagement/components/milestoneList.vue
@@ -84,8 +84,8 @@
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
           <el-button type="primary" @click="submitEditForm">纭畾</el-button>
+          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
         </div>
       </template>
     </el-dialog>
diff --git a/src/views/oaSystem/projectManagement/components/taskTree.vue b/src/views/oaSystem/projectManagement/components/taskTree.vue
index 11e3ae8..03a1a15 100644
--- a/src/views/oaSystem/projectManagement/components/taskTree.vue
+++ b/src/views/oaSystem/projectManagement/components/taskTree.vue
@@ -164,8 +164,8 @@
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button @click="dialogOpen = false">鍙栨秷</el-button>
           <el-button type="primary" @click="submitTaskForm">纭畾</el-button>
+          <el-button @click="dialogOpen = false">鍙栨秷</el-button>
         </div>
       </template>
     </el-dialog>
diff --git a/src/views/oaSystem/projectManagement/index.vue b/src/views/oaSystem/projectManagement/index.vue
index 2a0ec3a..79fbe7f 100644
--- a/src/views/oaSystem/projectManagement/index.vue
+++ b/src/views/oaSystem/projectManagement/index.vue
@@ -188,8 +188,8 @@
       />
       <template #footer>
         <div class="dialog-footer">
-          <el-button @click="cancel">鍙栨秷</el-button>
           <el-button type="primary" @click="submitForm">纭畾</el-button>
+          <el-button @click="cancel">鍙栨秷</el-button>
         </div>
       </template>
     </el-dialog>
diff --git a/src/views/oaSystem/projectManagement/projectDetail.vue b/src/views/oaSystem/projectManagement/projectDetail.vue
index c3b0779..578f76e 100644
--- a/src/views/oaSystem/projectManagement/projectDetail.vue
+++ b/src/views/oaSystem/projectManagement/projectDetail.vue
@@ -137,8 +137,8 @@
   </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button @click="cancel">鍙栨秷</el-button>
           <el-button type="primary" @click="submitForm">纭畾</el-button>
+          <el-button @click="cancel">鍙栨秷</el-button>
         </div>
       </template>
     </el-dialog>
@@ -239,8 +239,8 @@
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button @click="cancelGoal">鍙栨秷</el-button>
           <el-button type="primary" @click="submitGoalForm">纭畾</el-button>
+          <el-button @click="cancelGoal">鍙栨秷</el-button>
         </div>
       </template>
     </el-dialog>
diff --git a/src/views/personnelManagement/analytics/index.vue b/src/views/personnelManagement/analytics/index.vue
index 9e6e449..6c5a1d6 100644
--- a/src/views/personnelManagement/analytics/index.vue
+++ b/src/views/personnelManagement/analytics/index.vue
@@ -503,7 +503,6 @@
 <style scoped>
 .analytics-container {
   padding: 20px;
-  background-color: #f5f7fa;
   min-height: 100vh;
 }
 
diff --git a/src/views/personnelManagement/attendanceCheckin/checkinRules/components/form.vue b/src/views/personnelManagement/attendanceCheckin/checkinRules/components/form.vue
index a410be0..b17b234 100644
--- a/src/views/personnelManagement/attendanceCheckin/checkinRules/components/form.vue
+++ b/src/views/personnelManagement/attendanceCheckin/checkinRules/components/form.vue
@@ -110,12 +110,8 @@
     </el-form>
     <template #footer>
       <span class="dialog-footer">
+        <el-button type="primary" @click="submitForm" v-if="operationType !== 'view'">纭畾</el-button>
         <el-button @click="dialogVisible = false">鍙栨秷</el-button>
-        <el-button type="primary"
-                   @click="submitForm"
-                   v-if="operationType !== 'view'">
-          纭畾
-        </el-button>
       </span>
     </template>
   </el-dialog>
diff --git a/src/views/personnelManagement/attendanceCheckin/checkinRules/index.vue b/src/views/personnelManagement/attendanceCheckin/checkinRules/index.vue
index 58d4b4f..c47dd2e 100644
--- a/src/views/personnelManagement/attendanceCheckin/checkinRules/index.vue
+++ b/src/views/personnelManagement/attendanceCheckin/checkinRules/index.vue
@@ -99,11 +99,9 @@
                          align="center">
           <template #default="scope">
             <el-button type="primary"
-                       size="small"
                        link
                        @click="openForm('edit', scope.row)">缂栬緫</el-button>
             <el-button type="danger"
-                       size="small"
                        link
                        @click="handleDelete(scope.row.id)">鍒犻櫎</el-button>
           </template>
diff --git a/src/views/personnelManagement/attendanceCheckin/index.vue b/src/views/personnelManagement/attendanceCheckin/index.vue
index b7b0f92..168a71d 100644
--- a/src/views/personnelManagement/attendanceCheckin/index.vue
+++ b/src/views/personnelManagement/attendanceCheckin/index.vue
@@ -455,6 +455,9 @@
 </script>
 
 <style scoped lang="scss">
+.table_list {
+	margin-top: unset;
+}
   .mb16 {
     margin-bottom: 16px;
   }
@@ -497,7 +500,7 @@
     color: #333;
   }
 
-  ::v-deep(.row-abnormal) {
+  :deep(.row-abnormal) {
     background-color: #fff5f5;
   }
 
diff --git a/src/views/personnelManagement/classsSheduling/index.vue b/src/views/personnelManagement/classsSheduling/index.vue
index 6891a57..140ffba 100644
--- a/src/views/personnelManagement/classsSheduling/index.vue
+++ b/src/views/personnelManagement/classsSheduling/index.vue
@@ -49,13 +49,11 @@
           <div class="search-actions">
             <el-button size="small"
                        type="primary"
-                       @click="refreshTable()"
-                       :icon="Search">
+                       @click="refreshTable()">
               鏌ヨ
             </el-button>
             <el-button size="small"
                        @click="refresh()"
-                       :icon="Refresh"
                        style="margin-left: 8px">
               閲嶇疆
             </el-button>
@@ -63,22 +61,19 @@
           <div class="search-buttons">
             <el-button size="small"
                        type="primary"
-                       @click="configTime"
-                       :icon="Setting">
+                       @click="configTime">
               鐝閰嶇疆
             </el-button>
             <el-button size="small"
                        type="success"
                        @click="handleDown"
                        :loading="downLoading"
-                       :icon="Download"
                        style="margin-left: 8px">
               瀵煎嚭
             </el-button>
             <el-button size="small"
                        type="warning"
                        @click="schedulingVisible = true"
-                       :icon="Calendar"
                        style="margin-left: 8px">
               鎺掔彮
             </el-button>
@@ -674,10 +669,12 @@
     })
       .then(res => {
         proxy.$modal.msgSuccess("涓嬭浇鎴愬姛");
-        downLoading.value = false;
-        const blob = new Blob([res], {
-          type: "application/force-download",
-        });
+        const blob =
+          res instanceof Blob
+            ? res
+            : new Blob([res], {
+                type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+              });
         let fileName = "";
         if (query.month) {
           fileName = year + "-" + query.month + " 鐝淇℃伅";
@@ -687,6 +684,8 @@
         proxy.$download.saveAs(blob, fileName + ".xlsx");
       })
       .catch(err => {
+      })
+      .finally(() => {
         downLoading.value = false;
       });
   };
diff --git a/src/views/personnelManagement/contractManagement/index.vue b/src/views/personnelManagement/contractManagement/index.vue
index 1d2aab7..a55a502 100644
--- a/src/views/personnelManagement/contractManagement/index.vue
+++ b/src/views/personnelManagement/contractManagement/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form mb20">
       <div>
         <span class="search_title">濮撳悕锛�</span>
         <el-input v-model="searchForm.staffName" style="width: 240px" placeholder="璇疯緭鍏ュ鍚嶆悳绱�" @change="handleQuery"
diff --git a/src/views/personnelManagement/dimission/components/formDia.vue b/src/views/personnelManagement/dimission/components/formDia.vue
index 6d3cb46..84922ec 100644
--- a/src/views/personnelManagement/dimission/components/formDia.vue
+++ b/src/views/personnelManagement/dimission/components/formDia.vue
@@ -101,6 +101,7 @@
                 <el-date-picker
                     v-model="form.leaveDate"
                     type="date"
+                    :disabled="operationType === 'edit'"
                     placeholder="璇烽�夋嫨绂昏亴鏃ユ湡"
                     value-format="YYYY-MM-DD"
                     format="YYYY-MM-DD"
diff --git a/src/views/personnelManagement/dimission/index.vue b/src/views/personnelManagement/dimission/index.vue
index 5993b3f..c2b8c3e 100644
--- a/src/views/personnelManagement/dimission/index.vue
+++ b/src/views/personnelManagement/dimission/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form mb20">
       <div>
         <span class="search_title">濮撳悕锛�</span>
         <el-input
diff --git a/src/views/personnelManagement/employeeRecord/components/JobInfoSection.vue b/src/views/personnelManagement/employeeRecord/components/JobInfoSection.vue
index d3b8c4c..be33436 100644
--- a/src/views/personnelManagement/employeeRecord/components/JobInfoSection.vue
+++ b/src/views/personnelManagement/employeeRecord/components/JobInfoSection.vue
@@ -19,6 +19,7 @@
             placeholder="璇烽�夋嫨"
             style="width: 100%"
             clearable
+            @change="calculateContractTerm"
           />
         </el-form-item>
       </el-col>
@@ -43,6 +44,7 @@
             placeholder="璇烽�夋嫨"
             style="width: 100%"
             clearable
+            @change="calculateContractTerm"
           />
         </el-form-item>
       </el-col>
@@ -131,6 +133,34 @@
 });
 
 const { form, postOptions, deptOptions } = toRefs(props);
+
+// 璁$畻鍚堝悓骞撮檺
+const calculateContractTerm = () => {
+  if (form.value.contractStartTime && form.value.contractEndTime) {
+    const startDate = new Date(form.value.contractStartTime);
+    const endDate = new Date(form.value.contractEndTime);
+
+    if (endDate > startDate) {
+      // 璁$畻骞翠唤宸�
+      const yearDiff = endDate.getFullYear() - startDate.getFullYear();
+      const monthDiff = endDate.getMonth() - startDate.getMonth();
+      const dayDiff = endDate.getDate() - startDate.getDate();
+
+      let years = yearDiff;
+
+      // 濡傛灉缁撴潫鏃ユ湡鐨勬湀鏃ュ皬浜庡紑濮嬫棩鏈熺殑鏈堟棩锛屽垯鍑忓幓1骞�
+      if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) {
+        years = yearDiff - 1;
+      }
+
+      form.value.contractTerm = Math.max(0, years);
+    } else {
+      form.value.contractTerm = 0;
+    }
+  } else {
+    form.value.contractTerm = 0;
+  }
+};
 </script>
 
 <style scoped>
diff --git a/src/views/personnelManagement/employeeRecord/index.vue b/src/views/personnelManagement/employeeRecord/index.vue
index d0c97b4..cd4ecf5 100644
--- a/src/views/personnelManagement/employeeRecord/index.vue
+++ b/src/views/personnelManagement/employeeRecord/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form mb20">
       <div>
         <span class="search_title">濮撳悕锛�</span>
         <el-input
@@ -11,15 +11,32 @@
             clearable
             :prefix-icon="Search"
         />
-        <span  style="margin-left: 10px" class="search_title">鍚堝悓缁撴潫鏃ユ湡锛�</span>
-        <el-date-picker  v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
-                         placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
+        <span class="search_title search_title2">閮ㄩ棬锛�</span>
+          <el-tree-select
+            v-model="searchForm.sysDeptId"
+            :data="deptOptions"
+            check-strictly
+            :render-after-expand="false"
+            style="width: 240px"
+            placeholder="璇烽�夋嫨"
+          />
+          <span class="search_title search_title2">鍏ヨ亴鏃ユ湡锛�</span>
+          <el-date-picker
+            v-model="searchForm.contractStartTime"
+            value-format="YYYY-MM-DD"
+            format="YYYY-MM-DD"
+            placeholder="璇烽�夋嫨"
+          />
+        <!-- <span  style="margin-left: 10px" class="search_title">鍚堝悓缁撴潫鏃ユ湡锛�</span> -->
+        <!-- <el-date-picker  v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
+                         placeholder="璇烽�夋嫨" clearable @change="changeDaterange" /> -->
         <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
         >鎼滅储</el-button
         >
       </div>
       <div>
         <el-button type="primary" @click="openFormNewOrEditFormDia('add')">鏂板鍏ヨ亴</el-button>
+        <el-button type="info" @click="handleImport">瀵煎叆</el-button>
         <el-button @click="handleOut">瀵煎嚭</el-button>
         <!-- <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button> -->
       </div>
@@ -45,15 +62,49 @@
         :id="id"
         @completed="handleQuery"
     />
+    
+    <!-- 瀵煎叆瀵硅瘽妗� -->
+    <el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
+      <el-upload
+        ref="uploadRef"
+        :limit="1"
+        accept=".xlsx, .xls"
+        :headers="upload.headers"
+        :action="upload.url"
+        :disabled="upload.isUploading"
+        :on-progress="handleFileUploadProgress"
+        :on-success="handleFileSuccess"
+        :auto-upload="false"
+        drag
+      >
+        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+        <div class="el-upload__text">灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em></div>
+        <template #tip>
+          <div class="el-upload__tip text-center">
+            <span>浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�</span>
+            <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline; margin-left: 5px;" @click="importTemplate">涓嬭浇妯℃澘</el-link>
+          </div>
+        </template>
+      </el-upload>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button>
+          <el-button @click="upload.open = false">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { Search } from "@element-plus/icons-vue";
+import { Search, UploadFilled } from "@element-plus/icons-vue";
 import {onMounted, ref} from "vue";
 import {ElMessageBox} from "element-plus";
+import { deptTreeSelect } from "@/api/system/user.js";
 import {batchDeleteStaffOnJobs, staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js";
+import { getToken } from "@/utils/auth";
 import dayjs from "dayjs";
+
 const NewOrEditFormDia = defineAsyncComponent(() => import("@/views/personnelManagement/employeeRecord/components/NewOrEditFormDia.vue"));
 const ShowFormDia = defineAsyncComponent(() => import( "@/views/personnelManagement/employeeRecord/components/Show.vue"));
 const RenewContract = defineAsyncComponent(() => import( "@/views/personnelManagement/employeeRecord/components/RenewContract.vue"));
@@ -65,8 +116,9 @@
     entryDateStart: undefined,
     entryDateEnd: undefined,
   },
+  deptOptions: [],
 });
-const { searchForm } = toRefs(data);
+const { searchForm, deptOptions } = toRefs(data);
 const isShowRenewContractModal = ref(false);
 const id = ref(0);
 const tableColumn = ref([
@@ -117,6 +169,11 @@
   {
     label: "鍑虹敓鏃ユ湡",
     prop: "birthDate",
+    width: 120,
+  },
+  {
+    label: "鍏ヨ亴鏃ユ湡",
+    prop: "contractStartTime",
     width: 120,
   },
   {
@@ -182,6 +239,40 @@
 const formDiaNewOrEditFormDia = ref()
 const { proxy } = getCurrentInstance()
 
+// 瀵煎叆鐩稿叧
+const uploadRef = ref(null)
+const upload = reactive({
+  // 鏄惁鏄剧ず寮瑰嚭灞�
+  open: false,
+  // 寮瑰嚭灞傛爣棰�
+  title: "",
+  // 鏄惁绂佺敤涓婁紶
+  isUploading: false,
+  // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+  headers: { Authorization: "Bearer " + getToken() },
+  // 涓婁紶鐨勫湴鍧�
+  url: import.meta.env.VITE_APP_BASE_API + "/staff/staffOnJob/import"
+})
+
+const fetchDeptOptions = () => {
+    deptTreeSelect().then(response => {
+      console.log(response.data)
+      deptOptions.value = filterDisabledDept(
+        JSON.parse(JSON.stringify(response.data))
+      );
+    });
+  };
+const filterDisabledDept = deptList => {
+    return deptList.filter(dept => {
+      if (dept.disabled) {
+        return false;
+      }
+      if (dept.children && dept.children.length) {
+        dept.children = filterDisabledDept(dept.children);
+      }
+      return true;
+    });
+  };
 const changeDaterange = (value) => {
   searchForm.value.entryDateStart = undefined;
   searchForm.value.entryDateEnd = undefined;
@@ -203,6 +294,7 @@
   getList();
 };
 const getList = () => {
+  fetchDeptOptions();
   tableLoading.value = true;
   const params = { ...searchForm.value, ...page };
   params.entryDate = undefined
@@ -270,9 +362,44 @@
         proxy.$modal.msg("宸插彇娑�");
       });
 };
+
+// 瀵煎叆鎸夐挳鎿嶄綔
+const handleImport = () => {
+  upload.title = "鍛樺伐瀵煎叆"
+  upload.open = true
+}
+
+// 涓嬭浇妯℃澘鎿嶄綔
+const importTemplate = () => {
+  proxy.download("/staff/staffOnJob/downloadTemplate", {}, `鍛樺伐瀵煎叆妯℃澘_${new Date().getTime()}.xlsx`)
+}
+
+// 鏂囦欢涓婁紶涓鐞�
+const handleFileUploadProgress = (event, file, fileList) => {
+  upload.isUploading = true
+}
+
+// 鏂囦欢涓婁紶鎴愬姛澶勭悊
+const handleFileSuccess = (response, file, fileList) => {
+  upload.open = false
+  upload.isUploading = false
+  proxy.$refs["uploadRef"].handleRemove(file)
+  proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "瀵煎叆缁撴灉", { dangerouslyUseHTMLString: true })
+  getList()
+}
+
+// 鎻愪氦涓婁紶鏂囦欢
+const submitFileForm = () => {
+  proxy.$refs["uploadRef"].submit()
+}
+
 onMounted(() => {
   getList();
 });
 </script>
 
-<style scoped></style>
+<style scoped>
+.search_title2 {
+  margin-left: 10px;
+}
+</style>
diff --git a/src/views/personnelManagement/monthlyStatistics/index.vue b/src/views/personnelManagement/monthlyStatistics/index.vue
index f389f57..4ac9e97 100644
--- a/src/views/personnelManagement/monthlyStatistics/index.vue
+++ b/src/views/personnelManagement/monthlyStatistics/index.vue
@@ -36,7 +36,7 @@
       </div>
     </div>
     <div class="table_list">
-      <div style="margin-bottom: 10px">
+      <div style="margin-bottom: 10px;text-align: right">
         <el-button type="primary" @click="openForm('add')">鏂板缓宸ヨ祫琛�</el-button>
         <el-button @click="handleDelete">鍒犻櫎</el-button>
         <el-button @click="openBankSetting">璁剧疆閾惰</el-button>
diff --git a/src/views/personnelManagement/socialSecuritySet/index.vue b/src/views/personnelManagement/socialSecuritySet/index.vue
index 2a1ff65..1f3f104 100644
--- a/src/views/personnelManagement/socialSecuritySet/index.vue
+++ b/src/views/personnelManagement/socialSecuritySet/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form mb20">
       <div>
         <span class="search_title">涓婚锛�</span>
         <el-input
diff --git a/src/views/procurementManagement/paymentEntry/index.vue b/src/views/procurementManagement/paymentEntry/index.vue
index 3f4512b..9ce59ad 100644
--- a/src/views/procurementManagement/paymentEntry/index.vue
+++ b/src/views/procurementManagement/paymentEntry/index.vue
@@ -89,8 +89,12 @@
 									placeholder="璇烽�夋嫨"
 									clearable
 								>
-									<el-option label="鐢垫眹" value="鐢垫眹" />
-									<el-option label="鎵垮厬" value="鎵垮厬" />
+									<el-option
+										v-for="item in checkout_payment"
+										:key="item.value"
+										:label="item.label"
+										:value="item.value"
+									/>
 								</el-select>
 							</template>
 						</el-table-column>
@@ -101,7 +105,6 @@
 								<el-button
 									link
 									type="primary"
-									size="small"
 									@click="changeEditType(scope.row)"
 									v-if="!scope.row.editType"
 								>缂栬緫</el-button
@@ -109,7 +112,6 @@
 								<el-button
 									link
 									type="primary"
-									size="small"
 									@click="saveReceiptPayment(scope.row)"
 									v-if="scope.row.editType"
 								>淇濆瓨</el-button
@@ -117,7 +119,6 @@
 								<el-button
 									link
 									type="primary"
-									size="small"
 									@click="handleDelete(scope.row)"
 								>鍒犻櫎</el-button
 								>
@@ -186,8 +187,12 @@
         <el-table-column label="浠樻鏂瑰紡" width="160">
           <template #default="{ row }">
             <el-select v-model="row.paymentMethod" placeholder="璇烽�夋嫨" clearable>
-              <el-option label="鐢垫眹" value="鐢垫眹" />
-              <el-option label="鎵垮厬" value="鎵垮厬" />
+              <el-option
+                v-for="item in checkout_payment"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              />
             </el-select>
           </template>
         </el-table-column>
@@ -242,6 +247,7 @@
 import { getCurrentDate } from "@/utils/index.js";
 
 const { proxy } = getCurrentInstance();
+const { checkout_payment } = proxy.useDict("checkout_payment");
 const tableColumn = ref([
 	{
 		type: "expand",
@@ -515,7 +521,15 @@
       return;
     }
   }
-  paymentRegistrationAdd(forms.value).then(() => {
+  const normalizePaymentMethodValue = (method) => {
+    const hit = checkout_payment.value.find((item) => item.value == method || item.label == method);
+    return hit ? hit.value : method;
+  };
+  const submitRows = forms.value.map((item) => ({
+    ...item,
+    paymentMethod: normalizePaymentMethodValue(item.paymentMethod),
+  }));
+  paymentRegistrationAdd(submitRows).then(() => {
     proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
     closeDia();
     getList();
@@ -572,7 +586,7 @@
 .table_list {
   margin-top: unset;
 }
-::v-deep(.el-checkbox__label) {
+:deep(.el-checkbox__label) {
   font-weight: bold;
 }
 .empty-tip {
diff --git a/src/views/procurementManagement/paymentHistory/index.vue b/src/views/procurementManagement/paymentHistory/index.vue
index 179373b..c9e9836 100644
--- a/src/views/procurementManagement/paymentHistory/index.vue
+++ b/src/views/procurementManagement/paymentHistory/index.vue
@@ -70,7 +70,6 @@
           <el-button
             type="primary"
             link
-            size="small"
             @click="handleDelete(row)"
           >
             鍒犻櫎
diff --git a/src/views/procurementManagement/paymentLedger/index.vue b/src/views/procurementManagement/paymentLedger/index.vue
index 294561d..23a62aa 100644
--- a/src/views/procurementManagement/paymentLedger/index.vue
+++ b/src/views/procurementManagement/paymentLedger/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form" style="margin-bottom: 20px;">
       <div>
         <span class="search_title">渚涘簲鍟嗗悕绉�:</span>
         <el-input
diff --git a/src/views/procurementManagement/procurementInvoiceLedger/index.vue b/src/views/procurementManagement/procurementInvoiceLedger/index.vue
index d82e3e7..c94d8c2 100644
--- a/src/views/procurementManagement/procurementInvoiceLedger/index.vue
+++ b/src/views/procurementManagement/procurementInvoiceLedger/index.vue
@@ -69,7 +69,7 @@
           <el-button
             type="primary"
             link
-            @click="downLoadFile(row)"
+            @click="openFileDialog(row)"
           >
             闄勪欢
           </el-button>
@@ -83,16 +83,7 @@
         </template>
       </PIMTable>
     </div>
-    <FileListDialog 
-      ref="fileListRef" 
-      v-model="fileListDialogVisible"
-      title="闄勪欢鍒楄〃"
-      :showUploadButton="true"
-      :showDeleteButton="true"
-      :deleteMethod="handleDeleteFile"
-      :uploadMethod="handleFileUpload"
-      :rulesRegulationsManagementId="currentRowId"
-    />
+    <FileList v-if="fileDialogVisible"  v-model:visible="fileDialogVisible" record-type="ticket_registration" :record-id="recordId"  />
     <EditModal ref="editmodalRef" @success="getTableData"></EditModal>
   </div>
 </template>
@@ -113,9 +104,9 @@
 import { onMounted } from "vue";
 import { ElMessageBox } from "element-plus";
 import EditModal from "./Modal/EditModal.vue";
-import FileListDialog from '@/components/Dialog/FileListDialog.vue';
 import useUserStore from "@/store/modules/user.js";
 const userStore = useUserStore();
+const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
 
 defineOptions({
   name: "鏉ョエ鍙拌处",
@@ -290,143 +281,15 @@
   onCurrentChange(page);
 };
 
-const downLoadFile = row => {
-  currentRowId.value = row.id;
-  if (fileListRef.value) {
-    fileListRef.value.open(row.commonFiles || []);
-  }
-};
+// 鎵撳紑闄勪欢寮圭獥
+const recordId =ref(0)
+const fileDialogVisible = ref(false)
 
-// 涓婁紶闄勪欢锛堣嚜瀹氫箟涓婁紶鏂规硶锛�
-const handleFileUpload = async () => {
-  if (!currentRowId.value) {
-    proxy.$modal.msgWarning("缂哄皯鐧昏ID锛屾棤娉曚繚瀛橀檮浠�");
-    return;
-  }
-  
-  return new Promise((resolve) => {
-    // 鍒涘缓涓�涓殣钘忕殑鏂囦欢杈撳叆鍏冪礌
-    const input = document.createElement('input');
-    input.type = 'file';
-    input.style.display = 'none';
-    input.onchange = async (e) => {
-      const file = e.target.files[0];
-      if (!file) {
-        resolve(null);
-        return;
-      }
-      
-      try {
-        // 浣跨敤 FormData 涓婁紶鏂囦欢
-        const formData = new FormData();
-        formData.append('file', file);
-        formData.append('type', '4'); // type 鍙傛暟锛岀敤鎴锋湭鎸囧畾鍏蜂綋鍊硷紝鍏堜紶绌哄瓧绗︿覆
-        formData.append('id', currentRowId.value); // 褰撳墠琛岀殑 id
-        
-        const uploadRes = await request({
-          url: '/file/uploadByCommon',
-          method: 'post',
-          data: formData,
-          headers: {
-            'Content-Type': 'multipart/form-data',
-            Authorization: `Bearer ${getToken()}`
-          }
-        });
-        
-        if (uploadRes.code === 200) {
-          proxy.$modal.msgSuccess("闄勪欢涓婁紶鎴愬姛");
-          
-          // 鍒锋柊鍒楄〃鑾峰彇鏈�鏂版暟鎹�
-          await new Promise((resolveRefresh) => {
-            // 璋冪敤 API 鑾峰彇鏈�鏂板垪琛ㄦ暟鎹�
-            productRecordPage({
-              ...filters,
-              current: pagination.currentPage,
-              size: pagination.pageSize
-            }).then(({ code, data }) => {
-              if (code === 200) {
-                // 鏇存柊鏁版嵁鍒楄〃
-                dataList.value = data.records;
-                pagination.total = data.total;
-                
-                // 浠庡閮ㄦ暟鎹幏鍙� commonFiles
-                const currentRow = dataList.value.find(row => row.id === currentRowId.value);
-                if (currentRow && fileListRef.value) {
-                  // 鍒锋柊闄勪欢鍒楄〃锛屼娇鐢ㄤ粠澶栭儴鑾峰彇鐨勬渶鏂� commonFiles
-                  fileListRef.value.open(currentRow.commonFiles || []);
-                }
-                resolveRefresh();
-              } else {
-                resolveRefresh();
-              }
-            }).catch(() => {
-              resolveRefresh();
-            });
-          });
-          
-          resolve({
-            name: uploadRes.data?.originalName || file.name,
-            url: uploadRes.data?.tempPath || uploadRes.data?.url,
-            id: uploadRes.data?.id
-          });
-        } else {
-          proxy.$modal.msgError(uploadRes.msg || "鏂囦欢涓婁紶澶辫触");
-          resolve(null);
-        }
-      } catch (error) {
-        console.error("闄勪欢涓婁紶澶辫触:", error);
-        proxy.$modal.msgError("闄勪欢涓婁紶澶辫触");
-        resolve(null);
-      } finally {
-        document.body.removeChild(input);
-      }
-    };
-    
-    document.body.appendChild(input);
-    input.click();
-  });
-};
-
-// 鍒犻櫎闄勪欢
-const handleDeleteFile = async (file) => {
-  try {
-    await delCommonFile([file.id]);
-    proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-    
-    // 鍒锋柊鍒楄〃鑾峰彇鏈�鏂版暟鎹�
-    await new Promise((resolveRefresh) => {
-      // 璋冪敤 API 鑾峰彇鏈�鏂板垪琛ㄦ暟鎹�
-      productRecordPage({
-        ...filters,
-        current: pagination.currentPage,
-        size: pagination.pageSize
-      }).then(({ code, data }) => {
-        if (code === 200) {
-          // 鏇存柊鏁版嵁鍒楄〃
-          dataList.value = data.records;
-          pagination.total = data.total;
-          
-          // 浠庡閮ㄦ暟鎹幏鍙� commonFiles
-          const currentRow = dataList.value.find(row => row.id === currentRowId.value);
-          if (currentRow && fileListRef.value) {
-            // 鍒锋柊闄勪欢鍒楄〃锛屼娇鐢ㄤ粠澶栭儴鑾峰彇鐨勬渶鏂� commonFiles
-            fileListRef.value.open(currentRow.commonFiles || []);
-          }
-          resolveRefresh();
-        } else {
-          resolveRefresh();
-        }
-      }).catch(() => {
-        resolveRefresh();
-      });
-    });
-    
-    return true;
-  } catch (error) {
-    proxy.$modal.msgError("鍒犻櫎澶辫触");
-    return false;
-  }
-};
+// 鎵撳紑闄勪欢寮规
+const openFileDialog = async (row) => {
+  recordId.value = row.id
+  fileDialogVisible.value = true
+}
 
 const openEdit = (row) => {
   editmodalRef.value.open(row);
diff --git a/src/views/procurementManagement/procurementLedger/index.vue b/src/views/procurementManagement/procurementLedger/index.vue
index 20c6b1c..cffdcc6 100644
--- a/src/views/procurementManagement/procurementLedger/index.vue
+++ b/src/views/procurementManagement/procurementLedger/index.vue
@@ -44,7 +44,8 @@
           </el-form-item>
           <el-form-item>
             <el-button type="primary"
-                       @click="handleQuery"> 鎼滅储 </el-button>
+                       @click="handleQuery"> 鎼滅储
+            </el-button>
           </el-form-item>
         </el-form>
       </div>
@@ -53,11 +54,14 @@
       <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
         <el-button type="primary"
                    @click="openForm('add')">鏂板鍙拌处</el-button>
-        <el-button type="primary" plain @click="handleImport">瀵煎叆</el-button>
+        <el-button type="primary"
+                   plain
+                   @click="handleImport">瀵煎叆</el-button>
         <el-button @click="handleOut">瀵煎嚭</el-button>
         <el-button type="danger"
                    plain
-                   @click="handleDelete">鍒犻櫎</el-button>
+                   @click="handleDelete">鍒犻櫎
+        </el-button>
       </div>
       <el-table :data="tableData"
                 border
@@ -90,6 +94,10 @@
                                prop="unit" />
               <el-table-column label="鏁伴噺"
                                prop="quantity" />
+              <el-table-column label="鍙敤鏁伴噺"
+                               prop="availableQuality" />
+              <el-table-column label="閫�璐ф暟閲�"
+                               prop="returnQuality" />
               <el-table-column label="绋庣巼(%)"
                                prop="taxRate" />
               <el-table-column label="鍚◣鍗曚环(鍏�)"
@@ -114,11 +122,11 @@
                          show-overflow-tooltip />
         <el-table-column label="閿�鍞悎鍚屽彿"
                          prop="salesContractNo"
-                          width="160"
+                         width="160"
                          show-overflow-tooltip />
         <el-table-column label="渚涘簲鍟嗗悕绉�"
                          prop="supplierName"
-                          width="160"
+                         width="160"
                          show-overflow-tooltip />
         <el-table-column label="椤圭洰鍚嶇О"
                          prop="projectName"
@@ -129,9 +137,8 @@
                          width="100"
                          show-overflow-tooltip>
           <template #default="scope">
-            <el-tag 
-              :type="getApprovalStatusType(scope.row.approvalStatus)"
-              size="small">
+            <el-tag :type="getApprovalStatusType(scope.row.approvalStatus)"
+                    size="small">
               {{ approvalStatusText[scope.row.approvalStatus] || '鏈煡鐘舵��' }}
             </el-tag>
           </template>
@@ -168,13 +175,12 @@
           <template #default="scope">
             <el-button link
                        type="primary"
-                       size="small"
                        @click="openForm('edit', scope.row)"
-                       :disabled="scope.row.approvalStatus !== 1 && scope.row.approvalStatus !== 4">缂栬緫</el-button>
+                       :disabled="scope.row.approvalStatus !== 1 && scope.row.approvalStatus !== 4">缂栬緫
+            </el-button>
             <el-button link
                        type="primary"
-                       size="small"
-                       @click="downLoadFile(scope.row)">闄勪欢</el-button>
+                       @click="openFileDialog(scope.row)">闄勪欢</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -186,12 +192,12 @@
                   @pagination="paginationChange" />
     </div>
     <FormDialog v-model="dialogFormVisible"
-               :title="operationType === 'add' ? '鏂板閲囪喘鍙拌处椤甸潰' : '缂栬緫閲囪喘鍙拌处椤甸潰'"
-               :width="'70%'"
-               :operation-type="operationType"
-               @close="closeDia"
-               @confirm="submitForm"
-               @cancel="closeDia">
+                :title="operationType === 'add' ? '鏂板閲囪喘鍙拌处椤甸潰' : '缂栬緫閲囪喘鍙拌处椤甸潰'"
+                :width="'70%'"
+                :operation-type="operationType"
+                @close="closeDia"
+                @confirm="submitForm"
+                @cancel="closeDia">
       <el-form :model="form"
                label-width="140px"
                label-position="top"
@@ -233,7 +239,8 @@
                 <el-option v-for="item in supplierList"
                            :key="item.id"
                            :label="item.supplierName"
-													 :value="item.id" >{{item.supplierName + '---' + item.supplierType}}</el-option>
+                           :value="item.id">{{ item.supplierName + '---' + item.supplierType }}
+                </el-option>
               </el-select>
             </el-form-item>
           </el-col>
@@ -295,58 +302,16 @@
             </el-form-item>
           </el-col>
         </el-row>
-        <el-row :gutter="30">
-          <el-col :span="24">
-            <el-form-item>
-              <template #label>
-                <div style="display: flex; align-items: center; justify-content: space-between; width: 100%;">
-                  <span>瀹℃壒浜洪�夋嫨锛�</span>
-                  <el-button type="primary" size="small" @click="addApproverNode" icon="Plus">鏂板鑺傜偣</el-button>
-                </div>
-              </template>
-              <div class="approver-nodes-container">
-                <div
-                  v-for="(node, index) in approverNodes"
-                  :key="node.id"
-                  class="approver-node-item"
-                >
-                  <div class="approver-node-header">
-                    <span class="approver-node-label">瀹℃壒鑺傜偣 {{ index + 1 }}</span>
-                    <el-button
-                      v-if="approverNodes.length > 1"
-                      type="danger"
-                      size="small"
-                      text
-                      @click="removeApproverNode(index)"
-                      icon="Delete"
-                    >鍒犻櫎</el-button>
-                  </div>
-                  <el-select
-                    v-model="node.userId"
-                    placeholder="璇烽�夋嫨瀹℃壒浜�"
-                    filterable
-                    style="width: 100%;"
-                  >
-                    <el-option
-                      v-for="user in userList"
-                      :key="user.userId"
-                      :label="user.nickName"
-                      :value="user.userId"
-                    />
-                  </el-select>
-                </div>
-              </div>
-            </el-form-item>
-          </el-col>
-        </el-row>
         <el-row>
           <el-form-item label="浜у搧淇℃伅锛�"
                         prop="entryDate">
             <el-button type="primary"
-                       @click="openProductForm('add')">娣诲姞</el-button>
+                       @click="openProductForm('add')">娣诲姞
+            </el-button>
             <el-button plain
                        type="danger"
-                       @click="deleteProduct">鍒犻櫎</el-button>
+                       @click="deleteProduct">鍒犻櫎
+            </el-button>
           </el-form-item>
           <div class="select-button-group"
                style="width: 500px; margin: 20px 0;"
@@ -370,11 +335,10 @@
                          :value="item.templateName">
                 <div style="display: flex; justify-content: space-between; align-items: center;">
                   <span>{{ item.templateName }}</span>
-                  <el-icon 
-                    v-if="item.id"
-                    class="delete-icon"
-                    @click.stop="handleDeleteTemplate(item)"
-                    style="cursor: pointer; color: #f56c6c; font-size: 14px; margin-left: 8px;">
+                  <el-icon v-if="item.id"
+                           class="delete-icon"
+                           @click.stop="handleDeleteTemplate(item)"
+                           style="cursor: pointer; color: #f56c6c; font-size: 14px; margin-left: 8px;">
                     <Delete />
                   </el-icon>
                 </div>
@@ -446,8 +410,8 @@
             <template #default="scope">
               <el-button link
                          type="primary"
-                         size="small"
-                         @click="openProductForm('edit', scope.row, scope.$index)">缂栬緫</el-button>
+                         @click="openProductForm('edit', scope.row, scope.$index)">缂栬緫
+              </el-button>
             </template>
           </el-table-column>
         </el-table>
@@ -467,52 +431,31 @@
           <el-col :span="24">
             <el-form-item label="闄勪欢鏉愭枡锛�"
                           prop="purchaseLedgerFiles">
-              <el-upload v-model:file-list="fileList"
-                         :action="upload.url"
-                         multiple
-                         ref="fileUpload"
-                         auto-upload
-                         :headers="upload.headers"
-                         :before-upload="handleBeforeUpload"
-                         :on-error="handleUploadError"
-                         :on-success="handleUploadSuccess"
-                         :on-remove="handleRemove">
-                <el-button type="primary">涓婁紶</el-button>
-                <template #tip>
-                  <div class="el-upload__tip">
-                    鏂囦欢鏍煎紡鏀寔
-                    doc锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�7z
-                  </div>
-                </template>
-              </el-upload>
+              <FileUpload v-model:file-list="fileList" />
             </el-form-item>
           </el-col>
         </el-row>
       </el-form>
     </FormDialog>
     <!-- 瀵煎叆寮圭獥 -->
-    <FormDialog
-      v-model="importUpload.open"
-      :title="importUpload.title"
-      :width="'600px'"
-      @close="importUpload.open = false"
-      @confirm="submitImportFile"
-      @cancel="importUpload.open = false"
-    >
-      <el-upload
-        ref="importUploadRef"
-        :limit="1"
-        accept=".xlsx,.xls"
-        :action="importUpload.url"
-        :headers="importUpload.headers"
-        :before-upload="importUpload.beforeUpload"
-        :on-success="importUpload.onSuccess"
-        :on-error="importUpload.onError"
-        :on-progress="importUpload.onProgress"
-        :on-change="importUpload.onChange"
-        :auto-upload="false"
-        drag
-      >
+    <FormDialog v-model="importUpload.open"
+                :title="importUpload.title"
+                :width="'600px'"
+                @close="importUpload.open = false"
+                @confirm="submitImportFile"
+                @cancel="importUpload.open = false">
+      <el-upload ref="importUploadRef"
+                 :limit="1"
+                 accept=".xlsx,.xls"
+                 :action="importUpload.url"
+                 :headers="importUpload.headers"
+                 :before-upload="importUpload.beforeUpload"
+                 :on-success="importUpload.onSuccess"
+                 :on-error="importUpload.onError"
+                 :on-progress="importUpload.onProgress"
+                 :on-change="importUpload.onChange"
+                 :auto-upload="false"
+                 drag>
         <i class="el-icon-upload"></i>
         <div class="el-upload__text">
           灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em>
@@ -520,18 +463,20 @@
         <template #tip>
           <div class="el-upload__tip">
             浠呮敮鎸� xls/xlsx锛屽ぇ灏忎笉瓒呰繃 10MB銆�
-            <el-button link type="primary" @click="downloadTemplate">涓嬭浇瀵煎叆妯℃澘</el-button>
+            <el-button link
+                       type="primary"
+                       @click="downloadTemplate">涓嬭浇瀵煎叆妯℃澘</el-button>
           </div>
         </template>
       </el-upload>
     </FormDialog>
     <FormDialog v-model="productFormVisible"
-               :title="productOperationType === 'add' ? '鏂板浜у搧' : '缂栬緫浜у搧'"
-               :width="'40%'"
-               :operation-type="productOperationType"
-               @close="closeProductDia"
-               @confirm="submitProduct"
-               @cancel="closeProductDia">
+                :title="productOperationType === 'add' ? '鏂板浜у搧' : '缂栬緫浜у搧'"
+                :width="'40%'"
+                :operation-type="productOperationType"
+                @close="closeProductDia"
+                @confirm="submitProduct"
+                @cancel="closeProductDia">
       <el-form :model="productForm"
                label-width="140px"
                label-position="top"
@@ -544,6 +489,7 @@
               <el-tree-select v-model="productForm.productId"
                               placeholder="璇烽�夋嫨"
                               clearable
+                              filterable
                               check-strictly
                               @change="getModels"
                               :data="productOptions"
@@ -558,6 +504,7 @@
                           prop="productModelId">
               <el-select v-model="productForm.productModelId"
                          placeholder="璇烽�夋嫨"
+                         filterable
                          clearable
                          @change="getProductModel">
                 <el-option v-for="item in modelOptions"
@@ -584,12 +531,10 @@
                          placeholder="璇烽�夋嫨"
                          clearable
                          @change="mathNum">
-                <el-option label="1"
-                           value="1" />
-                <el-option label="6"
-                           value="6" />
-                <el-option label="13"
-                           value="13" />
+                <el-option v-for="dict in tax_rate"
+                           :key="dict.value"
+                           :label="dict.label"
+                           :value="dict.value" />
               </el-select>
             </el-form-item>
           </el-col>
@@ -688,15 +633,16 @@
         </el-row>
       </el-form>
     </FormDialog>
-    <FileListDialog 
-      ref="fileListRef" 
-      v-model="fileListDialogVisible"
-      title="闄勪欢鍒楄〃"
-    />
+    <FileList v-if="fileListDialogVisible"
+              v-model:visible="fileListDialogVisible"
+              record-type="purchase_ledger"
+              :record-id="recordId" />
   </div>
 </template>
 
 <script setup>
+  import FormDialog from "@/components/Dialog/FormDialog.vue";
+  import FileListDialog from "@/components/Dialog/FileListDialog.vue";
   import { getToken } from "@/utils/auth";
   import pagination from "@/components/PIMTable/Pagination.vue";
   import {
@@ -710,10 +656,7 @@
   import { Search, Delete } from "@element-plus/icons-vue";
   import { ElMessageBox, ElMessage } from "element-plus";
   import { userListNoPage } from "@/api/system/user.js";
-  import FormDialog from '@/components/Dialog/FormDialog.vue';
-  import FileListDialog from '@/components/Dialog/FileListDialog.vue';
   import {
-    getSalesLedgerWithProducts,
     addOrUpdateSalesLedgerProduct,
     delProduct,
     delLedgerFile,
@@ -734,8 +677,12 @@
     delPurchaseTemplate,
   } from "@/api/procurementManagement/procurementLedger.js";
   import useFormData from "@/hooks/useFormData.js";
+  const FileList = defineAsyncComponent(() =>
+    import("@/components/Dialog/FileList.vue")
+  );
 
   const { proxy } = getCurrentInstance();
+  const { tax_rate } = proxy.useDict("tax_rate");
   const tableData = ref([]);
   const productData = ref([]);
   const selectedRows = ref([]);
@@ -746,6 +693,7 @@
   const salesContractList = ref([]);
   const supplierList = ref([]);
   const tableLoading = ref(false);
+  const fileListDialogVisible = ref(false);
   const page = reactive({
     current: 1,
     size: 100,
@@ -755,18 +703,9 @@
   import useUserStore from "@/store/modules/user";
   import { modelList, productTreeList } from "@/api/basicData/product.js";
   import dayjs from "dayjs";
+  import FileUpload from "@/components/AttachmentUpload/file/index.vue";
 
   const userStore = useUserStore();
-
-  // 瀹℃壒浜鸿妭鐐癸紙浠块攢鍞彴璐﹀彂璐у鎵逛汉锛�
-  const approverNodes = ref([{ id: 1, userId: null }]);
-  let nextApproverId = 2;
-  const addApproverNode = () => {
-    approverNodes.value.push({ id: nextApproverId++, userId: null });
-  };
-  const removeApproverNode = (index) => {
-    approverNodes.value.splice(index, 1);
-  };
 
   // 璁㈠崟瀹℃壒鐘舵�佹樉绀烘枃鏈�
   const approvalStatusText = {
@@ -777,12 +716,12 @@
   };
 
   // 鑾峰彇瀹℃壒鐘舵�佹爣绛剧被鍨�
-  const getApprovalStatusType = (status) => {
+  const getApprovalStatusType = status => {
     const typeMap = {
-      1: "info",      // 寰呭鏍� - 鐏拌壊
-      2: "warning",   // 瀹℃壒涓� - 姗欒壊
-      3: "success",   // 瀹℃壒閫氳繃 - 缁胯壊
-      4: "danger",    // 瀹℃壒澶辫触 - 绾㈣壊
+      1: "info", // 寰呭鏍� - 鐏拌壊
+      2: "warning", // 瀹℃壒涓� - 姗欒壊
+      3: "success", // 瀹℃壒閫氳繃 - 缁胯壊
+      4: "danger", // 瀹℃壒澶辫触 - 绾㈣壊
     };
     return typeMap[status] || "";
   };
@@ -864,7 +803,8 @@
         form.value.paymentMethod = matchedTemplate.paymentMethod;
       }
       // 妯℃澘鏁版嵁涓殑浜у搧瀛楁鏄� productList锛岄渶瑕佽浆鎹负 productData
-      productData.value = matchedTemplate.productList || matchedTemplate.productData || [];
+      productData.value =
+        matchedTemplate.productList || matchedTemplate.productData || [];
     } else {
       // 鏈尮閰嶅埌宸叉湁妯℃澘锛岃涓烘柊妯℃澘
       currentTemplateId.value = null;
@@ -912,14 +852,11 @@
       supplierId: "",
       paymentMethod: "",
       executionDate: "",
-      isChecked: true,
+      isChecked: false,
     },
     rules: {
       purchaseContractNumber: [
         { required: true, message: "璇疯緭鍏�", trigger: "blur" },
-      ],
-      approverId: [
-        { required: true, message: "璇烽�夋嫨瀹℃壒浜�", trigger: "change" },
       ],
       projectName: [
         { required: true, message: "璇疯緭鍏ラ」鐩悕绉�", trigger: "blur" },
@@ -960,7 +897,7 @@
       taxExclusiveTotalPrice: "",
       invoiceType: "",
       warnNum: "",
-      isChecked: true,
+      isChecked: false,
     },
     productRules: {
       productId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
@@ -998,7 +935,7 @@
     url: import.meta.env.VITE_APP_BASE_API + "/purchase/ledger/import",
     headers: { Authorization: "Bearer " + getToken() },
     isUploading: false,
-    beforeUpload: (file) => {
+    beforeUpload: file => {
       const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
       const isLt10M = file.size / 1024 / 1024 < 10;
       if (!isExcel) {
@@ -1047,7 +984,11 @@
 
   // 涓嬭浇瀵煎叆妯℃澘锛堝鍚庣璺緞涓嶅悓锛屽彲鍦ㄦ澶勮皟鏁达級
   const downloadTemplate = () => {
-    proxy.download("/purchase/ledger/exportTemplate", {}, "閲囪喘鍙拌处瀵煎叆妯℃澘.xlsx");
+    proxy.download(
+      "/purchase/ledger/exportTemplate",
+      {},
+      "閲囪喘鍙拌处瀵煎叆妯℃澘.xlsx"
+    );
   };
 
   const submitImportFile = () => {
@@ -1112,29 +1053,27 @@
     // 妫�鏌ユ槸鍚︽湁浜у搧鏁版嵁
     if (!productData.value || productData.value.length === 0) {
       ElMessage({
-        message: '璇峰厛娣诲姞浜у搧淇℃伅',
-        type: 'warning',
+        message: "璇峰厛娣诲姞浜у搧淇℃伅",
+        type: "warning",
       });
       return;
     }
 
     try {
-      // 鑾峰彇瀹℃壒浜篒D瀛楃涓�
-      const approveUserIds = approverNodes.value
-        .filter(node => node.userId)
-        .map(node => node.userId)
-        .join(",");
-      
       let params = {
         productData: proxy.HaveJson(productData.value),
         supplierId: form.value.supplierId,
         paymentMethod: form.value.paymentMethod,
         recorderId: form.value.recorderId,
         projectName: form.value.projectName,
-        approveUserIds: approveUserIds,
         templateName: templateName.value.trim(),
       };
-      console.log("template params ===>", params, "currentTemplateId:", currentTemplateId.value);
+      console.log(
+        "template params ===>",
+        params,
+        "currentTemplateId:",
+        currentTemplateId.value
+      );
 
       // 濡傛灉 currentTemplateId 鏈夊�硷紝璇存槑褰撳墠鏄�滅紪杈戝凡鏈夋ā鏉库�� 鈫� 璋冪敤鏇存柊鎺ュ彛
       // 鍚﹀垯涓衡�滄柊寤烘ā鏉库�� 鈫� 璋冪敤鏂板鎺ュ彛
@@ -1273,8 +1212,11 @@
         return;
       }
     }
-    
+
     await getTemplateList();
+    await userListNoPage().then(res => {
+      userList.value = res.data;
+    });
     operationType.value = type;
     form.value = {};
     productData.value = [];
@@ -1282,26 +1224,19 @@
     templateName.value = "";
     filterInputValue.value = "";
     isTemplateNameDuplicate.value = false;
-    // 閲嶇疆瀹℃壒浜鸿妭鐐癸紙榛樿涓�涓┖鑺傜偣锛�
-    approverNodes.value = [{ id: 1, userId: null }];
-    nextApproverId = 2;
     try {
       // 骞惰鍔犺浇鍩虹鏁版嵁
-      const [userRes, salesRes, supplierRes] = await Promise.all([
-        userListNoPage(),
+      const [salesRes, supplierRes] = await Promise.all([
         getSalesNo(),
         getOptions(),
       ]);
 
-      userList.value = userRes.data || [];
       salesContractList.value = salesRes || [];
       // 渚涘簲鍟嗚繃婊ゅ嚭isWhite=0 鐨勬暟鎹�
       supplierList.value = (supplierRes.data || []).filter(
         item => item.isWhite === 0
       );
 
-      // 璁剧疆榛樿鍊�
-      form.value.recorderId = userStore.id;
       form.value.entryDate = getCurrentDate();
 
       if (type === "add") {
@@ -1322,16 +1257,7 @@
           const purchaseRes = await getPurchaseById({ id: row.id, type: 2 });
           form.value = { ...purchaseRes };
           productData.value = purchaseRes.productData || [];
-          fileList.value = purchaseRes.salesLedgerFiles || [];
-          // 濡傛灉缂栬緫鏃舵湁瀹℃壒浜猴紝瑙f瀽瀹℃壒浜篒D瀛楃涓插苟璁剧疆鍒拌妭鐐逛腑
-          if (purchaseRes.approveUserIds) {
-            const approverIds = purchaseRes.approveUserIds.split(",");
-            approverNodes.value = approverIds.map((id, index) => ({
-              id: index + 1,
-              userId: Number(id)
-            }));
-            nextApproverId = approverIds.length + 1;
-          }
+          fileList.value = purchaseRes.storageBlobVOS || [];
         } catch (error) {
           console.error("鍔犺浇閲囪喘鍙拌处鏁版嵁澶辫触:", error);
           proxy.$modal.msgError("鍔犺浇鏁版嵁澶辫触");
@@ -1349,6 +1275,7 @@
       proxy.$modal.msgError("鍔犺浇鍩虹鏁版嵁澶辫触");
     }
   };
+
   // 涓婁紶鍓嶆牎妫�
   function handleBeforeUpload(file) {
     // 鏍℃鏂囦欢澶у皬
@@ -1359,11 +1286,13 @@
     proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
     return true;
   }
+
   // 涓婁紶澶辫触
   function handleUploadError(err) {
     proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
     proxy.$modal.closeLoading();
   }
+
   // 涓婁紶鎴愬姛鍥炶皟
   function handleUploadSuccess(res, file, uploadFiles) {
     proxy.$modal.closeLoading();
@@ -1375,6 +1304,7 @@
       proxy.$refs.fileUpload.handleRemove(file);
     }
   }
+
   // 绉婚櫎鏂囦欢
   async function handleRemove(file) {
     if (!file?.id) {
@@ -1396,18 +1326,11 @@
       }
     }
   }
+
   // 鎻愪氦琛ㄥ崟
   const submitForm = () => {
     proxy.$refs["formRef"].validate(valid => {
       if (valid) {
-        // 瀹℃壒浜哄繀濉牎楠岋紙鎵�鏈夎妭鐐归兘瑕侀�変汉锛�
-        const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
-        if (hasEmptyApprover) {
-          proxy.$modal.msgError("璇蜂负鎵�鏈夊鎵硅妭鐐归�夋嫨瀹℃壒浜猴紒");
-          return;
-        }
-        const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
-        
         if (productData.value.length > 0) {
           // 鏂板鏃讹紝闇�瑕佷粠姣忎釜浜у搧瀵硅薄涓垹闄� id 瀛楁
           let processedProductData = productData.value;
@@ -1422,13 +1345,8 @@
           proxy.$modal.msgWarning("璇锋坊鍔犱骇鍝佷俊鎭�");
           return;
         }
-        let tempFileIds = [];
-        if (fileList.value.length > 0) {
-          tempFileIds = fileList.value.map(item => item.tempId);
-        }
-        form.value.tempFileIds = tempFileIds;
+        form.value.storageBlobDTOS = fileList.value;
         form.value.type = 2;
-        form.value.approveUserIds = approveUserIds;
 
         // 濡傛灉salesLedgerId涓虹┖锛屽垯涓嶄紶閫抯alesContractNo
         if (!form.value.salesLedgerId) {
@@ -1452,9 +1370,6 @@
   // 鍏抽棴寮规
   const closeDia = () => {
     proxy.resetForm("formRef");
-    // 閲嶇疆瀹℃壒浜鸿妭鐐癸紙榛樿涓�涓┖鑺傜偣锛�
-    approverNodes.value = [{ id: 1, userId: null }];
-    nextApproverId = 2;
     dialogFormVisible.value = false;
   };
   // 鎵撳紑浜у搧寮规
@@ -1464,17 +1379,21 @@
     productForm.value = {};
     proxy.resetForm("productFormRef");
     productFormVisible.value = true;
-    
+
     // 鍏堣幏鍙栦骇鍝侀�夐」锛岀‘淇濇暟鎹姞杞藉畬鎴�
     await getProductOptions();
-    
+
     // 绛夊緟 DOM 鏇存柊
     await nextTick();
-    
+
+    if (type === "add") {
+      productForm.value.isChecked = false;
+    }
+
     if (type === "edit") {
       // 澶嶅埗琛屾暟鎹�
       productForm.value = { ...row };
-      
+
       // 濡傛灉鏄粠妯℃澘鍔犺浇鐨勬暟鎹紝鍙兘娌℃湁 productId 鍜� productModelId
       // 闇�瑕佹牴鎹� productCategory 鍜� specificationModel 鏉ユ煡鎵惧搴旂殑 ID
       if (!productForm.value.productId && productForm.value.productCategory) {
@@ -1485,25 +1404,34 @@
               return nodes[i].value;
             }
             if (nodes[i].children && nodes[i].children.length > 0) {
-              const found = findProductIdByCategory(nodes[i].children, categoryName);
+              const found = findProductIdByCategory(
+                nodes[i].children,
+                categoryName
+              );
               if (found) return found;
             }
           }
           return null;
         };
-        
-        const productId = findProductIdByCategory(productOptions.value, productForm.value.productCategory);
+
+        const productId = findProductIdByCategory(
+          productOptions.value,
+          productForm.value.productCategory
+        );
         if (productId) {
           productForm.value.productId = productId;
           // 鑾峰彇鍨嬪彿鍒楄〃骞剁瓑寰呭畬鎴�
           const modelRes = await modelList({ id: productId });
           modelOptions.value = modelRes;
-          
+
           // 绛夊緟 DOM 鏇存柊
           await nextTick();
-          
+
           // 鏍规嵁 specificationModel 鏌ユ壘 productModelId
-          if (productForm.value.specificationModel && modelOptions.value.length > 0) {
+          if (
+            productForm.value.specificationModel &&
+            modelOptions.value.length > 0
+          ) {
             const modelItem = modelOptions.value.find(
               item => item.model === productForm.value.specificationModel
             );
@@ -1517,15 +1445,15 @@
       } else if (productForm.value.productId) {
         // 濡傛灉鏈� productId锛屾甯稿姞杞藉瀷鍙峰垪琛�
         await getModels(productForm.value.productId);
-        
+
         // 绛夊緟 DOM 鏇存柊
         await nextTick();
-        
+
         if (productForm.value.productModelId) {
           getProductModel(productForm.value.productModelId);
         }
       }
-      
+
       // 鏈�鍚庡啀绛夊緟涓�娆� DOM 鏇存柊锛岀‘淇濇墍鏈夋暟鎹兘宸茶缃�
       await nextTick();
     }
@@ -1574,6 +1502,7 @@
     }
     return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
   };
+
   function convertIdToValue(data) {
     return data.map(item => {
       const { id, children, ...rest } = item;
@@ -1588,6 +1517,7 @@
       return newItem;
     });
   }
+
   // 鎻愪氦浜у搧琛ㄥ崟
   const submitProduct = () => {
     proxy.$refs["productFormRef"].validate(valid => {
@@ -1648,11 +1578,9 @@
           delProduct(ids).then(res => {
             proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
             closeProductDia();
-            getPurchaseById({ id: currentId.value, type: 2 }).then(
-              res => {
-                productData.value = res.productData;
-              }
-            );
+            getPurchaseById({ id: currentId.value, type: 2 }).then(res => {
+              productData.value = res.productData;
+            });
           });
         })
         .catch(() => {
@@ -1683,12 +1611,14 @@
   const handleDelete = () => {
     let ids = [];
     if (selectedRows.value.length > 0) {
-      ids = selectedRows.value.map(item => item.id);
+      ids = selectedRows.value
+        .filter(item => item.salesLedgerId === null)
+        .map(item => item.id);
     } else {
       proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
       return;
     }
-    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎", {
       confirmButtonText: "纭",
       cancelButtonText: "鍙栨秷",
       type: "warning",
@@ -1703,6 +1633,7 @@
         proxy.$modal.msg("宸插彇娑�");
       });
   };
+
   // 鑾峰彇褰撳墠鏃ユ湡骞舵牸寮忓寲涓� YYYY-MM-DD
   function getCurrentDate() {
     const today = new Date();
@@ -1711,6 +1642,7 @@
     const day = String(today.getDate()).padStart(2, "0");
     return `${year}-${month}-${day}`;
   }
+
   const mathNum = () => {
     if (!productForm.value.taxRate) {
       proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
@@ -1843,14 +1775,6 @@
     }
   };
 
-  const fileListRef = ref(null);
-  const fileListDialogVisible = ref(false);
-  const downLoadFile = row => {
-    if (fileListRef.value) {
-      fileListRef.value.open(row.salesLedgerFiles);
-    }
-  };
-
   // 鑾峰彇妯℃澘淇℃伅
   const getTemplateList = async () => {
     let res = await getPurchaseTemplateList();
@@ -1859,13 +1783,19 @@
     }
   };
 
+  // 鎵撳紑闄勪欢寮规
+  const openFileDialog = async row => {
+    recordId.value = row.id;
+    fileListDialogVisible.value = true;
+  };
+
   // 鍒犻櫎妯℃澘
-  const handleDeleteTemplate = async (item) => {
+  const handleDeleteTemplate = async item => {
     if (!item.id) {
       proxy.$modal.msgWarning("鏃犳硶鍒犻櫎璇ユā鏉�");
       return;
     }
-    
+
     try {
       await ElMessageBox.confirm(
         `纭畾瑕佸垹闄ゆā鏉�"${item.templateName}"鍚楋紵`,
@@ -1876,7 +1806,7 @@
           type: "warning",
         }
       );
-      
+
       const res = await delPurchaseTemplate([item.id]);
       if (res && res.code === 200) {
         ElMessage({
@@ -1918,18 +1848,21 @@
     opacity: 0.6;
     background-color: #f5f7fa;
   }
+
   .el-row {
     justify-content: space-between;
     align-items: center;
   }
+
   .no-arrow-select {
     --el-select-suffix-icon-color: transparent; /* 闅愯棌榛樿涓嬫媺绠ご */
   }
+
   .select-button-group {
     display: flex;
     align-items: center;
   }
-  
+
   // 瀹℃壒浜鸿妭鐐瑰鍣ㄦ牱寮�
   .approver-nodes-container {
     display: flex;
@@ -1940,7 +1873,7 @@
     border-radius: 4px;
     border: 1px solid #e4e7ed;
   }
-  
+
   .approver-node-item {
     flex: 0 0 calc(33.333% - 12px);
     min-width: 200px;
@@ -1949,41 +1882,42 @@
     border-radius: 4px;
     border: 1px solid #dcdfe6;
     transition: all 0.3s;
-    
+
     &:hover {
       border-color: #409eff;
       box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
     }
   }
-  
+
   .approver-node-header {
     display: flex;
     justify-content: space-between;
     align-items: center;
     margin-bottom: 8px;
   }
-  
+
   .approver-node-label {
     font-size: 13px;
     font-weight: 500;
     color: #606266;
   }
-  
+
   @media (max-width: 1200px) {
     .approver-node-item {
       flex: 0 0 calc(50% - 8px);
     }
   }
-  
+
   @media (max-width: 768px) {
     .approver-node-item {
       flex: 0 0 100%;
     }
   }
-  
+
   // 鍒犻櫎鍥炬爣鏍峰紡
   .delete-icon {
     transition: all 0.3s;
+
     &:hover {
       color: #f56c6c !important;
       transform: scale(1.2);
diff --git a/src/views/procurementManagement/procurementReport/index.vue b/src/views/procurementManagement/procurementReport/index.vue
index adf554d..26e682d 100644
--- a/src/views/procurementManagement/procurementReport/index.vue
+++ b/src/views/procurementManagement/procurementReport/index.vue
@@ -54,6 +54,10 @@
               <span class="stat-label">鍟嗗搧绉嶇被锛�</span>
               <span class="stat-value">{{ businessSummaryStats.productTypes }}</span>
             </div>
+            <div class="stat-item">
+              <span class="stat-label">閫�娆炬�婚锛�</span>
+              <span class="stat-value">{{ businessSummaryStats.returnAmount }}</span>
+            </div>
           </div>
         </div>
         
@@ -119,7 +123,23 @@
     }
   },
   {
-    label: '閲囪喘閲戦',
+    label: '閫�璐ф暟閲�',
+    prop: 'returnQuantity',
+    width: 120,
+    formatData: (val) => {
+      return val ? parseFloat(val).toLocaleString() : '0'
+    }
+  },
+  {
+    label: '閫�璐ч噾棰�',
+    prop: 'returnAmount',
+    width: 120,
+    formatData: (val) => {
+      return val ? `楼${parseFloat(val).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : '楼0.00'
+    }
+  },
+  {
+    label: '閫�娆惧偍閲�',
     prop: 'purchaseAmount',
     formatData: (val) => {
       return val ? `楼${parseFloat(val).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : '楼0.00'
@@ -239,7 +259,10 @@
         businessSummaryStats.value.totalAmount = businessSummaryData.value.reduce((sum, item) => {
           return sum + (parseFloat(item.purchaseAmount) || 0)
         }, 0)
-        businessSummaryStats.value.productTypes = new Set(businessSummaryData.value.map(item => item.productCategory)).size
+        businessSummaryStats.value.returnAmount = businessSummaryData.value.reduce((sum, item) => {
+          return sum + (parseFloat(item.returnAmount) || 0)
+        }, 0)
+        businessSummaryStats.value.productTypes = businessSummaryData.value.length
       } else {
         businessSummaryStats.value = {
           totalAmount: 0,
@@ -311,12 +334,6 @@
 </script>
 
 <style scoped>
-.app-container {
-  padding: 20px;
-  background-color: #f5f7fa;
-  min-height: 100vh;
-}
-
 .page-header {
   text-align: center;
   margin-bottom: 20px;
diff --git a/src/views/procurementManagement/purchaseReturnOrder/New.vue b/src/views/procurementManagement/purchaseReturnOrder/New.vue
index d2fe2a9..dffdeea 100644
--- a/src/views/procurementManagement/purchaseReturnOrder/New.vue
+++ b/src/views/procurementManagement/purchaseReturnOrder/New.vue
@@ -26,6 +26,7 @@
               v-model="formState.no"
               :placeholder="formState.isDefaultNo ? '浣跨敤绯荤粺缂栧彿' : '璇疯緭鍏ラ��鏂欏崟鍙�'"
               :disabled="formState.isDefaultNo"
+              style="width: 240px"
           >
             <template #append>
               <el-checkbox v-model="formState.isDefaultNo" size="large" @change="handleChangeIsDefaultNo" />
@@ -69,6 +70,7 @@
               v-model="formState.supplierId"
               placeholder="璇烽�夋嫨渚涘簲鍟�"
               style="width: 240px"
+							filterable
               @focus="fetchSupplierOptions"
               @change="handleChangeSupplierId"
           >
@@ -77,25 +79,6 @@
               :key="item.id"
               :label="item.supplierName"
               :value="item.id"
-            />
-          </el-select>
-        </el-form-item>
-
-        <el-form-item
-            label="椤圭洰"
-            prop="projectId"
-        >
-          <el-select
-              v-model="formState.projectId"
-              placeholder="璇烽�夋嫨椤圭洰"
-              style="width: 240px"
-              @focus="fetchProjectOptions"
-          >
-            <el-option
-                v-for="item in projectOptions"
-                :key="item.id"
-                :label="item.name"
-                :value="item.id"
             />
           </el-select>
         </el-form-item>
@@ -159,6 +142,7 @@
               :reserve-keyword="false"
               style="width: 240px"
               @focus="fetchUserOptions"
+              @change="formState.preparedUserName = userOptions.find(item => item.userId === formState.preparedUserId)?.nickName || ''"
           >
             <el-option
                 v-for="item in userOptions"
@@ -189,6 +173,7 @@
               style="width: 240px"
               :reserve-keyword="false"
               @focus="fetchUserOptions"
+              @change="formState.returnUserName = userOptions.find(item => item.userId === formState.returnUserId)?.nickName || ''"
           >
             <el-option
                 v-for="item in userOptions"
@@ -233,7 +218,7 @@
             label="澶囨敞锛�"
             prop="remark"
         >
-          <el-input v-model="formState.remark" type="textarea" placeholder="璇疯緭鍏ュ娉�"/>
+          <el-input style="width: 240px" v-model="formState.remark" :rows="1" type="textarea" placeholder="璇疯緭鍏ュ娉�"/>
         </el-form-item>
 
         <div style="margin: 20px 0;">
@@ -264,7 +249,10 @@
                                width="70" />
               <el-table-column label="鏁伴噺"
                                prop="quantity"
-                               width="70" />
+                               width="100" />
+                               <el-table-column label="鍙��璐ф暟閲�"
+                               prop="availableQuality"
+                               width="130" />
               <el-table-column label="閫�璐ф暟閲�"
                                prop="returnQuantity"
                                width="180">
@@ -272,8 +260,10 @@
                   <el-input-number v-model="scope.row.returnQuantity"
                             controls-position="right"
                             :step="1"
-                            :min="1"
-                            :max="scope.row.quantity"
+                            :min="0"
+                            :max="getReturnQtyMax(scope.row)"
+                            :disabled="getReturnQtyMax(scope.row) <= 0"
+                            @change="syncReturnTotal(scope.row)"
                             required
                             placeholder="璇疯緭鍏ラ��璐ф暟閲�" />
                 </template>
@@ -289,14 +279,13 @@
                                prop="taxInclusiveUnitPrice"
                                :formatter="formattedNumber"
                                width="150" />
-              <el-table-column label="鍚◣鎬讳环(鍏�)"
+              <el-table-column label="閫�璐ф�讳环(鍏�)"
                                prop="taxInclusiveTotalPrice"
-                               :formatter="formattedNumber"
-                               width="150" />
-              <el-table-column label="涓嶅惈绋庢�讳环(鍏�)"
-                               prop="taxExclusiveTotalPrice"
-                               :formatter="formattedNumber"
-                               width="150" />
+                               width="180">
+                <template #default="scope">
+                  {{ formatAmount(getReturnTotal(scope.row)) || '--' }}
+                </template>
+              </el-table-column>
               <el-table-column label="鏄惁璐ㄦ"
                                prop="isChecked"
                                width="150">
@@ -346,26 +335,54 @@
             label="鏁村崟鎶樻墸鐜囷細"
             prop="totalDiscountAmount"
         >
-          <el-input-number v-model="formState.totalDiscountRate"
+          <el-input v-model="formState.totalDiscountRate"
                            controls-position="right"
                            :step="0.01"
                            :precision="2"
                            style="width: 100%;"
-                           placeholder="璇疯緭鍏ユ暣鍗曟姌鎵g巼"/>
+                           @change="totalDiscount"
+                           placeholder="璇疯緭鍏ユ暣鍗曟姌鎵g巼">
+            <template #append>
+                %
+            </template>
+          </el-input>
         </el-form-item>
 
         <el-form-item
             label="鎴愪氦閲戦锛�"
             prop="totalAmount"
+            :rules="[
+              {
+                required: true,
+                message: '璇疯緭鍏ユ垚浜ら噾棰�',
+                trigger: 'change',
+              }
+            ]"
         >
           <el-input-number v-model="formState.totalAmount"
                            controls-position="right"
                            :step="0.01"
                            :precision="2"
                            style="width: 100%;"
-                           @change="handleChangeTotalAmount"
                            placeholder="璇疯緭鍏ユ垚浜ら噾棰�"/>
         </el-form-item>
+        <el-form-item label="鏀舵鏂瑰紡" prop="incomeType" :rules="[
+              {
+                required: true,
+                message: '璇烽�夋嫨鏀舵鏂瑰紡',
+                trigger: 'change',
+              }
+            ]">
+        <el-select
+          style="width: 240px;"
+          v-model="formState.incomeType"
+          placeholder="璇烽�夋嫨"
+          clearable
+          
+        >
+          <el-option :label="item.label" :value="item.value" v-for="(item,index) in payment_methods" :key="index" />
+        </el-select>
+      </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
@@ -386,11 +403,14 @@
 </template>
 
 <script setup>
-import {ref, computed, getCurrentInstance} from "vue";
+import {ref, computed, getCurrentInstance, watch, defineAsyncComponent} from "vue";
 import {createPurchaseReturnOrder} from "@/api/procurementManagement/purchase_return_order.js";
 import {getOptions, purchaseList} from "@/api/procurementManagement/procurementLedger.js";
 import {userListNoPageByTenantId} from "@/api/system/user.js";
 const ProductList = defineAsyncComponent(() => import("@/views/procurementManagement/purchaseReturnOrder/ProductList.vue"));
+  import {
+    productList,
+  } from "@/api/procurementManagement/procurementLedger.js";
 const props = defineProps({
   visible: {
     type: Boolean,
@@ -398,6 +418,24 @@
   }
 });
 let { proxy } = getCurrentInstance()
+const payment_methods  = [
+    {
+        "label": "鐜伴噾",
+        "value": "0",
+    },
+    {
+        "label": "鏀エ",
+        "value": "1",
+    },
+    {
+        "label": "閾惰杞处",
+        "value": "2",
+    },
+    {
+        "label": "鍏朵粬",
+        "value": "3",
+    },
+]
 const emit = defineEmits(['update:visible', 'completed']);
 
 // 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
@@ -405,6 +443,7 @@
   no: '',
   isDefaultNo: true,
   returnType: 0,
+  incomeType: undefined,
   remark: '',
   supplierId: undefined,
   projectId: undefined,
@@ -420,8 +459,6 @@
 });
 // 渚涘簲鍟嗛�夐」
 const supplierOptions = ref([])
-// 椤圭洰閫夐」
-const projectOptions = ref([])
 // 椤圭洰闃舵閫夐」
 const projectStageOptions = ref([
   {
@@ -465,6 +502,64 @@
   return parseFloat(cellValue).toFixed(2);
 };
 
+const formatAmount = (value) => {
+  if (value === null || value === undefined || value === '') {
+    return '--'
+  }
+  const num = Number(value)
+  if (Number.isNaN(num)) {
+    return '--'
+  }
+  return num.toFixed(2)
+}
+
+const toNumber = (val) => {
+  const num = Number(val)
+  return Number.isNaN(num) ? 0 : num
+}
+
+const getReturnTotal = (row) => {
+  const qty = toNumber(row?.returnQuantity)
+  const unitPrice = toNumber(row?.taxInclusiveUnitPrice)
+  const total = qty * unitPrice
+  return Number(total.toFixed(2))
+}
+
+const syncReturnTotal = (row) => {
+  if (!row) {
+    return
+  }
+  row.taxInclusiveTotalPrice = getReturnTotal(row)
+}
+
+const getBaseAmount = () => {
+  const rows = formState.value.purchaseReturnOrderProductsDtos || []
+  return rows.reduce((sum, item) => {
+    return sum + toNumber(item.taxInclusiveTotalPrice)
+  }, 0)
+}
+
+// 鍚屾鎶樻墸棰�
+const totalDiscount = () => {
+  const discountRate = toNumber(formState.value.totalDiscountRate)
+  if (discountRate < 0 || discountRate > 100) {
+    proxy.$modal.msgError("璇疯緭鍏�0-100涔嬮棿鐨勬姌鎵g巼")
+    return
+  }
+  const baseAmount = getBaseAmount()
+  // 鎶樻墸棰� = 浜у搧閫�璐ф�讳环鍚堣 * 鎶樻墸鐜�
+  formState.value.totalDiscountAmount = Number((baseAmount * (discountRate / 100)).toFixed(2))
+  syncTotalAmount()
+}
+
+const getReturnQtyMax = (row) => {
+  const max = Number(row?.availableQuality)
+  if (Number.isNaN(max) || max < 0) {
+    return 0
+  }
+  return max
+}
+
 const closeModal = () => {
   isShow.value = false;
 };
@@ -474,6 +569,7 @@
       param,
       [
         "quantity",
+        "availableQuality",
         "returnQuantity",
         "taxInclusiveUnitPrice",
         "taxInclusiveTotalPrice",
@@ -482,16 +578,48 @@
       {
         quantity: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
         returnQuantity: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
+        availableQuality: { noDecimal: true }, // 涓嶄繚鐣欏皬鏁�
       }
   );
 };
 
 const handleChangeTotalDiscountAmount= () => {
-  formState.value.totalAmount = formState.value.totalDiscountAmount * -1
+  const discountAmount = toNumber(formState.value.totalDiscountAmount)
+  if (discountAmount < 0) {
+    proxy.$modal.msgError("鏁村崟鎶樻墸棰濅笉鑳藉皬浜�0")
+    formState.value.totalDiscountAmount = 0
+  }
+
+  const baseAmount = getBaseAmount()
+  const normalizedAmount = toNumber(formState.value.totalDiscountAmount)
+  if (baseAmount <= 0) {
+    formState.value.totalDiscountRate = 0
+    syncTotalAmount()
+    return
+  }
+
+  if (normalizedAmount > baseAmount) {
+    proxy.$modal.msgError("鏁村崟鎶樻墸棰濅笉鑳藉ぇ浜庝骇鍝侀��璐ф�讳环鍚堣")
+    formState.value.totalDiscountAmount = Number(baseAmount.toFixed(2))
+  }
+
+  const discountRate = (toNumber(formState.value.totalDiscountAmount) / baseAmount) * 100
+  formState.value.totalDiscountRate = Number(discountRate.toFixed(2))
+  syncTotalAmount()
 }
 
-const handleChangeTotalAmount= () => {
-  formState.value.totalDiscountAmount = formState.value.totalAmount * -1
+const resetFeeInfo = () => {
+  formState.value.totalDiscountAmount = 0
+  formState.value.totalDiscountRate = undefined
+  formState.value.totalAmount = 0
+  formState.value.incomeType = undefined
+}
+
+const syncTotalAmount = () => {
+  const baseAmount = getBaseAmount()
+  const discount = toNumber(formState.value.totalDiscountAmount)
+  // 鎴愪氦閲戦 = 浜у搧閫�璐ф�讳环鍚堣 - 鎶樻墸棰�
+  formState.value.totalAmount = Number((baseAmount - discount).toFixed(2))
 }
 
 // 鑾峰彇渚涘簲鍟嗛�夐」
@@ -504,13 +632,6 @@
   });
 }
 
-// 鑾峰彇椤圭洰閫夐」
-const fetchProjectOptions = () => {
-  if (projectOptions.value.length > 0) {
-    return
-  }
-  // todo 椤圭洰閫夐」
-}
 
 // 鑾峰彇鐢ㄦ埛閫夐」
 const fetchUserOptions = () => {
@@ -525,6 +646,7 @@
 // 澶勭悊鏀瑰彉渚涘簲鍟嗘暟鎹�
 const handleChangeSupplierId = () => {
   formState.value.purchaseLedgerId = undefined
+  formState.value.supplierName = supplierOptions.value.find(item => item.id === formState.value.supplierId)?.supplierName || ''
   fetchPurchaseLedgerOptions()
 }
 
@@ -532,15 +654,27 @@
 const fetchPurchaseLedgerOptions = () => {
   purchaseLedgerOptions.value = []
   if (formState.value.supplierId) {
-    purchaseList({supplierId: formState.value.supplierId}).then((res) => {
+    purchaseList({supplierId: formState.value.supplierId,approvalStatus:3}).then((res) => {
       purchaseLedgerOptions.value = res.rows;
     });
   }
 }
 
 // 澶勭悊鏀瑰彉閲囪喘鍙拌处鏁版嵁
-const handleChangePurchaseLedgerId = () => {
-  formState.value.purchaseReturnOrderProductsDtos = []
+const handleChangePurchaseLedgerId = async () => {
+  resetFeeInfo()
+  if (!formState.value.purchaseLedgerId) {
+    formState.value.purchaseReturnOrderProductsDtos = []
+    return
+  }
+  const res = await productList({ salesLedgerId: formState.value.purchaseLedgerId, type: 2 });
+  formState.value.purchaseReturnOrderProductsDtos = res.data.map(item => ({
+    ...item,
+    returnQuantity: undefined,
+    taxInclusiveTotalPrice: 0,
+    salesLedgerProductId: item.id,
+  }))
+  syncTotalAmount()
 }
 
 // 澶勭悊鏀瑰彉鏄惁榛樿缂栧彿
@@ -556,23 +690,50 @@
   const newProducts = selectedRows.filter(item => !existingIds.has(item.id)).map(item => ({
     ...item,
     returnQuantity: undefined,
+    taxInclusiveTotalPrice: 0,
     salesLedgerProductId: item.id,
   }));
   formState.value.purchaseReturnOrderProductsDtos.push(...newProducts);
+  syncTotalAmount()
 }
 
 // 鍒犻櫎鍗曢」浜у搧
 const delProduct = (index) => {
   formState.value.purchaseReturnOrderProductsDtos.splice(index, 1)
+  syncTotalAmount()
 }
 
 // 鎻愪氦琛ㄥ崟
 const handleSubmit = () => {
-  // 楠岃瘉閫�璐ф暟閲�
-  const hasEmptyReturnQuantity = formState.value.purchaseReturnOrderProductsDtos.some(item => !item.returnQuantity || item.returnQuantity <= 0);
-  if (hasEmptyReturnQuantity) {
-    proxy.$modal.msgError("璇蜂负鎵�鏈変骇鍝佸~鍐欓��璐ф暟閲�");
-    return;
+  const productList = formState.value.purchaseReturnOrderProductsDtos || []
+
+  productList.forEach(syncReturnTotal)
+
+  if (productList.length === 0) {
+    proxy.$modal.msgError("璇峰厛閫夋嫨浜у搧")
+    return
+  }
+
+  // 閫愯鏍¢獙閫�璐ф暟閲忥細浠绘剰涓�琛屾湭濉�/闈炴硶/瓒呴檺閮戒笉鍏佽鎻愪氦
+  const invalidRowIndex = productList.findIndex((item) => {
+    const qty = Number(item.returnQuantity)
+    const maxQty = Number(item.availableQuality)
+
+    if (item.returnQuantity === null || item.returnQuantity === undefined || item.returnQuantity === "") {
+      return true
+    }
+    if (Number.isNaN(qty) || qty <= 0) {
+      return true
+    }
+    if (!Number.isNaN(maxQty) && maxQty > 0 && qty > maxQty) {
+      return true
+    }
+    return false
+  })
+
+  if (invalidRowIndex !== -1) {
+    proxy.$modal.msgError(`绗�${invalidRowIndex + 1}琛岄��璐ф暟閲忔湭濉啓鎴栦笉鍚堟硶`)
+    return
   }
 
   proxy.$refs["formRef"].validate(valid => {
@@ -588,6 +749,15 @@
   })
 };
 
+watch(
+  () => formState.value.purchaseReturnOrderProductsDtos,
+  (rows) => {
+    (rows || []).forEach(syncReturnTotal)
+    syncTotalAmount()
+  },
+  { deep: true }
+)
+
 defineExpose({
   closeModal,
   handleSubmit,
diff --git a/src/views/procurementManagement/purchaseReturnOrder/index.vue b/src/views/procurementManagement/purchaseReturnOrder/index.vue
index 4e91e59..cd2c7f4 100644
--- a/src/views/procurementManagement/purchaseReturnOrder/index.vue
+++ b/src/views/procurementManagement/purchaseReturnOrder/index.vue
@@ -23,37 +23,98 @@
     </div>
 
     <div class="table_list">
-      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange" :row-key="row => row.id" style="width: 100%" height="calc(100vh - 18.5em)">
-        <el-table-column align="center" type="selection" width="55" />
-        <el-table-column align="center" label="搴忓彿" type="index" width="60" />
-        <el-table-column label="閫�鏂欏崟鍙�" prop="no" show-overflow-tooltip />
-        <el-table-column label="閫�璐ф柟寮�" prop="returnType" show-overflow-tooltip />
-        <el-table-column label="渚涘簲鍟嗗悕绉�" prop="supplierName" show-overflow-tooltip />
-        <el-table-column label="鍏宠仈鍗曞彿" prop="purchaseContractNumber" show-overflow-tooltip />
-        <el-table-column label="閫�鏂欎汉" prop="returnUserName" show-overflow-tooltip />
-        <el-table-column label="澶囨敞" prop="remark"  show-overflow-tooltip />
-        <el-table-column label="鍒涘缓浜�" prop="createUserName"  show-overflow-tooltip />
-        <el-table-column label="鍒涘缓鏃堕棿" prop="createTime" show-overflow-tooltip />
-        <el-table-column label="鏈�杩戞洿鏂版椂闂�" prop="updateTime" show-overflow-tooltip />
-        <el-table-column fixed="right" label="鎿嶄綔" min-width="60" align="center">
-          <template #default="scope">
-            <el-button link type="primary" size="small">璇︽儏</el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
-                  :page="page.current" :limit="page.size" @pagination="paginationChange" />
+      <PIMTable
+        rowKey="id"
+        :column="tableColumn"
+        :tableData="tableData"
+        :tableLoading="tableLoading"
+        :isSelection="true"
+        :page="page"
+        :height="'calc(100vh - 18.5em)'"
+        @selection-change="handleSelectionChange"
+        @pagination="paginationChange"
+      >
+        <template #operation="{ row }">
+          <el-button link type="primary" size="small" style="color: #67C23A" @click="handleDetail(row)">璇︽儏</el-button>
+          <el-button link size="small" @click="handleDelete(row)">鍒犻櫎</el-button>
+        </template>
+      </PIMTable>
     </div>
     <new v-if="isShowNewModal"
          v-model:visible="isShowNewModal"
          @completed="handleQuery" />
+
+    <el-dialog
+      v-model="detailVisible"
+      title="閲囪喘閫�璐ц鎯�"
+      width="1200"
+      destroy-on-close
+    >
+      <div v-loading="detailLoading">
+        <el-descriptions :column="3" border>
+          <el-descriptions-item label="閫�鏂欏崟鍙�">{{ detailData.no || '--' }}</el-descriptions-item>
+          <el-descriptions-item label="閫�璐ф柟寮�">{{ getReturnTypeLabel(detailData.returnType) }}</el-descriptions-item>
+          <el-descriptions-item label="渚涘簲鍟嗗悕绉�">{{ detailData.supplierName || '--' }}</el-descriptions-item>
+          <el-descriptions-item label="椤圭洰闃舵">{{ getProjectPhaseLabel(detailData.projectPhase) }}</el-descriptions-item>
+          <el-descriptions-item label="鍏宠仈鍗曞彿">{{ detailData.purchaseContractNumber || '--' }}</el-descriptions-item>
+          <el-descriptions-item label="鍒朵綔鏃ユ湡">{{ detailData.preparedAt || '--' }}</el-descriptions-item>
+          <el-descriptions-item label="鍒跺崟浜�">{{ detailData.preparedUserName || '--' }}</el-descriptions-item>
+          <el-descriptions-item label="閫�鏂欎汉">{{ detailData.returnUserName || '--' }}</el-descriptions-item>
+          <el-descriptions-item label="鏁村崟鎶樻墸棰�">{{ formatAmount(detailData.totalDiscountAmount) }}</el-descriptions-item>
+          <el-descriptions-item label="鏁村崟鎶樻墸鐜�">{{ detailData.totalDiscountRate ?? '--' }}</el-descriptions-item>
+          <el-descriptions-item label="鎴愪氦閲戦">{{ formatAmount(detailData.totalAmount) }}</el-descriptions-item>
+          <el-descriptions-item label="鍒涘缓浜�">{{ detailData.createUserName || '--' }}</el-descriptions-item>
+          <el-descriptions-item label="鍒涘缓鏃堕棿">{{ detailData.createTime || '--' }}</el-descriptions-item>
+          <el-descriptions-item label="鏈�杩戞洿鏂版椂闂�">{{ detailData.updateTime || '--' }}</el-descriptions-item>
+          <el-descriptions-item label="澶囨敞" :span="3">{{ detailData.remark || '--' }}</el-descriptions-item>
+        </el-descriptions>
+
+        <el-divider content-position="left">浜у搧鍒楄〃</el-divider>
+
+        <el-table
+          :data="detailProducts"
+          border
+          max-height="420"
+          style="width: 100%"
+        >
+          <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+          <el-table-column label="浜у搧澶х被" prop="productCategory" min-width="120" show-overflow-tooltip />
+          <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" min-width="140" show-overflow-tooltip />
+          <el-table-column label="鍗曚綅" prop="unit" width="80" />
+          <el-table-column label="鏁伴噺" prop="quantity" width="80" />
+          <el-table-column label="閫�璐ф暟閲�" prop="returnQuantity" width="100" />
+          <el-table-column label="搴撳瓨棰勮鏁伴噺" prop="warnNum" width="120" />
+          <el-table-column label="绋庣巼(%)" prop="taxRate" width="90" />
+          <el-table-column label="鍚◣鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" width="130">
+            <template #default="scope">{{ formatAmount(scope.row.taxInclusiveUnitPrice) }}</template>
+          </el-table-column>
+          <el-table-column label="鍚◣鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" width="130">
+            <template #default="scope">{{ formatAmount(scope.row.taxInclusiveTotalPrice) }}</template>
+          </el-table-column>
+          <el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" width="140">
+            <template #default="scope">{{ formatAmount(scope.row.taxExclusiveTotalPrice) }}</template>
+          </el-table-column>
+          <el-table-column label="鏄惁璐ㄦ" prop="isChecked" width="100" align="center">
+            <template #default="scope">
+              <el-tag :type="scope.row.isChecked ? 'success' : 'info'">
+                {{ scope.row.isChecked ? '鏄�' : '鍚�' }}
+              </el-tag>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      <template #footer>
+        <el-button @click="detailVisible = false">鍏抽棴</el-button>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-import pagination from '@/components/PIMTable/Pagination.vue'
-import { ref, reactive, toRefs, onMounted } from 'vue'
-import {findPurchaseReturnOrderListPage} from "@/api/procurementManagement/purchase_return_order.js";
+import PIMTable from '@/components/PIMTable/PIMTable.vue'
+import { ref, reactive, toRefs, onMounted, defineAsyncComponent, getCurrentInstance } from 'vue'
+const { proxy } = getCurrentInstance()
+import {findPurchaseReturnOrderListPage, getPurchaseReturnOrderDetail, deletePurchaseReturnOrder} from "@/api/procurementManagement/purchase_return_order.js";
 const New = defineAsyncComponent(() => import("@/views/procurementManagement/purchaseReturnOrder/New.vue"));
 const tableData = ref([])
 const selectedRows = ref([])
@@ -61,10 +122,122 @@
 const page = reactive({
   current: 1,
   size: 100,
+  total: 0,
 })
-const total = ref(0)
+const detailVisible = ref(false)
+const detailLoading = ref(false)
+const detailData = ref({})
+const detailProducts = ref([])
 // 鏄惁鏄剧ず鏂板寮规
 const isShowNewModal = ref(false)
+const returnTypeOptions = [
+  { label: '閫�璐ч��娆�', value: 0 },
+  { label: '鎷掓敹', value: 1 },
+]
+const projectPhaseOptions = [
+  { label: '绔嬮」', value: 0 },
+  { label: '璁捐', value: 1 },
+  { label: '閲囪喘', value: 2 },
+  { label: '鐢熶骇', value: 3 },
+  { label: '鍑鸿揣', value: 4 },
+]
+const tableColumn = ref([
+  {
+    label: '閫�鏂欏崟鍙�',
+    prop: 'no',
+  },
+  {
+    label: '閫�璐ф柟寮�',
+    prop: 'returnType',
+    formatData: (val) => returnTypeOptions.find(item => item.value === val)?.label || '--',
+  },
+  {
+    label: '渚涘簲鍟嗗悕绉�',
+    prop: 'supplierName',
+    width: 180,
+  },
+  {
+    label: '椤圭洰闃舵',
+    prop: 'projectPhase',
+    width: 100,
+    formatData: (val) => projectPhaseOptions.find(item => String(item.value) === String(val))?.label || '--',
+  },
+  {
+    label: '鍏宠仈鍗曞彿',
+    prop: 'purchaseContractNumber',
+    width: 160,
+  },
+  {
+    label: '鍒朵綔鏃ユ湡',
+    prop: 'preparedAt',
+    width: 130,
+  },
+  {
+    label: '鍒跺崟浜�',
+    prop: 'preparedUserName',
+    width: 110,
+  },
+  {
+    label: '閫�鏂欎汉',
+    prop: 'returnUserName',
+    width: 110,
+  },
+  
+  {
+    label: '鏁村崟鎶樻墸棰�',
+    prop: 'totalDiscountAmount',
+    width: 120,
+  },
+  {
+    label: '鏁村崟鎶樻墸鐜�',
+    prop: 'totalDiscountRate',
+    width: 120,
+  },
+  {
+    label: '鎴愪氦閲戦',
+    prop: 'totalAmount',
+    width: 120,
+  },
+  {
+    label: '鍒涘缓浜�',
+    prop: 'createUserName',
+    width: 110,
+  },
+  {
+    label: '鍒涘缓鏃堕棿',
+    prop: 'createTime',
+    width: 170,
+  },
+  {
+    label: '鏈�杩戞洿鏂版椂闂�',
+    prop: 'updateTime',
+    width: 170,
+  },
+  {
+    label: '澶囨敞',
+    prop: 'remark',
+    width: 180,
+  },
+  {
+    dataType: "action",
+    width: 120,
+      label: "鎿嶄綔",
+      align: "center",
+      fixed: "right",
+    operation: [
+      {
+				name: "璇︽儏",
+				type: "text",
+				clickFun: row => {handleDetail(row);},
+			},
+      {
+        name: "鍒犻櫎",
+        clickFun: row => {handleDelete(row)},
+      },
+  ],
+  },
+  
+])
 const data = reactive({
   searchForm: {
     no: '',
@@ -79,6 +252,48 @@
   getList()
 }
 
+// 鍒犻櫎鎿嶄綔
+const handleDelete = (row) => {
+  console.log('鍒犻櫎琛屾暟鎹細', row)
+  proxy?.$modal?.confirm('纭畾瑕佸垹闄ゅ悧锛熷垹闄ゅ皢鏃犳硶鎭㈠').then(() => {
+    // 杩欓噷璋冪敤鍒犻櫎鎺ュ彛锛屼紶鍏� row.id
+    deletePurchaseReturnOrder(row.id).then(() => {
+      proxy?.$modal?.msgSuccess?.("鍒犻櫎鎴愬姛");
+      getList()
+    }).catch(() => {
+      proxy?.$modal?.msgError?.('鍒犻櫎澶辫触')
+    })
+  }).catch(() => {
+    // 鍙栨秷鍒犻櫎
+    proxy?.$modal?.msgInfo?.('宸插彇娑堝垹闄�')
+
+  })
+}
+// 鏌ョ湅璇︽儏
+const handleDetail = (row) => {
+  if (!row?.id) {
+    proxy?.$modal?.msgWarning?.('鏈幏鍙栧埌鍗曟嵁ID')
+    return
+  }
+  detailVisible.value = true
+  detailLoading.value = true
+  getPurchaseReturnOrderDetail(row.id).then(res => {
+    const payload = res?.data || {}
+    detailData.value = payload
+    // 鎷兼帴杩炰釜瀵硅薄鎴愪竴涓璞★紝鏂逛究灞曠ず item 鍜� item.salesLedgerProduct 閲岀殑瀛楁
+    
+    
+    detailProducts.value =
+      payload.purchaseReturnOrderProductsDetailVoList.map(item => ({ ...item, ...item.salesLedgerProduct })) ||
+      []
+  }).catch(() => {
+    proxy?.$modal?.msgError?.('鑾峰彇璇︽儏澶辫触')
+  }).finally(() => {
+    detailLoading.value = false
+  })
+}
+
+
 const paginationChange = (obj) => {
   page.current = obj.page;
   page.size = obj.limit;
@@ -90,7 +305,7 @@
   findPurchaseReturnOrderListPage({ ...searchForm.value, ...page }).then(res => {
     tableLoading.value = false
     tableData.value = res.data.records
-    total.value = res.data.total
+    page.total = res.data.total
   }).catch(() => {
     tableLoading.value = false
   })
@@ -102,8 +317,32 @@
   selectedRows.value = selection.filter(item => item.id);
 }
 
+const getReturnTypeLabel = (value) => {
+  return returnTypeOptions.find(item => String(item.value) === String(value))?.label || '--'
+}
+
+const getProjectPhaseLabel = (value) => {
+  return projectPhaseOptions.find(item => String(item.value) === String(value))?.label || '--'
+}
+
+const formatAmount = (value) => {
+  if (value === null || value === undefined || value === '') {
+    return '--'
+  }
+  const num = Number(value)
+  if (Number.isNaN(num)) {
+    return value
+  }
+  return num.toFixed(2)
+}
+
 onMounted(() => {
   getList()
 })
 </script>
+<style scoped>
+.table_list {
+	margin-top: unset;
+}
+</style>
 
diff --git a/src/views/productManagement/productIdentifier/index.vue b/src/views/productManagement/productIdentifier/index.vue
index 519f745..94359e4 100644
--- a/src/views/productManagement/productIdentifier/index.vue
+++ b/src/views/productManagement/productIdentifier/index.vue
@@ -101,7 +101,7 @@
           <template #default="scope">
             <el-button link
                        type="primary"
-                       @click="handleView(scope.row)">鏌ョ湅</el-button>
+                       @click="handleView(scope.row)" style="color: #67C23A">鏌ョ湅</el-button>
             <el-button link
                        type="primary"
                        @click="handleEdit(scope.row)">缂栬緫</el-button>
diff --git a/src/views/productionManagement/processRoute/New.vue b/src/views/productionManagement/processRoute/New.vue
index 62c6873..b7f4f26 100644
--- a/src/views/productionManagement/processRoute/New.vue
+++ b/src/views/productionManagement/processRoute/New.vue
@@ -1,71 +1,63 @@
 <template>
   <div>
-    <el-dialog
-        v-model="isShow"
-        title="鏂板宸ヨ壓璺嚎"
-        width="400"
-        @close="closeModal"
-    >
-      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
-        <el-form-item
-            label="浜у搧鍚嶇О"
-            prop="productModelId"
-            :rules="[
+    <el-dialog v-model="isShow"
+               title="鏂板宸ヨ壓璺嚎"
+               width="400"
+               @close="closeModal">
+      <el-form label-width="140px"
+               :model="formState"
+               label-position="top"
+               ref="formRef">
+        <el-form-item label="浜у搧鍚嶇О"
+                      prop="productModelId"
+                      :rules="[
                 {
                 required: true,
                 message: '璇烽�夋嫨浜у搧',
                 trigger: 'change',
               }
-            ]"
-        >
-          <el-button type="primary" @click="showProductSelectDialog = true">
+            ]">
+          <el-button type="primary"
+                     @click="showProductSelectDialog = true">
             {{ formState.productName && formState.productModelName 
               ? `${formState.productName} - ${formState.productModelName}` 
               : '閫夋嫨浜у搧' }}
           </el-button>
         </el-form-item>
-
-        <el-form-item
-            label="BOM"
-            prop="bomId"
-            :rules="[
+        <el-form-item label="BOM"
+                      prop="bomId"
+                      :rules="[
                 {
                 required: true,
                 message: '璇烽�夋嫨BOM',
                 trigger: 'change',
               }
-            ]"
-        >
-          <el-select
-              v-model="formState.bomId"
-              placeholder="璇烽�夋嫨BOM"
-              clearable
-              :disabled="!formState.productModelId || bomOptions.length === 0"
-              style="width: 100%"
-          >
-            <el-option
-                v-for="item in bomOptions"
-                :key="item.id"
-                :label="item.bomNo || `BOM-${item.id}`"
-                :value="item.id"
-            />
+            ]">
+          <el-select v-model="formState.bomId"
+                     placeholder="璇烽�夋嫨BOM"
+                     clearable
+                     :disabled="!formState.productModelId || bomOptions.length === 0"
+                     style="width: 100%">
+            <el-option v-for="item in bomOptions"
+                       :key="item.id"
+                       :label="item.bomNo || `BOM-${item.id}`"
+                       :value="item.id" />
           </el-select>
         </el-form-item>
-
-        <el-form-item label="澶囨敞" prop="description">
-          <el-input v-model="formState.description" type="textarea" />
+        <el-form-item label="澶囨敞"
+                      prop="description">
+          <el-input v-model="formState.description"
+                    type="textarea" />
         </el-form-item>
       </el-form>
-      
       <!-- 浜у搧閫夋嫨寮圭獥 -->
-      <ProductSelectDialog
-          v-model="showProductSelectDialog"
-          @confirm="handleProductSelect"
-          single
-      />
+      <ProductSelectDialog v-model="showProductSelectDialog"
+                           @confirm="handleProductSelect"
+                           single />
       <template #footer>
         <div class="dialog-footer">
-          <el-button type="primary" @click="handleSubmit">纭</el-button>
+          <el-button type="primary"
+                     @click="handleSubmit">纭</el-button>
           <el-button @click="closeModal">鍙栨秷</el-button>
         </div>
       </template>
@@ -74,121 +66,122 @@
 </template>
 
 <script setup>
-import {ref, computed, getCurrentInstance} from "vue";
-import {add} from "@/api/productionManagement/processRoute.js";
-import {getByModel} from "@/api/productionManagement/productBom.js";
-import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+  import { ref, computed, getCurrentInstance } from "vue";
+  import { add } from "@/api/productionManagement/processRoute.js";
+  import { getByModel } from "@/api/productionManagement/productBom.js";
+  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
 
-const props = defineProps({
-  visible: {
-    type: Boolean,
-    required: true,
-  },
-});
+  const props = defineProps({
+    visible: {
+      type: Boolean,
+      required: true,
+    },
+  });
 
-const emit = defineEmits(['update:visible', 'completed']);
+  const emit = defineEmits(["update:visible", "completed"]);
 
-// 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
-const formState = ref({
-  productId: undefined,
-  productModelId: undefined,
-  productName: "",
-  productModelName: "",
-  bomId: undefined,
-  description: '',
-});
-
-const isShow = computed({
-  get() {
-    return props.visible;
-  },
-  set(val) {
-    emit('update:visible', val);
-  },
-});
-
-const showProductSelectDialog = ref(false);
-const bomOptions = ref([]);
-
-let { proxy } = getCurrentInstance()
-
-const closeModal = () => {
-  // 閲嶇疆琛ㄥ崟鏁版嵁
-  formState.value = {
+  // 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
+  const formState = ref({
     productId: undefined,
     productModelId: undefined,
     productName: "",
     productModelName: "",
     bomId: undefined,
-    description: '',
-  };
-  bomOptions.value = [];
-  isShow.value = false;
-};
+    description: "",
+  });
 
-// 浜у搧閫夋嫨澶勭悊
-const handleProductSelect = async (products) => {
-  if (products && products.length > 0) {
-    const product = products[0];
-    // 鍏堟煡璇OM鍒楄〃锛堝繀閫夛級
-    try {
-      const res = await getByModel(product.id);
-      // 澶勭悊杩斿洖鐨凚OM鏁版嵁锛氬彲鑳芥槸鏁扮粍銆佸璞℃垨鍖呭惈data瀛楁
-      let bomList = [];
-      if (Array.isArray(res)) {
-        bomList = res;
-      } else if (res && res.data) {
-        bomList = Array.isArray(res.data) ? res.data : [res.data];
-      } else if (res && typeof res === 'object') {
-        bomList = [res];
-      }
-      
-      if (bomList.length > 0) {
-        formState.value.productModelId = product.id;
-        formState.value.productName = product.productName;
-        formState.value.productModelName = product.model;
-        formState.value.bomId = undefined; // 閲嶇疆BOM閫夋嫨
-        bomOptions.value = bomList;
-        showProductSelectDialog.value = false;
-        // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
-        proxy.$refs["formRef"]?.validateField('productModelId');
-      } else {
+  const isShow = computed({
+    get() {
+      return props.visible;
+    },
+    set(val) {
+      emit("update:visible", val);
+    },
+  });
+
+  const showProductSelectDialog = ref(false);
+  const bomOptions = ref([]);
+
+  let { proxy } = getCurrentInstance();
+
+  const closeModal = () => {
+    // 閲嶇疆琛ㄥ崟鏁版嵁
+    formState.value = {
+      productId: undefined,
+      productModelId: undefined,
+      productName: "",
+      productModelName: "",
+      bomId: undefined,
+      description: "",
+    };
+    bomOptions.value = [];
+    isShow.value = false;
+  };
+
+  // 浜у搧閫夋嫨澶勭悊
+  const handleProductSelect = async products => {
+    if (products && products.length > 0) {
+      const product = products[0];
+      // 鍏堟煡璇OM鍒楄〃锛堝繀閫夛級
+      try {
+        const res = await getByModel(product.id);
+        // 澶勭悊杩斿洖鐨凚OM鏁版嵁锛氬彲鑳芥槸鏁扮粍銆佸璞℃垨鍖呭惈data瀛楁
+        let bomList = [];
+        if (Array.isArray(res)) {
+          bomList = res;
+        } else if (res && res.data) {
+          bomList = Array.isArray(res.data) ? res.data : [res.data];
+        } else if (res && typeof res === "object") {
+          bomList = [res];
+        }
+
+        if (bomList.length > 0) {
+          formState.value.productModelId = product.id;
+          formState.value.productName = product.productName;
+          formState.value.productModelName = product.model;
+          formState.value.bomId = undefined; // 閲嶇疆BOM閫夋嫨
+          bomOptions.value = bomList;
+          showProductSelectDialog.value = false;
+          // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
+          proxy.$refs["formRef"]?.validateField("productModelId");
+        } else {
+          proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
+        }
+      } catch (error) {
+        // 濡傛灉鎺ュ彛杩斿洖404鎴栧叾浠栭敊璇紝璇存槑娌℃湁BOM
         proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
       }
-    } catch (error) {
-      // 濡傛灉鎺ュ彛杩斿洖404鎴栧叾浠栭敊璇紝璇存槑娌℃湁BOM
-      proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
     }
-  }
-};
+  };
 
-const handleSubmit = () => {
-  proxy.$refs["formRef"].validate(valid => {
-    if (valid) {
-      // 楠岃瘉鏄惁閫夋嫨浜嗕骇鍝佸拰BOM
-      if (!formState.value.productModelId) {
-        proxy.$modal.msgError("璇烽�夋嫨浜у搧");
-        return;
+  const handleSubmit = () => {
+    proxy.$refs["formRef"].validate(valid => {
+      if (valid) {
+        // 楠岃瘉鏄惁閫夋嫨浜嗕骇鍝佸拰BOM
+        if (!formState.value.productModelId) {
+          proxy.$modal.msgError("璇烽�夋嫨浜у搧");
+          return;
+        }
+        if (!formState.value.bomId) {
+          proxy.$modal.msgError("璇烽�夋嫨BOM");
+          return;
+        }
+        console.log(formState.value, "formState.value====");
+
+        add(formState.value).then(res => {
+          // 鍏抽棴妯℃�佹
+          isShow.value = false;
+          // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
+          emit("completed");
+          proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+        });
       }
-      if (!formState.value.bomId) {
-        proxy.$modal.msgError("璇烽�夋嫨BOM");
-        return;
-      }
-      add(formState.value).then(res => {
-        // 鍏抽棴妯℃�佹
-        isShow.value = false;
-        // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
-        emit('completed');
-        proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-      })
-    }
-  })
-};
+    });
+  };
 
-
-defineExpose({
-  closeModal,
-  handleSubmit,
-  isShow,
-});
+  defineExpose({
+    closeModal,
+    handleSubmit,
+    isShow,
+  });
 </script>
diff --git a/src/views/productionManagement/processRoute/index.vue b/src/views/productionManagement/processRoute/index.vue
index 41103f9..43425c6 100644
--- a/src/views/productionManagement/processRoute/index.vue
+++ b/src/views/productionManagement/processRoute/index.vue
@@ -1,191 +1,279 @@
 <template>
   <div class="app-container">
     <div class="search_form">
-      <el-form :model="searchForm" :inline="true">
+      <el-form :model="searchForm"
+               :inline="true">
         <el-form-item label="瑙勬牸鍚嶇О:">
-          <el-input v-model="searchForm.model" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
+          <el-input v-model="searchForm.model"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    prefix-icon="Search"
                     style="width: 200px;"
                     @change="handleQuery" />
         </el-form-item>
         <el-form-item>
-          <el-button type="primary" @click="handleQuery">鎼滅储</el-button>
+          <el-button type="primary"
+                     @click="handleQuery">鎼滅储</el-button>
         </el-form-item>
       </el-form>
     </div>
     <div class="table_list">
-      <div style="text-align: right" class="mb10">
-        <el-button type="primary" @click="showNewModal">鏂板宸ヨ壓璺嚎</el-button>
-        <el-button type="danger" @click="handleDelete" :disabled="selectedRows.length === 0" plain>鍒犻櫎宸ヨ壓璺嚎</el-button>
+      <div style="text-align: right"
+           class="mb10">
+        <el-button type="primary"
+                   @click="showNewModal">鏂板宸ヨ壓璺嚎</el-button>
+        <el-button type="danger"
+                   @click="handleDelete"
+                   :disabled="selectedRows.length === 0"
+                   plain>鍒犻櫎宸ヨ壓璺嚎</el-button>
       </div>
-      <PIMTable
-          rowKey="id"
-          :column="tableColumn"
-          :tableData="tableData"
-          :page="page"
-          :isSelection="true"
-          @selection-change="handleSelectionChange"
-          :tableLoading="tableLoading"
-          @pagination="pagination"
-          :total="page.total"
-      />
+      <PIMTable rowKey="id"
+                :column="tableColumn"
+                :tableData="tableData"
+                :page="page"
+                :isSelection="true"
+                @selection-change="handleSelectionChange"
+                :tableLoading="tableLoading"
+                @pagination="pagination"
+                :total="page.total" />
     </div>
-    <new-process
-        v-if="isShowNewModal"
-        v-model:visible="isShowNewModal"
-        @completed="getList"
-    />
-
-    <edit-process
-        v-if="isShowEditModal"
-        v-model:visible="isShowEditModal"
-        :record="record"
-        @completed="getList"
-    />
-
-    <route-item-form
-        v-if="isShowItemModal"
-        v-model:visible="isShowItemModal"
-        :record="record"
-        @completed="getList"
-    />
+    <new-process v-if="isShowNewModal"
+                 v-model:visible="isShowNewModal"
+                 @completed="getList" />
+    <edit-process v-if="isShowEditModal"
+                  v-model:visible="isShowEditModal"
+                  :record="record"
+                  @completed="getList" />
+    <route-item-form v-if="isShowItemModal"
+                     v-model:visible="isShowItemModal"
+                     :record="record"
+                     @completed="getList" />
+    <FileList v-if="fileDialogVisible"
+              v-model:visible="fileDialogVisible"
+              :record-type="'technology_routing'"
+              :record-id="currentProcessRouteId" />
   </div>
 </template>
 
 <script setup>
-import {onMounted, ref} from "vue";
-import NewProcess from "@/views/productionManagement/processRoute/New.vue";
-import EditProcess from "@/views/productionManagement/processRoute/Edit.vue";
-import RouteItemForm from "@/views/productionManagement/processRoute/ItemsForm.vue";
-import {listPage, del} from "@/api/productionManagement/processRoute.js";
-import { useRouter } from 'vue-router'
+  import { onMounted, ref } from "vue";
+  import NewProcess from "@/views/productionManagement/processRoute/New.vue";
+  import EditProcess from "@/views/productionManagement/processRoute/Edit.vue";
+  import RouteItemForm from "@/views/productionManagement/processRoute/ItemsForm.vue";
+  import { listPage, del } from "@/api/productionManagement/processRoute.js";
+  const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
 
-const router = useRouter()
-const data = reactive({
-  searchForm: {
-    model: "",
-  },
-});
-const { searchForm } = toRefs(data);
-const tableColumn = ref([
-  {
-    label: "宸ヨ壓璺嚎缂栧彿",
-    prop: "processRouteCode",
-  },
-  {
-    label: "浜у搧鍚嶇О",
-    prop: "productName",
-  },
-  {
-    label: "瑙勬牸鍚嶇О",
-    prop: "model",
-  },
-  {
-    label: "BOM缂栧彿",
-    prop: "bomNo",
-  },
-  {
-    label: "鎻忚堪",
-    prop: "description",
-  },
-  {
-    dataType: "action",
-    label: "鎿嶄綔",
-    align: "center",
-    fixed: "right",
-    width: 280,
-    operation: [
-      {
-        name: "缂栬緫",
-        type: "text",
-        clickFun: (row) => {
-          showEditModal(row);
-        }
-      },
-      {
-        name: "璺嚎椤圭洰",
-        type: "text",
-        clickFun: (row) => {
-          showItemModal(row);
-        }
-      }
-    ]
-  }
-]);
-const tableData = ref([]);
-const selectedRows = ref([]);
-const tableLoading = ref(false);
-const isShowNewModal = ref(false);
-const isShowEditModal = ref(false);
-const isShowItemModal = ref(false);
-const record = ref({});
-const page = reactive({
-  current: 1,
-  size: 100,
-  total: 0,
-});
-const { proxy } = getCurrentInstance()
+  import { useRouter } from "vue-router";
+  import { ElMessage, ElMessageBox } from "element-plus";
 
-// 鏌ヨ鍒楄〃
-/** 鎼滅储鎸夐挳鎿嶄綔 */
-const handleQuery = () => {
-  page.current = 1;
-  getList();
-};
+  const router = useRouter();
+  const data = reactive({
+    searchForm: {
+      model: "",
+    },
+  });
+  const { searchForm } = toRefs(data);
+  const tableColumn = ref([
+    {
+      label: "宸ヨ壓璺嚎缂栧彿",
+      prop: "processRouteCode",
+    },
+    {
+      label: "浜у搧鍚嶇О",
+      prop: "productName",
+    },
+    {
+      label: "瑙勬牸鍚嶇О",
+      prop: "model",
+    },
+    {
+      label: "BOM缂栧彿",
+      prop: "bomNo",
+    },
+    {
+      label: "鎻忚堪",
+      prop: "description",
+    },
+    {
+      dataType: "action",
+      label: "鎿嶄綔",
+      align: "center",
+      fixed: "right",
+      width: 280,
+      operation: [
+        {
+          name: "缂栬緫",
+          type: "text",
+          clickFun: row => {
+            showEditModal(row);
+          },
+        },
+        {
+          name: "璺嚎椤圭洰",
+          type: "text",
+          clickFun: row => {
+            showItemModal(row);
+          },
+        },
+        {
+          name: "闄勪欢",
+          type: "text",
+          clickFun: row => {
+            openFileDialog(row);
+          },
+        },
+      ],
+    },
+  ]);
+  const tableData = ref([]);
+  const selectedRows = ref([]);
+  const tableLoading = ref(false);
+  const isShowNewModal = ref(false);
+  const isShowEditModal = ref(false);
+  const isShowItemModal = ref(false);
+  const record = ref({});
+  const page = reactive({
+    current: 1,
+    size: 100,
+    total: 0,
+  });
 
-const pagination = (obj) => {
-  page.current = obj.page;
-  page.size = obj.limit;
-  getList();
-};
-const getList = () => {
-  tableLoading.value = true;
-  const params = { ...searchForm.value, ...page };
-  params.entryDate = undefined
-  listPage(params).then(res => {
-    tableLoading.value = false;
-    tableData.value = res.data.records.map(item => ({
-      ...item,
+  // 闄勪欢鐩稿叧
+  const fileDialogVisible = ref(false);
+  const fileListDialogRef = ref(null);
+  const currentProcessRouteId = ref(null);
+  const filePage = reactive({
+    current: 1,
+    size: 1000,
+    total: 0,
+  });
+
+  const { proxy } = getCurrentInstance();
+
+  // 闄勪欢锛氭煡璇�
+  const fetchProcessRouteFiles = async processRouteId => {
+    const params = {
+      current: filePage.current,
+      size: filePage.size,
+      processRouteId,
+    };
+    const res = await listProcessRouteFiles(params);
+    const records = res?.data?.records || [];
+    filePage.total = res?.data?.total || records.length;
+    const mapped = records.map(item => ({
+      id: item.id,
+      name: item.fileName || item.name,
+      url: item.fileUrl || item.url,
+      raw: item,
     }));
-    page.total = res.data.total;
-  }).catch(err => {
-    tableLoading.value = false;
-  })
-};
-// 琛ㄦ牸閫夋嫨鏁版嵁
-const handleSelectionChange = (selection) => {
-  selectedRows.value = selection;
-};
+    fileListDialogRef.value?.setList(mapped);
+  };
 
-// 鎵撳紑鏂板寮规
-const showNewModal = () => {
-  isShowNewModal.value = true
-};
+  // 鎵撳紑闄勪欢寮圭獥
+  const openFileDialog = async row => {
+    currentProcessRouteId.value = row.id;
+    fileDialogVisible.value = true;
+    await fetchProcessRouteFiles(row.id);
+  };
 
-const showEditModal = (row) => {
-  isShowEditModal.value = true
-  record.value = row
-};
+  // 鍒锋柊闄勪欢鍒楄〃
+  const refreshFileList = async () => {
+    if (!currentProcessRouteId.value) return;
+    await fetchProcessRouteFiles(currentProcessRouteId.value);
+  };
 
-const showItemModal = (row) => {
-  router.push({
-    path: '/productionManagement/processRouteItem',
-    query: {
-      id: row.id,
-      processRouteCode: row.processRouteCode || '',
-      productName: row.productName || '',
-      model: row.model || '',
-      bomNo: row.bomNo || '',
-      description: row.description || '',
-      type: 'route',
+  // 涓婁紶闄勪欢
+  const handleAttachmentUpload = async filePayload => {
+    if (!currentProcessRouteId.value) return;
+    const payload = {
+      fileName: filePayload?.fileName || filePayload?.name,
+      fileUrl: filePayload?.fileUrl || filePayload?.url,
+      processRouteId: currentProcessRouteId.value,
+    };
+    await addProcessRouteFile(payload);
+    ElMessage.success("鏂囦欢涓婁紶鎴愬姛");
+    await refreshFileList();
+  };
+
+  // 鍒犻櫎闄勪欢
+  const handleAttachmentDelete = async row => {
+    if (!row?.id) return false;
+    try {
+      await ElMessageBox.confirm("纭鍒犻櫎璇ラ檮浠讹紵", "鎻愮ず", {
+        type: "warning",
+      });
+    } catch {
+      return false;
     }
-  })
-};
+    await delProcessRouteFile([row.id]);
+    ElMessage.success("鍒犻櫎鎴愬姛");
+    await refreshFileList();
+  };
 
-// 鍒犻櫎
-function handleDelete() {
-  const ids = selectedRows.value.map((item) => item.id);
-  proxy.$modal
-      .confirm('鏄惁纭鍒犻櫎宸插嬀閫夌殑鏁版嵁椤癸紵')
+  // 鏌ヨ鍒楄〃
+  /** 鎼滅储鎸夐挳鎿嶄綔 */
+  const handleQuery = () => {
+    page.current = 1;
+    getList();
+  };
+
+  const pagination = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    getList();
+  };
+  const getList = () => {
+    tableLoading.value = true;
+    const params = { ...searchForm.value, ...page };
+    params.entryDate = undefined;
+    listPage(params)
+      .then(res => {
+        tableLoading.value = false;
+        tableData.value = res.data.records.map(item => ({
+          ...item,
+        }));
+        page.total = res.data.total;
+      })
+      .catch(err => {
+        tableLoading.value = false;
+      });
+  };
+  // 琛ㄦ牸閫夋嫨鏁版嵁
+  const handleSelectionChange = selection => {
+    selectedRows.value = selection;
+  };
+
+  // 鎵撳紑鏂板寮规
+  const showNewModal = () => {
+    isShowNewModal.value = true;
+  };
+
+  const showEditModal = row => {
+    isShowEditModal.value = true;
+    record.value = row;
+  };
+
+  const showItemModal = row => {
+    router.push({
+      path: "/productionManagement/processRouteItem",
+      query: {
+        id: row.id,
+        processRouteCode: row.processRouteCode || "",
+        productName: row.productName || "",
+        model: row.model || "",
+        bomNo: row.bomNo || "",
+        bomId: row.bomId || "",
+        description: row.description || "",
+        type: "route",
+      },
+    });
+  };
+
+  // 鍒犻櫎
+  function handleDelete() {
+    const ids = selectedRows.value.map(item => item.id);
+    proxy.$modal
+      .confirm("鏄惁纭鍒犻櫎宸插嬀閫夌殑鏁版嵁椤癸紵")
       .then(function () {
         return del(ids);
       })
@@ -194,11 +282,15 @@
         proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
       })
       .catch(() => {});
-}
+  }
 
-onMounted(() => {
-  getList();
-});
+  onMounted(() => {
+    getList();
+  });
 </script>
 
-<style scoped></style>
+<style scoped>
+  .table_list {
+    margin-top: unset;
+  }
+</style>
diff --git a/src/views/productionManagement/processRoute/processRouteItem/index.vue b/src/views/productionManagement/processRoute/processRouteItem/index.vue
index c1c490c..6d64c30 100644
--- a/src/views/productionManagement/processRoute/processRouteItem/index.vue
+++ b/src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -1,9 +1,10 @@
 <template>
   <div class="app-container">
     <PageHeader content="宸ヨ壓璺嚎椤圭洰" />
-    
     <!-- 宸ヨ壓璺嚎淇℃伅灞曠ず -->
-    <el-card v-if="routeInfo.processRouteCode" class="route-info-card" shadow="hover">
+    <el-card v-if="routeInfo.processRouteCode"
+             class="route-info-card"
+             shadow="hover">
       <div class="route-info">
         <div class="info-item">
           <div class="info-label-wrapper">
@@ -37,7 +38,17 @@
             <span class="info-value">{{ routeInfo.bomNo || '-' }}</span>
           </div>
         </div>
-        <div class="info-item full-width" v-if="routeInfo.description">
+        <div class="info-item"
+             v-if="routeInfo.quantity && routeInfo.quantity !== 0">
+          <div class="info-label-wrapper">
+            <span class="info-label">闇�姹傛暟閲�</span>
+          </div>
+          <div class="info-value-wrapper">
+            <span class="info-value">{{ routeInfo.quantity || '-' }}</span>
+          </div>
+        </div>
+        <div class="info-item full-width"
+             v-if="routeInfo.description">
           <div class="info-label-wrapper">
             <span class="info-label">鎻忚堪</span>
           </div>
@@ -47,850 +58,1565 @@
         </div>
       </div>
     </el-card>
-    
     <!-- 琛ㄦ牸瑙嗗浘 -->
-    <div v-if="viewMode === 'table'" class="section-header">
+    <div v-if="viewMode === 'table'"
+         class="section-header">
       <div class="section-title">宸ヨ壓璺嚎椤圭洰鍒楄〃</div>
       <div class="section-actions">
-        <el-button 
-            icon="Grid" 
-            @click="toggleView"
-            style="margin-right: 10px;"
-        >
+        <el-button icon="Grid"
+                   @click="toggleView"
+                   style="margin-right: 10px;">
           鍗$墖瑙嗗浘
         </el-button>
-        <el-button type="primary" @click="handleAdd">鏂板</el-button>
+        <el-button v-if="editable"
+                   type="primary"
+                   @click="handleAdd">鏂板</el-button>
       </div>
     </div>
-    <el-table
-        v-if="viewMode === 'table'"
-        ref="tableRef"
-        v-loading="tableLoading"
-        border
-        :data="tableData"
-        :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
-        row-key="id"
-        tooltip-effect="dark"
-        class="lims-table"
-    >
-      <el-table-column align="center" label="搴忓彿" width="60" type="index" />
-      <el-table-column label="宸ュ簭鍚嶇О" prop="processId" width="200">
+    <el-table v-if="viewMode === 'table'"
+              ref="tableRef"
+              v-loading="tableLoading"
+              border
+              :data="tableData"
+              :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
+              row-key="id"
+              tooltip-effect="dark"
+              class="lims-table">
+      <el-table-column align="center"
+                       label="搴忓彿"
+                       width="60"
+                       type="index" />
+      <el-table-column label="宸ュ簭鍚嶇О"
+                       prop="technologyOperationId"
+                       width="200">
         <template #default="scope">
-          {{ getProcessName(scope.row.processId) || '-' }}
+          {{ scope.row.technologyOperationName || scope.row.operationName || '-' }}
         </template>
       </el-table-column>
-      <el-table-column label="浜у搧鍚嶇О" prop="productName" min-width="160" />
-      <el-table-column label="瑙勬牸鍚嶇О" prop="model" min-width="140" />
-      <el-table-column label="鍗曚綅" prop="unit" width="100" />
-      <el-table-column label="鏄惁璐ㄦ" prop="isQuality" width="100">
+      <el-table-column label="鍙傛暟鍒楄〃"
+                       min-width="160">
+        <template #default="scope">
+          <el-button type="primary"
+                     link
+                     size="small"
+                     @click="handleViewParams(scope.row)">鍙傛暟鍒楄〃</el-button>
+        </template>
+      </el-table-column>
+      <el-table-column label="浜у搧鍚嶇О"
+                       prop="productName"
+                       min-width="160" />
+      <el-table-column label="瑙勬牸鍚嶇О"
+                       prop="model"
+                       min-width="140" />
+      <el-table-column label="鍗曚綅"
+                       prop="unit"
+                       width="100" />
+      <el-table-column label="鏄惁璐ㄦ"
+                       prop="isQuality"
+                       width="100">
         <template #default="scope">
           {{scope.row.isQuality ? "鏄�" : "鍚�"}}
         </template>
       </el-table-column>
-      <el-table-column label="鎿嶄綔" align="center" fixed="right" width="150">
+      <el-table-column label="鏄惁鐢熶骇"
+                       prop="isProduction"
+                       width="100">
         <template #default="scope">
-          <el-button type="primary" link size="small" @click="handleEdit(scope.row)" :disabled="scope.row.isComplete">缂栬緫</el-button>
-          <el-button type="danger" link size="small" @click="handleDelete(scope.row)" :disabled="scope.row.isComplete">鍒犻櫎</el-button>
+          {{scope.row.isProduction ? "鏄�" : "鍚�"}}
+        </template>
+      </el-table-column>
+      <el-table-column label="鎿嶄綔"
+                       align="center"
+                       fixed="right"
+                       width="150">
+        <template #default="scope">
+          <el-button type="primary"
+                     link
+                     size="small"
+                     @click="handleEdit(scope.row)"
+                     :disabled="scope.row.isComplete || !editable">缂栬緫</el-button>
+          <el-button type="danger"
+                     link
+                     size="small"
+                     @click="handleDelete(scope.row)"
+                     :disabled="scope.row.isComplete || !editable">鍒犻櫎</el-button>
         </template>
       </el-table-column>
     </el-table>
-    
     <!-- 鍗$墖瑙嗗浘 -->
     <template v-else>
       <div class="section-header">
         <div class="section-title">宸ヨ壓璺嚎椤圭洰鍒楄〃</div>
         <div class="section-actions">
-          <el-button 
-              icon="Menu" 
-              @click="toggleView"
-              style="margin-right: 10px;"
-          >
+          <el-button icon="Menu"
+                     @click="toggleView"
+                     style="margin-right: 10px;">
             琛ㄦ牸瑙嗗浘
           </el-button>
-          <el-button type="primary" @click="handleAdd">鏂板</el-button>
+          <el-button v-if="editable"
+                     type="primary"
+                     @click="handleAdd">鏂板</el-button>
         </div>
       </div>
-      <div v-loading="tableLoading" class="card-container">
-        <div 
-            ref="cardsContainer" 
-            class="cards-wrapper"
-        >
-        <div
-            v-for="(item, index) in tableData"
-            :key="item.id || index"
-            class="process-card"
-            :data-index="index"
-        >
-          <!-- 搴忓彿鍦嗗湀 -->
-          <div class="card-header">
-            <div class="card-number">{{ index + 1 }}</div>
-            <div class="card-process-name">{{ getProcessName(item.processId) || '-' }}</div>
-          </div>
-          
-          <!-- 浜у搧淇℃伅 -->
-          <div class="card-content">
-            <div v-if="item.productName" class="product-info">
-              <div class="product-name">{{ item.productName }}</div>
-              <div v-if="item.model" class="product-model">
-                {{ item.model }}
-                <!-- <span v-if="item.unit" class="product-unit">{{ item.unit }}</span> -->
-              </div>
-              <el-tag type="primary" class="product-tag" v-if="item.isQuality">璐ㄦ</el-tag>
+      <div v-loading="tableLoading"
+           class="card-container">
+        <div ref="cardsContainer"
+             class="cards-wrapper">
+          <div v-for="(item, index) in tableData"
+               :key="item.id || index"
+               class="process-card"
+               :data-index="index">
+            <!-- 搴忓彿鍦嗗湀 -->
+            <div class="card-header">
+              <div class="card-number">{{ index + 1 }}</div>
+              <div class="card-process-name">{{ item.technologyOperationName || item.operationName || '-' }}</div>
             </div>
-            <div v-else class="product-info empty">鏆傛棤浜у搧淇℃伅</div>
-          </div>
-          
-          <!-- 鎿嶄綔鎸夐挳 -->
-          <div class="card-footer">
-            <el-button type="primary" link size="small" @click="handleEdit(item)" :disabled="item.isComplete">缂栬緫</el-button>
-            <el-button type="danger" link size="small" @click="handleDelete(item)" :disabled="item.isComplete">鍒犻櫎</el-button>
+            <!-- 浜у搧淇℃伅 -->
+            <div class="card-content">
+              <div v-if="item.productName"
+                   class="product-info">
+                <div class="product-name">{{ item.productName }}</div>
+                <div v-if="item.model"
+                     class="product-model">
+                  {{ item.model }}
+                  <!-- <span v-if="item.unit" class="product-unit">{{ item.unit }}</span> -->
+                </div>
+                <el-tag type="primary"
+                        class="product-tag"
+                        v-if="item.isQuality">璐ㄦ</el-tag>
+                <el-tag type="primary"
+                        class="product-tag"
+                        :style="item.isQuality?'margin-left:8px':''"
+                        v-if="item.isProduction">鐢熶骇</el-tag>
+              </div>
+              <div v-else
+                   class="product-info empty">鏆傛棤浜у搧淇℃伅</div>
+            </div>
+            <!-- 鎿嶄綔鎸夐挳 -->
+            <div class="card-footer">
+              <el-button type="primary"
+                         link
+                         size="small"
+                         @click="handleEdit(item)"
+                         :disabled="item.isComplete || !editable">缂栬緫</el-button>
+              <el-button type="info"
+                         link
+                         size="small"
+                         @click="handleViewParams(item)">鍙傛暟鍒楄〃</el-button>
+              <el-button type="danger"
+                         link
+                         size="small"
+                         @click="handleDelete(item)"
+                         :disabled="item.isComplete || !editable">鍒犻櫎</el-button>
+            </div>
           </div>
         </div>
-      </div>
       </div>
     </template>
-
+    <!-- bom妯″潡 -->
+    <div class="section-header"
+         style="margin-top: 20px;">
+      <div class="section-title">BOM 缁撴瀯</div>
+      <div class="section-actions"
+           v-if="pageType === 'order' && editable">
+        <el-button v-if="!bomDataValue.isEdit"
+                   type="primary"
+                   @click="bomDataValue.isEdit = true">
+          缂栬緫
+        </el-button>
+        <el-button v-if="bomDataValue.isEdit"
+                   @click="cancelEditBom">
+          鍙栨秷
+        </el-button>
+        <el-button v-if="bomDataValue.isEdit"
+                   type="primary"
+                   @click="handleSaveBom"
+                   :loading="bomDataValue.loading">
+          淇濆瓨BOM
+        </el-button>
+      </div>
+    </div>
+    <el-table :data="bomTableData"
+              border
+              :preserve-expanded-content="false"
+              :default-expand-all="true"
+              style="width: 100%">
+      <el-table-column type="expand">
+        <template #default>
+          <el-form ref="bomFormRef"
+                   :model="bomDataValue">
+            <el-table :data="bomDataValue.dataList"
+                      row-key="tempId"
+                      default-expand-all
+                      :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
+                      style="width: 100%">
+              <el-table-column prop="productName"
+                               label="浜у搧" />
+              <el-table-column prop="model"
+                               label="瑙勬牸">
+                <template #default="{ row }">
+                  <el-form-item v-if="pageType === 'order' && bomDataValue.isEdit"
+                                :rules="[{ required: true, message: '璇烽�夋嫨瑙勬牸', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-select v-model="row.model"
+                               placeholder="璇烽�夋嫨瑙勬牸"
+                               clearable
+                               :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)"
+                               style="width: 100%"
+                               @visible-change="(v) => { if (v) openBomDialog(row.tempId) }">
+                      <el-option v-if="row.model"
+                                 :label="row.model"
+                                 :value="row.model" />
+                    </el-select>
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="processName"
+                               label="娑堣�楀伐搴�">
+                <template #default="{ row }">
+                  <el-form-item v-if="pageType === 'order' && bomDataValue.isEdit"
+                                :rules="bomDataValue.dataList.some(item => (item).tempId === row.tempId) ? [] : [{ required: true, message: '璇烽�夋嫨娑堣�楀伐搴�', trigger: 'change' }]"
+                                style="margin: 0">
+                    <el-select v-model="row.processId"
+                               placeholder="璇烽�夋嫨"
+                               filterable
+                               clearable
+                               style="width: 100%"
+                               @change="value => handleBomProcessChange(row, value)"
+                               :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)">
+                      <el-option v-for="item in bomDataValue.processOptions"
+                                 :key="item.id"
+                                 :label="item.name"
+                                 :value="item.id" />
+                    </el-select>
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="unitQuantity"
+                               label="鍗曚綅浜у嚭鎵�闇�鏁伴噺">
+                <template #default="{ row }">
+                  <el-form-item v-if="pageType === 'order' && bomDataValue.isEdit"
+                                :rules="[{ required: true, message: '璇疯緭鍏ュ崟浣嶄骇鍑烘墍闇�鏁伴噺', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-input-number v-model="row.unitQuantity"
+                                     :min="0"
+                                     :precision="2"
+                                     :step="1"
+                                     controls-position="right"
+                                     style="width: 100%"
+                                     @change="handleUnitQuantityChange(row)"
+                                     :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)" />
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column v-if="pageType === 'order'"
+                               prop="demandedQuantity"
+                               label="闇�姹傛�婚噺">
+                <template #default="{ row }">
+                  <el-form-item v-if="pageType === 'order' && bomDataValue.isEdit"
+                                :rules="[{ required: true, message: '璇疯緭鍏ラ渶姹傛�婚噺', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-input-number v-model="row.demandedQuantity"
+                                     :min="0"
+                                     :precision="2"
+                                     :step="1"
+                                     controls-position="right"
+                                     style="width: 100%"
+                                     :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)" />
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="unit"
+                               label="鍗曚綅">
+                <template #default="{ row }">
+                  <el-form-item v-if="pageType === 'order' && bomDataValue.isEdit"
+                                :rules="[{ required: true, message: '璇疯緭鍏ュ崟浣�', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-input v-model="row.unit"
+                              placeholder="璇疯緭鍏ュ崟浣�"
+                              clearable
+                              :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)" />
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column label="鎿嶄綔"
+                               fixed="right"
+                               width="200"
+                               v-if="pageType === 'order' && bomDataValue.isEdit">
+                <template #default="{ row }">
+                  <el-button v-if="bomDataValue.isEdit && !bomDataValue.dataList.some(item => (item).tempId === row.tempId)"
+                             type="danger"
+                             text
+                             @click="removeBomItem(row.tempId)">鍒犻櫎
+                  </el-button>
+                  <el-button v-if="bomDataValue.isEdit"
+                             type="primary"
+                             text
+                             @click="addBomItem(row.tempId)">娣诲姞
+                  </el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </el-form>
+        </template>
+      </el-table-column>
+      <el-table-column label="BOM缂栧彿"
+                       prop="bomNo" />
+      <el-table-column label="浜у搧鍚嶇О"
+                       prop="productName" />
+      <el-table-column label="瑙勬牸鍨嬪彿"
+                       prop="model" />
+    </el-table>
+    <ProductSelectDialog v-if="bomDataValue.showProductDialog"
+                         v-model="bomDataValue.showProductDialog"
+                         :single="true"
+                         @confirm="handleBomProduct" />
     <!-- 鏂板/缂栬緫寮圭獥 -->
-    <el-dialog
-        v-model="dialogVisible"
-        :title="operationType === 'add' ? '鏂板宸ヨ壓璺嚎椤圭洰' : '缂栬緫宸ヨ壓璺嚎椤圭洰'"
-        width="500px"
-        @close="closeDialog"
-    >
-      <el-form
-          ref="formRef"
-          :model="form"
-          :rules="rules"
-          label-width="120px"
-      >
-        <el-form-item label="宸ュ簭" prop="processId">
-          <el-select
-              v-model="form.processId"
-              placeholder="璇烽�夋嫨宸ュ簭"
-              clearable
-              style="width: 100%"
-          >
-            <el-option
-                v-for="process in processOptions"
-                :key="process.id"
-                :label="process.name"
-                :value="process.id"
-            />
+    <el-dialog v-model="dialogVisible"
+               :title="operationType === 'add' ? '鏂板宸ヨ壓璺嚎椤圭洰' : '缂栬緫宸ヨ壓璺嚎椤圭洰'"
+               width="500px"
+               @close="closeDialog">
+      <el-form ref="formRef"
+               :model="form"
+               :rules="rules"
+               label-width="120px">
+        <el-form-item label="宸ュ簭"
+                      v-if="operationType === 'add' || pageType === 'route'"
+                      prop="technologyOperationId">
+          <el-select v-model="form.technologyOperationId"
+                     placeholder="璇烽�夋嫨宸ュ簭"
+                     clearable
+                     @change="processChange"
+                     style="width: 100%">
+            <el-option v-for="process in processOptions"
+                       :key="process.id"
+                       :label="process.name"
+                       :value="process.id" />
           </el-select>
         </el-form-item>
-
-        <el-form-item label="浜у搧鍚嶇О" prop="productModelId">
-          <el-button type="primary" @click="showProductSelectDialog = true">
-            {{ form.productName && form.model 
-              ? `${form.productName} - ${form.model}` 
+        <el-form-item label="宸ュ簭"
+                      v-else>
+          <span>{{ getProcessName(form.technologyOperationId) }}</span>
+        </el-form-item>
+        <el-form-item label="浜у搧鍚嶇О"
+                      v-if="operationType === 'add' || pageType === 'route'"
+                      prop="productModelId">
+          <el-button type="primary"
+                     @click="showProductSelectDialog = true">
+            {{ form.productName
+              ? (form.model ? `${form.productName} - ${form.model}` : form.productName)
               : '閫夋嫨浜у搧' }}
           </el-button>
         </el-form-item>
-
-        <el-form-item label="鍗曚綅" prop="unit">
-          <el-input 
-              v-model="form.unit" 
-              :placeholder="form.productModelId ? '鏍规嵁閫夋嫨鐨勪骇鍝佽嚜鍔ㄥ甫鍑�' : '璇峰厛閫夋嫨浜у搧'" 
-              clearable 
-              :disabled="true" 
-          />
+        <el-form-item label="浜у搧鍚嶇О"
+                      v-else>
+          <span>{{ form.productName }}{{ form.model ? ' - ' + form.model : '' }}</span>
         </el-form-item>
-
-        <el-form-item label="鏄惁璐ㄦ" prop="isQuality">
-          <el-switch v-model="form.isQuality" :active-value="true" inactive-value="false"/>
+        <el-form-item label="鍗曚綅"
+                      v-if="operationType === 'add' || pageType === 'route'"
+                      prop="unit">
+          <el-input v-model="form.unit"
+                    :placeholder="form.productModelId ? '鏍规嵁閫夋嫨鐨勪骇鍝佽嚜鍔ㄥ甫鍑�' : '璇峰厛閫夋嫨浜у搧'"
+                    clearable
+                    :disabled="true" />
+        </el-form-item>
+        <el-form-item label="鍗曚綅"
+                      v-else>
+          <span>{{ form.unit }}</span>
+        </el-form-item>
+        <el-form-item label="鏄惁璐ㄦ"
+                      prop="isQuality">
+          <el-switch v-model="form.isQuality"
+                     :active-value="true"
+                     :inactive-value="false" />
+        </el-form-item>
+        <el-form-item label="鏄惁鐢熶骇"
+                      prop="isProduction">
+          <el-switch v-model="form.isProduction"
+                     :active-value="true"
+                     :inactive-value="false" />
         </el-form-item>
       </el-form>
-
       <template #footer>
-        <el-button type="primary" @click="handleSubmit" :loading="submitLoading">纭畾</el-button>
+        <el-button type="primary"
+                   @click="handleSubmit"
+                   :loading="submitLoading">纭畾</el-button>
         <el-button @click="closeDialog">鍙栨秷</el-button>
       </template>
     </el-dialog>
-
     <!-- 浜у搧閫夋嫨瀵硅瘽妗� -->
-    <ProductSelectDialog
-        v-model="showProductSelectDialog"
-        @confirm="handleProductSelect"
-        single
-    />
+    <ProductSelectDialog v-model="showProductSelectDialog"
+                         @confirm="handleProductSelect"
+                         single />
+    <!-- 鍙傛暟鍒楄〃瀵硅瘽妗� -->
+    <ProcessParamListDialog v-model="showParamListDialog"
+                            :title="`${currentProcess ? (currentProcess.processName || currentProcess.technologyOperationName || currentProcess.operationName) : ''} - 鍙傛暟鍒楄〃`"
+                            :route-id="routeId"
+                            :order-id="orderId"
+                            :process="currentProcess"
+                            :page-type="pageType"
+                            :param-list="paramList"
+                            :editable="editable"
+                            @getsyncProcessParamItem="getsyncProcessParamItem"
+                            @refresh="refreshParamList" />
   </div>
 </template>
 
 <script setup>
-import { ref, computed, getCurrentInstance, onMounted, onUnmounted, nextTick } from "vue";
-import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
-import { findProcessRouteItemList, addOrUpdateProcessRouteItem, sortProcessRouteItem, batchDeleteProcessRouteItem } from "@/api/productionManagement/processRouteItem.js";
-import { findProductProcessRouteItemList, deleteRouteItem, addRouteItem, addOrUpdateProductProcessRouteItem, sortRouteItem } from "@/api/productionManagement/productProcessRoute.js";
-import { processList } from "@/api/productionManagement/productionProcess.js";
-import { useRoute } from 'vue-router'
-import { ElMessageBox } from 'element-plus'
-import Sortable from 'sortablejs'
+  import {
+    ref,
+    computed,
+    getCurrentInstance,
+    onMounted,
+    onUnmounted,
+    nextTick,
+  } from "vue";
+  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+  import ProcessParamListDialog from "@/components/ProcessParamListDialog.vue";
+  import {
+    findProcessRouteItemList,
+    addOrUpdateProcessRouteItem,
+    addOrUpdateProcessRouteItem1,
+    sortProcessRouteItem,
+    batchDeleteProcessRouteItem,
+    getProcessParamList,
+  } from "@/api/productionManagement/processRouteItem.js";
+  import {
+    syncProcessParamItem,
+    syncProcessParamItemOrder,
+  } from "@/api/productionManagement/processRouteItem.js";
+  import {
+    findProductProcessRouteItemList,
+    deleteRouteItem,
+    addRouteItem,
+    findProcessParamListOrder,
+    addOrUpdateProductProcessRouteItem,
+    sortRouteItem,
+  } from "@/api/productionManagement/productProcessRoute.js";
+  import { processList } from "@/api/productionManagement/productionProcess.js";
+  import { listProcessBom } from "@/api/productionManagement/productionOrder.js";
+  import {
+    queryList,
+    queryList2,
+    add2,
+  } from "@/api/productionManagement/productStructure.js";
 
-const route = useRoute()
-const { proxy } = getCurrentInstance() || {};
+  import { useRoute } from "vue-router";
+  import { ElMessageBox, ElMessage } from "element-plus";
+  import Sortable from "sortablejs";
 
-const routeId = computed(() => route.query.id);
-const orderId = computed(() => route.query.orderId);
-const pageType = computed(() => route.query.type);
+  const route = useRoute();
+  const { proxy } = getCurrentInstance() || {};
 
-const tableLoading = ref(false);
-const tableData = ref([]);
-const dialogVisible = ref(false);
-const operationType = ref('add'); // add | edit
-const formRef = ref(null);
-const submitLoading = ref(false);
-const cardsContainer = ref(null);
-const tableRef = ref(null);
-const viewMode = ref('table'); // table | card
-const routeInfo = ref({
-  processRouteCode: '',
-  productName: '',
-  model: '',
-  bomNo: '',
-  description: ''
-});
+  const routeId = computed(() => route.query.id);
+  const orderId = computed(() => route.query.orderId);
+  const pageType = computed(() => route.query.type);
+  const editable = computed(() => route.query.editable !== "false");
 
-const processOptions = ref([]);
-const showProductSelectDialog = ref(false);
-let tableSortable = null;
-let cardSortable = null;
-
-// 鍒囨崲瑙嗗浘
-const toggleView = () => {
-  viewMode.value = viewMode.value === 'table' ? 'card' : 'table';
-  // 鍒囨崲瑙嗗浘鍚庨噸鏂板垵濮嬪寲鎷栨嫿鎺掑簭
-  nextTick(() => {
-    initSortable();
+  const tableLoading = ref(false);
+  const tableData = ref([]);
+  const dialogVisible = ref(false);
+  const operationType = ref("add"); // add | edit
+  const formRef = ref(null);
+  const bomFormRef = ref(null);
+  const submitLoading = ref(false);
+  const cardsContainer = ref(null);
+  const tableRef = ref(null);
+  const viewMode = ref("card"); // table | card
+  const routeInfo = ref({
+    processRouteCode: "",
+    productName: "",
+    model: "",
+    bomNo: "",
+    description: "",
+    quantity: 0,
   });
-};
 
-const form = ref({
-  id: undefined,
-  routeId: routeId.value,
-  processId: undefined,
-  productModelId: undefined,
-  productName: "",
-  model: "",
-  unit: "",
-  isQuality: false,
-});
+  const processOptions = ref([]);
+  const showProductSelectDialog = ref(false);
+  const showParamListDialog = ref(false);
+  const currentProcess = ref(null);
+  const paramList = ref([]);
+  let tableSortable = null;
+  let cardSortable = null;
 
-const rules = {
-  processId: [{ required: true, message: '璇烽�夋嫨宸ュ簭', trigger: 'change' }],
-  productModelId: [{ required: true, message: '璇烽�夋嫨浜у搧', trigger: 'change' }],
-};
-
-// 鏍规嵁宸ュ簭ID鑾峰彇宸ュ簭鍚嶇О
-const getProcessName = (processId) => {
-  if (!processId) return '';
-  const process = processOptions.value.find(p => p.id === processId);
-  return process ? process.name : '';
-};
-
-// 鑾峰彇鍒楄〃
-const getList = () => {
-  tableLoading.value = true;
-  const listPromise =
-    pageType.value === "order"
-      ? findProductProcessRouteItemList({ orderId: orderId.value })
-      : findProcessRouteItemList({ routeId: routeId.value });
-
-  listPromise
-    .then(res => {
-      tableData.value = res.data || [];
-      tableLoading.value = false;
-      // 鍒楄〃鍔犺浇瀹屾垚鍚庡垵濮嬪寲鎷栨嫿鎺掑簭
-      nextTick(() => {
-        initSortable();
-      });
-    })
-    .catch(err => {
-      tableLoading.value = false;
-      console.error("鑾峰彇鍒楄〃澶辫触锛�", err);
-      proxy?.$modal?.msgError("鑾峰彇鍒楄〃澶辫触");
+  // 鍒囨崲瑙嗗浘
+  const toggleView = () => {
+    viewMode.value = viewMode.value === "table" ? "card" : "table";
+    // 鍒囨崲瑙嗗浘鍚庨噸鏂板垵濮嬪寲鎷栨嫿鎺掑簭
+    nextTick(() => {
+      initSortable();
     });
-};
-
-// 鑾峰彇宸ュ簭鍒楄〃
-const getProcessList = () => {
-  processList({})
-    .then(res => {
-      processOptions.value = res.data || [];
-    })
-    .catch(err => {
-      console.error("鑾峰彇宸ュ簭澶辫触锛�", err);
-    });
-};
-
-// 鑾峰彇宸ヨ壓璺嚎璇︽儏锛堜粠璺敱鍙傛暟鑾峰彇锛�
-const getRouteInfo = () => {
-  routeInfo.value = {
-    processRouteCode: route.query.processRouteCode || '',
-    productName: route.query.productName || '',
-    model: route.query.model || '',
-    bomNo: route.query.bomNo || '',
-    description: route.query.description || ''
   };
-};
 
-// 鏂板
-const handleAdd = () => {
-  operationType.value = 'add';
-  resetForm();
-  dialogVisible.value = true;
-};
-
-// 缂栬緫
-const handleEdit = (row) => {
-  operationType.value = 'edit';
-  form.value = {
-    id: row.id,
-    routeId: routeId.value,
-    processId: row.processId,
-    productModelId: row.productModelId,
-    productName: row.productName || "",
-    model: row.model || "",
-    unit: row.unit || "",
-    isQuality: row.isQuality,
-  };
-  dialogVisible.value = true;
-};
-
-// 鍒犻櫎
-const handleDelete = (row) => {
-  ElMessageBox.confirm('纭鍒犻櫎璇ュ伐鑹鸿矾绾块」鐩紵', '鎻愮ず', {
-    confirmButtonText: '纭',
-    cancelButtonText: '鍙栨秷',
-    type: 'warning'
-  })
-    .then(() => {
-      // 鐢熶骇璁㈠崟涓嬩娇鐢� productProcessRoute 鐨勫垹闄ゆ帴鍙o紙璺敱鍚庢嫾鎺� id锛夛紝鍏跺畠鎯呭喌浣跨敤宸ヨ壓璺嚎椤圭洰鎵归噺鍒犻櫎鎺ュ彛
-      const deletePromise =
-        pageType.value === 'order'
-          ? deleteRouteItem(row.id)
-          : batchDeleteProcessRouteItem([row.id]);
-
-      deletePromise
-        .then(() => {
-          proxy?.$modal?.msgSuccess('鍒犻櫎鎴愬姛');
-          getList();
-        })
-        .catch(() => {
-          proxy?.$modal?.msgError('鍒犻櫎澶辫触');
-        });
-    })
-    .catch(() => {});
-};
-
-// 浜у搧閫夋嫨
-const handleProductSelect = (products) => {
-  if (products && products.length > 0) {
-    const product = products[0];
-    form.value.productModelId = product.id;
-    form.value.productName = product.productName;
-    form.value.model = product.model;
-    form.value.unit = product.unit || "";
-    showProductSelectDialog.value = false;
-    // 瑙﹀彂琛ㄥ崟楠岃瘉
-    formRef.value?.validateField('productModelId');
-  }
-};
-
-// 鎻愪氦
-const handleSubmit = () => {
-  formRef.value.validate((valid) => {
-    if (valid) {
-      submitLoading.value = true;
-      
-      if (operationType.value === 'add') {
-        // 鏂板锛氫紶鍗曚釜瀵硅薄锛屽寘鍚玠ragSort瀛楁
-        // dragSort = 褰撳墠鍒楄〃闀垮害 + 1锛岃〃绀烘柊澧炶褰曟帓鍦ㄦ渶鍚�
-        const dragSort = tableData.value.length + 1;
-        const isOrderPage = pageType.value === 'order';
-
-        const addPromise = isOrderPage
-          ? addRouteItem({
-              productOrderId: orderId.value,
-              productRouteId: routeId.value,
-              processId: form.value.processId,
-              productModelId: form.value.productModelId,
-              isQuality: form.value.isQuality,
-              dragSort,
-            })
-          : addOrUpdateProcessRouteItem({
-              routeId: routeId.value,
-              processId: form.value.processId,
-              productModelId: form.value.productModelId,
-              isQuality: form.value.isQuality,
-              dragSort,
-            });
-
-        addPromise
-          .then(() => {
-            proxy?.$modal?.msgSuccess('鏂板鎴愬姛');
-            closeDialog();
-            getList();
-          })
-          .catch(() => {
-            proxy?.$modal?.msgError('鏂板澶辫触');
-          })
-          .finally(() => {
-            submitLoading.value = false;
-          });
-      } else {
-        // 缂栬緫锛氱敓浜ц鍗曚笅浣跨敤 productProcessRoute/updateRouteItem锛屽叾瀹冩儏鍐典娇鐢ㄥ伐鑹鸿矾绾块」鐩洿鏂版帴鍙�
-        const isOrderPage = pageType.value === 'order';
-        
-        const updatePromise = isOrderPage
-          ? addOrUpdateProductProcessRouteItem({
-              id: form.value.id,
-              processId: form.value.processId,
-              productModelId: form.value.productModelId,
-              isQuality: form.value.isQuality,
-            })
-          : addOrUpdateProcessRouteItem({
-              routeId: routeId.value,
-              processId: form.value.processId,
-              productModelId: form.value.productModelId,
-              id: form.value.id,
-              isQuality: form.value.isQuality,
-            });
-
-        updatePromise
-          .then(() => {
-            proxy?.$modal?.msgSuccess('淇敼鎴愬姛');
-            closeDialog();
-            getList();
-          })
-          .catch(() => {
-            proxy?.$modal?.msgError('淇敼澶辫触');
-          })
-          .finally(() => {
-            submitLoading.value = false;
-          });
-      }
-    }
-  });
-};
-
-// 閲嶇疆琛ㄥ崟
-const resetForm = () => {
-  form.value = {
+  const form = ref({
     id: undefined,
     routeId: routeId.value,
-    processId: undefined,
+    technologyOperationId: undefined,
     productModelId: undefined,
     productName: "",
     model: "",
     unit: "",
+    isQuality: false,
+    isProduction: false,
+  });
+
+  const rules = {
+    technologyOperationId: [
+      { required: true, message: "璇烽�夋嫨宸ュ簭", trigger: "change" },
+    ],
+    productModelId: [
+      { required: true, message: "璇烽�夋嫨浜у搧", trigger: "change" },
+    ],
   };
-  formRef.value?.resetFields();
-};
 
-// 鍏抽棴寮圭獥
-const closeDialog = () => {
-  dialogVisible.value = false;
-  resetForm();
-};
+  const getsyncProcessParamItem = () => {
+    ElMessageBox.confirm("鏄惁瑕嗙洊褰撳墠宸ュ簭宸插瓨鍦ㄥ弬鏁帮紵", "鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        if (pageType.value === "order") {
+          syncProcessParamItemOrder({
+            replaceExisting: true,
+            technologyRoutingOperationId: currentProcess.value.id,
+          }).then(res => {
+            if (res.code === 200) {
+              ElMessage.success("鍚屾鎴愬姛");
+              refreshParamList();
+            } else {
+              ElMessage.error(res.msg || "鍚屾澶辫触");
+            }
+          });
+        } else {
+          syncProcessParamItem({
+            replaceExisting: true,
+            technologyRoutingOperationId: currentProcess.value.id,
+          }).then(res => {
+            if (res.code === 200) {
+              ElMessage.success("鍚屾鎴愬姛");
+              refreshParamList();
+            } else {
+              ElMessage.error(res.msg || "鍚屾澶辫触");
+            }
+          });
+        }
+      })
+      .catch(() => {});
+  };
 
-// 鍒濆鍖栨嫋鎷芥帓搴�
-const initSortable = () => {
-  destroySortable();
-  
-  if (viewMode.value === 'table') {
-    // 琛ㄦ牸瑙嗗浘鐨勬嫋鎷芥帓搴�
-    if (!tableRef.value) return;
-    
-    const tbody = tableRef.value.$el.querySelector('.el-table__body tbody') ||
-        tableRef.value.$el.querySelector('.el-table__body-wrapper > table > tbody');
-    
-    if (!tbody) return;
+  // 鏍规嵁宸ュ簭ID鑾峰彇宸ュ簭鍚嶇О
+  const getProcessName = technologyOperationId => {
+    if (!technologyOperationId) return "";
+    const process = processOptions.value.find(
+      p => p.id === technologyOperationId
+    );
+    return process ? process.name : "";
+  };
 
-    tableSortable = new Sortable(tbody, {
-      animation: 150,
-      ghostClass: 'sortable-ghost',
-      handle: '.el-table__row',
-      filter: '.el-button, .el-select',
-      onEnd: (evt) => {
-        if (evt.oldIndex === evt.newIndex || !tableData.value[evt.oldIndex]) return;
+  // 鑾峰彇鍒楄〃
+  const getList = () => {
+    tableLoading.value = true;
+    const listPromise =
+      pageType.value === "order"
+        ? findProductProcessRouteItemList({ orderId: orderId.value })
+        : findProcessRouteItemList({ routeId: routeId.value });
 
-        // 閲嶆柊鎺掑簭鏁扮粍
-        const moveItem = tableData.value.splice(evt.oldIndex, 1)[0];
-        tableData.value.splice(evt.newIndex, 0, moveItem);
-        
-        // 璁$畻鏂扮殑搴忓彿锛坉ragSort浠�1寮�濮嬶級
-        const newIndex = evt.newIndex;
-        const dragSort = newIndex + 1;
-        
-        // 璋冪敤鎺掑簭鎺ュ彛
-        if (moveItem.id) {
-          const isOrderPage = pageType.value === 'order';
-          const sortPromise = isOrderPage
-            ? sortRouteItem({
-                id: moveItem.id,
-                dragSort: dragSort
+    listPromise
+      .then(res => {
+        tableData.value = res.data || [];
+        tableLoading.value = false;
+        // 鍒楄〃鍔犺浇瀹屾垚鍚庡垵濮嬪寲鎷栨嫿鎺掑簭
+        nextTick(() => {
+          initSortable();
+        });
+      })
+      .catch(err => {
+        tableLoading.value = false;
+        console.error("鑾峰彇鍒楄〃澶辫触锛�", err);
+        proxy?.$modal?.msgError("鑾峰彇鍒楄〃澶辫触");
+      });
+  };
+
+  // 鑾峰彇宸ュ簭鍒楄〃
+  const getProcessList = () => {
+    processList({ size: -1, current: -1 })
+      .then(res => {
+        processOptions.value = res.data.records || [];
+        bomDataValue.value.processOptions = processOptions.value;
+      })
+      .catch(err => {
+        console.error("鑾峰彇宸ュ簭澶辫触锛�", err);
+      });
+  };
+
+  // 鑾峰彇宸ヨ壓璺嚎璇︽儏锛堜粠璺敱鍙傛暟鑾峰彇锛�
+  const getRouteInfo = () => {
+    routeInfo.value = {
+      processRouteCode: route.query.processRouteCode || "",
+      productName: route.query.productName || "",
+      model: route.query.model || "",
+      bomNo: route.query.bomNo || "",
+      bomId: route.query.bomId || "",
+      description: route.query.description || "",
+      quantity: route.query.quantity || 0,
+      status: !(route.query.status == 1 || route.query.status === "false"),
+    };
+    bomTableData.value[0].productName = routeInfo.value.productName;
+    bomTableData.value[0].model = routeInfo.value.model;
+    bomTableData.value[0].bomNo = routeInfo.value.bomNo;
+  };
+
+  // 鏂板
+  const handleAdd = () => {
+    operationType.value = "add";
+    resetForm();
+    dialogVisible.value = true;
+  };
+
+  // 缂栬緫
+  const handleEdit = row => {
+    operationType.value = "edit";
+    form.value = {
+      id: row.id,
+      routeId: routeId.value,
+      technologyOperationId: row.technologyOperationId,
+      productModelId: row.productModelId,
+      productName: row.productName || "",
+      model: row.model || "",
+      unit: row.unit || "",
+      isQuality: row.isQuality,
+      isProduction: row.isProduction,
+    };
+    dialogVisible.value = true;
+  };
+
+  // 鍒犻櫎
+  const handleDelete = row => {
+    ElMessageBox.confirm("纭鍒犻櫎璇ュ伐鑹鸿矾绾块」鐩紵", "鎻愮ず", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        // 鐢熶骇璁㈠崟涓嬩娇鐢� productProcessRoute 鐨勫垹闄ゆ帴鍙o紙璺敱鍚庢嫾鎺� id锛夛紝鍏跺畠鎯呭喌浣跨敤宸ヨ壓璺嚎椤圭洰鎵归噺鍒犻櫎鎺ュ彛
+        const deletePromise =
+          pageType.value === "order"
+            ? deleteRouteItem(row.id)
+            : batchDeleteProcessRouteItem([row.id]);
+
+        deletePromise
+          .then(() => {
+            proxy?.$modal?.msgSuccess("鍒犻櫎鎴愬姛");
+            getList();
+          })
+          .catch(() => {
+            proxy?.$modal?.msgError("鍒犻櫎澶辫触");
+          });
+      })
+      .catch(() => {});
+  };
+
+  // 浜у搧閫夋嫨
+  const handleProductSelect = products => {
+    console.log(products, "===products===");
+    if (products && products.length > 0) {
+      const product = products[0];
+      console.log(product, "product");
+      form.value = {
+        ...form.value,
+        productModelId: product.id,
+        productName: product.productName,
+        model: product.model,
+        unit: product.unit || "",
+      };
+      showProductSelectDialog.value = false;
+      // 瑙﹀彂琛ㄥ崟楠岃瘉
+      // formRef.value?.validateField("productModelId");
+    }
+  };
+
+  // 鎻愪氦
+  const handleSubmit = () => {
+    formRef.value.validate(valid => {
+      if (valid) {
+        submitLoading.value = true;
+
+        if (operationType.value === "add") {
+          // 鏂板锛氫紶鍗曚釜瀵硅薄锛屽寘鍚玠ragSort瀛楁
+          // dragSort = 褰撳墠鍒楄〃闀垮害 + 1锛岃〃绀烘柊澧炶褰曟帓鍦ㄦ渶鍚�
+          const dragSort = tableData.value.length + 1;
+          const isOrderPage = pageType.value === "order";
+
+          const addPromise = isOrderPage
+            ? addRouteItem({
+                productionOrderId: Number(orderId.value),
+                orderRoutingId: Number(routeId.value),
+                technologyOperationId: form.value.technologyOperationId,
+                technologyRoutingId: Number(routeId.value),
+                operationName: getProcessName(form.value.technologyOperationId),
+                productModelId: form.value.productModelId,
+                isQuality: form.value.isQuality,
+                isProduction: form.value.isProduction,
+                dragSort,
               })
-            : sortProcessRouteItem({
-                id: moveItem.id,
-                dragSort: dragSort
+            : addOrUpdateProcessRouteItem({
+                technologyRoutingId: Number(routeId.value),
+                technologyOperationId: form.value.technologyOperationId,
+                productModelId: form.value.productModelId,
+                isQuality: form.value.isQuality,
+                isProduction: form.value.isProduction,
+                dragSort,
               });
 
-          sortPromise
+          addPromise
             .then(() => {
-              // 鏇存柊鎵�鏈夎鐨刣ragSort
-              tableData.value.forEach((item, index) => {
-                if (item.id) {
-                  item.dragSort = index + 1;
-                }
-              });
-              proxy?.$modal?.msgSuccess('鎺掑簭鎴愬姛');
+              proxy?.$modal?.msgSuccess("鏂板鎴愬姛");
+              closeDialog();
+              getList();
             })
-            .catch((err) => {
-              // 鎺掑簭澶辫触锛屾仮澶嶅師鏁扮粍
-              tableData.value.splice(newIndex, 1);
-              tableData.value.splice(evt.oldIndex, 0, moveItem);
-              proxy?.$modal?.msgError('鎺掑簭澶辫触');
-              console.error("鎺掑簭澶辫触锛�", err);
+            .catch(() => {
+              proxy?.$modal?.msgError("鏂板澶辫触");
+            })
+            .finally(() => {
+              submitLoading.value = false;
+            });
+        } else {
+          // 缂栬緫锛氱敓浜ц鍗曚笅浣跨敤 productProcessRoute/updateRouteItem锛屽叾瀹冩儏鍐典娇鐢ㄥ伐鑹鸿矾绾块」鐩洿鏂版帴鍙�
+          const isOrderPage = pageType.value === "order";
+
+          const updatePromise = isOrderPage
+            ? addOrUpdateProductProcessRouteItem({
+                id: form.value.id,
+                technologyOperationId: form.value.technologyOperationId,
+                operationName: getProcessName(form.value.technologyOperationId),
+                productModelId: form.value.productModelId,
+                isQuality: form.value.isQuality,
+                isProduction: form.value.isProduction,
+              })
+            : addOrUpdateProcessRouteItem1({
+                technologyRoutingId: Number(routeId.value),
+                technologyOperationId: form.value.technologyOperationId,
+                productModelId: form.value.productModelId,
+                id: form.value.id,
+                isQuality: form.value.isQuality,
+                isProduction: form.value.isProduction,
+              });
+
+          updatePromise
+            .then(() => {
+              proxy?.$modal?.msgSuccess("淇敼鎴愬姛");
+              closeDialog();
+              getList();
+            })
+            .catch(() => {
+              proxy?.$modal?.msgError("淇敼澶辫触");
+            })
+            .finally(() => {
+              submitLoading.value = false;
             });
         }
       }
     });
-  } else {
-    // 鍗$墖瑙嗗浘鐨勬嫋鎷芥帓搴�
-    if (!cardsContainer.value) return;
+  };
 
-    cardSortable = new Sortable(cardsContainer.value, {
-      animation: 150,
-      ghostClass: 'sortable-ghost',
-      handle: '.process-card',
-      filter: '.el-button',
-      onEnd: (evt) => {
-        if (evt.oldIndex === evt.newIndex || !tableData.value[evt.oldIndex]) return;
+  // 閲嶇疆琛ㄥ崟
+  const resetForm = () => {
+    form.value = {
+      id: undefined,
+      routeId: routeId.value,
+      technologyOperationId: undefined,
+      productModelId: undefined,
+      productName: "",
+      model: "",
+      unit: "",
+      isQuality: false,
+      isProduction: false,
+    };
+    formRef.value?.resetFields();
+  };
 
-        // 閲嶆柊鎺掑簭鏁扮粍
-        const moveItem = tableData.value.splice(evt.oldIndex, 1)[0];
-        tableData.value.splice(evt.newIndex, 0, moveItem);
-        
-        // 璁$畻鏂扮殑搴忓彿锛坉ragSort浠�1寮�濮嬶級
-        const newIndex = evt.newIndex;
-        const dragSort = newIndex + 1;
-        
-        // 璋冪敤鎺掑簭鎺ュ彛
-        if (moveItem.id) {
-          const isOrderPage = pageType.value === 'order';
-          const sortPromise = isOrderPage
-            ? sortRouteItem({
-                id: moveItem.id,
-                dragSort: dragSort
+  // 鍏抽棴寮圭獥
+  const closeDialog = () => {
+    dialogVisible.value = false;
+    resetForm();
+  };
+
+  // 鏌ョ湅鍙傛暟鍒楄〃
+  const handleViewParams = row => {
+    currentProcess.value = row;
+    const param = {
+      productionOrderRoutingOperationId: row.id,
+      productionOrderId: orderId.value,
+    };
+    const param1 = {
+      technologyRoutingOperationId: row.id,
+      productionOrderId: orderId.value,
+    };
+
+    const apiPromise =
+      pageType.value === "order"
+        ? findProcessParamListOrder(param)
+        : getProcessParamList(param1);
+
+    apiPromise
+      .then(res => {
+        paramList.value = res.data || [];
+        showParamListDialog.value = true;
+      })
+      .catch(err => {
+        console.error("鑾峰彇鍙傛暟鍒楄〃澶辫触锛�", err);
+        proxy?.$modal?.msgError("鑾峰彇鍙傛暟鍒楄〃澶辫触");
+      });
+  };
+
+  // 鍒锋柊鍙傛暟鍒楄〃
+  const refreshParamList = () => {
+    if (currentProcess.value) {
+      handleViewParams(currentProcess.value);
+    }
+  };
+
+  // 鍒濆鍖栨嫋鎷芥帓搴�
+  const initSortable = () => {
+    destroySortable();
+    if (!editable.value) return;
+
+    if (viewMode.value === "table") {
+      // 琛ㄦ牸瑙嗗浘鐨勬嫋鎷芥帓搴�
+      if (!tableRef.value) return;
+
+      const tbody =
+        tableRef.value.$el.querySelector(".el-table__body tbody") ||
+        tableRef.value.$el.querySelector(
+          ".el-table__body-wrapper > table > tbody"
+        );
+
+      if (!tbody) return;
+
+      tableSortable = new Sortable(tbody, {
+        animation: 150,
+        ghostClass: "sortable-ghost",
+        handle: ".el-table__row",
+        filter: ".el-button, .el-select",
+        onEnd: evt => {
+          if (evt.oldIndex === evt.newIndex || !tableData.value[evt.oldIndex])
+            return;
+
+          // 閲嶆柊鎺掑簭鏁扮粍
+          const moveItem = tableData.value.splice(evt.oldIndex, 1)[0];
+          tableData.value.splice(evt.newIndex, 0, moveItem);
+
+          // 璁$畻鏂扮殑搴忓彿锛坉ragSort浠�1寮�濮嬶級
+          const newIndex = evt.newIndex;
+          const dragSort = newIndex + 1;
+
+          // 璋冪敤鎺掑簭鎺ュ彛
+          if (moveItem.id) {
+            const isOrderPage = pageType.value === "order";
+            const sortPromise = isOrderPage
+              ? sortRouteItem({
+                  id: moveItem.id,
+                  dragSort: dragSort,
+                })
+              : sortProcessRouteItem({
+                  id: moveItem.id,
+                  dragSort: dragSort,
+                });
+
+            sortPromise
+              .then(() => {
+                // 鏇存柊鎵�鏈夎鐨刣ragSort
+                tableData.value.forEach((item, index) => {
+                  if (item.id) {
+                    item.dragSort = index + 1;
+                  }
+                });
+                proxy?.$modal?.msgSuccess("鎺掑簭鎴愬姛");
               })
-            : sortProcessRouteItem({
-                id: moveItem.id,
-                dragSort: dragSort
+              .catch(err => {
+                // 鎺掑簭澶辫触锛屾仮澶嶅師鏁扮粍
+                tableData.value.splice(newIndex, 1);
+                tableData.value.splice(evt.oldIndex, 0, moveItem);
+                proxy?.$modal?.msgError("鎺掑簭澶辫触");
+                console.error("鎺掑簭澶辫触锛�", err);
               });
+          }
+        },
+      });
+    } else {
+      // 鍗$墖瑙嗗浘鐨勬嫋鎷芥帓搴�
+      if (!cardsContainer.value) return;
 
-          sortPromise
-            .then(() => {
-              // 鏇存柊鎵�鏈夎鐨刣ragSort
-              tableData.value.forEach((item, index) => {
-                if (item.id) {
-                  item.dragSort = index + 1;
-                }
+      cardSortable = new Sortable(cardsContainer.value, {
+        animation: 150,
+        ghostClass: "sortable-ghost",
+        handle: ".process-card",
+        filter: ".el-button",
+        onEnd: evt => {
+          if (evt.oldIndex === evt.newIndex || !tableData.value[evt.oldIndex])
+            return;
+
+          // 閲嶆柊鎺掑簭鏁扮粍
+          const moveItem = tableData.value.splice(evt.oldIndex, 1)[0];
+          tableData.value.splice(evt.newIndex, 0, moveItem);
+
+          // 璁$畻鏂扮殑搴忓彿锛坉ragSort浠�1寮�濮嬶級
+          const newIndex = evt.newIndex;
+          const dragSort = newIndex + 1;
+
+          // 璋冪敤鎺掑簭鎺ュ彛
+          if (moveItem.id) {
+            const isOrderPage = pageType.value === "order";
+            const sortPromise = isOrderPage
+              ? sortRouteItem({
+                  id: moveItem.id,
+                  dragSort: dragSort,
+                })
+              : sortProcessRouteItem({
+                  id: moveItem.id,
+                  dragSort: dragSort,
+                });
+
+            sortPromise
+              .then(() => {
+                // 鏇存柊鎵�鏈夎鐨刣ragSort
+                tableData.value.forEach((item, index) => {
+                  if (item.id) {
+                    item.dragSort = index + 1;
+                  }
+                });
+                proxy?.$modal?.msgSuccess("鎺掑簭鎴愬姛");
+              })
+              .catch(err => {
+                // 鎺掑簭澶辫触锛屾仮澶嶅師鏁扮粍
+                tableData.value.splice(newIndex, 1);
+                tableData.value.splice(evt.oldIndex, 0, moveItem);
+                proxy?.$modal?.msgError("鎺掑簭澶辫触");
+                console.error("鎺掑簭澶辫触锛�", err);
               });
-              proxy?.$modal?.msgSuccess('鎺掑簭鎴愬姛');
-            })
-            .catch((err) => {
-              // 鎺掑簭澶辫触锛屾仮澶嶅師鏁扮粍
-              tableData.value.splice(newIndex, 1);
-              tableData.value.splice(evt.oldIndex, 0, moveItem);
-              proxy?.$modal?.msgError('鎺掑簭澶辫触');
-              console.error("鎺掑簭澶辫触锛�", err);
-            });
-        }
+          }
+        },
+      });
+    }
+  };
+
+  // 閿�姣佹嫋鎷芥帓搴�
+  const destroySortable = () => {
+    if (tableSortable) {
+      tableSortable.destroy();
+      tableSortable = null;
+    }
+    if (cardSortable) {
+      cardSortable.destroy();
+      cardSortable = null;
+    }
+  };
+
+  // BOM鐩稿叧鐘舵�佸拰鏂规硶
+  const bomTableData = ref([
+    {
+      productName: "",
+      model: "",
+      bomNo: "",
+    },
+  ]);
+
+  const bomDataValue = ref({
+    dataList: [],
+    processOptions: [],
+    showProductDialog: false,
+    currentRowName: null,
+    loading: false,
+    isEdit: false,
+  });
+
+  const syncProcessOperationFields = item => {
+    const processId =
+      item.processId ?? item.operationId ?? item.technologyOperationId ?? "";
+    if (!processId) {
+      item.processId = "";
+      return;
+    }
+    const option = bomDataValue.value.processOptions.find(
+      p => p.id === processId
+    );
+    const processName =
+      option?.name || item.processName || item.operationName || "";
+
+    item.processId = processId;
+    if (pageType.value === "order") {
+      item.technologyOperationId = processId;
+    } else {
+      item.operationId = processId;
+    }
+    item.processName = processName;
+    item.operationName = processName;
+  };
+
+  const normalizeTreeData = items => {
+    items.forEach(item => {
+      item.tempId = item.tempId || item.id || `${Date.now()}_${Math.random()}`;
+      syncProcessOperationFields(item);
+      if (Array.isArray(item.children) && item.children.length > 0) {
+        normalizeTreeData(item.children);
       }
     });
-  }
-};
+  };
+  const processChange = value => {
+    processOptions.value.forEach(item => {
+      if (item.id == value) {
+        form.value.isQuality = item.isQuality;
+        form.value.isProduction = item.isProduction;
+      }
+    });
+  };
 
-// 閿�姣佹嫋鎷芥帓搴�
-const destroySortable = () => {
-  if (tableSortable) {
-    tableSortable.destroy();
-    tableSortable = null;
-  }
-  if (cardSortable) {
-    cardSortable.destroy();
-    cardSortable = null;
-  }
-};
+  const handleBomProcessChange = (row, value) => {
+    row.processId = value || "";
+    syncProcessOperationFields(row);
+  };
 
-onMounted(() => {
-  getRouteInfo();
-  getList();
-  getProcessList();
-});
+  const openBomDialog = tempId => {
+    bomDataValue.value.currentRowName = tempId;
+    bomDataValue.value.showProductDialog = true;
+  };
 
-onUnmounted(() => {
-  destroySortable();
-});
+  const fetchBomData = async () => {
+    try {
+      const isOrderPage = pageType.value === "order";
+      const { data } = await (isOrderPage ? queryList2 : queryList)(
+        routeInfo.value.bomId
+      );
+      bomDataValue.value.dataList = data || [];
+      normalizeTreeData(bomDataValue.value.dataList);
+    } catch (err) {
+      console.error("鑾峰彇BOM鏁版嵁澶辫触锛�", err);
+    }
+  };
+
+  const childItem = (item, tempId, productData) => {
+    if (item.tempId === tempId) {
+      item.productName = productData.productName;
+      item.model = productData.model;
+      item.productModelId = productData.id;
+      item.unit = productData.unit || "";
+      return true;
+    }
+    if (item.children && item.children.length > 0) {
+      for (let child of item.children) {
+        if (childItem(child, tempId, productData)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  };
+
+  const handleBomProduct = row => {
+    if (!Array.isArray(row) || row.length === 0) {
+      ElMessage.warning("璇烽�夋嫨涓�涓骇鍝�");
+      return;
+    }
+    const productData = row[row.length - 1];
+
+    const isTopLevel = bomDataValue.value.dataList.some(
+      item => item.tempId === bomDataValue.value.currentRowName
+    );
+    if (isTopLevel) {
+      if (
+        productData.productName === bomTableData.value[0].productName &&
+        productData.model === bomTableData.value[0].model
+      ) {
+        const hasOther = bomDataValue.value.dataList.some(
+          item =>
+            item.tempId !== bomDataValue.value.currentRowName &&
+            item.productName === bomTableData.value[0].productName &&
+            item.model === bomTableData.value[0].model
+        );
+        if (hasOther) {
+          ElMessage.warning("鏈�澶栧眰鍜屽綋鍓嶄骇鍝佷竴鏍风殑涓�绾у彧鑳芥湁涓�涓�");
+          return;
+        }
+      }
+    }
+    bomDataValue.value.dataList.forEach(item => {
+      if (item.tempId === bomDataValue.value.currentRowName) {
+        item.productName = productData.productName;
+        item.model = productData.model;
+        item.productModelId = productData.id;
+        item.unit = productData.unit || "";
+        return;
+      }
+      childItem(item, bomDataValue.value.currentRowName, productData);
+    });
+    bomDataValue.value.showProductDialog = false;
+  };
+
+  const removeBomItem = tempId => {
+    const topIndex = bomDataValue.value.dataList.findIndex(
+      item => item.tempId === tempId
+    );
+    if (topIndex !== -1) {
+      bomDataValue.value.dataList.splice(topIndex, 1);
+      return;
+    }
+
+    const delchildItem = (items, tempId) => {
+      for (let i = 0; i < items.length; i++) {
+        const item = items[i];
+        if (item.tempId === tempId) {
+          items.splice(i, 1);
+          return true;
+        }
+        if (item.children && item.children.length > 0) {
+          if (delchildItem(item.children, tempId)) {
+            return true;
+          }
+        }
+      }
+      return false;
+    };
+
+    bomDataValue.value.dataList.forEach(item => {
+      if (item.children && item.children.length > 0) {
+        delchildItem(item.children, tempId);
+      }
+    });
+  };
+
+  const handleUnitQuantityChange = row => {
+    if (routeInfo.value.quantity && routeInfo.value.quantity !== 0) {
+      row.demandedQuantity = (row.unitQuantity || 0) * routeInfo.value.quantity;
+    }
+  };
+
+  const addchildItem = (item, tempId) => {
+    if (item.tempId === tempId) {
+      if (!item.children) {
+        item.children = [];
+      }
+      item.children.push({
+        parentId: item.id || "",
+        parentTempId: item.tempId || "",
+        productName: "",
+        productId: "",
+        model: undefined,
+        productModelId: undefined,
+        processId: "",
+        processName: "",
+        [pageType.value === "order" ? "technologyOperationId" : "operationId"]:
+          "",
+        operationName: "",
+        unitQuantity: 1,
+        demandedQuantity:
+          routeInfo.value.quantity && routeInfo.value.quantity !== 0
+            ? 1 * routeInfo.value.quantity
+            : 0,
+        children: [],
+        unit: "",
+        tempId: new Date().getTime(),
+      });
+      return true;
+    }
+    if (item.children && item.children.length > 0) {
+      for (let child of item.children) {
+        if (addchildItem(child, tempId)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  };
+
+  const addBomItem = tempId => {
+    bomDataValue.value.dataList.forEach(item => {
+      if (item.tempId === tempId) {
+        if (!item.children) {
+          item.children = [];
+        }
+        item.children.push({
+          parentId: item.id || "",
+          parentTempId: item.tempId || "",
+          productName: "",
+          productId: "",
+          model: undefined,
+          productModelId: undefined,
+          processId: "",
+          processName: "",
+          [pageType.value === "order" ? "technologyOperationId" : "operationId"]:
+            "",
+          operationName: "",
+          unitQuantity: 1,
+          demandedQuantity:
+            routeInfo.value.quantity && routeInfo.value.quantity !== 0
+              ? 1 * routeInfo.value.quantity
+              : 0,
+          unit: "",
+          children: [],
+          tempId: new Date().getTime(),
+        });
+        return;
+      }
+      addchildItem(item, tempId);
+    });
+  };
+
+  const validateAllBom = () => {
+    let isValid = true;
+    const isOrderPage = pageType.value === "order";
+
+    const validateItem = (item, isTopLevel = false) => {
+      if (!item.model) {
+        ElMessage.error("璇烽�夋嫨瑙勬牸");
+        isValid = false;
+        return;
+      }
+      if (!isTopLevel && !item.processId) {
+        ElMessage.error("璇烽�夋嫨娑堣�楀伐搴�");
+        isValid = false;
+        return;
+      }
+      if (!item.unitQuantity) {
+        ElMessage.error("璇疯緭鍏ュ崟浣嶄骇鍑烘墍闇�鏁伴噺");
+        isValid = false;
+        return;
+      }
+      if (isOrderPage && !item.demandedQuantity) {
+        ElMessage.error("璇疯緭鍏ラ渶姹傛�婚噺");
+        isValid = false;
+        return;
+      }
+
+      if (item.children && item.children.length > 0) {
+        item.children.forEach(child => {
+          validateItem(child, false);
+        });
+      }
+    };
+
+    bomDataValue.value.dataList.forEach(item => {
+      validateItem(item, true);
+    });
+
+    return isValid;
+  };
+
+  const buildSubmitTree = items => {
+    return items.map(item => {
+      const current = { ...item };
+      syncProcessOperationFields(current);
+      current.children = Array.isArray(current.children)
+        ? buildSubmitTree(current.children)
+        : [];
+      return current;
+    });
+  };
+
+  const cancelEditBom = () => {
+    bomDataValue.value.isEdit = false;
+    fetchBomData();
+  };
+
+  const handleSaveBom = () => {
+    bomDataValue.value.loading = true;
+    console.log(bomDataValue.value.dataList, "bomDataValue.value.dataList");
+
+    normalizeTreeData(bomDataValue.value.dataList);
+
+    const valid = validateAllBom();
+    if (valid) {
+      add2({
+        // bomId: Number(routeInfo.value.bomId),
+        productionOrderBomId: Number(routeInfo.value.bomId) || null,
+        children: buildSubmitTree(bomDataValue.value.dataList || []),
+      })
+        .then(() => {
+          ElMessage.success("BOM淇濆瓨鎴愬姛");
+          bomDataValue.value.isEdit = false;
+          fetchBomData();
+        })
+        .catch(() => {
+          ElMessage.error("BOM淇濆瓨澶辫触");
+        })
+        .finally(() => {
+          bomDataValue.value.loading = false;
+        });
+    } else {
+      bomDataValue.value.loading = false;
+    }
+  };
+
+  onMounted(() => {
+    getRouteInfo();
+    getList();
+    getProcessList();
+    fetchBomData();
+  });
+
+  onUnmounted(() => {
+    destroySortable();
+  });
 </script>
 
 <style scoped>
-.card-container {
-  padding: 20px 0;
-}
+  .card-container {
+    padding: 20px 0;
+  }
 
-.cards-wrapper {
-  display: flex;
-  gap: 16px;
-  overflow-x: auto;
-  padding: 10px 0;
-  min-height: 200px;
-}
+  .cards-wrapper {
+    display: flex;
+    gap: 16px;
+    overflow-x: auto;
+    padding: 10px 0;
+    min-height: 200px;
+  }
 
-.cards-wrapper::-webkit-scrollbar {
-  height: 8px;
-}
+  .cards-wrapper::-webkit-scrollbar {
+    height: 8px;
+  }
 
-.cards-wrapper::-webkit-scrollbar-track {
-  background: #f1f1f1;
-  border-radius: 4px;
-}
+  .cards-wrapper::-webkit-scrollbar-track {
+    background: #f1f1f1;
+    border-radius: 4px;
+  }
 
-.cards-wrapper::-webkit-scrollbar-thumb {
-  background: #c1c1c1;
-  border-radius: 4px;
-}
+  .cards-wrapper::-webkit-scrollbar-thumb {
+    background: #c1c1c1;
+    border-radius: 4px;
+  }
 
-.cards-wrapper::-webkit-scrollbar-thumb:hover {
-  background: #a8a8a8;
-}
+  .cards-wrapper::-webkit-scrollbar-thumb:hover {
+    background: #a8a8a8;
+  }
 
-.process-card {
-  flex-shrink: 0;
-  width: 220px;
-  background: #fff;
-  border-radius: 8px;
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-  padding: 16px;
-  display: flex;
-  flex-direction: column;
-  cursor: move;
-  transition: all 0.3s;
-}
+  .process-card {
+    flex-shrink: 0;
+    width: 220px;
+    background: #fff;
+    border-radius: 8px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+    padding: 16px;
+    display: flex;
+    flex-direction: column;
+    cursor: move;
+    transition: all 0.3s;
+  }
 
-.process-card:hover {
-  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
-  transform: translateY(-2px);
-}
+  .process-card:hover {
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+    transform: translateY(-2px);
+  }
 
-.card-header {
-  text-align: center;
-  margin-bottom: 12px;
-}
+  .card-header {
+    text-align: center;
+    margin-bottom: 12px;
+  }
 
-.card-number {
-  width: 36px;
-  height: 36px;
-  line-height: 36px;
-  border-radius: 50%;
-  background: #409eff;
-  color: #fff;
-  font-weight: bold;
-  font-size: 16px;
-  margin: 0 auto 8px;
-}
+  .card-number {
+    width: 36px;
+    height: 36px;
+    line-height: 36px;
+    border-radius: 50%;
+    background: #409eff;
+    color: #fff;
+    font-weight: bold;
+    font-size: 16px;
+    margin: 0 auto 8px;
+  }
 
-.card-process-name {
-  font-size: 14px;
-  color: #333;
-  font-weight: 500;
-  word-break: break-all;
-}
+  .card-process-name {
+    font-size: 14px;
+    color: #333;
+    font-weight: 500;
+    word-break: break-all;
+  }
 
-.card-content {
-  flex: 1;
-  margin-bottom: 12px;
-  min-height: 60px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
+  .card-content {
+    flex: 1;
+    margin-bottom: 12px;
+    min-height: 60px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
 
-.product-info {
-  font-size: 13px;
-  color: #666;
-  text-align: center;
-  width: 100%;
-}
+  .product-info {
+    font-size: 13px;
+    color: #666;
+    text-align: center;
+    width: 100%;
+  }
 
-.product-info.empty {
-  color: #999;
-  text-align: center;
-  padding: 20px 0;
-}
+  .product-info.empty {
+    color: #999;
+    text-align: center;
+    padding: 20px 0;
+  }
 
-.product-name {
-  margin-bottom: 6px;
-  word-break: break-all;
-  line-height: 1.5;
-  text-align: center;
-}
+  .product-name {
+    margin-bottom: 6px;
+    word-break: break-all;
+    line-height: 1.5;
+    text-align: center;
+  }
 
-.product-model {
-  color: #909399;
-  font-size: 12px;
-  word-break: break-all;
-  line-height: 1.5;
-  text-align: center;
-}
+  .product-model {
+    color: #909399;
+    font-size: 12px;
+    word-break: break-all;
+    line-height: 1.5;
+    text-align: center;
+  }
 
-.product-unit {
-  margin-left: 4px;
-  color: #409eff;
-}
+  .product-unit {
+    margin-left: 4px;
+    color: #409eff;
+  }
 
-.product-tag {
-  margin: 10px 0;
-}
+  .product-tag {
+    margin: 10px 0;
+  }
 
-.card-footer {
-  display: flex;
-  justify-content: space-around;
-  padding-top: 12px;
-  border-top: 1px solid #f0f0f0;
-}
+  .card-footer {
+    display: flex;
+    justify-content: space-around;
+    padding-top: 12px;
+    border-top: 1px solid #f0f0f0;
+  }
 
-.card-footer .el-button {
-  padding: 0;
-  font-size: 12px;
-}
+  .card-footer .el-button {
+    padding: 0;
+    font-size: 12px;
+  }
 
-:deep(.sortable-ghost) {
-  opacity: 0.5;
-  background-color: #f5f7fa !important;
-}
+  :deep(.sortable-ghost) {
+    opacity: 0.5;
+    background-color: #f5f7fa !important;
+  }
 
-:deep(.sortable-drag) {
-  opacity: 0.8;
-}
+  :deep(.sortable-drag) {
+    opacity: 0.8;
+  }
 
-/* 琛ㄦ牸瑙嗗浘鏍峰紡 */
-:deep(.el-table__row) {
-  transition: background-color 0.2s;
-  cursor: move;
-}
+  /* 琛ㄦ牸瑙嗗浘鏍峰紡 */
+  :deep(.el-table__row) {
+    transition: background-color 0.2s;
+    cursor: move;
+  }
 
-:deep(.el-table__row:hover) {
-  background-color: #f9fafc !important;
-}
+  :deep(.el-table__row:hover) {
+    background-color: #f9fafc !important;
+  }
 
-/* 鍖哄煙鏍囬鏍峰紡 */
-.section-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 12px;
-}
+  /* 鍖哄煙鏍囬鏍峰紡 */
+  .section-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 12px;
+  }
 
-.section-title {
-  font-size: 16px;
-  font-weight: 600;
-  color: #303133;
-  padding-left: 12px;
-  position: relative;
-  margin-bottom: 0;
-}
+  .section-title {
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+    padding-left: 12px;
+    position: relative;
+    margin-bottom: 0;
+  }
 
-.section-title::before {
-  content: '';
-  position: absolute;
-  left: 0;
-  top: 50%;
-  transform: translateY(-50%);
-  width: 3px;
-  height: 16px;
-  background: #409eff;
-  border-radius: 2px;
-}
+  .section-title::before {
+    content: "";
+    position: absolute;
+    left: 0;
+    top: 50%;
+    transform: translateY(-50%);
+    width: 3px;
+    height: 16px;
+    background: #409eff;
+    border-radius: 2px;
+  }
 
-.section-actions {
-  display: flex;
-  align-items: center;
-}
+  .section-actions {
+    display: flex;
+    align-items: center;
+  }
 
-/* 宸ヨ壓璺嚎淇℃伅鍗$墖鏍峰紡 */
-.route-info-card {
-  margin-bottom: 20px;
-  border: 1px solid #e4e7ed;
-  background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
-  border-radius: 8px;
-  overflow: hidden;
-}
+  /* 宸ヨ壓璺嚎淇℃伅鍗$墖鏍峰紡 */
+  .route-info-card {
+    margin-bottom: 20px;
+    border: 1px solid #e4e7ed;
+    background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
+    border-radius: 8px;
+    overflow: hidden;
+  }
 
-.route-info {
-  display: grid;
-  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
-  gap: 16px;
-  padding: 4px;
-}
+  .route-info {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+    gap: 16px;
+    padding: 4px;
+  }
 
-.info-item {
-  display: flex;
-  flex-direction: column;
-  background: #ffffff;
-  border-radius: 6px;
-  padding: 14px 16px;
-  border: 1px solid #f0f2f5;
-  transition: all 0.3s ease;
-  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
-}
+  .info-item {
+    display: flex;
+    flex-direction: column;
+    background: #ffffff;
+    border-radius: 6px;
+    padding: 14px 16px;
+    border: 1px solid #f0f2f5;
+    transition: all 0.3s ease;
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+  }
 
-.info-item:hover {
-  border-color: #409eff;
-  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.15);
-  transform: translateY(-1px);
-}
+  .info-item:hover {
+    border-color: #409eff;
+    box-shadow: 0 2px 8px rgba(64, 158, 255, 0.15);
+    transform: translateY(-1px);
+  }
 
-.info-item.full-width {
-  grid-column: 1 / -1;
-}
+  .info-item.full-width {
+    grid-column: 1 / -1;
+  }
 
-.info-label-wrapper {
-  margin-bottom: 8px;
-}
+  .info-label-wrapper {
+    margin-bottom: 8px;
+  }
 
-.info-label {
-  display: inline-block;
-  color: #909399;
-  font-size: 12px;
-  font-weight: 500;
-  text-transform: uppercase;
-  letter-spacing: 0.5px;
-  padding: 2px 0;
-  position: relative;
-}
+  .info-label {
+    display: inline-block;
+    color: #909399;
+    font-size: 12px;
+    font-weight: 500;
+    text-transform: uppercase;
+    letter-spacing: 0.5px;
+    padding: 2px 0;
+    position: relative;
+  }
 
-.info-label::after {
-  content: '';
-  position: absolute;
-  left: 0;
-  bottom: 0;
-  width: 20px;
-  height: 2px;
-  background: linear-gradient(90deg, #409eff, transparent);
-  border-radius: 1px;
-}
+  .info-label::after {
+    content: "";
+    position: absolute;
+    left: 0;
+    bottom: 0;
+    width: 20px;
+    height: 2px;
+    background: linear-gradient(90deg, #409eff, transparent);
+    border-radius: 1px;
+  }
 
-.info-value-wrapper {
-  flex: 1;
-}
+  .info-value-wrapper {
+    flex: 1;
+  }
 
-.info-value {
-  display: block;
-  color: #303133;
-  font-size: 15px;
-  font-weight: 500;
-  line-height: 1.5;
-  word-break: break-all;
-}
+  .info-value {
+    display: block;
+    color: #303133;
+    font-size: 15px;
+    font-weight: 500;
+    line-height: 1.5;
+    word-break: break-all;
+  }
 </style>
diff --git a/src/views/productionManagement/processStatistics/index.vue b/src/views/productionManagement/processStatistics/index.vue
new file mode 100644
index 0000000..25fa531
--- /dev/null
+++ b/src/views/productionManagement/processStatistics/index.vue
@@ -0,0 +1,268 @@
+<template>
+  <div class="app-container">
+    <div class="search-bar">
+      <el-form :model="searchForm"
+               inline>
+        <el-form-item label="鏃ユ湡鍖洪棿:">
+          <el-date-picker v-model="searchForm.dateRange"
+                          type="daterange"
+                          range-separator="鑷�"
+                          start-placeholder="寮�濮嬫棩鏈�"
+                          end-placeholder="缁撴潫鏃ユ湡"
+                          value-format="YYYY-MM-DD"
+                          style="width: 240px" />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary"
+                     icon="Search"
+                     @click="handleQuery">鎼滅储</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <div class="stats-grid"
+         v-loading="loading">
+      <el-row :gutter="16"
+              v-if="statsData.length > 0">
+        <el-col v-for="(item, index) in statsData"
+                :key="index"
+                :xs="24"
+                :sm="12"
+                :md="8"
+                :lg="4.8"
+                :xl="4.8"
+                class="mb-16">
+          <div class="stats-card">
+            <div class="card-header">
+              <span class="process-name">{{ item.name }}</span>
+              <div class="header-stats">
+                <div class="stat-row">
+                  <span class="label">璁″垝鏁�</span>
+                  <span class="value">{{ item.planned }}</span>
+                </div>
+                <div class="stat-row">
+                  <span class="label">鑹搧鏁�</span>
+                  <span class="value">{{ item.good }}</span>
+                </div>
+                <div class="stat-row">
+                  <span class="label">涓嶈壇鍝佹暟</span>
+                  <span class="value">{{ item.bad }}</span>
+                </div>
+              </div>
+            </div>
+            <div class="card-body">
+              <div class="main-stat">
+                <div class="big-number">{{ item.total }}</div>
+                <div class="sub-label">鐢熶骇浠诲姟鏁�</div>
+              </div>
+            </div>
+            <div class="card-footer">
+              <div class="progress-info">
+                <span class="progress-label">杩涘害:</span>
+                <el-progress :percentage="Math.min(item.percentage, 100)"
+                             :color="getProgressColor(item.percentage)"
+                             :stroke-width="10"
+                             :show-text="false"
+                             class="flex-1" />
+                <span class="percentage-text">{{ item.percentage }}%</span>
+              </div>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+      <el-empty v-else
+                description="鏆傛棤鏁版嵁" />
+    </div>
+  </div>
+</template>
+
+<script setup>
+  import { reactive, ref, onMounted } from "vue";
+  import dayjs from "dayjs";
+  import { getOperationStatistics } from "@/api/productionManagement/workOrder.js";
+
+  const loading = ref(false);
+  const searchForm = reactive({
+    dateRange: [],
+  });
+
+  const statsData = ref([]);
+
+  const getProgressColor = percentage => {
+    if (percentage >= 100) return "#67c23a";
+    if (percentage >= 50) return "#409eff";
+    if (percentage >= 25) return "#e6a23c";
+    return "red";
+  };
+
+  const getList = () => {
+    loading.value = true;
+    const params = {
+      startDate: searchForm.dateRange?.[0] || "",
+      endDate: searchForm.dateRange?.[1] || "",
+    };
+    getOperationStatistics(params)
+      .then(res => {
+        // 鏍规嵁瀹為檯鎺ュ彛杩斿洖鐨勫瓧娈佃繘琛屾槧灏�
+        statsData.value = (res.data || []).map(item => ({
+          name: item.operationName || "-",
+          total: item.productionTaskCount || 0,
+          planned: item.planQuantity || 0,
+          good: item.goodQuantity || 0,
+          bad: item.scrapQty || 0,
+          percentage: Number(item.completionStatus || 0),
+        }));
+      })
+      .finally(() => {
+        loading.value = false;
+      });
+  };
+
+  const handleQuery = () => {
+    getList();
+  };
+
+  onMounted(() => {
+    getList();
+  });
+</script>
+
+<style scoped lang="scss">
+  .app-container {
+    padding: 20px;
+    background-color: #f0f2f5;
+    min-height: calc(100vh - 84px);
+  }
+
+  .search-bar {
+    background: #fff;
+    padding: 15px 20px 0;
+    border-radius: 4px;
+    margin-bottom: 20px;
+    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
+  }
+
+  .mb-16 {
+    margin-bottom: 16px;
+  }
+
+  // 妯℃嫙 lg="4.8" 鍥犱负 element 涓嶆敮鎸� 24/5
+  @media only screen and (min-width: 1200px) {
+    .el-col-lg-4-8 {
+      width: 20%;
+      max-width: 20%;
+      flex: 0 0 20%;
+    }
+  }
+
+  .stats-card {
+    background: #fff;
+    border-radius: 8px;
+    padding: 16px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+    transition: transform 0.3s;
+
+    &:hover {
+      transform: translateY(-2px);
+    }
+
+    .card-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: flex-start;
+      margin-bottom: 12px;
+
+      .process-name {
+        background-color: #e6f7ff;
+        color: #1890ff;
+        padding: 2px 8px;
+        border-radius: 4px;
+        font-size: 14px;
+        font-weight: 500;
+      }
+
+      .header-stats {
+        text-align: right;
+
+        .stat-row {
+          display: flex;
+          justify-content: flex-end;
+          align-items: center;
+          gap: 8px;
+          margin-bottom: 2px;
+
+          .label {
+            font-size: 12px;
+            color: #909399;
+          }
+
+          .value {
+            font-size: 13px;
+            color: #303133;
+            font-weight: bold;
+            min-width: 24px;
+          }
+        }
+      }
+    }
+
+    .card-body {
+      padding: 10px 0;
+
+      .main-stat {
+        .big-number {
+          font-size: 28px;
+          font-weight: bold;
+          color: #303133;
+          line-height: 1;
+        }
+
+        .sub-label {
+          font-size: 14px;
+          color: #606266;
+          margin-top: 8px;
+          font-weight: 500;
+        }
+      }
+    }
+
+    .card-footer {
+      margin-top: 16px;
+
+      .progress-info {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+
+        .progress-label {
+          font-size: 12px;
+          color: #909399;
+          white-space: nowrap;
+        }
+
+        .flex-1 {
+          flex: 1;
+        }
+
+        .percentage-text {
+          font-size: 12px;
+          color: #606266;
+          min-width: 45px;
+          text-align: right;
+        }
+      }
+    }
+  }
+
+  // 淇 el-col 甯冨眬閫傞厤 5 鍒�
+  :deep(.el-row) {
+    display: flex;
+    flex-wrap: wrap;
+  }
+
+  @media only screen and (min-width: 1200px) {
+    .el-col-lg-4\.8 {
+      flex: 0 0 20%;
+      max-width: 20%;
+    }
+  }
+</style>
diff --git a/src/views/productionManagement/productStructure/Detail/index.vue b/src/views/productionManagement/productStructure/Detail/index.vue
index 6734830..750d584 100644
--- a/src/views/productionManagement/productStructure/Detail/index.vue
+++ b/src/views/productionManagement/productStructure/Detail/index.vue
@@ -64,6 +64,7 @@
                                filterable
                                clearable
                                style="width: 100%"
+                               @change="value => handleProcessChange(row, value)"
                                :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)">
                       <el-option v-for="item in dataValue.processOptions"
                                  :key="item.id"
@@ -148,6 +149,7 @@
     </el-table>
     <product-select-dialog v-if="dataValue.showProductDialog"
                            v-model:model-value="dataValue.showProductDialog"
+                           :single="true"
                            @confirm="handleProduct" />
   </div>
 </template>
@@ -161,7 +163,10 @@
     reactive,
     ref,
   } from "vue";
-  import { queryList, add } from "@/api/productionManagement/productStructure.js";
+  import {
+    queryList,
+    addBomDetail,
+  } from "@/api/productionManagement/productStructure.js";
   import { listProcessBom } from "@/api/productionManagement/productionOrder.js";
   import { list } from "@/api/productionManagement/productionProcess";
   import { ElMessage } from "element-plus";
@@ -212,6 +217,73 @@
     isEdit: false,
   });
 
+  const normalizeListData = (source: any) => {
+    if (Array.isArray(source)) {
+      return source;
+    }
+    if (Array.isArray(source?.records)) {
+      return source.records;
+    }
+    return [];
+  };
+
+  const getProcessOptionById = (id: any) => {
+    if (id === undefined || id === null || id === "") {
+      return null;
+    }
+    return (
+      normalizeListData(dataValue.processOptions).find(
+        option => String(option.id) === String(id)
+      ) || null
+    );
+  };
+
+  const syncProcessOperationFields = (item: any) => {
+    const processId = item.processId ?? item.operationId ?? "";
+    if (!processId) {
+      item.processId = "";
+      item.operationId = "";
+      item.processName = "";
+      item.operationName = "";
+      return;
+    }
+
+    const option = getProcessOptionById(processId);
+    const processName =
+      option?.name || item.processName || item.operationName || "";
+
+    item.processId = processId;
+    item.operationId = processId;
+    item.processName = processName;
+    item.operationName = processName;
+  };
+
+  const normalizeTreeData = (items: any[]) => {
+    items.forEach((item: any) => {
+      item.tempId = item.tempId || item.id || `${Date.now()}_${Math.random()}`;
+      syncProcessOperationFields(item);
+      if (Array.isArray(item.children) && item.children.length > 0) {
+        normalizeTreeData(item.children);
+      }
+    });
+  };
+
+  const buildSubmitTree = (items: any[]) => {
+    return items.map((item: any) => {
+      const current = { ...item };
+      syncProcessOperationFields(current);
+      current.children = Array.isArray(current.children)
+        ? buildSubmitTree(current.children)
+        : [];
+      return current;
+    });
+  };
+
+  const handleProcessChange = (row: any, value: any) => {
+    row.processId = value || "";
+    syncProcessOperationFields(row);
+  };
+
   const tableData = reactive([
     {
       productName: "",
@@ -231,37 +303,30 @@
       // 璁㈠崟鎯呭喌锛氫娇鐢ㄨ鍗曠殑浜у搧缁撴瀯鎺ュ彛
       const { data } = await listProcessBom({ orderId: routeOrderId.value });
       dataValue.dataList = (data as any) || [];
+      normalizeTreeData(dataValue.dataList);
     } else {
       // 闈炶鍗曟儏鍐碉細浣跨敤鍘熸潵鐨勬帴鍙�
       const { data } = await queryList(routeId.value);
       dataValue.dataList = (data as any) || [];
-      // 涓烘墍鏈夐」鍙婂叾瀛愰」璁剧疆name灞炴��
-      const setNameRecursively = (items: any[]) => {
-        items.forEach((item: any) => {
-          item.tempId = item.id;
-          item.processName =
-            dataValue.processOptions.find(option => option.id === item.processId)
-              ?.name || "";
-          if (item.children && item.children.length > 0) {
-            setNameRecursively(item.children);
-          }
-        });
-      };
-      setNameRecursively(dataValue.dataList);
+      console.log(dataValue);
+      normalizeTreeData(dataValue.dataList);
       console.log(dataValue.dataList, "dataValue.dataList");
     }
   };
 
   const fetchProcessOptions = async () => {
-    const { data } = await list();
-    dataValue.processOptions = data as any;
+    const { data } = await list({});
+    console.log(data, "dataValue.dataList");
+    dataValue.processOptions = normalizeListData(data);
   };
 
   const handleProduct = (row: any) => {
-    if (row?.length > 1) {
-      ElMessage.error("鍙兘閫夋嫨涓�涓骇鍝�");
+    if (!Array.isArray(row) || row.length === 0) {
+      ElMessage.warning("璇烽�夋嫨涓�涓骇鍝�");
+      return;
     }
-    const productData = row[0];
+    // 鍙厑璁镐竴涓細濡傛灉涓婃父杩斿洖浜嗗涓紝榛樿浣跨敤鏈�鍚庝竴娆¢�夋嫨骞惰鐩栧綋鍓嶅��
+    const productData = row[row.length - 1];
 
     //  鏈�澶栧眰缁勪欢涓紝涓庡綋鍓嶄骇鍝佺浉鍚岀殑浜у搧鍙兘鏈変竴涓�
     const isTopLevel = dataValue.dataList.some(
@@ -371,19 +436,18 @@
 
   const submit = () => {
     dataValue.loading = true;
+    normalizeTreeData(dataValue.dataList);
 
     // 鍏堣繘琛岃〃鍗曟牎楠�
     const valid = validateAll();
     console.log(dataValue.dataList, "dataValue.dataList");
     if (valid) {
-      add({
+      addBomDetail({
         bomId: routeId.value,
-        children: dataValue.dataList || [],
+        children: buildSubmitTree(dataValue.dataList || []),
       })
         .then(res => {
-          router.push({
-            path: "/productionManagement/productionManagement/productStructure/index",
-          });
+          router.go(-1);
           ElMessage.success("淇濆瓨鎴愬姛");
           dataValue.loading = false;
         })
@@ -441,7 +505,9 @@
           productModelId: undefined,
           processId: "",
           processName: "",
-          unitQuantity: 0,
+          operationId: "",
+          operationName: "",
+          unitQuantity: 1,
           demandedQuantity: 0,
           unit: "",
           children: [],
@@ -467,7 +533,10 @@
         model: undefined,
         productModelId: undefined,
         processId: "",
-        unitQuantity: 0,
+        processName: "",
+        operationId: "",
+        operationName: "",
+        unitQuantity: 1,
         demandedQuantity: 0,
         children: [],
         unit: "",
diff --git a/src/views/productionManagement/productStructure/index.vue b/src/views/productionManagement/productStructure/index.vue
index 2c109cd..e05ed3c 100644
--- a/src/views/productionManagement/productStructure/index.vue
+++ b/src/views/productionManagement/productStructure/index.vue
@@ -1,380 +1,514 @@
 <template>
   <div class="app-container">
-    <div style="text-align: right; margin-bottom: 10px;">
-      <el-button type="info" plain icon="Upload" @click="handleImport"
-        v-hasPermi="['product:bom:import']">瀵煎叆</el-button>
-      <el-button type="warning" plain icon="Download" @click="handleExport" :disabled="selectedRows.length !== 1"
-        v-hasPermi="['product:bom:export']">瀵煎嚭</el-button>
-      <el-button type="primary" @click="handleAdd">鏂板</el-button>
-      <el-button type="danger" plain @click="handleBatchDelete" :disabled="selectedRows.length === 0">鍒犻櫎</el-button>
+    <div class="table_list">
+      <div style="text-align: right; margin-bottom: 10px;">
+        <el-button type="primary"
+                   @click="handleAdd">鏂板</el-button>
+        <el-button type="info"
+                   plain
+                   icon="Upload"
+                   @click="handleImport"
+                   v-hasPermi="['product:bom:import']">瀵煎叆</el-button>
+        <el-button type="warning"
+                   plain
+                   icon="Download"
+                   @click="handleExport"
+                   :disabled="selectedRows.length !== 1"
+                   v-hasPermi="['product:bom:export']">瀵煎嚭</el-button>
+        <el-button type="danger"
+                   plain
+                   @click="handleBatchDelete"
+                   :disabled="selectedRows.length === 0">鍒犻櫎</el-button>
+      </div>
+      <PIMTable rowKey="id"
+                :column="tableColumn"
+                :tableData="tableData"
+                :page="page"
+                :isSelection="true"
+                @selection-change="handleSelectionChange"
+                :tableLoading="tableLoading"
+                @pagination="pagination">
+        <template #detail="{ row }">
+          <el-button type="primary"
+                     text
+                     @click="showDetail(row)">{{ row.bomNo }}
+          </el-button>
+        </template>
+      </PIMTable>
     </div>
-    <PIMTable rowKey="id" :column="tableColumn" :tableData="tableData" :page="page" :isSelection="true"
-      @selection-change="handleSelectionChange" :tableLoading="tableLoading" @pagination="pagination">
-      <template #detail="{ row }">
-        <el-button type="primary" text @click="showDetail(row)">{{ row.bomNo }}
-        </el-button>
-      </template>
-    </PIMTable>
-    <StructureEdit v-if="showEdit" v-model:show-model="showEdit" :record="currentRow" />
-
+    <StructureEdit v-if="showEdit"
+                   v-model:show-model="showEdit"
+                   :record="currentRow" />
     <!-- 鏂板/缂栬緫寮圭獥 -->
-    <el-dialog v-model="dialogVisible" :title="operationType === 'add' ? '鏂板BOM' : '缂栬緫BOM'" width="600px"
-      @close="closeDialog">
-      <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
-        <el-form-item label="浜у搧鍚嶇О" prop="productModelId">
-          <el-button type="primary" @click="showProductSelectDialog = true">
+    <el-dialog v-model="dialogVisible"
+               :title="operationType === 'add' ? '鏂板BOM' : '缂栬緫BOM'"
+               width="600px"
+               @close="closeDialog">
+      <el-form ref="formRef"
+               :model="form"
+               :rules="rules"
+               label-width="120px">
+        <el-form-item label="浜у搧鍚嶇О"
+                      prop="productModelId">
+          <el-button type="primary"
+                     @click="showProductSelectDialog = true">
             {{ form.productName || '閫夋嫨浜у搧' }}
           </el-button>
         </el-form-item>
-        <el-form-item label="鐗堟湰鍙�" prop="version">
-          <el-input v-model="form.version" placeholder="璇疯緭鍏ョ増鏈彿" clearable />
+        <el-form-item label="鐗堟湰鍙�"
+                      prop="version">
+          <el-input v-model="form.version"
+                    placeholder="璇疯緭鍏ョ増鏈彿"
+                    clearable />
         </el-form-item>
-        <el-form-item label="澶囨敞" prop="remark">
-          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" clearable />
+        <el-form-item label="澶囨敞"
+                      prop="remark">
+          <el-input v-model="form.remark"
+                    type="textarea"
+                    :rows="3"
+                    placeholder="璇疯緭鍏ュ娉�"
+                    clearable />
         </el-form-item>
       </el-form>
       <template #footer>
-        <el-button type="primary" @click="handleSubmit">纭畾</el-button>
+        <el-button type="primary"
+                   @click="handleSubmit">纭畾</el-button>
         <el-button @click="closeDialog">鍙栨秷</el-button>
       </template>
     </el-dialog>
-
     <!-- 浜у搧閫夋嫨寮圭獥 -->
-    <ProductSelectDialog v-model="showProductSelectDialog" @confirm="handleProductSelect" single />
-
+    <ProductSelectDialog v-model="showProductSelectDialog"
+                         @confirm="handleProductSelect"
+                         single />
     <!-- BOM瀵煎叆瀵硅瘽妗� -->
-    <ImportDialog ref="uploadRef" v-model="upload.open" :title="upload.title" :action="upload.url"
-      :headers="upload.headers" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress"
-      :on-success="handleFileSuccess" :show-download-template="true" @confirm="submitFileForm"
-      @download-template="handleDownloadTemplate" @close="handleImportClose" />
+    <ImportDialog ref="uploadRef"
+                  v-model="upload.open"
+                  :title="upload.title"
+                  :action="upload.url"
+                  :headers="upload.headers"
+                  :disabled="upload.isUploading"
+                  :on-progress="handleFileUploadProgress"
+                  :on-success="handleFileSuccess"
+                  :show-download-template="true"
+                  @confirm="submitFileForm"
+                  @download-template="handleDownloadTemplate"
+                  @close="handleImportClose" />
   </div>
 </template>
 
 <script setup>
-import { ref, reactive, toRefs, onMounted, getCurrentInstance, defineAsyncComponent } from "vue";
-import { getToken } from "@/utils/auth";
-import { listPage, add, update, batchDelete, exportBom, downloadTemplate } from "@/api/productionManagement/productBom.js";
-import { useRouter } from 'vue-router'
-import { ElMessageBox } from 'element-plus'
-import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
-import ImportDialog from "@/components/Dialog/ImportDialog.vue";
+  import {
+    ref,
+    reactive,
+    toRefs,
+    onMounted,
+    getCurrentInstance,
+    defineAsyncComponent,
+  } from "vue";
+  import { getToken } from "@/utils/auth";
+  import {
+    listPage,
+    add,
+    copy,
+    update,
+    batchDelete,
+    exportBom,
+    downloadTemplate,
+  } from "@/api/productionManagement/productBom.js";
+  import { useRouter } from "vue-router";
+  import { ElMessageBox } from "element-plus";
+  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+  import ImportDialog from "@/components/Dialog/ImportDialog.vue";
 
-const router = useRouter()
-const { proxy } = getCurrentInstance()
-const StructureEdit = defineAsyncComponent(() => import('@/views/productionManagement/productStructure/StructureEdit.vue'))
+  const router = useRouter();
+  const { proxy } = getCurrentInstance();
+  const StructureEdit = defineAsyncComponent(() =>
+    import("@/views/productionManagement/productStructure/StructureEdit.vue")
+  );
 
-const tableColumn = ref([
-  {
-    label: "BOM缂栧彿",
-    prop: "bomNo",
-    dataType: 'slot',
-    slot: "detail",
-    minWidth: 140
-  },
-  {
-    label: "浜у搧鍚嶇О",
-    prop: "productName",
+  const tableColumn = ref([
+    {
+      label: "BOM缂栧彿",
+      prop: "bomNo",
+      dataType: "slot",
+      slot: "detail",
+      minWidth: 140,
+    },
+    {
+      label: "浜у搧鍚嶇О",
+      prop: "productName",
 
-    minWidth: 160
-  },
-  {
-    label: "瑙勬牸鍨嬪彿",
-    prop: "productModelName",
-    minWidth: 140
-  },
-  {
-    label: "鐗堟湰鍙�",
-    prop: "version",
-    width: 100
-  },
-  {
-    label: "澶囨敞",
-    prop: "remark",
-    minWidth: 160
-  },
-  {
-    dataType: "action",
-    label: "鎿嶄綔",
-    align: "center",
-    fixed: "right",
-    width: 150,
-    operation: [
-      {
-        name: "缂栬緫",
-        type: "text",
-        clickFun: (row) => {
-          handleEdit(row)
-        }
-      },
-      {
-        name: "鍒犻櫎",
-        type: "danger",
-        link: true,
-        clickFun: (row) => {
-          handleDelete(row)
-        }
-      }
-    ]
-  }
-]);
+      minWidth: 160,
+    },
+    {
+      label: "瑙勬牸鍨嬪彿",
+      prop: "productModelName",
+      minWidth: 140,
+    },
+    {
+      label: "鐗堟湰鍙�",
+      prop: "version",
+      width: 100,
+    },
+    {
+      label: "澶囨敞",
+      prop: "remark",
+      minWidth: 160,
+    },
+    {
+      dataType: "action",
+      label: "鎿嶄綔",
+      align: "center",
+      fixed: "right",
+      width: 250,
+      operation: [
+        {
+          name: "澶嶅埗",
+          type: "text",
+          clickFun: row => {
+            handleCopy(row);
+          },
+        },
+        {
+          name: "缂栬緫",
+          type: "text",
+          clickFun: row => {
+            handleEdit(row);
+          },
+        },
+        {
+          name: "鍒犻櫎",
+          type: "danger",
+          link: true,
+          clickFun: row => {
+            handleDelete(row);
+          },
+        },
+      ],
+    },
+  ]);
 
-const tableData = ref([]);
-const tableLoading = ref(false);
-const showEdit = ref(false);
-const selectedRows = ref([]);
-const currentRow = ref({});
-const dialogVisible = ref(false);
-const operationType = ref('add'); // add | edit
-const formRef = ref(null);
-const showProductSelectDialog = ref(false);
+  const tableData = ref([]);
+  const tableLoading = ref(false);
+  const showEdit = ref(false);
+  const selectedRows = ref([]);
+  const currentRow = ref({});
+  const dialogVisible = ref(false);
+  const operationType = ref("add"); // add | edit
+  const formRef = ref(null);
+  const showProductSelectDialog = ref(false);
 
-//  BOM瀵煎叆鍙傛暟
-const upload = reactive({
-  // 鏄惁鏄剧ず寮瑰嚭灞傦紙BOM瀵煎叆锛�
-  open: false,
-  // 寮瑰嚭灞傛爣棰橈紙BOM瀵煎叆锛�
-  title: "",
-  // 鏄惁绂佺敤涓婁紶
-  isUploading: false,
-  // 璁剧疆涓婁紶鐨勮姹傚ご閮�
-  headers: { Authorization: "Bearer " + getToken() },
-  // 涓婁紶鐨勫湴鍧�
-  url: import.meta.env.VITE_APP_BASE_API + "/productBom/uploadBom"
-});
-
-const page = reactive({
-  current: 1,
-  size: 10,
-  total: 0,
-});
-
-const data = reactive({
-  form: {
-    id: undefined,
-    productName: "",
-    productModelName: "",
-    productModelId: "",
-    remark: "",
-    version: ""
-  },
-  rules: {
-    productModelId: [{ required: true, message: "璇烽�夋嫨浜у搧", trigger: "change" }],
-    version: [{ required: true, message: "璇疯緭鍏ョ増鏈彿", trigger: "blur" }]
-  }
-});
-
-const { form, rules } = toRefs(data);
-
-// 琛ㄦ牸閫夋嫨鏁版嵁
-const handleSelectionChange = (selection) => {
-  selectedRows.value = selection;
-};
-
-// 鍒嗛〉
-const pagination = (obj) => {
-  page.current = obj.page;
-  page.size = obj.limit;
-  getList();
-};
-
-// 鏌ヨ鍒楄〃
-const getList = () => {
-  tableLoading.value = true;
-  listPage({
-    current: page.current,
-    size: page.size,
-  })
-    .then((res) => {
-      const records = res?.data?.records || [];
-      tableData.value = records;
-      page.total = res?.data?.total || 0;
-    })
-    .catch((err) => {
-      console.error("鑾峰彇鍒楄〃澶辫触锛�", err);
-    })
-    .finally(() => {
-      tableLoading.value = false;
-    });
-};
-
-// 鏂板
-const handleAdd = () => {
-  operationType.value = 'add';
-  Object.assign(form.value, {
-    id: undefined,
-    productName: "",
-    productModelName: "",
-    productModelId: "",
-    remark: "",
-    version: ""
+  //  BOM瀵煎叆鍙傛暟
+  const upload = reactive({
+    // 鏄惁鏄剧ず寮瑰嚭灞傦紙BOM瀵煎叆锛�
+    open: false,
+    // 寮瑰嚭灞傛爣棰橈紙BOM瀵煎叆锛�
+    title: "",
+    // 鏄惁绂佺敤涓婁紶
+    isUploading: false,
+    // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+    headers: { Authorization: "Bearer " + getToken() },
+    // 涓婁紶鐨勫湴鍧�
+    url: import.meta.env.VITE_APP_BASE_API + "/technologyBom/uploadBom",
   });
-  dialogVisible.value = true;
-};
 
-// 缂栬緫
-const handleEdit = (row) => {
-  operationType.value = 'edit';
-  Object.assign(form.value, {
-    id: row.id,
-    productName: row.productName || "",
-    productModelName: row.productModelName || "",
-    productModelId: row.productModelId || "",
-    remark: row.remark || "",
-    version: row.version || ""
+  const page = reactive({
+    current: 1,
+    size: 10,
+    total: 0,
   });
-  dialogVisible.value = true;
-};
 
-// 鍒犻櫎锛堝崟鏉★級
-const handleDelete = (row) => {
-  ElMessageBox.confirm('纭鍒犻櫎璇OM锛�', '鎻愮ず', {
-    confirmButtonText: '纭',
-    cancelButtonText: '鍙栨秷',
-    type: 'warning'
-  })
-    .then(() => {
-      batchDelete([row.id])
-        .then(() => {
-          proxy.$modal.msgSuccess('鍒犻櫎鎴愬姛');
-          getList();
-        })
-        .catch(() => {
-          proxy.$modal.msgError('鍒犻櫎澶辫触');
-        });
-    })
-    .catch(() => { });
-};
-
-// 鎵归噺鍒犻櫎
-const handleBatchDelete = () => {
-  if (!selectedRows.value.length) {
-    proxy.$modal.msgWarning('璇烽�夋嫨鏁版嵁');
-    return;
-  }
-  const ids = selectedRows.value.map(item => item.id);
-  ElMessageBox.confirm('閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�', '鍒犻櫎鎻愮ず', {
-    confirmButtonText: '纭',
-    cancelButtonText: '鍙栨秷',
-    type: 'warning'
-  })
-    .then(() => {
-      batchDelete(ids)
-        .then(() => {
-          proxy.$modal.msgSuccess('鍒犻櫎鎴愬姛');
-          getList();
-        })
-        .catch(() => {
-          proxy.$modal.msgError('鍒犻櫎澶辫触');
-        });
-    })
-    .catch(() => { });
-};
-
-// 浜у搧閫夋嫨
-const handleProductSelect = (products) => {
-  if (products && products.length > 0) {
-    const product = products[0];
-    form.value.productModelId = product.id;
-    form.value.productName = product.productName;
-    form.value.productModelName = product.model;
-  }
-  showProductSelectDialog.value = false;
-};
-
-// 鎻愪氦琛ㄥ崟
-const handleSubmit = () => {
-  formRef.value.validate((valid) => {
-    if (valid) {
-      const payload = { ...form.value };
-      if (operationType.value === 'add') {
-        add(payload)
-          .then(() => {
-            proxy.$modal.msgSuccess('鏂板鎴愬姛');
-            closeDialog();
-            getList();
-          })
-          .catch(() => {
-            proxy.$modal.msgError('鏂板澶辫触');
-          });
-      } else {
-        update(payload)
-          .then(() => {
-            proxy.$modal.msgSuccess('淇敼鎴愬姛');
-            closeDialog();
-            getList();
-          })
-          .catch(() => {
-            proxy.$modal.msgError('淇敼澶辫触');
-          });
-      }
-    }
+  const data = reactive({
+    form: {
+      id: undefined,
+      productName: "",
+      productModelName: "",
+      productModelId: "",
+      remark: "",
+      version: "",
+    },
+    rules: {
+      productModelId: [
+        { required: true, message: "璇烽�夋嫨浜у搧", trigger: "change" },
+      ],
+      version: [{ required: true, message: "璇疯緭鍏ョ増鏈彿", trigger: "blur" }],
+    },
   });
-};
 
-// 鍏抽棴寮圭獥
-const closeDialog = () => {
-  dialogVisible.value = false;
-  formRef.value?.resetFields();
-};
+  const { form, rules } = toRefs(data);
 
-//  瀵煎叆鎸夐挳鎿嶄綔
-const handleImport = () => {
-  upload.title = "BOM瀵煎叆";
-  upload.open = true;
-};
+  // 琛ㄦ牸閫夋嫨鏁版嵁
+  const handleSelectionChange = selection => {
+    selectedRows.value = selection;
+  };
 
-// 鍏抽棴瀵煎叆瀵硅瘽妗嗘椂娓呴櫎鏂囦欢
-const handleImportClose = () => {
-  proxy.$refs["uploadRef"].clearFiles();
-};
-
-//  鏂囦欢涓婁紶涓鐞�
-const handleFileUploadProgress = (event, file, fileList) => {
-  upload.isUploading = true;
-};
-
-//  鏂囦欢涓婁紶鎴愬姛澶勭悊
-const handleFileSuccess = (response, file, fileList) => {
-  upload.open = false;
-  upload.isUploading = false;
-  proxy.$refs["uploadRef"].clearFiles();
-  if (response.code === 200) {
-    proxy.$modal.msgSuccess(response.msg || "瀵煎叆鎴愬姛");
+  // 鍒嗛〉
+  const pagination = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
     getList();
-  } else {
-    proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "瀵煎叆缁撴灉", { dangerouslyUseHTMLString: true });
-  }
-};
+  };
 
-// 鎻愪氦涓婁紶鏂囦欢
-const submitFileForm = () => {
-  proxy.$refs["uploadRef"].submit();
-};
+  // 鏌ヨ鍒楄〃
+  const getList = () => {
+    tableLoading.value = true;
+    listPage({
+      current: page.current,
+      size: page.size,
+    })
+      .then(res => {
+        const records = res?.data?.records || [];
+        tableData.value = records;
+        page.total = res?.data?.total || 0;
+      })
+      .catch(err => {
+        console.error("鑾峰彇鍒楄〃澶辫触锛�", err);
+      })
+      .finally(() => {
+        tableLoading.value = false;
+      });
+  };
 
-//  瀵煎嚭鎸夐挳鎿嶄綔
-const handleExport = () => {
-  if (selectedRows.value.length !== 1) {
-    proxy.$modal.msgWarning("璇烽�夋嫨涓�鏉℃暟鎹繘琛屽鍑�");
-    return;
-  }
+  // 鏂板
+  const handleAdd = () => {
+    operationType.value = "add";
+    Object.assign(form.value, {
+      id: undefined,
+      productName: "",
+      productModelName: "",
+      productModelId: "",
+      remark: "",
+      version: "",
+    });
+    dialogVisible.value = true;
+  };
+  const handleCopy = row => {
+    // handleAdd(row);
+    ElMessageBox.confirm("纭澶嶅埗璇OM锛�", "鎻愮ず", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        copy({
+          id: row.id,
+        })
+          .then(() => {
+            proxy.$modal.msgSuccess("澶嶅埗鎴愬姛");
+            getList();
+          })
+          .catch(() => {
+            proxy.$modal.msgError("澶嶅埗澶辫触");
+          });
+      })
+      .catch(() => {});
+  };
 
-  const bomId = selectedRows.value[0].id;
-  const fileName = `BOM_${selectedRows.value[0].bomNo || bomId}.xlsx`;
+  // 缂栬緫
+  const handleEdit = row => {
+    operationType.value = "edit";
+    Object.assign(form.value, {
+      id: row.id,
+      productName: row.productName || "",
+      productModelName: row.productModelName || "",
+      productModelId: row.productModelId || "",
+      remark: row.remark || "",
+      version: row.version || "",
+    });
+    dialogVisible.value = true;
+  };
 
-  exportBom(bomId).then(res => {
-    // 杩斿洖鐨勬暟鎹槸鍚︿负绌�
-    if (!res) {
-      proxy.$modal.msgError("瀵煎嚭澶辫触锛岃繑鍥炴暟鎹负绌�");
+  // 鍒犻櫎锛堝崟鏉★級
+  const handleDelete = row => {
+    ElMessageBox.confirm("纭鍒犻櫎璇OM锛�", "鎻愮ず", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        batchDelete([row.id])
+          .then(() => {
+            proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+            getList();
+          })
+          .catch(() => {
+            proxy.$modal.msgError("鍒犻櫎澶辫触");
+          });
+      })
+      .catch(() => {});
+  };
+
+  // 鎵归噺鍒犻櫎
+  const handleBatchDelete = () => {
+    if (!selectedRows.value.length) {
+      proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+      return;
+    }
+    const ids = selectedRows.value.map(item => item.id);
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        batchDelete(ids)
+          .then(() => {
+            proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+            getList();
+          })
+          .catch(() => {
+            proxy.$modal.msgError("鍒犻櫎澶辫触");
+          });
+      })
+      .catch(() => {});
+  };
+
+  // 浜у搧閫夋嫨
+  const handleProductSelect = products => {
+    if (products && products.length > 0) {
+      const product = products[0];
+      form.value.productModelId = product.id;
+      form.value.productName = product.productName;
+      form.value.productModelName = product.model;
+    }
+    showProductSelectDialog.value = false;
+  };
+
+  // 鎻愪氦琛ㄥ崟
+  const handleSubmit = () => {
+    formRef.value.validate(valid => {
+      if (valid) {
+        const payload = { ...form.value };
+        if (operationType.value === "add") {
+          add(payload)
+            .then(() => {
+              proxy.$modal.msgSuccess("鏂板鎴愬姛");
+              closeDialog();
+              getList();
+            })
+            .catch(() => {
+              proxy.$modal.msgError("鏂板澶辫触");
+            });
+        } else {
+          update(payload)
+            .then(() => {
+              proxy.$modal.msgSuccess("淇敼鎴愬姛");
+              closeDialog();
+              getList();
+            })
+            .catch(() => {
+              proxy.$modal.msgError("淇敼澶辫触");
+            });
+        }
+      }
+    });
+  };
+
+  // 鍏抽棴寮圭獥
+  const closeDialog = () => {
+    dialogVisible.value = false;
+    formRef.value?.resetFields();
+  };
+
+  //  瀵煎叆鎸夐挳鎿嶄綔
+  const handleImport = () => {
+    upload.title = "BOM瀵煎叆";
+    upload.open = true;
+  };
+
+  // 鍏抽棴瀵煎叆瀵硅瘽妗嗘椂娓呴櫎鏂囦欢
+  const handleImportClose = () => {
+    proxy.$refs["uploadRef"].clearFiles();
+  };
+
+  //  鏂囦欢涓婁紶涓鐞�
+  const handleFileUploadProgress = (event, file, fileList) => {
+    upload.isUploading = true;
+  };
+
+  //  鏂囦欢涓婁紶鎴愬姛澶勭悊
+  const handleFileSuccess = (response, file, fileList) => {
+    upload.open = false;
+    upload.isUploading = false;
+    proxy.$refs["uploadRef"].clearFiles();
+    if (response.code === 200) {
+      proxy.$modal.msgSuccess(response.msg || "瀵煎叆鎴愬姛");
+      getList();
+    } else {
+      proxy.$alert(
+        "<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" +
+          response.msg +
+          "</div>",
+        "瀵煎叆缁撴灉",
+        { dangerouslyUseHTMLString: true }
+      );
+    }
+  };
+
+  // 鎻愪氦涓婁紶鏂囦欢
+  const submitFileForm = () => {
+    proxy.$refs["uploadRef"].submit();
+  };
+
+  //  瀵煎嚭鎸夐挳鎿嶄綔
+  const handleExport = () => {
+    if (selectedRows.value.length !== 1) {
+      proxy.$modal.msgWarning("璇烽�夋嫨涓�鏉℃暟鎹繘琛屽鍑�");
       return;
     }
 
-    const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
-    const downloadElement = document.createElement('a');
+    const bomId = selectedRows.value[0].id;
+    const fileName = `BOM_${selectedRows.value[0].bomNo || bomId}.xlsx`;
+
+    exportBom(bomId)
+      .then(res => {
+        // 杩斿洖鐨勬暟鎹槸鍚︿负绌�
+        if (!res) {
+          proxy.$modal.msgError("瀵煎嚭澶辫触锛岃繑鍥炴暟鎹负绌�");
+          return;
+        }
+
+        const blob = new Blob([res], {
+          type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+        });
+        const downloadElement = document.createElement("a");
+        const href = window.URL.createObjectURL(blob);
+
+        downloadElement.style.display = "none";
+        downloadElement.href = href;
+        downloadElement.download = fileName;
+
+        document.body.appendChild(downloadElement);
+        downloadElement.click();
+
+        document.body.removeChild(downloadElement);
+        window.URL.revokeObjectURL(href);
+
+        proxy.$modal.msgSuccess("瀵煎嚭鎴愬姛");
+      })
+      .catch(err => {
+        console.error("瀵煎嚭寮傚父锛�", err);
+        proxy.$modal.msgError("绯荤粺寮傚父锛屽鍑哄け璐�");
+      });
+  };
+
+  //  涓嬭浇妯℃澘
+  const handleDownloadTemplate = async () => {
+    const res = await downloadTemplate();
+    // 杩斿洖鐨勬暟鎹槸鍚︿负绌�
+    if (!res) {
+      proxy.$modal.msgError("涓嬭浇澶辫触锛岃繑鍥炴暟鎹负绌�");
+      return;
+    }
+
+    const blob = new Blob([res], {
+      type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+    });
+    const downloadElement = document.createElement("a");
     const href = window.URL.createObjectURL(blob);
 
-    downloadElement.style.display = 'none';
     downloadElement.href = href;
-    downloadElement.download = fileName;
+    downloadElement.download = "BOM妯℃澘.xlsx";
 
     document.body.appendChild(downloadElement);
     downloadElement.click();
@@ -382,52 +516,23 @@
     document.body.removeChild(downloadElement);
     window.URL.revokeObjectURL(href);
 
-    proxy.$modal.msgSuccess("瀵煎嚭鎴愬姛");
-  }).catch(err => {
-    console.error("瀵煎嚭寮傚父锛�", err);
-    proxy.$modal.msgError("绯荤粺寮傚父锛屽鍑哄け璐�");
+    proxy.$modal.msgSuccess("涓嬭浇鎴愬姛");
+  };
+
+  // 鏌ョ湅璇︽儏
+  const showDetail = row => {
+    router.push({
+      path: "/productionManagement/productStructureDetail",
+      query: {
+        id: row.id,
+        bomNo: row.bomNo || "",
+        productName: row.productName || "",
+        productModelName: row.productModelName || "",
+      },
+    });
+  };
+
+  onMounted(() => {
+    getList();
   });
-};
-
-//  涓嬭浇妯℃澘
-const handleDownloadTemplate = async () => {
-  const res = await downloadTemplate();
-  // 杩斿洖鐨勬暟鎹槸鍚︿负绌�
-  if (!res) {
-    proxy.$modal.msgError("涓嬭浇澶辫触锛岃繑鍥炴暟鎹负绌�");
-    return;
-  }
-
-  const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
-  const downloadElement = document.createElement('a');
-  const href = window.URL.createObjectURL(blob);
-
-  downloadElement.href = href;
-  downloadElement.download = "BOM妯℃澘.xlsx";
-
-  document.body.appendChild(downloadElement);
-  downloadElement.click();
-
-  document.body.removeChild(downloadElement);
-  window.URL.revokeObjectURL(href);
-
-  proxy.$modal.msgSuccess("涓嬭浇鎴愬姛");
-};
-
-// 鏌ョ湅璇︽儏
-const showDetail = (row) => {
-  router.push({
-    path: '/productionManagement/productStructureDetail',
-    query: {
-      id: row.id,
-      bomNo: row.bomNo || '',
-      productName: row.productName || '',
-      productModelName: row.productModelName || ''
-    }
-  });
-};
-
-onMounted(() => {
-  getList();
-});
 </script>
diff --git a/src/views/productionManagement/productionCosting/index.vue b/src/views/productionManagement/productionCosting/index.vue
index 8e1d40b..f438518 100644
--- a/src/views/productionManagement/productionCosting/index.vue
+++ b/src/views/productionManagement/productionCosting/index.vue
@@ -1,371 +1,389 @@
 <template>
-	<div class="app-container">
-		<el-row :gutter="16" class="content-row">
-			<!-- 宸︿晶鍙拌处 + 椤堕儴绛涢�� -->
-			<el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8" class="left-col">
-				<div class="left-panel">
-				<div class="left-header">
-          <el-form :model="searchForm" inline>
-            <el-form-item prop="dateType">
-              <el-radio-group v-model="searchForm.dateType" size="small" @change="handleDateTypeChange">
-                <el-radio-button label="day">鏃�</el-radio-button>
-                <el-radio-button label="month">鏈�</el-radio-button>
-              </el-radio-group>
-            </el-form-item>
-
-            <el-form-item label="鏃ユ湡锛�" prop="dateRange">
-              <el-date-picker
-                  v-model="searchForm.dateRange"
-                  :type="searchForm.dateType === 'day' ? 'date' : 'daterange'"
-                  range-separator="鑷�"
-                  start-placeholder="寮�濮嬫棩鏈�"
-                  end-placeholder="缁撴潫鏃ユ湡"
-                  format="YYYY-MM-DD"
-                  value-format="YYYY-MM-DD"
-                  style="width: 200px"
-                  @change="handleDateRangeChange"
-              />
-            </el-form-item>
-          </el-form>
-				</div>
-				<PIMTable
-					rowKey="id"
-					:column="leftTableColumn"
-					:tableData="leftTableData"
-					:tableLoading="tableLoading"
-          :page="page"
-          @row-click="handleLeftRowClick"
-          @pagination="pagination"
-        ></PIMTable>
-				</div>
-			</el-col>
-
-			<!-- 鍙充晶鏄庣粏 -->
-			<el-col :xs="24" :sm="24" :md="24" :lg="16" :xl="16" class="right-col">
-				<div class="right-panel">
-				
-					<el-form inline>
-						<el-form-item>
-							<el-button type="primary" @click="handleOut">瀵煎嚭</el-button>
-						</el-form-item>
-					</el-form>
-					<PIMTable
-						rowKey="id"
-						:column="tableColumn"
-						:tableData="tableData"
-						:page="page1"
-						:tableLoading="tableLoading1"
-						style="margin-right: 20px;"
-						@pagination="pagination1"
-					></PIMTable>
-				</div>
-			</el-col>
-		</el-row>
-	</div>
+  <div class="app-container">
+    <div class="table_list">
+      <el-row :gutter="16"
+              class="content-row">
+        <!-- 宸︿晶鍙拌处 + 椤堕儴绛涢�� -->
+        <el-col :xs="24"
+                :sm="24"
+                :md="24"
+                :lg="8"
+                :xl="8"
+                class="left-col">
+          <div class="left-panel">
+            <div class="left-header">
+              <el-form :model="searchForm"
+                       inline>
+                <el-form-item prop="dateType">
+                  <el-radio-group v-model="searchForm.dateType"
+                                  size="small"
+                                  @change="handleDateTypeChange">
+                    <el-radio-button label="day">鏃�</el-radio-button>
+                    <el-radio-button label="month">鏈�</el-radio-button>
+                  </el-radio-group>
+                </el-form-item>
+                <el-form-item label="鏃ユ湡锛�"
+                              prop="dateRange">
+                  <el-date-picker v-model="searchForm.dateRange"
+                                  :type="searchForm.dateType === 'day' ? 'date' : 'daterange'"
+                                  range-separator="鑷�"
+                                  start-placeholder="寮�濮嬫棩鏈�"
+                                  end-placeholder="缁撴潫鏃ユ湡"
+                                  format="YYYY-MM-DD"
+                                  value-format="YYYY-MM-DD"
+                                  style="width: 200px"
+                                  @change="handleDateRangeChange" />
+                </el-form-item>
+              </el-form>
+            </div>
+            <PIMTable rowKey="id"
+                      :column="leftTableColumn"
+                      :tableData="leftTableData"
+                      :tableLoading="tableLoading"
+                      :page="page"
+                      @row-click="handleLeftRowClick"
+                      @pagination="pagination"></PIMTable>
+          </div>
+        </el-col>
+        <!-- 鍙充晶鏄庣粏 -->
+        <el-col :xs="24"
+                :sm="24"
+                :md="24"
+                :lg="16"
+                :xl="16"
+                class="right-col">
+          <div class="right-panel">
+            <el-form inline>
+              <el-form-item>
+                <el-button type="primary"
+                           @click="handleOut">瀵煎嚭</el-button>
+              </el-form-item>
+            </el-form>
+            <PIMTable rowKey="id"
+                      :column="tableColumn"
+                      :tableData="tableData"
+                      :page="page1"
+                      :tableLoading="tableLoading1"
+                      style="margin-right: 20px;"
+                      @pagination="pagination1"></PIMTable>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+  </div>
 </template>
 
 <script setup>
-import {onMounted, ref} from "vue";
-import { ElMessageBox } from "element-plus";
-import dayjs from "dayjs";
-import {salesLedgerProductionAccountingListProductionDetails, salesLedgerProductionAccountingList} from "@/api/productionManagement/productionCosting.js";
-const { proxy } = getCurrentInstance();
+  import { onMounted, ref } from "vue";
+  import { ElMessageBox } from "element-plus";
+  import dayjs from "dayjs";
+  import {
+    salesLedgerProductionAccountingListProductionDetails,
+    salesLedgerProductionAccountingList,
+  } from "@/api/productionManagement/productionCosting.js";
+  const { proxy } = getCurrentInstance();
 
-const tableColumn = ref([
-	{
-		label: "鐢熶骇鏃ユ湡",
-		prop: "schedulingDate",
-    minWidth: 100,
-	},
-	{
-		label: "鐢熶骇浜�",
-		prop: "schedulingUserName",
-    minWidth: 100,
-	},
-	{
-		label: "鍚堝悓鍙�",
-		prop: "salesContractNo",
-    minWidth: 100,
-	},
-	{
-		label: "瀹㈡埛鍚嶇О",
-		prop: "customerName",
-    minWidth: 100,
-	},
-	{
-		label: "浜у搧澶х被",
-		prop: "productName",
-    minWidth: 100,
-	},
-	{
-		label: "瑙勬牸鍨嬪彿",
-		prop: "productModelName",
-    minWidth: 100,
-	},
-	{
-		label: "鍗曚綅",
-		prop: "unit",
-    minWidth: 100,
-	},
-	{
-		label: "宸ュ簭",
-		prop: "process",
-    minWidth: 100,
-	},
-	{
-		label: "鐢熶骇鏁伴噺",
-		prop: "quantity",
-    minWidth: 100,
-	},
-	{
-		label: "宸ユ椂瀹氶",
-		prop: "workHours",
-    minWidth: 100,
-	},
-	{
-		label: "宸ヨ祫",
-		prop: "wages",
-    minWidth: 100,
-	},
-]);
-
-// 宸︿晶姹囨�诲彴璐﹀垪锛堢敓浜т汉銆佷骇閲忋�佸伐璧勩�佸悎鏍肩巼锛�
-const leftTableColumn = ref([
-	{
-		label: "鐢熶骇浜�",
-		prop: "schedulingUserName",
-    minWidth: 100,
-	},
-	{
-		label: "浜ч噺",
-		prop: "outputNum",
-    minWidth: 100,
-
-  },
-	{
-		label: "宸ヨ祫",
-		prop: "wages",
-    minWidth: 100,
-
-	},
-	{
-		label: "鍚堟牸鐜�",
-		prop: "outputRate",
-    minWidth: 100,
-    formatData: (val) => {
-      if (val == null || val === '') return '-'
-      return parseFloat(val).toFixed(2)
+  const tableColumn = ref([
+    {
+      label: "鐢熶骇鏃ユ湡",
+      prop: "schedulingDate",
+      minWidth: 100,
     },
-	},
-]);
+    {
+      label: "鐢熶骇浜�",
+      prop: "schedulingUserName",
+      minWidth: 100,
+    },
+    // {
+    //   label: "鍚堝悓鍙�",
+    //   prop: "salesContractNo",
+    //   minWidth: 100,
+    // },
+    // {
+    //   label: "瀹㈡埛鍚嶇О",
+    //   prop: "customerName",
+    //   minWidth: 100,
+    // },
+    {
+      label: "浜у搧澶х被",
+      prop: "productName",
+      minWidth: 100,
+    },
+    {
+      label: "瑙勬牸鍨嬪彿",
+      prop: "productModelName",
+      minWidth: 100,
+    },
+    {
+      label: "鍗曚綅",
+      prop: "unit",
+      minWidth: 100,
+    },
+    {
+      label: "宸ュ簭",
+      prop: "process",
+      minWidth: 100,
+    },
+    {
+      label: "鐢熶骇鏁伴噺",
+      prop: "quantity",
+      minWidth: 100,
+    },
+    {
+      label: "宸ユ椂瀹氶",
+      prop: "workHours",
+      minWidth: 100,
+    },
+    {
+      label: "宸ヨ祫",
+      prop: "wages",
+      minWidth: 100,
+    },
+  ]);
 
-const tableData = ref([]);
-const tableLoading = ref(false);
-const tableLoading1 = ref(false);
-const leftTableData = ref([]);
-// 鏃� / 鏈� 鍒囨崲锛堥粯璁ゆ寜鏃ワ級
-const page = reactive({
-	current: 1,
-	size: 100,
-	total: 0,
-});
+  // 宸︿晶姹囨�诲彴璐﹀垪锛堢敓浜т汉銆佷骇閲忋�佸伐璧勩�佸悎鏍肩巼锛�
+  const leftTableColumn = ref([
+    {
+      label: "鐢熶骇浜�",
+      prop: "schedulingUserName",
+      minWidth: 100,
+    },
+    {
+      label: "浜ч噺",
+      prop: "finishedNum",
+      minWidth: 100,
+    },
+    {
+      label: "宸ヨ祫",
+      prop: "wages",
+      minWidth: 100,
+    },
+    {
+      label: "鍚堟牸鐜�",
+      prop: "outputRate",
+      minWidth: 100,
+      formatData: val => {
+        if (val == null || val === "") return "-";
+        return parseFloat(val).toFixed(2) + "%";
+      },
+    },
+  ]);
 
-const page1 = reactive({
-  current: 1,
-  size: 100,
-  total: 0,
-});
+  const tableData = ref([]);
+  const tableLoading = ref(false);
+  const tableLoading1 = ref(false);
+  const leftTableData = ref([]);
+  // 鏃� / 鏈� 鍒囨崲锛堥粯璁ゆ寜鏃ワ級
+  const page = reactive({
+    current: 1,
+    size: 100,
+    total: 0,
+  });
 
-const data = reactive({
-	searchForm: {
-		schedulingUserName: "",
-		salesContractNo: "",
-    dateType: "day",
-    dateRange: dayjs().format("YYYY-MM-DD"),
-		entryDate: dayjs().format("YYYY-MM-DD"),
-		entryDateStart: undefined,
-		entryDateEnd: undefined,
-	},
-});
-const { searchForm } = toRefs(data);
+  const page1 = reactive({
+    current: 1,
+    size: 100,
+    total: 0,
+  });
 
-const pagination = (obj) => {
-	page.current = obj.page;
-	page.size = obj.limit;
-	getList();
-};
+  const data = reactive({
+    searchForm: {
+      schedulingUserName: "",
+      salesContractNo: "",
+      dateType: "day",
+      dateRange: dayjs().format("YYYY-MM-DD"),
+      entryDate: dayjs().format("YYYY-MM-DD"),
+      entryDateStart: undefined,
+      entryDateEnd: undefined,
+    },
+  });
+  const { searchForm } = toRefs(data);
 
-const pagination1 = (obj) => {
-  page1.current = obj.page;
-  page1.size = obj.limit;
-	getList1();
-};
+  const pagination = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    getList();
+  };
 
-const handleDateRangeChange = (value) => {
-	if (value) {
-    if (searchForm.value.dateType === "day") {
-      searchForm.value.entryDate = value;
+  const pagination1 = obj => {
+    page1.current = obj.page;
+    page1.size = obj.limit;
+    getList1();
+  };
+
+  const handleDateRangeChange = value => {
+    if (value) {
+      if (searchForm.value.dateType === "day") {
+        searchForm.value.entryDate = value;
+      } else {
+        searchForm.value.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
+        searchForm.value.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
+      }
     } else {
-      searchForm.value.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
-      searchForm.value.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
+      searchForm.value.entryDate = undefined;
+      searchForm.value.entryDateStart = undefined;
+      searchForm.value.entryDateEnd = undefined;
+    }
+    reloadData();
+  };
+
+  const getList = () => {
+    tableLoading.value = true;
+    const params = { ...searchForm.value, ...page };
+
+    salesLedgerProductionAccountingList(params)
+      .then(res => {
+        const records = res.data.records || [];
+        leftTableData.value = records;
+        page.total = res.data.total || 0;
+      })
+      .finally(() => {
+        tableLoading.value = false;
+      });
+  };
+
+  const getList1 = () => {
+    tableLoading1.value = true;
+    const params = { ...page1, ...searchForm.value };
+    salesLedgerProductionAccountingListProductionDetails(params)
+      .then(res => {
+        tableData.value = res.data.records || [];
+        page1.total = res.data.total || 0;
+      })
+      .finally(() => {
+        tableLoading1.value = false;
+      });
+  };
+
+  // 鏋勫缓宸︿晶姹囨�诲彴璐︼紙鎸夌敓浜т汉姹囨�讳骇閲忋�佸伐璧勭瓑锛�
+  const buildLeftTableData = records => {
+    const map = {};
+    records.forEach(item => {
+      const key = item.schedulingUserName || "鏈煡";
+      if (!map[key]) {
+        map[key] = {
+          id: key,
+          schedulingUserName: key,
+          finishedNum: 0,
+          wages: 0,
+          qualifiedRate: item.qualifiedRate ?? null,
+        };
+      }
+      map[key].finishedNum += Number(item.finishedNum || 0);
+      map[key].wages += Number(item.wages || 0);
+      if (item.qualifiedRate != null) {
+        map[key].qualifiedRate = item.qualifiedRate;
+      }
+    });
+    leftTableData.value = Object.values(map);
+  };
+
+  // 宸︿晶鏃�/鏈堝垏鎹�
+  const handleDateTypeChange = value => {
+    // 杩欓噷鍙綔涓虹瓫閫夋潯浠剁殑涓�閮ㄥ垎锛岀洿鎺ラ噸鏂版煡璇㈠垪琛�
+    if (value === "day") {
+      searchForm.value.entryDate = dayjs().format("YYYY-MM-DD");
+      searchForm.value.dateRange = searchForm.value.entryDate;
+    } else {
+      searchForm.value.entryDateStart = dayjs()
+        .startOf("month")
+        .format("YYYY-MM-DD");
+      searchForm.value.entryDateEnd = dayjs().endOf("month").format("YYYY-MM-DD");
+      searchForm.value.dateRange = [
+        searchForm.value.entryDateStart,
+        searchForm.value.entryDateEnd,
+      ];
     }
 
-	} else {
-		searchForm.value.entryDate = undefined;
-		searchForm.value.entryDateStart = undefined;
-		searchForm.value.entryDateEnd = undefined;
-	}
-  reloadData()
-};
+    reloadData();
+  };
 
-const getList = () => {
-	tableLoading.value = true;
-	const params = { ...searchForm.value, ...page };
+  const reloadData = () => {
+    page.current = 1;
+    page1.current = 1;
+    getList();
+    tableData.value = [];
+  };
 
-  salesLedgerProductionAccountingList(params).then((res) => {
-		const records = res.data.records || [];
-    leftTableData.value = records;
-		page.total = res.data.total || 0;
-	}).finally(() => {
-    tableLoading.value = false;
-  })
+  // 鐐瑰嚮宸︿晶琛岋紝鍒峰彸渚ф槑缁嗭紙鎸夌敓浜т汉杩囨护锛�
+  const handleLeftRowClick = row => {
+    searchForm.value.schedulingUserName = row.schedulingUserName || "";
+    handleQuery();
+  };
 
+  // 鏌ヨ鍒楄〃
+  /** 鎼滅储鎸夐挳鎿嶄綔 */
+  const handleQuery = () => {
+    page1.current = 1;
+    getList1();
+  };
 
+  // 瀵煎嚭
+  const handleOut = () => {
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        proxy.download(
+          "/salesLedger/productionAccounting/export",
+          {},
+          "鐢熶骇鏍哥畻.xlsx"
+        );
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
+  };
 
-};
-
-const getList1 = () => {
-  tableLoading1.value = true;
-  const params = { ...page1, ...searchForm.value };
-  salesLedgerProductionAccountingListProductionDetails(params).then((res) => {
-    tableData.value = res.data.records || [];;
-    page1.total = res.data.total || 0;
-  }).finally(() => {
-    tableLoading1.value = false;
-  })
-};
-
-// 鏋勫缓宸︿晶姹囨�诲彴璐︼紙鎸夌敓浜т汉姹囨�讳骇閲忋�佸伐璧勭瓑锛�
-const buildLeftTableData = (records) => {
-	const map = {};
-	records.forEach((item) => {
-		const key = item.schedulingUserName || "鏈煡";
-		if (!map[key]) {
-			map[key] = {
-				id: key,
-				schedulingUserName: key,
-				finishedNum: 0,
-				wages: 0,
-				qualifiedRate: item.qualifiedRate ?? null,
-			};
-		}
-		map[key].finishedNum += Number(item.finishedNum || 0);
-		map[key].wages += Number(item.wages || 0);
-		if (item.qualifiedRate != null) {
-			map[key].qualifiedRate = item.qualifiedRate;
-		}
-	});
-	leftTableData.value = Object.values(map);
-};
-
-// 宸︿晶鏃�/鏈堝垏鎹�
-const handleDateTypeChange = (value) => {
-	// 杩欓噷鍙綔涓虹瓫閫夋潯浠剁殑涓�閮ㄥ垎锛岀洿鎺ラ噸鏂版煡璇㈠垪琛�
-  if (value === "day") {
-    searchForm.value.entryDate = dayjs().format("YYYY-MM-DD");
-    searchForm.value.dateRange = searchForm.value.entryDate
-  } else {
-    searchForm.value.entryDateStart = dayjs().startOf("month").format("YYYY-MM-DD");
-    searchForm.value.entryDateEnd = dayjs().endOf("month").format("YYYY-MM-DD");
-    searchForm.value.dateRange = [searchForm.value.entryDateStart, searchForm.value.entryDateEnd]
-  }
-
-  reloadData()
-};
-
-const reloadData = () => {
-  page.current = 1;
-  page1.current = 1;
-  getList();
-  tableData.value = []
-}
-
-// 鐐瑰嚮宸︿晶琛岋紝鍒峰彸渚ф槑缁嗭紙鎸夌敓浜т汉杩囨护锛�
-const handleLeftRowClick = (row) => {
-	searchForm.value.schedulingUserName = row.schedulingUserName || "";
-	handleQuery();
-};
-
-// 鏌ヨ鍒楄〃
-/** 鎼滅储鎸夐挳鎿嶄綔 */
-const handleQuery = () => {
-  page1.current = 1;
-  getList1();
-};
-
-
-// 瀵煎嚭
-const handleOut = () => {
-	ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
-		confirmButtonText: "纭",
-		cancelButtonText: "鍙栨秷",
-		type: "warning",
-	})
-		.then(() => {
-			proxy.download("/salesLedger/productionAccounting/export", {}, "鐢熶骇鏍哥畻.xlsx");
-		})
-		.catch(() => {
-			proxy.$modal.msg("宸插彇娑�");
-		});
-};
-
-onMounted(() => {
-	getList();
-});
+  onMounted(() => {
+    getList();
+  });
 </script>
 
 <style scoped lang="scss">
-.content-row {
-  width: 100%;
-}
+  .content-row {
+    width: 100%;
+  }
 
-.content-row .left-col,
-.content-row .right-col {
-  margin-bottom: 16px;
-}
+  .content-row .left-col,
+  .content-row .right-col {
+    margin-bottom: 16px;
+  }
 
-.left-panel,
-.right-panel {
-  display: flex;
-  flex-direction: column;
-  gap: 10px;
-  min-width: 0;
-}
+  .left-panel,
+  .right-panel {
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+    min-width: 0;
+  }
 
-.left-header {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-}
+  .left-header {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+  }
 
-.left-title {
-  font-size: 16px;
-  color: #ffffff;
-}
+  .left-title {
+    font-size: 16px;
+    color: #ffffff;
+  }
 
-.header-filters {
-  display: flex;
-  align-items: center;
-  flex: 1;
-  justify-content: flex-end;
-  gap: 8px;
-}
+  .header-filters {
+    display: flex;
+    align-items: center;
+    flex: 1;
+    justify-content: flex-end;
+    gap: 8px;
+  }
 
-.search_title {
-  color: #ffffff;
-}
+  .search_title {
+    color: #ffffff;
+  }
 
-.ml10 {
-  margin-left: 10px;
-}
+  .ml10 {
+    margin-left: 10px;
+  }
 </style>
diff --git a/src/views/productionManagement/productionOrder/New.vue b/src/views/productionManagement/productionOrder/New.vue
index c9c478b..ea74186 100644
--- a/src/views/productionManagement/productionOrder/New.vue
+++ b/src/views/productionManagement/productionOrder/New.vue
@@ -1,44 +1,39 @@
 <template>
   <div>
-    <el-dialog
-        v-model="isShow"
-        title="鏂板鐢熶骇璁㈠崟"
-        width="800"
-        @close="closeModal"
-    >
-      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
-        <el-form-item
-            label="浜у搧鍚嶇О"
-            prop="productModelId"
-            :rules="[
+    <el-dialog v-model="isShow"
+               title="鏂板鐢熶骇璁㈠崟"
+               width="800"
+               @close="closeModal">
+      <el-form label-width="140px"
+               :model="formState"
+               label-position="top"
+               ref="formRef">
+        <el-form-item label="浜у搧鍚嶇О"
+                      prop="productModelId"
+                      :rules="[
                 {
                 required: true,
                 message: '璇烽�夋嫨浜у搧',
                 trigger: 'change',
               }
-            ]"
-        >
-          <el-button type="primary" @click="showProductSelectDialog = true">
+            ]">
+          <el-button type="primary"
+                     @click="showProductSelectDialog = true">
             {{ formState.productName ? formState.productName : '閫夋嫨浜у搧' }}
           </el-button>
         </el-form-item>
-
-        <el-form-item
-            label="瑙勬牸"
-            prop="productModelName"
-        >
-          <el-input v-model="formState.productModelName"  disabled />
+        <el-form-item label="瑙勬牸"
+                      prop="productModelName">
+          <el-input v-model="formState.productModelName"
+                    disabled />
         </el-form-item>
-
-        <el-form-item
-            label="鍗曚綅"
-            prop="unit"
-        >
-          <el-input v-model="formState.unit"  disabled />
+        <el-form-item label="鍗曚綅"
+                      prop="unit">
+          <el-input v-model="formState.unit"
+                    disabled />
         </el-form-item>
-
         <el-form-item label="宸ヨ壓璺嚎">
-          <el-select v-model="formState.routeId"
+          <el-select v-model="formState.technologyRoutingId"
                      placeholder="璇烽�夋嫨宸ヨ壓璺嚎"
                      style="width: 100%;"
                      :loading="bindRouteLoading">
@@ -48,24 +43,30 @@
                        :value="item.id" />
           </el-select>
         </el-form-item>
-
-        <el-form-item
-            label="闇�姹傛暟閲�"
-            prop="quantity"
-        >
-          <el-input-number v-model="formState.quantity" :step="1" :min="1" style="width: 100%" />
+        <el-form-item label="闇�姹傛暟閲�"
+                      prop="quantity">
+          <el-input-number v-model="formState.quantity"
+                           :step="1"
+                           :min="0"
+                           style="width: 100%" />
+        </el-form-item>
+        <el-form-item label="璁″垝瀹屾垚鏃堕棿"
+                      prop="planCompleteTime">
+          <el-date-picker v-model="formState.planCompleteTime"
+                          type="date"
+                          placeholder="閫夋嫨鏃ユ湡"
+                          value-format="YYYY-MM-DD"
+                          style="width: 100%" />
         </el-form-item>
       </el-form>
-
       <!-- 浜у搧閫夋嫨寮圭獥 -->
-      <ProductSelectDialog
-          v-model="showProductSelectDialog"
-          @confirm="handleProductSelect"
-          single
-      />
+      <ProductSelectDialog v-model="showProductSelectDialog"
+                           @confirm="handleProductSelect"
+                           single />
       <template #footer>
         <div class="dialog-footer">
-          <el-button type="primary" @click="handleSubmit">纭</el-button>
+          <el-button type="primary"
+                     @click="handleSubmit">纭</el-button>
           <el-button @click="closeModal">鍙栨秷</el-button>
         </div>
       </template>
@@ -74,119 +75,130 @@
 </template>
 
 <script setup>
-import {ref, computed, getCurrentInstance} from "vue";
-import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
-import {addProductOrder, listProcessRoute} from "@/api/productionManagement/productionOrder.js";
+  import { ref, computed, getCurrentInstance } from "vue";
+  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+  import {
+    addProductOrder,
+    listProcessRoute,
+  } from "@/api/productionManagement/productionOrder.js";
+  import { listPage } from "@/api/productionManagement/processRoute.js";
+  const props = defineProps({
+    visible: {
+      type: Boolean,
+      required: true,
+    },
 
-const props = defineProps({
-  visible: {
-    type: Boolean,
-    required: true,
-  },
+    type: {
+      type: String,
+      required: true,
+      default: "qualified",
+    },
+  });
 
-  type: {
-    type: String,
-    required: true,
-    default: 'qualified',
-  },
-});
+  const emit = defineEmits(["update:visible", "completed"]);
 
-const emit = defineEmits(['update:visible', 'completed']);
-
-// 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
-const formState = ref({
-  productId: undefined,
-  productModelId: undefined,
-  routeId: undefined,
-  productName: "",
-  productModelName: "",
-  unit: "",
-  quantity: 0,
-});
-
-const isShow = computed({
-  get() {
-    return props.visible;
-  },
-  set(val) {
-    emit('update:visible', val);
-  },
-});
-
-const showProductSelectDialog = ref(false);
-
-let { proxy } = getCurrentInstance()
-
-const closeModal = () => {
-  // 閲嶇疆琛ㄥ崟鏁版嵁
-  formState.value = {
+  // 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
+  const formState = ref({
     productId: undefined,
     productModelId: undefined,
-    routeId: undefined,
+    technologyRoutingId: undefined,
     productName: "",
     productModelName: "",
-    quantity: '',
+    unit: "",
+    quantity: 0,
+    planCompleteTime: "",
+  });
+
+  const isShow = computed({
+    get() {
+      return props.visible;
+    },
+    set(val) {
+      emit("update:visible", val);
+    },
+  });
+
+  const showProductSelectDialog = ref(false);
+
+  let { proxy } = getCurrentInstance();
+
+  const closeModal = () => {
+    // 閲嶇疆琛ㄥ崟鏁版嵁
+    formState.value = {
+      productId: undefined,
+      productModelId: undefined,
+      technologyRoutingId: undefined,
+      productName: "",
+      productModelName: "",
+      unit: "",
+      quantity: "",
+      planCompleteTime: "",
+    };
+    isShow.value = false;
   };
-  isShow.value = false;
-};
 
-// 浜у搧閫夋嫨澶勭悊
-const handleProductSelect = async (products) => {
-  if (products && products.length > 0) {
-    const product = products[0];
-    formState.value.productId = product.productId;
-    formState.value.productName = product.productName;
-    formState.value.productModelName = product.model;
-    formState.value.productModelId = product.id;
-    formState.value.unit = product.unit;
-    showProductSelectDialog.value = false;
-    fetchRouteOptions( product.id);
-    // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
-    proxy.$refs["formRef"]?.validateField('productModelId');
-  }
-};
-
-const routeOptions = ref([]);
-const bindRouteLoading = ref(false);
-const fetchRouteOptions = (productModelId) => {
-  formState.value.routeId = undefined;
-  routeOptions.value = []
-  bindRouteLoading.value = true;
-  listProcessRoute({ productModelId: productModelId }).then(res => {
-    routeOptions.value = res.data || [];
-  }).finally(() => {
-    bindRouteLoading.value = false;
-  })
-}
-
-const handleSubmit = () => {
-  proxy.$refs["formRef"].validate(valid => {
-    if (valid) {
-      // 楠岃瘉鏄惁閫夋嫨浜嗕骇鍝佸拰瑙勬牸
-      if (!formState.value.productModelId) {
-        proxy.$modal.msgError("璇烽�夋嫨浜у搧");
-        return;
-      }
-      if (!formState.value.productModelId) {
-        proxy.$modal.msgError("璇烽�夋嫨瑙勬牸");
-        return;
-      }
-
-      addProductOrder(formState.value).then(res => {
-        // 鍏抽棴妯℃�佹
-        isShow.value = false;
-        // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
-        emit('completed');
-        proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-      })
+  // 浜у搧閫夋嫨澶勭悊
+  const handleProductSelect = async products => {
+    if (products && products.length > 0) {
+      const product = products[0];
+      formState.value.productId = product.productId;
+      formState.value.productName = product.productName;
+      formState.value.productModelName = product.model;
+      formState.value.productModelId = product.id;
+      formState.value.unit = product.unit;
+      showProductSelectDialog.value = false;
+      fetchRouteOptions(product.id);
+      // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
+      proxy.$refs["formRef"]?.validateField("productModelId");
     }
-  })
-};
+  };
 
+  const routeOptions = ref([]);
+  const bindRouteLoading = ref(false);
+  const fetchRouteOptions = productModelId => {
+    formState.value.technologyRoutingId = undefined;
+    routeOptions.value = [];
+    bindRouteLoading.value = true;
+    listPage({ productModelId: productModelId })
+      .then(res => {
+        routeOptions.value = res.data.records || [];
+      })
+      .finally(() => {
+        bindRouteLoading.value = false;
+      });
+  };
 
-defineExpose({
-  closeModal,
-  handleSubmit,
-  isShow,
-});
+  const handleSubmit = () => {
+    proxy.$refs["formRef"].validate(valid => {
+      if (valid) {
+        // 楠岃瘉鏄惁閫夋嫨浜嗕骇鍝佸拰瑙勬牸
+        if (!formState.value.productModelId) {
+          proxy.$modal.msgError("璇烽�夋嫨浜у搧");
+          return;
+        }
+        if (!formState.value.productModelId) {
+          proxy.$modal.msgError("璇烽�夋嫨瑙勬牸");
+          return;
+        }
+        if (formState.value.quantity <= 0) {
+          proxy.$modal.msgError("闇�姹傛暟閲忓繀椤诲ぇ浜�0");
+          return;
+        }
+
+        addProductOrder(formState.value).then(res => {
+          // 鍏抽棴妯℃�佹
+          isShow.value = false;
+          // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
+          emit("completed");
+          proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+        });
+      }
+    });
+  };
+
+  defineExpose({
+    closeModal,
+    handleSubmit,
+    isShow,
+  });
 </script>
diff --git a/src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue b/src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue
new file mode 100644
index 0000000..370815e
--- /dev/null
+++ b/src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue
@@ -0,0 +1,284 @@
+<template>
+  <div>
+    <el-dialog v-model="dialogVisible"
+               title="棰嗘枡璇︽儏"
+               width="1400px"
+               @close="handleClose">
+      <el-table v-loading="materialDetailLoading"
+                :data="materialDetailTableData"
+                border
+                row-key="id">
+        <el-table-column label="宸ュ簭鍚嶇О"
+                         prop="operationName"
+                         min-width="180" />
+        <el-table-column label="鍘熸枡鍚嶇О"
+                         prop="productName"
+                         min-width="160" />
+        <el-table-column label="鍘熸枡鍨嬪彿"
+                         prop="model"
+                         min-width="180" />
+        <el-table-column label="鎵瑰彿"
+                         prop="batchNo"
+                         min-width="150" />
+        <el-table-column label="闇�姹傛暟閲�"
+                         prop="demandedQuantity"
+                         min-width="110" />
+        <el-table-column label="璁¢噺鍗曚綅"
+                         prop="unit"
+                         width="100" />
+        <el-table-column label="棰嗙敤鏁伴噺"
+                         prop="pickQuantity"
+                         min-width="110" />
+        <el-table-column label="琛ユ枡鏁伴噺"
+                         min-width="120">
+          <template #default="{ row }">
+            <el-button type="primary"
+                       link
+                       @click="handleViewSupplementRecord(row)">
+              {{ row.feedingQty ?? 0 }}
+            </el-button>
+          </template>
+        </el-table-column>
+        <el-table-column label="閫�鏂欐暟閲�"
+                         min-width="110">
+          <template #default="{ row }">
+            {{ row.returnQty ?? 0 }}
+          </template>
+        </el-table-column>
+        <el-table-column label="瀹為檯鏁伴噺"
+                         min-width="140">
+          <template #default="{ row }">
+            <el-input-number v-model="row.actualQty"
+                             :min="0"
+                             :precision="3"
+                             :step="1"
+                             controls-position="right"
+                             placeholder="杈撳叆瀹為檯鏁伴噺"
+                             style="width: 100%;"
+                             :disabled="row.returned || orderRow?.end"
+                             @change="val => handleActualQtyChange(row, val)" />
+          </template>
+        </el-table-column>
+      </el-table>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button v-if="!orderRow?.end"
+                     type="warning"
+                     :loading="materialReturnConfirming"
+                     :disabled="!canOpenReturnSummary"
+                     @click="openReturnSummaryDialog">
+            閫�鏂欑‘璁�
+          </el-button>
+          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <el-dialog v-model="supplementRecordDialogVisible"
+               title="琛ユ枡璁板綍"
+               width="800px">
+      <el-table v-loading="supplementRecordLoading"
+                :data="supplementRecordTableData"
+                border
+                row-key="id">
+        <el-table-column label="琛ユ枡鏁伴噺"
+                         prop="pickQuantity"
+                         min-width="120" />
+        <el-table-column label="琛ユ枡浜�"
+                         prop="supplementUserName"
+                         min-width="120" />
+        <el-table-column label="琛ユ枡鏃ユ湡"
+                         prop="supplementTime"
+                         min-width="160" />
+        <el-table-column label="琛ユ枡鍘熷洜"
+                         prop="feedingReason"
+                         min-width="200" />
+      </el-table>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="supplementRecordDialogVisible = false">鍏抽棴</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <el-dialog v-model="returnSummaryDialogVisible"
+               title="閫�鏂欐眹鎬荤‘璁�"
+               width="900px">
+      <el-table :data="returnSummaryList"
+                border
+                row-key="summaryKey">
+        <el-table-column label="鍘熸枡鍚嶇О"
+                         prop="materialName"
+                         min-width="180" />
+        <el-table-column label="鍘熸枡鍨嬪彿"
+                         prop="materialModel"
+                         min-width="180" />
+        <el-table-column label="璁¢噺鍗曚綅"
+                         prop="unit"
+                         min-width="100" />
+        <el-table-column label="閫�鏂欐眹鎬绘暟閲�"
+                         prop="returnQtyTotal"
+                         min-width="140" />
+      </el-table>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary"
+                     :loading="materialReturnConfirming"
+                     @click="handleReturnConfirm">纭鎻愪氦</el-button>
+          <el-button @click="returnSummaryDialogVisible = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+  import { computed, ref, watch } from "vue";
+  import { ElMessage } from "element-plus";
+  import {
+    listMaterialPickingDetail,
+    listMaterialSupplementRecord,
+    updateMaterialPickingLedger,
+  } from "@/api/productionManagement/productionOrder.js";
+
+  const props = defineProps({
+    modelValue: { type: Boolean, default: false },
+    orderRow: { type: Object, default: null },
+  });
+  const emit = defineEmits(["update:modelValue", "confirmed"]);
+
+  const dialogVisible = computed({
+    get: () => props.modelValue,
+    set: val => emit("update:modelValue", val),
+  });
+
+  const materialDetailLoading = ref(false);
+  const materialDetailTableData = ref([]);
+  const materialReturnConfirming = ref(false);
+  const supplementRecordDialogVisible = ref(false);
+  const supplementRecordLoading = ref(false);
+  const supplementRecordTableData = ref([]);
+  const returnSummaryDialogVisible = ref(false);
+  const returnSummaryList = ref([]);
+  const calcReturnQty = item =>
+    Number(item.pickQuantity || 0) +
+    Number(item.feedingQty || 0) -
+    Number(item.actualQty || 0);
+  const canOpenReturnSummary = computed(() =>
+    materialDetailTableData.value.some(
+      item => item.returned !== true && calcReturnQty(item) > 0
+    )
+  );
+
+  const loadDetailList = async () => {
+    if (!props.orderRow?.id) return;
+    materialDetailLoading.value = true;
+    materialDetailTableData.value = [];
+    try {
+      const res = await listMaterialPickingDetail(props.orderRow.id);
+      materialDetailTableData.value = (res.data || []).map(item => ({
+        ...item,
+        actualQty:
+          item.actualQty ??
+          Number(item.pickQuantity || 0) + Number(item.feedingQty || 0),
+        returnQty: item.returnQty ?? 0,
+      }));
+    } finally {
+      materialDetailLoading.value = false;
+    }
+  };
+
+  watch(
+    () => dialogVisible.value,
+    visible => {
+      if (visible) {
+        loadDetailList();
+      }
+    }
+  );
+
+  const handleClose = () => {
+    materialDetailTableData.value = [];
+  };
+
+  const handleActualQtyChange = (row, val) => {
+    row.returnQty = calcReturnQty(row);
+  };
+
+  const handleViewSupplementRecord = async row => {
+    if (!row?.id) return;
+    supplementRecordDialogVisible.value = true;
+    supplementRecordLoading.value = true;
+    supplementRecordTableData.value = [];
+    try {
+      const res = await listMaterialSupplementRecord({
+        pickId: row.id,
+        productionOrderId: props.orderRow.id,
+      });
+      supplementRecordTableData.value = res.data || [];
+    } finally {
+      supplementRecordLoading.value = false;
+    }
+  };
+
+  const buildReturnSummary = () => {
+    const map = new Map();
+    materialDetailTableData.value.forEach(item => {
+      const returnQty = calcReturnQty(item);
+      if (returnQty <= 0) return;
+      const key = `${item.productModelId || ""}_${item.productName || ""}_${
+        item.model || ""
+      }_${item.unit || ""}`;
+      const old = map.get(key) || {
+        summaryKey: key,
+        materialName: item.productName || "",
+        materialModel: item.model || "",
+        unit: item.unit || "",
+        returnQtyTotal: 0,
+      };
+      old.returnQtyTotal += returnQty;
+      map.set(key, old);
+    });
+    return Array.from(map.values());
+  };
+
+  const openReturnSummaryDialog = async () => {
+    if (!canOpenReturnSummary.value) {
+      ElMessage.warning("閫�鏂欐暟閲�=棰嗙敤鏁伴噺+琛ユ枡鏁伴噺-瀹為檯鏁伴噺锛屼笖闇�澶т簬0");
+      return;
+    }
+    returnSummaryList.value = buildReturnSummary();
+    returnSummaryDialogVisible.value = true;
+  };
+
+  const handleReturnConfirm = async () => {
+    if (!props.orderRow?.id) return;
+    materialReturnConfirming.value = true;
+    try {
+      await updateMaterialPickingLedger({
+        productionOrderId: props.orderRow.id,
+        productionOrderPickDto: materialDetailTableData.value.map(item => ({
+          id: item.id,
+          technologyOperationId: item.technologyOperationId,
+          operationName: item.operationName,
+          bom: item.bom === true,
+          productModelId: item.productModelId,
+          demandedQuantity: item.demandedQuantity,
+          unit: item.unit,
+          pickQuantity: item.pickQuantity,
+          batchNo: item.batchNo,
+          feedingQty: item.feedingQty,
+          returnQty: item.returnQty,
+          actualQty: item.actualQty,
+          feedingReason: item.feedingReason,
+          returned: true,
+        })),
+      });
+      returnSummaryDialogVisible.value = false;
+      dialogVisible.value = false;
+      emit("confirmed");
+    } finally {
+      materialReturnConfirming.value = false;
+    }
+  };
+</script>
+
+<style scoped lang="scss"></style>
diff --git a/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue b/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
new file mode 100644
index 0000000..210dbba
--- /dev/null
+++ b/src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
@@ -0,0 +1,391 @@
+<template>
+  <div>
+    <el-dialog v-model="dialogVisible"
+               title="棰嗘枡鍙拌处"
+               width="1200px"
+               @close="handleClose">
+      <div class="material-toolbar">
+        <el-button type="primary"
+                   @click="handleAddMaterialRow">鏂板</el-button>
+      </div>
+      <el-table v-loading="materialTableLoading"
+                :data="materialTableData"
+                border
+                row-key="tempId">
+        <el-table-column label="宸ュ簭鍚嶇О"
+                         min-width="140">
+          <template #default="{ row }">
+            <span v-if="row.bom === true">{{ row.operationName || "-" }}</span>
+            <el-select v-else
+                       v-model="row.operationName"
+                       placeholder="璇烽�夋嫨宸ュ簭"
+                       clearable
+                       filterable
+                       style="width: 100%;"
+                       @change="val => handleProcessNameChange(row, val)">
+              <el-option v-for="item in processOptions"
+                         :key="item.technologyOperationId"
+                         :label="item.name"
+                         :value="item.name" />
+            </el-select>
+          </template>
+        </el-table-column>
+        <el-table-column label="鍘熸枡鍚嶇О"
+                         min-width="140">
+          <template #default="{ row }">
+            <span v-if="row.bom === true">{{ row.materialName || "-" }}</span>
+            <el-button v-else
+                       type="primary"
+                       link
+                       @click="openMaterialProductSelect(row)">
+              {{ row.materialName || "閫夋嫨鍘熸枡" }}
+            </el-button>
+          </template>
+        </el-table-column>
+        <el-table-column label="鍘熸枡鍨嬪彿"
+                         min-width="140">
+          <template #default="{ row }">
+            {{ row.materialModel || "-" }}
+          </template>
+        </el-table-column>
+        <!-- 鎵瑰彿澶氶�� -->
+        <el-table-column min-width="200"
+                         label="鎵瑰彿">
+          <template #default="{ row }">
+            <el-select v-model="row.batchNo"
+                       multiple
+                       collapse-tags
+                       collapse-tags-indicator
+                       placeholder="璇烽�夋嫨鎵瑰彿"
+                       style="width: 100%;">
+              <el-option v-for="item in row.batchNoList"
+                         :key="item"
+                         :label="item"
+                         :value="item" />
+            </el-select>
+          </template>
+        </el-table-column>
+        <el-table-column label="闇�姹傛暟閲�"
+                         min-width="120">
+          <template #default="{ row }">
+            <span v-if="row.bom === true">{{ row.demandedQuantity ?? "-" }}</span>
+            <el-input-number v-else
+                             v-model="row.demandedQuantity"
+                             :min="0"
+                             :precision="3"
+                             :step="1"
+                             controls-position="right"
+                             style="width: 100%;"
+                             @change="val => handleRequiredQtyChange(row, val)" />
+          </template>
+        </el-table-column>
+        <el-table-column label="璁¢噺鍗曚綅"
+                         width="100">
+          <template #default="{ row }">
+            {{ row.unit || "-" }}
+          </template>
+        </el-table-column>
+        <el-table-column label="棰嗙敤鏁伴噺"
+                         min-width="120">
+          <template #default="{ row }">
+            <el-input-number v-model="row.pickQty"
+                             :min="0"
+                             :precision="3"
+                             :step="1"
+                             controls-position="right"
+                             style="width: 100%;" />
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔"
+                         width="90"
+                         fixed="right">
+          <template #default="{ $index, row }">
+            <el-button v-if="row.bom !== true"
+                       type="danger"
+                       link
+                       @click="handleDeleteMaterialRow($index)">鍒犻櫎</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary"
+                     :loading="materialSaving"
+                     @click="handleMaterialSave">淇濆瓨</el-button>
+          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <ProductSelectDialog v-model="materialProductDialogVisible"
+                         @confirm="handleMaterialProductConfirm"
+                         single />
+    <!-- request-url="/stockInventory/rawMaterials" -->
+  </div>
+</template>
+
+<script setup>
+  import { computed, ref, watch } from "vue";
+  import { ElMessage } from "element-plus";
+  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+  import {
+    findProductProcessRouteItemList,
+    listMain,
+  } from "@/api/productionManagement/productProcessRoute.js";
+  import {
+    listMaterialPickingDetail,
+    listMaterialPickingBom,
+    listMaterialPickingLedger,
+    saveMaterialPickingLedger,
+    updateMaterialPickingLedger,
+  } from "@/api/productionManagement/productionOrder.js";
+  import { queryList2 } from "@/api/productionManagement/productStructure.js";
+
+  const props = defineProps({
+    modelValue: { type: Boolean, default: false },
+    orderRow: { type: Object, default: null },
+  });
+  const emit = defineEmits(["update:modelValue", "saved"]);
+
+  const dialogVisible = computed({
+    get: () => props.modelValue,
+    set: val => emit("update:modelValue", val),
+  });
+
+  const materialProductDialogVisible = ref(false);
+  const materialTableLoading = ref(false);
+  const materialSaving = ref(false);
+  const materialTableData = ref([]);
+  const processOptions = ref([]);
+  const currentMaterialSelectRowIndex = ref(-1);
+  let materialTempId = 0;
+
+  const createMaterialRow = (row = {}) => ({
+    tempId: row.id || `temp_${++materialTempId}`,
+    id: row.id,
+    processId: row.processId || row.technologyOperationId,
+    technologyOperationId: row.technologyOperationId || row.processId,
+    operationName: row.operationName || "",
+    bom: row.bom === true,
+    materialModelId: row.materialModelId || row.productModelId,
+    materialName: row.materialName || row.productName || "",
+    materialModel: row.materialModel || row.model || "",
+    demandedQuantity: Number(row.requiredQty ?? row.demandedQuantity ?? 0),
+    unit: row.unit || "",
+    pickQty: Number(row.pickQty ?? row.pickQuantity ?? 0),
+    batchNo: row.batchNo
+      ? typeof row.batchNo === "string"
+        ? row.batchNo.split(",")
+        : row.batchNo
+      : [],
+    batchNoList: row.batchNoList || [],
+  });
+
+  const getProcessOptions = async () => {
+    if (!props.orderRow?.id) return;
+    const res = await findProductProcessRouteItemList({
+      orderId: props.orderRow.id,
+    });
+    const routeList = Array.isArray(res?.data)
+      ? res.data
+      : res?.data?.records || [];
+    const processMap = new Map();
+    routeList.forEach(item => {
+      const processId = item.technologyOperationId;
+      const operationName = item.operationName;
+      if (!processId || !operationName) return;
+      const key = `${processId}_${operationName}`;
+      if (!processMap.has(key)) {
+        processMap.set(key, {
+          id: processId,
+          name: operationName,
+        });
+      }
+    });
+    processOptions.value = Array.from(processMap.values());
+  };
+  const isDetail = ref(true);
+
+  const loadMaterialData = async () => {
+    if (!props.orderRow?.id) return;
+    materialTableLoading.value = true;
+    materialTableData.value = [];
+    await getProcessOptions();
+    try {
+      const detailRes = await listMaterialPickingDetail(props.orderRow.id);
+      const detailList = Array.isArray(detailRes?.data)
+        ? detailRes.data
+        : detailRes?.data?.records || [];
+      if (detailList.length > 0) {
+        isDetail.value = true;
+        materialTableData.value = detailList.map(item => createMaterialRow(item));
+        return;
+      } else {
+        isDetail.value = false;
+        const bomRes = await listMaterialPickingBom(props.orderRow.id);
+        const bomList = Array.isArray(bomRes?.data)
+          ? bomRes.data
+          : bomRes?.data?.records || [];
+        materialTableData.value = bomList.map(item => createMaterialRow(item));
+        return;
+      }
+    } finally {
+      materialTableLoading.value = false;
+    }
+  };
+
+  watch(
+    () => dialogVisible.value,
+    visible => {
+      if (visible) {
+        loadMaterialData();
+      }
+    }
+  );
+
+  const handleClose = () => {
+    materialTableData.value = [];
+    currentMaterialSelectRowIndex.value = -1;
+  };
+
+  const handleAddMaterialRow = () => {
+    materialTableData.value.push(createMaterialRow());
+  };
+
+  const handleDeleteMaterialRow = index => {
+    materialTableData.value.splice(index, 1);
+  };
+
+  const handleProcessNameChange = (row, operationName) => {
+    const process = processOptions.value.find(
+      item => item.name === operationName
+    );
+    row.technologyOperationId = process?.technologyOperationId;
+  };
+
+  const handleRequiredQtyChange = (row, val) => {
+    const required = Number(val ?? 0);
+    row.demandedQuantity = required;
+    if (!row.pickQty || Number(row.pickQty) === 0) {
+      row.pickQty = required;
+    }
+  };
+
+  const openMaterialProductSelect = row => {
+    currentMaterialSelectRowIndex.value = materialTableData.value.findIndex(
+      item => item.tempId === row.tempId
+    );
+    materialProductDialogVisible.value = true;
+  };
+
+  const handleMaterialProductConfirm = products => {
+    console.log(products, "products");
+
+    if (!products || products.length === 0) return;
+    const index = currentMaterialSelectRowIndex.value;
+    if (index < 0 || !materialTableData.value[index]) return;
+    const product = products[0];
+    const row = materialTableData.value[index];
+    row.materialModelId =
+      product.materialModelId || product.modelId || product.id;
+    row.materialName =
+      product.materialName || product.productName || product.name || "";
+    row.materialModel = product.materialModel || product.model || "";
+    row.unit = product.unit || product.measureUnit || "";
+    row.batchNoList = product.batchNoList;
+    currentMaterialSelectRowIndex.value = -1;
+    materialProductDialogVisible.value = false;
+  };
+
+  const validateMaterialRows = () => {
+    if (materialTableData.value.length === 0) {
+      return { valid: false, message: "璇峰厛鏂板棰嗘枡鏁版嵁" };
+    }
+    const invalidNewRow = materialTableData.value.find(
+      item => item.bom !== true && (!item.operationName || !item.materialName)
+    );
+    if (invalidNewRow) {
+      return { valid: false, message: "鏂板琛岀殑宸ュ簭鍚嶇О鍜屽師鏂欏悕绉颁负蹇呭~椤�" };
+    }
+    const invalidRow = materialTableData.value.find(
+      item =>
+        !item.operationName ||
+        !item.materialName ||
+        (Number(item.pickQty) > 0 &&
+          (!item.batchNo || item.batchNo.length === 0)) ||
+        item.demandedQuantity === null ||
+        item.demandedQuantity === undefined ||
+        item.pickQty === null ||
+        item.pickQty === undefined
+    );
+    if (invalidRow) {
+      return { valid: false, message: "璇峰畬鍠勫伐搴忋�佸師鏂欍�佹壒鍙峰拰鏁伴噺鍚庡啀淇濆瓨" };
+    }
+    return { valid: true, message: "" };
+  };
+
+  const handleMaterialSave = async () => {
+    if (!props.orderRow?.id) return;
+    const validateResult = validateMaterialRows();
+    if (!validateResult.valid) {
+      ElMessage.warning(validateResult.message);
+      return;
+    }
+    materialSaving.value = true;
+    try {
+      if (isDetail.value) {
+        await updateMaterialPickingLedger({
+          productionOrderId: props.orderRow.id,
+          productionOrderPickDto: materialTableData.value.map(item => ({
+            id: item.id,
+            // processId: item.operationName,
+            technologyOperationId: item.technologyOperationId,
+            operationName: item.operationName,
+            bom: item.bom === true,
+            productModelId: item.materialModelId,
+            // materialName: item.materialName,
+            // materialModel: item.materialModel,
+            demandedQuantity: item.demandedQuantity,
+            unit: item.unit,
+            pickQuantity: item.pickQty,
+            batchNo: Array.isArray(item.batchNo)
+              ? item.batchNo.join(",")
+              : item.batchNo,
+          })),
+        });
+      } else {
+        await saveMaterialPickingLedger({
+          productionOrderId: props.orderRow.id,
+          productionOrderPickDto: materialTableData.value.map(item => ({
+            id: item.id,
+            // processId: item.operationName,
+            technologyOperationId: item.technologyOperationId,
+            operationName: item.operationName,
+            bom: item.bom === true,
+            productModelId: item.materialModelId,
+            // materialName: item.materialName,
+            // materialModel: item.materialModel,
+            demandedQuantity: item.demandedQuantity,
+            unit: item.unit,
+            pickQuantity: item.pickQty,
+            batchNo: Array.isArray(item.batchNo)
+              ? item.batchNo.join(",")
+              : item.batchNo,
+          })),
+        });
+      }
+
+      ElMessage({ message: "棰嗘枡鎴愬姛", type: "success" });
+      emit("saved");
+      dialogVisible.value = false;
+    } finally {
+      materialSaving.value = false;
+    }
+  };
+</script>
+
+<style scoped lang="scss">
+  .material-toolbar {
+    margin-bottom: 12px;
+    text-align: right;
+  }
+</style>
diff --git a/src/views/productionManagement/productionOrder/components/MaterialSupplementDialog.vue b/src/views/productionManagement/productionOrder/components/MaterialSupplementDialog.vue
new file mode 100644
index 0000000..4f052ed
--- /dev/null
+++ b/src/views/productionManagement/productionOrder/components/MaterialSupplementDialog.vue
@@ -0,0 +1,165 @@
+<template>
+  <el-dialog v-model="dialogVisible"
+             title="琛ユ枡"
+             width="1200px"
+             @close="handleClose">
+    <el-table v-loading="loading"
+              :data="tableData"
+              border
+              row-key="id">
+      <el-table-column label="宸ュ簭鍚嶇О"
+                       prop="operationName"
+                       min-width="140" />
+      <el-table-column label="鍘熸枡鍚嶇О"
+                       prop="productName"
+                       min-width="140" />
+      <el-table-column label="鍘熸枡鍨嬪彿"
+                       prop="model"
+                       min-width="140" />
+      <el-table-column label="璁¢噺鍗曚綅"
+                       prop="unit"
+                       width="100" />
+      <el-table-column label="闇�姹傛暟閲�"
+                       prop="demandedQuantity"
+                       width="100" />
+      <el-table-column label="棰嗙敤鏁伴噺"
+                       prop="pickQuantity"
+                       width="100" />
+      <el-table-column label="宸茶ˉ鏁伴噺"
+                       prop="feedingQty"
+                       width="100" />
+      <el-table-column label="琛ユ枡鏁伴噺"
+                       min-width="150">
+        <template #default="{ row }">
+          <el-input-number v-model="row.newSupplementQty"
+                           :min="0"
+                           :precision="3"
+                           :step="1"
+                           controls-position="right"
+                           placeholder="杈撳叆琛ユ枡鏁伴噺"
+                           style="width: 100%;" />
+        </template>
+      </el-table-column>
+      <el-table-column label="琛ユ枡鍘熷洜"
+                       min-width="200">
+        <template #default="{ row }">
+          <el-input v-model="row.newSupplementReason"
+                    placeholder="杈撳叆琛ユ枡鍘熷洜"
+                    maxlength="200"
+                    show-word-limit />
+        </template>
+      </el-table-column>
+    </el-table>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button type="primary"
+                   :loading="submitting"
+                   @click="handleSubmit">纭� 瀹�</el-button>
+        <el-button @click="dialogVisible = false">鍙� 娑�</el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+  import { computed, ref, watch } from "vue";
+  import { ElMessage } from "element-plus";
+  import {
+    listMaterialPickingDetail,
+    updateMaterialPickingLedger,
+  } from "@/api/productionManagement/productionOrder.js";
+
+  const props = defineProps({
+    modelValue: { type: Boolean, default: false },
+    orderRow: { type: Object, default: null },
+  });
+  const emit = defineEmits(["update:modelValue", "saved"]);
+
+  const dialogVisible = computed({
+    get: () => props.modelValue,
+    set: val => emit("update:modelValue", val),
+  });
+
+  const loading = ref(false);
+  const submitting = ref(false);
+  const tableData = ref([]);
+
+  const loadData = async () => {
+    if (!props.orderRow?.id) return;
+    loading.value = true;
+    try {
+      const res = await listMaterialPickingDetail(props.orderRow.id);
+      tableData.value = (res.data || []).map(item => ({
+        ...item,
+        newSupplementQty: 0,
+        newSupplementReason: "",
+      }));
+    } catch (e) {
+      console.error("鑾峰彇鐗╂枡鏄庣粏澶辫触锛�", e);
+      ElMessage.error("鑾峰彇鐗╂枡鏄庣粏澶辫触");
+    } finally {
+      loading.value = false;
+    }
+  };
+
+  watch(
+    () => dialogVisible.value,
+    visible => {
+      if (visible) {
+        loadData();
+      }
+    }
+  );
+
+  const handleClose = () => {
+    tableData.value = [];
+  };
+
+  const handleSubmit = async () => {
+    const supplementList = tableData.value.filter(
+      item => item.newSupplementQty > 0
+    );
+    if (supplementList.length === 0) {
+      ElMessage.warning("璇疯嚦灏戣緭鍏ヤ竴鏉¤ˉ鏂欐暟閲�");
+      return;
+    }
+
+    const invalidRow = supplementList.find(item => !item.newSupplementReason);
+    if (invalidRow) {
+      ElMessage.warning("璇疯緭鍏ヨˉ鏂欏師鍥�");
+      return;
+    }
+
+    submitting.value = true;
+    try {
+      await updateMaterialPickingLedger({
+        productionOrderId: props.orderRow.id,
+        productionOrderPickDto: tableData.value.map(item => ({
+          id: item.id,
+          technologyOperationId: item.technologyOperationId,
+          operationName: item.operationName,
+          bom: item.bom === true,
+          productModelId: item.productModelId,
+          demandedQuantity: item.demandedQuantity,
+          unit: item.unit,
+          pickQuantity: item.pickQuantity,
+          batchNo: item.batchNo,
+          feedingQuantity: item.newSupplementQty || 0,
+          feedingReason: item.newSupplementReason || "",
+          pickType: 2,
+        })),
+      });
+      ElMessage.success("琛ユ枡鎴愬姛");
+      dialogVisible.value = false;
+      emit("saved");
+    } catch (e) {
+      console.error("琛ユ枡澶辫触锛�", e);
+      ElMessage.error("琛ユ枡澶辫触");
+    } finally {
+      submitting.value = false;
+    }
+  };
+</script>
+
+<style scoped lang="scss">
+</style>
diff --git a/src/views/productionManagement/productionOrder/components/PrintMaterialRequisition.vue b/src/views/productionManagement/productionOrder/components/PrintMaterialRequisition.vue
new file mode 100644
index 0000000..d1aa267
--- /dev/null
+++ b/src/views/productionManagement/productionOrder/components/PrintMaterialRequisition.vue
@@ -0,0 +1,225 @@
+<template>
+  <div class="print-container"
+       id="print-requisition">
+    <div class="print-content">
+      <div class="bill-title">鐢熶骇棰嗘枡鍗�</div>
+      <div class="info-grid">
+        <div class="info-row">
+          <div class="info-item">
+            <span class="label">鍒涘缓鏃ユ湡锛�</span>
+            <span class="value">{{ formatDate(orderRow?.createTime) }}</span>
+          </div>
+          <div class="info-item">
+            <span class="label">棰嗘枡鍗曞彿锛�</span>
+            <span class="value">{{ orderRow?.npsNo }}</span>
+          </div>
+          <div class="info-item">
+            <span class="label">鐢宠浜猴細</span>
+            <span class="value">{{ userName }}</span>
+          </div>
+        </div>
+        <div class="info-row">
+          <div class="info-item"
+               style="width: 50%;">
+            <span class="label">浜у搧鍚嶇О/鍨嬪彿锛�</span>
+            <span class="value">{{ orderRow?.productName }} / {{ orderRow?.model }}</span>
+          </div>
+          <div class="info-item"
+               style="width: 25%;">
+            <span class="label">鐢熶骇鏁伴噺锛�</span>
+            <span class="value">{{ orderRow?.quantity }}</span>
+          </div>
+          <div class="info-item"
+               style="width: 25%;">
+            <span class="label">闇�姹傛棩鏈燂細</span>
+            <span class="value">{{ formatDate(orderRow?.planCompleteTime) }}</span>
+          </div>
+        </div>
+      </div>
+      <table class="material-table">
+        <thead>
+          <tr>
+            <th width="50">搴忓彿</th>
+            <th>宸ュ簭鍚嶇О</th>
+            <th>瑙勬牸/鍚嶇О</th>
+            <th>鎵瑰彿</th>
+            <th width="80">闇�姹傛暟閲�</th>
+            <th width="80">棰嗘枡鏁伴噺</th>
+            <th width="60">鍗曚綅</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr v-for="(item, index) in materialList"
+              :key="index">
+            <td align="center">{{ index + 1 }}</td>
+            <td>{{ item.operationName || '-' }}</td>
+            <td>{{ item.materialName || item.productName }} {{ item.materialModel || item.model }}</td>
+            <td>{{ item.batchNo || '-' }}</td>
+            <td align="right">{{ item.demandedQuantity }}</td>
+            <td align="right">{{ item.pickQuantity || item.pickQty || 0 }}</td>
+            <td align="center">{{ item.unit }}</td>
+          </tr>
+        </tbody>
+      </table>
+      <div class="print-footer">
+        <div class="footer-item">棰嗘枡锛歘_______________</div>
+        <div class="footer-item">鍙戞枡锛歘_______________</div>
+        <div class="footer-item">瀹℃牳锛歘_______________</div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+  import dayjs from "dayjs";
+  import useUserStore from "@/store/modules/user";
+  import { computed } from "vue";
+
+  const props = defineProps({
+    orderRow: {
+      type: Object,
+      default: () => ({}),
+    },
+    materialList: {
+      type: Array,
+      default: () => [],
+    },
+  });
+
+  const userStore = useUserStore();
+  const userName = computed(() => userStore.nickName || userStore.name || "-");
+
+  const formatDate = date => {
+    return date ? dayjs(date).format("YYYY骞碝M鏈圖D鏃�") : "-";
+  };
+</script>
+
+<style lang="scss">
+  /* 灞忓箷鏄剧ず鏍峰紡 */
+  .print-requisition-wrapper {
+    display: none;
+  }
+
+  /* 鎵撳嵃涓撶敤鏍峰紡 */
+  @media print {
+    @page {
+      size: landscape;
+      margin: 10mm;
+    }
+
+    /* 鍩虹鎵撳嵃璁剧疆 */
+    html,
+    body {
+      visibility: hidden;
+      height: auto !important;
+      overflow: visible !important;
+      margin: 0 !important;
+      padding: 0 !important;
+      width: 100%;
+    }
+
+    /* 鏄惧紡鏄剧ず鎵撳嵃瀹瑰櫒鍙婂叾鎵�鏈夊瓙鍏冪礌 */
+    .print-requisition-wrapper,
+    .print-requisition-wrapper * {
+      visibility: visible !important;
+    }
+
+    /* 纭繚鎵撳嵃瀹瑰櫒鍗犳嵁鏁翠釜椤甸潰骞剁Щ闄ょ粷瀵瑰畾浣嶅共鎵� */
+    .print-requisition-wrapper {
+      display: block !important;
+      position: absolute;
+      left: 0;
+      top: 0;
+      width: 100%;
+      height: auto;
+      background: white;
+      margin: 0 !important;
+      padding: 0 !important;
+      z-index: 99999;
+    }
+
+    .print-container {
+      width: 100% !important;
+      padding: 0 10mm; /* 浣跨敤瀵圭О鐨勫乏鍙冲唴杈硅窛纭繚灞呬腑 */
+      box-sizing: border-box;
+      height: auto;
+      overflow: visible;
+      color: #000;
+      font-family: "SimSun", "STSong", serif;
+      page-break-inside: avoid;
+      display: block;
+      .print-content {
+        width: 100%;
+        text-align: center;
+      }
+      .bill-title {
+        font-size: 20px;
+        font-weight: bold;
+        text-align: center;
+        margin-bottom: 20px;
+        letter-spacing: 5px;
+        text-decoration: underline;
+      }
+
+      .info-grid {
+        margin-bottom: 10px;
+        font-size: 14px;
+
+        .info-row {
+          display: flex;
+          flex-wrap: wrap;
+          margin-bottom: 8px;
+
+          .info-item {
+            width: 33.33%;
+            display: flex;
+            align-items: flex-end;
+
+            .label {
+              font-weight: bold;
+              white-space: nowrap;
+            }
+            .value {
+              border-bottom: 1px solid #000;
+              padding: 0 5px;
+              flex: 1;
+              min-height: 20px;
+            }
+          }
+        }
+      }
+
+      .material-table {
+        width: 100%;
+        border-collapse: collapse;
+        border: 2px solid #000;
+        font-size: 13px;
+
+        th,
+        td {
+          border: 1px solid #000 !important;
+          padding: 6px 4px;
+          height: 25px;
+          -webkit-print-color-adjust: exact;
+          print-color-adjust: exact;
+        }
+
+        th {
+          background-color: #f2f2f2 !important;
+          font-weight: bold;
+        }
+      }
+
+      .print-footer {
+        display: flex;
+        justify-content: space-between;
+        margin-top: 30px;
+        padding: 0 10px;
+
+        .footer-item {
+          font-size: 14px;
+        }
+      }
+    }
+  }
+</style>
diff --git a/src/views/productionManagement/productionOrder/index.vue b/src/views/productionManagement/productionOrder/index.vue
index fc64063..8d44b85 100644
--- a/src/views/productionManagement/productionOrder/index.vue
+++ b/src/views/productionManagement/productionOrder/index.vue
@@ -3,16 +3,8 @@
     <div class="search_form">
       <el-form :model="searchForm"
                :inline="true">
-        <el-form-item label="瀹㈡埛鍚嶇О:">
-          <el-input v-model="searchForm.customerName"
-                    placeholder="璇疯緭鍏�"
-                    clearable
-                    prefix-icon="Search"
-                    style="width: 160px;"
-                    @change="handleQuery" />
-        </el-form-item>
-        <el-form-item label="鍚堝悓鍙�:">
-          <el-input v-model="searchForm.salesContractNo"
+        <el-form-item label="鐢熶骇璁㈠崟鍙�:">
+          <el-input v-model="searchForm.npsNo"
                     placeholder="璇疯緭鍏�"
                     clearable
                     prefix-icon="Search"
@@ -20,7 +12,7 @@
                     @change="handleQuery" />
         </el-form-item>
         <el-form-item label="浜у搧鍚嶇О:">
-          <el-input v-model="searchForm.productCategory"
+          <el-input v-model="searchForm.productName"
                     placeholder="璇疯緭鍏�"
                     clearable
                     prefix-icon="Search"
@@ -28,21 +20,42 @@
                     @change="handleQuery" />
         </el-form-item>
         <el-form-item label="瑙勬牸:">
-          <el-input v-model="searchForm.specificationModel"
+          <el-input v-model="searchForm.model"
                     placeholder="璇疯緭鍏�"
                     clearable
                     prefix-icon="Search"
                     style="width: 160px;"
                     @change="handleQuery" />
         </el-form-item>
+        <el-form-item label="鐘舵��:">
+          <el-select v-model="searchForm.status"
+                     placeholder="璇烽�夋嫨"
+                     style="width: 160px;"
+                     @change="handleQuery">
+            <el-option label="寰呭紑濮�"
+                       value="1" />
+            <el-option label="杩涜涓�"
+                       value="2" />
+            <el-option label="宸插畬鎴�"
+                       value="3" />
+            <el-option label="宸插彇娑�"
+                       value="4" />
+            <el-option label="宸茬粨鏉�"
+                       value="5" />
+          </el-select>
+        </el-form-item>
         <el-form-item>
           <el-button type="primary"
                      @click="handleQuery">鎼滅储</el-button>
+          <el-button type="info"
+                     @click="handleReset">閲嶇疆</el-button>
         </el-form-item>
       </el-form>
-      <div>
-        <el-button type="primary" @click="isShowNewModal = true">鏂板</el-button>
-        <el-button type="danger" @click="handleDelete">鍒犻櫎</el-button>
+      <div class="action-buttons">
+        <!-- <el-button type="primary"
+                   @click="isShowNewModal = true">鏂板</el-button> -->
+        <el-button type="danger"
+                   @click="handleDelete">閫�鍥�</el-button>
         <el-button @click="handleOut">瀵煎嚭</el-button>
       </div>
     </div>
@@ -54,14 +67,33 @@
                 :tableLoading="tableLoading"
                 :row-class-name="tableRowClassName"
                 :isSelection="true"
+                :selectable="row => !row.endOrder"
                 @selection-change="handleSelectionChange"
                 @pagination="pagination">
         <template #completionStatus="{ row }">
-          <el-progress
-            :percentage="toProgressPercentage(row?.completionStatus)"
-            :color="progressColor(toProgressPercentage(row?.completionStatus))"
-            :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''"
-          />
+          <el-progress :percentage="toProgressPercentage(row?.completionStatus)"
+                       :color="progressColor(toProgressPercentage(row?.completionStatus))"
+                       :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''" />
+        </template>
+        <template #processRouteStatus="{ row }">
+          <div v-if="row.processRouteStatus && row.processRouteStatus.length"
+               class="process-progress-container">
+            <div v-for="(item, index) in row.processRouteStatus"
+                 :key="index"
+                 class="process-step">
+              <div class="step-content">
+                <div class="step-circle"
+                     :class="{ 'is-completed': item.percentage >= 100 }">
+                  <span class="step-percentage"
+                        :style="{ color: item.percentage >= 70 ? item.percentage >= 100 ? '#67c23a' : '#f56c6c' : '#000' }">{{ item.percentage }}%</span>
+                </div>
+                <div class="step-name">{{ item.name }}</div>
+              </div>
+              <div v-if="index < row.processRouteStatus.length - 1"
+                   class="step-line"></div>
+            </div>
+          </div>
+          <span v-else>-</span>
         </template>
       </PIMTable>
     </div>
@@ -90,15 +122,107 @@
         </span>
       </template>
     </el-dialog>
-
+    <!-- 鏉ユ簮鏁版嵁寮圭獥 -->
+    <el-dialog v-model="sourceDataDialogVisible"
+               title="鏉ユ簮鏁版嵁"
+               width="1200px">
+      <div v-if="sourceRowData"
+           class="applyno-summary1">
+        <div class="summary-item">
+          <span class="summary-label">浜у搧鍚嶇О锛�</span>
+          <span class="summary-value">
+            <el-tag type="primary">{{ sourceRowData.productName || '-' }}</el-tag>
+          </span>
+        </div>
+        <div class="summary-item">
+          <span class="summary-label">瑙勬牸锛�</span>
+          <span class="summary-value">{{ sourceRowData.model || '-' }}</span>
+        </div>
+        <div class="summary-item">
+          <span class="summary-label">璁㈠崟闇�姹傛暟閲忥細</span>
+          <span class="summary-value">{{ sourceRowData.quantity || 0 }}</span>
+        </div>
+      </div>
+      <div class="source-table-container">
+        <div class="source-data-cards-container">
+          <div v-for="(item, index) in sourceTableData"
+               :key="index"
+               class="source-data-card">
+            <div class="card-body">
+              <div class="info-grid">
+                <div class="info-item">
+                  <div class="info-label">璁″垝鍙�</div>
+                  <div class="info-value">{{ item.mpsNo || '-' }}</div>
+                </div>
+                <div class="info-item">
+                  <div class="info-label">鏁版嵁鏉ユ簮</div>
+                  <div class="info-value">
+                    <el-tag :type="item.source === '閿�鍞�' ? 'primary' : 'warning'">
+                      {{ item.source || '鏈煡' }}
+                    </el-tag>
+                  </div>
+                </div>
+                <div class="info-item">
+                  <div class="info-label">鍚堝悓鍙�</div>
+                  <div class="info-value">{{ item.salesContractNo || '-' }}</div>
+                </div>
+                <div class="info-item">
+                  <div class="info-label">瀹㈡埛鍚嶇О</div>
+                  <div class="info-value">{{ item.customerName || '-' }}</div>
+                </div>
+                <div class="info-item">
+                  <div class="info-label">椤圭洰鍚嶇О</div>
+                  <div class="info-value">{{ item.projectName || '-' }}</div>
+                </div>
+                <div class="info-item">
+                  <div class="info-label">璁″垝闇�姹傛暟閲�</div>
+                  <div class="info-value">{{ item.qtyRequired || 0 }} {{ item.unit || '' }}</div>
+                </div>
+                <div class="info-item">
+                  <div class="info-label">鍗曚綅</div>
+                  <div class="info-value">{{ item.unit || '-' }}</div>
+                </div>
+                <div class="info-item">
+                  <div class="info-label">闇�姹傛棩鏈�</div>
+                  <div class="info-value">{{ item.requiredDate ? dayjs(item.requiredDate).format('YYYY-MM-DD') : '-' }}</div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </el-dialog>
+    <MaterialLedgerDialog v-model="materialDialogVisible"
+                          :order-row="currentMaterialOrder"
+                          @saved="getList" />
+    <MaterialDetailDialog v-model="materialDetailDialogVisible"
+                          :order-row="currentMaterialDetailOrder"
+                          @confirmed="getList" />
+    <MaterialSupplementDialog v-model="materialSupplementDialogVisible"
+                              :order-row="currentMaterialSupplementOrder"
+                              @saved="getList" />
     <new-product-order v-if="isShowNewModal"
-                         v-model:visible="isShowNewModal"
-                         @completed="handleQuery" />
+                       v-model:visible="isShowNewModal"
+                       @completed="handleQuery" />
+    <!-- 鎵撳嵃棰嗘枡鍗曠粍浠� -->
+    <div class="print-requisition-wrapper">
+      <PrintMaterialRequisition ref="printRef"
+                                :order-row="printOrderRow"
+                                :material-list="printMaterialList" />
+    </div>
   </div>
 </template>
 
 <script setup>
-  import { onMounted, ref } from "vue";
+  import {
+    computed,
+    defineAsyncComponent,
+    getCurrentInstance,
+    onMounted,
+    reactive,
+    ref,
+    toRefs,
+  } from "vue";
   import { ElMessageBox } from "element-plus";
   import dayjs from "dayjs";
   import { useRouter } from "vue-router";
@@ -106,48 +230,95 @@
     productOrderListPage,
     listProcessRoute,
     bindingRoute,
-    listProcessBom, delProductOrder,
+    listProcessBom,
+    delProductOrder,
+    getProductOrderSource,
+    updateProductOrder,
   } from "@/api/productionManagement/productionOrder.js";
+  import { productWorkOrderPage } from "@/api/productionManagement/workOrder.js";
   import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
-  import {fileDel} from "@/api/financialManagement/revenueManagement.js";
+  import MaterialLedgerDialog from "@/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue";
+  import MaterialDetailDialog from "@/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue";
+  import MaterialSupplementDialog from "@/views/productionManagement/productionOrder/components/MaterialSupplementDialog.vue";
+  import PrintMaterialRequisition from "@/views/productionManagement/productionOrder/components/PrintMaterialRequisition.vue";
   import PIMTable from "@/components/PIMTable/PIMTable.vue";
-  const NewProductOrder = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/New.vue"));
+  import { listPage } from "@/api/productionManagement/processRoute.js";
+  import {
+    listMaterialPickingDetail,
+    listMaterialPickingBom,
+  } from "@/api/productionManagement/productionOrder.js";
+  const NewProductOrder = defineAsyncComponent(() =>
+    import("@/views/productionManagement/productionOrder/New.vue")
+  );
 
   const { proxy } = getCurrentInstance();
 
   const router = useRouter();
   const isShowNewModal = ref(false);
+  const sourceDataDialogVisible = ref(false);
+  const sourceTableData = ref([]);
+  const sourceRowData = ref(null);
+  const sourcePage = reactive({
+    total: 0,
+  });
 
-  const tableColumn = ref([
+  const processColumnWidth = computed(() => {
+    if (!tableData.value || tableData.value.length === 0) return "200px";
+    const maxProcesses = Math.max(
+      ...tableData.value.map(row => row.processRouteStatus?.length || 0)
+    );
+    if (maxProcesses === 0) return "100px";
+    // 姣忎釜宸ュ簭鍦嗗湀 36px + 绾挎潯 30px = 66px锛岄澶栧姞 60px 杈硅窛鍜屾枃瀛楃┖闂�
+    return `${maxProcesses * 66 + 60}px`;
+  });
+
+  const tableColumn = computed(() => [
     {
       label: "鐢熶骇璁㈠崟鍙�",
       prop: "npsNo",
-      width: '120px',
+      width: "150px",
     },
+    // 1.寰呭紑濮嬨��2.杩涜涓��3.宸插畬鎴愩��4.宸插彇娑堛��5.宸茬粨鏉�
     {
-      label: "閿�鍞悎鍚屽彿",
-      prop: "salesContractNo",
-      width: '150px',
-    },
-    {
-      label: "瀹㈡埛鍚嶇О",
-      prop: "customerName",
-      width: '200px',
+      label: "鐘舵��",
+      prop: "status",
+      width: "150px",
+      dataType: "tag",
+      formatData: val =>
+        val === 1
+          ? "寰呭紑濮�"
+          : val === 2
+          ? "杩涜涓�"
+          : val === 3
+          ? "宸插畬鎴�"
+          : val === 5
+          ? "宸茬粨鏉�"
+          : "宸插彇娑�",
+      formatType: val =>
+        val === 1
+          ? "primary"
+          : val === 2
+          ? "warning"
+          : val === 3
+          ? "success"
+          : val === 5
+          ? "danger"
+          : "info",
     },
     {
       label: "浜у搧鍚嶇О",
-      prop: "productCategory",
-      width: '120px',
+      prop: "productName",
+      width: "120px",
     },
     {
       label: "瑙勬牸",
-      prop: "specificationModel",
-      width: '120px',
+      prop: "model",
+      width: "120px",
     },
     {
       label: "宸ヨ壓璺嚎缂栧彿",
       prop: "processRouteCode",
-      width: '200px',
+      width: "200px",
     },
     {
       label: "闇�姹傛暟閲�",
@@ -156,6 +327,13 @@
     {
       label: "瀹屾垚鏁伴噺",
       prop: "completeQuantity",
+    },
+    {
+      label: "宸ュ簭鐢熶骇杩涘害",
+      prop: "processRouteStatus",
+      dataType: "slot",
+      slot: "processRouteStatus",
+      width: processColumnWidth.value,
     },
     {
       dataType: "slot",
@@ -177,8 +355,8 @@
       width: 120,
     },
     {
-      label: "浜や粯鏃ユ湡",
-      prop: "deliveryDate",
+      label: "璁″垝瀹屾垚鏃堕棿",
+      prop: "planCompleteTime",
       formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
       width: 120,
     },
@@ -187,11 +365,12 @@
       label: "鎿嶄綔",
       align: "center",
       fixed: "right",
-      width: 200,
+      width: 280,
       operation: [
         {
           name: "宸ヨ壓璺嚎",
           type: "text",
+          showHide: row => row.processRouteCode,
           clickFun: row => {
             showRouteItemModal(row);
           },
@@ -199,16 +378,83 @@
         {
           name: "缁戝畾宸ヨ壓璺嚎",
           type: "text",
-          showHide: row => !row.processRouteCode,
+          showHide: row => !row.processRouteCode && !row.endOrder,
           clickFun: row => {
-            openBindRouteDialog(row);
+            openBindRouteDialog(row, "add");
           },
         },
         {
-          name: "浜у搧缁撴瀯",
+          name: "鏇存崲宸ヨ壓璺嚎",
+          type: "text",
+          showHide: row => row.processRouteCode && !row.endOrder,
+          clickFun: row => {
+            openBindRouteDialog(row, "change");
+          },
+        },
+        {
+          name: "鏉ユ簮",
           type: "text",
           clickFun: row => {
-            showProductStructure(row);
+            showSourceData(row);
+          },
+        },
+        {
+          name: "棰嗘枡",
+          type: "text",
+          color: "#5EC7AB",
+          showHide: row => !row.endOrder && !row.returned,
+          clickFun: row => {
+            openMaterialDialog(row);
+          },
+        },
+        {
+          name: "琛ユ枡",
+          type: "text",
+          color: "#5EC7AB",
+          showHide: row => !row.endOrder && !row.returned,
+          clickFun: row => {
+            openMaterialSupplementDialog(row);
+          },
+        },
+        {
+          name: "棰嗘枡璇︽儏",
+          type: "text",
+          color: "#5EC7AB",
+          clickFun: row => {
+            openMaterialDetailDialog(row);
+          },
+        },
+        {
+          name: "鎵撳嵃棰嗘枡鍗�",
+          type: "text",
+          color: "#5EC7AB",
+          showHide: row => !row.endOrder,
+          clickFun: row => {
+            handlePrint(row);
+          },
+        },
+        {
+          name: "鐢熶骇杩芥函",
+          type: "text",
+          color: "#409eff",
+          clickFun: row => {
+            router.push({
+              path: "/productionManagement/productionTraceability",
+              query: {
+                npsNo: row.npsNo,
+                productName: row.productName,
+                model: row.model,
+              },
+            });
+          },
+        },
+        {
+          name: "缁撴潫璁㈠崟",
+          type: "text",
+          color: "red",
+          showHide: row => !row.endOrder,
+          clickFun: row => {
+            handleEndOrder(row);
           },
         },
       ],
@@ -225,11 +471,13 @@
 
   const data = reactive({
     searchForm: {
+      npsNo: "",
       customerName: "",
       salesContractNo: "",
       projectName: "",
-      productCategory: "",
-      specificationModel: "",
+      productName: "",
+      model: "",
+      status: "",
     },
   });
   const { searchForm } = toRefs(data);
@@ -253,18 +501,18 @@
 
   // 娣诲姞琛ㄨ绫诲悕鏂规硶
   const tableRowClassName = ({ row }) => {
-    if (!row.deliveryDate) return '';
-    if (row.isFh) return '';
+    if (!row.planCompleteTime) return "";
+    if (row.isFh) return "";
 
     const diff = row.deliveryDaysDiff;
     if (diff === 15) {
-      return 'yellow';
+      return "yellow";
     } else if (diff === 10) {
-      return 'pink';
+      return "pink";
     } else if (diff === 2) {
-      return 'purple';
+      return "purple";
     } else if (diff < 2) {
-      return 'red';
+      return "red";
     }
   };
 
@@ -277,10 +525,52 @@
     orderId: null,
     routeId: null,
   });
+  const materialDialogVisible = ref(false);
+  const currentMaterialOrder = ref(null);
+  const materialDetailDialogVisible = ref(false);
+  const currentMaterialDetailOrder = ref(null);
+  const materialSupplementDialogVisible = ref(false);
+  const currentMaterialSupplementOrder = ref(null);
 
-  const openBindRouteDialog = async row => {
+  // 鎵撳嵃鐩稿叧
+  const printOrderRow = ref(null);
+  const printMaterialList = ref([]);
+  const handlePrint = async row => {
+    printOrderRow.value = row;
+    proxy.$modal.loading("姝e湪鑾峰彇棰嗘枡鏁版嵁...");
+    try {
+      printMaterialList.value = [];
+      const detailRes = await listMaterialPickingDetail(row.id);
+      const detailList = Array.isArray(detailRes?.data)
+        ? detailRes.data
+        : detailRes?.data?.records || [];
+
+      if (detailList.length > 0) {
+        printMaterialList.value = detailList;
+      }
+
+      if (printMaterialList.value.length === 0) {
+        proxy.$modal.msgWarning("鏆傛棤棰嗘枡鏁版嵁");
+        return;
+      }
+
+      // 绛夊緟 DOM 鏇存柊鍚庢墽琛屾墦鍗�
+      proxy.$nextTick(() => {
+        setTimeout(() => {
+          window.print();
+        }, 800);
+      });
+    } catch (e) {
+      console.error("鑾峰彇棰嗘枡鏁版嵁澶辫触锛�", e);
+      proxy.$modal.msgError("鑾峰彇棰嗘枡鏁版嵁澶辫触");
+    } finally {
+      proxy.$modal.closeLoading();
+    }
+  };
+
+  const openBindRouteDialog = async (row, type) => {
     bindForm.orderId = row.id;
-    bindForm.routeId = null;
+    bindForm.routeId = type === "add" ? null : row.processRouteCode;
     bindRouteDialogVisible.value = true;
     routeOptions.value = [];
     if (!row.productModelId) {
@@ -290,8 +580,8 @@
     }
     bindRouteLoading.value = true;
     try {
-      const res = await listProcessRoute({ productModelId: row.productModelId });
-      routeOptions.value = res.data || [];
+      const res = await listPage({ productModelId: row.productModelId });
+      routeOptions.value = res.data.records || [];
     } catch (e) {
       console.error("鑾峰彇宸ヨ壓璺嚎鍒楄〃澶辫触锛�", e);
       proxy.$modal.msgError("鑾峰彇宸ヨ壓璺嚎鍒楄〃澶辫触");
@@ -309,7 +599,7 @@
     try {
       await bindingRoute({
         id: bindForm.orderId,
-        routeId: bindForm.routeId,
+        technologyRoutingId: bindForm.routeId,
       });
       proxy.$modal.msgSuccess("缁戝畾鎴愬姛");
       bindRouteDialogVisible.value = false;
@@ -320,6 +610,35 @@
     } finally {
       bindRouteSaving.value = false;
     }
+  };
+
+  const openMaterialDialog = row => {
+    currentMaterialOrder.value = row;
+    materialDialogVisible.value = true;
+  };
+
+  const openMaterialDetailDialog = async row => {
+    currentMaterialDetailOrder.value = row;
+    materialDetailDialogVisible.value = true;
+  };
+
+  const openMaterialSupplementDialog = row => {
+    currentMaterialSupplementOrder.value = row;
+    materialSupplementDialogVisible.value = true;
+  };
+
+  const handleReset = () => {
+    searchForm.value = {
+      ...searchForm.value,
+      npsNo: "",
+      customerName: "",
+      salesContractNo: "",
+      projectName: "",
+      productName: "",
+      model: "",
+      status: "",
+    };
+    handleQuery();
   };
 
   // 鏌ヨ鍒楄〃
@@ -349,10 +668,35 @@
     const params = { ...searchForm.value, ...page };
     params.entryDate = undefined;
     productOrderListPage(params)
-      .then(res => {
-        tableLoading.value = false;
-        tableData.value = res.data.records;
+      .then(async res => {
+        const records = res.data.records || [];
+        // 涓烘瘡涓鍗曟煡璇㈠搴旂殑宸ュ簭杩涘害鏁版嵁
+        const processPromises = records.map(async item => {
+          if (item.npsNo) {
+            try {
+              const workOrderRes = await productWorkOrderPage({
+                npsNo: item.npsNo,
+                size: 100,
+              });
+              const workOrders = workOrderRes.data.records || [];
+              // 鎸夌収宸ュ簭椤哄簭鎺掑簭锛堝鏋滄湁椤哄簭瀛楁锛屽亣璁句负 orderNum 鎴栨寜杩斿洖椤哄簭锛�
+              // 杞崲涓� processRouteStatus 鏍煎紡
+              const processRouteStatus = workOrders.map(wo => ({
+                name: wo.operationName || "鏈煡宸ュ簭",
+                percentage: wo.completionStatus > 100 ? 100 : wo.completionStatus,
+              }));
+              return { ...item, processRouteStatus };
+            } catch (error) {
+              console.error(`鑾峰彇宸ュ崟 ${item.npsNo} 杩涘害澶辫触:`, error);
+              return { ...item, processRouteStatus: [] };
+            }
+          }
+          return { ...item, processRouteStatus: [] };
+        });
+
+        tableData.value = await Promise.all(processPromises);
         page.total = res.data.total;
+        tableLoading.value = false;
       })
       .catch(() => {
         tableLoading.value = false;
@@ -372,13 +716,16 @@
         path: "/productionManagement/processRouteItem",
         query: {
           id: data.id,
+          bomId: data.orderBomId,
           processRouteCode: data.processRouteCode || "",
-          productName: data.productName || "",
-          model: data.model || "",
-          bomNo: data.bomNo || "",
+          productName: row.productName || "",
+          model: row.model || "",
+          bomNo: row.bomNo || "",
           description: data.description || "",
+          quantity: row.quantity || 0,
           orderId,
           type: "order",
+          editable: !row.endOrder,
         },
       });
     } catch (e) {
@@ -393,39 +740,65 @@
       query: {
         id: row.id,
         bomNo: row.bomNo || "",
-        productName: row.productCategory || "",
-        productModelName: row.specificationModel || "",
+        productName: row.productName || "",
+        productModelName: row.model || "",
         orderId: row.id,
         type: "order",
       },
     });
   };
 
+  // 鏌ョ湅鏉ユ簮鐢熶骇璁″垝鏁版嵁
+  const showSourceData = row => {
+    // 瀛樺偍鐐瑰嚮鏉ユ簮鎸夐挳鏃朵紶閫掔殑row鍙傛暟
+    sourceRowData.value = row;
+    // 璋冪敤API鑾峰彇鏉ユ簮鏁版嵁
+    getProductOrderSource(row.id)
+      .then(res => {
+        if (res.code === 200) {
+          // 鐩存帴瀛樺偍杩斿洖鐨勬墎骞冲寲鏁版嵁
+          sourceTableData.value = res.data || [];
+          sourcePage.total = sourceTableData.value.length;
+          // 鎵撳紑寮圭獥
+          sourceDataDialogVisible.value = true;
+        } else {
+          proxy.$modal.msgError(res.msg || "鑾峰彇鏉ユ簮鏁版嵁澶辫触");
+        }
+      })
+      .catch(err => {
+        proxy.$modal.msgError("鑾峰彇鏉ユ簮鏁版嵁澶辫触");
+        console.error(err);
+      });
+  };
+
   // 琛ㄦ牸閫夋嫨鏁版嵁
-  const handleSelectionChange = (selection) => {
+  const handleSelectionChange = selection => {
     selectedRows.value = selection;
   };
 
   const handleDelete = () => {
     let ids = [];
     if (selectedRows.value.length > 0) {
-      ids = selectedRows.value.map((item) => item.id);
+      ids = selectedRows.value.map(item => item.id);
     } else {
       proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
       return;
     }
-    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
+    ElMessageBox.confirm("鏄惁閫�鍥炶鐢熶骇璁㈠崟锛�", "閫�鍥�", {
       confirmButtonText: "纭",
       cancelButtonText: "鍙栨秷",
       type: "warning",
-    }).then(() => {
-      delProductOrder(ids).then((res) => {
-        proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-        getList();
+    })
+      .then(() => {
+        console.log(ids, "ids");
+        delProductOrder(ids).then(res => {
+          proxy.$modal.msgSuccess("閫�鍥炴垚鍔�");
+          getList();
+        });
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
       });
-    }).catch(() => {
-      proxy.$modal.msg("宸插彇娑�");
-    });
   };
 
   // 瀵煎嚭
@@ -436,11 +809,35 @@
       type: "warning",
     })
       .then(() => {
-        proxy.download("/productOrder/export", {...searchForm.value}, "鐢熶骇璁㈠崟.xlsx");
+        proxy.download(
+          "/productOrder/export",
+          { ...searchForm.value },
+          "鐢熶骇璁㈠崟.xlsx"
+        );
       })
       .catch(() => {
         proxy.$modal.msg("宸插彇娑�");
       });
+  };
+
+  // 缁撴潫璁㈠崟
+  const handleEndOrder = row => {
+    ElMessageBox.confirm(`鏄惁纭缁撴潫璁㈠崟锛�${row.npsNo}锛焋, "鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        const params = {
+          id: row.id,
+          endOrder: true,
+        };
+        updateProductOrder(params).then(() => {
+          proxy.$modal.msgSuccess("缁撴潫璁㈠崟鎴愬姛");
+          getList();
+        });
+      })
+      .catch(() => {});
   };
 
   const handleConfirmRoute = () => {};
@@ -451,23 +848,177 @@
 </script>
 
 <style scoped lang="scss">
-.search_form{
-  align-items: start;
-}
+  .search_form {
+    align-items: start;
+  }
 
-::v-deep .yellow {
-  background-color: #FAF0DE;
-}
+  .action-buttons {
+    display: flex;
+    flex-wrap: nowrap;
+    gap: 8px;
+  }
 
-::v-deep .pink {
-  background-color: #FAE1DE;
-}
+  :deep(.yellow) {
+    background-color: #faf0de;
+  }
 
-::v-deep .red {
-  background-color: #f80202;
-}
+  :deep(.pink) {
+    background-color: #fae1de;
+  }
 
-::v-deep .purple{
-  background-color: #F4DEFA;
-}
+  :deep(.red) {
+    background-color: #f80202;
+  }
+
+  :deep(.purple) {
+    background-color: #f4defa;
+  }
+  .table_list {
+    margin-top: unset;
+  }
+
+  .process-progress-container {
+    display: inline-flex;
+    align-items: center;
+    padding: 10px 0;
+    white-space: nowrap;
+
+    .process-step {
+      display: flex;
+      align-items: center;
+      position: relative;
+
+      .step-content {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        z-index: 1;
+
+        .step-circle {
+          width: 36px;
+          height: 36px;
+          border-radius: 50%;
+          border: 2px solid #409eff;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          background-color: #fff;
+          margin-bottom: 4px;
+
+          .step-percentage {
+            font-size: 11px;
+            font-weight: bold;
+          }
+
+          &.is-completed {
+            border-color: #67c23a;
+            .step-percentage {
+              color: #67c23a;
+            }
+          }
+        }
+
+        .step-name {
+          font-size: 12px;
+          color: #606266;
+          white-space: nowrap;
+        }
+      }
+
+      .step-line {
+        width: 30px;
+        height: 1px;
+        background-color: #dcdfe6;
+        margin: 0 -2px;
+        margin-top: -20px; // 鍚戜笂鍋忕Щ浠ュ榻愬渾蹇�
+      }
+    }
+  }
+</style>
+<style lang="scss">
+  .status-cell {
+    font-weight: 600;
+    color: #409eff;
+    font-family: "Courier New", monospace;
+    text-shadow: 0 1px 2px rgba(64, 158, 255, 0.2);
+  }
+
+  .source-table-container {
+    margin-top: 20px;
+  }
+
+  .source-data-cards-container {
+    display: flex;
+    flex-direction: column;
+    gap: 16px;
+    max-height: 500px;
+    overflow-y: auto;
+    padding: 10px;
+    background-color: #f5f7fa;
+    border-radius: 4px;
+    padding-bottom: 20px;
+
+    .source-data-card {
+      background: #fff;
+      border-radius: 8px;
+      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+      overflow: hidden;
+
+      .card-body {
+        padding: 20px;
+
+        .info-grid {
+          display: grid;
+          grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+          gap: 16px;
+
+          .info-item {
+            display: flex;
+            flex-direction: column;
+
+            .info-label {
+              font-size: 12px;
+              color: #909399;
+              margin-bottom: 4px;
+              font-weight: 500;
+            }
+
+            .info-value {
+              font-size: 14px;
+              color: #303133;
+              font-weight: 500;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .applyno-summary1 {
+    padding: 16px 20px;
+    background: #f5f7fa;
+    border-bottom: 1px solid #e4e7ed;
+    display: flex;
+    flex-wrap: wrap;
+    gap: 16px;
+
+    .summary-item {
+      display: flex;
+      align-items: center;
+      margin-right: 20px;
+
+      .summary-label {
+        font-size: 13px;
+        color: #909399;
+        margin-right: 8px;
+        font-weight: 500;
+      }
+
+      .summary-value {
+        font-size: 14px;
+        color: #303133;
+        font-weight: 500;
+      }
+    }
+  }
 </style>
diff --git a/src/views/productionManagement/productionProcess/Edit.vue b/src/views/productionManagement/productionProcess/Edit.vue
index fb0ee74..28077b6 100644
--- a/src/views/productionManagement/productionProcess/Edit.vue
+++ b/src/views/productionManagement/productionProcess/Edit.vue
@@ -46,6 +46,12 @@
         <el-form-item label="鏄惁璐ㄦ" prop="isQuality">
           <el-switch v-model="formState.isQuality" :active-value="true" inactive-value="false"/>
         </el-form-item>
+        <el-form-item label="鏄惁鍏ュ簱" prop="inbound">
+          <el-switch v-model="formState.inbound" :active-value="true" inactive-value="false"/>
+        </el-form-item>
+        <el-form-item label="鏄惁鎶ュ伐" prop="reportWork">
+          <el-switch v-model="formState.reportWork" :active-value="true" inactive-value="false"/>
+        </el-form-item>
         <el-form-item label="澶囨敞" prop="remark">
           <el-input v-model="formState.remark" type="textarea" />
         </el-form-item>
@@ -87,6 +93,8 @@
   remark: props.record.remark,
   salaryQuota: props.record.salaryQuota,
   isQuality: props.record.isQuality,
+  inbound: props.record.inbound,
+  reportWork: props.record.reportWork,
 });
 
 const isShow = computed({
@@ -109,6 +117,8 @@
       remark: newRecord.remark || '',
       salaryQuota: newRecord.salaryQuota || '',
       isQuality: props.record.isQuality,
+      inbound: newRecord.inbound,
+      reportWork: newRecord.reportWork,
     };
   }
 }, { immediate: true, deep: true });
@@ -124,6 +134,8 @@
       remark: props.record.remark || '',
       salaryQuota: props.record.salaryQuota || '',
       isQuality: props.record.isQuality,
+      inbound: props.record.inbound,
+      reportWork: props.record.reportWork,
     };
   }
 });
diff --git a/src/views/productionManagement/productionProcess/New.vue b/src/views/productionManagement/productionProcess/New.vue
index a5f00aa..0b3fd47 100644
--- a/src/views/productionManagement/productionProcess/New.vue
+++ b/src/views/productionManagement/productionProcess/New.vue
@@ -48,6 +48,12 @@
         <el-form-item label="鏄惁璐ㄦ" prop="isQuality">
           <el-switch v-model="formState.isQuality" :active-value="true" inactive-value="false"/>
         </el-form-item>
+        <el-form-item label="鏄惁鍏ュ簱" prop="inbound">
+          <el-switch v-model="formState.inbound" :active-value="true" inactive-value="false"/>
+        </el-form-item>
+        <el-form-item label="鏄惁鎶ュ伐" prop="reportWork">
+          <el-switch v-model="formState.reportWork" :active-value="true" inactive-value="false"/>
+        </el-form-item>
         <el-form-item label="澶囨敞" prop="remark">
           <el-input v-model="formState.remark" type="textarea" />
         </el-form-item>
@@ -82,6 +88,8 @@
   remark: '',
   salaryQuota:  '',
   isQuality: false,
+  inbound: false,
+  reportWork: false,
 });
 
 const isShow = computed({
diff --git a/src/views/productionManagement/productionProcess/index.vue b/src/views/productionManagement/productionProcess/index.vue
index ffe13fc..216e73b 100644
--- a/src/views/productionManagement/productionProcess/index.vue
+++ b/src/views/productionManagement/productionProcess/index.vue
@@ -1,319 +1,1085 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
-      <el-form :model="searchForm"
-               :inline="true">
-        <el-form-item label="宸ュ簭鍚嶇О:">
-          <el-input v-model="searchForm.name"
-                    placeholder="璇疯緭鍏�"
-                    clearable
-                    prefix-icon="Search"
-                    style="width: 200px;"
-                    @change="handleQuery" />
-        </el-form-item>
-        <el-form-item label="宸ュ簭缂栧彿:">
-          <el-input v-model="searchForm.no"
-                    placeholder="璇疯緭鍏�"
-                    clearable
-                    prefix-icon="Search"
-                    style="width: 200px;"
-                    @change="handleQuery" />
-        </el-form-item>
-        <el-form-item>
+    <div class="process-config-container">
+      <!-- 宸︿晶宸ュ簭鍒楄〃 -->
+      <div class="process-list-section">
+        <div class="section-header">
+          <h3 class="section-title">宸ュ簭鍒楄〃</h3>
           <el-button type="primary"
-                     @click="handleQuery">鎼滅储</el-button>
+                     size="small"
+                     @click="handleAddProcess">
+            <el-icon>
+              <Plus />
+            </el-icon>鏂板宸ュ簭
+          </el-button>
+        </div>
+        <div class="process-card-list"
+             v-loading="processLoading">
+          <div v-for="process in processValueList"
+               :key="process.id"
+               class="process-card"
+               :class="{ active: selectedProcess?.id === process.id }"
+               @click="selectProcess(process)">
+            <div class="card-header">
+              <div class="process-name">{{ process.name }} <span class="process-code">{{ process.no }}</span></div>
+              <div class="card-actions">
+                <el-button link
+                           type="primary"
+                           @click.stop="handleEditProcess(process)">
+                  <el-icon>
+                    <Edit />
+                  </el-icon>
+                  缂栬緫
+                </el-button>
+                <el-button link
+                           type="danger"
+                           @click.stop="handleDeleteProcess(process)">
+                  <el-icon>
+                    <Delete />
+                  </el-icon>
+                  鍒犻櫎
+                </el-button>
+              </div>
+            </div>
+            <div class="card-body">
+              <!-- <div class="process-name">{{ process.name }}</div> -->
+              <div class="process-desc">{{ process.remark || '鏆傛棤鎻忚堪' }}</div>
+              <div class="process-device">鍏宠仈璁惧: {{ deviceOptions.find(item => item.id === Number(process.deviceLedgerId))?.deviceName|| '鏈叧鑱�' }}</div>
+            </div>
+            <div class="card-footer">
+              <div class="status-tag">
+                <el-tag size="small"
+                        :type="process.isQuality ? 'warning' : 'info'">
+                  {{ process.isQuality ? '璐ㄦ' : '闈炶川妫�' }}
+                </el-tag>
+                <el-tag size="small"
+                        style="margin-left: 8px"
+                        :type="process.isProduction ? 'warning' : 'info'">
+                  {{ process.isProduction ? '鐢熶骇' : '涓嶇敓浜�' }}
+                </el-tag>
+                <el-tag v-if="process.type !== null && process.type !== undefined"
+                        size="small"
+                        :type="process.type == 1 ? 'primary' : 'success'"
+                        style="margin-left: 8px">
+                  {{ process.type == 0 ? '璁℃椂' : '璁′欢' }}
+                </el-tag>
+              </div>
+              <span class="param-count">宸ヨ祫瀹氶: 楼{{ process.salaryQuota || 0 }}</span>
+            </div>
+          </div>
+        </div>
+      </div>
+      <!-- 鍙充晶鍙傛暟鍒楄〃 -->
+      <div class="param-list-section">
+        <div class="section-header">
+          <h3 class="section-title">
+            {{ selectedProcess ? selectedProcess.name + ' - 鍙傛暟閰嶇疆' : '璇烽�夋嫨宸ュ簭' }}
+          </h3>
+          <el-button type="primary"
+                     size="small"
+                     :disabled="!selectedProcess"
+                     @click="openParamDialog">
+            <el-icon>
+              <Plus />
+            </el-icon>閫夋嫨鍙傛暟
+          </el-button>
+        </div>
+        <div class="param-table-wrapper">
+          <PIMTable v-if="selectedProcess"
+                    rowKey="id"
+                    :column="paramColumn"
+                    :tableData="paramList"
+                    :page="paramPage2"
+                    height="calc(100vh - 280px)"
+                    :isSelection="false"
+                    @pagination="handleParamPagination" />
+          <div v-else
+               class="empty-tip">
+            <el-empty description="璇蜂粠宸︿晶閫夋嫨涓�涓伐搴�" />
+          </div>
+        </div>
+      </div>
+    </div>
+    <!-- 宸ュ簭鏂板/缂栬緫瀵硅瘽妗� -->
+    <el-dialog v-model="processDialogVisible"
+               :title="isProcessEdit ? '缂栬緫宸ュ簭' : '鏂板宸ュ簭'"
+               width="500px">
+      <el-form :model="processForm"
+               :rules="processRules"
+               ref="processFormRef"
+               label-width="100px">
+        <el-form-item label="宸ュ簭缂栫爜"
+                      prop="no">
+          <el-input v-model="processForm.no"
+                    placeholder="璇疯緭鍏ュ伐搴忕紪鐮�" />
+        </el-form-item>
+        <el-form-item label="宸ュ簭鍚嶇О"
+                      prop="name">
+          <el-input v-model="processForm.name"
+                    placeholder="璇疯緭鍏ュ伐搴忓悕绉�" />
+        </el-form-item>
+        <el-form-item label="宸ヨ祫瀹氶"
+                      prop="salaryQuota">
+          <el-input v-model="processForm.salaryQuota"
+                    type="number"
+                    :step="0.001" />
+        </el-form-item>
+        <el-form-item label="鏄惁璐ㄦ"
+                      prop="isQuality">
+          <el-switch v-model="processForm.isQuality" />
+        </el-form-item>
+        <el-form-item label="鏄惁鐢熶骇"
+                      prop="isProduction">
+          <el-switch v-model="processForm.isProduction" />
+        </el-form-item>
+        <el-form-item label="璁¤垂绫诲瀷"
+                      prop="type">
+          <el-radio-group v-model="processForm.type">
+            <el-radio :label="0">璁℃椂</el-radio>
+            <el-radio :label="1">璁′欢</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="鍏宠仈璁惧"
+                      prop="deviceLedgerId">
+          <el-select v-model="processForm.deviceLedgerId"
+                     placeholder="璇烽�夋嫨璁惧"
+                     clearable
+                     filterable
+                     style="width: 100%">
+            <el-option v-for="item in deviceOptions"
+                       :key="item.id"
+                       :label="item.deviceName"
+                       :value="item.id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="宸ュ簭鎻忚堪"
+                      prop="remark">
+          <el-input v-model="processForm.remark"
+                    type="textarea"
+                    :rows="3"
+                    placeholder="璇疯緭鍏ュ伐搴忔弿杩�" />
         </el-form-item>
       </el-form>
-    </div>
-    <div class="table_list">
-      <div style="text-align: right"
-           class="mb10">
-        <el-button type="primary"
-                   @click="showNewModal">鏂板宸ュ簭</el-button>
-        <el-button type="info"
-                   plain
-                   @click="handleImport">瀵煎叆</el-button>
-        <el-button type="danger"
-                   @click="handleDelete"
-                   :disabled="selectedRows.length === 0"
-                   plain>鍒犻櫎宸ュ簭</el-button>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary" @click="handleProcessSubmit">纭畾</el-button>
+          <el-button @click="processDialogVisible = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <!-- 閫夋嫨鍙傛暟瀵硅瘽妗� -->
+    <el-dialog v-model="paramDialogVisible"
+               title="閫夋嫨鍙傛暟"
+               width="1000px">
+      <div class="param-select-container">
+        <!-- 宸︿晶鍙傛暟鍒楄〃 -->
+        <div class="param-list-area">
+          <div class="area-title">鍙�夊弬鏁�</div>
+          <div class="search-box">
+            <el-input v-model="paramSearchKeyword"
+                      placeholder="璇疯緭鍏ュ弬鏁板悕绉版悳绱�"
+                      clearable
+                      size="small"
+                      @input="handleSelectParam">
+              <template #prefix>
+                <el-icon>
+                  <Search />
+                </el-icon>
+              </template>
+            </el-input>
+          </div>
+          <el-table :data="filteredParamList"
+                    height="300"
+                    border
+                    highlight-current-row
+                    @current-change="handleParamSelect">
+            <el-table-column prop="paramName"
+                             label="鍙傛暟鍚嶇О" />
+            <el-table-column prop="paramType"
+                             label="鍙傛暟绫诲瀷">
+              <template #default="scope">
+                <el-tag size="small"
+                        :type="getParamTypeTag(scope.row.paramType)">
+                  {{ getParamTypeText(scope.row.paramType) }}
+                </el-tag>
+              </template>
+            </el-table-column>
+          </el-table>
+          <!-- 鍒嗛〉鎺т欢 -->
+          <div class="pagination-container"
+               style="margin-top: 10px;">
+            <el-pagination v-model:current-page="paramPage.current"
+                           v-model:page-size="paramPage.size"
+                           :page-sizes="[10, 20, 50, 100]"
+                           layout="total, sizes, prev, pager, next, jumper"
+                           :total="paramPage.total"
+                           @size-change="handleParamSizeChange"
+                           @current-change="handleParamCurrentChange"
+                           size="small" />
+          </div>
+        </div>
+        <!-- 鍙充晶鍙傛暟璇︽儏 -->
+        <div class="param-detail-area">
+          <div class="area-title">鍙傛暟璇︽儏</div>
+          <el-form v-if="selectedParam"
+                   :model="selectedParam"
+                   label-width="100px"
+                   class="param-detail-form">
+            <el-form-item label="鍙傛暟鍚嶇О">
+              <span class="detail-text">{{ selectedParam.paramName }}</span>
+            </el-form-item>
+            <el-form-item label="鍙傛暟绫诲瀷">
+              <el-tag size="small"
+                      :type="getParamTypeTag(selectedParam.paramType)">
+                {{ getParamTypeText(selectedParam.paramType) }}
+              </el-tag>
+            </el-form-item>
+            <el-form-item label="鍙傛暟鏍煎紡">
+              <span class="detail-text">{{ selectedParam.paramFormat || '-' }}</span>
+            </el-form-item>
+            <el-form-item label="鍗曚綅">
+              <span class="detail-text">{{ selectedParam.unit || '-' }}</span>
+            </el-form-item>
+            <el-form-item label="鏍囧噯鍊�">
+              <el-input v-model="selectedParam.standardValue"
+                        placeholder="璇疯緭鍏ラ粯璁ゅ��" />
+            </el-form-item>
+          </el-form>
+          <el-empty v-else
+                    description="璇蜂粠宸︿晶閫夋嫨鍙傛暟" />
+        </div>
       </div>
-      <PIMTable rowKey="id"
-                :column="tableColumn"
-                :tableData="tableData"
-                :page="page"
-                :isSelection="true"
-                @selection-change="handleSelectionChange"
-                :tableLoading="tableLoading"
-                @pagination="pagination"
-                :total="page.total"></PIMTable>
-    </div>
-    <new-process v-if="isShowNewModal"
-                 v-model:visible="isShowNewModal"
-                 @completed="getList" />
-    <edit-process v-if="isShowEditModal"
-                  v-model:visible="isShowEditModal"
-                  :record="record"
-                  @completed="getList" />
-    <ImportDialog ref="importDialogRef"
-                  v-model="importDialogVisible"
-                  title="瀵煎叆宸ュ簭"
-                  :action="importAction"
-                  :headers="importHeaders"
-                  :auto-upload="false"
-                  :on-success="handleImportSuccess"
-                  :on-error="handleImportError"
-                  @confirm="handleImportConfirm"
-                  @download-template="handleDownloadTemplate"
-                  @close="handleImportClose" />
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary" :disabled="!selectedParam" @click="handleParamSubmit">纭畾</el-button>
+          <el-button @click="paramDialogVisible = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <!-- 缂栬緫鍙傛暟瀵硅瘽妗� -->
+    <el-dialog v-model="editParamDialogVisible"
+               title="缂栬緫鍙傛暟"
+               width="600px">
+      <el-form :model="editParamForm"
+               :rules="editParamRules"
+               ref="editParamFormRef"
+               label-width="120px">
+        <el-form-item label="鍙傛暟鍚嶇О">
+          <span class="detail-text">{{ editParamForm.paramName }}</span>
+        </el-form-item>
+        <el-form-item label="鏍囧噯鍊�"
+                      prop="standardValue">
+          <el-input v-model="editParamForm.standardValue"
+                    placeholder="璇疯緭鍏ユ爣鍑嗗��" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="editParamDialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary"
+                     @click="handleEditParamSubmit">纭畾</el-button>
+        </span>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-  import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
-  import NewProcess from "@/views/productionManagement/productionProcess/New.vue";
-  import EditProcess from "@/views/productionManagement/productionProcess/Edit.vue";
-  import ImportDialog from "@/components/Dialog/ImportDialog.vue";
+  import { ref, reactive, computed, onMounted } from "vue";
+  import { ElMessage, ElMessageBox } from "element-plus";
+  import { Plus, Edit, Delete, Search } from "@element-plus/icons-vue";
+  import PIMTable from "@/components/PIMTable/PIMTable.vue";
+  import { listType } from "@/api/system/dict/type";
   import {
-    listPage,
+    add,
+    update,
     del,
-    importData,
-    downloadTemplate,
+    list as getProcessListApi,
+    processList,
+    getProcessParamList,
+    addProcessParam,
+    editProcessParam,
+    deleteProcessParam,
   } from "@/api/productionManagement/productionProcess.js";
-  import { getToken } from "@/utils/auth";
+  import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
+  import { getBaseParamList } from "@/api/basicData/parameterMaintenance.js";
 
-  const data = reactive({
-    searchForm: {
-      name: "",
-      no: "",
-    },
+  // 宸ュ簭鍒楄〃鏁版嵁
+  const processValueList = ref([]);
+  const selectedProcess = ref(null);
+  const processLoading = ref(false);
+  const deviceOptions = ref([]);
+
+  // 宸ュ簭宸查�夊弬鏁拌〃鏍煎垎椤碉紙鎺ュ彛涓�娆¤繑鍥炲叏閲忥級
+  const paramPage2 = ref({
+    current: 1,
+    size: 10,
+    total: 0,
   });
-  const { searchForm } = toRefs(data);
-  const tableColumn = ref([
+  const paramListRaw = ref([]);
+  const paramList = computed(() => {
+    const all = paramListRaw.value;
+    const { current, size } = paramPage2.value;
+    const start = (current - 1) * size;
+    return all.slice(start, start + size);
+  });
+  const paramLoading = ref(false);
+
+  // 鏁版嵁瀛楀吀
+  const dictTypes = ref([]);
+
+  // 宸ュ簭瀵硅瘽妗�
+  const processDialogVisible = ref(false);
+  const isProcessEdit = ref(false);
+  const processFormRef = ref(null);
+  const processForm = reactive({
+    id: null,
+    no: "",
+    name: "",
+    salaryQuota: null,
+    isQuality: false,
+    isProduction: false,
+    remark: "",
+    deviceLedgerId: null,
+    type: 0,
+  });
+  const processRules = {
+    no: [{ required: true, message: "璇疯緭鍏ュ伐搴忕紪鐮�", trigger: "blur" }],
+    name: [{ required: true, message: "璇疯緭鍏ュ伐搴忓悕绉�", trigger: "blur" }],
+    salaryQuota: [
+      {
+        required: false,
+        message: "璇疯緭鍏ュ伐璧勫畾棰�",
+        trigger: "blur",
+        validator: (rule, value, callback) => {
+          if (isNaN(value) || value < 0) {
+            callback(new Error("宸ヨ祫瀹氶蹇呴』鏄潪璐熸暟瀛�"));
+          } else {
+            callback();
+          }
+        },
+      },
+    ],
+    deviceLedgerId: [
+      { required: false, message: "璇烽�夋嫨璁惧", trigger: "change" },
+    ],
+    type: [{ required: false, message: "璇烽�夋嫨璁¤垂绫诲瀷", trigger: "change" }],
+  };
+
+  // 鍙傛暟瀵硅瘽妗�
+  const paramDialogVisible = ref(false);
+  const availableParamList = ref([]);
+  const filteredParamList = ref([]);
+  const selectedParam = ref(null);
+  const paramSearchKeyword = ref("");
+
+  // 鍙�夊弬鏁板垎椤�
+  const paramPage = reactive({
+    current: 1,
+    size: 10,
+    total: 0,
+  });
+
+  // 缂栬緫鍙傛暟瀵硅瘽妗�
+  const editParamDialogVisible = ref(false);
+  const editParamFormRef = ref(null);
+  const editParamForm = reactive({
+    id: null,
+    technologyOperationId: null,
+    technologyParamId: null,
+    paramName: "",
+    standardValue: null,
+  });
+  const editParamRules = {
+    standardValue: [
+      {
+        required: true,
+        message: "璇疯緭鍏ユ爣鍑嗗��",
+        trigger: "blur",
+        validator: (rule, value, callback) => {
+          if (value === null || value === undefined || value === "") {
+            callback(new Error("璇疯緭鍏ユ爣鍑嗗��"));
+          } else {
+            callback();
+          }
+        },
+      },
+    ],
+  };
+
+  // 鍙傛暟琛ㄦ牸鍒楅厤缃�
+  const paramColumn = ref([
     {
-      label: "宸ュ簭缂栧彿",
-      prop: "no",
+      label: "鍙傛暟鍚嶇О",
+      prop: "paramName",
     },
     {
-      label: "宸ュ簭鍚嶇О",
-      prop: "name",
-    },
-    {
-      label: "宸ュ簭绫诲瀷",
-      prop: "typeText",
-    },
-    {
-      label: "宸ヨ祫瀹氶",
-      prop: "salaryQuota",
-    },
-    {
-      label: "鏄惁璐ㄦ",
-      prop: "isQuality",
-      formatData: (params) => {
-        return params ? "鏄�" : "鍚�";
+      label: "鍙傛暟绫诲瀷",
+      prop: "paramType",
+      dataType: "tag",
+      formatType: params => {
+        const typeMap = {
+          1: "primary",
+          2: "info",
+          3: "warning",
+          4: "success",
+        };
+        return typeMap[params] || "default";
+      },
+      formatData: val => {
+        const labelMap = {
+          1: "鏁板�兼牸寮�",
+          2: "鏂囨湰鏍煎紡",
+          3: "涓嬫媺閫夐」",
+          4: "鏃堕棿鏍煎紡",
+        };
+        return labelMap[val] || val;
       },
     },
     {
-      label: "澶囨敞",
-      prop: "remark",
+      label: "鍙栧�兼牸寮�",
+      prop: "paramFormat",
     },
     {
-      label: "鏇存柊鏃堕棿",
-      prop: "updateTime",
+      label: "鏍囧噯鍊�",
+      prop: "standardValue",
     },
     {
-      dataType: "action",
+      label: "鍗曚綅",
+      prop: "unit",
+    },
+    {
       label: "鎿嶄綔",
-      align: "center",
-      fixed: "right",
-      width: 280,
+      dataType: "action",
+      width: "150",
       operation: [
         {
           name: "缂栬緫",
-          type: "text",
-          clickFun: row => {
-            showEditModal(row);
-          },
+          clickFun: row => handleEditParam(row),
+        },
+        {
+          name: "鍒犻櫎",
+          clickFun: row => handleDeleteParam(row),
         },
       ],
     },
   ]);
-  const tableData = ref([]);
-  const selectedRows = ref([]);
-  const tableLoading = ref(false);
-  const isShowNewModal = ref(false);
-  const isShowEditModal = ref(false);
-  const record = ref({});
-  const importDialogVisible = ref(false);
-  const importDialogRef = ref(null);
-  const page = reactive({
-    current: 1,
-    size: 100,
-    total: 0,
-  });
-  const { proxy } = getCurrentInstance();
 
-  // 瀵煎叆鐩稿叧閰嶇疆
-  const importAction =
-    import.meta.env.VITE_APP_BASE_API + "/productProcess/importData";
-  const importHeaders = { Authorization: "Bearer " + getToken() };
-
-  // 鏌ヨ鍒楄〃
-  /** 鎼滅储鎸夐挳鎿嶄綔 */
-  const handleQuery = () => {
-    page.current = 1;
-    getList();
-  };
-
-  const pagination = obj => {
-    page.current = obj.page;
-    page.size = obj.limit;
-    getList();
-  };
-  const getList = () => {
-    tableLoading.value = true;
-    const params = { ...searchForm.value, ...page };
-    params.entryDate = undefined;
-    listPage(params)
+  // 鑾峰彇宸ュ簭鍒楄〃
+  const getProcessList = () => {
+    processLoading.value = true;
+    getProcessListApi({ size: -1, current: -1 })
       .then(res => {
-        tableLoading.value = false;
-        tableData.value = res.data.records.map(item => ({
-          ...item,
-          typeText: item.type !== undefined && item.type !== null ? (item.type === 0 ? "璁℃椂" : "璁′欢") : "",
-        }));
-        page.total = res.data.total;
+        processValueList.value = res.data.records || [];
+        console.log(
+          processValueList.value,
+          "reprocessValueList.value==========s"
+        );
       })
-      .catch(err => {
-        tableLoading.value = false;
+      .catch(() => {
+        ElMessage.error("鑾峰彇宸ュ簭鍒楄〃澶辫触");
+      })
+      .finally(() => {
+        processLoading.value = false;
       });
   };
-  // 琛ㄦ牸閫夋嫨鏁版嵁
-  const handleSelectionChange = selection => {
-    selectedRows.value = selection;
-  };
 
-  // 鎵撳紑鏂板寮规
-  const showNewModal = () => {
-    isShowNewModal.value = true;
-  };
-
-  const showEditModal = row => {
-    isShowEditModal.value = true;
-    record.value = row;
-  };
-
-  // 鍒犻櫎
-  function handleDelete() {
-    const no = selectedRows.value.map(item => item.no);
-    const ids = selectedRows.value.map(item => item.id);
-    if (no.length > 2) {
-      proxy.$modal
-        .confirm(
-          '鏄惁纭鍒犻櫎宸ュ簭缂栧彿涓�"' +
-            no[0] +
-            "銆�" +
-            no[1] +
-            '"绛�' +
-            no.length +
-            "鏉℃暟鎹」锛�"
-        )
-        .then(function () {
-          return del(ids);
-        })
-        .then(() => {
-          getList();
-          proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-        })
-        .catch(() => {});
-    } else {
-      proxy.$modal
-        .confirm('鏄惁纭鍒犻櫎宸ュ簭缂栧彿涓�"' + no + '"鐨勬暟鎹」锛�')
-        .then(function () {
-          return del(ids);
-        })
-        .then(() => {
-          getList();
-          proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-        })
-        .catch(() => {});
-    }
-  }
-
-  // 瀵煎叆
-  const handleImport = () => {
-    importDialogVisible.value = true;
-  };
-
-  // 纭瀵煎叆
-  const handleImportConfirm = () => {
-    if (importDialogRef.value) {
-      importDialogRef.value.submit();
-    }
-  };
-
-  // 瀵煎叆鎴愬姛
-  const handleImportSuccess = response => {
-    if (response.code === 200) {
-      proxy.$modal.msgSuccess("瀵煎叆鎴愬姛");
-      importDialogVisible.value = false;
-      if (importDialogRef.value) {
-        importDialogRef.value.clearFiles();
-      }
-      getList();
-    } else {
-      proxy.$modal.msgError(response.msg || "瀵煎叆澶辫触");
-    }
-  };
-
-  // 瀵煎叆澶辫触
-  const handleImportError = error => {
-    proxy.$modal.msgError("瀵煎叆澶辫触锛�" + (error.message || "鏈煡閿欒"));
-  };
-
-  // 鍏抽棴瀵煎叆寮圭獥
-  const handleImportClose = () => {
-    if (importDialogRef.value) {
-      importDialogRef.value.clearFiles();
-    }
-  };
-
-  // 涓嬭浇妯℃澘
-  const handleDownloadTemplate = async () => {
+  const loadDeviceName = async () => {
     try {
-      const res = await downloadTemplate();
-      const blob = new Blob([res], {
-        type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
-      });
-      const url = window.URL.createObjectURL(blob);
-      const link = document.createElement("a");
-      link.href = url;
-      link.download = "宸ュ簭瀵煎叆妯℃澘.xlsx";
-      link.click();
-      window.URL.revokeObjectURL(url);
-      proxy.$modal.msgSuccess("妯℃澘涓嬭浇鎴愬姛");
+      const { data } = await getDeviceLedger();
+      deviceOptions.value = data || [];
     } catch (error) {
-      proxy.$modal.msgError("妯℃澘涓嬭浇澶辫触");
+      console.error("鍔犺浇璁惧鍒楄〃澶辫触", error);
     }
   };
 
-  // 瀵煎嚭
-  // const handleOut = () => {
-  // 	ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
-  // 		confirmButtonText: "纭",
-  // 		cancelButtonText: "鍙栨秷",
-  // 		type: "warning",
-  // 	})
-  // 		.then(() => {
-  // 			proxy.download("/salesLedger/scheduling/exportTwo", {}, "宸ュ簭鎺掍骇.xlsx");
-  // 		})
-  // 		.catch(() => {
-  // 			proxy.$modal.msg("宸插彇娑�");
-  // 		});
-  // };
+  // 鑾峰彇鍙傛暟鍒楄〃
+  const getParamList = processId => {
+    paramLoading.value = true;
+    getProcessParamList({ technologyOperationId: processId })
+      .then(res => {
+        const list = res.data || [];
+        paramListRaw.value = Array.isArray(list) ? list : [];
+        paramPage2.value.total = paramListRaw.value.length;
+        const maxPage = Math.max(
+          1,
+          Math.ceil(paramPage2.value.total / paramPage2.value.size) || 1
+        );
+        if (paramPage2.value.current > maxPage) {
+          paramPage2.value.current = maxPage;
+        }
+      })
+      .catch(() => {
+        ElMessage.error("鑾峰彇鍙傛暟鍒楄〃澶辫触");
+      })
+      .finally(() => {
+        paramLoading.value = false;
+      });
+  };
+
+  // 閫夋嫨宸ュ簭
+  const selectProcess = process => {
+    selectedProcess.value = process;
+    paramPage2.value.current = 1;
+    getParamList(process.id);
+  };
+
+  // 宸ュ簭鎿嶄綔
+  const handleAddProcess = () => {
+    isProcessEdit.value = false;
+    processForm.id = null;
+    processForm.no = "";
+    processForm.name = "";
+    processForm.salaryQuota = null;
+    processForm.isQuality = false;
+    processForm.isProduction = false;
+    processForm.remark = "";
+    processForm.deviceLedgerId = null;
+    processForm.type = 0;
+    processDialogVisible.value = true;
+  };
+
+  const handleEditProcess = async process => {
+    isProcessEdit.value = true;
+    processForm.id = process.id;
+    processForm.no = process.no;
+    processForm.name = process.name;
+    processForm.salaryQuota = process.salaryQuota;
+    processForm.isQuality = !!process.isQuality;
+    processForm.isProduction = !!process.isProduction;
+    processForm.remark = process.remark || "";
+    processForm.deviceLedgerId = Number(process.deviceLedgerId);
+    processForm.type = process.type;
+    processDialogVisible.value = true;
+  };
+
+  const handleDeleteProcess = process => {
+    ElMessageBox.confirm("纭畾瑕佸垹闄よ宸ュ簭鍚楋紵", "鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    }).then(() => {
+      del([process.id])
+        .then(() => {
+          ElMessage.success("鍒犻櫎鎴愬姛");
+          getProcessList();
+          if (selectedProcess.value?.id === process.id) {
+            selectedProcess.value = null;
+            paramListRaw.value = [];
+            paramPage2.value.total = 0;
+          }
+        })
+        .catch(() => {
+          ElMessage.error("鍒犻櫎澶辫触");
+        });
+    });
+  };
+
+  const handleProcessSubmit = () => {
+    processFormRef.value.validate(valid => {
+      if (valid) {
+        if (processForm.id) {
+          update(processForm)
+            .then(() => {
+              ElMessage.success("缂栬緫鎴愬姛");
+              processDialogVisible.value = false;
+              getProcessList();
+            })
+            .catch(() => {
+              ElMessage.error("缂栬緫澶辫触");
+            });
+        } else {
+          add(processForm)
+            .then(() => {
+              ElMessage.success("鏂板鎴愬姛");
+              processDialogVisible.value = false;
+              getProcessList();
+            })
+            .catch(() => {
+              ElMessage.error("鏂板澶辫触");
+            });
+        }
+      }
+    });
+  };
+  const openParamDialog = () => {
+    paramSearchKeyword.value = "";
+    if (!selectedProcess.value) {
+      ElMessage.warning("璇峰厛閫夋嫨涓�涓伐搴�");
+      return;
+    }
+    // 鑾峰彇鍙�夊弬鏁板垪琛�
+    getBaseParamList({
+      paramName: paramSearchKeyword.value,
+      current: paramPage.current,
+      size: paramPage.size,
+    }).then(res => {
+      if (res.code === 200) {
+        filteredParamList.value = res.data?.records || [];
+        paramPage.total = res.data?.total || 0;
+      } else {
+        ElMessage.error(res.msg || "鏌ヨ澶辫触");
+      }
+    });
+    console.log(filteredParamList.value, "鍙�夊弬鏁板垪琛�");
+    selectedParam.value = null;
+    paramDialogVisible.value = true;
+  };
+
+  // 鍙傛暟鎿嶄綔
+  const handleSelectParam = () => {
+    if (!selectedProcess.value) {
+      ElMessage.warning("璇峰厛閫夋嫨涓�涓伐搴�");
+      return;
+    }
+    // 鑾峰彇鍙�夊弬鏁板垪琛�
+    getBaseParamList({
+      paramName: paramSearchKeyword.value,
+      current: paramPage.current,
+      size: paramPage.size,
+    }).then(res => {
+      if (res.code === 200) {
+        filteredParamList.value = res.data?.records || [];
+        paramPage.total = res.data?.total || 0;
+      } else {
+        ElMessage.error(res.msg || "鏌ヨ澶辫触");
+      }
+    });
+    console.log(filteredParamList.value, "鍙�夊弬鏁板垪琛�");
+    selectedParam.value = null;
+    paramDialogVisible.value = true;
+  };
+
+  const handleParamSelect = row => {
+    selectedParam.value = row;
+  };
+
+  const handleParamSearch = () => {
+    // 閲嶇疆鍒嗛〉
+    paramPage.current = 1;
+    // 閲嶆柊鍔犺浇鏁版嵁
+    handleSelectParam();
+  };
+
+  // 澶勭悊鍒嗛〉澶у皬鍙樺寲
+  const handleParamSizeChange = size => {
+    paramPage.size = size;
+    handleSelectParam();
+  };
+
+  // 澶勭悊褰撳墠椤电爜鍙樺寲
+  const handleParamCurrentChange = current => {
+    paramPage.current = current;
+    handleSelectParam();
+  };
+  const getParamTypeText = type => {
+    const typeMap = {
+      1: "鏁板�兼牸寮�",
+      2: "鏂囨湰鏍煎紡",
+      3: "涓嬫媺閫夐」",
+      4: "鏃堕棿鏍煎紡",
+    };
+    return typeMap[type] || "鏈煡鍙傛暟绫诲瀷";
+  };
+  const getParamTypeTag = type => {
+    const typeMap = {
+      1: "primary",
+      2: "info",
+      3: "warning",
+      4: "success",
+    };
+    return typeMap[type] || "default";
+  };
+
+  const handleDeleteParam = row => {
+    ElMessageBox.confirm("纭畾瑕佸垹闄よ鍙傛暟鍚楋紵", "鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    }).then(() => {
+      deleteProcessParam(row.id)
+        .then(() => {
+          ElMessage.success("鍒犻櫎鎴愬姛");
+          getParamList(selectedProcess.value.id);
+        })
+        .catch(() => {
+          ElMessage.error("鍒犻櫎澶辫触");
+        });
+    });
+  };
+
+  const handleEditParam = row => {
+    editParamForm.id = row.id;
+    editParamForm.technologyOperationId = row.technologyOperationId;
+    editParamForm.technologyParamId = row.technologyParamId;
+    editParamForm.paramName = row.paramName;
+    editParamForm.standardValue = row.standardValue;
+    editParamDialogVisible.value = true;
+  };
+
+  const handleEditParamSubmit = () => {
+    editParamFormRef.value.validate(valid => {
+      if (valid) {
+        editProcessParam(editParamForm)
+          .then(() => {
+            ElMessage.success("缂栬緫鎴愬姛");
+            editParamDialogVisible.value = false;
+            getParamList(selectedProcess.value.id);
+          })
+          .catch(() => {
+            ElMessage.error("缂栬緫澶辫触");
+          });
+      }
+    });
+  };
+
+  const handleParamSubmit = () => {
+    if (!selectedParam.value) {
+      ElMessage.warning("璇峰厛閫夋嫨涓�涓弬鏁�");
+      return;
+    }
+    addProcessParam({
+      technologyOperationId: selectedProcess.value.id,
+      technologyParamId: selectedParam.value.id,
+      standardValue: selectedParam.value.standardValue,
+    })
+      .then(() => {
+        ElMessage.success("娣诲姞鎴愬姛");
+        paramDialogVisible.value = false;
+        getParamList(selectedProcess.value.id);
+      })
+      .catch(() => {
+        ElMessage.error("娣诲姞澶辫触");
+      });
+  };
+
+  const handleParamPagination = obj => {
+    paramPage2.value.current = obj.page;
+    paramPage2.value.size = obj.limit;
+  };
+
+  // 鑾峰彇鏁版嵁瀛楀吀
+  const getDictTypes = () => {
+    listType({ pageNum: 1, pageSize: 1000 }).then(res => {
+      dictTypes.value = res.rows || [];
+    });
+  };
 
   onMounted(() => {
-    getList();
+    loadDeviceName();
+    getProcessList();
+    getDictTypes();
   });
 </script>
 
-<style scoped></style>
+<style scoped lang="scss">
+  .app-container {
+    padding: 20px;
+    background-color: #f0f2f5;
+    min-height: calc(100vh - 84px);
+  }
+
+  .process-config-container {
+    display: flex;
+    gap: 20px;
+    height: calc(100vh - 124px);
+  }
+
+  // 宸︿晶宸ュ簭鍒楄〃
+  .process-list-section {
+    width: 370px;
+    min-width: 370px;
+    flex-shrink: 0;
+    background: #fff;
+    border-radius: 8px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+    display: flex;
+    flex-direction: column;
+  }
+
+  .section-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 16px 20px;
+    border-bottom: 1px solid #ebeef5;
+
+    .section-title {
+      margin: 0;
+      font-size: 16px;
+      font-weight: 600;
+      color: #303133;
+    }
+  }
+
+  .process-card-list {
+    flex: 1;
+    overflow-y: auto;
+    padding: 16px;
+  }
+
+  .process-card {
+    background: #fff;
+    border: 1px solid #ebeef5;
+    border-radius: 8px;
+    padding: 16px;
+    margin-bottom: 12px;
+    cursor: pointer;
+    transition: all 0.3s ease;
+
+    &:hover {
+      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+      transform: translateY(-2px);
+    }
+
+    &.active {
+      border-color: #409eff;
+      background: #f5f7fa;
+      box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);
+    }
+
+    .card-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 8px;
+
+      .process-code {
+        font-size: 12px;
+        // color: #909399;
+        color: #cb9b18;
+        font-family: "Courier New", monospace;
+      }
+
+      .card-actions {
+        display: flex;
+        gap: 4px;
+
+        .el-button {
+          padding: 4px;
+        }
+      }
+    }
+
+    .card-body {
+      margin-bottom: 12px;
+
+      .process-name {
+        font-size: 16px;
+        font-weight: 600;
+        color: #303133;
+        margin-bottom: 4px;
+      }
+
+      .process-desc {
+        font-size: 12px;
+        color: #909399;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        margin-bottom: 4px;
+      }
+
+      .process-device {
+        font-size: 12px;
+        color: #606266;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+      }
+    }
+
+    .card-footer {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+
+      .param-count {
+        font-size: 12px;
+        color: #606266;
+      }
+    }
+  }
+
+  // 鍙充晶鍙傛暟鍒楄〃
+  .param-list-section {
+    flex: 1;
+    background: #fff;
+    border-radius: 8px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+    display: flex;
+    flex-direction: column;
+    min-width: 0;
+  }
+
+  .param-table-wrapper {
+    flex: 1;
+    padding: 0 20px 20px;
+    overflow: auto;
+    min-width: 100%;
+  }
+
+  /* 琛ㄦ牸妯悜婊氬姩 */
+  .param-table-wrapper :deep(.el-table) {
+    min-width: 100%;
+  }
+
+  .param-table-wrapper :deep(.el-table__body-wrapper) {
+    overflow-x: auto;
+  }
+
+  .pagination-container {
+    margin-top: 10px;
+    overflow-x: auto;
+    padding-bottom: 8px;
+  }
+
+  .pagination-container .el-pagination {
+    white-space: nowrap;
+  }
+
+  /* 鍝嶅簲寮忚皟鏁� */
+  @media screen and (max-width: 768px) {
+    .pagination-container {
+      font-size: 12px;
+    }
+
+    .pagination-container .el-pagination__sizes {
+      margin-right: 8px;
+    }
+
+    .pagination-container .el-pagination__jump {
+      margin-left: 8px;
+    }
+
+    .pagination-container .el-pagination__page-size {
+      font-size: 12px;
+    }
+  }
+
+  .empty-tip {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+
+  // 琛ㄦ牸鏍峰紡
+  :deep(.el-table) {
+    border: none;
+    border-radius: 6px;
+    overflow: hidden;
+
+    .el-table__header-wrapper {
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+
+      th {
+        background: transparent;
+        font-weight: 600;
+        // color: #ffffff;
+        border-bottom: none;
+        padding: 16px 0;
+      }
+    }
+
+    .el-table__body-wrapper {
+      tr {
+        transition: all 0.3s ease;
+
+        &:hover {
+          background: linear-gradient(
+            90deg,
+            rgba(102, 126, 234, 0.05) 0%,
+            rgba(118, 75, 162, 0.05) 100%
+          );
+        }
+
+        td {
+          border-bottom: 1px solid #f0f0f0;
+          padding: 14px 0;
+          color: #303133;
+        }
+      }
+    }
+  }
+
+  // 缂栫爜鍗曞厓鏍兼牱寮�
+  :deep(.code-cell) {
+    color: #e6a23c;
+    font-family: "Courier New", monospace;
+    font-weight: 500;
+  }
+
+  // 鏁板�煎崟鍏冩牸鏍峰紡
+  :deep(.quantity-cell) {
+    font-weight: 600;
+    color: #409eff;
+    font-family: "Courier New", monospace;
+  }
+
+  // 閫夋嫨鍙傛暟瀵硅瘽妗嗘牱寮�
+  .param-select-container {
+    display: flex;
+    gap: 20px;
+    height: 450px;
+
+    .param-list-area {
+      // flex: 1;
+      width: 380px;
+      display: flex;
+      flex-direction: column;
+
+      .area-title {
+        font-size: 14px;
+        font-weight: 600;
+        color: #303133;
+        margin-bottom: 12px;
+        padding-bottom: 8px;
+        border-bottom: 1px solid #ebeef5;
+      }
+
+      .search-box {
+        margin-bottom: 12px;
+
+        .el-input {
+          width: 100%;
+        }
+      }
+    }
+
+    .param-detail-area {
+      // width: 380px;
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      background: #f5f7fa;
+      border-radius: 8px;
+      padding: 16px;
+
+      .area-title {
+        font-size: 14px;
+        font-weight: 600;
+        color: #303133;
+        margin-bottom: 16px;
+        padding-bottom: 8px;
+        border-bottom: 1px solid #ebeef5;
+      }
+
+      .param-detail-form {
+        .el-form-item {
+          margin-bottom: 12px;
+
+          .el-form-item__label {
+            color: #606266;
+            font-weight: 500;
+          }
+        }
+
+        .detail-text {
+          color: #303133;
+          font-weight: 500;
+        }
+      }
+    }
+  }
+</style>
diff --git a/src/views/productionManagement/productionReporting/index.vue b/src/views/productionManagement/productionReporting/index.vue
index 0b42dae..aff050f 100644
--- a/src/views/productionManagement/productionReporting/index.vue
+++ b/src/views/productionManagement/productionReporting/index.vue
@@ -99,8 +99,7 @@
                                 style="width: 100%" />
               </template>
             </el-table-column>
-            <el-table-column label="鎿嶄綔"
-                             >
+            <el-table-column label="鎿嶄綔">
               <template #default="scope">
                 <el-button link
                            type="primary"
@@ -124,11 +123,36 @@
     <input-modal v-if="isShowInput"
                  v-model:visible="isShowInput"
                  :production-product-main-id="isShowingId" />
+    <!-- 鍙傛暟璇︽儏寮圭獥 -->
+    <el-dialog v-model="paramDetailVisible"
+               title="鍙傛暟璇︽儏"
+               width="600px">
+      <div v-if="currentParams && currentParams.length > 0"
+           class="param-detail-list">
+        <el-descriptions :column="1"
+                         border>
+          <el-descriptions-item v-for="param in currentParams"
+                                :key="param.id"
+                                :label="param.paramName">
+            {{ param.inputValue }}
+            <span v-if="param.unit && param.unit !== '/'"
+                  class="unit-text">({{ param.unit }})</span>
+          </el-descriptions-item>
+        </el-descriptions>
+      </div>
+      <el-empty v-else
+                description="鏆傛棤鍙傛暟鏁版嵁" />
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="paramDetailVisible = false">鍏抽棴</el-button>
+        </span>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-  import { onMounted, ref } from "vue";
+  import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
   import FormDia from "@/views/productionManagement/productionReporting/components/formDia.vue";
   import { ElMessageBox } from "element-plus";
   import {
@@ -202,7 +226,7 @@
       prop: "unit",
       width: 120,
     },
-    
+
     {
       label: "鍒涘缓鏃堕棿",
       prop: "createTime",
@@ -213,12 +237,20 @@
       label: "鎿嶄綔",
       align: "center",
       fixed: "right",
+      width: 250,
       operation: [
         {
           name: "鏌ョ湅鎶曞叆",
           type: "text",
           clickFun: row => {
             showInput(row);
+          },
+        },
+        {
+          name: "鍙傛暟璇︽儏",
+          type: "text",
+          clickFun: row => {
+            showParamDetail(row);
           },
         },
         {
@@ -232,6 +264,13 @@
     },
   ]);
   const tableData = ref([]);
+  const paramDetailVisible = ref(false);
+  const currentParams = ref([]);
+
+  const showParamDetail = row => {
+    currentParams.value = row.productionOperationParamList || [];
+    paramDetailVisible.value = true;
+  };
   const selectedRows = ref([]);
   const tableLoading = ref(false);
   const childrenLoading = ref(false);
@@ -417,4 +456,16 @@
   });
 </script>
 
-<style scoped></style>
+<style scoped>
+  .unit-text {
+    margin-left: 5px;
+    color: #909399;
+    font-size: 12px;
+  }
+  .param-detail-list {
+    padding: 10px;
+  }
+  .table_list {
+    margin-top: unset;
+  }
+</style>
diff --git a/src/views/productionManagement/productionTraceability/index.vue b/src/views/productionManagement/productionTraceability/index.vue
new file mode 100644
index 0000000..a850788
--- /dev/null
+++ b/src/views/productionManagement/productionTraceability/index.vue
@@ -0,0 +1,706 @@
+<template>
+  <div class="app-container">
+    <el-card style="height:82vh;overflow:auto;">
+      <template #header>
+        <div class="card-header">
+          <el-form :inline="true"
+                   :model="searchForm"
+                   class="search-form">
+            <el-form-item label="鐢熶骇璁㈠崟鍙�">
+              <el-select v-model="selectedNpsNo"
+                         filterable
+                         remote
+                         reserve-keyword
+                         placeholder="璇疯緭鍏ョ敓浜ц鍗曞彿"
+                         :loading="npsNoLoading"
+                         :remote-method="handleNpsNoSearch"
+                         @change="handleSearch"
+                         style="width: 400px;">
+                <el-option v-for="option in npsNoOptions"
+                           :key="option.id"
+                           :label="option.npsNo + '-' + option.productName + '-' + option.model"
+                           :value="option.id" />
+              </el-select>
+            </el-form-item>
+          </el-form>
+        </div>
+      </template>
+      <!-- 鍩虹淇℃伅 -->
+      <div v-if="rowData.productionOrderDto"
+           class="detail-section">
+        <h3 class="section-title">鍩虹淇℃伅</h3>
+        <el-descriptions :column="3"
+                         border>
+          <el-descriptions-item label="鐢熶骇璁㈠崟鍙�">{{ rowData.productionOrderDto?.npsNo || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="浜у搧鍚嶇О">{{ rowData.productionOrderDto?.productName || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="浜у搧瑙勬牸">{{ rowData.productionOrderDto?.model || '-' }}</el-descriptions-item>
+          <!-- <el-descriptions-item label="鐗╂枡缂栫爜">{{ rowData.productionOrderDto?.materialCode || '-' }}</el-descriptions-item> -->
+          <el-descriptions-item label="璁″垝鏁伴噺">{{ rowData.productionOrderDto?.quantity || 0 }} <span class="unit">{{ rowData.productionOrderDto?.unit || '-' }}</span></el-descriptions-item>
+          <el-descriptions-item label="褰撳墠鐘舵��">
+            <el-tag :type="getStatusType(rowData.productionOrderDto?.status)">
+              {{ getStatusText(rowData.productionOrderDto?.status) }}
+            </el-tag>
+          </el-descriptions-item>
+          <el-descriptions-item label="瀹㈡埛鍚嶇О">{{ rowData.productionOrderDto?.customerName || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="寮�濮嬫棩鏈�">{{ parseTime(rowData.productionOrderDto?.startTime) || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="瀹屾垚杩涘害">
+            <el-progress :percentage="rowData.productionOrderDto?.completionStatus>=100?100:rowData.productionOrderDto?.completionStatus"
+                         :color="customColors(rowData.productionOrderDto?.completionStatus)"
+                         :status="rowData.productionOrderDto?.completionStatus >= 100 ? 'success' : ''"
+                         style="width: 80%;" />
+          </el-descriptions-item>
+        </el-descriptions>
+      </div>
+      <el-empty v-else
+                description="璇锋悳绱㈢敓浜ц鍗曞彿" />
+      <!-- 鐢熶骇鎶ュ伐璁板綍 -->
+      <div v-if="rowData.productionRecords && rowData.productionRecords.length > 0"
+           class="progress-container">
+        <div class="progress-section">
+          <h3 class="section-title">宸ュ崟淇℃伅</h3>
+          <div class="order-item">
+            <el-table :data="rowData.productionRecords"
+                      border
+                      style="width: 100%">
+              <el-table-column prop="workOrder.workOrderNo"
+                               label="宸ュ崟缂栧彿"
+                               align="center">
+              </el-table-column>
+              <el-table-column label="浜у搧鍚嶇О"
+                               align="center">
+                <template #default="{ row }">
+                  {{ row.workOrder.productName || '-' }}
+                </template>
+              </el-table-column>
+              <el-table-column label="瑙勬牸"
+                               align="center">
+                <template #default="{ row }">
+                  {{ row.workOrder.model || '-' }}
+                </template>
+              </el-table-column>
+              <el-table-column prop="workOrder.planQuantity"
+                               label="闇�姹傛暟閲�"
+                               align="center" />
+              <el-table-column prop="workOrder.completeQuantity"
+                               label="瀹屾垚鏁伴噺"
+                               align="center" />
+              <el-table-column prop="workOrder.completionStatus"
+                               label="瀹屾垚杩涘害"
+                               align="center">
+                <template #default="{ row }">
+                  <span :style="{ color: customColors(row.workOrder.completionStatus) }">{{ row.workOrder.completionStatus || 0 }}%</span>
+                </template>
+              </el-table-column>
+              <el-table-column label="璇︽儏"
+                               align="center"
+                               width="200">
+                <template #default="{ row }">
+                  <el-link @click="handleClickStep(row)"
+                           type="primary">鎶ュ伐璁板綍</el-link>
+                  <el-link @click="handleClickQuality(row)"
+                           style="margin-left:20px"
+                           type="primary">璐ㄦ淇℃伅</el-link>
+                </template>
+              </el-table-column>
+            </el-table>
+          </div>
+        </div>
+      </div>
+      <el-empty v-else-if="rowData.productionOrderDto"
+                description="鏆傛棤鎶ュ伐璁板綍" />
+    </el-card>
+    <!-- 鐢熶骇鎶ュ伐璇︽儏寮圭獥 -->
+    <el-dialog v-model="detailDialogVisible"
+               title="鐢熶骇鎶ュ伐璇︽儏"
+               width="1000px"
+               :close-on-click-modal="false"
+               custom-class="custom-dialog">
+      <div class="detail-container">
+        <!-- 鍩虹淇℃伅 -->
+        <div class="detail-section">
+          <h3 class="section-title">宸ュ崟鍩虹淇℃伅</h3>
+          <el-descriptions :column="3"
+                           border>
+            <el-descriptions-item label="鐢熶骇宸ュ崟鍙�">{{ detailData.workOrder.workOrderNo || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="璁″垝鏁伴噺">{{ detailData.workOrder.planQuantity || 0 }}</el-descriptions-item>
+            <el-descriptions-item label="瀹屾垚鏁伴噺">{{ detailData.workOrder.completeQuantity || 0 }}</el-descriptions-item>
+            <el-descriptions-item label="瀹為檯寮�濮嬫椂闂�">{{ parseTime(detailData.workOrder.actualStartTime) || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="瀹為檯缁撴潫鏃堕棿">{{ parseTime(detailData.workOrder.actualEndTime) || '-' }}</el-descriptions-item>
+            <!-- <el-descriptions-item label="瀹屾垚杩涘害">
+              <el-progress :percentage="detailData.workOrder.completionStatus >= 100 ? 100 : (detailData.workOrder.completionStatus || 0)"
+                           :color="customColors(detailData.workOrder.completionStatus)"
+                           :status="detailData.workOrder.completionStatus >= 100 ? 'success' : ''"
+                           style="width:500px;" />
+            </el-descriptions-item> -->
+            <el-descriptions-item label="瀹屾垚杩涘害"><span :style="{ color: customColors(detailData.workOrder.completionStatus) }">{{ detailData.workOrder.completionStatus || 0 }}%</span></el-descriptions-item>
+          </el-descriptions>
+        </div>
+        <div class="detail-section">
+          <h3 class="section-title">鎶ュ伐鏄庣粏</h3>
+          <el-table :data="detailData.reports"
+                    border
+                    style="width: 100%">
+            <el-table-column label="鎶ュ伐鍗曞彿"
+                             prop="productNo"
+                             align="center" />
+            <el-table-column label="鍒涘缓浜�"
+                             prop="userName"
+                             align="center" />
+            <el-table-column label="鍒涘缓鏃堕棿"
+                             align="center">
+              <template #default="{ row }">
+                {{ parseTime(row.createTime) }}
+              </template>
+            </el-table-column>
+            <el-table-column label="鎿嶄綔"
+                             align="center"
+                             width="200">
+              <template #default="{ row }">
+                <el-button type="primary"
+                           link
+                           @click="showInput(row.id)">鏌ョ湅鎶曞叆</el-button>
+                <el-button type="primary"
+                           link
+                           @click="showParamDetail(row.productionOperationParamList)">鍙傛暟璇︽儏</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="detailDialogVisible = false">鍏抽棴</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 鎶曞叆妯℃�佹 -->
+    <input-modal v-if="isShowInput"
+                 v-model:visible="isShowInput"
+                 :production-product-main-id="isShowingId" />
+    <!-- 鍙傛暟璇︽儏寮圭獥 -->
+    <el-dialog v-model="paramDetailVisible"
+               title="鍙傛暟璇︽儏"
+               width="600px">
+      <div v-if="currentParams && currentParams.length > 0"
+           class="param-detail-list">
+        <el-descriptions :column="1"
+                         border>
+          <el-descriptions-item v-for="param in currentParams"
+                                :key="param.id"
+                                :label="param.paramName">
+            {{ param.inputValue }}
+            <span v-if="param.unit && param.unit !== '/'"
+                  class="unit-text">({{ param.unit }})</span>
+          </el-descriptions-item>
+        </el-descriptions>
+      </div>
+      <el-empty v-else
+                description="鏆傛棤鍙傛暟鏁版嵁" />
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="paramDetailVisible = false">鍏抽棴</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <!-- 璐ㄦ淇℃伅寮圭獥 -->
+    <el-dialog v-model="qualityDialogVisible"
+               title="璐ㄦ璇︽儏"
+               width="1000px"
+               :close-on-click-modal="false"
+               custom-class="custom-dialog">
+      <div class="detail-container">
+        <div v-for="(record, index) in qualityRecords"
+             :key="record.id"
+             class="quality-record-block">
+          <div class="detail-section">
+            <h3 class="section-title">妫�娴嬭褰� {{ index + 1 }} - {{ parseTime(record.createTime) }}</h3>
+            <el-descriptions :column="3"
+                             border>
+              <el-descriptions-item label="妫�娴嬫棩鏈�">{{ parseTime(record.createTime) || '-' }}</el-descriptions-item>
+              <el-descriptions-item label="鎶ュ伐鍗曞彿">{{ record.reportNo || '-' }}</el-descriptions-item>
+              <el-descriptions-item label="妫�楠屽憳">{{ record.userName || '-' }}</el-descriptions-item>
+              <el-descriptions-item label="浜у搧鍚嶇О">{{ record.productName || '-' }}</el-descriptions-item>
+              <el-descriptions-item label="瑙勬牸鍨嬪彿">{{ record.model || '-' }}</el-descriptions-item>
+              <el-descriptions-item label="鏁伴噺">{{ record.quantity || 0 }} {{ record.unit || '-' }}</el-descriptions-item>
+              <el-descriptions-item label="妫�娴嬪崟浣�">{{ record.checkCompany || '-' }}</el-descriptions-item>
+              <el-descriptions-item label="妫�娴嬬粨鏋�">
+                <el-tag :type="record.checkResult === '鍚堟牸' ? 'success' : 'danger'">
+                  {{ record.checkResult || '寰呮娴�' }}
+                </el-tag>
+              </el-descriptions-item>
+            </el-descriptions>
+            <h4 class="sub-section-title">妫�楠屾寚鏍囧垪琛�</h4>
+            <el-table :data="record.inspectParamList"
+                      border
+                      style="width: 100%">
+              <el-table-column label="搴忓彿"
+                               type="index"
+                               width="60"
+                               align="center" />
+              <el-table-column label="鎸囨爣"
+                               prop="parameterItem"
+                               align="center" />
+              <el-table-column label="鍗曚綅"
+                               prop="unit"
+                               align="center" />
+              <el-table-column label="鏍囧噯鍊�"
+                               prop="standardValue"
+                               align="center" />
+              <el-table-column label="鍐呮帶鍊�"
+                               prop="controlValue"
+                               align="center" />
+              <el-table-column label="瀹為檯鍊�"
+                               prop="testValue"
+                               align="center" />
+            </el-table>
+          </div>
+          <el-divider v-if="index < qualityRecords.length - 1" />
+        </div>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="qualityDialogVisible = false">鍏抽棴</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+  import { ref, reactive, onMounted } from "vue";
+  import { useRoute, useRouter } from "vue-router";
+  import { ElMessage } from "element-plus";
+  import { parseTime } from "@/utils/ruoyi";
+  import InputModal from "@/views/productionManagement/productionReporting/Input.vue";
+  import {
+    getOrderDetail,
+    productOrderListPage,
+  } from "@/api/productionManagement/productionOrder";
+
+  const route = useRoute();
+  const router = useRouter();
+
+  // 鎼滅储鐩稿叧
+  const searchForm = reactive({
+    npsNo: "",
+  });
+  const selectedNpsNo = ref(null);
+  const npsNoLoading = ref(false);
+  const npsNoOptions = ref([]);
+
+  // 璇︽儏鏁版嵁
+  const rowData = reactive({
+    productionOrderDto: null,
+    productionRecords: [],
+  });
+
+  // 鎶ュ伐璇︽儏寮圭獥
+  const detailDialogVisible = ref(false);
+  const detailData = ref({
+    workOrder: {},
+    reports: [],
+  });
+
+  // 鎶曞叆妯℃�佹
+  const isShowInput = ref(false);
+  const isShowingId = ref(0);
+  const showInput = id => {
+    isShowInput.value = true;
+    isShowingId.value = id;
+  };
+
+  // 鍙傛暟璇︽儏寮圭獥
+  const paramDetailVisible = ref(false);
+  const currentParams = ref([]);
+  const showParamDetail = params => {
+    currentParams.value = params || [];
+    paramDetailVisible.value = true;
+  };
+
+  // 璐ㄦ淇℃伅寮圭獥
+  const qualityDialogVisible = ref(false);
+  const qualityRecords = ref([]);
+
+  // 鐘舵�佸鐞�
+  const getStatusType = status => {
+    const typeMap = { 1: "primary", 2: "warning", 3: "success", 5: "danger" };
+    return typeMap[status] || "info";
+  };
+  const getStatusText = status => {
+    const statusMap = { 1: "寰呭紑濮�", 2: "杩涜涓�", 3: "宸插畬鎴�", 5: "宸茬粨鏉�" };
+    return statusMap[status] || "宸插彇娑�";
+  };
+  const customColors = percentage => {
+    if (percentage < 30) return "#f56c6c";
+    if (percentage < 70) return "#e6a23c";
+    return "#67c23a";
+  };
+
+  // 妯℃嫙鎼滅储鏂规硶
+  const handleNpsNoSearch = async query => {
+    npsNoLoading.value = true;
+    try {
+      const res = await productOrderListPage({
+        npsNo: query || "",
+        pageNum: 1,
+        pageSize: 50,
+      });
+      // 鍙傜収 productionOrder/index.vue 鐨勬暟鎹粨鏋� res.data.records
+      npsNoOptions.value = res.data?.records || res.rows || [];
+    } catch (error) {
+      console.error(error);
+    } finally {
+      npsNoLoading.value = false;
+    }
+  };
+
+  const handleSearch = async id => {
+    const selected = npsNoOptions.value.find(item => item.id === id);
+    if (selected) {
+      try {
+        const res = await getOrderDetail(selected.npsNo);
+        if (res.code === 200) {
+          const { productionOrder, workOrderList } = res.data;
+          rowData.productionOrderDto = productionOrder || selected;
+          rowData.productionRecords = workOrderList || [];
+        } else {
+          rowData.productionOrderDto = selected;
+          rowData.productionRecords = [];
+        }
+      } catch (error) {
+        console.error(error);
+        ElMessage.error("鑾峰彇璁㈠崟璇︽儏澶辫触");
+        rowData.productionOrderDto = selected;
+      }
+    }
+  };
+
+  const handleBack = () => {
+    router.back();
+  };
+
+  const handleClickStep = row => {
+    // row 鏄� workOrderList 涓殑涓�椤癸紝鍖呭惈 workOrder, reportList, inspectList
+    detailData.value = {
+      workOrder: row.workOrder || {},
+      reports: (row.reportList || []).map(r => ({
+        ...r.reportMain,
+        productionOperationParamList: r.reportParamList || [],
+      })),
+    };
+    detailDialogVisible.value = true;
+  };
+
+  const handleClickQuality = row => {
+    // row 鏄� workOrderList 涓殑涓�椤�
+    const inspects = row.inspectList || [];
+    qualityRecords.value = inspects.map(i => ({
+      ...i.inspect,
+      reportNo: i.reportNo,
+      userName: i.reportMain?.userName || "-",
+      inspectParamList: i.inspectParamList || [],
+      inspectFileList: i.inspectFileList || [],
+    }));
+    qualityDialogVisible.value = true;
+  };
+
+  onMounted(async () => {
+    // 鍒濆鍔犺浇鍒楄〃
+    await handleNpsNoSearch();
+
+    if (route.query.npsNo) {
+      const npsNo = route.query.npsNo;
+      const found = npsNoOptions.value.find(item => item.npsNo === npsNo);
+      if (found) {
+        selectedNpsNo.value = found.id;
+        handleSearch(found.id);
+      } else {
+        // 濡傛灉鍒楄〃涓病鏈夛紙鍙兘鏄垎椤靛師鍥狅級锛屽垯鏍规嵁 npsNo 鍐嶆绮惧噯鎼滅储
+        try {
+          const res = await productOrderListPage({
+            npsNo,
+            pageNum: -1,
+            pageSize: -1,
+          });
+          const records = res.data?.records || res.rows || [];
+          if (records.length > 0) {
+            const item = records[0];
+            npsNoOptions.value.unshift(item);
+            selectedNpsNo.value = item.id;
+            handleSearch(item.id);
+          }
+        } catch (error) {
+          console.error("鑾峰彇璺敱鍙傛暟瀵瑰簲鐨勮鍗曞け璐�", error);
+        }
+      }
+    }
+  });
+</script>
+
+<style scoped>
+  .app-container {
+    padding: 20px;
+    background-color: #f5f7fa;
+    min-height: 100vh;
+  }
+
+  .card-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 0 10px;
+  }
+
+  .search-form {
+    width: 100%;
+  }
+
+  .search-form .el-form-item {
+    margin-right: 10px;
+  }
+
+  .detail-section {
+    margin-bottom: 24px;
+    background-color: #ffffff;
+    border-radius: 10px;
+    padding: 24px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
+    transition: all 0.3s ease;
+  }
+
+  .detail-section:hover {
+    box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.12);
+  }
+
+  .section-title {
+    font-size: 16px;
+    font-weight: 600;
+    margin-bottom: 20px;
+    color: #1a1a1a;
+    border-bottom: 2px solid #409eff;
+    padding-bottom: 10px;
+    display: flex;
+    align-items: center;
+  }
+
+  .section-title::before {
+    content: "";
+    display: inline-block;
+    width: 4px;
+    height: 16px;
+    background-color: #409eff;
+    margin-right: 8px;
+    border-radius: 2px;
+  }
+
+  .sub-section-title {
+    font-size: 14px;
+    font-weight: 600;
+    margin-bottom: 16px;
+    color: #303133;
+    display: flex;
+    align-items: center;
+  }
+
+  .sub-section-title::before {
+    content: "";
+    display: inline-block;
+    width: 3px;
+    height: 12px;
+    background-color: #67c23a;
+    margin-right: 8px;
+    border-radius: 2px;
+  }
+
+  .unit {
+    font-size: 12px;
+    color: #909399;
+    margin-left: 4px;
+  }
+
+  :deep(.el-descriptions) {
+    border-radius: 8px;
+    overflow: hidden;
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+  }
+
+  :deep(.el-descriptions__row:nth-child(odd)) {
+    background-color: #f9f9f9;
+  }
+
+  :deep(.el-descriptions__label) {
+    font-weight: 500;
+    color: #606266;
+    background-color: #f5f7fa;
+  }
+
+  :deep(.el-descriptions__content) {
+    color: #303133;
+    font-weight: 500;
+  }
+
+  .progress-container {
+    display: flex;
+    gap: 24px;
+  }
+
+  .progress-section {
+    margin-bottom: 24px;
+    background-color: #ffffff;
+    border-radius: 10px;
+    padding: 24px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
+    flex: 1;
+    transition: all 0.3s ease;
+    width: 100%;
+  }
+
+  .progress-section:hover {
+    box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.12);
+  }
+
+  .order-item {
+    margin-bottom: 20px;
+    border-radius: 8px;
+    overflow: hidden;
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+  }
+
+  :deep(.el-table) {
+    border-radius: 8px;
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+  }
+
+  :deep(.el-table th) {
+    background-color: #f5f7fa !important;
+    font-weight: 600;
+    color: #606266;
+  }
+
+  :deep(.el-progress-bar__inner) {
+    border-radius: 10px;
+  }
+
+  :deep(.el-tag) {
+    border-radius: 12px;
+    padding: 2px 10px;
+  }
+
+  /* 寮圭獥鏍峰紡 */
+  .detail-container {
+    max-height: 600px;
+    overflow-y: auto;
+    padding: 0 16px;
+  }
+
+  .process-item {
+    margin-bottom: 24px;
+    padding: 20px;
+    background-color: #ffffff;
+    border-radius: 8px;
+    border: 1px solid #ebeef5;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+  }
+
+  .process-header {
+    margin-bottom: 20px;
+    padding-bottom: 12px;
+    border-bottom: 1px solid #f0f2f5;
+  }
+
+  .process-title {
+    font-size: 15px;
+    font-weight: 600;
+    margin-bottom: 12px;
+    color: #1a1a1a;
+    display: flex;
+    align-items: center;
+  }
+
+  .process-title::before {
+    content: "";
+    display: inline-block;
+    width: 4px;
+    height: 16px;
+    background-color: #409eff;
+    margin-right: 8px;
+    border-radius: 2px;
+  }
+
+  .process-info {
+    display: flex;
+    gap: 20px;
+    font-size: 13px;
+    color: #606266;
+  }
+
+  .process-label {
+    padding: 4px 12px;
+    background-color: #ecf5ff;
+    border-radius: 4px;
+    color: #409eff;
+    font-weight: 500;
+  }
+
+  .process-details {
+    margin-bottom: 20px;
+  }
+
+  .num1 {
+    color: #1107cc;
+    font-weight: 600;
+  }
+
+  .num2 {
+    color: #0fcf25;
+    font-weight: 600;
+  }
+
+  .num3 {
+    color: #d31818;
+    font-weight: 600;
+  }
+
+  .dialog-footer {
+    text-align: center;
+    padding: 20px;
+    border-top: 1px solid #ebeef5;
+  }
+
+  .dialog-footer .el-button {
+    min-width: 100px;
+    padding: 8px 20px;
+  }
+
+  /* 鑷畾涔夊璇濇鏍峰紡 */
+  :deep(.custom-dialog) {
+    border-radius: 12px;
+    overflow: hidden;
+  }
+
+  :deep(.custom-dialog .el-dialog__header) {
+    background-color: #f5f7fa;
+    padding: 20px;
+    border-bottom: 1px solid #ebeef5;
+  }
+
+  :deep(.custom-dialog .el-dialog__title) {
+    font-size: 18px;
+    font-weight: 600;
+    color: #1a1a1a;
+  }
+
+  :deep(.custom-dialog .el-dialog__body) {
+    padding: 20px;
+  }
+
+  .unit-text {
+    margin-left: 5px;
+    color: #909399;
+    font-size: 12px;
+  }
+
+  .param-detail-list {
+    padding: 10px;
+  }
+</style>
diff --git a/src/views/productionManagement/workOrder/index.vue b/src/views/productionManagement/workOrder/index.vue
index 162dd91..1e4810a 100644
--- a/src/views/productionManagement/workOrder/index.vue
+++ b/src/views/productionManagement/workOrder/index.vue
@@ -596,7 +596,7 @@
 
   const showReportDialog = row => {
     currentReportRowData.value = row;
-    reportForm.planQuantity = row.planQuantity;
+    reportForm.planQuantity = row.planQuantity - row.completeQuantity;
     reportForm.quantity =
       row.quantity !== undefined && row.quantity !== null ? row.quantity : null;
     reportForm.productProcessRouteItemId = row.productProcessRouteItemId;
diff --git a/src/views/productionManagement/workOrderEdit/index.vue b/src/views/productionManagement/workOrderEdit/index.vue
index 50f9fce..1cde5ea 100644
--- a/src/views/productionManagement/workOrderEdit/index.vue
+++ b/src/views/productionManagement/workOrderEdit/index.vue
@@ -1,10 +1,19 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form mb20">
       <div class="search-row">
         <div class="search-item">
           <span class="search_title">宸ュ崟缂栧彿锛�</span>
           <el-input v-model="searchForm.workOrderNo"
+                    style="width: 240px"
+                    placeholder="璇疯緭鍏�"
+                    @change="handleQuery"
+                    clearable
+                    prefix-icon="Search" />
+        </div>
+        <div class="search-item">
+          <span class="search_title">鐢熶骇璁㈠崟鍙凤細</span>
+          <el-input v-model="searchForm.npsNo"
                     style="width: 240px"
                     placeholder="璇疯緭鍏�"
                     @change="handleQuery"
@@ -31,8 +40,6 @@
         </template>
       </PIMTable>
     </div>
-    
-    <!-- 缂栬緫鏃堕棿寮圭獥 -->
     <el-dialog v-model="editDialogVisible"
                title="缂栬緫璁″垝鏃堕棿"
                width="500px">
@@ -61,18 +68,68 @@
         </span>
       </template>
     </el-dialog>
+    <!-- 鎸囧畾鎶ュ伐浜哄脊绐� -->
+    <el-dialog v-model="assignReporterDialogVisible"
+               title="鎸囧畾鎶ュ伐浜�"
+               width="800px">
+      <div class="assign-reporter-content">
+        <div class="selected-tags-box"
+             v-if="selectedEmployeeIds.length > 0">
+          <div class="tags-label">宸查�夋嫨锛�</div>
+          <div class="tags-list">
+            <el-tag v-for="id in selectedEmployeeIds"
+                    :key="id"
+                    closable
+                    @close="removeEmployeeTag(id)"
+                    class="employee-tag">
+              {{ getEmployeeNameById(id) }}
+            </el-tag>
+          </div>
+        </div>
+        <div class="employee-list-container"
+             v-loading="employeeTableLoading">
+          <el-checkbox-group v-model="selectedEmployeeIds">
+            <div class="employee-grid">
+              <div v-for="item in employeeTableData"
+                   :key="item.userId"
+                   class="employee-item">
+                <el-checkbox :label="item.userId"
+                             border>
+                  <div class="employee-info">
+                    <span class="name">{{ item.nickName }}</span>
+                    <span class="dept">{{ item.dept?.deptName }}</span>
+                  </div>
+                </el-checkbox>
+              </div>
+            </div>
+          </el-checkbox-group>
+          <div v-if="employeeTableData.length === 0"
+               class="empty-text">
+            鏆傛棤鍖归厤浜哄憳
+          </div>
+        </div>
+      </div>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="assignReporterDialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary"
+                     @click="handleSaveReporters">纭畾</el-button>
+        </span>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-  import { onMounted, ref, nextTick } from "vue";
+  import { getCurrentInstance, onMounted, reactive, ref, toRefs } from "vue";
   import { ElMessageBox } from "element-plus";
-  import dayjs from "dayjs";
   import {
     productWorkOrderPage,
     updateProductWorkOrder,
+    assignProductWorkOrder,
   } from "@/api/productionManagement/workOrder.js";
-  import { getCurrentInstance, reactive, toRefs } from "vue";
+  import { listUser } from "@/api/system/user.js";
+
   const { proxy } = getCurrentInstance();
 
   const tableColumn = ref([
@@ -88,7 +145,7 @@
     },
     {
       label: "鐢熶骇璁㈠崟鍙�",
-      prop: "productOrderNpsNo",
+      prop: "npsNo",
       width: "140",
     },
     {
@@ -106,7 +163,8 @@
     },
     {
       label: "宸ュ簭鍚嶇О",
-      prop: "processName",
+      prop: "operationName",
+      width: "100",
     },
     {
       label: "闇�姹傛暟閲�",
@@ -146,8 +204,13 @@
       width: "140",
     },
     {
+      label: "鎸囧畾鎶ュ伐浜�",
+      prop: "userNames",
+      width: "180",
+    },
+    {
       label: "鎿嶄綔",
-      width: "100",
+      width: "200",
       align: "center",
       dataType: "action",
       fixed: "right",
@@ -158,26 +221,49 @@
             handleEdit(row);
           },
         },
+        {
+          name: "鎸囧畾鎶ュ伐浜�",
+          clickFun: row => {
+            handleAssignReporter(row);
+          },
+        },
       ],
     },
   ]);
-  
+
   const tableData = ref([]);
   const tableLoading = ref(false);
   const editDialogVisible = ref(false);
-  let editrow = ref(null);
+  const editrow = ref(null);
   const page = reactive({
     current: 1,
     size: 100,
     total: 0,
   });
 
+  // 鎸囧畾鎶ュ伐浜虹浉鍏�
+  const assignReporterDialogVisible = ref(false);
+  const employeeTableLoading = ref(false);
+  const employeeTableData = ref([]);
+  const employeePage = reactive({
+    current: 1,
+    size: 100,
+    total: 0,
+  });
+  const employeeSearchForm = reactive({
+    staffName: "",
+  });
+  const selectedEmployeeIds = ref([]);
+  const currentWorkOrder = ref(null);
+
   const data = reactive({
     searchForm: {
       workOrderNo: "",
+      npsNo: "",
     },
   });
   const { searchForm } = toRefs(data);
+
   const toProgressPercentage = val => {
     const n = Number(val);
     if (!Number.isFinite(n)) return 0;
@@ -185,6 +271,7 @@
     if (n >= 100) return 100;
     return Math.round(n);
   };
+
   const progressColor = percentage => {
     const p = toProgressPercentage(percentage);
     if (p < 30) return "#f56c6c";
@@ -193,17 +280,17 @@
     return "#67c23a";
   };
 
-  // 鏌ヨ鍒楄〃
-  /** 鎼滅储鎸夐挳鎿嶄綔 */
   const handleQuery = () => {
     page.current = 1;
     getList();
   };
+
   const pagination = obj => {
     page.current = obj.page;
     page.size = obj.limit;
     getList();
   };
+
   const getList = () => {
     tableLoading.value = true;
     const params = { ...searchForm.value, ...page };
@@ -225,7 +312,7 @@
 
   const handleUpdate = () => {
     updateProductWorkOrder(editrow.value)
-      .then(res => {
+      .then(() => {
         proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
         editDialogVisible.value = false;
         getList();
@@ -234,6 +321,74 @@
         ElMessageBox.alert("淇敼澶辫触", "鎻愮ず", {
           confirmButtonText: "纭畾",
         });
+      });
+  };
+
+  const handleAssignReporter = row => {
+    currentWorkOrder.value = row;
+    assignReporterDialogVisible.value = true;
+    // 鍥炴樉宸插嬀閫夌殑浜哄憳
+    if (row.userIds) {
+      try {
+        selectedEmployeeIds.value = JSON.parse(row.userIds);
+      } catch (e) {
+        selectedEmployeeIds.value = [];
+      }
+    } else {
+      selectedEmployeeIds.value = [];
+    }
+    employeeSearchForm.staffName = "";
+    getEmployeeList();
+  };
+
+  const getEmployeeList = () => {
+    employeeTableLoading.value = true;
+    const params = {
+      pageNum: 1,
+      pageSize: 100,
+    };
+    listUser(params)
+      .then(res => {
+        employeeTableLoading.value = false;
+        employeeTableData.value = res.rows;
+        employeePage.total = res.total;
+      })
+      .catch(() => {
+        employeeTableLoading.value = false;
+      });
+  };
+
+  const getEmployeeNameById = id => {
+    const employee = employeeTableData.value.find(item => item.userId === id);
+    return employee ? employee.nickName : id;
+  };
+
+  const removeEmployeeTag = id => {
+    selectedEmployeeIds.value = selectedEmployeeIds.value.filter(
+      item => item !== id
+    );
+  };
+
+  const handleSaveReporters = () => {
+    if (selectedEmployeeIds.value.length === 0) {
+      proxy.$modal.msgWarning("璇烽�夋嫨鎶ュ伐浜�");
+      return;
+    }
+
+    const updateData = {
+      id: currentWorkOrder.value.id,
+      userIds: JSON.stringify(selectedEmployeeIds.value),
+    };
+    console.log(updateData, "updateData");
+
+    assignProductWorkOrder(updateData)
+      .then(() => {
+        proxy.$modal.msgSuccess("鎸囧畾鎴愬姛");
+        assignReporterDialogVisible.value = false;
+        getList();
+      })
+      .catch(() => {
+        proxy.$modal.msgError("鎸囧畾澶辫触");
       });
   };
 
@@ -248,13 +403,94 @@
     align-items: center;
     gap: 12px;
   }
+
   .search-item {
     display: flex;
     align-items: center;
   }
+
   .search_title {
     margin-right: 8px;
     font-size: 14px;
     color: #606266;
   }
-</style>
\ No newline at end of file
+
+  .assign-reporter-content {
+    .selected-tags-box {
+      margin-bottom: 16px;
+      padding: 12px;
+      background-color: #f5f7fa;
+      border-radius: 4px;
+      display: flex;
+      align-items: flex-start;
+
+      .tags-label {
+        font-size: 14px;
+        color: #606266;
+        margin-right: 8px;
+        white-space: nowrap;
+        margin-top: 4px;
+      }
+
+      .tags-list {
+        display: flex;
+        flex-wrap: wrap;
+        gap: 8px;
+
+        .employee-tag {
+          margin-bottom: 4px;
+        }
+      }
+    }
+
+    .employee-list-container {
+      max-height: 400px;
+      overflow-y: auto;
+      padding: 10px;
+      border: 1px solid #f0f0f0;
+      border-radius: 4px;
+
+      .employee-grid {
+        display: grid;
+        grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
+        gap: 12px;
+      }
+
+      .employee-item {
+        :deep(.el-checkbox) {
+          width: 100%;
+          margin-right: 0;
+          height: auto;
+          padding: 8px;
+
+          .el-checkbox__label {
+            width: 100%;
+          }
+        }
+
+        .employee-info {
+          display: flex;
+          flex-direction: column;
+          gap: 4px;
+
+          .name {
+            font-weight: bold;
+            font-size: 14px;
+            color: #303133;
+          }
+
+          .dept {
+            font-size: 12px;
+            color: #909399;
+          }
+        }
+      }
+
+      .empty-text {
+        text-align: center;
+        color: #909399;
+        padding: 20px;
+      }
+    }
+  }
+</style>
diff --git a/src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue b/src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue
new file mode 100644
index 0000000..45944b5
--- /dev/null
+++ b/src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue
@@ -0,0 +1,320 @@
+<template>
+  <div>
+    <el-dialog v-model="dialogVisible"
+               title="鐗╂枡"
+               width="1200px"
+               @close="handleCloseMaterialDialog">
+      <el-table v-loading="materialTableLoading"
+                :data="materialTableData"
+                border
+                row-key="id">
+        <el-table-column label="宸ュ簭鍚嶇О"
+                         prop="processName"
+                         min-width="140" />
+        <el-table-column label="鍘熸枡鍚嶇О"
+                         prop="materialName"
+                         min-width="140" />
+        <el-table-column label="鍘熸枡鍨嬪彿"
+                         prop="materialModel"
+                         min-width="140" />
+        <el-table-column label="璁¢噺鍗曚綅"
+                         prop="unit"
+                         min-width="100" />
+        <el-table-column label="绾胯竟浠撴暟閲�"
+                         prop="pickQty"
+                         min-width="100" />
+        <el-table-column label="琛ユ枡鏁伴噺"
+                         prop="supplementQty"
+                         min-width="100" />
+        <el-table-column label="瀹為檯鏁伴噺"
+                         min-width="140">
+          <template #default="{ row }">
+            <el-input-number v-model="row.actualQty"
+                             :min="0"
+                             :precision="3"
+                             :step="1"
+                             controls-position="right"
+                             style="width: 100%;" />
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔"
+                         align="center"
+                         fixed="right"
+                         width="180">
+          <template #default="{ row }">
+            <el-button type="primary"
+                       link
+                       @click="openSupplementDialog(row)">琛ユ枡</el-button>
+            <el-button type="info"
+                       link
+                       @click="openSupplementRecordDialog(row)">琛ユ枡璁板綍</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary"
+                     :loading="pickSubmitting"
+                     @click="handleSubmitPick">棰嗙敤</el-button>
+          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <FormDialog v-model="supplementDialogVisible"
+                title="琛ユ枡"
+                width="500px"
+                @confirm="handleSubmitSupplement">
+      <el-form ref="supplementFormRef"
+               :model="supplementForm"
+               :rules="supplementRules"
+               label-width="100px">
+        <el-form-item label="琛ユ枡鏁伴噺"
+                      prop="supplementQty">
+          <el-input-number v-model="supplementForm.supplementQty"
+                           :min="0.001"
+                           :precision="3"
+                           :step="1"
+                           style="width: 100%;" />
+        </el-form-item>
+        <el-form-item label="琛ユ枡鍘熷洜"
+                      prop="supplementReason">
+          <el-input v-model="supplementForm.supplementReason"
+                    type="textarea"
+                    :rows="3"
+                    maxlength="200"
+                    show-word-limit
+                    placeholder="璇疯緭鍏ヨˉ鏂欏師鍥�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary"
+                     :loading="supplementSubmitting"
+                     @click="handleSubmitSupplement">纭畾</el-button>
+          <el-button @click="supplementDialogVisible = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </FormDialog>
+    <el-dialog v-model="supplementRecordDialogVisible"
+               title="琛ユ枡璁板綍"
+               width="900px">
+      <el-table v-loading="supplementRecordLoading"
+                :data="supplementRecordTableData"
+                border
+                row-key="id">
+        <el-table-column label="琛ユ枡鏁伴噺"
+                         prop="supplementQty"
+                         min-width="100" />
+        <el-table-column label="琛ユ枡鍘熷洜"
+                         prop="supplementReason"
+                         min-width="200" />
+        <el-table-column label="琛ユ枡浜�"
+                         prop="supplementUserName"
+                         min-width="120" />
+        <el-table-column label="琛ユ枡鏃ユ湡"
+                         prop="supplementTime"
+                         min-width="160" />
+      </el-table>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="supplementRecordDialogVisible = false">鍏抽棴</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+  import { computed, nextTick, reactive, ref, watch } from "vue";
+  import { ElMessage } from "element-plus";
+  import FormDialog from "@/components/Dialog/FormDialog.vue";
+  import {
+    listWorkOrderMaterialLedger,
+    addWorkOrderMaterialSupplement,
+    listWorkOrderMaterialSupplementRecord,
+    pickWorkOrderMaterial,
+  } from "@/api/productionManagement/workOrder.js";
+
+  const props = defineProps({
+    modelValue: {
+      type: Boolean,
+      default: false,
+    },
+    rowData: {
+      type: Object,
+      default: () => null,
+    },
+  });
+
+  const emit = defineEmits(["update:modelValue", "refresh"]);
+
+  const dialogVisible = computed({
+    get: () => props.modelValue,
+    set: val => emit("update:modelValue", val),
+  });
+
+  const materialTableLoading = ref(false);
+  const materialTableData = ref([]);
+  const currentMaterialRow = ref(null);
+  const currentMaterialOrderRow = ref(null);
+  const pickSubmitting = ref(false);
+
+  const supplementDialogVisible = ref(false);
+  const supplementSubmitting = ref(false);
+  const supplementFormRef = ref(null);
+  const supplementForm = reactive({
+    supplementQty: null,
+    supplementReason: "",
+  });
+
+  const supplementRecordDialogVisible = ref(false);
+  const supplementRecordLoading = ref(false);
+  const supplementRecordTableData = ref([]);
+
+  const supplementRules = {
+    supplementQty: [
+      { required: true, message: "璇疯緭鍏ヨˉ鏂欐暟閲�", trigger: "blur" },
+    ],
+    supplementReason: [
+      { required: true, message: "璇疯緭鍏ヨˉ鏂欏師鍥�", trigger: "blur" },
+    ],
+  };
+  const loadMaterialTable = async row => {
+    if (!row?.id) return;
+    currentMaterialOrderRow.value = row;
+    materialTableLoading.value = true;
+    materialTableData.value = [];
+    try {
+      const res = await listWorkOrderMaterialLedger({
+        workOrderId: row.id,
+        processId: row.processId,
+        productProcessRouteItemId: row.productProcessRouteItemId,
+      });
+      materialTableData.value = res.data || [];
+    } catch (e) {
+      console.error("鑾峰彇鐗╂枡鍙拌处澶辫触", e);
+      ElMessage.error("鑾峰彇鐗╂枡鍙拌处澶辫触");
+    } finally {
+      materialTableLoading.value = false;
+    }
+  };
+
+  watch(
+    () => props.modelValue,
+    visible => {
+      if (visible && props.rowData) {
+        loadMaterialTable(props.rowData);
+      }
+    }
+  );
+
+  const handleCloseMaterialDialog = () => {
+    materialTableData.value = [];
+    currentMaterialRow.value = null;
+    currentMaterialOrderRow.value = null;
+  };
+
+  const openSupplementDialog = row => {
+    currentMaterialRow.value = row;
+    supplementForm.supplementQty = null;
+    supplementForm.supplementReason = "";
+    supplementDialogVisible.value = true;
+    nextTick(() => {
+      supplementFormRef.value?.clearValidate();
+    });
+  };
+
+  const handleSubmitSupplement = () => {
+    supplementFormRef.value?.validate(async valid => {
+      if (!valid || !currentMaterialRow.value?.id) {
+        ElMessage.warning("缂哄皯鐗╂枡鏄庣粏ID");
+        return;
+      }
+      supplementSubmitting.value = true;
+      try {
+        await addWorkOrderMaterialSupplement({
+          materialLedgerId: currentMaterialRow.value.id,
+          supplementQty: Number(supplementForm.supplementQty),
+          supplementReason: supplementForm.supplementReason,
+          workOrderId: currentMaterialOrderRow.value?.id,
+        });
+        supplementDialogVisible.value = false;
+        await loadMaterialTable(currentMaterialOrderRow.value);
+        ElMessage.success("琛ユ枡鎴愬姛");
+        emit("refresh");
+      } catch (e) {
+        console.error("琛ユ枡澶辫触", e);
+        ElMessage.error("琛ユ枡澶辫触");
+      } finally {
+        supplementSubmitting.value = false;
+      }
+    });
+  };
+
+  const openSupplementRecordDialog = async row => {
+    supplementRecordDialogVisible.value = true;
+    supplementRecordLoading.value = true;
+    supplementRecordTableData.value = [];
+    try {
+      const res = await listWorkOrderMaterialSupplementRecord({
+        materialLedgerId: row.id,
+      });
+      supplementRecordTableData.value = res.data || [];
+    } catch (e) {
+      console.error("鑾峰彇琛ユ枡璁板綍澶辫触", e);
+      ElMessage.error("鑾峰彇琛ユ枡璁板綍澶辫触");
+    } finally {
+      supplementRecordLoading.value = false;
+    }
+  };
+
+  const validatePickRows = () => {
+    if (materialTableData.value.length === 0) {
+      return { valid: false, message: "鏆傛棤鍙鐢ㄧ墿鏂�" };
+    }
+    const invalidRow = materialTableData.value.find(
+      item =>
+        item.actualQty === null ||
+        item.actualQty === undefined ||
+        item.actualQty === ""
+    );
+    if (invalidRow) {
+      return { valid: false, message: "璇峰~鍐欏疄闄呮暟閲忓悗鍐嶉鐢�" };
+    }
+    const exceedRow = materialTableData.value.find(item => {
+      const maxQty = Number(item.pickQty || 0) + Number(item.supplementQty || 0);
+      return Number(item.actualQty || 0) > maxQty;
+    });
+    if (exceedRow) {
+      return { valid: false, message: "瀹為檯鏁伴噺涓嶈兘澶т簬棰嗙敤鏁伴噺+琛ユ枡鏁伴噺" };
+    }
+    return { valid: true, message: "" };
+  };
+
+  const handleSubmitPick = async () => {
+    if (!currentMaterialOrderRow.value?.id) return;
+    const validateResult = validatePickRows();
+    if (!validateResult.valid) {
+      ElMessage.warning(validateResult.message);
+      return;
+    }
+    pickSubmitting.value = true;
+    try {
+      await pickWorkOrderMaterial({
+        workOrderId: currentMaterialOrderRow.value.id,
+        items: materialTableData.value.map(item => ({
+          materialLedgerId: item.id,
+          actualQty: Number(item.actualQty || 0),
+        })),
+      });
+      ElMessage.success("棰嗙敤鎴愬姛");
+      await loadMaterialTable(currentMaterialOrderRow.value);
+      emit("refresh");
+    } catch (e) {
+      console.error("棰嗙敤澶辫触", e);
+      ElMessage.error("棰嗙敤澶辫触");
+    } finally {
+      pickSubmitting.value = false;
+    }
+  };
+</script>
diff --git a/src/views/productionManagement/workOrderManagement/index.vue b/src/views/productionManagement/workOrderManagement/index.vue
index 6def04c..600b274 100644
--- a/src/views/productionManagement/workOrderManagement/index.vue
+++ b/src/views/productionManagement/workOrderManagement/index.vue
@@ -1,10 +1,19 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form mb20">
       <div class="search-row">
         <div class="search-item">
           <span class="search_title">宸ュ崟缂栧彿锛�</span>
           <el-input v-model="searchForm.workOrderNo"
+                    style="width: 240px"
+                    placeholder="璇疯緭鍏�"
+                    @change="handleQuery"
+                    clearable
+                    prefix-icon="Search" />
+        </div>
+        <div class="search-item">
+          <span class="search_title">鐢熶骇璁㈠崟鍙凤細</span>
+          <el-input v-model="searchForm.npsNo"
                     style="width: 240px"
                     placeholder="璇疯緭鍏�"
                     @change="handleQuery"
@@ -31,7 +40,6 @@
         </template>
       </PIMTable>
     </div>
-    
     <!-- 娴佽浆鍗″脊绐� -->
     <el-dialog v-model="transferCardVisible"
                title="娴佽浆鍗�"
@@ -107,7 +115,6 @@
                    @click="printTransferCard">鎵撳嵃娴佽浆鍗�</el-button>
       </div>
     </el-dialog>
-    
     <!-- 鎶ュ伐寮圭獥 -->
     <el-dialog v-model="reportDialogVisible"
                title="鎶ュ伐"
@@ -121,14 +128,14 @@
                     readonly
                     style="width: 300px" />
         </el-form-item>
-        <el-form-item label="鏈鐢熶骇鏁伴噺"
+        <el-form-item label="鐢熶骇鍚堟牸鏁伴噺"
                       prop="quantity">
           <el-input v-model.number="reportForm.quantity"
                     type="number"
-                    min="1"
+                    min="0"
                     step="1"
                     style="width: 300px"
-                    placeholder="璇疯緭鍏ユ湰娆$敓浜ф暟閲�"
+                    placeholder="璇疯緭鍏ョ敓浜у悎鏍兼暟閲�"
                     @input="handleQuantityInput" />
         </el-form-item>
         <el-form-item label="鎶ュ簾鏁伴噺"
@@ -154,6 +161,75 @@
                        :value="user.userId" />
           </el-select>
         </el-form-item>
+        <div v-if="params.length > 0"
+             class="param-grid"
+             v-loading="paramLoading">
+          <el-form-item v-for="param in params"
+                        :key="param.id"
+                        :label="param.paramName"
+                        :label-width="120"
+                        class="param-item">
+            <template v-if="param.paramType == '1'">
+              <div class="param-input-group">
+                <el-input-number v-model="reportForm.paramGroups[param.id]"
+                                 controls-position="right"
+                                 :key="param.id"
+                                 style="width: 250px"
+                                 class="param-input" />
+                <span v-if="param.unit && param.unit != '/'"
+                      class="param-unit">{{ param.unit }}</span>
+              </div>
+            </template>
+            <template v-else-if="param.paramType == '2'">
+              <div class="param-input-group">
+                <el-input v-model="reportForm.paramGroups[param.id]"
+                          :key="param.id"
+                          style="width: 250px"
+                          class="param-input" />
+                <span v-if="param.unit && param.unit != '/'"
+                      class="param-unit">{{ param.unit }}</span>
+              </div>
+            </template>
+            <template v-else-if="param.paramType == '3'">
+              <div class="param-input-group">
+                <el-select v-model="reportForm.paramGroups[param.id]"
+                           placeholder="璇烽�夋嫨"
+                           :key="param.id"
+                           class="param-select"
+                           style="width: 250px">
+                  <el-option v-for="option in dictOptions[param.paramFormat] || []"
+                             :key="option.dictLabel"
+                             :label="option.dictLabel"
+                             :value="option.dictLabel" />
+                </el-select>
+                <span v-if="param.unit && param.unit != '/'"
+                      class="param-unit">{{ param.unit }}</span>
+              </div>
+            </template>
+            <template v-else-if="param.paramType == '4'">
+              <div class="param-input-group">
+                <el-date-picker :value-format="param.paramFormat.replace('yyyy', 'YYYY').replace('dd', 'DD')"
+                                :format="param.paramFormat.replace('yyyy', 'YYYY').replace('dd', 'DD')"
+                                :key="param.id"
+                                :type="param.paramFormat=='yyyy-MM-dd'?'date':'datetime'"
+                                v-model="reportForm.paramGroups[param.id]"
+                                class="param-input"
+                                style="width: 250px" />
+                <span v-if="param.unit && param.unit != '/'"
+                      class="param-unit">{{ param.unit }}</span>
+              </div>
+            </template>
+            <template v-else>
+              <div class="param-input-group">
+                <el-input v-model="reportForm.paramGroups[param.id]"
+                          :key="param.id"
+                          class="param-input" />
+                <span v-if="param.unit && param.unit != '/'"
+                      class="param-unit">{{ param.unit }}</span>
+              </div>
+            </template>
+          </el-form-item>
+        </div>
       </el-form>
       <template #footer>
         <span class="dialog-footer">
@@ -163,8 +239,14 @@
         </span>
       </template>
     </el-dialog>
-    
-    <FilesDia ref="workOrderFilesRef" />
+    <MaterialDialog v-model="materialDialogVisible"
+                    :row-data="currentMaterialOrderRow"
+                    @refresh="getList" />
+    <FileList v-if="fileDialogVisible"
+              v-model:visible="fileDialogVisible"
+              :editable="!currentWorkOrderRow?.endOrder"
+              :record-type="'production_operation_task'"
+              :record-id="currentWorkOrderId" />
   </div>
 </template>
 
@@ -177,11 +259,19 @@
     addProductMain,
     downProductWorkOrder,
   } from "@/api/productionManagement/workOrder.js";
+  import { findProcessParamListOrder } from "@/api/productionManagement/productProcessRoute.js";
   import { getUserProfile, userListNoPageByTenantId } from "@/api/system/user.js";
+  import { getDicts } from "@/api/system/dict/data";
   import QRCode from "qrcode";
   import { getCurrentInstance, reactive, toRefs } from "vue";
-  import FilesDia from "./components/filesDia.vue";
+  import MaterialDialog from "./components/MaterialDialog.vue";
+  const FileList = defineAsyncComponent(() =>
+    import("@/components/Dialog/FileList.vue")
+  );
+
+  import useUserStore from "@/store/modules/user";
   const { proxy } = getCurrentInstance();
+  const userStore = useUserStore();
 
   const tableColumn = ref([
     {
@@ -196,7 +286,7 @@
     },
     {
       label: "鐢熶骇璁㈠崟鍙�",
-      prop: "productOrderNpsNo",
+      prop: "npsNo",
       width: "140",
     },
     {
@@ -214,7 +304,8 @@
     },
     {
       label: "宸ュ簭鍚嶇О",
-      prop: "processName",
+      prop: "operationName",
+      width: "100",
     },
     {
       label: "闇�姹傛暟閲�",
@@ -255,7 +346,7 @@
     },
     {
       label: "鎿嶄綔",
-      width: "200",
+      width: "260",
       align: "center",
       dataType: "action",
       fixed: "right",
@@ -272,17 +363,38 @@
             openWorkOrderFiles(row);
           },
         },
+        // {
+        //   name: "鐗╂枡",
+        //   clickFun: row => {
+        //     openMaterialDialog(row);
+        //   },
+        // },
         {
           name: "鎶ュ伐",
           clickFun: row => {
             showReportDialog(row);
           },
-          disabled: row => row.planQuantity <= 0,
+          showHide: row => !row.endOrder,
+          disabled: row => {
+            if (row.planQuantity <= 0) return true;
+            if (!row.userIds) return false;
+            try {
+              const userIds =
+                typeof row.userIds === "string"
+                  ? JSON.parse(row.userIds)
+                  : row.userIds;
+              if (Array.isArray(userIds)) {
+                return !userIds.some(id => String(id) === String(userStore.id));
+              }
+              return true;
+            } catch (e) {
+              return true;
+            }
+          },
         },
       ],
     },
   ]);
-  
   const tableData = ref([]);
   const tableLoading = ref(false);
   const transferCardVisible = ref(false);
@@ -290,7 +402,8 @@
   const transferCardQrUrl = ref("");
   const transferCardRowData = ref(null);
   const reportDialogVisible = ref(false);
-  const workOrderFilesRef = ref(null);
+  const fileDialogVisible = ref(false);
+  const currentWorkOrderId = ref(null);
   const reportFormRef = ref(null);
   const userOptions = ref([]);
   const reportForm = reactive({
@@ -303,18 +416,25 @@
     productProcessRouteItemId: "",
     userId: "",
     productMainId: null,
+    productionOrderRoutingOperationId: "",
+    productionOrderId: "",
+    paramGroups: {},
   });
 
-  // 鏈鐢熶骇鏁伴噺楠岃瘉瑙勫垯
+  const params = ref({});
+  const dictOptions = ref({});
+  const paramLoading = ref(false);
+
+  // 鐢熶骇鍚堟牸鏁伴噺楠岃瘉瑙勫垯
   const validateQuantity = (rule, value, callback) => {
     if (value === null || value === undefined || value === "") {
-      callback(new Error("璇疯緭鍏ユ湰娆$敓浜ф暟閲�"));
+      callback(new Error("璇疯緭鍏ョ敓浜у悎鏍兼暟閲�"));
       return;
     }
     const num = Number(value);
     // 鏁存暟涓斿ぇ浜庣瓑浜�1
-    if (isNaN(num) || !Number.isInteger(num) || num < 1) {
-      callback(new Error("鏈鐢熶骇鏁伴噺蹇呴』澶т簬绛変簬1"));
+    if (isNaN(num) || !Number.isInteger(num) || num < 0) {
+      callback(new Error("鐢熶骇鍚堟牸鏁伴噺蹇呴』澶т簬绛変簬0"));
       return;
     }
     callback();
@@ -341,7 +461,7 @@
     scrapQty: [{ validator: validateScrapQty, trigger: "blur" }],
   };
 
-  // 澶勭悊鏈鐢熶骇鏁伴噺杈撳叆锛岄檺鍒跺繀椤诲ぇ浜庣瓑浜�1
+  // 澶勭悊鐢熶骇鍚堟牸鏁伴噺杈撳叆锛岄檺鍒跺繀椤诲ぇ浜庣瓑浜�0
   const handleQuantityInput = value => {
     if (value === "" || value === null || value === undefined) {
       reportForm.quantity = null;
@@ -352,7 +472,7 @@
       return;
     }
     // 濡傛灉灏忎簬1锛屾竻闄�
-    if (num < 1) {
+    if (num < 0) {
       reportForm.quantity = null;
       return;
     }
@@ -360,7 +480,7 @@
     if (!Number.isInteger(num)) {
       const intValue = Math.floor(num);
       // 濡傛灉鍙栨暣鍚庡皬浜�1锛屾竻闄�
-      if (intValue < 1) {
+      if (intValue < 0) {
         reportForm.quantity = null;
         return;
       }
@@ -394,8 +514,10 @@
     // 鏈夋晥鐨勯潪璐熸暣鏁帮紙鍖呮嫭0锛�
     reportForm.scrapQty = num;
   };
-  
+
   const currentReportRowData = ref(null);
+  const materialDialogVisible = ref(false);
+  const currentMaterialOrderRow = ref(null);
   const page = reactive({
     current: 1,
     size: 100,
@@ -405,6 +527,7 @@
   const data = reactive({
     searchForm: {
       workOrderNo: "",
+      npsNo: "",
     },
   });
   const { searchForm } = toRefs(data);
@@ -429,13 +552,13 @@
     page.current = 1;
     getList();
   };
-  
+
   const pagination = obj => {
     page.current = obj.page;
     page.size = obj.limit;
     getList();
   };
-  
+
   const getList = () => {
     tableLoading.value = true;
     const params = { ...searchForm.value, ...page };
@@ -513,9 +636,12 @@
   const printTransferCard = () => {
     window.print();
   };
+  const currentWorkOrderRow = ref(null);
 
   const openWorkOrderFiles = row => {
-    workOrderFilesRef.value?.openDialog(row);
+    currentWorkOrderId.value = row.id;
+    currentWorkOrderRow.value = row;
+    fileDialogVisible.value = true;
   };
 
   const showReportDialog = row => {
@@ -529,10 +655,15 @@
     reportForm.productMainId = row.productMainId;
     reportForm.scrapQty =
       row.scrapQty !== undefined && row.scrapQty !== null ? row.scrapQty : null;
+    reportForm.productionOrderRoutingOperationId =
+      row.productionOrderRoutingOperationId;
+    reportForm.productionOrderId = row.productionOrderId;
     nextTick(() => {
       reportFormRef.value?.clearValidate();
+      if (row.productionOrderRoutingOperationId && row.productionOrderId) {
+        loadParams(row.productionOrderRoutingOperationId, row.productionOrderId);
+      }
     });
-    // 鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛淇℃伅锛岃缃负榛樿閫変腑
     getUserProfile()
       .then(res => {
         if (res.code === 200) {
@@ -545,6 +676,11 @@
       });
 
     reportDialogVisible.value = true;
+  };
+
+  const openMaterialDialog = row => {
+    currentMaterialOrderRow.value = row;
+    materialDialogVisible.value = true;
   };
 
   const handleReport = () => {
@@ -560,13 +696,13 @@
         return;
       }
 
-      // 楠岃瘉鏈鐢熶骇鏁伴噺
+      // 楠岃瘉鐢熶骇鍚堟牸鏁伴噺
       if (
         reportForm.quantity === null ||
         reportForm.quantity === undefined ||
         reportForm.quantity === ""
       ) {
-        ElMessageBox.alert("璇疯緭鍏ユ湰娆$敓浜ф暟閲�", "鎻愮ず", {
+        ElMessageBox.alert("璇疯緭鍏ョ敓浜у悎鏍兼暟閲�", "鎻愮ず", {
           confirmButtonText: "纭畾",
         });
         return;
@@ -574,19 +710,19 @@
 
       const quantity = Number(reportForm.quantity);
 
-      if (isNaN(quantity) || quantity <= 0) {
-        ElMessageBox.alert("鏈鐢熶骇鏁伴噺蹇呴』澶т簬0", "鎻愮ず", {
+      if (isNaN(quantity) || quantity < 0) {
+        ElMessageBox.alert("鐢熶骇鍚堟牸鏁伴噺蹇呴』澶т簬绛変簬0", "鎻愮ず", {
           confirmButtonText: "纭畾",
         });
         return;
       }
 
-      if (quantity > reportForm.planQuantity) {
-        ElMessageBox.alert("鏈鐢熶骇鏁伴噺涓嶈兘瓒呰繃寰呯敓浜ф暟閲�", "鎻愮ず", {
-          confirmButtonText: "纭畾",
-        });
-        return;
-      }
+      // if (quantity > reportForm.planQuantity) {
+      //   ElMessageBox.alert("鏈鐢熶骇鏁伴噺涓嶈兘瓒呰繃寰呯敓浜ф暟閲�", "鎻愮ず", {
+      //     confirmButtonText: "纭畾",
+      //   });
+      //   return;
+      // }
 
       // 楠岃瘉鎶ュ簾鏁伴噺
       const scrapQty = Number(reportForm.scrapQty);
@@ -597,25 +733,34 @@
         return;
       }
 
-      if (!isNaN(scrapQty) && scrapQty > quantity) {
-        ElMessageBox.alert("鎶ュ簾鏁伴噺涓嶈兘澶т簬鏈鐢熶骇鏁伴噺", "鎻愮ず", {
-          confirmButtonText: "纭畾",
-        });
-        return;
-      }
+      // if (!isNaN(scrapQty) && scrapQty > quantity) {
+      //   ElMessageBox.alert("鎶ュ簾鏁伴噺涓嶈兘澶т簬鏈鐢熶骇鏁伴噺", "鎻愮ず", {
+      //     confirmButtonText: "纭畾",
+      //   });
+      //   return;
+      // }
 
-      const params = {
+      const productionOperationParamList = params.value.map(param => ({
+        ...param,
+        inputValue: reportForm.paramGroups[param.id] ?? "",
+      }));
+
+      const submitParams = {
         quantity: quantity,
         scrapQty: isNaN(scrapQty) ? 0 : scrapQty,
         userId: reportForm.userId,
         userName: reportForm.userName,
-        workOrderId: reportForm.workOrderId,
+        productionOperationTaskId: reportForm.workOrderId,
         productProcessRouteItemId: reportForm.productProcessRouteItemId,
         reportWork: reportForm.reportWork,
         productMainId: reportForm.productMainId,
+        productionOrderRoutingOperationId:
+          reportForm.productionOrderRoutingOperationId,
+        productionOrderId: reportForm.productionOrderId,
+        productionOperationParamList: productionOperationParamList,
       };
 
-      addProductMain(params)
+      addProductMain(submitParams)
         .then(res => {
           proxy.$modal.msgSuccess("鎶ュ伐鎴愬姛");
           reportDialogVisible.value = false;
@@ -634,7 +779,53 @@
     reportForm.userName = user ? user.nickName : "";
   };
 
+  const getDictOptions = async dictType => {
+    if (!dictType) return [];
+    if (dictOptions.value[dictType]) return dictOptions.value[dictType];
+    try {
+      const res = await getDicts(dictType);
+      if (res.code === 200) {
+        dictOptions.value[dictType] = res.data;
+        return res.data;
+      }
+      return [];
+    } catch (error) {
+      console.error("鑾峰彇瀛楀吀鏁版嵁澶辫触:", error);
+      return [];
+    }
+  };
+
+  const loadParams = (productionOrderRoutingOperationId, productionOrderId) => {
+    paramLoading.value = true;
+    findProcessParamListOrder({
+      productionOrderRoutingOperationId,
+      productionOrderId,
+    })
+      .then(res => {
+        if (res.code === 200) {
+          const paramList = res.data || [];
+          params.value = paramList;
+          reportForm.paramGroups = {};
+          paramList.forEach(param => {
+            if (!reportForm.paramGroups[param.id]) {
+              reportForm.paramGroups[param.id] = "";
+            }
+            if (param.paramType == "3" && param.paramFormat) {
+              getDictOptions(param.paramFormat);
+            }
+          });
+        }
+      })
+      .catch(err => {
+        console.error("鑾峰彇宸ュ簭鍙傛暟澶辫触:", err);
+      })
+      .finally(() => {
+        paramLoading.value = false;
+      });
+  };
+
   onMounted(() => {
+    userStore.getInfo();
     getList();
     // 鑾峰彇鐢ㄦ埛鍒楄〃
     userListNoPageByTenantId().then(res => {
@@ -703,6 +894,30 @@
   .print-button-container {
     text-align: center;
     margin-top: 20px;
+  }
+  .param-grid {
+    margin-top: 10px;
+    border-top: 1px solid #ebe9f3;
+    padding-top: 10px;
+  }
+  .param-item {
+    margin-bottom: 12px;
+  }
+  .param-input-group {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+  }
+  .param-input {
+    flex: 1;
+  }
+  .param-select {
+    flex: 1;
+  }
+  .param-unit {
+    color: #909399;
+    font-size: 12px;
+    min-width: 30px;
   }
 </style>
 
@@ -786,4 +1001,4 @@
       height: 140px !important;
     }
   }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/views/productionPlan/productionPlan/components/PIMTable.vue b/src/views/productionPlan/productionPlan/components/PIMTable.vue
new file mode 100644
index 0000000..9431002
--- /dev/null
+++ b/src/views/productionPlan/productionPlan/components/PIMTable.vue
@@ -0,0 +1,470 @@
+<template>
+  <el-table ref="multipleTable"
+            v-loading="tableLoading"
+            :border="border"
+            :data="tableData"
+            :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
+            :height="height"
+            :highlight-current-row="highlightCurrentRow"
+            :row-class-name="rowClassName"
+            :row-style="rowStyle"
+            :row-key="rowKey"
+            :style="tableStyle"
+            tooltip-effect="dark"
+            :expand-row-keys="expandRowKeys"
+            :show-summary="isShowSummary"
+            :summary-method="summaryMethod"
+            @row-click="rowClick"
+            @current-change="currentChange"
+            @selection-change="handleSelectionChange"
+            @expand-change="expandChange"
+            @select-all="handleSelectAll"
+            class="lims-table">
+    <el-table-column align="center"
+                     type="selection"
+                     width="55"
+                     v-if="isSelection"
+                     :selectable="selectable" />
+    <el-table-column align="center"
+                     label="搴忓彿"
+                     type="index"
+                     width="60" />
+    <el-table-column v-for="(item, index) in column"
+                     :key="index"
+                     :column-key="item.columnKey"
+                     :filter-method="item.filterHandler"
+                     :filter-multiple="item.filterMultiple"
+                     :filtered-value="item.filteredValue"
+                     :filters="item.filters"
+                     :fixed="item.fixed"
+                     :label="item.label"
+                     :prop="item.prop"
+                     :show-overflow-tooltip="item.dataType !== 'action' && item.dataType !== 'slot'"
+                     :align="item.align"
+                     :sortable="!!item.sortable"
+                     :type="item.type"
+                     :width="item.width"
+                     :class-name="item.className || ''">
+      <template #header="scope">
+        <div class="pim-table-header-cell">
+          <div class="pim-table-header-title">
+            {{ item.label }}
+          </div>
+          <div v-if="item.headerSlot"
+               class="pim-table-header-extra">
+            <slot :name="item.headerSlot"
+                  :column="scope.column" />
+          </div>
+        </div>
+      </template>
+      <template v-if="item.hasOwnProperty('colunmTemplate')"
+                #[item.colunmTemplate]="scope">
+        <slot v-if="item.theadSlot"
+              :name="item.theadSlot"
+              :index="scope.$index"
+              :row="scope.row" />
+      </template>
+      <template #default="scope">
+        <!-- 鎻掓Ы -->
+        <div v-if="item.dataType == 'slot'"
+             :class="item.className || ''">
+          <slot v-if="item.slot"
+                :index="scope.$index"
+                :name="item.slot"
+                :row="scope.row" />
+        </div>
+        <!-- 杩涘害鏉� -->
+        <div v-else-if="item.dataType == 'progress'"
+             :class="item.className || ''">
+          <el-progress :percentage="Number(scope.row[item.prop])" />
+        </div>
+        <!-- 鍥剧墖 -->
+        <div v-else-if="item.dataType == 'image'"
+             :class="item.className || ''">
+          <img :src="javaApi + '/img/' + scope.row[item.prop]"
+               alt=""
+               style="width: 40px; height: 40px; margin-top: 10px" />
+        </div>
+        <!-- tag -->
+        <div v-else-if="item.dataType == 'tag'"
+             :class="item.className || ''">
+          <el-tag v-if="
+              typeof dataTypeFn(scope.row[item.prop], item.formatData) ===
+              'string'
+            "
+                  :title="formatters(scope.row[item.prop], item.formatData)"
+                  :type="formatType(scope.row[item.prop], item.formatType)">
+            {{ formatters(scope.row[item.prop], item.formatData) }}
+          </el-tag>
+          <el-tag v-for="(tag, index) in dataTypeFn(
+              scope.row[item.prop],
+              item.formatData
+            )"
+                  v-else-if="
+              typeof dataTypeFn(scope.row[item.prop], item.formatData) ===
+              'object'
+            "
+                  :key="index"
+                  :title="formatters(scope.row[item.prop], item.formatData)"
+                  :type="formatType(tag, item.formatType)">
+            {{ item.tagGroup ? tag[item.tagGroup.label] ?? tag : tag }}
+          </el-tag>
+          <el-tag v-else
+                  :title="formatters(scope.row[item.prop], item.formatData)"
+                  :type="formatType(scope.row[item.prop], item.formatType)">
+            {{ formatters(scope.row[item.prop], item.formatData) }}
+          </el-tag>
+        </div>
+        <!-- 鎸夐挳 -->
+        <div v-else-if="item.dataType == 'action'"
+             :class="item.className || ''"
+             @click.stop>
+          <template v-for="(o, key) in item.operation"
+                    :key="key">
+            <el-button v-show="o.type != 'upload'"
+                       v-if="o.showHide ? o.showHide(scope.row) : true"
+                       :disabled="o.disabled ? o.disabled(scope.row) : false"
+                       :plain="o.plain"
+                       type="primary"
+                       :style="{
+                color:
+                  o.name === '鍒犻櫎' || o.name === 'delete'
+                    ? '#f56c6c'
+                    : o.color,
+              }"
+                       link
+                       @click.stop="o.clickFun(scope.row)"
+                       :key="key">
+              {{ o.name }}
+            </el-button>
+            <el-upload :action="
+                javaApi +
+                o.url +
+                '?id=' +
+                (o.uploadIdFun ? o.uploadIdFun(scope.row) : scope.row.id)
+              "
+                       ref="uploadRef"
+                       :multiple="o.multiple ? o.multiple : false"
+                       :limit="1"
+                       :disabled="o.disabled ? o.disabled(scope.row) : false"
+                       :accept="
+                o.accept
+                  ? o.accept
+                  : '.jpg,.jpeg,.png,.gif,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip,.rar'
+              "
+                       v-if="o.type == 'upload'"
+                       style="display: inline-block; width: 50px"
+                       v-show="o.showHide ? o.showHide(scope.row) : true"
+                       :headers="uploadHeader"
+                       :before-upload="(file) => beforeUpload(file, scope.$index)"
+                       :on-change="
+                (file, fileList) => handleChange(file, fileList, scope.$index)
+              "
+                       :on-error="
+                (error, file, fileList) =>
+                  onError(error, file, fileList, scope.$index)
+              "
+                       :on-success="
+                (response, file, fileList) =>
+                  handleSuccessUp(response, file, fileList, scope.$index)
+              "
+                       :on-exceed="onExceed"
+                       :show-file-list="false">
+              <el-button link
+                         type="primary"
+                         :disabled="o.disabled ? o.disabled(scope.row) : false">{{ o.name }}</el-button>
+            </el-upload>
+          </template>
+        </div>
+        <!-- 鍙偣鍑荤殑鏂囧瓧 -->
+        <div v-else-if="item.dataType == 'link'"
+             :class="item.className || ''"
+             class="cell link"
+             style="width: 100%"
+             @click="goLink(scope.row, item.linkMethod)">
+          <span v-if="!item.formatData">{{ scope.row[item.prop] }}</span>
+        </div>
+        <!-- 榛樿绾睍绀烘暟鎹� -->
+        <div v-else
+             class="cell"
+             :class="item.className || ''"
+             style="width: 100%">
+          <span v-if="!item.formatData">{{ scope.row[item.prop] }}</span>
+          <span v-else>{{
+            formatters(scope.row[item.prop], item.formatData)
+          }}</span>
+        </div>
+      </template>
+    </el-table-column>
+  </el-table>
+  <pagination v-if="isShowPagination"
+              :total="page.total"
+              :layout="page.layout"
+              :page="page.current"
+              :limit="page.size"
+              @pagination="paginationSearch" />
+</template>
+
+<script setup>
+  import pagination from "../../../../components/PIMTable/Pagination.vue";
+  import { ref, inject, getCurrentInstance } from "vue";
+  import { ElMessage } from "element-plus";
+
+  // 鑾峰彇鍏ㄥ眬鐨� uploadHeader
+  const { proxy } = getCurrentInstance();
+  const uploadHeader = proxy.uploadHeader;
+  const javaApi = proxy.javaApi;
+
+  const emit = defineEmits([
+    "pagination",
+    "expand-change",
+    "selection-change",
+    "row-click",
+  ]);
+
+  // Filters
+  const typeFn = (val, row) => {
+    return typeof val === "function" ? val(row) : val;
+  };
+
+  const formatters = (val, format) => {
+    return typeof format === "function" ? format(val) : val;
+  };
+
+  // Props锛堜娇鐢� defineProps 鐨勯潪 TS 褰㈠紡锛�
+  const props = defineProps({
+    tableLoading: {
+      type: Boolean,
+      default: false,
+    },
+    height: {
+      type: [Number, String],
+      default: "calc(100vh - 22em)",
+    },
+    expandRowKeys: {
+      type: Array,
+      default: () => [],
+    },
+    summaryMethod: {
+      type: Function,
+      default: () => {},
+    },
+    rowClick: {
+      type: Function,
+      default: () => {},
+    },
+    currentChange: {
+      type: Function,
+      default: () => {},
+    },
+    border: {
+      type: Boolean,
+      default: true,
+    },
+    isSelection: {
+      type: Boolean,
+      default: false,
+    },
+    selectable: {
+      type: Function,
+      default: () => true,
+    },
+    isShowPagination: {
+      type: Boolean,
+      default: true,
+    },
+    isShowSummary: {
+      type: Boolean,
+      default: false,
+    },
+    highlightCurrentRow: {
+      type: Boolean,
+      default: false,
+    },
+    headerCellStyle: {
+      type: Object,
+      default: () => ({}),
+    },
+    column: {
+      type: Array,
+      default: () => [],
+    },
+    rowClassName: {
+      type: Function,
+      default: () => "",
+    },
+    rowStyle: {
+      type: [Object, Function],
+      default: () => ({}),
+    },
+    tableData: {
+      type: Array,
+      default: () => [],
+    },
+    rowKey: {
+      type: String,
+      default: "id",
+    },
+    page: {
+      type: Object,
+      default: () => ({
+        total: 0,
+        current: 0,
+        size: 10,
+        layout: "total, sizes, prev, pager, next, jumper",
+      }),
+    },
+    total: {
+      type: Number,
+      default: 0,
+    },
+    tableStyle: {
+      type: [String, Object],
+      default: () => ({ width: "100%" }),
+    },
+  });
+
+  // Data
+  const multipleTable = ref(null);
+  const uploadRefs = ref([]);
+  const currentFiles = ref({});
+  const uploadKeys = ref({});
+
+  const indexMethod = index => {
+    return (props.page.current - 1) * props.page.size + index + 1;
+  };
+
+  // 鐐瑰嚮 link 浜嬩欢
+  const goLink = (row, linkMethod) => {
+    if (!linkMethod) {
+      return ElMessage.warning("璇烽厤缃� link 浜嬩欢");
+    }
+    const parentMethod = getParentMethod(linkMethod);
+    if (typeof parentMethod === "function") {
+      parentMethod(row);
+    } else {
+      console.warn(`鐖剁粍浠朵腑鏈壘鍒版柟娉�: ${linkMethod}`);
+    }
+  };
+
+  // 鑾峰彇鐖剁粍浠舵柟娉曪紙绀轰緥瀹炵幇锛�
+  const getParentMethod = methodName => {
+    const parentMethods = inject("parentMethods", {});
+    return parentMethods[methodName];
+  };
+
+  const dataTypeFn = (val, format) => {
+    if (typeof format === "function") {
+      return format(val);
+    } else return val;
+  };
+
+  const formatType = (val, format) => {
+    if (typeof format === "function") {
+      return format(val);
+    } else return "";
+  };
+
+  // 鏂囦欢鍙樺寲澶勭悊
+  const handleChange = (file, fileList, index) => {
+    if (fileList.length > 1) {
+      const earliestFile = fileList[0];
+      uploadRefs.value[index]?.handleRemove(earliestFile);
+    }
+    currentFiles.value[index] = file;
+  };
+
+  // 鏂囦欢涓婁紶鍓嶆牎楠�
+  const beforeUpload = (rawFile, index) => {
+    currentFiles.value[index] = {};
+    if (rawfile.size > 1024 * 1024 * 10 * 10) {
+      ElMessage.error("涓婁紶鏂囦欢涓嶈秴杩�10M");
+      return false;
+    }
+    return true;
+  };
+
+  // 涓婁紶鎴愬姛
+  const handleSuccessUp = (response, file, fileList, index) => {
+    if (response.code == 200) {
+      if (uploadRefs[index]) {
+        uploadRefs[index].clearFiles();
+      }
+      currentFiles[index] = file;
+      ElMessage.success("涓婁紶鎴愬姛");
+      resetUploadComponent(index);
+    } else {
+      ElMessage.error(response.message);
+    }
+  };
+
+  const resetUploadComponent = index => {
+    uploadKeys[index] = Date.now();
+  };
+
+  // 涓婁紶澶辫触
+  const onError = (error, file, fileList, index) => {
+    ElMessage.error("鏂囦欢涓婁紶澶辫触锛岃閲嶈瘯");
+    if (uploadRefs.value[index]) {
+      uploadRefs.value[index].clearFiles();
+    }
+  };
+
+  // 鏂囦欢鏁伴噺瓒呴檺鎻愮ず
+  const onExceed = () => {
+    ElMessage.warning("瓒呭嚭鏂囦欢涓暟");
+  };
+
+  const paginationSearch = ({ page, limit }) => {
+    emit("pagination", { page: page, limit: limit });
+  };
+
+  const rowClick = row => {
+    emit("row-click", row);
+  };
+
+  const expandChange = (row, expandedRows) => {
+    emit("expand-change", row, expandedRows);
+  };
+
+  const handleSelectionChange = newSelection => {
+    emit("selection-change", newSelection);
+  };
+
+  // 澶勭悊鍏ㄩ�夋搷浣�
+  const handleSelectAll = selection => {
+    if (selection.length) {
+      console.log(selection, "selection");
+      // 鍏ㄩ�夋椂锛屽彧閫夋嫨鍙�夋嫨鐨勮
+      const selectableRows = props.tableData.filter(row => props.selectable(row));
+      // 娓呯┖褰撳墠閫夋嫨
+      multipleTable.value.clearSelection();
+      // 鍙�夋嫨鍙�夋嫨鐨勮
+      selectableRows.forEach(row => {
+        multipleTable.value.toggleRowSelection(row, true);
+      });
+    } else {
+      // 鍙栨秷鍏ㄩ�夋椂锛屽彧鍙栨秷鍙�夋嫨鐨勮鐨勯�夋嫨
+      props.tableData.forEach(row => {
+        if (props.selectable(row)) {
+          multipleTable.value.toggleRowSelection(row, false);
+        }
+      });
+    }
+  };
+</script>
+
+<style scoped lang="scss">
+  .cell {
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    padding-right: 0 !important;
+    padding-left: 0 !important;
+  }
+
+  .pim-table-header-extra :deep(.el-input),
+  .pim-table-header-extra :deep(.el-select) {
+    width: 100%;
+  }
+</style>
diff --git a/src/views/productionPlan/productionPlan/index.vue b/src/views/productionPlan/productionPlan/index.vue
new file mode 100644
index 0000000..46d0548
--- /dev/null
+++ b/src/views/productionPlan/productionPlan/index.vue
@@ -0,0 +1,1332 @@
+<template>
+  <div class="app-container">
+    <div class="search_form">
+      <el-form :model="searchForm"
+               ref="queryRef"
+               :inline="true">
+        <!-- 绠�鍖栫増鎼滅储鏉′欢 -->
+        <el-form-item label="涓荤敓浜ц鍒掑彿:"
+                      prop="mpsNo">
+          <el-input v-model="searchForm.mpsNo"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    style="width: 160px;"
+                    @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item label="闇�姹傛棩鏈熻寖鍥�:"
+                      prop="dateRange">
+          <el-date-picker v-model="searchForm.dateRange"
+                          type="daterange"
+                          range-separator="鑷�"
+                          start-placeholder="寮�濮嬫棩鏈�"
+                          end-placeholder="缁撴潫鏃ユ湡"
+                          value-format="YYYY-MM-DD"
+                          style="width: 240px;"
+                          @change="handleQuery" />
+        </el-form-item>
+        <el-form-item label="涓嬪彂鐘舵��:"
+                      prop="status">
+          <el-select v-model="searchForm.status"
+                     placeholder="璇烽�夋嫨鐘舵��"
+                     clearable
+                     filterable
+                     style="width: 100px">
+            <el-option label="寰呬笅鍙�"
+                       value="0" />
+            <el-option label="閮ㄥ垎涓嬪彂"
+                       value="1" />
+            <el-option label="宸蹭笅鍙�"
+                       value="2" />
+          </el-select>
+        </el-form-item>
+        <!-- 灞曞紑鐗堟悳绱㈡潯浠� -->
+        <el-form-item label="浜у搧鍚嶇О:"
+                      prop="productName">
+          <el-input v-model="searchForm.productName"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    style="width: 160px;"
+                    @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item label="浜у搧瑙勬牸:"
+                      prop="model">
+          <el-input v-model="searchForm.model"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    style="width: 160px;"
+                    @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary"
+                     @click="handleQuery">鎼滅储</el-button>
+          <el-button type="info"
+                     @click="handleReset">閲嶇疆</el-button>
+          <el-button type="primary"
+                     @click="handleAdd">鏂板</el-button>
+          <el-button type="warning"
+                     @click="handleMerge">鍚堝苟涓嬪彂</el-button>
+          <el-button type="warning"
+                     @click="handleImport">瀵煎叆</el-button>
+          <el-button type="warning"
+                     @click="handleExport">瀵煎嚭</el-button>
+        </el-form-item>
+      </el-form>
+      <div>
+      </div>
+    </div>
+    <div class="table_list">
+      <PIMTable rowKey="id"
+                :column="tableColumn"
+                :tableData="tableData"
+                :page="page"
+                height="calc(100vh - 350px)"
+                :tableLoading="tableLoading"
+                :isSelection="true"
+                :selectable="isSelectable"
+                @selection-change="handleSelectionChange"
+                @pagination="pagination">
+        <template #qtyRequired="{ row }">
+          {{ row.qtyRequired || '-' }}<span style="color:rgba(12, 46, 40, 0.76)"> {{ row.unit || '鏂�' }}</span>
+        </template>
+        <template #salesContractNo="{ row }">
+          <el-button type="primary"
+                     text
+                     link
+                     @click="showDetail(row)">{{ row.salesContractNo }}
+          </el-button>
+        </template>
+      </PIMTable>
+    </div>
+    <!-- 鍚堝苟涓嬪彂寮圭獥 -->
+    <el-dialog v-model="isShowNewModal"
+               destroy-on-close
+               title="鍚堝苟涓嬪彂"
+               width="600px">
+      <el-form :model="mergeForm"
+               label-width="120px">
+        <el-row :gutter="20">
+          <el-col :span="10">
+            <el-form-item label="浜у搧鍚嶇О">
+              <el-tag class="info-display">{{ mergeForm.productName || '-' }}</el-tag>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col>
+            <el-form-item label="浜у搧瑙勬牸">
+              <div class="info-display">{{ mergeForm.model || '-' }}</div>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="璁″垝瀹屾垚鏃堕棿">
+          <el-date-picker v-model="mergeForm.planCompleteTime"
+                          type="date"
+                          value-format="YYYY-MM-DD"
+                          style="width: 100%" />
+        </el-form-item>
+        <el-form-item label="鐢熶骇鏁伴噺">
+          <el-input-number v-model="mergeForm.totalAssignedQuantity"
+                           :min="0"
+                           :max="sumAssignedQuantity"
+                           @change="onBlur"
+                           style="width: 100%" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary" @click="handleMergeSubmit">纭畾涓嬪彂</el-button>
+          <el-button @click="isShowNewModal = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <!-- 瀵煎叆寮圭獥 -->
+    <ImportDialog ref="importDialogRef"
+                  v-model="importDialogVisible"
+                  title="瀵煎叆鐢熶骇璁″垝"
+                  :action="importAction"
+                  :headers="importHeaders"
+                  :auto-upload="false"
+                  :on-success="handleImportSuccess"
+                  :on-error="handleImportError"
+                  @confirm="handleImportConfirm"
+                  @download-template="handleDownloadTemplate"
+                  @close="handleImportClose" />
+    <!-- 鏂板/缂栬緫寮圭獥 -->
+    <el-dialog v-model="dialogVisible"
+               destroy-on-close
+               :title="operationType === 'add' ? '鏂板鐢熶骇璁″垝' : '缂栬緫鐢熶骇璁″垝'"
+               width="600px">
+      <el-form ref="formRef"
+               :model="form"
+               :rules="rules"
+               label-width="120px">
+        <el-form-item label="涓荤敓浜ц鍒掑彿"
+                      prop="mpsNo">
+          <el-input v-model="form.mpsNo"
+                    disabled
+                    placeholder="鏂板鍚庤嚜鍔ㄧ敓鎴�" />
+        </el-form-item>
+        <el-form-item label="浜у搧鍚嶇О"
+                      prop="productId">
+          <el-tree-select v-model="form.productId"
+                          placeholder="璇烽�夋嫨"
+                          clearable
+                          :data="productOptions"
+                          :render-after-expand="false"
+                          filterable
+                          @change="handleProductChange"
+                          style="width: 100%" />
+        </el-form-item>
+        <el-form-item label="浜у搧瑙勬牸"
+                      prop="productModelId">
+          <el-select v-model="form.productModelId"
+                     @change="handleChangeSpecification"
+                     filterable
+                     style="width: 100%"
+                     placeholder="璇烽�夋嫨">
+            <el-option v-for="item in specificationOptions"
+                       :key="item.id"
+                       :label="item.model"
+                       :value="item.id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鎵�闇�鏁伴噺"
+                      prop="qtyRequired">
+          <el-input-number v-model="form.qtyRequired"
+                           :min="0"
+                           style="width: 100%"
+                           placeholder="璇疯緭鍏ユ暟閲�" />
+        </el-form-item>
+        <el-form-item label="鍗曚綅"
+                      prop="unit">
+          <el-input v-model="form.unit"
+                    placeholder="璇疯緭鍏ュ崟浣�" />
+        </el-form-item>
+        <el-form-item label="闇�姹傛棩鏈�"
+                      prop="requiredDate">
+          <el-date-picker v-model="form.requiredDate"
+                          type="date"
+                          value-format="YYYY-MM-DD"
+                          style="width: 100%"
+                          placeholder="璇烽�夋嫨闇�姹傛棩鏈�" />
+        </el-form-item>
+        <el-form-item label="鎵胯鏃ユ湡"
+                      prop="promisedDeliveryDate">
+          <el-date-picker v-model="form.promisedDeliveryDate"
+                          type="date"
+                          value-format="YYYY-MM-DD"
+                          style="width: 100%"
+                          placeholder="璇烽�夋嫨鎵胯鏃ユ湡" />
+        </el-form-item>
+        <el-form-item label="澶囨敞"
+                      prop="remark">
+          <el-input v-model="form.remark"
+                    type="textarea"
+                    placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary" @click="handleSubmit">纭畾</el-button>
+          <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+  import {
+    reactive,
+    ref,
+    onMounted,
+    toRefs,
+    getCurrentInstance,
+    computed,
+  } from "vue";
+  import { useRouter } from "vue-router";
+  import dayjs from "dayjs";
+  import { ElMessage } from "element-plus";
+  import { ArrowUp, ArrowDown } from "@element-plus/icons-vue";
+  import { getToken } from "@/utils/auth";
+  import { useDict } from "@/utils/dict";
+  import {
+    productionPlanListPage,
+    productionPlanAdd,
+    productionPlanUpdate,
+    productionPlanDelete,
+    productionPlanCombine,
+  } from "@/api/productionPlan/productionPlan.js";
+  import { productTreeList, modelListPage } from "@/api/basicData/product.js";
+  import PIMTable from "./components/PIMTable.vue";
+  import ImportDialog from "@/components/Dialog/ImportDialog.vue";
+
+  const { proxy } = getCurrentInstance();
+  const router = useRouter();
+
+  const loadProdData = () => {
+    console.log("Mock loadProdData called");
+    return Promise.resolve({ code: 200, msg: "鍚屾鎴愬姛" });
+  };
+
+  const exportProductionPlan = () => {
+    console.log("Mock exportProductionPlan called");
+    return Promise.resolve();
+  };
+
+  // const productionPlanCombine = payload => {
+  //   console.log("Mock productionPlanCombine called with:", payload);
+  //   return Promise.resolve({ code: 200, msg: "鍚堝苟涓嬪彂鎴愬姛" });
+  // };
+
+  const tableColumn = ref([
+    {
+      label: "涓荤敓浜ц鍒掑彿",
+      prop: "mpsNo",
+      width: "150px",
+    },
+    {
+      label: "鏉ユ簮",
+      prop: "source",
+      width: "150px",
+      dataType: "tag",
+      formatType: params => {
+        return params == "閿�鍞�" ? "primary" : "info";
+      },
+      formatData: params => {
+        return params == "閿�鍞�" ? "閿�鍞�" : "鍐呴儴";
+      },
+    },
+
+    {
+      label: "浜у搧鍚嶇О",
+      prop: "productName",
+      width: "200px",
+      dataType: "tag",
+      formatType: params => {
+        return "primary";
+      },
+    },
+    {
+      label: "浜у搧瑙勬牸",
+      prop: "model",
+      width: "150px",
+      className: "spec-cell",
+    },
+    {
+      label: "鍗曚綅",
+      prop: "unit",
+      width: "100px",
+    },
+    {
+      label: "鎵�闇�鏁伴噺",
+      prop: "qtyRequired",
+      width: "150px",
+      align: "right",
+      dataType: "slot",
+      slot: "qtyRequired",
+      className: "volume-cell",
+    },
+    {
+      label: "涓嬪彂鐘舵��",
+      prop: "status",
+      width: "120px",
+      className: "status-cell",
+      dataType: "tag",
+      formatType: params => {
+        const typeMap = {
+          0: "warning",
+          1: "primary",
+          2: "info",
+        };
+        return typeMap[params] || "info";
+      },
+      formatData: cell => {
+        const statusMap = {
+          0: "寰呬笅鍙�",
+          1: "閮ㄥ垎涓嬪彂",
+          2: "宸蹭笅鍙�",
+        };
+        return statusMap[cell] || "";
+      },
+    },
+    {
+      label: "宸蹭笅鍙戞暟閲�",
+      prop: "quantityIssued",
+      width: "120px",
+      className: "spec-cell",
+      // formatData: (cell, row) => (cell ? `${cell}${row.unit || "鏂�"}` : 0),
+    },
+    {
+      label: "闇�姹傛棩鏈�",
+      prop: "requiredDate",
+      width: "160px",
+      className: "date-cell",
+      formatData: cell => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""),
+    },
+    {
+      label: "鎵胯鏃ユ湡",
+      prop: "promisedDeliveryDate",
+      width: "160px",
+      className: "date-cell",
+      formatData: cell => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""),
+    },
+    {
+      label: "閿�鍞悎鍚屽彿",
+      prop: "salesContractNo",
+      width: "200px",
+      dataType: "slot",
+      slot: "salesContractNo",
+    },
+    {
+      label: "瀹㈡埛鍚嶇О",
+      prop: "customerName",
+      width: "150px",
+    },
+    {
+      label: "椤圭洰鍚嶇О",
+      prop: "projectName",
+      width: "150px",
+    },
+    {
+      label: "澶囨敞",
+      width: "150px",
+      prop: "remark",
+    },
+    {
+      dataType: "action",
+      label: "鎿嶄綔",
+      align: "center",
+      fixed: "right",
+      width: 250,
+      operation: [
+        {
+          name: "缂栬緫",
+          type: "primary",
+          link: true,
+          showHide: row => {
+            return row.status == 0 && row.source != "閿�鍞�";
+          },
+          clickFun: row => {
+            handleEdit(row);
+          },
+        },
+        {
+          name: "涓嬪彂",
+          type: "text",
+          showHide: row => {
+            return row.status != 2;
+          },
+          clickFun: row => {
+            mergeForm.productName = row.productName || "";
+            mergeForm.model = row.model || "";
+            mergeForm.totalAssignedQuantity =
+              Number(row.qtyRequired || 0) - Number(row.quantityIssued || 0);
+            mergeForm.planCompleteTime = row.requiredDate || "";
+            mergeForm.productId = row.productId || "";
+            mergeForm.ids = [row.id];
+            sumAssignedQuantity.value =
+              Number(row.qtyRequired || 0) - Number(row.quantityIssued || 0);
+            isShowNewModal.value = true;
+          },
+        },
+        {
+          name: "鍒犻櫎",
+          type: "danger",
+          link: true,
+          showHide: row => {
+            return row.status == 0;
+          },
+          clickFun: row => {
+            handleDelete(row);
+          },
+        },
+      ],
+    },
+  ]);
+  const tableData = ref([]);
+  const tableLoading = ref(false);
+  const page = reactive({
+    current: 1,
+    size: 100,
+    total: 0,
+  });
+  const selectedRows = ref([]);
+
+  // 浜у搧绫诲埆姹囨�荤粺璁℃暟鎹�
+  const categorySummary = ref([]);
+  // 浜у搧绫诲埆姹囨�诲脊绐楁帶鍒�
+  const showCategorySummaryDialog = ref(false);
+
+  // 鍚堝苟涓嬪彂寮圭獥鎺у埗
+  const isShowNewModal = ref(false);
+  // 鍚堝苟涓嬪彂琛ㄥ崟鏁版嵁
+  const mergeForm = reactive({
+    productName: "",
+    model: "",
+    totalAssignedQuantity: 0,
+    planCompleteTime: "",
+    productId: "",
+  });
+
+  // 瀵煎叆鐩稿叧
+  const importDialogRef = ref(null);
+  const importDialogVisible = ref(false);
+  const importAction =
+    import.meta.env.VITE_APP_BASE_API + "/productionPlan/import";
+  const importHeaders = ref({
+    Authorization: `Bearer ${getToken()}`,
+  });
+
+  // 鏂板/缂栬緫鐩稿叧
+  const dialogVisible = ref(false);
+  const operationType = ref("add"); // add | edit
+  const productOptions = ref([]);
+  const specificationOptions = ref([]);
+  const queryRef = ref(null);
+  const formRef = ref(null);
+  const form = reactive({
+    id: undefined,
+    mpsNo: "",
+    productId: undefined,
+    productModelId: undefined,
+    productName: "",
+    model: "",
+    qtyRequired: 0,
+    unit: "鏂�",
+    requiredDate: "",
+    promisedDeliveryDate: "",
+    remark: "",
+  });
+  const rules = reactive({
+    productId: [{ required: true, message: "璇烽�夋嫨浜у搧", trigger: "change" }],
+    productModelId: [
+      { required: true, message: "璇烽�夋嫨浜у搧瑙勬牸", trigger: "change" },
+    ],
+    qtyRequired: [{ required: true, message: "璇疯緭鍏ユ暟閲�", trigger: "blur" }],
+    requiredDate: [
+      { required: true, message: "璇烽�夋嫨闇�姹傛棩鏈�", trigger: "change" },
+    ],
+  });
+
+  // 澶勭悊杩借釜杩涘害鎸夐挳鐐瑰嚮
+  const handleTrackProgress = row => {
+    // 璺宠浆鍒拌拷韪繘搴﹂〉闈�
+    router.push({
+      path: "/productionPlan/trackProgress",
+      query: {
+        id: row.id,
+        productName: row.productName,
+        model: row.model,
+      },
+    });
+  };
+  const onBlur = value => {
+    // 闄愬埗鍥涗綅灏忔暟
+    mergeForm.totalAssignedQuantity = Number(value.toFixed(4));
+  };
+
+  const fetchProductOptions = () => {
+    return productTreeList().then(res => {
+      productOptions.value = convertIdToValue(res || []);
+    });
+  };
+
+  const convertIdToValue = data => {
+    return data.map(item => {
+      const newItem = {
+        value: item.id,
+        label: item.label,
+      };
+      if (item.children && item.children.length > 0) {
+        newItem.children = convertIdToValue(item.children);
+      }
+      return newItem;
+    });
+  };
+
+  const handleProductChange = value => {
+    form.productModelId = undefined;
+    form.model = undefined;
+    // 鏌ユ壘閫変腑鐨勪骇鍝佸悕绉�
+    const findProductName = (options, val) => {
+      for (const option of options) {
+        if (option.value === val) {
+          return option.label;
+        }
+        if (option.children) {
+          const found = findProductName(option.children, val);
+          if (found) {
+            return found;
+          }
+        }
+      }
+      return "";
+    };
+    form.productName = findProductName(productOptions.value, value);
+    fetchSpecificationOptions(value);
+  };
+
+  const fetchSpecificationOptions = productId => {
+    specificationOptions.value = [];
+    if (productId) {
+      modelListPage({ id: productId, size: 1000, current: 1 }).then(res => {
+        specificationOptions.value = res.records || [];
+      });
+    }
+  };
+
+  const handleChangeSpecification = value => {
+    form.model = undefined;
+    form.unit = "";
+    const selectedModel = specificationOptions.value.find(
+      item => item.id === value
+    );
+    if (selectedModel) {
+      form.model = selectedModel.model;
+      form.unit = selectedModel.unit || "鏂�";
+    }
+  };
+
+  const data = reactive({
+    searchForm: {
+      mpsNo: "",
+      productName: "",
+      model: "",
+      status: "",
+      dateRange: [],
+    },
+    searchFormExpanded: false,
+  });
+  const { searchForm, searchFormExpanded } = toRefs(data);
+
+  // 鍒囨崲鎼滅储琛ㄥ崟灞曞紑/鏀惰捣鐘舵��
+  const toggleSearchForm = () => {
+    data.searchFormExpanded = !data.searchFormExpanded;
+  };
+
+  // 鏌ヨ鍒楄〃
+  /** 鎼滅储鎸夐挳鎿嶄綔 */
+  const handleQuery = () => {
+    page.current = 1;
+    getList();
+  };
+
+  /** 閲嶇疆鎸夐挳鎿嶄綔 */
+  const handleReset = () => {
+    if (proxy.resetForm) {
+      proxy.resetForm("queryRef");
+    }
+    Object.assign(searchForm.value, {
+      mpsNo: "",
+      productName: "",
+      model: "",
+      status: "",
+      dateRange: [],
+    });
+    page.current = 1;
+    getList();
+  };
+  const pagination = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    getList();
+  };
+  // 璁$畻浜у搧绫诲埆姹囨�荤粺璁�
+  const calculateCategorySummary = () => {
+    const summary = {};
+
+    // 閬嶅巻琛ㄦ牸鏁版嵁锛屾寜浜у搧绫诲埆姹囨��
+    tableData.value.forEach(row => {
+      const category = row.productName || "鏈煡浜у搧";
+      if (!summary[category]) {
+        summary[category] = {
+          materialCode: category,
+          totalAssignedQuantity: 0,
+        };
+      }
+      summary[category].totalAssignedQuantity += Number(
+        (Number(row.qtyRequired || 0) - Number(row.quantityIssued || 0)).toFixed(
+          4
+        )
+      );
+    });
+
+    // 杞崲涓烘暟缁勬牸寮�
+    categorySummary.value = Object.values(summary);
+  };
+
+  const getList = () => {
+    tableLoading.value = true;
+    // 鏋勯�犳悳绱㈠弬鏁�
+    const params = { ...searchForm.value, ...page };
+    params.requiredDateStart =
+      params.dateRange && params.dateRange.length > 0 ? params.dateRange[0] : "";
+    params.requiredDateEnd =
+      params.dateRange && params.dateRange.length > 1 ? params.dateRange[1] : "";
+    delete params.dateRange;
+    productionPlanListPage(params)
+      .then(res => {
+        tableLoading.value = false;
+        tableData.value = res.data.records;
+        page.total = res.data.total;
+        // 璁$畻浜у搧绫诲埆姹囨�荤粺璁�
+        calculateCategorySummary();
+      })
+      .catch(() => {
+        tableLoading.value = false;
+      });
+  };
+
+  // 閫変腑鐨勪骇鍝佽鏍糏D
+  const selectedProductModelId = ref("");
+
+  // 琛ㄦ牸閫夋嫨鏁版嵁
+  const handleSelectionChange = selection => {
+    selectedRows.value = selection;
+    // 濡傛灉鏈夐�変腑鐨勮锛岃褰曠涓�涓�変腑琛岀殑浜у搧瑙勬牸ID
+    if (selection.length > 0) {
+      selectedProductModelId.value = selection[0].productModelId;
+    } else {
+      // 濡傛灉娌℃湁閫変腑鐨勮锛屾竻绌轰骇鍝佽鏍糏D
+      selectedProductModelId.value = "";
+    }
+  };
+
+  // 鍒ゆ柇琛屾槸鍚﹀彲閫夋嫨
+  const isSelectable = row => {
+    // 濡傛灉鏄凡涓嬪彂鐘舵�侊紝绂佹鍕鹃��
+    if (row.status == 2) {
+      return false;
+    }
+    // 璁$畻鍓╀綑鏁伴噺
+    const remainingQty = (row.qtyRequired || 0) - (row.quantityIssued || 0);
+    // 濡傛灉鍓╀綑鏁伴噺灏忎簬绛変簬0锛岀姝㈤�夋嫨
+    if (remainingQty <= 0) {
+      return false;
+    }
+    // 濡傛灉娌℃湁閫変腑鐨勮锛屾墍鏈夎閮藉彲閫夋嫨
+    if (!selectedProductModelId.value) {
+      return true;
+    }
+    // 濡傛灉鏈夐�変腑鐨勮锛屽彧鏈変骇鍝佽鏍糏D鐩稿悓鐨勮鎵嶅彲閫夋嫨
+    return row.productModelId === selectedProductModelId.value;
+  };
+  // 鎷夊彇鏁版嵁鎸夐挳鎿嶄綔
+  const loadProdDataLoading = ref(false);
+  const sumAssignedQuantity = ref(0);
+
+  // 澶勭悊鍚堝苟涓嬪彂鎸夐挳鐐瑰嚮
+  const handleMerge = () => {
+    if (selectedRows.value.length === 0) {
+      ElMessage.warning("璇烽�夋嫨瑕佸悎骞朵笅鍙戠殑鐢熶骇璁″垝");
+      return;
+    }
+    console.log(selectedRows.value);
+    const firstRow = selectedRows.value[0];
+    const productName = firstRow.productName || "";
+
+    // 璁$畻鎬诲埗閫犳暟閲� (榛樿qtyRequired鐨勫拰)
+    const totalAssignedQuantity = selectedRows.value.reduce((sum, row) => {
+      return sum + Number(row.qtyRequired || 0) - Number(row.quantityIssued || 0);
+    }, 0);
+    sumAssignedQuantity.value = totalAssignedQuantity;
+    console.log(totalAssignedQuantity);
+    // 璁剧疆琛ㄥ崟鏁版嵁
+    mergeForm.productName = productName;
+    mergeForm.model = firstRow.model || "";
+    mergeForm.totalAssignedQuantity = totalAssignedQuantity;
+    mergeForm.planCompleteTime = firstRow.requiredDate || "";
+    mergeForm.productId = firstRow.productId || "";
+    mergeForm.ids = selectedRows.value.map(row => row.id);
+
+    // 鎵撳紑寮圭獥
+    isShowNewModal.value = true;
+  };
+  const showDetail = row => {
+    router.push({
+      path: "/salesManagement/salesLedger",
+      query: {
+        salesContractNo: row.salesContractNo,
+      },
+    });
+  };
+
+  // 澶勭悊鍚堝苟涓嬪彂鎻愪氦
+  const handleMergeSubmit = () => {
+    if (mergeForm.totalAssignedQuantity === 0) {
+      ElMessage.warning("璇疯緭鍏ョ敓浜ф暟閲�");
+      return;
+    }
+    console.log(sumAssignedQuantity.value, "sumAssignedQuantity");
+
+    // 楠岃瘉totalAssignedQuantity涓嶈兘澶т簬鎬绘柟鏁�
+    if (mergeForm.totalAssignedQuantity > sumAssignedQuantity.value) {
+      ElMessage.error("鐢熶骇鏁伴噺涓嶈兘澶т簬褰撳墠璁$畻鐨勬�诲��");
+      return;
+    }
+
+    console.log(mergeForm, "mergeForm");
+    const payload = {
+      ...mergeForm,
+    };
+    productionPlanCombine(payload)
+      .then(res => {
+        if (res.code === 200) {
+          ElMessage.success("涓嬪彂鎴愬姛");
+          getList();
+          isShowNewModal.value = false;
+          // 鍙互閫夋嫨鍒锋柊鍒楄〃鎴栧叾浠栨搷浣�
+          getList();
+        } else {
+          ElMessage.error(res.message || "涓嬪彂澶辫触");
+        }
+      })
+      .catch(err => {
+        console.error("鍚堝苟涓嬪彂寮傚父锛�", err);
+        ElMessage.error("绯荤粺寮傚父锛屽悎骞朵笅鍙戝け璐�");
+      });
+    // 鍙互閫夋嫨鍒锋柊鍒楄〃鎴栧叾浠栨搷浣�
+  };
+
+  // 瀵煎叆
+  const handleImport = () => {
+    importDialogVisible.value = true;
+  };
+
+  // 瀵煎嚭
+  const handleExport = () => {
+    const fileName = `鐢熶骇璁″垝.xlsx`;
+    exportProductionPlan()
+      .then(res => {
+        // 杩斿洖鐨勬暟鎹槸鍚︿负绌�
+        if (!res) {
+          proxy.$modal.msgError("瀵煎嚭澶辫触锛岃繑鍥炴暟鎹负绌�");
+          return;
+        }
+
+        const blob = new Blob([res], {
+          type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+        });
+        const downloadElement = document.createElement("a");
+        const href = window.URL.createObjectURL(blob);
+
+        downloadElement.style.display = "none";
+        downloadElement.href = href;
+        downloadElement.download = fileName;
+
+        document.body.appendChild(downloadElement);
+        downloadElement.click();
+
+        document.body.removeChild(downloadElement);
+        window.URL.revokeObjectURL(href);
+
+        proxy.$modal.msgSuccess("瀵煎嚭鎴愬姛");
+      })
+      .catch(err => {
+        console.error("瀵煎嚭寮傚父锛�", err);
+        proxy.$modal.msgError("绯荤粺寮傚父锛屽鍑哄け璐�");
+      });
+  };
+
+  // 瀵煎叆鎴愬姛
+  const handleImportSuccess = response => {
+    if (response.code === 200) {
+      ElMessage.success("瀵煎叆鎴愬姛");
+      importDialogVisible.value = false;
+      getList();
+    } else {
+      ElMessage.error(response.msg || "瀵煎叆澶辫触");
+    }
+  };
+
+  // 瀵煎叆澶辫触
+  const handleImportError = error => {
+    ElMessage.error("瀵煎叆澶辫触锛岃妫�鏌ユ枃浠舵牸寮忔槸鍚︽纭�");
+  };
+
+  // 纭瀵煎叆
+  const handleImportConfirm = () => {
+    if (importDialogRef.value) {
+      importDialogRef.value.submit();
+    }
+  };
+
+  // 涓嬭浇妯℃澘
+  const handleDownloadTemplate = () => {
+    proxy.download(
+      "/productionPlan/downloadTemplate",
+      {},
+      "鐢熶骇璁″垝瀵煎叆妯℃澘.xlsx"
+    );
+  };
+
+  // 鍏抽棴瀵煎叆寮圭獥
+  const handleImportClose = () => {
+    importDialogVisible.value = false;
+  };
+
+  // 鏂板
+  const handleAdd = () => {
+    operationType.value = "add";
+    Object.assign(form, {
+      id: undefined,
+      mpsNo: "",
+      productName: "",
+      productId: undefined,
+      productModelId: undefined,
+      model: "",
+      qtyRequired: 0,
+      unit: "鏂�",
+      requiredDate: "",
+      promisedDeliveryDate: "",
+      remark: "",
+    });
+    dialogVisible.value = true;
+    fetchProductOptions();
+  };
+
+  // 缂栬緫
+  const handleEdit = row => {
+    operationType.value = "edit";
+    Object.assign(form, {
+      id: row.id,
+      mpsNo: row.mpsNo || "",
+      productName: row.productName || "",
+      productId: row.productId || undefined,
+      productModelId: row.productModelId || undefined,
+      model: row.model || "",
+      qtyRequired: row.qtyRequired || 0,
+      unit: row.unit || "鏂�",
+      requiredDate: row.requiredDate || "",
+      promisedDeliveryDate: row.promisedDeliveryDate || "",
+      remark: row.remark || "",
+    });
+    dialogVisible.value = true;
+    fetchProductOptions();
+    fetchSpecificationOptions(row.productId);
+  };
+
+  // 鍒犻櫎
+  const handleDelete = row => {
+    proxy.$modal
+      .confirm("纭鍒犻櫎璇ョ敓浜ц鍒掞紵", "鎻愮ず", {
+        confirmButtonText: "纭",
+        cancelButtonText: "鍙栨秷",
+        type: "warning",
+      })
+      .then(() => {
+        productionPlanDelete([row.id])
+          .then(() => {
+            proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+            getList();
+          })
+          .catch(() => {
+            proxy.$modal.msgError("鍒犻櫎澶辫触");
+          });
+      })
+      .catch(() => {});
+  };
+
+  // 鎻愪氦琛ㄥ崟
+  const handleSubmit = () => {
+    formRef.value.validate(valid => {
+      if (valid) {
+        if (form.qtyRequired === 0) {
+          proxy.$modal.msgError("鏁伴噺涓嶈兘涓�0");
+          return;
+        }
+        const payload = { ...form };
+        if (operationType.value === "add") {
+          payload.id = null;
+          productionPlanAdd(payload)
+            .then(() => {
+              proxy.$modal.msgSuccess("鏂板鎴愬姛");
+              dialogVisible.value = false;
+              getList();
+            })
+            .catch(() => {
+              proxy.$modal.msgError("鏂板澶辫触");
+            });
+        }
+        if (operationType.value === "edit") {
+          productionPlanUpdate(payload)
+            .then(() => {
+              proxy.$modal.msgSuccess("淇敼鎴愬姛");
+              dialogVisible.value = false;
+              getList();
+            })
+            .catch(() => {
+              proxy.$modal.msgError("淇敼澶辫触");
+            });
+        }
+      }
+    });
+  };
+
+  onMounted(() => {
+    getList();
+  });
+</script>
+
+<style scoped lang="scss">
+  .app-container {
+    padding: 24px;
+    background-color: #f0f2f5;
+    min-height: calc(100vh - 48px);
+  }
+
+  .search_form {
+    // margin-bottom: 24px;
+    padding: 20px;
+    background-color: #ffffff;
+    border-radius: 6px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+    transition: all 0.3s ease;
+
+    &:hover {
+      box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
+    }
+  }
+
+  .search-header {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    // margin-bottom: 5px;
+    // padding-bottom: 5px;
+    position: relative;
+    bottom: 35px;
+    // border-bottom: 1px solid #ebeef5;
+  }
+
+  .search-title {
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+  }
+
+  .search-header .el-button {
+    color: #606266;
+    transition: all 0.3s ease;
+  }
+
+  .search-header .el-button:hover {
+    color: #409eff;
+  }
+
+  .search-header .el-icon {
+    margin-right: 4px;
+  }
+
+  .table_list {
+    // margin-bottom: 24px;
+    background-color: #ffffff;
+    border-radius: 6px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+    overflow: hidden;
+    height: calc(100vh - 250px);
+    margin-top: 0px !important;
+  }
+
+  :deep(.el-table) {
+    border: none;
+    border-radius: 6px;
+    overflow: hidden;
+    box-shadow: 0 4px 16px rgba(102, 126, 234, 0.1);
+
+    .el-table__header-wrapper {
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+
+      th {
+        background: transparent;
+        font-weight: 600;
+        color: #ffffff;
+        border-bottom: none;
+        padding: 16px 0;
+        letter-spacing: 0.5px;
+      }
+    }
+
+    .el-table__body-wrapper {
+      tr {
+        transition: all 0.3s ease;
+
+        &:hover {
+          background: linear-gradient(
+            90deg,
+            rgba(102, 126, 234, 0.05) 0%,
+            rgba(118, 75, 162, 0.05) 100%
+          );
+          transform: scale(1.002);
+          box-shadow: 0 2px 8px rgba(102, 126, 234, 0.1);
+        }
+
+        td {
+          border-bottom: 1px solid #f0f0f0;
+          padding: 14px 0;
+          color: #303133;
+        }
+      }
+
+      tr.current-row {
+        background: linear-gradient(
+          90deg,
+          rgba(102, 126, 234, 0.08) 0%,
+          rgba(118, 75, 162, 0.08) 100%
+        );
+      }
+
+      // 鏁板�煎瓧娈垫牱寮�
+      .volume-cell,
+      .dimension-cell {
+        font-weight: 600;
+        color: #409eff;
+        font-family: "Courier New", monospace;
+        text-shadow: 0 1px 2px rgba(64, 158, 255, 0.2);
+      }
+
+      // 瑙勬牸瀛楁鏍峰紡
+      .spec-cell {
+        color: #67c23a;
+        font-weight: 500;
+
+        padding: 4px 8px;
+        border-radius: 4px;
+      }
+
+      // 缂栫爜瀛楁鏍峰紡
+      .code-cell {
+        color: #e6a23c;
+        font-family: "Courier New", monospace;
+        font-weight: 500;
+        padding: 4px 8px;
+        border-radius: 4px;
+      }
+
+      // 鏃ユ湡瀛楁鏍峰紡
+      .date-cell {
+        color: #909399;
+        font-style: italic;
+      }
+
+      // 鐘舵�佹爣绛炬牱寮�
+      .status-tag {
+        &.pending {
+          background: linear-gradient(135deg, #ffeaa7 0%, #fdcb6e 100%);
+          color: #d63031;
+          padding: 4px 12px;
+          border-radius: 12px;
+          font-weight: 500;
+          box-shadow: 0 2px 4px rgba(253, 203, 110, 0.3);
+        }
+
+        &.processing {
+          background: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%);
+          color: #ffffff;
+          padding: 4px 12px;
+          border-radius: 12px;
+          font-weight: 500;
+          box-shadow: 0 2px 4px rgba(9, 132, 227, 0.3);
+        }
+
+        &.completed {
+          background: linear-gradient(135deg, #55efc4 0%, #00b894 100%);
+          color: #ffffff;
+          padding: 4px 12px;
+          border-radius: 12px;
+          font-weight: 500;
+          box-shadow: 0 2px 4px rgba(0, 184, 148, 0.3);
+        }
+      }
+    }
+
+    .el-table__empty-block {
+      padding: 60px 0;
+      background-color: #fafafa;
+    }
+  }
+
+  // 鎿嶄綔鎸夐挳鏍峰紡
+  :deep(.el-table .cell .el-button--text) {
+    padding: 6px 10px;
+    border-radius: 4px;
+    transition: all 0.3s ease;
+    font-weight: 500;
+
+    &:hover {
+      background-color: rgba(64, 158, 255, 0.1);
+      transform: translateY(-1px);
+      box-shadow: 0 2px 4px rgba(64, 158, 255, 0.2);
+    }
+
+    &:nth-of-type(1) {
+      color: #409eff;
+      background: linear-gradient(
+        135deg,
+        rgba(64, 158, 255, 0.1) 0%,
+        rgba(64, 158, 255, 0.05) 100%
+      );
+    }
+
+    &:nth-of-type(2) {
+      color: #67c23a;
+      background: linear-gradient(
+        135deg,
+        rgba(103, 194, 58, 0.1) 0%,
+        rgba(103, 194, 58, 0.05) 100%
+      );
+    }
+  }
+
+  // 淇℃伅灞曠ず鏍峰紡
+  .info-display {
+    border-radius: 6px;
+    color: #303133;
+    font-size: 14px;
+    min-height: 32px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+  }
+
+  .pagination-container {
+    display: flex;
+    justify-content: flex-end;
+    padding: 16px 20px;
+    background-color: #ffffff;
+    border-top: 1px solid #ebeef5;
+    border-radius: 0 0 12px 12px;
+  }
+
+  :deep(.el-button) {
+    transition: all 0.3s ease;
+
+    &:hover {
+      transform: translateY(-1px);
+    }
+  }
+
+  :deep(.el-dialog) {
+    border-radius: 6px;
+    overflow: hidden;
+
+    .el-dialog__header {
+      background-color: #fafafa;
+      border-bottom: 1px solid #ebeef5;
+      padding: 20px 24px;
+
+      .el-dialog__title {
+        font-size: 16px;
+        font-weight: 600;
+        color: #303133;
+      }
+    }
+
+    .el-dialog__body {
+      padding: 24px;
+    }
+
+    .el-dialog__footer {
+      padding: 16px 24px;
+      border-top: 1px solid #ebeef5;
+      background-color: #fafafa;
+    }
+  }
+
+  :deep(.el-form) {
+    .el-form-item {
+      margin-bottom: 20px;
+
+      .el-form-item__label {
+        font-weight: 500;
+        color: #303133;
+      }
+
+      .el-input,
+      .el-select,
+      .el-date-picker,
+      .el-input-number {
+        width: 100%;
+
+        // .el-input__inner {
+        //   border-radius: 6px;
+        //   border: 1px solid #dcdfe6;
+        //   transition: all 0.3s ease;
+
+        //   &:focus {
+        //     border-color: #409eff;
+        //     box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
+        //   }
+        // }
+      }
+    }
+  }
+
+  :deep(.el-tag) {
+    border-radius: 4px;
+    padding: 2px 8px;
+    font-size: 12px;
+  }
+
+  @media (max-width: 768px) {
+    .app-container {
+      padding: 16px;
+    }
+
+    .search_form {
+      flex-direction: column;
+      align-items: flex-start;
+      gap: 12px;
+
+      .el-form {
+        width: 100%;
+
+        .el-form-item {
+          width: 100%;
+        }
+      }
+
+      > div {
+        width: 100%;
+        display: flex;
+        gap: 12px;
+
+        .el-button {
+          flex: 1;
+        }
+      }
+    }
+
+    :deep(.el-table) {
+      th,
+      td {
+        padding: 10px 0;
+        font-size: 12px;
+      }
+    }
+
+    :deep(.el-dialog) {
+      width: 90% !important;
+      margin: 20px auto !important;
+    }
+  }
+  .consumption-value {
+    font-weight: bold;
+    color: #409eff;
+  }
+
+  .consumption-unit {
+    font-size: 12px;
+    color: #909399;
+    margin-left: 4px;
+  }
+  // .search_form {
+  //   :deep(.el-form-item) {
+  //     margin-bottom: 0px !important;
+  //   }
+  // }
+  :deep(.el-table .el-table__body-wrapper tr td) {
+    background-color: #fff;
+  }
+</style>
diff --git a/src/views/projectManagement/Management/components/formDia.vue b/src/views/projectManagement/Management/components/formDia.vue
index f29512b..c2ee9c2 100644
--- a/src/views/projectManagement/Management/components/formDia.vue
+++ b/src/views/projectManagement/Management/components/formDia.vue
@@ -571,9 +571,12 @@
         <el-col :span="12">
           <el-form-item label="绋庣巼(%)锛�" prop="taxRate">
             <el-select v-model="productForm.taxRate" placeholder="璇烽�夋嫨" clearable @change="calculateFromTaxRate">
-              <el-option label="1" value="1" />
-              <el-option label="6" value="6" />
-              <el-option label="13" value="13" />
+              <el-option
+                v-for="dict in tax_rate"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
             </el-select>
           </el-form-item>
         </el-col>
@@ -650,7 +653,7 @@
 
 const emit = defineEmits(['completed'])
 const { proxy } = getCurrentInstance()
-const { bill_status, project_management, plan_status } = proxy.useDict('bill_status', 'project_management', 'plan_status')
+const { bill_status, project_management, plan_status, tax_rate } = proxy.useDict('bill_status', 'project_management', 'plan_status', 'tax_rate')
 
 const dialogVisible = ref(false)
 const operationType = ref('add')
diff --git a/src/views/projectManagement/Management/index.vue b/src/views/projectManagement/Management/index.vue
index 7547209..0438aef 100644
--- a/src/views/projectManagement/Management/index.vue
+++ b/src/views/projectManagement/Management/index.vue
@@ -37,7 +37,7 @@
         </el-dropdown> -->
         <el-button :loading="submitLoading" @click="handleSubmit">鎻愪氦</el-button>
         <el-button :loading="auditLoading" @click="handleAudit">瀹℃牳</el-button>
-        <el-button :loading="reverseAuditLoading" @click="handleReverseAudit">鍙嶅鏍�</el-button>
+        <!-- <el-button :loading="reverseAuditLoading" @click="handleReverseAudit">鍙嶅鏍�</el-button> -->
         <el-button :loading="deleteLoading" @click="handleDelete">鍒犻櫎</el-button>
       </div>
 
@@ -384,7 +384,7 @@
       actualStartTime: payload.actualStartTime || null,
       actualEndTime: payload.actualEndTime || null,
       progress: Number(payload.totalProgress ?? payload.completionProgress ?? 0) || 0,
-      attachmentIds: Array.isArray(payload.attachmentIds) ? payload.attachmentIds : []
+      storageBlobDTOs: Array.isArray(payload.storageBlobDTOs) ? payload.storageBlobDTOs : []
     }
     const res = await saveStage(req)
     if (res?.code === 200) {
@@ -422,9 +422,7 @@
   border-radius: 4px;
 }
 .table-actions {
-  margin-bottom: 15px;
-  display: flex;
-  align-items: center;
-  gap: 10px;
+  text-align: right;
+	margin-bottom: 10px;
 }
 </style>
diff --git a/src/views/projectManagement/Management/projectDetail.vue b/src/views/projectManagement/Management/projectDetail.vue
index c54a389..b526fc6 100644
--- a/src/views/projectManagement/Management/projectDetail.vue
+++ b/src/views/projectManagement/Management/projectDetail.vue
@@ -319,7 +319,7 @@
       actualStartTime: payload.actualStartTime || null,
       actualEndTime: payload.actualEndTime || null,
       progress: Number(payload.totalProgress ?? payload.completionProgress ?? 0) || 0,
-      attachmentIds: Array.isArray(payload.attachmentIds) ? payload.attachmentIds : []
+      storageBlobDTOs: Array.isArray(payload.storageBlobDTOs) ? payload.storageBlobDTOs : []
     }
     const res = await saveStage(req)
     if (res?.code === 200) {
diff --git a/src/views/projectManagement/projectType/components/ProjectTypeDialog.vue b/src/views/projectManagement/projectType/components/ProjectTypeDialog.vue
index 2888516..bc1e196 100644
--- a/src/views/projectManagement/projectType/components/ProjectTypeDialog.vue
+++ b/src/views/projectManagement/projectType/components/ProjectTypeDialog.vue
@@ -29,24 +29,15 @@
             style="width: 220px"
           />
         </div>
+      </div>
+      <div class="base-info-row">
         <div class="info-item">
           <span class="item-label">闄勪欢</span>
-          <el-upload
-            v-if="isEdit"
-            :action="uploadUrl"
-            :headers="uploadHeaders"
-            :on-success="handleUploadSuccess"
-            :on-remove="handleRemove"
-            v-model:file-list="uploadFileList"
-            :limit="3"
-            name="files"
-            multiple
-          >
-            <el-button type="primary">涓婁紶闄勪欢</el-button>
-          </el-upload>
+          <FileUpload v-if="isEdit" v-model:file-list="uploadFileList" />
           <span v-else class="text-gray-400 text-sm">璇峰厛淇濆瓨鍚庡啀涓婁紶闄勪欢</span>
         </div>
       </div>
+
 
       <!-- 姝ラ閰嶇疆琛ㄦ牸 -->
        <p class="top-tip">璇锋寜鐓ч『搴忛厤缃」鐩樁娈碉紝鎷栨嫿<b>姝ラ</b>鎺掑簭鍗冲彲</p>
@@ -148,8 +139,8 @@
 
     <template #footer>
       <div class="dialog-footer">
-        <el-button @click="visible = false">鍙栨秷</el-button>
         <el-button type="primary" @click="submitForm">鎻愪氦</el-button>
+        <el-button @click="visible = false">鍙栨秷</el-button>
       </div>
     </template>
   </el-dialog>
@@ -162,6 +153,7 @@
 import { ElMessage, ElMessageBox } from 'element-plus';
 import { getToken } from '@/utils/auth';
 import Sortable from 'sortablejs';
+import FileUpload from "@/components/AttachmentUpload/file/index.vue";
 
 const props = defineProps({
   modelValue: Boolean,
@@ -175,16 +167,12 @@
 const formRef = ref(null);
 const userOptions = ref([]);
 const isEdit = ref(false);
-const uploadHeaders = { Authorization: "Bearer " + getToken() };
-// 涓婁紶鍦板潃
-const uploadUrl = import.meta.env.VITE_APP_BASE_API + "/basic/customer-follow/upload";
 let sortable = null;
 
 const form = ref({
   id: undefined,
   name: '',
   description: '',
-  attachmentIds: [],
   savePlanNodeList: []
 });
 const uploadFileList = ref([]);
@@ -204,11 +192,9 @@
         id: props.data.id,
         name: props.data.name,
         description: props.data.description,
-        attachmentIds: Array.isArray(props.data.attachmentIds)
-          ? props.data.attachmentIds
-          : (props.data.attachmentList || []).map(f => f.id).filter(Boolean),
         savePlanNodeList: []
       };
+      uploadFileList.value = props.data.storageBlobDTOs || [];
       
       // 鍥炴樉姝ラ鑺傜偣
       if (props.data.planNodeList && props.data.planNodeList.length > 0) {
@@ -279,7 +265,6 @@
     id: undefined,
     name: '',
     description: '',
-    attachmentIds: [],
     savePlanNodeList: [createDefaultNode()]
   };
   uploadFileList.value = [];
@@ -306,29 +291,6 @@
   if (user) {
     row.leaderName = user.nickName;
   }
-}
-
-/** 澶勭悊鏂囦欢涓婁紶鎴愬姛 */
-function handleUploadSuccess(response, file, fileList) {
-  if (response.code === 200) {
-    const newFile = response.data;
-    const list = Array.isArray(newFile) ? newFile : [newFile];
-    list.forEach(element => {
-      const id = element?.id;
-      if (id && !form.value.attachmentIds.includes(id)) {
-        form.value.attachmentIds.push(id);
-      }
-    });
-  } else {
-    ElMessage.error(response.msg || '涓婁紶澶辫触');
-  }
-}
-
-/** 澶勭悊鏂囦欢绉婚櫎 */
-function handleRemove(file) {
-  const removedId = file?.id || file?.response?.data?.id;
-  if (!removedId) return;
-  form.value.attachmentIds = form.value.attachmentIds.filter(id => id !== removedId);
 }
 
 /** 娣诲姞姝ラ */
@@ -374,6 +336,7 @@
       form.value.savePlanNodeList.forEach((node, index) => {
         node.sort = index;
       });
+      form.value.storageBlobDTOs = uploadFileList.value;
       emit('submit', form.value);
     }
   } catch (error) {
@@ -457,10 +420,7 @@
 }
 
 .dialog-footer {
-  display: flex;
-  justify-content: flex-end;
-  gap: 15px;
-  padding-top: 10px;
+  text-align: center;
 }
 .top-tip {
   
diff --git a/src/views/projectManagement/projectType/index.vue b/src/views/projectManagement/projectType/index.vue
index c2ea441..16a17e0 100644
--- a/src/views/projectManagement/projectType/index.vue
+++ b/src/views/projectManagement/projectType/index.vue
@@ -266,8 +266,6 @@
 
 <style scoped lang="scss">
 .app-container {
-  background-color: #f5f7fa;
-  height: calc(100vh - 84px);
   padding: 20px;
   display: flex;
   flex-direction: column;
diff --git a/src/views/projectManagement/roles/index.vue b/src/views/projectManagement/roles/index.vue
index b3abe10..1e161f8 100644
--- a/src/views/projectManagement/roles/index.vue
+++ b/src/views/projectManagement/roles/index.vue
@@ -60,45 +60,46 @@
     </el-row>
 
     <!-- 琛ㄦ牸鏁版嵁 -->
-    <el-table v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
-      <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="瑙掕壊缂栧彿" prop="no" />
-      <el-table-column label="瑙掕壊鍚嶇О" prop="name" :show-overflow-tooltip="true" />
-      <el-table-column label="鐘舵��" align="center" width="100">
-        <template #default="scope">
-          <el-switch
-              v-model="scope.row.status"
-              :active-value="0"
-              :inactive-value="1"
-              @change="handleStatusChange(scope.row)"
-          ></el-switch>
-        </template>
-      </el-table-column>
-      <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime">
-        <template #default="scope">
-          <span>{{ parseTime(scope.row.createTime) }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column fixed="right" label="鎿嶄綔" align="center" width="120">
-        <template #default="scope">
-          <el-tooltip content="淇敼" placement="top" v-if="scope.row.roleId !== 1">
-            <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
-          </el-tooltip>
-          <el-tooltip content="鍒犻櫎" placement="top" v-if="scope.row.roleId !== 1">
-            <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:role:remove']"></el-button>
-          </el-tooltip>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <pagination
-        v-show="total > 0"
-        :total="total"
-        v-model:page="queryParams.current"
-        v-model:limit="queryParams.size"
-        @pagination="getList"
-    />
-
+		<div class="table_list">
+			<el-table v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
+				<el-table-column type="selection" width="55" align="center" />
+				<el-table-column label="瑙掕壊缂栧彿" prop="no" />
+				<el-table-column label="瑙掕壊鍚嶇О" prop="name" :show-overflow-tooltip="true" />
+				<el-table-column label="鐘舵��" align="center" width="100">
+					<template #default="scope">
+						<el-switch
+							v-model="scope.row.status"
+							:active-value="0"
+							:inactive-value="1"
+							@change="handleStatusChange(scope.row)"
+						></el-switch>
+					</template>
+				</el-table-column>
+				<el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime">
+					<template #default="scope">
+						<span>{{ parseTime(scope.row.createTime) }}</span>
+					</template>
+				</el-table-column>
+				<el-table-column fixed="right" label="鎿嶄綔" align="center" width="120">
+					<template #default="scope">
+						<el-tooltip content="淇敼" placement="top" v-if="scope.row.roleId !== 1">
+							<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
+						</el-tooltip>
+						<el-tooltip content="鍒犻櫎" placement="top" v-if="scope.row.roleId !== 1">
+							<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:role:remove']"></el-button>
+						</el-tooltip>
+					</template>
+				</el-table-column>
+			</el-table>
+			
+			<pagination
+				v-show="total > 0"
+				:total="total"
+				v-model:page="queryParams.current"
+				v-model:limit="queryParams.size"
+				@pagination="getList"
+			/>
+		</div>
     <!-- 娣诲姞鎴栦慨鏀硅鑹查厤缃璇濇 -->
     <el-dialog :title="title" v-model="open" width="500px" append-to-body>
       <el-form ref="roleRef" :model="form" :rules="rules" label-width="100px">
diff --git a/src/views/qualityManagement/finalInspection/index.vue b/src/views/qualityManagement/finalInspection/index.vue
index db44222..a2d1acc 100644
--- a/src/views/qualityManagement/finalInspection/index.vue
+++ b/src/views/qualityManagement/finalInspection/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form mb20">
       <div>
         <span class="search_title">浜у搧鍚嶇О锛�</span>
         <el-input
@@ -11,11 +11,12 @@
             clearable
             :prefix-icon="Search"
         />
-        <span  style="margin-left: 10px" class="search_title">妫�娴嬫棩鏈燂細</span>
-        <el-date-picker  v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
-                         placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
+        <span style="margin-left: 10px" class="search_title">妫�娴嬫棩鏈燂細</span>
+        <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
+                        placeholder="璇烽�夋嫨" clearable @change="changeDaterange"/>
         <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
-        >鎼滅储</el-button
+        >鎼滅储
+        </el-button
         >
       </div>
       <div>
@@ -40,37 +41,37 @@
     <InspectionFormDia ref="inspectionFormDia" @close="handleQuery"></InspectionFormDia>
     <FormDia ref="formDia" @close="handleQuery"></FormDia>
     <files-dia ref="filesDia" @close="handleQuery"></files-dia>
-		<el-dialog v-model="dialogFormVisible" title="缂栬緫妫�楠屽憳" width="30%"
-							 @close="closeDia">
-			<el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
-				<el-form-item label="妫�楠屽憳锛�" prop="checkName">
-					<el-select v-model="form.checkName" placeholder="璇烽�夋嫨" clearable>
-						<el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
-											 :value="item.nickName"/>
-					</el-select>
-				</el-form-item>
-			</el-form>
-			<template #footer>
-				<div class="dialog-footer">
-					<el-button type="primary" @click="submitForm">纭</el-button>
-					<el-button @click="closeDia">鍙栨秷</el-button>
-				</div>
-			</template>
-		</el-dialog>
+    <el-dialog v-model="dialogFormVisible" title="缂栬緫妫�楠屽憳" width="30%"
+               @close="closeDia">
+      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
+        <el-form-item label="妫�楠屽憳锛�" prop="checkName">
+          <el-select v-model="form.checkName" placeholder="璇烽�夋嫨" clearable>
+            <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
+                       :value="item.nickName"/>
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitForm">纭</el-button>
+          <el-button @click="closeDia">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { Search } from "@element-plus/icons-vue";
+import {Search} from "@element-plus/icons-vue";
 import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
 import InspectionFormDia from "@/views/qualityManagement/finalInspection/components/inspectionFormDia.vue";
 import FormDia from "@/views/qualityManagement/finalInspection/components/formDia.vue";
 import {ElMessageBox} from "element-plus";
 import {
-	downloadQualityInspect,
-	qualityInspectDel,
-	qualityInspectListPage, qualityInspectUpdate,
-	submitQualityInspect
+  downloadQualityInspect,
+  qualityInspectDel,
+  qualityInspectListPage, qualityInspectUpdate,
+  submitQualityInspect
 } from "@/api/qualityManagement/rawMaterialInspection.js";
 import FilesDia from "@/views/qualityManagement/finalInspection/components/filesDia.vue";
 import dayjs from "dayjs";
@@ -84,15 +85,20 @@
     entryDateStart: undefined,
     entryDateEnd: undefined,
   },
-	rules: {
-		checkName: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
-	},
+  rules: {
+    checkName: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+  },
 });
-const { searchForm } = toRefs(data);
+const {searchForm} = toRefs(data);
 const tableColumn = ref([
   {
     label: "妫�娴嬫棩鏈�",
     prop: "checkTime",
+    width: 120
+  },
+  {
+    label: "閿�鍞崟鍙�",
+    prop: "salesContractNo",
     width: 120
   },
   {
@@ -140,17 +146,17 @@
       }
     },
   },
-	{
-		label: "鎻愪氦鐘舵��",
-		prop: "inspectState",
-		formatData: (params) => {
-			if (params) {
-				return "宸叉彁浜�";
-			} else {
-				return "鏈彁浜�";
-			}
-		},
-	},
+  {
+    label: "鎻愪氦鐘舵��",
+    prop: "inspectState",
+    formatData: (params) => {
+      if (params) {
+        return "宸叉彁浜�";
+      } else {
+        return "鏈彁浜�";
+      }
+    },
+  },
   {
     dataType: "action",
     label: "鎿嶄綔",
@@ -164,15 +170,15 @@
         clickFun: (row) => {
           openForm("edit", row);
         },
-				disabled: (row) => {
-					// 宸叉彁浜ゅ垯绂佺敤
-					if (row.inspectState == 1) return true;
-					// 濡傛灉妫�楠屽憳鏈夊�硷紝鍙湁褰撳墠鐧诲綍鐢ㄦ埛鑳界紪杈�
-					if (row.checkName) {
-						return row.checkName !== userStore.nickName;
-					}
-					return false;
-				}
+        disabled: (row) => {
+          // 宸叉彁浜ゅ垯绂佺敤
+          if (row.inspectState == 1) return true;
+          // 濡傛灉妫�楠屽憳鏈夊�硷紝鍙湁褰撳墠鐧诲綍鐢ㄦ埛鑳界紪杈�
+          if (row.checkName) {
+            return row.checkName !== userStore.nickName;
+          }
+          return false;
+        }
       },
       {
         name: "闄勪欢",
@@ -181,43 +187,43 @@
           openFilesFormDia(row);
         },
       },
-			{
-				name: "鎻愪氦",
-				type: "text",
-				clickFun: (row) => {
-					submit(row.id);
-				},
-				disabled: (row) => {
-					// 宸叉彁浜ゅ垯绂佺敤
-					if (row.inspectState == 1) return true;
-					// 濡傛灉妫�楠屽憳鏈夊�硷紝鍙湁褰撳墠鐧诲綍鐢ㄦ埛鑳芥彁浜�
-					if (row.checkName) {
-						return row.checkName !== userStore.nickName;
-					}
-					return false;
-				}
-			},
-			{
-				name: "鍒嗛厤妫�楠屽憳",
-				type: "text",
-				clickFun: (row) => {
-					if (!row.checkName) {
-						open(row)
-					} else {
-						proxy.$modal.msgError("妫�楠屽憳宸插瓨鍦�");
-					}
-				},
-				disabled: (row) => {
-					return row.inspectState == 1 || row.checkName;
-				}
-			},
-			{
-				name: "涓嬭浇",
-				type: "text",
-				clickFun: (row) => {
-					downLoadFile(row);
-				},
-			},
+      {
+        name: "鎻愪氦",
+        type: "text",
+        clickFun: (row) => {
+          submit(row.id);
+        },
+        disabled: (row) => {
+          // 宸叉彁浜ゅ垯绂佺敤
+          if (row.inspectState == 1) return true;
+          // 濡傛灉妫�楠屽憳鏈夊�硷紝鍙湁褰撳墠鐧诲綍鐢ㄦ埛鑳芥彁浜�
+          if (row.checkName) {
+            return row.checkName !== userStore.nickName;
+          }
+          return false;
+        }
+      },
+      {
+        name: "鍒嗛厤妫�楠屽憳",
+        type: "text",
+        clickFun: (row) => {
+          if (!row.checkName) {
+            open(row)
+          } else {
+            proxy.$modal.msgError("妫�楠屽憳宸插瓨鍦�");
+          }
+        },
+        disabled: (row) => {
+          return row.inspectState == 1 || row.checkName;
+        }
+      },
+      {
+        name: "涓嬭浇",
+        type: "text",
+        clickFun: (row) => {
+          downLoadFile(row);
+        },
+      },
     ],
   },
 ]);
@@ -233,11 +239,11 @@
 const formDia = ref()
 const filesDia = ref()
 const inspectionFormDia = ref()
-const { proxy } = getCurrentInstance()
+const {proxy} = getCurrentInstance()
 const userStore = useUserStore()
 const userList = ref([]);
 const form = ref({
-	checkName: ""
+  checkName: ""
 });
 const dialogFormVisible = ref(false);
 
@@ -263,7 +269,7 @@
 };
 const getList = () => {
   tableLoading.value = true;
-  const params = { ...searchForm.value, ...page };
+  const params = {...searchForm.value, ...page};
   params.entryDate = undefined
   qualityInspectListPage({...params, inspectType: 2}).then(res => {
     tableLoading.value = false;
@@ -338,56 +344,56 @@
 
 // 鎻愪环
 const submit = async (id) => {
-	const res = await submitQualityInspect({id: id})
-	if (res.code === 200) {
-		proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-		getList();
-	}
+  const res = await submitQualityInspect({id: id})
+  if (res.code === 200) {
+    proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+    getList();
+  }
 }
 
 // 鍏抽棴寮规
 const closeDia = () => {
-	proxy.resetForm("formRef");
-	dialogFormVisible.value = false;
+  proxy.resetForm("formRef");
+  dialogFormVisible.value = false;
 };
 
 const submitForm = () => {
-	if (currentRow.value) {
-		const data = {
-			...form.value,
-			id: currentRow.value.id
-		}
-		qualityInspectUpdate(data).then(res => {
-			proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-			closeDia();
-			getList();
-		})
-	}
+  if (currentRow.value) {
+    const data = {
+      ...form.value,
+      id: currentRow.value.id
+    }
+    qualityInspectUpdate(data).then(res => {
+      proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+      closeDia();
+      getList();
+    })
+  }
 };
 
 const open = async (row) => {
-	let userLists = await userListNoPage();
-	userList.value = userLists.data;
-	currentRow.value = row
-	dialogFormVisible.value = true
+  let userLists = await userListNoPage();
+  userList.value = userLists.data;
+  currentRow.value = row
+  dialogFormVisible.value = true
 }
 
 const downLoadFile = (row) => {
-	downloadQualityInspect({ id: row.id }).then((blobData) => {
-		const blob = new Blob([blobData], {
-			type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
-		})
-		const downloadUrl = window.URL.createObjectURL(blob)
-		
-		const link = document.createElement('a')
-		link.href = downloadUrl
-		link.download = '鍘熸潗鏂欐楠屾姤鍛�.docx'
-		document.body.appendChild(link)
-		link.click()
-		
-		document.body.removeChild(link)
-		window.URL.revokeObjectURL(downloadUrl)
-	})
+  downloadQualityInspect({id: row.id}).then((blobData) => {
+    const blob = new Blob([blobData], {
+      type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+    })
+    const downloadUrl = window.URL.createObjectURL(blob)
+
+    const link = document.createElement('a')
+    link.href = downloadUrl
+    link.download = '鍘熸潗鏂欐楠屾姤鍛�.docx'
+    document.body.appendChild(link)
+    link.click()
+
+    document.body.removeChild(link)
+    window.URL.revokeObjectURL(downloadUrl)
+  })
 };
 onMounted(() => {
   getList();
diff --git a/src/views/qualityManagement/metricBinding/index.vue b/src/views/qualityManagement/metricBinding/index.vue
index 2a78e1d..1ac268a 100644
--- a/src/views/qualityManagement/metricBinding/index.vue
+++ b/src/views/qualityManagement/metricBinding/index.vue
@@ -432,10 +432,6 @@
 </script>
 
 <style scoped>
-.metric-binding {
-  padding: 0;
-}
-
 .metric-binding-row {
   width: 100%;
 }
diff --git a/src/views/qualityManagement/metricMaintenance/index.vue b/src/views/qualityManagement/metricMaintenance/index.vue
index 5b2c6bd..deb3e5f 100644
--- a/src/views/qualityManagement/metricMaintenance/index.vue
+++ b/src/views/qualityManagement/metricMaintenance/index.vue
@@ -127,10 +127,10 @@
         <el-table-column prop="defaultValue" label="榛樿鍊�" min-width="120" />
         <el-table-column label="鎿嶄綔" width="140" fixed="right" align="center">
           <template #default="{ row }">
-            <el-button link type="primary" size="small" :disabled="isStandardReadonly" @click="openParamDialog('edit', row)">
+            <el-button link type="primary" :disabled="isStandardReadonly" @click="openParamDialog('edit', row)">
               缂栬緫
             </el-button>
-            <el-button link type="danger" size="small" :disabled="isStandardReadonly" @click="handleParamDelete(row)">
+            <el-button link type="danger" :disabled="isStandardReadonly" @click="handleParamDelete(row)">
               鍒犻櫎
             </el-button>
           </template>
@@ -432,6 +432,18 @@
 
 const handleSelectionChange = (selection) => {
   selectedRows.value = selection
+
+  if (!selection.length) {
+    currentStandard.value = null
+    detailTableData.value = []
+    return
+  }
+
+  const nextStandard = selection[selection.length - 1]
+  if (currentStandard.value?.id === nextStandard.id) return
+
+  currentStandard.value = nextStandard
+  loadDetail(nextStandard.id)
 }
 
 // 鎵归噺瀹℃牳锛氱姸鎬� 1=鎵瑰噯锛�2=鎾ら攢
@@ -698,11 +710,6 @@
 </script>
 
 <style scoped>
-.metric-maintenance {
-  padding: 0;
-  min-width: 0;
-}
-
 .metric-maintenance-row {
   width: 100%;
 }
@@ -829,4 +836,4 @@
   width: 100%;
   margin-top: 4px;
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/views/qualityManagement/nonconformingManagement/index.vue b/src/views/qualityManagement/nonconformingManagement/index.vue
index 55d2472..6306397 100644
--- a/src/views/qualityManagement/nonconformingManagement/index.vue
+++ b/src/views/qualityManagement/nonconformingManagement/index.vue
@@ -1,24 +1,21 @@
 <template>
   <div class="app-container">
     <div class="search_form">
-      <div style="display: flex;flex-direction: row;align-items: center;">
-        <div>
-          <span class="search_title">绫诲瀷锛�</span>
+      <el-form :model="searchForm" inline style="margin-bottom: 0;">
+        <el-form-item label="绫诲瀷锛�">
           <el-select v-model="searchForm.inspectType" clearable style="width: 200px" @change="handleQuery">
             <el-option label="鍘熸潗鏂欐楠�" :value="0" />
             <el-option label="杩囩▼妫�楠�" :value="1" />
             <el-option label="鍑哄巶妫�楠�" :value="2" />
           </el-select>
-        </div>
-        <div style="margin-left: 10px">
-          <span class="search_title">鐘舵�侊細</span>
+        </el-form-item>
+        <el-form-item label="鐘舵�侊細">
           <el-select v-model="searchForm.inspectState" clearable style="width: 200px" @change="handleQuery">
             <el-option label="寰呭鐞�" :value="0" />
             <el-option label="宸插鐞�" :value="1" />
           </el-select>
-        </div>
-        <div style="margin-left: 10px">
-          <span class="search_title">浜у搧鍚嶇О锛�</span>
+        </el-form-item>
+        <el-form-item label="浜у搧鍚嶇О锛�">
           <el-input
               v-model="searchForm.productName"
               style="width: 200px"
@@ -27,19 +24,22 @@
               clearable
               :prefix-icon="Search"
           />
-        </div>
-        <span  style="margin-left: 10px" class="search_title">妫�娴嬫棩鏈燂細</span>
-        <el-date-picker  v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
-												 style="width: 300px"
-                         placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
-        <el-button type="primary" @click="handleQuery" style="margin-left: 10px">鎼滅储</el-button>
-      </div>
-      <div>
+        </el-form-item>
+        <el-form-item label="妫�娴嬫棩鏈燂細">
+          <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
+                          style="width: 300px"
+                          placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleQuery">鎼滅储</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <div class="mb20" style="text-align: right;">
         <el-button type="primary" @click="openForm('add')">鏂板</el-button>
         <el-button @click="handleOut">瀵煎嚭</el-button>
         <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
       </div>
-    </div>
     <div class="table_list">
       <PIMTable
           rowKey="id"
diff --git a/src/views/qualityManagement/processInspection/index.vue b/src/views/qualityManagement/processInspection/index.vue
index cbeab71..e5504b6 100644
--- a/src/views/qualityManagement/processInspection/index.vue
+++ b/src/views/qualityManagement/processInspection/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form mb20">
       <div>
         <span class="search_title">宸ュ簭锛�</span>
         <el-input
diff --git a/src/views/qualityManagement/rawMaterialInspection/index.vue b/src/views/qualityManagement/rawMaterialInspection/index.vue
index 26504b0..cc2c151 100644
--- a/src/views/qualityManagement/rawMaterialInspection/index.vue
+++ b/src/views/qualityManagement/rawMaterialInspection/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form mb20">
       <div>
         <span class="search_title">渚涘簲鍟嗭細</span>
         <el-input
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue b/src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue
index da736e1..58c83d8 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue
@@ -45,7 +45,7 @@
 import PanelHeader from '../PanelHeader.vue'
 import DateTypeSwitch from '../DateTypeSwitch.vue'
 import { customerRevenueAnalysis } from '@/api/viewIndex.js'
-import { listCustomer } from '@/api/basicData/customerFile.js'
+import { listCustomer } from '@/api/basicData/customer.js'
 
 const dateType = ref(1) // 1=鍛� 2=鏈� 3=瀛e害
 const customerValue = ref(null)
diff --git a/src/views/reportAnalysis/dataDashboard/index0.vue b/src/views/reportAnalysis/dataDashboard/index0.vue
index 5c318c8..a89a174 100644
--- a/src/views/reportAnalysis/dataDashboard/index0.vue
+++ b/src/views/reportAnalysis/dataDashboard/index0.vue
@@ -316,7 +316,7 @@
   	getWorkInProcessTurnover
 } from "@/api/viewIndex.js";
 import {staffOnJobListPage} from "@/api/personnelManagement/employeeRecord.js";
-import {listCustomer} from "@/api/basicData/customerFile.js";
+import { listCustomer } from '@/api/basicData/customer.js'
 import {listSupplier} from "@/api/basicData/supplierManageFile.js";
 import {getLedgerPage} from "@/api/equipmentManagement/ledger.js";
 import {getRepairPage} from "@/api/equipmentManagement/repair.js";
diff --git a/src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue b/src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue
index 33f431d..9f6a8c1 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue
@@ -157,9 +157,11 @@
 
 .panel-item-customers {
   border: 1px solid #1a58b0;
+  border-radius: 16px;
   padding: 18px;
   width: 100%;
   height: 449px;
+  overflow: hidden;
 }
 
 .chart-wrapper {
diff --git a/src/views/reportAnalysis/productionAnalysis/components/left-top.vue b/src/views/reportAnalysis/productionAnalysis/components/left-top.vue
index 932def8..fd52b1b 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/left-top.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/left-top.vue
@@ -189,9 +189,11 @@
 
 .panel-item-customers {
   border: 1px solid #1a58b0;
+  border-radius: 16px;
   padding: 18px;
   width: 100%;
   height: 449px;
+  overflow: hidden;
 }
 
 .filters-row {
diff --git a/src/views/reportAnalysis/reportManagement/index.vue b/src/views/reportAnalysis/reportManagement/index.vue
index dc9d486..cd8ddd5 100644
--- a/src/views/reportAnalysis/reportManagement/index.vue
+++ b/src/views/reportAnalysis/reportManagement/index.vue
@@ -912,7 +912,6 @@
 <style scoped>
 .report-management {
   padding: 20px;
-  background-color: #f5f5f5;
   min-height: 100vh;
   /* height: 87vh;
   overflow: hidden; */
diff --git a/src/views/safeProduction/accidentReportingRecord/index.vue b/src/views/safeProduction/accidentReportingRecord/index.vue
index ff5b301..5a6c345 100644
--- a/src/views/safeProduction/accidentReportingRecord/index.vue
+++ b/src/views/safeProduction/accidentReportingRecord/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form mb20">
       <div>
         <span class="search_title">浜嬫晠鍚嶇О锛�</span>
         <el-input v-model="searchForm.accidentName"
diff --git a/src/views/safeProduction/dangerInvestigation/index.vue b/src/views/safeProduction/dangerInvestigation/index.vue
index 49965e9..cb09e52 100644
--- a/src/views/safeProduction/dangerInvestigation/index.vue
+++ b/src/views/safeProduction/dangerInvestigation/index.vue
@@ -415,17 +415,12 @@
         </el-row>
       </el-form>
     </FormDialog>
-    <!-- 闄勪欢鍒楄〃寮圭獥 -->
-    <FileListDialog ref="fileListRef"
-                    v-model="fileListDialogVisible"
-                    :show-upload-button="true"
-                    :show-delete-button="true"
-                    :is-show-pagination="true"
-                    :page="filePagination"
-                    :upload-method="handleUpload"
-                    :delete-method="handleFileDelete"
-                    @pagination="paginationSearch"
-                    title="闄勪欢鍒楄〃" />
+    <FileListDialog
+        v-if="fileListDialogVisible"
+        :record-id="currentRecordId"
+        record-type="safe_hidden"
+        v-model:visible="fileListDialogVisible"/>
+
   </div>
 </template>
 
@@ -436,7 +431,6 @@
   import { ElMessageBox, ElMessage } from "element-plus";
   import useUserStore from "@/store/modules/user";
   import { userListNoPage } from "@/api/system/user.js";
-  import FileListDialog from "@/components/Dialog/FileListDialog.vue";
   import FormDialog from "@/components/Dialog/FormDialog.vue";
   import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
   import {
@@ -445,13 +439,9 @@
     safeHiddenUpdate,
     safeHiddenDel,
     fileListPage,
-    safeHiddenFileAdd,
-    safeHiddenFileDel,
   } from "@/api/safeProduction/dangerInvestigation.js";
   import useFormData from "@/hooks/useFormData.js";
-  import request from "@/utils/request";
   import dayjs from "dayjs";
-  import { get } from "@vueuse/core";
 
   const userStore = useUserStore();
   const { proxy } = getCurrentInstance();
@@ -459,6 +449,7 @@
   const selectedRows = ref([]);
   const userList = ref([]);
   const tableLoading = ref(false);
+  const currentRecordId = ref(0);
   const page = reactive({
     current: 1,
     size: 100,
@@ -943,18 +934,8 @@
   const fileListDialogVisible = ref(false);
   const currentFileRow = ref(null);
   const downLoadFile = row => {
-    currentFileRow.value = row;
-    fileListPage({
-      safeHiddenId: row.id,
-      current: filePagination.value.current,
-      size: filePagination.value.size,
-    }).then(res => {
-      if (fileListRef.value) {
-        fileListRef.value.open(res.data.records || []);
-        console.log("res.data", res.data);
-        filePagination.value.total = res.data.total || 0;
-      }
-    });
+    currentRecordId.value = row.id;
+    fileListDialogVisible.value = true;
   };
   const currentUserId = ref("");
   const currentUserName = ref("");
@@ -990,147 +971,6 @@
       userList.value = res.data;
     });
   });
-  // 涓婁紶闄勪欢
-  const handleUpload = async () => {
-    if (!currentFileRow.value) {
-      proxy.$modal.msgWarning("璇峰厛閫夋嫨鏁版嵁");
-      return null;
-    }
-
-    return new Promise(resolve => {
-      // 鍒涘缓涓�涓殣钘忕殑鏂囦欢杈撳叆鍏冪礌
-      const input = document.createElement("input");
-      input.type = "file";
-      input.style.display = "none";
-      input.onchange = async e => {
-        const file = e.target.files[0];
-        if (!file) {
-          resolve(null);
-          return;
-        }
-
-        try {
-          // 浣跨敤 FormData 涓婁紶鏂囦欢
-          const formData = new FormData();
-          formData.append("file", file);
-
-          const uploadRes = await request({
-            url: "/file/upload",
-            method: "post",
-            data: formData,
-            headers: {
-              "Content-Type": "multipart/form-data",
-              Authorization: `Bearer ${getToken()}`,
-            },
-          });
-
-          if (uploadRes.code === 200) {
-            // 淇濆瓨闄勪欢淇℃伅
-            const fileData = {
-              safeHiddenId: currentFileRow.value.id,
-              name: uploadRes.data.originalName || file.name,
-              url: uploadRes.data.tempPath || uploadRes.data.url,
-            };
-
-            const saveRes = await safeHiddenFileAdd(fileData);
-            if (saveRes.code === 200) {
-              proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛");
-              // 閲嶆柊鍔犺浇鏂囦欢鍒楄〃
-              const listRes = await fileListPage({
-                safeHiddenId: currentFileRow.value.id,
-                current: filePagination.value.current,
-                size: filePagination.value.size,
-              });
-              if (listRes.code === 200 && fileListRef.value) {
-                const fileList = (listRes.data?.records || []).map(item => ({
-                  name: item.name,
-                  url: item.url,
-                  id: item.id,
-                  ...item,
-                }));
-                fileListRef.value.setList(fileList);
-                filePagination.value.total = listRes.data?.total || 0;
-              }
-              // 杩斿洖鏂版枃浠朵俊鎭�
-              resolve({
-                name: fileData.name,
-                url: fileData.url,
-                id: saveRes.data?.id,
-              });
-            } else {
-              proxy.$modal.msgError(saveRes.msg || "鏂囦欢淇濆瓨澶辫触");
-              resolve(null);
-            }
-          } else {
-            proxy.$modal.msgError(uploadRes.msg || "鏂囦欢涓婁紶澶辫触");
-            resolve(null);
-          }
-        } catch (error) {
-          proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触");
-          resolve(null);
-        } finally {
-          document.body.removeChild(input);
-        }
-      };
-
-      document.body.appendChild(input);
-      input.click();
-    });
-  };
-  // 鍒嗛〉鏌ヨ鏂囦欢鍒楄〃
-  const paginationSearch = async (page, size) => {
-    filePagination.value.current = page;
-    filePagination.value.size = size;
-    const listRes = await fileListPage({
-      safeHiddenId: currentFileRow.value.id,
-      current: filePagination.value.current,
-      size: filePagination.value.size,
-    });
-    if (listRes.code === 200) {
-      const fileList = (listRes.data?.records || []).map(item => ({
-        name: item.name,
-        url: item.url,
-        id: item.id,
-        ...item,
-      }));
-      fileListRef.value.setList(fileList);
-      filePagination.value.total = listRes.data?.total || 0;
-    }
-  };
-  // 鍒犻櫎闄勪欢
-  const handleFileDelete = async row => {
-    try {
-      const res = await safeHiddenFileDel([row.id]);
-      if (res.code === 200) {
-        proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-        // 閲嶆柊鍔犺浇鏂囦欢鍒楄〃
-        if (currentFileRow.value && fileListRef.value) {
-          const listRes = await fileListPage({
-            safeHiddenId: currentFileRow.value.id,
-            current: filePagination.value.current,
-            size: filePagination.value.size,
-          });
-          if (listRes.code === 200) {
-            const fileList = (listRes.data?.records || []).map(item => ({
-              name: item.name,
-              url: item.url,
-              id: item.id,
-              ...item,
-            }));
-            fileListRef.value.setList(fileList);
-            filePagination.value.total = listRes.data?.total || 0;
-          }
-        }
-        return true; // 杩斿洖 true 琛ㄧず鍒犻櫎鎴愬姛锛岀粍浠朵細鏇存柊鍒楄〃
-      } else {
-        proxy.$modal.msgError(res.msg || "鍒犻櫎澶辫触");
-        return false;
-      }
-    } catch (error) {
-      proxy.$modal.msgError("鍒犻櫎澶辫触");
-      return false;
-    }
-  };
 </script>
 
 <style scoped lang="scss">
diff --git a/src/views/safeProduction/emergencyPlanReview/index.vue b/src/views/safeProduction/emergencyPlanReview/index.vue
index eb68508..1850c18 100644
--- a/src/views/safeProduction/emergencyPlanReview/index.vue
+++ b/src/views/safeProduction/emergencyPlanReview/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form mb20">
       <div>
         <span class="search_title">搴旀�ラ妗堝悕绉帮細</span>
         <el-input v-model="searchForm.planName"
diff --git a/src/views/safeProduction/hazardSourceLedger/index.vue b/src/views/safeProduction/hazardSourceLedger/index.vue
index 9aa131a..416202e 100644
--- a/src/views/safeProduction/hazardSourceLedger/index.vue
+++ b/src/views/safeProduction/hazardSourceLedger/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form mb20">
       <div>
         <span class="search_title">鍗遍櫓婧愬悕绉帮細</span>
         <el-input v-model="searchForm.name"
diff --git a/src/views/safeProduction/hazardousMaterialsControl/index.vue b/src/views/safeProduction/hazardousMaterialsControl/index.vue
index a53490c..e43b4f6 100644
--- a/src/views/safeProduction/hazardousMaterialsControl/index.vue
+++ b/src/views/safeProduction/hazardousMaterialsControl/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form mb20">
       <div>
         <span class="search_title">鍗遍櫓婧愬悕绉帮細</span>
         <el-input v-model="searchForm.name"
diff --git a/src/views/safeProduction/safeQualifications/index.vue b/src/views/safeProduction/safeQualifications/index.vue
index e61a39c..39111be 100644
--- a/src/views/safeProduction/safeQualifications/index.vue
+++ b/src/views/safeProduction/safeQualifications/index.vue
@@ -110,7 +110,7 @@
             <el-button link
                        type="primary"
                        size="small"
-                       @click="downLoadFile(scope.row)">闄勪欢</el-button>
+                       @click="openFileDialog(scope.row)">闄勪欢</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -202,17 +202,8 @@
         </el-row>
       </el-form>
     </FormDialog>
-    <!-- 闄勪欢鍒楄〃寮圭獥 -->
-    <FileListDialog ref="fileListRef"
-                    v-model="fileListDialogVisible"
-                    :show-upload-button="true"
-                    :show-delete-button="true"
-                    :is-show-pagination="true"
-                    :page="filePagination"
-                    :upload-method="handleUpload"
-                    :delete-method="handleFileDelete"
-                    @pagination="paginationSearch"
-                    title="闄勪欢鍒楄〃" />
+<!-- todo 闄勪欢棰勮鐩稿叧 -->
+    <FileList v-if="fileDialogVisible"  v-model:visible="fileDialogVisible" record-type="safe_certification" :record-id="recordId"  />
   </div>
 </template>
 
@@ -223,7 +214,6 @@
   import { ElMessageBox, ElMessage } from "element-plus";
   import useUserStore from "@/store/modules/user";
   import { userListNoPage } from "@/api/system/user.js";
-  import FileListDialog from "@/components/Dialog/FileListDialog.vue";
   import FormDialog from "@/components/Dialog/FormDialog.vue";
   import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
   import {
@@ -238,7 +228,7 @@
   import useFormData from "@/hooks/useFormData.js";
   import request from "@/utils/request";
   import dayjs from "dayjs";
-
+  const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
   const userStore = useUserStore();
   const { proxy } = getCurrentInstance();
   const tableData = ref([]);
@@ -408,7 +398,9 @@
         executionDate: "",
       };
     } else {
-      form.value = row;
+      // 鍏抽敭锛氱紪杈戞椂涓嶈鐩存帴寮曠敤琛ㄦ牸琛屽璞★紝閬垮厤鍙栨秷/閲嶇疆鏃舵妸鍒楄〃鏁版嵁涓�璧锋竻绌�
+      // 浣跨敤娣辨嫹璐濇柇寮�寮曠敤鍏崇郴
+      form.value = JSON.parse(JSON.stringify(row || {}));
     }
     dialogFormVisible.value = true;
   };
@@ -522,19 +514,17 @@
     size: 10,
     total: 0,
   });
-  const downLoadFile = row => {
-    currentFileRow.value = row;
-    fileListPage({
-      safeCertificationId: row.id,
-      current: filePagination.value.current,
-      size: filePagination.value.size,
-    }).then(res => {
-      if (fileListRef.value) {
-        fileListRef.value.open(res.data.records);
-      }
-      filePagination.value.total = res.data.total || 0;
-    });
-  };
+
+  // 鎵撳紑闄勪欢寮圭獥
+  const recordId =ref(0)
+  const fileDialogVisible = ref(false)
+
+  // 鎵撳紑闄勪欢寮规
+  const openFileDialog = async (row) => {
+    recordId.value = row.id
+    fileDialogVisible.value = true
+  }
+  
   const currentFactoryName = ref("");
   const getCurrentFactoryName = async () => {
     let res = await userStore.getInfo();
diff --git a/src/views/safeProduction/safeWorkApproval/components/infoFormDia.vue b/src/views/safeProduction/safeWorkApproval/components/infoFormDia.vue
index 02b728e..a0146ba 100644
--- a/src/views/safeProduction/safeWorkApproval/components/infoFormDia.vue
+++ b/src/views/safeProduction/safeWorkApproval/components/infoFormDia.vue
@@ -171,26 +171,7 @@
           <el-col :span="24">
             <el-form-item label="闄勪欢鏉愭枡锛�"
                           prop="remark">
-              <el-upload v-model:file-list="fileList"
-                         :action="upload.url"
-                         multiple
-                         ref="fileUpload"
-                         auto-upload
-                         :headers="upload.headers"
-                         :before-upload="handleBeforeUpload"
-                         :on-error="handleUploadError"
-                         :on-success="handleUploadSuccess"
-                         :on-remove="handleRemove">
-                <el-button type="primary"
-                           v-if="operationType !== 'view'">涓婁紶</el-button>
-                <template #tip
-                          v-if="operationType !== 'view'">
-                  <div class="el-upload__tip">
-                    鏂囦欢鏍煎紡鏀寔
-                    doc锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�7z
-                  </div>
-                </template>
-              </el-upload>
+              <FileUpload v-model:file-list="fileList" />
             </el-form-item>
           </el-col>
         </el-row>
@@ -222,17 +203,12 @@
   import useUserStore from "@/store/modules/user";
   import { getCurrentDate } from "@/utils/index.js";
   import log from "@/views/monitor/job/log.vue";
+  import FileUpload from "@/components/AttachmentUpload/file/index.vue";
   const userStore = useUserStore();
 
   const dialogFormVisible = ref(false);
   const operationType = ref("");
   const fileList = ref([]);
-  const upload = reactive({
-    // 涓婁紶鐨勫湴鍧�
-    url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
-    // 璁剧疆涓婁紶鐨勮姹傚ご閮�
-    headers: { Authorization: "Bearer " + getToken() },
-  });
   const data = reactive({
     form: {
       approveTime: "",
@@ -242,7 +218,7 @@
       approveDeptName: "",
       approveReason: "",
       checkResult: "",
-      tempFileIds: [],
+      storageBlobDTOs: [],
       approverList: [], // 鏂板瀛楁锛屽瓨鍌ㄦ墍鏈夎妭鐐圭殑瀹℃壒浜篿d
       startDate: "", // 璇峰亣寮�濮嬫椂闂�
       endDate: "", // 璇峰亣缁撴潫鏃堕棿
@@ -317,12 +293,12 @@
     // 鍔犺浇閮ㄩ棬閫夐」锛屽苟鍦ㄥ姞杞藉畬鎴愬悗璁剧疆閮ㄩ棬鍚嶇О
     getProductOptions();
     if (operationType.value === "edit") {
-      fileList.value = row.commonFileList;
-      form.value.tempFileIds = fileList.value.map(file => file.id);
+      fileList.value = row.storageBlobVOs;
       currentApproveStatus.value = row.approveStatus;
       approveProcessGetInfo({ id: row.approveId, approveReason: "1" }).then(
         res => {
           form.value = { ...res.data };
+          form.value.storageBlobDTOs = res.data.storageBlobVOS;
           // 鍙嶆樉瀹℃壒浜�
           if (res.data && res.data.approveUserIds) {
             const userIds = res.data.approveUserIds.split(",");
@@ -412,6 +388,7 @@
         return;
       }
     }
+    form.value.storageBlobDTOs = fileList.value;
     proxy.$refs.formRef.validate(valid => {
       if (valid) {
         if (operationType.value === "add" || currentApproveStatus.value == 3) {
@@ -435,47 +412,6 @@
     dialogFormVisible.value = false;
     emit("close");
   };
-
-  // 涓婁紶鍓嶆牎妫�
-  function handleBeforeUpload(file) {
-    // 鏍℃鏂囦欢澶у皬
-    // if (file.size > 1024 * 1024 * 10) {
-    //   proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃10MB!");
-    //   return false;
-    // }
-    proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
-    return true;
-  }
-  // 涓婁紶澶辫触
-  function handleUploadError(err) {
-    proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
-    proxy.$modal.closeLoading();
-  }
-  // 涓婁紶鎴愬姛鍥炶皟
-  function handleUploadSuccess(res, file, uploadFiles) {
-    proxy.$modal.closeLoading();
-    if (res.code === 200) {
-      // 纭繚 tempFileIds 瀛樺湪涓斾负鏁扮粍
-      if (!form.value.tempFileIds) {
-        form.value.tempFileIds = [];
-      }
-      form.value.tempFileIds.push(res.data.tempId);
-      proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
-    } else {
-      proxy.$modal.msgError(res.msg);
-      proxy.$refs.fileUpload.handleRemove(file);
-    }
-  }
-  // 绉婚櫎鏂囦欢
-  function handleRemove(file) {
-    if (operationType.value === "edit") {
-      let ids = [];
-      ids.push(file.id);
-      delLedgerFile(ids).then(res => {
-        proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-      });
-    }
-  }
 
   defineExpose({
     openDialog,
diff --git a/src/views/safeProduction/safeWorkApproval/index.vue b/src/views/safeProduction/safeWorkApproval/index.vue
index 2d8362e..bfd1d90 100644
--- a/src/views/safeProduction/safeWorkApproval/index.vue
+++ b/src/views/safeProduction/safeWorkApproval/index.vue
@@ -1,7 +1,7 @@
 <template>
   <div class="app-container">
     <!-- 鏍囩椤靛垏鎹笉鍚岀殑瀹℃壒绫诲瀷 -->
-    <div class="search_form">
+    <div class="search_form mb20">
       <div>
         <span class="search_title">娴佺▼缂栧彿锛�</span>
         <el-input v-model="searchForm.approveId"
diff --git a/src/views/safeProduction/safetyTrainingAssessment/index.vue b/src/views/safeProduction/safetyTrainingAssessment/index.vue
index cbc35b8..2c98308 100644
--- a/src/views/safeProduction/safetyTrainingAssessment/index.vue
+++ b/src/views/safeProduction/safetyTrainingAssessment/index.vue
@@ -239,7 +239,7 @@
           <el-descriptions-item label="闄勪欢鍒楄〃:">
             <el-button type="primary"
                        size="small"
-                       @click="downLoadFile(endform)">闄勪欢鍒楄〃</el-button>
+                       @click="openFileDialog(endform)">闄勪欢鍒楄〃</el-button>
           </el-descriptions-item>
         </el-descriptions>
         <!-- <el-divider style="margin: 20px 0;" /> -->
@@ -358,23 +358,13 @@
         </span>
       </template>
     </el-dialog>
-    <!-- 闄勪欢鍒楄〃寮圭獥 -->
-    <FileListDialog ref="fileListRef"
-                    v-model="fileListDialogVisible"
-                    :show-upload-button="true"
-                    :show-delete-button="true"
-                    :is-show-pagination="true"
-                    :page="filePagination"
-                    :upload-method="handleUpload"
-                    :delete-method="handleFileDelete"
-                    @pagination="paginationSearch"
-                    title="闄勪欢鍒楄〃" />
+    <!--  todo 闄勪欢棰勮鐩稿叧 -->
+    <FileList v-if="fileDialogVisible"  v-model:visible="fileDialogVisible" record-type="safe_training" :record-id="recordId"  />
   </div>
 </template>
 
 <script setup>
   import { Search } from "@element-plus/icons-vue";
-  import FileListDialog from "@/components/Dialog/FileListDialog.vue";
   import {
     onMounted,
     ref,
@@ -403,6 +393,7 @@
   import useUserStore from "@/store/modules/user";
   import dayjs from "dayjs";
   const userStore = useUserStore();
+  const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
 
   // 琛ㄥ崟楠岃瘉瑙勫垯
   const rules = {
@@ -621,7 +612,7 @@
           name: "闄勪欢",
           type: "text",
           clickFun: row => {
-            downLoadFile(row);
+            openFileDialog(row);
           },
           color: "#007AFF",
         },
@@ -783,27 +774,17 @@
       form.value.principalMobile = selectedUser.phonenumber;
     }
   };
-  /**
-   * 涓嬭浇鏂囦欢
-   *
-   * @param row 涓嬭浇鏂囦欢鐨勭浉鍏充俊鎭璞�
-   */
-  const fileListRef = ref(null);
-  const fileListDialogVisible = ref(false);
-  const currentFileRow = ref(null);
-  const downLoadFile = row => {
-    currentFileRow.value = row;
-    safeTrainingFileListPage({
-      safeTrainingId: row.id,
-      current: filePagination.value.current,
-      size: filePagination.value.size,
-    }).then(res => {
-      if (fileListRef.value) {
-        fileListRef.value.open(res.data.records);
-        filePagination.value.total = res.data?.total || 0;
-      }
-    });
-  };
+
+  // 鎵撳紑闄勪欢寮圭獥
+  const recordId =ref(0)
+  const fileDialogVisible = ref(false)
+
+  // 鎵撳紑闄勪欢寮规
+  const openFileDialog = async (row) => {
+    recordId.value = row.id
+    fileDialogVisible.value = true
+  }
+  
   // 涓婁紶闄勪欢
   const handleUpload = async () => {
     if (!currentFileRow.value) {
diff --git a/src/views/salesManagement/deliveryLedger/index.vue b/src/views/salesManagement/deliveryLedger/index.vue
index 2b1b0b0..d7f47dd 100644
--- a/src/views/salesManagement/deliveryLedger/index.vue
+++ b/src/views/salesManagement/deliveryLedger/index.vue
@@ -3,19 +3,22 @@
     <div class="search_form">
       <el-form :model="searchForm" :inline="true">
         <el-form-item label="閿�鍞鍗曞彿锛�">
-          <el-input v-model="searchForm.salesContractNo" placeholder="璇疯緭鍏�" clearable prefix-icon="Search" style="width: 200px"
-            @change="handleQuery" />
+          <el-input v-model="searchForm.salesContractNo" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
+                    style="width: 200px"
+                    @change="handleQuery"/>
         </el-form-item>
         <el-form-item label="杞︾墝鍙凤細">
-          <el-input v-model="searchForm.shippingCarNumber" placeholder="璇疯緭鍏�" clearable prefix-icon="Search" style="width: 200px"
-            @change="handleQuery" />
+          <el-input v-model="searchForm.shippingCarNumber" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
+                    style="width: 200px"
+                    @change="handleQuery"/>
         </el-form-item>
         <el-form-item label="蹇�掑崟鍙凤細">
-          <el-input v-model="searchForm.expressNumber" placeholder="璇疯緭鍏�" clearable prefix-icon="Search" style="width: 200px"
-            @change="handleQuery" />
+          <el-input v-model="searchForm.expressNumber" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
+                    style="width: 200px"
+                    @change="handleQuery"/>
         </el-form-item>
         <el-form-item>
-          <el-button type="primary" @click="handleQuery"> 鎼滅储 </el-button>
+          <el-button type="primary" @click="handleQuery"> 鎼滅储</el-button>
         </el-form-item>
       </el-form>
     </div>
@@ -28,18 +31,18 @@
         </div>
       </div>
       <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
-        :row-key="(row) => row.id" style="width: 100%" height="calc(100vh - 21.5em)">
-        <el-table-column align="center" type="selection" width="55" />
-        <el-table-column align="center" label="搴忓彿" type="index" width="60" />
-        <el-table-column label="閿�鍞鍗�" prop="salesContractNo" show-overflow-tooltip />
-        <el-table-column label="鍙戣揣璁㈠崟鍙�" prop="shippingNo" show-overflow-tooltip />
-        <el-table-column label="瀹㈡埛鍚嶇О" prop="customerName" show-overflow-tooltip />
-        <el-table-column label="浜у搧鍚嶇О" prop="productName" show-overflow-tooltip />
-        <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" show-overflow-tooltip />
-        <el-table-column label="鍙戣揣鏃堕棿" prop="shippingDate" show-overflow-tooltip />
-        <el-table-column label="鍙戣揣杞︾墝鍙�" prop="shippingCarNumber" show-overflow-tooltip />
-        <el-table-column label="蹇�掑叕鍙�" prop="expressCompany" show-overflow-tooltip />
-        <el-table-column label="蹇�掑崟鍙�" prop="expressNumber" show-overflow-tooltip />
+                :row-key="(row) => row.id" style="width: 100%" height="calc(100vh - 21.5em)">
+        <el-table-column align="center" type="selection" width="55"/>
+        <el-table-column align="center" label="搴忓彿" type="index" width="60"/>
+        <el-table-column label="閿�鍞鍗�" prop="salesContractNo" show-overflow-tooltip/>
+        <el-table-column label="鍙戣揣璁㈠崟鍙�" prop="shippingNo" show-overflow-tooltip/>
+        <el-table-column label="瀹㈡埛鍚嶇О" prop="customerName" show-overflow-tooltip/>
+        <el-table-column label="浜у搧鍚嶇О" prop="productName" show-overflow-tooltip/>
+        <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" show-overflow-tooltip/>
+        <el-table-column label="鍙戣揣鏃堕棿" prop="shippingDate" show-overflow-tooltip/>
+        <el-table-column label="鍙戣揣杞︾墝鍙�" prop="shippingCarNumber" show-overflow-tooltip/>
+        <el-table-column label="蹇�掑叕鍙�" prop="expressCompany" show-overflow-tooltip/>
+        <el-table-column label="蹇�掑崟鍙�" prop="expressNumber" show-overflow-tooltip/>
         <el-table-column label="瀹℃牳鐘舵��" prop="status" align="center" width="120">
           <template #default="scope">
             <el-tag :type="getApprovalStatusType(scope.row.status)">
@@ -47,46 +50,48 @@
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column fixed="right" label="鎿嶄綔" width="200" align="center">
+        <el-table-column fixed="right" label="鎿嶄綔" width="220" align="center">
           <template #default="scope">
-            <el-button 
-              link 
-              type="primary" 
-              size="small" 
-              :disabled="!isApproved(scope.row.status)"
-              @click="openForm('edit', scope.row)">琛ュ厖鍙戣揣淇℃伅</el-button>
             <el-button
-              link
-              type="primary"
-              size="small"
-              @click="openDetail(scope.row)"
-            >璇︽儏</el-button>
-            <el-button 
-              link 
-              type="danger" 
-              size="small" 
-              :disabled="isApproving(scope.row.status)"
-              @click="handleDeleteSingle(scope.row)">鍒犻櫎</el-button>
+                link
+                type="primary"
+                :disabled="!isApproved(scope.row.status)"
+                @click="openForm('edit', scope.row)">琛ュ厖鍙戣揣淇℃伅
+            </el-button>
+            <el-button
+                link
+                type="primary"
+                style="color: #67C23A"
+                @click="openDetail(scope.row)"
+            >璇︽儏
+            </el-button>
+            <el-button
+                link
+                type="danger"
+                :disabled="isApproving(scope.row.status)"
+                @click="handleDeleteSingle(scope.row)">鍒犻櫎
+            </el-button>
           </template>
         </el-table-column>
       </el-table>
       <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
-        :page="page.current" :limit="page.size" @pagination="paginationChange" />
+                  :page="page.current" :limit="page.size" @pagination="paginationChange"/>
     </div>
-    <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '鏂板鍙戣揣鍙拌处' : '缂栬緫鍙戣揣鍙拌处'" width="40%"
-      @close="closeDia">
+    <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '鏂板鍙戣揣鍙拌处' : '缂栬緫鍙戣揣鍙拌处'"
+               width="40%"
+               @close="closeDia">
       <el-form :model="form" label-width="120px" label-position="top" :rules="rules" ref="formRef">
         <el-row :gutter="30">
           <el-col :span="24">
             <el-form-item label="鍙戣揣绫诲瀷锛�" prop="type">
               <el-select
-                v-model="form.type"
-                placeholder="璇烽�夋嫨鍙戣揣绫诲瀷"
-                style="width: 100%"
-                @change="handleShippingTypeChange"
+                  v-model="form.type"
+                  placeholder="璇烽�夋嫨鍙戣揣绫诲瀷"
+                  style="width: 100%"
+                  @change="handleShippingTypeChange"
               >
-                <el-option label="璐ц溅" value="璐ц溅" />
-                <el-option label="蹇��" value="蹇��" />
+                <el-option label="璐ц溅" value="璐ц溅"/>
+                <el-option label="蹇��" value="蹇��"/>
               </el-select>
             </el-form-item>
           </el-col>
@@ -95,13 +100,13 @@
           <el-col :span="24">
             <el-form-item label="鍙戣揣鏃ユ湡锛�" prop="shippingDate">
               <el-date-picker
-                style="width: 100%"
-                v-model="form.shippingDate"
-                value-format="YYYY-MM-DD"
-                format="YYYY-MM-DD"
-                type="date"
-                placeholder="璇烽�夋嫨鍙戣揣鏃ユ湡"
-                clearable
+                  style="width: 100%"
+                  v-model="form.shippingDate"
+                  value-format="YYYY-MM-DD"
+                  format="YYYY-MM-DD"
+                  type="date"
+                  placeholder="璇烽�夋嫨鍙戣揣鏃ユ湡"
+                  clearable
               />
             </el-form-item>
           </el-col>
@@ -110,18 +115,18 @@
           <el-col :span="24" v-if="form.type === '璐ц溅'">
             <el-form-item label="鍙戣揣杞︾墝鍙凤細" prop="shippingCarNumber">
               <el-input
-                v-model="form.shippingCarNumber"
-                placeholder="璇疯緭鍏ュ彂璐ц溅鐗屽彿"
-                clearable
+                  v-model="form.shippingCarNumber"
+                  placeholder="璇疯緭鍏ュ彂璐ц溅鐗屽彿"
+                  clearable
               />
             </el-form-item>
           </el-col>
           <el-col :span="24" v-else>
             <el-form-item label="蹇�掑叕鍙革細" prop="expressCompany">
               <el-input
-                v-model="form.expressCompany"
-                placeholder="璇疯緭鍏ュ揩閫掑叕鍙�"
-                clearable
+                  v-model="form.expressCompany"
+                  placeholder="璇疯緭鍏ュ揩閫掑叕鍙�"
+                  clearable
               />
             </el-form-item>
           </el-col>
@@ -130,9 +135,9 @@
           <el-col :span="24">
             <el-form-item label="蹇�掑崟鍙凤細" prop="expressNumber">
               <el-input
-                v-model="form.expressNumber"
-                placeholder="璇疯緭鍏ュ揩閫掑崟鍙�"
-                clearable
+                  v-model="form.expressNumber"
+                  placeholder="璇疯緭鍏ュ揩閫掑崟鍙�"
+                  clearable
               />
             </el-form-item>
           </el-col>
@@ -140,29 +145,7 @@
         <el-row :gutter="30">
           <el-col :span="24">
             <el-form-item label="鍙戣揣鍥剧墖锛�">
-              <el-upload 
-                v-model:file-list="deliveryFileList" 
-                :action="upload.url" 
-                multiple 
-                ref="deliveryFileUpload" 
-                auto-upload
-                :headers="upload.headers" 
-                :data="{ type: 9 }"
-                :before-upload="handleDeliveryBeforeUpload" 
-                :on-error="handleDeliveryUploadError"
-                :on-success="handleDeliveryUploadSuccess" 
-                :on-remove="handleDeliveryRemove"
-                list-type="picture-card"
-                :limit="9"
-                accept="image/png,image/jpeg,image/jpg"
-              >
-                <el-icon class="avatar-uploader-icon"><Plus /></el-icon>
-                <template #tip>
-                  <div class="el-upload__tip">
-                    鏀寔 jpg銆乯peg銆乸ng 鏍煎紡锛屾渶澶氫笂浼� 9 寮狅紝鍗曞紶澶у皬涓嶈秴杩� 10MB
-                  </div>
-                </template>
-              </el-upload>
+              <ImageUpload v-model:file-list="deliveryFileList" :limit="9"/>
             </el-form-item>
           </el-col>
         </el-row>
@@ -191,19 +174,29 @@
           <el-descriptions-item label="蹇�掑叕鍙�">{{ detailRow.expressCompany || '--' }}</el-descriptions-item>
           <el-descriptions-item label="蹇�掑崟鍙�" :span="2">{{ detailRow.expressNumber || '--' }}</el-descriptions-item>
         </el-descriptions>
-
-        <div class="detail-images" v-if="detailImages.length">
-          <div class="detail-images-title">鍙戣揣鍥剧墖</div>
-          <el-image
-            v-for="img in detailImages"
-            :key="img.url"
-            :src="img.url"
-            :preview-src-list="detailImages.map(i => i.url)"
-            fit="cover"
-            class="detail-image"
-          />
-        </div>
-        <div v-else class="detail-images-empty">鏆傛棤鍙戣揣鍥剧墖</div>
+        <el-table :data="getDeliveryProductInfoList()"
+                  border
+                  size="small"
+                  class="delivery-product-table"
+                  style="width: 100%; margin-top: 16px;">
+          <el-table-column label="鎵瑰彿"
+                           prop="batchNo"
+                           min-width="160"
+                           show-overflow-tooltip/>
+          <el-table-column label="浜у搧鍚嶇О"
+                           prop="productName"
+                           min-width="160"
+                           show-overflow-tooltip/>
+          <el-table-column label="瑙勬牸鍨嬪彿"
+                           prop="specificationModel"
+                           min-width="160"
+                           show-overflow-tooltip/>
+          <el-table-column label="鍙戣揣鏁伴噺"
+                           prop="deliveryQuantity"
+                           min-width="120"
+                           align="center"/>
+        </el-table>
+        <ImagePreview :file-list="detailRow.storageBlobVOs || []" />
       </div>
       <template #footer>
         <div class="dialog-footer">
@@ -216,20 +209,21 @@
 
 <script setup>
 import pagination from "@/components/PIMTable/Pagination.vue";
-import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
-import { ElMessageBox } from "element-plus";
-import { Plus } from "@element-plus/icons-vue";
-import { getToken } from "@/utils/auth";
-import { getCurrentDate } from "@/utils/index.js";
+import {onMounted, ref, reactive, toRefs, getCurrentInstance} from "vue";
+import {ElMessageBox} from "element-plus";
+import {getCurrentDate} from "@/utils/index.js";
 import {
-	deliveryLedgerListPage,
-	addOrUpdateDeliveryLedger,
-	delDeliveryLedger, deductStock,
+  deliveryLedgerListPage,
+  delDeliveryLedger,
+  deductStock,
+  getDeliveryDetail,
 } from "@/api/salesManagement/deliveryLedger.js";
-import { delLedgerFile } from "@/api/salesManagement/salesLedger.js";
- 
+import {delLedgerFile} from "@/api/salesManagement/salesLedger.js";
+import ImageUpload from "@/components/AttachmentUpload/image/index.vue";
+import ImagePreview from "@/components/AttachmentPreview/image/index.vue";
 
-const { proxy } = getCurrentInstance();
+
+const {proxy} = getCurrentInstance();
 const tableData = ref([]);
 const selectedRows = ref([]);
 const tableLoading = ref(false);
@@ -240,40 +234,10 @@
 });
 const total = ref(0);
 const deliveryFileList = ref([]);
-const javaApi = proxy.javaApi;
 // 璇︽儏寮规
 const detailDialogVisible = ref(false);
 const detailRow = ref(null);
-const detailImages = ref([]);
-
-const normalizeFileUrl = (rawUrl = '') => {
-  let fileUrl = rawUrl || '';
-  // Windows 璺緞杞� URL
-  if (fileUrl && fileUrl.indexOf('\\') > -1) {
-    const uploadsIndex = fileUrl.toLowerCase().indexOf('uploads');
-    if (uploadsIndex > -1) {
-      const relativePath = fileUrl.substring(uploadsIndex).replace(/\\/g, '/');
-      fileUrl = '/' + relativePath;
-    } else {
-      const parts = fileUrl.split('\\');
-      const fileName = parts[parts.length - 1];
-      fileUrl = '/uploads/' + fileName;
-    }
-  }
-  if (fileUrl && !fileUrl.startsWith('http')) {
-    if (!fileUrl.startsWith('/')) fileUrl = '/' + fileUrl;
-    fileUrl = javaApi + fileUrl;
-  }
-  return fileUrl;
-};
-
-// 涓婁紶閰嶇疆
-const upload = reactive({
-  // 涓婁紶鐨勫湴鍧�
-  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
-  // 璁剧疆涓婁紶鐨勮姹傚ご閮�
-  headers: { Authorization: "Bearer " + getToken() },
-});
+const detailProductList = ref([]);
 
 // 鐢ㄦ埛淇℃伅琛ㄥ崟寮规鏁版嵁
 const operationType = ref("");
@@ -297,24 +261,23 @@
     expressNumber: "", // 蹇�掑崟鍙�
   },
   rules: {
-    salesContractNo: [{ required: true, message: "璇烽�夋嫨閿�鍞鍗�", trigger: "change" }],
-    customerName: [{ required: true, message: "璇疯緭鍏ュ鎴峰悕绉�", trigger: "blur" }],
+    salesContractNo: [{required: true, message: "璇烽�夋嫨閿�鍞鍗�", trigger: "change"}],
+    customerName: [{required: true, message: "璇疯緭鍏ュ鎴峰悕绉�", trigger: "blur"}],
     type: [
-      { required: true, message: "璇烽�夋嫨鍙戣揣绫诲瀷", trigger: "change" }
+      {required: true, message: "璇烽�夋嫨鍙戣揣绫诲瀷", trigger: "change"}
     ],
-    shippingDate: [{ required: true, message: "璇烽�夋嫨鍙戣揣鏃堕棿", trigger: "change" }],
+    shippingDate: [{required: true, message: "璇烽�夋嫨鍙戣揣鏃堕棿", trigger: "change"}],
     shippingCarNumber: [
-      { validator: (_, value, callback) => validateShippingCarNumber(value, callback), trigger: "blur" }
+      {validator: (_, value, callback) => validateShippingCarNumber(value, callback), trigger: "blur"}
     ],
     expressCompany: [
-      { validator: (_, value, callback) => validateExpressCompany(value, callback), trigger: "blur" }
+      {validator: (_, value, callback) => validateExpressCompany(value, callback), trigger: "blur"}
     ],
   },
 });
-const { form, rules } = toRefs(data);
-const { searchForm } = toRefs(data);
+const {form, rules} = toRefs(data);
+const {searchForm} = toRefs(data);
 
- 
 
 // 鏌ヨ鍒楄〃
 const handleQuery = () => {
@@ -330,15 +293,15 @@
 
 const getList = () => {
   tableLoading.value = true;
-  deliveryLedgerListPage({ ...searchForm.value, ...page })
-    .then((res) => {
-      tableLoading.value = false;
-      tableData.value = res.data.records || [];
-      total.value = res.data.total || 0;
-    })
-    .catch(() => {
-      tableLoading.value = false;
-    });
+  deliveryLedgerListPage({...searchForm.value, ...page})
+      .then((res) => {
+        tableLoading.value = false;
+        tableData.value = res.data.records || [];
+        total.value = res.data.total || 0;
+      })
+      .catch(() => {
+        tableLoading.value = false;
+      });
 };
 
 // 閿�鍞鍗曞彉鍖栨椂鑷姩濉厖瀹㈡埛鍚嶇О
@@ -361,10 +324,9 @@
     proxy.$modal.msgWarning("鍙湁瀹℃牳閫氳繃鐨勬暟鎹墠鍙互琛ュ厖鍙戣揣淇℃伅");
     return;
   }
-  
+
   operationType.value = type;
-  const baseUrl = import.meta.env.VITE_APP_BASE_API;
-  
+
   if (type === 'edit' && row) {
     form.value = {
       id: row.id ?? null,
@@ -376,69 +338,99 @@
       expressCompany: row.expressCompany ?? "",
       expressNumber: row.expressNumber ?? "",
     };
-    // 濡傛灉鏈夊浘鐗囷紝灏� commonFileList 杞崲涓烘枃浠跺垪琛ㄦ牸寮�
-    if (row.commonFileList && Array.isArray(row.commonFileList) && row.commonFileList.length > 0) {
-      deliveryFileList.value = row.commonFileList.map((file, index) => {
-        const fileUrl = normalizeFileUrl(file.url || '');
-        
-        return {
-          uid: file.id || Date.now() + index,
-          name: file.name || `image_${index + 1}.jpg`,
-          url: fileUrl,
-          status: 'success',
-          response: {
-            code: 200,
-            data: {
-              tempId: file.id,
-              url: fileUrl
-            }
-          },
-          tempId: file.id // 淇濆瓨鏂囦欢ID锛岀敤浜庢彁浜ゆ椂浣跨敤
-        };
-      });
-    } else {
-      deliveryFileList.value = [];
-    }
-  } else {
-    form.value = {
-      id: null,
-      salesContractNo: "",
-      customerName: "",
-      type: "璐ц溅",
-      shippingDate: getCurrentDate(),
-      shippingCarNumber: "",
-      expressCompany: "",
-      expressNumber: "",
-    };
-    deliveryFileList.value = [];
+    deliveryFileList.value = row.storageBlobVOs || [];
   }
-  
+
   dialogFormVisible.value = true;
 };
 
 // 鎵撳紑璇︽儏寮规
-const openDetail = (row) => {
+const openDetail = async (row) => {
   detailRow.value = row || null;
-  const list = Array.isArray(row?.commonFileList) ? row.commonFileList : [];
-  detailImages.value = list
-    .map((f) => ({ url: normalizeFileUrl(f?.url || '') }))
-    .filter((i) => !!i.url);
+  detailProductList.value = [];
   detailDialogVisible.value = true;
+  if (!row?.id) return;
+  try {
+    const res = await getDeliveryDetail(row.id);
+    const detailData = res?.data;
+    detailRow.value = {
+      ...row,
+      ...(Array.isArray(detailData) ? {} : detailData || {}),
+    };
+    detailProductList.value = resolveDeliveryDetailList(detailData);
+  } catch (error) {
+    proxy.$modal.msgError("鍔犺浇鍙戣揣鍙拌处璇︽儏澶辫触");
+  }
+};
+const resolveDeliveryDetailList = data => {
+  if (Array.isArray(data)) return data;
+  if (!data || typeof data !== "object") return [];
+  return [
+    data.batchNoDetailList,
+    data.batchNoList,
+    data.shippingBatchList,
+    data.shippingInfoDetailList,
+    data.detailList,
+    data.batchDetailList,
+    data.rows,
+    data.records,
+    data.list,
+    data.data,
+  ].find(value => Array.isArray(value) && value.length) || [];
+};
+const getDeliveryProductInfoList = () => {
+  const row = detailRow.value;
+  if (!row) return [];
+  const normalizeBatchNoList = value => {
+    if (Array.isArray(value)) return value;
+    if (typeof value === "string" && value.includes(",")) {
+      return value.split(",").map(item => item.trim()).filter(Boolean);
+    }
+    return value ? [value] : [];
+  };
+  const detailList = detailProductList.value.length ? detailProductList.value : [
+    row.batchNoDetailList,
+    row.batchNoList,
+    row.shippingBatchList,
+    row.shippingInfoDetailList,
+    row.detailList,
+    row.batchDetailList,
+  ].find(value => Array.isArray(value) && value.length);
+  const batchNoList = normalizeBatchNoList(row.batchNo);
+  const toTableRow = (item = {}) => ({
+    batchNo:
+        typeof item === "string" || typeof item === "number"
+            ? item
+            : item.batchNo ?? item.batchNumber ?? row.batchNo ?? "--",
+    productName: item.productName ?? row.productName ?? "--",
+    specificationModel:
+        item.specificationModel ?? item.model ?? row.specificationModel ?? "--",
+    deliveryQuantity:
+        item.deliveryQuantity ??
+        item.quantity ??
+        item.shippingQuantity ??
+        row.deliveryQuantity ??
+        row.quantity ??
+        "--",
+  });
+  if (detailList?.length) {
+    return detailList.map(toTableRow);
+  }
+  if (batchNoList.length) {
+    return batchNoList.map(batchNo => toTableRow({batchNo}));
+  }
+  return [toTableRow()];
 };
 const closeDetail = () => {
   detailDialogVisible.value = false;
   detailRow.value = null;
-  detailImages.value = [];
+  detailProductList.value = [];
 };
 
 // 鎻愪氦琛ㄥ崟
 const submitForm = () => {
   proxy.$refs["formRef"].validate((valid) => {
     if (valid) {
-      let tempFileIds = [];
-      if (deliveryFileList.value !== null && deliveryFileList.value.length > 0) {
-        tempFileIds = deliveryFileList.value.map((item) => item.tempId);
-      }
       const payload = {
         id: form.value.id,
         type: form.value.type,
@@ -446,9 +438,9 @@
         shippingCarNumber: form.value.type === "璐ц溅" ? form.value.shippingCarNumber : "",
         expressCompany: form.value.type === "蹇��" ? form.value.expressCompany : "",
         expressNumber: form.value.type === "蹇��" ? form.value.expressNumber : "",
-        tempFileIds: tempFileIds,
+        storageBlobDTOs: deliveryFileList.value || [],
       };
-			deductStock(payload).then((res) => {
+      deductStock(payload).then((res) => {
         proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛");
         closeDia();
         getList();
@@ -471,12 +463,12 @@
     cancelButtonText: "鍙栨秷",
     type: "warning",
   })
-    .then(() => {
-      proxy.download("/shippingInfo/export", {}, "鍙戣揣鍙拌处.xlsx");
-    })
-    .catch(() => {
-      proxy.$modal.msg("宸插彇娑�");
-    });
+      .then(() => {
+        proxy.download("/shippingInfo/export", {}, "鍙戣揣鍙拌处.xlsx");
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
 };
 
 // 鎵归噺鍒犻櫎
@@ -485,29 +477,29 @@
     proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
     return;
   }
-  
+
   // 妫�鏌ラ�変腑鐨勮鏄惁鏈�"瀹℃牳涓�"鐘舵��
   const approvingRows = selectedRows.value.filter(row => isApproving(row.status));
   if (approvingRows.length > 0) {
     proxy.$modal.msgWarning("瀹℃牳涓殑鏁版嵁涓嶈兘鍒犻櫎");
     return;
   }
-  
+
   const ids = selectedRows.value.map((item) => item.id);
   ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎", {
     confirmButtonText: "纭",
     cancelButtonText: "鍙栨秷",
     type: "warning",
   })
-    .then(() => {
-      delDeliveryLedger(ids).then((res) => {
-        proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-        getList();
+      .then(() => {
+        delDeliveryLedger(ids).then((res) => {
+          proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+          getList();
+        });
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
       });
-    })
-    .catch(() => {
-      proxy.$modal.msg("宸插彇娑�");
-    });
 };
 
 // 鍗曚釜鍒犻櫎
@@ -517,21 +509,21 @@
     proxy.$modal.msgWarning("瀹℃牳涓殑鏁版嵁涓嶈兘鍒犻櫎");
     return;
   }
-  
+
   ElMessageBox.confirm("姝ゆ搷浣滃皢鍒犻櫎璇ヨ褰曪紝鏄惁纭锛�", "鍒犻櫎", {
     confirmButtonText: "纭",
     cancelButtonText: "鍙栨秷",
     type: "warning",
   })
-    .then(() => {
-      delDeliveryLedger([row.id]).then((res) => {
-        proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-        getList();
+      .then(() => {
+        delDeliveryLedger([row.id]).then((res) => {
+          proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+          getList();
+        });
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
       });
-    })
-    .catch(() => {
-      proxy.$modal.msg("宸插彇娑�");
-    });
 };
 
 // 鍙戣揣绫诲瀷鏍¢獙锛氳揣杞︽椂瑕佹眰杞︾墝锛屽揩閫掓椂瑕佹眰蹇�掑叕鍙�
@@ -565,11 +557,13 @@
   proxy.$modal.loading("姝e湪涓婁紶鍥剧墖锛岃绋嶅��...");
   return true;
 }
+
 // 鍙戣揣鍥剧墖涓婁紶澶辫触
 function handleDeliveryUploadError(err) {
   proxy.$modal.msgError("涓婁紶鍥剧墖澶辫触");
   proxy.$modal.closeLoading();
 }
+
 // 鍙戣揣鍥剧墖涓婁紶鎴愬姛鍥炶皟
 function handleDeliveryUploadSuccess(res, file, uploadFiles) {
   proxy.$modal.closeLoading();
@@ -581,6 +575,7 @@
     proxy.$refs.deliveryFileUpload.handleRemove(file);
   }
 }
+
 // 绉婚櫎鍙戣揣鍥剧墖
 function handleDeliveryRemove(file) {
   console.log('file--', file)
@@ -729,17 +724,21 @@
     display: none;
   }
 }
+
 .detail-wrapper {
   padding: 8px 0;
 }
+
 .detail-images {
   margin-top: 16px;
 }
+
 .detail-images-title {
   font-weight: 600;
   margin-bottom: 10px;
   color: #303133;
 }
+
 .detail-image {
   width: 120px;
   height: 120px;
@@ -747,6 +746,7 @@
   margin-bottom: 10px;
   border-radius: 6px;
 }
+
 .detail-images-empty {
   margin-top: 16px;
   color: #909399;
diff --git a/src/views/salesManagement/indicatorStats/index.vue b/src/views/salesManagement/indicatorStats/index.vue
index 6101920..8ae15ed 100644
--- a/src/views/salesManagement/indicatorStats/index.vue
+++ b/src/views/salesManagement/indicatorStats/index.vue
@@ -406,7 +406,6 @@
 <style scoped lang="scss">
 .indicator-stats {
   padding: 20px;
-  background: #f5f7fa;
   min-height: calc(100vh - 84px);
 }
 
diff --git a/src/views/salesManagement/invoiceLedger/index.vue b/src/views/salesManagement/invoiceLedger/index.vue
index b35023f..444560d 100644
--- a/src/views/salesManagement/invoiceLedger/index.vue
+++ b/src/views/salesManagement/invoiceLedger/index.vue
@@ -43,9 +43,9 @@
         <el-table-column label="寮�绁ㄦ棩鏈�" prop="invoiceDate" show-overflow-tooltip width="120" />
         <el-table-column fixed="right" label="鎿嶄綔" width="150" align="center">
           <template #default="scope">
-            <el-button link type="primary" size="small" @click="openForm(scope.row)">缂栬緫</el-button>
-            <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">闄勪欢</el-button>
-            <el-button link type="primary" size="small" @click="delInvoiceLedger(scope.row)">鍒犻櫎</el-button>
+            <el-button link type="primary" @click="openForm(scope.row)">缂栬緫</el-button>
+            <el-button link type="primary" @click="openFileDialog(scope.row)">闄勪欢</el-button>
+            <el-button link type="primary" @click="delInvoiceLedger(scope.row)">鍒犻櫎</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -134,7 +134,7 @@
         </div>
       </template>
     </el-dialog>
-    <FileListDialog ref="fileListRef" v-model="fileListDialogVisible" />
+    <FileList v-if="fileDialogVisible"  v-model:visible="fileDialogVisible" record-type="invoice_registration_product" :record-id="recordId"  />
   </div>
 </template>
 
@@ -155,8 +155,8 @@
 import useUserStore from "@/store/modules/user.js";
 import useFormData from "@/hooks/useFormData";
 import dayjs from "dayjs";
-import FileListDialog from '@/components/Dialog/FileListDialog.vue';
 import { getCurrentDate } from "@/utils/index.js";
+const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
 
 const { proxy } = getCurrentInstance();
 const tableData = ref([]);
@@ -422,17 +422,14 @@
   getList();
 };
 
-//闄勪欢鐩稿叧
-const fileListRef = ref(null)
-const fileListDialogVisible = ref(false)
-//鏌ョ湅闄勪欢
-const downLoadFile = (row) => {
-	invoiceLedgerProductInfo({ id: row.id }).then((res) => {
-		if (fileListRef.value) {
-			fileListRef.value.open(res.data.fileList)
-			fileListDialogVisible.value = true
-		}
-	});
+// 鎵撳紑闄勪欢寮圭獥
+const recordId =ref(0)
+const fileDialogVisible = ref(false)
+
+// 鎵撳紑闄勪欢寮规
+const openFileDialog = async (row) => {
+  recordId.value = row.id
+  fileDialogVisible.value = true
 }
 
 onMounted(() => {
diff --git a/src/views/salesManagement/invoiceRegistration/index.vue b/src/views/salesManagement/invoiceRegistration/index.vue
index 2f6e60c..44e1c4e 100644
--- a/src/views/salesManagement/invoiceRegistration/index.vue
+++ b/src/views/salesManagement/invoiceRegistration/index.vue
@@ -803,7 +803,7 @@
 .justify-between {
 	justify-content: space-between;
 }
-::v-deep(.el-checkbox__label) {
+:deep(.el-checkbox__label) {
 	font-weight: bold;
 }
 </style>
diff --git a/src/views/salesManagement/orderManagement/index.vue b/src/views/salesManagement/orderManagement/index.vue
index 6107966..f44c53f 100644
--- a/src/views/salesManagement/orderManagement/index.vue
+++ b/src/views/salesManagement/orderManagement/index.vue
@@ -69,7 +69,7 @@
         </el-table-column>
         <el-table-column label="鎿嶄綔" width="250" fixed="right" align="center">
           <template #default="scope">
-            <el-button link type="primary" @click="handleView(scope.row)">鏌ョ湅</el-button>
+            <el-button link type="primary" @click="handleView(scope.row)" style="color: #67C23A">鏌ョ湅</el-button>
             <el-button link type="primary" @click="handleEdit(scope.row)" v-if="scope.row.status === '寰呭鏍�'">缂栬緫</el-button>
             <el-button link type="primary" @click="handleReview(scope.row)" v-if="scope.row.status === '寰呭鏍�'">瀹℃牳</el-button>
             <el-button link type="primary" @click="handleTransfer(scope.row)" v-if="scope.row.status === '宸插鏍�'">杞崟</el-button>
diff --git a/src/views/salesManagement/receiptPayment/index.vue b/src/views/salesManagement/receiptPayment/index.vue
index 83cf942..25bd280 100644
--- a/src/views/salesManagement/receiptPayment/index.vue
+++ b/src/views/salesManagement/receiptPayment/index.vue
@@ -102,7 +102,6 @@
                   <el-button
                     link
                     type="primary"
-                    size="small"
                     @click="changeEditType(scope.row)"
                     v-if="!scope.row.editType"
                     >缂栬緫</el-button
@@ -110,7 +109,6 @@
                   <el-button
                     link
                     type="primary"
-                    size="small"
                     @click="saveReceiptPayment(scope.row)"
                     v-if="scope.row.editType"
                     >淇濆瓨</el-button
@@ -118,7 +116,6 @@
                   <el-button
                     link
                     type="primary"
-                    size="small"
                     @click="delReceiptRecord(scope.row)"
                     >鍒犻櫎</el-button
                   >
@@ -592,7 +589,7 @@
 .table_list {
   margin-top: unset;
 }
-::v-deep(.el-checkbox__label) {
+:deep(.el-checkbox__label) {
   font-weight: bold;
 }
 .actions {
diff --git a/src/views/salesManagement/receiptPaymentHistory/index.vue b/src/views/salesManagement/receiptPaymentHistory/index.vue
index f66bed7..11a59a6 100644
--- a/src/views/salesManagement/receiptPaymentHistory/index.vue
+++ b/src/views/salesManagement/receiptPaymentHistory/index.vue
@@ -54,7 +54,6 @@
           <el-button
             type="primary"
             link
-            size="small"
             @click="handleDelete(row)"
           >
             鍒犻櫎
diff --git a/src/views/salesManagement/receiptPaymentLedger/index.vue b/src/views/salesManagement/receiptPaymentLedger/index.vue
index 7029cfc..e41e24f 100644
--- a/src/views/salesManagement/receiptPaymentLedger/index.vue
+++ b/src/views/salesManagement/receiptPaymentLedger/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
+    <div class="search_form" style="margin-bottom: 20px;">
       <div>
         <span class="search_title">瀹㈡埛鍚嶇О锛�</span>
         <el-input
diff --git a/src/views/salesManagement/returnOrder/components/formDia.vue b/src/views/salesManagement/returnOrder/components/formDia.vue
index 9f1bb9a..7a35ea9 100644
--- a/src/views/salesManagement/returnOrder/components/formDia.vue
+++ b/src/views/salesManagement/returnOrder/components/formDia.vue
@@ -168,10 +168,10 @@
 <script setup>
 import { reactive, ref, toRefs, getCurrentInstance } from "vue";
 import { returnManagementAdd, returnManagementUpdate, returnManagementGetByShippingId, getSalesLedger, returnManagementGetById } from "@/api/salesManagement/returnOrder.js";
-import { getAllCustomerList } from "@/api/customerService/index.js";
 import useUserStore from "@/store/modules/user.js";
 import { userListNoPageByTenantId } from "@/api/system/user.js";
 import { listProject } from "@/api/oaSystem/projectManagement.js";
+import {listCustomer} from "@/api/basicData/customer.js";
 
 const { proxy } = getCurrentInstance();
 const emit = defineEmits(['close'])
@@ -354,15 +354,14 @@
 };
 
 const initCustomers = async () => {
-  const res = await getAllCustomerList({});
-  if (res?.records) {
-    customerNameOptions.value = res.records.map(item => ({
-      label: item.customerName,
-      value: item.customerName, // Keep value as name if needed for other logic, but request says customerId
-      id: item.id,
-      code: item.customerCode
-    }));
-  }
+  listCustomer({current: -1,size:-1, type: 0}).then((res) => {
+		customerNameOptions.value = res.data.records.map(item => ({
+			label: item.customerName,
+			value: item.customerName, // Keep value as name if needed for other logic, but request says customerId
+			id: item.id,
+			code: item.customerCode
+		}));
+	});
 };
 
 const initUsers = async () => {
@@ -406,7 +405,7 @@
   }).then(res => {
     if(res.code === 200){
       outboundOptions.value = res.data.map(item => ({
-        label: item.salesContractNo, // Or whatever the outbound number field is
+        label: item.shippingNo, // Or whatever the outbound number field is
         value: item.id,
       }))
     }
diff --git a/src/views/salesManagement/returnOrder/index.vue b/src/views/salesManagement/returnOrder/index.vue
index 5a5aa05..d41d4bf 100644
--- a/src/views/salesManagement/returnOrder/index.vue
+++ b/src/views/salesManagement/returnOrder/index.vue
@@ -1,41 +1,29 @@
 <template>
   <div class="app-container">
-    <div class="search-wrapper">
-      <el-form :model="searchForm" class="demo-form-inline">
-        <el-row :gutter="20">
-          <el-col :span="4">
-            <el-form-item label="閫�璐у崟鍙�">
-              <el-input v-model="searchForm.returnNo" placeholder="璇疯緭鍏ラ��璐у崟鍙�" clearable />
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item label="瀹㈡埛鍚嶇О">
-              <el-input v-model="searchForm.customerName" placeholder="瀹㈡埛鍚嶇О" clearable />
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item label="閿�鍞崟鍙�">
-              <el-input v-model="searchForm.salesContractNo" placeholder="閿�鍞崟鍙�" clearable />
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item label="鍏宠仈鍑哄簱鍗曞彿">
-              <el-input v-model="searchForm.shippingNo" placeholder="鍏宠仈鍑哄簱鍗曞彿" clearable />
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item>
-              <el-button type="primary" @click="handleQuery">鎼滅储</el-button>
-              <el-button @click="handleReset">閲嶇疆</el-button>
-            </el-form-item>
-          </el-col>
-        </el-row>
+    <div class="search_form">
+      <el-form :model="searchForm" class="demo-form-inline" :inline="true">
+				<el-form-item label="閫�璐у崟鍙�">
+					<el-input v-model="searchForm.returnNo" placeholder="璇疯緭鍏ラ��璐у崟鍙�" clearable />
+				</el-form-item>
+				<el-form-item label="瀹㈡埛鍚嶇О">
+					<el-input v-model="searchForm.customerName" placeholder="瀹㈡埛鍚嶇О" clearable />
+				</el-form-item>
+				<el-form-item label="閿�鍞崟鍙�">
+					<el-input v-model="searchForm.salesContractNo" placeholder="閿�鍞崟鍙�" clearable />
+				</el-form-item>
+				<el-form-item label="鍏宠仈鍑哄簱鍗曞彿">
+					<el-input v-model="searchForm.shippingNo" placeholder="鍏宠仈鍑哄簱鍗曞彿" clearable />
+				</el-form-item>
+				<el-form-item>
+					<el-button type="primary" @click="handleQuery">鎼滅储</el-button>
+					<el-button @click="handleReset">閲嶇疆</el-button>
+				</el-form-item>
       </el-form>
     </div>
     <div class="table_list">
       <div class="table_header" style="display: flex;justify-content: flex-end;margin-bottom: 10px;">
         <el-button type="primary" @click="openForm('add')">鏂板缓閿�鍞��璐�</el-button>
-        <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+        <el-button type="danger" :disabled="selectedRows.length === 0 || selectedRows.some(row => row.status !== 0)" @click="handleDelete">鍒犻櫎</el-button>
       </div>
       <PIMTable
         rowKey="id"
@@ -225,10 +213,7 @@
 </script>
 
 <style scoped lang="scss">
-.search-wrapper {
-  background: white;
-  padding: 1rem 1rem 0 1rem;
-  border: 8px;
-  border-radius: 16px;
+.table_list {
+	margin-top: unset;
 }
 </style>
diff --git a/src/views/salesManagement/salesLedger/fileList.vue b/src/views/salesManagement/salesLedger/fileList.vue
index da37db2..57c4332 100644
--- a/src/views/salesManagement/salesLedger/fileList.vue
+++ b/src/views/salesManagement/salesLedger/fileList.vue
@@ -4,8 +4,8 @@
       <el-table-column label="闄勪欢鍚嶇О" prop="name" min-width="400" show-overflow-tooltip />
       <el-table-column fixed="right" label="鎿嶄綔" width="100" align="center">
         <template #default="scope">
-          <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">涓嬭浇</el-button>
-          <el-button link type="primary" size="small" @click="lookFile(scope.row)">棰勮</el-button>
+          <el-button link type="primary" @click="downLoadFile(scope.row)">涓嬭浇</el-button>
+          <el-button link type="primary" @click="lookFile(scope.row)">棰勮</el-button>
         </template>
       </el-table-column>
     </el-table>
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index e40cfb5..5340d99 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -1,25 +1,42 @@
 <template>
   <div class="app-container">
     <div class="search_form">
-      <el-form :model="searchForm" :inline="true">
+      <el-form :model="searchForm"
+               :inline="true">
         <el-form-item label="瀹㈡埛鍚嶇О锛�">
-          <el-input v-model="searchForm.customerName" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
-            @change="handleQuery" />
+          <el-input v-model="searchForm.customerName"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    prefix-icon="Search"
+                    @change="handleQuery"/>
         </el-form-item>
         <el-form-item label="閿�鍞悎鍚屽彿锛�">
-          <el-input v-model="searchForm.salesContractNo" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
-            @change="handleQuery" />
+          <el-input v-model="searchForm.salesContractNo"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    prefix-icon="Search"
+                    @change="handleQuery"/>
         </el-form-item>
         <el-form-item label="椤圭洰鍚嶇О锛�">
-          <el-input v-model="searchForm.projectName" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
-            @change="handleQuery" />
+          <el-input v-model="searchForm.projectName"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    prefix-icon="Search"
+                    @change="handleQuery"/>
         </el-form-item>
         <el-form-item label="褰曞叆鏃ユ湡锛�">
-          <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
-            placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
+          <el-date-picker v-model="searchForm.entryDate"
+                          value-format="YYYY-MM-DD"
+                          format="YYYY-MM-DD"
+                          type="daterange"
+                          placeholder="璇烽�夋嫨"
+                          clearable
+                          @change="changeDaterange"/>
         </el-form-item>
         <el-form-item>
-          <el-button type="primary" @click="handleQuery"> 鎼滅储 </el-button>
+          <el-button type="primary"
+                     @click="handleQuery"> 鎼滅储
+          </el-button>
         </el-form-item>
       </el-form>
     </div>
@@ -27,78 +44,141 @@
       <div class="actions">
         <div></div>
         <div>
-          <el-button type="primary" @click="openForm('add')">
+          <el-button type="primary"
+                     @click="openForm('add')">
             鏂板鍙拌处
           </el-button>
-          <el-button type="primary" plain @click="handleImport">瀵煎叆</el-button>
+          <el-button type="primary"
+                     plain
+                     @click="handleImport">瀵煎叆
+          </el-button>
           <el-button @click="handleOut">瀵煎嚭</el-button>
-          <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
-          <el-button type="primary" plain @click="handlePrint">鎵撳嵃</el-button>
+          <el-button type="danger"
+                     plain
+                     @click="handleDelete">鍒犻櫎
+          </el-button>
+          <el-button type="primary"
+                     plain
+                     @click="handlePrint">鎵撳嵃
+          </el-button>
         </div>
       </div>
-      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
-        :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" :row-class-name="tableRowClassName" show-summary style="width: 100%"
-        :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)">
-        <el-table-column align="center" type="selection" width="55" fixed="left"/>
-        <el-table-column type="expand" width="60" fixed="left">
+      <el-table :data="tableData"
+                border
+                v-loading="tableLoading"
+                @selection-change="handleSelectionChange"
+                :expand-row-keys="expandedRowKeys"
+                :row-key="(row) => row.id"
+                :row-class-name="tableRowClassName"
+                show-summary
+                style="width: 100%"
+                :summary-method="summarizeMainTable"
+                @expand-change="expandChange"
+                height="calc(100vh - 18.5em)">
+        <el-table-column align="center"
+                         type="selection"
+                         width="55"
+                         fixed="left"/>
+        <el-table-column type="expand"
+                         width="60"
+                         fixed="left">
           <template #default="props">
-            <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable">
-              <el-table-column align="center" label="搴忓彿" type="index"/>
-              <el-table-column label="浜у搧澶х被" prop="productCategory" />
-              <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
-              <el-table-column label="鍗曚綅" prop="unit" />
-							<el-table-column label="浜у搧鐘舵��"
-															 width="100px"
-															 align="center">
+            <el-table :data="props.row.children"
+                      border
+                      show-summary
+                      :summary-method="(param) => summarizeChildrenTable(param, props.row)">
+              <el-table-column align="center"
+                               label="搴忓彿"
+                               type="index"/>
+              <el-table-column label="浜у搧澶х被"
+                               prop="productCategory"/>
+              <el-table-column label="瑙勬牸鍨嬪彿"
+                               prop="specificationModel"/>
+              <el-table-column label="鍗曚綅"
+                               prop="unit"/>
+              <el-table-column label="浜у搧鐘舵��"
+                               width="100px"
+                               align="center">
                 <template #default="scope">
-									<el-tag v-if="scope.row.approveStatus === 1"
-													type="success">鍏呰冻</el-tag>
-									<el-tag v-else
-													type="danger">涓嶈冻</el-tag>
+                  <el-tag
+                      v-if="scope.row.approveStatus === 1 && scope.row.noQuantity !== 0"
+                      type="success">鍏呰冻
+                  </el-tag>
+                  <el-tag
+                      v-else-if="scope.row.approveStatus === 0 && scope.row.noQuantity === 0"
+                      type="success">宸插嚭搴�
+                  </el-tag>
+                  <el-tag v-else
+                          type="danger">涓嶈冻
+                  </el-tag>
                 </template>
               </el-table-column>
-							<el-table-column label="鍙戣揣鐘舵��" width="140" align="center">
-								<template #default="scope">
-									<el-tag :type="getShippingStatusType(scope.row)" size="small">
-										{{ getShippingStatusText(scope.row) }}
-									</el-tag>
-								</template>
-							</el-table-column>
-							<el-table-column label="蹇�掑叕鍙�" prop="expressCompany" show-overflow-tooltip />
-							<el-table-column label="蹇�掑崟鍙�" prop="expressNumber" show-overflow-tooltip />
-              <el-table-column label="鍙戣揣杞︾墝" minWidth="100px" align="center">
+              <el-table-column label="鍙戣揣鐘舵��"
+                               width="140"
+                               align="center">
+                <template #default="scope">
+                  <el-tag :type="getShippingStatusType(scope.row)"
+                          size="small">
+                    {{ getShippingStatusText(scope.row) }}
+                  </el-tag>
+                </template>
+              </el-table-column>
+              <el-table-column label="蹇�掑叕鍙�"
+                               prop="expressCompany"
+                               show-overflow-tooltip/>
+              <el-table-column label="蹇�掑崟鍙�"
+                               prop="expressNumber"
+                               show-overflow-tooltip/>
+              <el-table-column label="鍙戣揣杞︾墝"
+                               minWidth="100px"
+                               align="center">
                 <template #default="scope">
                   <div>
-                    <el-tag type="success" v-if="scope.row.shippingCarNumber">{{ scope.row.shippingCarNumber }}</el-tag>
-                    <el-tag v-else type="info">-</el-tag>
+                    <el-tag type="success"
+                            v-if="scope.row.shippingCarNumber">{{ scope.row.shippingCarNumber }}
+                    </el-tag>
+                    <el-tag v-else
+                            type="info">-
+                    </el-tag>
                   </div>
                 </template>
               </el-table-column>
-							<el-table-column label="鍙戣揣鏃ユ湡"
-															 minWidth="100px"
-															 align="center">
+              <el-table-column label="鍙戣揣鏃ユ湡"
+                               minWidth="100px"
+                               align="center">
                 <template #default="scope">
                   <div>
                     <div v-if="scope.row.shippingDate">{{ scope.row.shippingDate }}</div>
-										<el-tag v-else
-														type="info">-</el-tag>
+                    <el-tag v-else
+                            type="info">-
+                    </el-tag>
                   </div>
                 </template>
               </el-table-column>
-              <el-table-column label="鏁伴噺" prop="quantity" />
-              <el-table-column label="绋庣巼(%)" prop="taxRate" />
-              <el-table-column label="鍚◣鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
-              <el-table-column label="鍚◣鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
-              <el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
-            <!--鎿嶄綔-->
-              <el-table-column Width="60px" label="鎿嶄綔" align="center">
+              <el-table-column label="鏁伴噺"
+                               prop="quantity"/>
+              <el-table-column label="寰呭彂璐ф暟閲�"
+                               prop="noQuantity"/>
+              <el-table-column label="绋庣巼(%)"
+                               prop="taxRate"/>
+              <el-table-column label="鍚◣鍗曚环(鍏�)"
+                               prop="taxInclusiveUnitPrice"
+                               :formatter="sensitiveAmountFormatter"/>
+              <el-table-column label="鍚◣鎬讳环(鍏�)"
+                               prop="taxInclusiveTotalPrice"
+                               :formatter="sensitiveAmountFormatter"/>
+              <el-table-column label="涓嶅惈绋庢�讳环(鍏�)"
+                               prop="taxExclusiveTotalPrice"
+                               :formatter="sensitiveAmountFormatter"/>
+              <!--鎿嶄綔-->
+              <el-table-column Width="60px"
+                               label="鎿嶄綔"
+                               align="center">
                 <template #default="scope">
-                  <el-button 
-                    link 
-                    type="primary" 
-                    size="small"
-                    :disabled="!canShip(scope.row)"
-                    @click="openDeliveryForm(scope.row)">
+                  <el-button link
+                             type="primary"
+                             :disabled="!canShip(scope.row)"
+                             @click="openDeliveryForm(scope.row)">
                     鍙戣揣
                   </el-button>
                 </template>
@@ -106,62 +186,141 @@
             </el-table>
           </template>
         </el-table-column>
-        <el-table-column align="center" label="搴忓彿" type="index" width="60" />
-        <el-table-column label="閿�鍞悎鍚屽彿" prop="salesContractNo" width="180" show-overflow-tooltip />
-        <el-table-column label="瀹㈡埛鍚嶇О" prop="customerName" width="300" show-overflow-tooltip />
-        <el-table-column label="涓氬姟鍛�" prop="salesman" width="100" show-overflow-tooltip />
-        <el-table-column label="椤圭洰鍚嶇О" prop="projectName" width="180" show-overflow-tooltip />
-        <el-table-column label="浠樻鏂瑰紡" prop="paymentMethod" show-overflow-tooltip />
-        <el-table-column label="鍚堝悓閲戦(鍏�)" prop="contractAmount" width="220" show-overflow-tooltip
-          :formatter="formattedNumber" />
-        <el-table-column label="褰曞叆浜�" prop="entryPersonName" width="100" show-overflow-tooltip />
-        <el-table-column label="褰曞叆鏃ユ湡" prop="entryDate" width="120" show-overflow-tooltip />
-        <el-table-column label="绛捐鏃ユ湡" prop="executionDate" width="120" show-overflow-tooltip />
-        <el-table-column label="浜や粯鏃ユ湡" prop="deliveryDate" width="120" show-overflow-tooltip />
-        <el-table-column label="澶囨敞" prop="remarks" width="200" show-overflow-tooltip />
-        <el-table-column fixed="right" label="鎿嶄綔" min-width="100" align="center">
+        <el-table-column align="center"
+                         label="搴忓彿"
+                         type="index"
+                         width="60"/>
+        <el-table-column label="閿�鍞悎鍚屽彿"
+                         prop="salesContractNo"
+                         width="180"
+                         show-overflow-tooltip/>
+        <el-table-column label="瀹㈡埛鍚嶇О"
+                         prop="customerName"
+                         width="300"
+                         show-overflow-tooltip/>
+        <el-table-column label="涓氬姟鍛�"
+                         prop="salesman"
+                         width="100"
+                         show-overflow-tooltip/>
+        <el-table-column label="椤圭洰鍚嶇О"
+                         prop="projectName"
+                         width="180"
+                         show-overflow-tooltip/>
+        <el-table-column label="浠樻鏂瑰紡"
+                         prop="paymentMethod"
+                         show-overflow-tooltip/>
+        <el-table-column label="鍚堝悓閲戦(鍏�)"
+                         prop="contractAmount"
+                         width="220"
+                         show-overflow-tooltip
+                         :formatter="formattedNumber"/>
+        <el-table-column label="褰曞叆浜�"
+                         prop="entryPersonName"
+                         width="100"
+                         show-overflow-tooltip/>
+        <el-table-column label="褰曞叆鏃ユ湡"
+                         prop="entryDate"
+                         width="120"
+                         show-overflow-tooltip/>
+        <el-table-column label="绛捐鏃ユ湡"
+                         prop="executionDate"
+                         width="120"
+                         show-overflow-tooltip/>
+        <el-table-column label="浜や粯鏃ユ湡"
+                         prop="deliveryDate"
+                         width="120"
+                         show-overflow-tooltip/>
+        <el-table-column label="澶囨敞"
+                         prop="remarks"
+                         width="200"
+                         show-overflow-tooltip/>
+        <el-table-column fixed="right"
+                         label="鎿嶄綔"
+                         width="130"
+                         align="center">
           <template #default="scope">
-            <el-button link type="primary" size="small" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit">缂栬緫</el-button>
-<!--            <el-button link type="primary" size="small" @click="openForm('view', scope.row)">璇︽儏</el-button>-->
-            <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">闄勪欢</el-button>
-<!--            <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">鍙戣揣</el-button>-->
+            <el-button link
+                       type="primary"
+                       @click="openForm('edit', scope.row)"
+                       :disabled="!scope.row.isEdit || scope.row.hasProductionRecord || !canEditLedger(scope.row)">缂栬緫
+            </el-button>
+            <el-button link
+                       type="primary"
+                       @click="openFileDialog(scope.row)">闄勪欢
+            </el-button>
           </template>
         </el-table-column>
       </el-table>
-      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
-        :page="page.current" :limit="page.size" @pagination="paginationChange" />
+      <pagination v-show="total > 0"
+                  :total="total"
+                  layout="total, sizes, prev, pager, next, jumper"
+                  :page="page.current"
+                  :limit="page.size"
+                  @pagination="paginationChange"/>
     </div>
-    <FormDialog v-model="dialogFormVisible" :title="operationType === 'add' ? '鏂板閿�鍞彴璐﹂〉闈�' : '缂栬緫閿�鍞彴璐﹂〉闈�'" :width="'70%'"
-      :operation-type="operationType" @close="closeDia" @confirm="submitForm" @cancel="closeDia">
-      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
-				<!-- 鎶ヤ环鍗曞鍏ュ叆鍙o細鏀惧湪琛ㄥ崟椤堕儴锛岄�夋嫨鍚庡弽鏄惧鎴�/涓氬姟鍛樼瓑 -->
-				<el-row v-if="operationType === 'add'" style="margin-bottom: 10px;">
-					<el-col :span="24" style="text-align: right;">
-						<el-button type="primary" plain @click="openQuotationDialog">
-							浠庨攢鍞姤浠峰鍏�
-						</el-button>
-					</el-col>
-				</el-row>
+    <FormDialog v-model="dialogFormVisible"
+                :title="operationType === 'add' ? '鏂板閿�鍞彴璐﹂〉闈�' : '缂栬緫閿�鍞彴璐﹂〉闈�'"
+                :width="'70%'"
+                :operation-type="operationType"
+                @close="closeDia"
+                @confirm="submitForm"
+                @cancel="closeDia">
+      <el-form :model="form"
+               label-width="140px"
+               label-position="top"
+               :rules="rules"
+               ref="formRef">
+        <!-- 鎶ヤ环鍗曞鍏ュ叆鍙o細鏀惧湪琛ㄥ崟椤堕儴锛岄�夋嫨鍚庡弽鏄惧鎴�/涓氬姟鍛樼瓑 -->
+        <el-row v-if="operationType === 'add'"
+                style="margin-bottom: 10px;">
+          <el-col :span="24"
+                  style="text-align: right;">
+            <el-button type="primary"
+                       plain
+                       @click="openQuotationDialog">
+              浠庨攢鍞姤浠峰鍏�
+            </el-button>
+          </el-col>
+        </el-row>
         <el-row :gutter="30">
           <el-col :span="12">
-            <el-form-item label="閿�鍞悎鍚屽彿锛�" prop="salesContractNo">
-              <el-input v-model="form.salesContractNo" placeholder="鑷姩鐢熸垚" clearable disabled />
+            <el-form-item label="閿�鍞悎鍚屽彿锛�"
+                          prop="salesContractNo">
+              <el-input v-model="form.salesContractNo"
+                        placeholder="鑷姩鐢熸垚"
+                        clearable
+                        disabled/>
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="涓氬姟鍛橈細" prop="salesman">
-              <el-select v-model="form.salesman" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'view'">
-                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
-                  :value="item.nickName" />
+            <el-form-item label="涓氬姟鍛橈細"
+                          prop="salesman">
+              <el-select v-model="form.salesman"
+                         placeholder="璇烽�夋嫨"
+                         clearable
+                         :disabled="operationType === 'view'"
+                         filterable>
+                <el-option v-for="item in userList"
+                           :key="item.nickName"
+                           :label="item.nickName"
+                           :value="item.nickName"/>
               </el-select>
             </el-form-item>
           </el-col>
         </el-row>
         <el-row :gutter="30">
           <el-col :span="12">
-            <el-form-item label="瀹㈡埛鍚嶇О锛�" prop="customerId">
-              <el-select v-model="form.customerId" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'view'">
-                <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id">
+            <el-form-item label="瀹㈡埛鍚嶇О锛�"
+                          prop="customerId">
+              <el-select v-model="form.customerId"
+                         placeholder="璇烽�夋嫨"
+                         clearable
+                         :disabled="operationType === 'view'"
+                         filterable>
+                <el-option v-for="item in customerOption"
+                           :key="item.id"
+                           :label="item.customerName"
+                           :value="item.id">
                   {{
                     item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
                   }}
@@ -169,520 +328,674 @@
               </el-select>
             </el-form-item>
           </el-col>
-					<el-col :span="12">
-						<el-form-item label="椤圭洰鍚嶇О锛�" prop="projectName">
-							<el-input v-model="form.projectName" placeholder="璇疯緭鍏�" clearable :disabled="operationType === 'view'" />
-						</el-form-item>
-					</el-col>
-        </el-row>
-        <el-row :gutter="30">
-					<el-col :span="12">
-						<el-form-item label="绛捐鏃ユ湡锛�" prop="executionDate">
-							<el-date-picker style="width: 100%" v-model="form.executionDate" value-format="YYYY-MM-DD"
-															format="YYYY-MM-DD" type="date" placeholder="璇烽�夋嫨" clearable :disabled="operationType === 'view'" />
-						</el-form-item>
-					</el-col>
-					<el-col :span="12">
-						<el-form-item label="浠樻鏂瑰紡">
-							<el-input v-model="form.paymentMethod" placeholder="璇疯緭鍏�" clearable :disabled="operationType === 'view'" />
-						</el-form-item>
-					</el-col>
-				</el-row>
-				<el-row :gutter="30">
-					<el-col :span="12">
-						<el-form-item label="褰曞叆浜猴細" prop="entryPerson">
-							<el-select v-model="form.entryPerson"
-												 filterable
-												 default-first-option
-												 :reserve-keyword="false" placeholder="璇烽�夋嫨" clearable @change="changs">
-								<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
-							</el-select>
-						</el-form-item>
-					</el-col>
-					<el-col :span="12">
-						<el-form-item label="褰曞叆鏃ユ湡锛�" prop="entryDate">
-							<el-date-picker style="width: 100%" v-model="form.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
-															type="date" placeholder="璇烽�夋嫨" clearable />
-						</el-form-item>
-					</el-col>
-				</el-row>
-        <el-row :gutter="30">
           <el-col :span="12">
-            <el-form-item label="浜よ揣鏃ユ湡锛�" prop="entryDate">
-              <el-date-picker style="width: 100%" v-model="form.deliveryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
-                              type="date" placeholder="璇烽�夋嫨" clearable />
+            <el-form-item label="椤圭洰鍚嶇О锛�"
+                          prop="projectName">
+              <el-input v-model="form.projectName"
+                        placeholder="璇疯緭鍏�"
+                        clearable
+                        :disabled="operationType === 'view'"/>
             </el-form-item>
           </el-col>
         </el-row>
-				<el-row>
-					<el-form-item label="浜у搧淇℃伅锛�" prop="entryDate">
-						<el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">娣诲姞</el-button>
-						<el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" >鍒犻櫎</el-button>
-					</el-form-item>
-				</el-row>
-				<el-table :data="productData" border @selection-change="productSelected" show-summary
-									:summary-method="summarizeMainTable">
-					<el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'"
-						:selectable="(row) => !isProductShipped(row)" />
-					<el-table-column align="center" label="搴忓彿" type="index" width="60" />
-					<el-table-column label="浜у搧澶х被" prop="productCategory" />
-					<el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
-					<el-table-column label="鍗曚綅" prop="unit" />
-					<el-table-column label="鏁伴噺" prop="quantity" />
-					<el-table-column label="绋庣巼(%)" prop="taxRate" />
-					<el-table-column label="鍚◣鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
-					<el-table-column label="鍚◣鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
-					<el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
-					<el-table-column fixed="right" label="鎿嶄綔" min-width="60" align="center" v-if="operationType !== 'view'">
-						<template #default="scope">
-							<el-button link type="primary" size="small" 
-								:disabled="isProductShipped(scope.row)"
-								@click="openProductForm('edit', scope.row,scope.$index)">缂栬緫</el-button>
-						</template>
-					</el-table-column>
-				</el-table>
-				<el-row :gutter="30">
-					<el-col :span="24">
-						<el-form-item label="澶囨敞锛�" prop="remarks">
-							<el-input v-model="form.remarks" placeholder="璇疯緭鍏�" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
-						</el-form-item>
-					</el-col>
-				</el-row>
-				<el-row :gutter="30">
-					<el-col :span="24">
-						<el-form-item label="闄勪欢鏉愭枡锛�" prop="salesLedgerFiles">
-							<el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
-												 :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
-												 :on-success="handleUploadSuccess" :on-remove="handleRemove">
-								<el-button type="primary" v-if="operationType !== 'view'">涓婁紶</el-button>
-								<template #tip v-if="operationType !== 'view'">
-									<div class="el-upload__tip">
-										鏂囦欢鏍煎紡鏀寔
-										doc锛宒ocx锛寈ls锛寈lsx锛宲pt锛宲ptx锛宲df锛宼xt锛寈ml锛宩pg锛宩peg锛宲ng锛実if锛宐mp锛宺ar锛寊ip锛�7z
-									</div>
-								</template>
-							</el-upload>
-						</el-form-item>
-					</el-col>
-				</el-row>
-			</el-form>
-		</FormDialog>
-
-		<!-- 浠庢姤浠峰崟瀵煎叆锛堜粎瀹℃壒閫氳繃锛� -->
-		<el-dialog
-			v-model="quotationDialogVisible"
-			title="閫夋嫨瀹℃壒閫氳繃鐨勯攢鍞姤浠峰崟"
-			width="80%"
-			:close-on-click-modal="false"
-		>
-			<div style="margin-bottom: 12px; display:flex; gap: 12px; align-items:center;">
-				<el-input
-					v-model="quotationSearchForm.quotationNo"
-					placeholder="璇疯緭鍏ユ姤浠峰崟鍙�"
-					clearable
-					style="max-width: 260px;"
-					@change="fetchQuotationList"
-				/>
-				<el-input
-					v-model="quotationSearchForm.customer"
-					placeholder="璇疯緭鍏ュ鎴峰悕绉�"
-					clearable
-					style="max-width: 260px;"
-					@change="fetchQuotationList"
-				/>
-				<el-button type="primary" @click="fetchQuotationList">鎼滅储</el-button>
-				<el-button @click="resetQuotationSearch">閲嶇疆</el-button>
-			</div>
-			
-			<el-table
-				:data="quotationList"
-				border
-				stripe
-				v-loading="quotationLoading"
-				height="420px"
-			>
-				<el-table-column align="center" label="搴忓彿" type="index" width="60" />
-				<el-table-column prop="quotationNo" label="鎶ヤ环鍗曞彿" width="180" show-overflow-tooltip />
-				<el-table-column prop="customer" label="瀹㈡埛鍚嶇О" min-width="220" show-overflow-tooltip />
-				<el-table-column prop="salesperson" label="涓氬姟鍛�" width="120" show-overflow-tooltip />
-				<el-table-column prop="quotationDate" label="鎶ヤ环鏃ユ湡" width="140" />
-				<el-table-column prop="status" label="瀹℃壒鐘舵��" width="120" align="center" />
-				<el-table-column prop="totalAmount" label="鎶ヤ环閲戦(鍏�)" width="160" align="right">
-					<template #default="scope">
-						{{ Number(scope.row.totalAmount ?? 0).toFixed(2) }}
-					</template>
-				</el-table-column>
-				<el-table-column fixed="right" label="鎿嶄綔" width="120" align="center">
-					<template #default="scope">
-						<el-button type="primary" link @click="applyQuotation(scope.row)">閫夋嫨</el-button>
-					</template>
-				</el-table-column>
-			</el-table>
-			
-			<pagination
-				v-show="quotationPage.total > 0"
-				:total="quotationPage.total"
-				layout="total, sizes, prev, pager, next, jumper"
-				:page="quotationPage.current"
-				:limit="quotationPage.size"
-				@pagination="quotationPaginationChange"
-			/>
-			
-			<template #footer>
-				<el-button @click="quotationDialogVisible = false">鍏抽棴</el-button>
-			</template>
-		</el-dialog>
-		<FormDialog
-			v-model="productFormVisible"
-			:title="productOperationType === 'add' ? '鏂板浜у搧' : '缂栬緫浜у搧'"
-			:width="'40%'"
-			:operation-type="productOperationType"
-			@close="closeProductDia"
-			@confirm="submitProduct"
-			@cancel="closeProductDia">
-			<el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef">
-				<el-row :gutter="30">
-					<el-col :span="24">
-						<el-form-item label="浜у搧澶х被锛�" prop="productCategory">
-							<!-- <el-select v-model="productForm.productCategory" placeholder="璇烽�夋嫨" clearable>
-								<el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
-							</el-select> -->
-							<el-tree-select v-model="productForm.productCategory" placeholder="璇烽�夋嫨" clearable check-strictly
-															@change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" />
-						</el-form-item>
-					</el-col>
-				</el-row>
-				<el-row :gutter="30">
-					<el-col :span="24">
-						<el-form-item label="瑙勬牸鍨嬪彿锛�" prop="productModelId">
-							<el-select v-model="productForm.productModelId" placeholder="璇烽�夋嫨" clearable @change="getProductModel" filterable>
-								<el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
-							</el-select>
-						</el-form-item>
-					</el-col>
-				</el-row>
-				<el-row :gutter="30">
-					<el-col :span="12">
-						<el-form-item label="鍗曚綅锛�" prop="unit">
-							<el-input v-model="productForm.unit" placeholder="璇疯緭鍏�" clearable />
-						</el-form-item>
-					</el-col>
-					<el-col :span="12">
-						<el-form-item label="绋庣巼(%)锛�" prop="taxRate">
-							<el-select v-model="productForm.taxRate" placeholder="璇烽�夋嫨" clearable @change="calculateFromTaxRate">
-								<el-option label="1" value="1" />
-								<el-option label="6" value="6" />
-								<el-option label="13" value="13" />
-							</el-select>
-						</el-form-item>
-					</el-col>
-				</el-row>
-				<el-row :gutter="30">
-					<el-col :span="12">
-						<el-form-item label="鍚◣鍗曚环(鍏�)锛�" prop="taxInclusiveUnitPrice">
-							<el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%"
-															 :precision="2"
-															 placeholder="璇疯緭鍏�" clearable @change="calculateFromUnitPrice" />
-						</el-form-item>
-					</el-col>
-					<el-col :span="12">
-						<el-form-item label="鏁伴噺锛�" prop="quantity">
-							<el-input-number  :step="0.1" :min="0" v-model="productForm.quantity" placeholder="璇疯緭鍏�" clearable
-																:precision="2"
-																@change="calculateFromQuantity" style="width: 100%" />
-						</el-form-item>
-					</el-col>
-				</el-row>
-				<el-row :gutter="30">
-					<el-col :span="12">
-						<el-form-item label="鍚◣鎬讳环(鍏�)锛�" prop="taxInclusiveTotalPrice">
-							<el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="璇疯緭鍏�" clearable @change="calculateFromTotalPrice" />
-						</el-form-item>
-					</el-col>
-					<el-col :span="12">
-						<el-form-item label="涓嶅惈绋庢�讳环(鍏�)锛�" prop="taxExclusiveTotalPrice">
-							<el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="璇疯緭鍏�" clearable @change="calculateFromExclusiveTotalPrice" />
-						</el-form-item>
-					</el-col>
-				</el-row>
-				<el-row :gutter="30">
-					<el-col :span="12">
-						<el-form-item label="鍙戠エ绫诲瀷锛�" prop="invoiceType">
-							<el-select v-model="productForm.invoiceType" placeholder="璇烽�夋嫨" clearable>
-								<el-option label="澧炴櫘绁�" value="澧炴櫘绁�" />
-								<el-option label="澧炰笓绁�" value="澧炰笓绁�" />
-							</el-select>
-						</el-form-item>
-					</el-col>
-				</el-row>
-			</el-form>
-		</FormDialog>
-		<!-- 瀵煎叆寮圭獥 -->
-		<FormDialog
-			v-model="importUpload.open"
-			:title="importUpload.title"
-			:width="'600px'"
-			@close="importUpload.open = false"
-			@confirm="submitImportFile"
-			@cancel="importUpload.open = false"
-		>
-			<el-upload
-				ref="importUploadRef"
-				:limit="1"
-				accept=".xlsx,.xls"
-				:action="importUpload.url"
-				:headers="importUpload.headers"
-				:before-upload="importUpload.beforeUpload"
-				:on-success="importUpload.onSuccess"
-				:on-error="importUpload.onError"
-				:on-progress="importUpload.onProgress"
-				:on-change="importUpload.onChange"
-				:auto-upload="false"
-				drag
-			>
-				<i class="el-icon-upload"></i>
-				<div class="el-upload__text">
-					灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em>
-				</div>
-				<template #tip>
-					<div class="el-upload__tip">
-						浠呮敮鎸� xls/xlsx锛屽ぇ灏忎笉瓒呰繃 10MB銆�
-						<el-button link type="primary" @click="downloadTemplate">涓嬭浇瀵煎叆妯℃澘</el-button>
-					</div>
-				</template>
-			</el-upload>
-		</FormDialog>
-		<!-- 闄勪欢鍒楄〃寮圭獥 -->
-		<FileListDialog
-			ref="fileListRef"
-			v-model="fileListDialogVisible"
-			title="闄勪欢鍒楄〃"
-		/>
-		<!-- 鎵撳嵃棰勮寮圭獥 -->
-		<el-dialog
-			v-model="printPreviewVisible"
-			title="鎵撳嵃棰勮"
-			width="90%"
-			:close-on-click-modal="false"
-			class="print-preview-dialog"
-		>
-			<div class="print-preview-container">
-				<div class="print-preview-header">
-					<el-button type="primary" @click="executePrint">鎵ц鎵撳嵃</el-button>
-					<el-button @click="printPreviewVisible = false">鍏抽棴棰勮</el-button>
-				</div>
-				<div class="print-preview-content">
-					<div v-if="printData.length === 0" style="text-align: center; padding: 50px; color: #999;">
-						鏆傛棤鎵撳嵃鏁版嵁
-					</div>
-					<div v-else style="text-align: center; padding: 10px; color: #666; font-size: 14px; background: #e8f4fd; margin-bottom: 10px;">
-						鍏� {{ printData.length }} 鏉℃暟鎹緟鎵撳嵃
-					</div>
-					<div v-for="(item, index) in printData" :key="index" class="print-page">
-						<div class="delivery-note">
-							<div class="header">
-								<div class="company-name">鑻辨辰闃查攬鏂版潗鏂欐湁闄愬叕鍙�</div>
-								<div class="document-title">闆跺敭鍙戣揣鍗�</div>
-							</div>
-							
-							<div class="info-section">
-								<div class="info-row">
-									<div>
-										<span class="label">鍙戣揣鏃ユ湡锛�</span>
-										<span class="value">{{ formatDate(item.createTime) }}</span>
-									</div>
-									<div>
-										<span class="label">鍙戣揣杞︾墝鍙凤細</span>
-										<span class="value">{{ item.shippingCarNumber }}</span>
-									</div>
-								</div>
-								<div class="info-row">
-									<div>
-										<span class="label">瀹㈡埛鍚嶇О锛�</span>
-										<span class="value">{{ item.customerName || '寮犵埍鏈�' }}</span>
-									</div>
-									<span class="label">鍗曞彿锛�</span>
-									<span class="value">{{ item.salesContractNo }}</span>
-								</div>
-							</div>
-							
-							<div class="table-section">
-								<table class="product-table">
-									<thead>
-									<tr>
-										<th>浜у搧鍚嶇О</th>
-										<th>瑙勬牸鍨嬪彿</th>
-										<th>鍗曚綅</th>
-										<th>鍗曚环</th>
-										<th>闆跺敭鏁伴噺</th>
-										<th>闆跺敭閲戦</th>
-									</tr>
-									</thead>
-									<tbody>
-									<tr v-for="product in item.products" :key="product.id">
-										<td>{{ product.productCategory || '' }}</td>
-										<td>{{ product.specificationModel || '' }}</td>
-										<td>{{ product.unit || '' }}</td>
-										<td>{{ product.taxInclusiveUnitPrice || '0' }}</td>
-										<td>{{ product.quantity || '0' }}</td>
-										<td>{{ product.taxInclusiveTotalPrice || '0' }}</td>
-									</tr>
-									<tr v-if="!item.products || item.products.length === 0">
-										<td colspan="6" style="text-align: center; color: #999;">鏆傛棤浜у搧鏁版嵁</td>
-									</tr>
-									</tbody>
-									<tfoot>
-									<tr>
-										<td class="label">鍚堣</td>
-										<td class="total-value"></td>
-										<td class="total-value"></td>
-										<td class="total-value"></td>
-										<td class="total-value">{{ getTotalQuantity(item.products) }}</td>
-										<td class="total-value">{{ getTotalAmount(item.products) }}</td>
-									</tr>
-									</tfoot>
-								</table>
-							</div>
-							
-							<div class="footer-section">
-								<div class="footer-row">
-									<div class="footer-item">
-										<span class="label">鏀惰揣鐢佃瘽锛�</span>
-										<span class="value"></span>
-									</div>
-									<div class="footer-item">
-										<span class="label">鏀惰揣浜猴細</span>
-										<span class="value"></span>
-									</div>
-									<div class="footer-item address-item">
-										<span class="label">鏀惰揣鍦板潃锛�</span>
-										<span class="value address-value"></span>
-									</div>
-								</div>
-								<div class="footer-row">
-									<div class="footer-item">
-										<span class="label">鎿嶄綔鍛橈細</span>
-										<span class="value">{{ userStore.nickName || '鎾曞紑鍓�' }}</span>
-									</div>
-									<div class="footer-item">
-										<span class="label">鎵撳嵃鏃ユ湡锛�</span>
-										<span class="value">{{ formatDateTime(new Date()) }}</span>
-									</div>
-								</div>
-							</div>
-						</div>
-					</div>
-				</div>
-			</div>
-		</el-dialog>
-		<!-- 鍙戣揣寮规 -->
-		<el-dialog
-			v-model="deliveryFormVisible"
-			title="鍙戣揣淇℃伅"
-		width="40%"
-			@close="closeDeliveryDia"
-		>
-			<el-form :model="deliveryForm" label-width="120px" label-position="top" :rules="deliveryRules" ref="deliveryFormRef">
-				<el-row :gutter="30">
-					<el-col :span="24">
-						<el-form-item label="鍙戣揣绫诲瀷锛�" prop="type">
-							<el-select
-								v-model="deliveryForm.type"
-								placeholder="璇烽�夋嫨鍙戣揣绫诲瀷"
-								style="width: 100%"
-							>
-								<el-option label="璐ц溅" value="璐ц溅" />
-								<el-option label="蹇��" value="蹇��" />
-							</el-select>
-						</el-form-item>
-					</el-col>
-				</el-row>
-
-        <!-- 瀹℃壒浜洪�夋嫨锛堜豢鍗忓悓瀹℃壒閲岀殑瀹℃壒浜鸿妭鐐归�夋嫨锛� -->
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="绛捐鏃ユ湡锛�"
+                          prop="executionDate">
+              <el-date-picker style="width: 100%"
+                              v-model="form.executionDate"
+                              value-format="YYYY-MM-DD"
+                              format="YYYY-MM-DD"
+                              type="date"
+                              placeholder="璇烽�夋嫨"
+                              clearable
+                              :disabled="operationType === 'view'"/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="浠樻鏂瑰紡">
+              <el-input v-model="form.paymentMethod"
+                        placeholder="璇疯緭鍏�"
+                        clearable
+                        :disabled="operationType === 'view'"/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="褰曞叆浜猴細"
+                          prop="entryPerson">
+              <el-select v-model="form.entryPerson"
+                         filterable
+                         default-first-option
+                         :reserve-keyword="false"
+                         placeholder="璇烽�夋嫨"
+                         clearable
+                         @change="changs">
+                <el-option v-for="item in userList"
+                           :key="item.userId"
+                           :label="item.nickName"
+                           :value="item.userId"/>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="褰曞叆鏃ユ湡锛�"
+                          prop="entryDate">
+              <el-date-picker style="width: 100%"
+                              v-model="form.entryDate"
+                              value-format="YYYY-MM-DD"
+                              format="YYYY-MM-DD"
+                              type="date"
+                              placeholder="璇烽�夋嫨"
+                              clearable/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="浜よ揣鏃ユ湡锛�"
+                          prop="entryDate">
+              <el-date-picker style="width: 100%"
+                              v-model="form.deliveryDate"
+                              value-format="YYYY-MM-DD"
+                              format="YYYY-MM-DD"
+                              type="date"
+                              placeholder="璇烽�夋嫨"
+                              clearable/>
+            </el-form-item>
+          </el-col>
+        </el-row>
         <el-row>
+          <el-form-item label="浜у搧淇℃伅锛�"
+                        prop="entryDate">
+            <el-button v-if="operationType !== 'view'"
+                       type="primary"
+                       @click="openProductForm('add')">娣诲姞
+            </el-button>
+            <el-button v-if="operationType !== 'view'"
+                       plain
+                       type="danger"
+                       @click="deleteProduct">鍒犻櫎
+            </el-button>
+          </el-form-item>
+        </el-row>
+        <el-table :data="productData"
+                  border
+                  @selection-change="productSelected"
+                  show-summary
+                  :summary-method="summarizeMainTable">
+          <el-table-column align="center"
+                           type="selection"
+                           width="55"
+                           v-if="operationType !== 'view'"
+                           :selectable="(row) => !isProductShipped(row)"/>
+          <el-table-column align="center"
+                           label="搴忓彿"
+                           type="index"
+                           width="60"/>
+          <el-table-column label="浜у搧澶х被"
+                           prop="productCategory"/>
+          <el-table-column label="瑙勬牸鍨嬪彿"
+                           prop="specificationModel"/>
+          <el-table-column label="鍗曚綅"
+                           prop="unit"/>
+          <el-table-column label="鏁伴噺"
+                           prop="quantity"/>
+          <el-table-column label="绋庣巼(%)"
+                           prop="taxRate"/>
+          <el-table-column label="鍚◣鍗曚环(鍏�)"
+                           prop="taxInclusiveUnitPrice"
+                           :formatter="formattedNumber"/>
+          <el-table-column label="鍚◣鎬讳环(鍏�)"
+                           prop="taxInclusiveTotalPrice"
+                           :formatter="formattedNumber"/>
+          <el-table-column label="涓嶅惈绋庢�讳环(鍏�)"
+                           prop="taxExclusiveTotalPrice"
+                           :formatter="formattedNumber"/>
+          <el-table-column label="鏄惁鐢熶骇"
+                           prop="isProduction"
+                           width="150">
+            <template #default="scope">
+              <el-tag :type="scope.row.isProduction ? 'success' : 'info'">
+                {{ scope.row.isProduction ? '鏄�' : '鍚�' }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column fixed="right"
+                           label="鎿嶄綔"
+                           min-width="60"
+                           align="center"
+                           v-if="operationType !== 'view'">
+            <template #default="scope">
+              <el-button link
+                         type="primary"
+                         size="small"
+                         :disabled="isProductShipped(scope.row)"
+                         @click="openProductForm('edit', scope.row,scope.$index)">缂栬緫
+              </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <el-row :gutter="30">
           <el-col :span="24">
-            <el-form-item>
-              <template #label>
-                <span>瀹℃壒浜洪�夋嫨锛�</span>
-                <el-button type="primary" @click="addApproverNode" style="margin-left: 8px;">鏂板鑺傜偣</el-button>
-              </template>
-              <div style="display: flex; align-items: flex-end; flex-wrap: wrap;">
-                <div
-                  v-for="(node, index) in approverNodes"
-                  :key="node.id"
-                  style="margin-right: 20px; text-align: center; margin-bottom: 10px;"
-                >
+            <el-form-item label="澶囨敞锛�"
+                          prop="remarks">
+              <el-input v-model="form.remarks"
+                        placeholder="璇疯緭鍏�"
+                        clearable
+                        type="textarea"
+                        :rows="2"
+                        :disabled="operationType === 'view'"/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="24">
+            <el-form-item label="闄勪欢鏉愭枡锛�"
+                          prop="salesLedgerFiles">
+              <FileUpload v-model:file-list="fileList"/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </FormDialog>
+    <!-- 浠庢姤浠峰崟瀵煎叆锛堜粎瀹℃壒閫氳繃锛� -->
+    <el-dialog v-model="quotationDialogVisible"
+               title="閫夋嫨瀹℃壒閫氳繃鐨勯攢鍞姤浠峰崟"
+               width="80%"
+               :close-on-click-modal="false">
+      <div style="margin-bottom: 12px; display:flex; gap: 12px; align-items:center;">
+        <el-input v-model="quotationSearchForm.quotationNo"
+                  placeholder="璇疯緭鍏ユ姤浠峰崟鍙�"
+                  clearable
+                  style="max-width: 260px;"
+                  @change="fetchQuotationList"/>
+        <el-input v-model="quotationSearchForm.customer"
+                  placeholder="璇疯緭鍏ュ鎴峰悕绉�"
+                  clearable
+                  style="max-width: 260px;"
+                  @change="fetchQuotationList"/>
+        <el-button type="primary"
+                   @click="fetchQuotationList">鎼滅储
+        </el-button>
+        <el-button @click="resetQuotationSearch">閲嶇疆</el-button>
+      </div>
+      <el-table :data="quotationList"
+                border
+                stripe
+                v-loading="quotationLoading"
+                height="420px">
+        <el-table-column align="center"
+                         label="搴忓彿"
+                         type="index"
+                         width="60"/>
+        <el-table-column prop="quotationNo"
+                         label="鎶ヤ环鍗曞彿"
+                         width="180"
+                         show-overflow-tooltip/>
+        <el-table-column prop="customer"
+                         label="瀹㈡埛鍚嶇О"
+                         min-width="220"
+                         show-overflow-tooltip/>
+        <el-table-column prop="salesperson"
+                         label="涓氬姟鍛�"
+                         width="120"
+                         show-overflow-tooltip/>
+        <el-table-column prop="quotationDate"
+                         label="鎶ヤ环鏃ユ湡"
+                         width="140"/>
+        <el-table-column prop="status"
+                         label="瀹℃壒鐘舵��"
+                         width="120"
+                         align="center"/>
+        <el-table-column prop="totalAmount"
+                         label="鎶ヤ环閲戦(鍏�)"
+                         width="160"
+                         align="right">
+          <template #default="scope">
+            {{ Number(scope.row.totalAmount ?? 0).toFixed(2) }}
+          </template>
+        </el-table-column>
+        <el-table-column fixed="right"
+                         label="鎿嶄綔"
+                         width="120"
+                         align="center">
+          <template #default="scope">
+            <el-button type="primary"
+                       link
+                       @click="applyQuotation(scope.row)">閫夋嫨
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="quotationPage.total > 0"
+                  :total="quotationPage.total"
+                  layout="total, sizes, prev, pager, next, jumper"
+                  :page="quotationPage.current"
+                  :limit="quotationPage.size"
+                  @pagination="quotationPaginationChange"/>
+      <template #footer>
+        <el-button @click="quotationDialogVisible = false">鍏抽棴</el-button>
+      </template>
+    </el-dialog>
+    <FormDialog v-model="productFormVisible"
+                :title="productOperationType === 'add' ? '鏂板浜у搧' : '缂栬緫浜у搧'"
+                :width="'40%'"
+                :operation-type="productOperationType"
+                @close="closeProductDia"
+                @confirm="submitProduct"
+                @cancel="closeProductDia">
+      <el-form :model="productForm"
+               label-width="140px"
+               label-position="top"
+               :rules="productRules"
+               ref="productFormRef">
+        <el-row :gutter="30">
+          <el-col :span="24">
+            <el-form-item label="浜у搧澶х被锛�"
+                          prop="productCategory">
+              <el-tree-select v-model="productForm.productCategory"
+                              placeholder="璇烽�夋嫨"
+                              clearable
+                              filterable
+                              check-strictly
+                              @change="getModels"
+                              :data="productOptions"
+                              :render-after-expand="false"
+                              style="width: 100%"/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="24">
+            <el-form-item label="瑙勬牸鍨嬪彿锛�"
+                          prop="productModelId">
+              <el-select v-model="productForm.productModelId"
+                         placeholder="璇烽�夋嫨"
+                         clearable
+                         @change="getProductModel"
+                         filterable>
+                <el-option v-for="item in modelOptions"
+                           :key="item.id"
+                           :label="item.model"
+                           :value="item.id"/>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鍗曚綅锛�"
+                          prop="unit">
+              <el-input v-model="productForm.unit"
+                        placeholder="璇疯緭鍏�"
+                        clearable/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="绋庣巼(%)锛�"
+                          prop="taxRate">
+              <el-select v-model="productForm.taxRate"
+                         placeholder="璇烽�夋嫨"
+                         clearable
+                         @change="calculateFromTaxRate">
+                <el-option
+                  v-for="dict in tax_rate"
+                  :key="dict.value"
+                  :label="dict.label"
+                  :value="dict.value"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鍚◣鍗曚环(鍏�)锛�"
+                          prop="taxInclusiveUnitPrice">
+              <el-input-number :step="0.01"
+                               :min="0"
+                               v-model="productForm.taxInclusiveUnitPrice"
+                               style="width: 100%"
+                               :precision="2"
+                               placeholder="璇疯緭鍏�"
+                               clearable
+                               @change="calculateFromUnitPrice"/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鏁伴噺锛�"
+                          prop="quantity">
+              <el-input-number :step="0.1"
+                               :min="0"
+                               v-model="productForm.quantity"
+                               placeholder="璇疯緭鍏�"
+                               clearable
+                               :precision="2"
+                               @change="calculateFromQuantity"
+                               style="width: 100%"/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鍚◣鎬讳环(鍏�)锛�"
+                          prop="taxInclusiveTotalPrice">
+              <el-input v-model="productForm.taxInclusiveTotalPrice"
+                        placeholder="璇疯緭鍏�"
+                        clearable
+                        @change="calculateFromTotalPrice"/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="涓嶅惈绋庢�讳环(鍏�)锛�"
+                          prop="taxExclusiveTotalPrice">
+              <el-input v-model="productForm.taxExclusiveTotalPrice"
+                        placeholder="璇疯緭鍏�"
+                        clearable
+                        @change="calculateFromExclusiveTotalPrice"/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="鍙戠エ绫诲瀷锛�"
+                          prop="invoiceType">
+              <el-select v-model="productForm.invoiceType"
+                         placeholder="璇烽�夋嫨"
+                         clearable>
+                <el-option label="澧炴櫘绁�"
+                           value="澧炴櫘绁�"/>
+                <el-option label="澧炰笓绁�"
+                           value="澧炰笓绁�"/>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鏄惁鐢熶骇锛�"
+                          prop="isProduction">
+              <el-radio-group v-model="productForm.isProduction">
+                <el-radio label="鏄�"
+                          :value="true"/>
+                <el-radio label="鍚�"
+                          :value="false"/>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </FormDialog>
+    <!-- 瀵煎叆寮圭獥 -->
+    <FormDialog v-model="importUpload.open"
+                :title="importUpload.title"
+                :width="'600px'"
+                @close="importUpload.open = false"
+                @confirm="submitImportFile"
+                @cancel="importUpload.open = false">
+      <el-upload ref="importUploadRef"
+                 :limit="1"
+                 accept=".xlsx,.xls"
+                 :action="importUpload.url"
+                 :headers="importUpload.headers"
+                 :before-upload="importUpload.beforeUpload"
+                 :on-success="importUpload.onSuccess"
+                 :on-error="importUpload.onError"
+                 :on-progress="importUpload.onProgress"
+                 :on-change="importUpload.onChange"
+                 :auto-upload="false"
+                 drag>
+        <i class="el-icon-upload"></i>
+        <div class="el-upload__text">
+          灏嗘枃浠舵嫋鍒版澶勶紝鎴�<em>鐐瑰嚮涓婁紶</em>
+        </div>
+        <template #tip>
+          <div class="el-upload__tip">
+            浠呮敮鎸� xls/xlsx锛屽ぇ灏忎笉瓒呰繃 10MB銆�
+            <el-button link
+                       type="primary"
+                       @click="downloadTemplate">涓嬭浇瀵煎叆妯℃澘
+            </el-button>
+          </div>
+        </template>
+      </el-upload>
+    </FormDialog>
+    <!-- // todo 闄勪欢棰勮鐩稿叧 -->
+    <!-- 闄勪欢鍒楄〃寮圭獥 -->
+    <FileList v-if="fileDialogVisible" v-model:visible="fileDialogVisible" record-type="sales_ledger"
+              :record-id="recordId"/>
+    <!-- 鎵撳嵃棰勮寮圭獥 -->
+    <el-dialog v-model="printPreviewVisible"
+               title="鎵撳嵃棰勮"
+               width="90%"
+               :close-on-click-modal="false"
+               class="print-preview-dialog">
+      <div class="print-preview-container">
+        <div class="print-preview-header">
+          <el-button type="primary"
+                     @click="executePrint">鎵ц鎵撳嵃
+          </el-button>
+          <el-button @click="printPreviewVisible = false">鍏抽棴棰勮</el-button>
+        </div>
+        <div class="print-preview-content">
+          <div v-if="printData.length === 0"
+               style="text-align: center; padding: 50px; color: #999;">
+            鏆傛棤鎵撳嵃鏁版嵁
+          </div>
+          <div v-else
+               style="text-align: center; padding: 10px; color: #666; font-size: 14px; background: #e8f4fd; margin-bottom: 10px;">
+            鍏� {{ printData.length }} 鏉℃暟鎹緟鎵撳嵃
+          </div>
+          <div v-for="(item, index) in printData"
+               :key="index"
+               class="print-page">
+            <div class="delivery-note">
+              <div class="header">
+                <div class="document-title">闆跺敭鍙戣揣鍗�</div>
+              </div>
+              <div class="info-section">
+                <div class="info-row">
                   <div>
-                    <span>瀹℃壒浜�</span>
-                    鈫�
+                    <span class="label">鍙戣揣鏃ユ湡锛�</span>
+                    <span class="value">{{ formatDate(item.createTime) }}</span>
                   </div>
-                  <el-select
-                    v-model="node.userId"
-                    placeholder="閫夋嫨浜哄憳"
-                    filterable
-                    style="width: 140px; margin-bottom: 8px;"
-                  >
-                    <el-option
-                      v-for="user in userList"
-                      :key="user.userId"
-                      :label="user.nickName"
-                      :value="user.userId"
-                    />
-                  </el-select>
                   <div>
-                    <el-button
-                      type="danger"
-                      size="small"
-                      @click="removeApproverNode(index)"
-                      v-if="approverNodes.length > 1"
-                    >鍒犻櫎</el-button>
+                    <span class="label">鍙戣揣杞︾墝鍙凤細</span>
+                    <span class="value">{{ item.shippingCarNumber }}</span>
+                  </div>
+                </div>
+                <div class="info-row">
+                  <div>
+                    <span class="label">瀹㈡埛鍚嶇О锛�</span>
+                    <span class="value">{{ item.customerName }}</span>
+                  </div>
+                  <span class="label">鍗曞彿锛�</span>
+                  <span class="value">{{ item.salesContractNo }}</span>
+                </div>
+              </div>
+              <div class="table-section">
+                <table class="product-table">
+                  <thead>
+                  <tr>
+                    <th>浜у搧鍚嶇О</th>
+                    <th>瑙勬牸鍨嬪彿</th>
+                    <th>鍗曚綅</th>
+                    <th>鍗曚环</th>
+                    <th>闆跺敭鏁伴噺</th>
+                    <th>闆跺敭閲戦</th>
+                  </tr>
+                  </thead>
+                  <tbody>
+                  <tr v-for="product in item.products"
+                      :key="product.id">
+                    <td>{{ product.productCategory || '' }}</td>
+                    <td>{{ product.specificationModel || '' }}</td>
+                    <td>{{ product.unit || '' }}</td>
+                    <td>{{ product.taxInclusiveUnitPrice || '0' }}</td>
+                    <td>{{ product.quantity || '0' }}</td>
+                    <td>{{ product.taxInclusiveTotalPrice || '0' }}</td>
+                  </tr>
+                  <tr v-if="!item.products || item.products.length === 0">
+                    <td colspan="6"
+                        style="text-align: center; color: #999;">鏆傛棤浜у搧鏁版嵁
+                    </td>
+                  </tr>
+                  </tbody>
+                  <tfoot>
+                  <tr>
+                    <td class="label">鍚堣</td>
+                    <td class="total-value"></td>
+                    <td class="total-value"></td>
+                    <td class="total-value"></td>
+                    <td class="total-value">{{ getTotalQuantity(item.products) }}</td>
+                    <td class="total-value">{{ getTotalAmount(item.products) }}</td>
+                  </tr>
+                  </tfoot>
+                </table>
+              </div>
+              <div class="footer-section">
+                <div class="footer-row">
+                  <div class="footer-item">
+                    <span class="label">鏀惰揣鐢佃瘽锛�</span>
+                    <span class="value"></span>
+                  </div>
+                  <div class="footer-item">
+                    <span class="label">鏀惰揣浜猴細</span>
+                    <span class="value"></span>
+                  </div>
+                  <div class="footer-item address-item">
+                    <span class="label">鏀惰揣鍦板潃锛�</span>
+                    <span class="value address-value"></span>
+                  </div>
+                </div>
+                <div class="footer-row">
+                  <div class="footer-item">
+                    <span class="label">鎿嶄綔鍛橈細</span>
+                    <span class="value">{{ userStore.nickName || '鎾曞紑鍓�' }}</span>
+                  </div>
+                  <div class="footer-item">
+                    <span class="label">鎵撳嵃鏃ユ湡锛�</span>
+                    <span class="value">{{ formatDateTime(new Date()) }}</span>
                   </div>
                 </div>
               </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </el-dialog>
+    <!-- 鍙戣揣寮规 -->
+    <el-dialog v-model="deliveryFormVisible"
+               title="鍙戣揣淇℃伅"
+               width="40%"
+               @close="closeDeliveryDia">
+      <el-form :model="deliveryForm"
+               label-width="120px"
+               label-position="top"
+               :rules="deliveryRules"
+               ref="deliveryFormRef">
+        <el-row :gutter="30">
+          <el-col :span="24">
+            <el-form-item label="鍙戣揣绫诲瀷锛�"
+                          prop="type">
+              <el-select v-model="deliveryForm.type"
+                         placeholder="璇烽�夋嫨鍙戣揣绫诲瀷"
+                         style="width: 100%">
+                <el-option label="璐ц溅"
+                           value="璐ц溅"/>
+                <el-option label="蹇��"
+                           value="蹇��"/>
+              </el-select>
             </el-form-item>
           </el-col>
         </el-row>
-			</el-form>
-			<template #footer>
-				<div class="dialog-footer">
-					<el-button type="primary" @click="submitDelivery">纭鍙戣揣</el-button>
-					<el-button @click="closeDeliveryDia">鍙栨秷</el-button>
-				</div>
-			</template>
-		</el-dialog>
-	</div>
+        <el-row :gutter="30">
+          <el-col :span="24">
+            <el-form-item label="鎵瑰彿锛�"
+                          prop="batchNo">
+              <el-table :data="deliveryForm.batchNoList"
+                        border
+                        size="small"
+                        max-height="260"
+                        style="width: 100%;">
+                <el-table-column label="鎵瑰彿"
+                                 prop="batchNo"
+                                 min-width="180"/>
+                <el-table-column label="鏁伴噺"
+                                 min-width="120"
+                                 align="center">
+                  <template #default="scope">
+                    {{ getDeliveryBatchQuantity(scope.row) }}
+                  </template>
+                </el-table-column>
+                <el-table-column label="鍙戣揣鏁伴噺"
+                                 min-width="160"
+                                 align="center">
+                  <template #default="scope">
+                    <el-input-number v-model="scope.row.deliveryQuantity"
+                                     :min="0"
+                                     :max="getDeliveryBatchDeliveryMax(scope.row)"
+                                     :precision="2"
+                                     :step="0.01"
+                                     controls-position="right"
+                                     @change="handleDeliveryBatchQuantityChange(scope.row)"
+                                     style="width: 100%;"/>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="submitDelivery">纭鍙戣揣
+          </el-button>
+          <el-button @click="closeDeliveryDia">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
 </template>
 
 <script setup>
-import { getToken } from "@/utils/auth";
+import {getToken} from "@/utils/auth";
 import pagination from "@/components/PIMTable/Pagination.vue";
 import {onMounted, ref, getCurrentInstance} from "vue";
-import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js";
-import { ElMessageBox, ElMessage } from "element-plus";
-import { UploadFilled, Download } from "@element-plus/icons-vue";
+import {addShippingInfo} from "@/api/salesManagement/deliveryLedger.js";
+import {ElMessageBox, ElMessage} from "element-plus";
 import useUserStore from "@/store/modules/user";
-import { userListNoPage } from "@/api/system/user.js";
-import FileListDialog from '@/components/Dialog/FileListDialog.vue';
-import FormDialog from '@/components/Dialog/FormDialog.vue';
-import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
+import {userListNoPage} from "@/api/system/user.js";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+import {getQuotationList} from "@/api/salesManagement/salesQuotation.js";
 import {
-	ledgerListPage,
-	productList,
-	customerList,
-	addOrUpdateSalesLedger,
-	getSalesLedgerWithProducts,
-	delLedger,
-	addOrUpdateSalesLedgerProduct,
-	delProduct,
-	delLedgerFile, getProductInventory,
+  ledgerListPage,
+  productList,
+  customerList,
+  addOrUpdateSalesLedger,
+  getSalesLedgerWithProducts,
+  delLedger,
+  addOrUpdateSalesLedgerProduct,
+  delProduct,
+  delLedgerFile,
+  getProductInventory,
 } from "@/api/salesManagement/salesLedger.js";
-import { modelList, productTreeList } from "@/api/basicData/product.js";
+import {getStockInventoryByModelId} from "@/api/inventoryManagement/stockInventory.js";
+import {modelList, productTreeList} from "@/api/basicData/product.js";
 import useFormData from "@/hooks/useFormData.js";
 import dayjs from "dayjs";
+import FileUpload from "@/components/AttachmentUpload/file/index.vue";
 import { getCurrentDate } from "@/utils/index.js";
+import {listCustomer} from "@/api/basicData/customer.js";
 
+const FileList = defineAsyncComponent(() => import("@/components/Dialog/FileList.vue"));
+
+const router = useRouter();
+const route = useRoute();
 const userStore = useUserStore();
-const { proxy } = getCurrentInstance();
+const {proxy} = getCurrentInstance();
+const { tax_rate } = proxy.useDict("tax_rate");
 const tableData = ref([]);
 const productData = ref([]);
 const selectedRows = ref([]);
@@ -693,8 +1006,8 @@
 const modelOptions = ref([]);
 const tableLoading = ref(false);
 const page = reactive({
-	current: 1,
-	size: 100,
+  current: 1,
+  size: 100,
 });
 const total = ref(0);
 const fileList = ref([]);
@@ -703,81 +1016,78 @@
 const operationType = ref("");
 const dialogFormVisible = ref(false);
 const data = reactive({
-	searchForm: {
-		customerName: "", // 瀹㈡埛鍚嶇О
-		salesContractNo: "", // 閿�鍞悎鍚岀紪鍙�
-		entryDate: null, // 褰曞叆鏃ユ湡
-		entryDateStart: undefined,
-		entryDateEnd: undefined,
-	},
-	form: {
-		salesContractNo: "",
-		salesman: "",
-		customerId: "",
-		entryPerson: "",
-		entryDate: "",
+  searchForm: {
+    customerName: "", // 瀹㈡埛鍚嶇О
+    salesContractNo: "", // 閿�鍞悎鍚岀紪鍙�
+    entryDate: null, // 褰曞叆鏃ユ湡
+    entryDateStart: undefined,
+    entryDateEnd: undefined,
+  },
+  form: {
+    salesContractNo: "",
+    salesman: "",
+    customerId: "",
+    entryPerson: "",
+    entryDate: "",
     deliveryDate: "",
-		maintenanceTime: "",
-		productData: [],
-		executionDate: "",
-	},
-	rules: {
-		salesman: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-		customerId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-		entryPerson: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-		entryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-    deliveryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-		executionDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-	},
+    maintenanceTime: "",
+    productData: [],
+    executionDate: "",
+    hasProductionRecord: false,
+  },
+  rules: {
+    salesman: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+    customerId: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+    entryPerson: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+    entryDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+    deliveryDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+    executionDate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+  },
 });
-const { form, rules } = toRefs(data);
-const { form: searchForm } = useFormData(data.searchForm);
+const {form, rules} = toRefs(data);
+const {form: searchForm} = useFormData(data.searchForm);
 // 浜у搧琛ㄥ崟寮规鏁版嵁
 const productFormVisible = ref(false);
 const productOperationType = ref("");
 const currentId = ref("");
 const productFormData = reactive({
-	productForm: {
-		productCategory: "",
-		specificationModel: "",
-		unit: "",
-		quantity: "",
-		taxInclusiveUnitPrice: "",
-		taxRate: "",
-		taxInclusiveTotalPrice: "",
-		taxExclusiveTotalPrice: "",
-		invoiceType: "",
-	},
-	productRules: {
-		productCategory: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-		productModelId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-		specificationModel: [
-			{ required: true, message: "璇烽�夋嫨", trigger: "change" },
-		],
-		unit: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		quantity: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-		taxInclusiveUnitPrice: [
-			{ required: true, message: "璇疯緭鍏�", trigger: "blur" },
-		],
-		taxRate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-		taxInclusiveTotalPrice: [
-			{ required: true, message: "璇疯緭鍏�", trigger: "blur" },
-		],
-		taxExclusiveTotalPrice: [
-			{ required: true, message: "璇疯緭鍏�", trigger: "blur" },
-		],
-		invoiceType: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-	},
+  productForm: {
+    productCategory: "",
+    specificationModel: "",
+    unit: "",
+    quantity: "",
+    taxInclusiveUnitPrice: "",
+    taxRate: "",
+    taxInclusiveTotalPrice: "",
+    taxExclusiveTotalPrice: "",
+    invoiceType: "",
+    isProduction: false,
+  },
+  productRules: {
+    productCategory: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+    productModelId: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+    specificationModel: [
+      {required: true, message: "璇烽�夋嫨", trigger: "change"},
+    ],
+    unit: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
+    quantity: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
+    taxInclusiveUnitPrice: [
+      {required: true, message: "璇疯緭鍏�", trigger: "blur"},
+    ],
+    taxRate: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+    taxInclusiveTotalPrice: [
+      {required: true, message: "璇疯緭鍏�", trigger: "blur"},
+    ],
+    taxExclusiveTotalPrice: [
+      {required: true, message: "璇疯緭鍏�", trigger: "blur"},
+    ],
+    invoiceType: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+    isProduction: [{required: true, message: "璇烽�夋嫨", trigger: "change"}],
+  },
 });
-const { productForm, productRules } = toRefs(productFormData);
+const {productForm, productRules} = toRefs(productFormData);
 // 闃叉寰幆璁$畻鐨勬爣蹇�
 const isCalculating = ref(false);
-const upload = reactive({
-	// 涓婁紶鐨勫湴鍧�
-	url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
-	// 璁剧疆涓婁紶鐨勮姹傚ご閮�
-	headers: { Authorization: "Bearer " + getToken() },
-});
 // 鎵撳嵃鐩稿叧
 const printPreviewVisible = ref(false);
 const printData = ref([]);
@@ -787,1284 +1097,1450 @@
 const quotationLoading = ref(false);
 const quotationList = ref([]);
 const quotationSearchForm = reactive({
-	quotationNo: "",
-	customer: "",
+  quotationNo: "",
+  customer: "",
 });
 // 鎶ヤ环鍗曞脊妗嗗垎椤�
 const quotationPage = reactive({
-	current: 1,
-	size: 10,
-	total: 0,
+  current: 1,
+  size: 10,
+  total: 0,
 });
 const selectedQuotation = ref(null);
 
 // 鍙戣揣鐩稿叧
 const deliveryFormVisible = ref(false);
 const currentDeliveryRow = ref(null);
+const getDeliveryBatchQuantity = item => {
+  const quantity = item?.qualitity
+      ?? item?.quantity
+      ?? item?.unLockedQuantity
+      ?? item?.qualifiedUnLockedQuantity
+      ?? item?.qualifiedQuantity
+      ?? item?.stockQuantity;
+  return quantity ?? 0;
+};
+const getCurrentDeliveryRowQuantity = () => {
+  return Number(currentDeliveryRow.value?.noQuantity || 0);
+};
+const getDeliveryBatchDeliveryMax = row => {
+  const productQuantity = getCurrentDeliveryRowQuantity();
+  const batchQuantity = Number(getDeliveryBatchQuantity(row) || 0);
+  const otherBatchTotal = (deliveryForm.value.batchNoList || []).reduce(
+      (sum, item) => {
+        if (item?.id === row?.id) return sum;
+        return sum + Number(item?.deliveryQuantity || 0);
+      },
+      0
+  );
+  const remainingProductQuantity = Math.max(
+      0,
+      productQuantity - otherBatchTotal
+  );
+  return Math.max(0, Math.min(batchQuantity, remainingProductQuantity));
+};
+const handleDeliveryBatchQuantityChange = row => {
+  const max = getDeliveryBatchDeliveryMax(row);
+  const currentValue = Number(row?.deliveryQuantity || 0);
+  if (currentValue > max) {
+    row.deliveryQuantity = max;
+    proxy.$modal.msgWarning("鍙戣揣鏁伴噺涓嶈兘瓒呰繃杩欎釜浜у搧鐨勬暟閲�");
+  }
+};
+const getSelectedDeliveryBatchRows = () => {
+  return (deliveryForm.value.batchNoList || []).filter(
+      item => Number(item?.deliveryQuantity || 0) > 0
+  );
+};
+const getDeliveryBatchNoList = async productModelId => {
+  if (!productModelId) return [];
+  const res = await getStockInventoryByModelId(productModelId);
+  const rawList = Array.isArray(res?.data)
+      ? res.data
+      : res?.data?.records || res?.data?.rows || [];
+  const seenIds = new Set();
+  return rawList.filter(item => {
+    if (!item?.id || !item?.batchNo || seenIds.has(item.id)) {
+      return false;
+    }
+    seenIds.add(item.id);
+    return true;
+  }).map(item => ({
+    ...item,
+    deliveryQuantity: 0,
+  }));
+};
 const deliveryFormData = reactive({
   deliveryForm: {
     type: "璐ц溅", // 璐ц溅, 蹇��
   },
   deliveryRules: {
-    type: [
-      { required: true, message: "璇烽�夋嫨鍙戣揣绫诲瀷", trigger: "change" }
-    ]
+    type: [{required: true, message: "璇烽�夋嫨鍙戣揣绫诲瀷", trigger: "change"}],
   },
 });
-const { deliveryForm, deliveryRules } = toRefs(deliveryFormData);
-
-// 鍙戣揣瀹℃壒浜鸿妭鐐癸紙浠垮崗鍚屽鎵� infoFormDia.vue锛�
-const approverNodes = ref([{ id: 1, userId: null }]);
-let nextApproverId = 2;
-const addApproverNode = () => {
-  approverNodes.value.push({ id: nextApproverId++, userId: null });
-};
-const removeApproverNode = (index) => {
-  approverNodes.value.splice(index, 1);
-};
+const {deliveryForm, deliveryRules} = toRefs(deliveryFormData);
 
 // 瀵煎叆鐩稿叧
 const importUploadRef = ref(null);
 const importUpload = reactive({
-	title: "瀵煎叆閿�鍞彴璐�",
-	open: false,
-	url: import.meta.env.VITE_APP_BASE_API + "/sales/ledger/import",
-	headers: { Authorization: "Bearer " + getToken() },
-	isUploading: false,
-	beforeUpload: (file) => {
-		const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
-		const isLt10M = file.size / 1024 / 1024 < 10;
-		if (!isExcel) {
-			proxy.$modal.msgError("涓婁紶鏂囦欢鍙兘鏄� xlsx/xls 鏍煎紡!");
-			return false;
-		}
-		if (!isLt10M) {
-			proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 10MB!");
-			return false;
-		}
-		return true;
-	},
-	onChange: (file, fileList) => {
-		console.log('鏂囦欢鐘舵�佹敼鍙�', file, fileList);
-	},
-	onProgress: (event, file, fileList) => {
-		console.log('涓婁紶涓�...', event.percent);
-	},
-	onSuccess: (response, file, fileList) => {
-		console.log('涓婁紶鎴愬姛', response, file, fileList);
-		importUpload.isUploading = false;
-		if (response.code === 200) {
-			proxy.$modal.msgSuccess("瀵煎叆鎴愬姛");
-			importUpload.open = false;
-			if (importUploadRef.value) {
-				importUploadRef.value.clearFiles();
-			}
-			getList();
-		} else {
-			proxy.$modal.msgError(response.msg || "瀵煎叆澶辫触");
-		}
-	},
-	onError: (error, file, fileList) => {
-		console.error('涓婁紶澶辫触', error, file, fileList);
-		importUpload.isUploading = false;
-		proxy.$modal.msgError("瀵煎叆澶辫触锛岃閲嶈瘯");
-	},
+  title: "瀵煎叆閿�鍞彴璐�",
+  open: false,
+  url: import.meta.env.VITE_APP_BASE_API + "/sales/ledger/import",
+  headers: {Authorization: "Bearer " + getToken()},
+  isUploading: false,
+  beforeUpload: file => {
+    const isExcel = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
+    const isLt10M = file.size / 1024 / 1024 < 10;
+    if (!isExcel) {
+      proxy.$modal.msgError("涓婁紶鏂囦欢鍙兘鏄� xlsx/xls 鏍煎紡!");
+      return false;
+    }
+    if (!isLt10M) {
+      proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 10MB!");
+      return false;
+    }
+    return true;
+  },
+  onChange: (file, fileList) => {
+    console.log("鏂囦欢鐘舵�佹敼鍙�", file, fileList);
+  },
+  onProgress: (event, file, fileList) => {
+    console.log("涓婁紶涓�...", event.percent);
+  },
+  onSuccess: (response, file, fileList) => {
+    console.log("涓婁紶鎴愬姛", response, file, fileList);
+    importUpload.isUploading = false;
+    if (response.code === 200) {
+      proxy.$modal.msgSuccess("瀵煎叆鎴愬姛");
+      importUpload.open = false;
+      if (importUploadRef.value) {
+        importUploadRef.value.clearFiles();
+      }
+      getList();
+    } else {
+      proxy.$modal.msgError(response.msg || "瀵煎叆澶辫触");
+    }
+  },
+  onError: (error, file, fileList) => {
+    console.error("涓婁紶澶辫触", error, file, fileList);
+    importUpload.isUploading = false;
+    proxy.$modal.msgError("瀵煎叆澶辫触锛岃閲嶈瘯");
+  },
 });
 
-const changeDaterange = (value) => {
-	if (value) {
-		searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
-		searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
-	} else {
-		searchForm.entryDateStart = undefined;
-		searchForm.entryDateEnd = undefined;
-	}
-	handleQuery();
+const changeDaterange = value => {
+  if (value) {
+    searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
+    searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
+  } else {
+    searchForm.entryDateStart = undefined;
+    searchForm.entryDateEnd = undefined;
+  }
+  handleQuery();
 };
 
 // 鏌ヨ鍒楄〃
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
-	// 鍙湁鍦ㄧ偣鍑绘悳绱㈡寜閽椂鎵嶉噸缃〉鐮佸埌绗竴椤�
-	// 閬垮厤琛ㄥ崟瀛楁change浜嬩欢骞叉壈鍒嗛〉
-	if (arguments.length === 0) {
-		page.current = 1;
-	}
-	expandedRowKeys.value = [];
-	getList();
+  // 鍙湁鍦ㄧ偣鍑绘悳绱㈡寜閽椂鎵嶉噸缃〉鐮佸埌绗竴椤�
+  // 閬垮厤琛ㄥ崟瀛楁change浜嬩欢骞叉壈鍒嗛〉
+  if (arguments.length === 0) {
+    page.current = 1;
+  }
+  expandedRowKeys.value = [];
+  getList();
 };
-const paginationChange = (obj) => {
-	page.current = obj.page;
-	page.size = obj.limit;
-	getList();
+const paginationChange = obj => {
+  page.current = obj.page;
+  page.size = obj.limit;
+  getList();
 };
 const getList = () => {
-	tableLoading.value = true;
-	const { entryDate, ...rest } = searchForm;
-	// 灏嗚寖鍥存棩鏈熷瓧娈典紶閫掔粰鍚庣
-	const params = { ...rest, ...page };
-	// 绉婚櫎褰曞叆鏃ユ湡鐨勯粯璁ゅ�艰缃紝鍙繚鐣欒寖鍥存棩鏈熷瓧娈�
-	delete params.entryDate;
-	return ledgerListPage(params)
-		.then((res) => {
-			tableLoading.value = false;
-			tableData.value = res.records;
-			tableData.value.map((item) => {
-				item.children = [];
-			});
-			total.value = res.total;
-			return res;
-		})
-		.catch(() => {
-			tableLoading.value = false;
-		});
+  tableLoading.value = true;
+  const {entryDate, ...rest} = searchForm;
+  // 灏嗚寖鍥存棩鏈熷瓧娈典紶閫掔粰鍚庣
+  const params = {...rest, ...page};
+  // 绉婚櫎褰曞叆鏃ユ湡鐨勯粯璁ゅ�艰缃紝鍙繚鐣欒寖鍥存棩鏈熷瓧娈�
+  delete params.entryDate;
+  return ledgerListPage(params)
+      .then(res => {
+        tableLoading.value = false;
+        tableData.value = res.records;
+        tableData.value.map(item => {
+          item.children = [];
+        });
+        total.value = res.total;
+        return res;
+      })
+      .catch(() => {
+        tableLoading.value = false;
+      });
 };
 // 鑾峰彇浜у搧澶х被tree鏁版嵁
 const getProductOptions = () => {
-	// 杩斿洖 Promise锛屼究浜庡湪缂栬緫浜у搧鏃剁瓑寰呭姞杞藉畬鎴�
-	return productTreeList().then((res) => {
-		productOptions.value = convertIdToValue(res);
-		return productOptions.value;
-	});
+  // 杩斿洖 Promise锛屼究浜庡湪缂栬緫浜у搧鏃剁瓑寰呭姞杞藉畬鎴�
+  return productTreeList().then(res => {
+    productOptions.value = convertIdToValue(res);
+    return productOptions.value;
+  });
 };
 const formattedNumber = (row, column, cellValue) => {
-	return parseFloat(cellValue).toFixed(2);
+  if (cellValue === undefined || cellValue === null || cellValue === "") {
+    return "0.00";
+  }
+  return parseFloat(cellValue).toFixed(2);
+};
+const findLedgerRecordByRow = row => {
+  if (!row) return null;
+  if (
+      row.maintainer !== undefined ||
+      row.maintainerName !== undefined ||
+      row.entryPerson !== undefined ||
+      row.entryPersonName !== undefined
+  ) {
+    return row;
+  }
+  if (row.salesLedgerId !== undefined && row.salesLedgerId !== null) {
+    return (
+        tableData.value.find(
+            item => String(item.id) === String(row.salesLedgerId)
+        ) || null
+    );
+  }
+  return null;
+};
+const isCurrentUserMaintainer = row => {
+  const ledgerRecord = findLedgerRecordByRow(row);
+  if (!ledgerRecord) return true;
+  const currentUserId = String(userStore.id ?? "");
+  const currentNickName = String(userStore.nickName ?? "").trim();
+  const maintainerId = ledgerRecord.maintainerId ?? ledgerRecord.entryPerson;
+  const maintainerName =
+      ledgerRecord.maintainerName ??
+      ledgerRecord.maintainer ??
+      ledgerRecord.entryPersonName;
+  if (
+      maintainerId !== undefined &&
+      maintainerId !== null &&
+      String(maintainerId) !== ""
+  ) {
+    return String(maintainerId) === currentUserId;
+  }
+  if (
+      maintainerName !== undefined &&
+      maintainerName !== null &&
+      String(maintainerName).trim() !== ""
+  ) {
+    return String(maintainerName).trim() === currentNickName;
+  }
+  return true;
+};
+const canEditLedger = row => isCurrentUserMaintainer(row);
+const canDeleteLedger = row => isCurrentUserMaintainer(row);
+const sensitiveAmountFormatter = (row, column, cellValue) => {
+  if (!isCurrentUserMaintainer(row)) {
+    return "*****";
+  }
+  return formattedNumber(row, column, cellValue);
 };
 // 鑾峰彇tree瀛愭暟鎹�
-const getModels = (value) => {
-	productForm.value.productCategory = findNodeById(productOptions.value, value);
-	modelList({ id: value }).then((res) => {
-		modelOptions.value = res;
-	});
+const getModels = value => {
+  productForm.value.productCategory = findNodeById(productOptions.value, value);
+  modelList({id: value}).then(res => {
+    modelOptions.value = res;
+  });
 };
-const getProductModel = (value) => {
-	const index = modelOptions.value.findIndex((item) => item.id === value);
-	if (index !== -1) {
-		productForm.value.specificationModel = modelOptions.value[index].model;
-		productForm.value.unit = modelOptions.value[index].unit;
-	} else {
-		productForm.value.specificationModel = null;
-		productForm.value.unit = null;
-	}
+const getProductModel = value => {
+  const index = modelOptions.value.findIndex(item => item.id === value);
+  if (index !== -1) {
+    productForm.value.specificationModel = modelOptions.value[index].model;
+    productForm.value.unit = modelOptions.value[index].unit;
+  } else {
+    productForm.value.specificationModel = null;
+    productForm.value.unit = null;
+  }
 };
 const findNodeById = (nodes, productId) => {
-	for (let i = 0; i < nodes.length; i++) {
-		if (nodes[i].value === productId) {
-			return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥炶鑺傜偣
-		}
-		if (nodes[i].children && nodes[i].children.length > 0) {
-			const foundNode = findNodeById(nodes[i].children, productId);
-			if (foundNode) {
-				return foundNode; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝杩斿洖璇ヨ妭鐐�
-			}
-		}
-	}
-	return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
+  for (let i = 0; i < nodes.length; i++) {
+    if (nodes[i].value === productId) {
+      return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥炶鑺傜偣
+    }
+    if (nodes[i].children && nodes[i].children.length > 0) {
+      const foundNode = findNodeById(nodes[i].children, productId);
+      if (foundNode) {
+        return foundNode; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝杩斿洖璇ヨ妭鐐�
+      }
+    }
+  }
+  return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
 };
+
 function convertIdToValue(data) {
-	return data.map((item) => {
-		const { id, children, ...rest } = item;
-		const newItem = {
-			...rest,
-			value: id, // 灏� id 鏀逛负 value
-		};
-		if (children && children.length > 0) {
-			newItem.children = convertIdToValue(children);
-		}
-		
-		return newItem;
-	});
+  return data.map(item => {
+    const {id, children, ...rest} = item;
+    const newItem = {
+      ...rest,
+      value: id, // 灏� id 鏀逛负 value
+    };
+    if (children && children.length > 0) {
+      newItem.children = convertIdToValue(children);
+    }
+
+    return newItem;
+  });
 }
+
 // 鏍规嵁鍚嶇О鍙嶆煡浜у搧澶х被 id锛屼究浜庝粎瀛樺悕绉版椂鐨勫弽鏄�
 function findNodeIdByLabel(nodes, label) {
-	if (!label) return null;
-	for (let i = 0; i < nodes.length; i++) {
-		const node = nodes[i];
-		if (node.label === label) return node.value;
-		if (node.children && node.children.length > 0) {
-			const found = findNodeIdByLabel(node.children, label);
-			if (found !== null && found !== undefined) return found;
-		}
-	}
-	return null;
+  if (!label) return null;
+  for (let i = 0; i < nodes.length; i++) {
+    const node = nodes[i];
+    if (node.label === label) return node.value;
+    if (node.children && node.children.length > 0) {
+      const found = findNodeIdByLabel(node.children, label);
+      if (found !== null && found !== undefined) return found;
+    }
+  }
+  return null;
 }
+
 // 琛ㄦ牸閫夋嫨鏁版嵁
-const handleSelectionChange = (selection) => {
-	// 杩囨护鎺夊瓙鏁版嵁
-	selectedRows.value = selection.filter((item) => item.children !== undefined);
-	console.log("selection", selectedRows.value);
+const handleSelectionChange = selection => {
+  // 杩囨护鎺夊瓙鏁版嵁
+  selectedRows.value = selection.filter(item => item.children !== undefined);
+  console.log("selection", selectedRows.value);
 };
-const productSelected = (selectedRows) => {
-	productSelectedRows.value = selectedRows;
+const productSelected = selectedRows => {
+  productSelectedRows.value = selectedRows;
 };
 const expandedRowKeys = ref([]);
 // 灞曞紑琛�
 const expandChange = (row, expandedRows) => {
-	if (expandedRows.length > 0) {
-		expandedRowKeys.value = [];
-		try {
-			productList({ salesLedgerId: row.id, type: 1 }).then((res) => {
-				const index = tableData.value.findIndex((item) => item.id === row.id);
-				if (index > -1) {
-					tableData.value[index].children = res.data;
-				}
-				expandedRowKeys.value.push(row.id);
-			});
-		} catch (error) {
-			console.log(error);
-		}
-	} else {
-		expandedRowKeys.value = [];
-	}
+  if (expandedRows.length > 0) {
+    expandedRowKeys.value = [];
+    try {
+      productList({salesLedgerId: row.id, type: 1}).then(res => {
+        const index = tableData.value.findIndex(item => item.id === row.id);
+        if (index > -1) {
+          tableData.value[index].children = res.data;
+        }
+        expandedRowKeys.value.push(row.id);
+      });
+    } catch (error) {
+      console.log(error);
+    }
+  } else {
+    expandedRowKeys.value = [];
+  }
 };
 
 // 娣诲姞琛ㄨ绫诲悕鏂规硶
-const tableRowClassName = ({ row }) => {
-  if (!row.deliveryDate) return '';
-  if (row.isFh) return '';
+const tableRowClassName = ({row}) => {
+  if (!row.deliveryDate) return "";
+  if (row.isFh) return "";
 
   const diff = row.deliveryDaysDiff;
   if (diff === 15) {
-    return 'yellow';
+    return "yellow";
   } else if (diff === 10) {
-    return 'pink';
+    return "pink";
   } else if (diff === 2) {
-    return 'purple';
+    return "purple";
   } else if (diff < 2) {
-    return 'red';
+    return "red";
   }
 };
 // 涓昏〃鍚堣鏂规硶
-const summarizeMainTable = (param) => {
-	return proxy.summarizeTable(param, [
-		"contractAmount",
-		"taxInclusiveTotalPrice",
-		"taxExclusiveTotalPrice",
-	]);
+const summarizeMainTable = param => {
+  return proxy.summarizeTable(param, [
+    "contractAmount",
+    "taxInclusiveTotalPrice",
+    "taxExclusiveTotalPrice",
+  ]);
 };
 // 瀛愯〃鍚堣鏂规硶
-const summarizeChildrenTable = (param) => {
-	return proxy.summarizeTable(param, [
-		"taxInclusiveUnitPrice",
-		"taxInclusiveTotalPrice",
-		"taxExclusiveTotalPrice",
-	]);
+const summarizeChildrenTable = (param, parentRow) => {
+  if (!isCurrentUserMaintainer(parentRow)) {
+    const {columns} = param;
+    return columns.map((column, index) => {
+      if (index === 0) {
+        return "鍚堣";
+      }
+      if (
+          [
+            "taxInclusiveUnitPrice",
+            "taxInclusiveTotalPrice",
+            "taxExclusiveTotalPrice",
+          ].includes(column.property)
+      ) {
+        return "*****";
+      }
+      return "";
+    });
+  }
+  return proxy.summarizeTable(param, [
+    "taxInclusiveUnitPrice",
+    "taxInclusiveTotalPrice",
+    "taxExclusiveTotalPrice",
+  ]);
 };
 // 鎵撳紑寮规
 const openForm = async (type, row) => {
-	operationType.value = type;
-	form.value = {};
-	productData.value = [];
-	selectedQuotation.value = null;
-	let userLists = await userListNoPage();
-	userList.value = userLists.data;
-	customerList().then((res) => {
-		customerOption.value = res;
-	});
-	form.value.entryPerson = userStore.id;
-	if (type === "add") {
-		// 鏂板鏃惰缃綍鍏ユ棩鏈熶负褰撳ぉ
-		form.value.entryDate = getCurrentDate();
-		// 绛捐鏃ユ湡榛樿涓哄綋澶�
-		form.value.executionDate = getCurrentDate();
-	} else {
-		currentId.value = row.id;
-		getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
-			form.value = { ...res };
-			form.value.entryPerson = Number(res.entryPerson);
-			productData.value = form.value.productData;
-			fileList.value = form.value.salesLedgerFiles;
-		});
-	}
-	// let userAll = await userStore.getInfo()
-	// userList.value.forEach(element => {
-	//   if(userAll.user.nickName === element.nickName && userAll.user.userName === element.userName) {
-	//     form.value.entryPerson = userAll.user.userId // 璁剧疆榛樿涓氬姟鍛樹负褰撳墠鐢ㄦ埛
-	//   }
-	// });
-	form.value.entryDate = getCurrentDate(); // 璁剧疆榛樿褰曞叆鏃ユ湡涓哄綋鍓嶆棩鏈�
-	dialogFormVisible.value = true;
+  if (type === "edit" && row && !canEditLedger(row)) {
+    proxy.$modal.msgWarning("褰撳墠绯荤粺鐧诲綍浜轰笉鏄淮鎶や汉锛屼笉鑳界紪杈戞暟鎹�");
+    return;
+  }
+  operationType.value = type;
+  form.value = {};
+  productData.value = [];
+  selectedQuotation.value = null;
+  let userLists = await userListNoPage();
+  userList.value = userLists.data;
+  listCustomer({current: -1, size: -1}).then(res => {
+    customerOption.value = res.data.records;
+  });
+  form.value.entryPerson = userStore.id;
+  if (type === "add") {
+    // 鏂板鏃惰缃綍鍏ユ棩鏈熶负褰撳ぉ
+    form.value.entryDate = getCurrentDate();
+    // 绛捐鏃ユ湡榛樿涓哄綋澶�
+    form.value.executionDate = getCurrentDate();
+  } else {
+    currentId.value = row.id;
+    getSalesLedgerWithProducts({id: row.id, type: 1}).then(res => {
+      form.value = {...res};
+      form.value.entryPerson = Number(res.entryPerson);
+      productData.value = form.value.productData;
+      fileList.value = form.value.storageBlobVOs;
+    });
+  }
+  // let userAll = await userStore.getInfo()
+  // userList.value.forEach(element => {
+  //   if(userAll.user.nickName === element.nickName && userAll.user.userName === element.userName) {
+  //     form.value.entryPerson = userAll.user.userId // 璁剧疆榛樿涓氬姟鍛樹负褰撳墠鐢ㄦ埛
+  //   }
+  // });
+  form.value.entryDate = getCurrentDate(); // 璁剧疆榛樿褰曞叆鏃ユ湡涓哄綋鍓嶆棩鏈�
+  dialogFormVisible.value = true;
 };
 
 // 鎵撳紑鎶ヤ环鍗曢�夋嫨寮圭獥锛堜粎瀹℃壒閫氳繃锛�
 const openQuotationDialog = async () => {
-	if (operationType.value === "view") return;
-	quotationDialogVisible.value = true;
-	// 鎵撳紑寮圭獥鏃堕噸缃垎椤靛埌绗竴椤�
-	quotationPage.current = 1;
-	// 鍏堢‘淇濆鎴峰垪琛ㄥ凡鍔犺浇锛屼究浜庡悗缁洖濉� customerId
-	if (!customerOption.value || customerOption.value.length === 0) {
-		try {
-			const res = await customerList();
-			customerOption.value = res;
-		} catch (e) {
-			// ignore锛屽厑璁哥敤鎴峰悗缁墜鍔ㄩ�夋嫨瀹㈡埛
-		}
-	}
-	await fetchQuotationList();
+  if (operationType.value === "view") return;
+  quotationDialogVisible.value = true;
+  // 鎵撳紑寮圭獥鏃堕噸缃垎椤靛埌绗竴椤�
+  quotationPage.current = 1;
+  // 鍏堢‘淇濆鎴峰垪琛ㄥ凡鍔犺浇锛屼究浜庡悗缁洖濉� customerId
+  if (!customerOption.value || customerOption.value.length === 0) {
+    try {
+      listCustomer({current: -1, size: -1}).then(res => {
+        customerOption.value = res.data.records;
+      });
+    } catch (e) {
+      // ignore锛屽厑璁哥敤鎴峰悗缁墜鍔ㄩ�夋嫨瀹㈡埛
+    }
+  }
+  await fetchQuotationList();
 };
 
 const fetchQuotationList = async () => {
-	quotationLoading.value = true;
-	try {
-		const params = {
-			// 鍚庣鍒嗛〉瀛楁锛歝urrent / size
-			current: quotationPage.current,
-			size: quotationPage.size,
-			...quotationSearchForm,
-			status: "閫氳繃",
-		};
-		const res = await getQuotationList(params);
-		quotationList.value = res?.data?.records || [];
-		quotationPage.total = res?.data?.total || 0;
-	} finally {
-		quotationLoading.value = false;
-	}
+  quotationLoading.value = true;
+  try {
+    const params = {
+      // 鍚庣鍒嗛〉瀛楁锛歝urrent / size
+      current: quotationPage.current,
+      size: quotationPage.size,
+      ...quotationSearchForm,
+      status: "閫氳繃",
+    };
+    const res = await getQuotationList(params);
+    quotationList.value = res?.data?.records || [];
+    quotationPage.total = res?.data?.total || 0;
+  } finally {
+    quotationLoading.value = false;
+  }
 };
 
 const resetQuotationSearch = async () => {
-	quotationSearchForm.quotationNo = "";
-	quotationSearchForm.customer = "";
-	quotationPage.current = 1;
-	await fetchQuotationList();
+  quotationSearchForm.quotationNo = "";
+  quotationSearchForm.customer = "";
+  quotationPage.current = 1;
+  await fetchQuotationList();
 };
 
 // 鎶ヤ环鍗曞脊妗嗗垎椤靛垏鎹�
-const quotationPaginationChange = (obj) => {
-	quotationPage.current = obj.page;
-	quotationPage.size = obj.limit;
-	fetchQuotationList();
+const quotationPaginationChange = obj => {
+  quotationPage.current = obj.page;
+  quotationPage.size = obj.limit;
+  fetchQuotationList();
 };
 
 // 閫変腑鎶ヤ环鍗曞悗鍥炲~鍒板彴璐﹁〃鍗�
-const applyQuotation = (row) => {
-	if (!row) return;
-	selectedQuotation.value = row;
-	
-	// 涓氬姟鍛�
-	form.value.salesman = (row.salesperson || "").trim();
-	
-	// 瀹㈡埛鍚嶇О -> customerId
-	const qCustomerName = String(row.customer || "").trim();
-	const customer = (customerOption.value || []).find((c) => {
-		const name = String(c.customerName || "").trim();
-		return name === qCustomerName || name.includes(qCustomerName) || qCustomerName.includes(name);
-	});
-	if (customer?.id) {
-		form.value.customerId = customer.id;
-	} else {
-		// 濡傛灉鎵句笉鍒帮紝淇濈暀鍘熷�硷紙鍏佽鐢ㄦ埛鎵嬪姩閫夋嫨/涓嶆墦鏂凡鏈夎緭鍏ワ級
-		form.value.customerId = form.value.customerId || "";
-	}
-	
-	// 浜у搧淇℃伅鏄犲皠锛氭姤浠� products -> 鍙拌处 productData
-	const products = Array.isArray(row.products) ? row.products : [];
-	productData.value = products.map((p) => {
-		const quantity = Number(p.quantity ?? 0) || 0;
-		const unitPrice = Number(p.unitPrice ?? 0) || 0;
-		const taxRate = "13"; // 榛樿 13%锛屼究浜庣洿鎺ユ彁浜わ紙濡傞渶鍙湪浜у搧涓嚜琛屼慨鏀癸級
-		const taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
-		const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(taxInclusiveTotalPrice, taxRate);
-		return {
-			// 鍙拌处瀛楁
-			productCategory: p.product || p.productName || "",
-			specificationModel: p.specification || "",
-			unit: p.unit || "",
-			quantity: quantity,
-			taxRate: taxRate,
-			taxInclusiveUnitPrice: unitPrice.toFixed(2),
-			taxInclusiveTotalPrice: taxInclusiveTotalPrice,
-			taxExclusiveTotalPrice: taxExclusiveTotalPrice,
-			invoiceType: "澧炴櫘绁�",
-		};
-	});
-	
-	quotationDialogVisible.value = false;
+const applyQuotation = row => {
+  if (!row) return;
+  selectedQuotation.value = row;
+
+  // 涓氬姟鍛�
+  form.value.salesman = (row.salesperson || "").trim();
+
+  // 瀹㈡埛鍚嶇О -> customerId
+  const qCustomerName = String(row.customer || "").trim();
+  const customer = (customerOption.value || []).find(c => {
+    const name = String(c.customerName || "").trim();
+    return (
+        name === qCustomerName ||
+        name.includes(qCustomerName) ||
+        qCustomerName.includes(name)
+    );
+  });
+  if (customer?.id) {
+    form.value.customerId = customer.id;
+  } else {
+    // 濡傛灉鎵句笉鍒帮紝淇濈暀鍘熷�硷紙鍏佽鐢ㄦ埛鎵嬪姩閫夋嫨/涓嶆墦鏂凡鏈夎緭鍏ワ級
+    form.value.customerId = form.value.customerId || "";
+  }
+
+  // 浜у搧淇℃伅鏄犲皠锛氭姤浠� products -> 鍙拌处 productData
+  const products = Array.isArray(row.products) ? row.products : [];
+  productData.value = products.map(p => {
+    const quantity = Number(p.quantity ?? 0) || 0;
+    const unitPrice = Number(p.unitPrice ?? 0) || 0;
+    const taxRate = "13"; // 榛樿 13%锛屼究浜庣洿鎺ユ彁浜わ紙濡傞渶鍙湪浜у搧涓嚜琛屼慨鏀癸級
+    const taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
+    const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(
+        taxInclusiveTotalPrice,
+        taxRate
+    );
+    return {
+      // 鍙拌处瀛楁
+      productCategory: p.product || p.productName || "",
+      specificationModel: p.specification || "",
+      unit: p.unit || "",
+      quantity: quantity,
+      taxRate: taxRate,
+      taxInclusiveUnitPrice: unitPrice.toFixed(2),
+      taxInclusiveTotalPrice: taxInclusiveTotalPrice,
+      taxExclusiveTotalPrice: taxExclusiveTotalPrice,
+      invoiceType: "澧炴櫘绁�",
+      isProduction: true,
+    };
+  });
+
+  quotationDialogVisible.value = false;
 };
+
 function changs(val) {
-	console.log(val);
+  console.log(val);
 }
-// 涓婁紶鍓嶆牎妫�
-function handleBeforeUpload(file) {
-	// 鏍℃鏂囦欢澶у皬
-	// if (file.size > 1024 * 1024 * 10) {
-	//   proxy.$modal.msgError("涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃10MB!");
-	//   return false;
-	// }
-	proxy.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
-	return true;
-}
-// 涓婁紶澶辫触
-function handleUploadError(err) {
-	proxy.$modal.msgError("涓婁紶鏂囦欢澶辫触");
-	proxy.$modal.closeLoading();
-}
-// 涓婁紶鎴愬姛鍥炶皟
-function handleUploadSuccess(res, file, uploadFiles) {
-	proxy.$modal.closeLoading();
-	if (res.code === 200) {
-		file.tempId = res.data.tempId;
-		proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
-	} else {
-		proxy.$modal.msgError(res.msg);
-		proxy.$refs.fileUpload.handleRemove(file);
-	}
-}
-// 绉婚櫎鏂囦欢
-function handleRemove(file) {
-	if (operationType.value === "edit") {
-		let ids = [];
-		ids.push(file.id);
-		delLedgerFile(ids).then((res) => {
-			proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-		});
-	}
-}
+
 // 鎻愪氦琛ㄥ崟
 const submitForm = () => {
-	proxy.$refs["formRef"].validate((valid) => {
-		if (valid) {
-			console.log('productData.value--', productData.value)
-			if (productData.value !== null && productData.value.length > 0) {
-				form.value.productData = proxy.HaveJson(productData.value);
-			} else {
-				proxy.$modal.msgWarning("璇锋坊鍔犱骇鍝佷俊鎭�");
-				return;
-			}
-			let tempFileIds = [];
-			if (fileList.value !== null && fileList.value.length > 0) {
-				tempFileIds = fileList.value.map((item) => item.tempId);
-			}
-			form.value.tempFileIds = tempFileIds;
-			form.value.type = 1;
-			addOrUpdateSalesLedger(form.value).then((res) => {
-				proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-				closeDia();
-				getList();
-			});
-		}
-	});
+  proxy.$refs["formRef"].validate(valid => {
+    if (valid) {
+      console.log("productData.value--", productData.value);
+      if (productData.value !== null && productData.value.length > 0) {
+        form.value.productData = proxy.HaveJson(productData.value);
+      } else {
+        proxy.$modal.msgWarning("璇锋坊鍔犱骇鍝佷俊鎭�");
+        return;
+      }
+      form.value.storageBlobDTOs = fileList;
+      form.value.type = 1;
+      addOrUpdateSalesLedger(form.value).then(res => {
+        proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+        closeDia();
+        expandedRowKeys.value = [];
+        getList();
+      });
+    }
+  });
 };
 // 鍏抽棴寮规
 const closeDia = () => {
-	proxy.resetForm("formRef");
-	dialogFormVisible.value = false;
+  proxy.resetForm("formRef");
+  dialogFormVisible.value = false;
 };
 
 const productIndex = ref(0);
 // 鎵撳紑浜у搧寮规
 const openProductForm = async (type, row, index) => {
-	// 缂栬緫鏃舵鏌ヤ骇鍝佹槸鍚﹀凡鍙戣揣鎴栧鏍搁�氳繃
-	if (type === "edit" && isProductShipped(row)) {
-		proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳界紪杈�");
-		return;
-	}
-	
-	productOperationType.value = type;
-	productForm.value = {};
-	proxy.resetForm("productFormRef");
-	if (type === "edit") {
-		productForm.value = { ...row };
-		productIndex.value = index;
-		// 缂栬緫鏃舵牴鎹骇鍝佸ぇ绫诲悕绉板弽鏌� tree 鑺傜偣 id锛屽苟鍔犺浇瑙勬牸鍨嬪彿鍒楄〃
-		try {
-			const options = productOptions.value && productOptions.value.length > 0
-				? productOptions.value
-				: await getProductOptions();
-			const categoryId = findNodeIdByLabel(options, productForm.value.productCategory);
-			if (categoryId) {
-				const models = await modelList({ id: categoryId });
-				modelOptions.value = models || [];
-				// 鏍规嵁褰撳墠瑙勬牸鍨嬪彿鍚嶇О鍙嶆煡骞惰缃� productModelId锛屼究浜庝笅鎷夋鏄剧ず宸查�夊��
-				const currentModel = (modelOptions.value || []).find(
-					(m) => m.model === productForm.value.specificationModel
-				);
-				if (currentModel) {
-					productForm.value.productModelId = currentModel.id;
-				}
-			}
-		} catch (e) {
-			// 鍔犺浇澶辫触鏃朵繚鎸佸彲缂栬緫锛屼笉涓柇寮圭獥
-			console.error("鍔犺浇浜у搧瑙勬牸鍨嬪彿澶辫触", e);
-		}
-	} else {
-		getProductOptions()
-	}
-	productFormVisible.value = true;
+  // 缂栬緫鏃舵鏌ヤ骇鍝佹槸鍚﹀凡鍙戣揣鎴栧鏍搁�氳繃
+  if (type === "edit" && isProductShipped(row)) {
+    proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳界紪杈�");
+    return;
+  }
+
+  productOperationType.value = type;
+  productForm.value = {};
+  if (type === "add") {
+    productForm.value.isProduction = true;
+  }
+  proxy.resetForm("productFormRef");
+  if (type === "edit") {
+    productForm.value = {...row};
+    productIndex.value = index;
+    // 缂栬緫鏃舵牴鎹骇鍝佸ぇ绫诲悕绉板弽鏌� tree 鑺傜偣 id锛屽苟鍔犺浇瑙勬牸鍨嬪彿鍒楄〃
+    try {
+      const options =
+          productOptions.value && productOptions.value.length > 0
+              ? productOptions.value
+              : await getProductOptions();
+      const categoryId = findNodeIdByLabel(
+          options,
+          productForm.value.productCategory
+      );
+      if (categoryId) {
+        const models = await modelList({id: categoryId});
+        modelOptions.value = models || [];
+        // 鏍规嵁褰撳墠瑙勬牸鍨嬪彿鍚嶇О鍙嶆煡骞惰缃� productModelId锛屼究浜庝笅鎷夋鏄剧ず宸查�夊��
+        const currentModel = (modelOptions.value || []).find(
+            m => m.model === productForm.value.specificationModel
+        );
+        if (currentModel) {
+          productForm.value.productModelId = currentModel.id;
+        }
+      }
+    } catch (e) {
+      // 鍔犺浇澶辫触鏃朵繚鎸佸彲缂栬緫锛屼笉涓柇寮圭獥
+      console.error("鍔犺浇浜у搧瑙勬牸鍨嬪彿澶辫触", e);
+    }
+  } else {
+    getProductOptions();
+  }
+  productFormVisible.value = true;
 };
 // 鎻愪氦浜у搧琛ㄥ崟
 const submitProduct = () => {
-	proxy.$refs["productFormRef"].validate((valid) => {
-		if (valid) {
-			if (operationType.value === "edit") {
-				submitProductEdit();
-			} else {
-				if(productOperationType.value === "add"){
-					productData.value.push({ ...productForm.value });
-				}else{
-					productData.value[productIndex.value] = { ...productForm.value }
-				}
-				closeProductDia();
-			}
-		}
-	});
+  proxy.$refs["productFormRef"].validate(valid => {
+    if (valid) {
+      if (operationType.value === "edit") {
+        submitProductEdit();
+      } else {
+        if (productOperationType.value === "add") {
+          productData.value.push({...productForm.value});
+        } else {
+          productData.value[productIndex.value] = {...productForm.value};
+        }
+        closeProductDia();
+      }
+    }
+  });
 };
 const submitProductEdit = () => {
-	productForm.value.salesLedgerId = currentId.value;
-	productForm.value.type = 1
-	addOrUpdateSalesLedgerProduct(productForm.value).then((res) => {
-		proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-		closeProductDia();
-		getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then((res) => {
-			productData.value = res.productData;
-		});
-	});
+  productForm.value.salesLedgerId = currentId.value;
+  productForm.value.type = 1;
+  addOrUpdateSalesLedgerProduct(productForm.value).then(res => {
+    proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+    closeProductDia();
+    getSalesLedgerWithProducts({id: currentId.value, type: 1}).then(res => {
+      productData.value = res.productData;
+    });
+  });
 };
 // 鍒犻櫎浜у搧
 const deleteProduct = () => {
-	if (productSelectedRows.value.length === 0) {
-		proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
-		return;
-	}
-	
-	// 妫�鏌ユ槸鍚︽湁宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝�
-	const shippedProducts = productSelectedRows.value.filter(row => isProductShipped(row));
-	if (shippedProducts.length > 0) {
-		proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳藉垹闄�");
-		return;
-	}
-	
-	if (operationType.value === "add") {
-		productSelectedRows.value.forEach((selectedRow) => {
-			const index = productData.value.findIndex(
-				(product) => product.id === selectedRow.id
-			);
-			if (index !== -1) {
-				productData.value.splice(index, 1);
-			}
-		});
-	} else {
-		let ids = [];
-		if (productSelectedRows.value.length > 0) {
-			ids = productSelectedRows.value.map((item) => item.id);
-		}
-		ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
-			confirmButtonText: "纭",
-			cancelButtonText: "鍙栨秷",
-			type: "warning",
-		})
-			.then(() => {
-				delProduct(ids).then((res) => {
-					proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-					closeProductDia();
-					getSalesLedgerWithProducts({ id: currentId.value, type: 1 }).then(
-						(res) => {
-							productData.value = res.productData;
-						}
-					);
-				});
-			})
-			.catch(() => {
-				proxy.$modal.msg("宸插彇娑�");
-			});
-	}
+  if (productSelectedRows.value.length === 0) {
+    proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+    return;
+  }
+
+  // 妫�鏌ユ槸鍚︽湁宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝�
+  const shippedProducts = productSelectedRows.value.filter(row =>
+      isProductShipped(row)
+  );
+  if (shippedProducts.length > 0) {
+    proxy.$modal.msgWarning("宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝佷笉鑳藉垹闄�");
+    return;
+  }
+
+  if (operationType.value === "add") {
+    productSelectedRows.value.forEach(selectedRow => {
+      const index = productData.value.findIndex(
+          product => product.id === selectedRow.id
+      );
+      if (index !== -1) {
+        productData.value.splice(index, 1);
+      }
+    });
+  } else {
+    let ids = [];
+    if (productSelectedRows.value.length > 0) {
+      ids = productSelectedRows.value.map(item => item.id);
+    }
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+        .then(() => {
+          delProduct(ids).then(res => {
+            proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+            closeProductDia();
+            getSalesLedgerWithProducts({id: currentId.value, type: 1}).then(
+                res => {
+                  productData.value = res.productData;
+                }
+            );
+          });
+        })
+        .catch(() => {
+          proxy.$modal.msg("宸插彇娑�");
+        });
+  }
 };
 // 鍏抽棴浜у搧寮规
 const closeProductDia = () => {
-	proxy.resetForm("productFormRef");
-	productFormVisible.value = false;
+  proxy.resetForm("productFormRef");
+  productFormVisible.value = false;
 };
 // 瀵煎叆
 const handleImport = () => {
-	importUpload.title = "瀵煎叆閿�鍞彴璐�";
-	importUpload.open = true;
-	if (importUploadRef.value) {
-		importUploadRef.value.clearFiles();
-	}
+  importUpload.title = "瀵煎叆閿�鍞彴璐�";
+  importUpload.open = true;
+  if (importUploadRef.value) {
+    importUploadRef.value.clearFiles();
+  }
 };
 
 // 涓嬭浇瀵煎叆妯℃澘
 const downloadTemplate = () => {
-	proxy.download("/sales/ledger/exportTemplate", {}, "閿�鍞彴璐﹀鍏ユā鏉�.xlsx");
+  proxy.download("/sales/ledger/exportTemplate", {}, "閿�鍞彴璐﹀鍏ユā鏉�.xlsx");
 };
 
 // 鎻愪氦瀵煎叆鏂囦欢
 const submitImportFile = () => {
-	importUpload.isUploading = true;
-	proxy.$refs["importUploadRef"].submit();
+  importUpload.isUploading = true;
+  proxy.$refs["importUploadRef"].submit();
 };
 
 // 瀵煎嚭
 const handleOut = () => {
-	ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
-		confirmButtonText: "纭",
-		cancelButtonText: "鍙栨秷",
-		type: "warning",
-	})
-		.then(() => {
-			proxy.download("/sales/ledger/export", {}, "閿�鍞彴璐�.xlsx");
-		})
-		.catch(() => {
-			proxy.$modal.msg("宸插彇娑�");
-		});
+  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  })
+      .then(() => {
+        proxy.download("/sales/ledger/export", {}, "閿�鍞彴璐�.xlsx");
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
 };
 /** 鍒ゆ柇鍗曚釜浜у搧鏄惁宸插彂璐э紙鏍规嵁shippingStatus鍒ゆ柇锛屽凡鍙戣揣鎴栧鏍搁�氳繃涓嶅彲缂栬緫鍜屽垹闄わ級 */
-const isProductShipped = (product) => {
-	if (!product) return false;
-	const status = String(product.shippingStatus || "").trim();
-	// 濡傛灉鍙戣揣鐘舵�佹槸"宸插彂璐�"鎴�"瀹℃牳閫氳繃"锛屽垯涓嶅彲缂栬緫鍜屽垹闄�
-	return status === "宸插彂璐�" || status === "瀹℃牳閫氳繃";
+const isProductShipped = product => {
+  if (!product) return false;
+  const status = String(product.shippingStatus || "").trim();
+  // 濡傛灉鍙戣揣鐘舵�佹槸"宸插彂璐�"鎴�"瀹℃牳閫氳繃"锛屽垯涓嶅彲缂栬緫鍜屽垹闄�
+  return status === "宸插彂璐�" || status === "瀹℃牳閫氳繃";
 };
 
 /** 鍒ゆ柇閿�鍞鍗曚笅鏄惁瀛樺湪宸插彂璐�/鍙戣揣瀹屾垚鐨勪骇鍝侊紙涓嶅彲鍒犻櫎锛� */
-const hasShippedProducts = (products) => {
-	if (!products || !products.length) return false;
-	return products.some((p) => {
-		const status = String(p.shippingStatus || "").trim();
-		// 鏈夊彂璐ф棩鏈熸垨杞︾墝鍙疯涓哄凡鍙戣揣
-		if (p.shippingDate || p.shippingCarNumber) return true;
-		// 宸茶繘琛屽彂璐с�佸彂璐у畬鎴愩�佸凡鍙戣揣 鍧囦笉鍙垹闄�
-		return status === "宸茶繘琛屽彂璐�" || status === "鍙戣揣瀹屾垚" || status === "宸插彂璐�";
-	});
+const hasShippedProducts = products => {
+  if (!products || !products.length) return false;
+  return products.some(p => {
+    const status = String(p.shippingStatus || "").trim();
+    // 鏈夊彂璐ф棩鏈熸垨杞︾墝鍙疯涓哄凡鍙戣揣
+    if (p.shippingDate || p.shippingCarNumber) return true;
+    // 宸茶繘琛屽彂璐с�佸彂璐у畬鎴愩�佸凡鍙戣揣 鍧囦笉鍙垹闄�
+    return (
+        status === "宸茶繘琛屽彂璐�" || status === "鍙戣揣瀹屾垚" || status === "宸插彂璐�"
+    );
+  });
 };
 
 // 鍒犻櫎
 const handleDelete = async () => {
-	if (selectedRows.value.length === 0) {
-		proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
-		return;
-	}
-	const ids = selectedRows.value.map((item) => item.id);
+  if (selectedRows.value.length === 0) {
+    proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+    return;
+  }
+  const unauthorizedRows = selectedRows.value.filter(
+      row => !canDeleteLedger(row)
+  );
+  if (unauthorizedRows.length > 0) {
+    proxy.$modal.msgWarning("褰撳墠鐧诲綍鐢ㄦ埛涓嶆槸褰曞叆浜猴紝涓嶈兘鍒犻櫎璇ユ暟鎹�");
+    return;
+  }
+  const ids = selectedRows.value.map(item => item.id);
 
-	// 妫�鏌ユ槸鍚︽湁宸茶繘琛屽彂璐ф垨鍙戣揣瀹屾垚鐨勯攢鍞鍗曪紝鑻ユ湁鍒欎笉鍏佽鍒犻櫎
-	const cannotDeleteNames = [];
-	for (const row of selectedRows.value) {
-		let products = row.children && row.children.length > 0 ? row.children : null;
-		if (!products) {
-			try {
-				const res = await productList({ salesLedgerId: row.id, type: 1 });
-				products = res.data || [];
-			} catch {
-				products = [];
-			}
-		}
-		if (hasShippedProducts(products)) {
-			cannotDeleteNames.push(row.salesContractNo || `ID:${row.id}`);
-		}
-	}
-	if (cannotDeleteNames.length > 0) {
-		proxy.$modal.msgWarning("宸茶繘琛屽彂璐ф垨鍙戣揣瀹屾垚鐨勯攢鍞鍗曚笉鑳藉垹闄わ細" + cannotDeleteNames.join("銆�"));
-		return;
-	}
+  // 妫�鏌ユ槸鍚︽湁宸茶繘琛屽彂璐ф垨鍙戣揣瀹屾垚鐨勯攢鍞鍗曪紝鑻ユ湁鍒欎笉鍏佽鍒犻櫎
+  const cannotDeleteNames = [];
+  for (const row of selectedRows.value) {
+    let products =
+        row.children && row.children.length > 0 ? row.children : null;
+    if (!products) {
+      try {
+        const res = await productList({salesLedgerId: row.id, type: 1});
+        products = res.data || [];
+      } catch {
+        products = [];
+      }
+    }
+    if (hasShippedProducts(products)) {
+      cannotDeleteNames.push(row.salesContractNo || `ID:${row.id}`);
+    }
+  }
+  if (cannotDeleteNames.length > 0) {
+    proxy.$modal.msgWarning(
+        "宸茶繘琛屽彂璐ф垨鍙戣揣瀹屾垚鐨勯攢鍞鍗曚笉鑳藉垹闄わ細" + cannotDeleteNames.join("銆�")
+    );
+    return;
+  }
 
-	ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
-		confirmButtonText: "纭",
-		cancelButtonText: "鍙栨秷",
-		type: "warning",
-	})
-		.then(() => {
-			delLedger(ids).then((res) => {
-				proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-				getList();
-			});
-		})
-		.catch(() => {
-			proxy.$modal.msg("宸插彇娑�");
-		});
+  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  })
+      .then(() => {
+        delLedger(ids).then(res => {
+          proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+          getList();
+        });
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
 };
 
 // 鎵撳嵃鍔熻兘
 const handlePrint = async () => {
-	if (selectedRows.value.length === 0) {
-		proxy.$modal.msgWarning("璇烽�夋嫨瑕佹墦鍗扮殑鏁版嵁");
-		return;
-	}
-	
-	// 鏄剧ず鍔犺浇鐘舵��
-	proxy.$modal.loading("姝e湪鑾峰彇浜у搧鏁版嵁锛岃绋嶅��...");
-	
-	try {
-		// 涓烘瘡涓�変腑鐨勯攢鍞彴璐﹁褰曟煡璇㈠搴旂殑浜у搧鏁版嵁
-		const printDataWithProducts = [];
-		
-		for (const row of selectedRows.value) {
-			try {
-				// 璋冪敤productList鎺ュ彛鏌ヨ浜у搧鏁版嵁
-				const productRes = await productList({ salesLedgerId: row.id, type: 1 });
-				
-				// 灏嗕骇鍝佹暟鎹暣鍚堝埌閿�鍞彴璐﹁褰曚腑
-				const rowWithProducts = {
-					...row,
-					products: productRes.data || []
-				};
-				
-				printDataWithProducts.push(rowWithProducts);
-			} catch (error) {
-				console.error(`鑾峰彇閿�鍞彴璐� ${row.id} 鐨勪骇鍝佹暟鎹け璐�:`, error);
-				// 鍗充娇鏌愪釜璁板綍鐨勪骇鍝佹暟鎹幏鍙栧け璐ワ紝涔熻鍖呭惈璇ヨ褰�
-				printDataWithProducts.push({
-					...row,
-					products: []
-				});
-			}
-		}
-		
-		printData.value = printDataWithProducts;
-		console.log('鎵撳嵃鏁版嵁锛堝寘鍚骇鍝侊級:', printData.value);
-		printPreviewVisible.value = true;
-		
-	} catch (error) {
-		console.error('鑾峰彇浜у搧鏁版嵁澶辫触:', error);
-		proxy.$modal.msgError("鑾峰彇浜у搧鏁版嵁澶辫触锛岃閲嶈瘯");
-	} finally {
-		proxy.$modal.closeLoading();
-	}
+  if (selectedRows.value.length === 0) {
+    proxy.$modal.msgWarning("璇烽�夋嫨瑕佹墦鍗扮殑鏁版嵁");
+    return;
+  }
+
+  // 鏄剧ず鍔犺浇鐘舵��
+  proxy.$modal.loading("姝e湪鑾峰彇浜у搧鏁版嵁锛岃绋嶅��...");
+
+  try {
+    // 涓烘瘡涓�変腑鐨勯攢鍞彴璐﹁褰曟煡璇㈠搴旂殑浜у搧鏁版嵁
+    const printDataWithProducts = [];
+
+    for (const row of selectedRows.value) {
+      try {
+        // 璋冪敤productList鎺ュ彛鏌ヨ浜у搧鏁版嵁
+        const productRes = await productList({
+          salesLedgerId: row.id,
+          type: 1,
+        });
+
+        // 灏嗕骇鍝佹暟鎹暣鍚堝埌閿�鍞彴璐﹁褰曚腑
+        const rowWithProducts = {
+          ...row,
+          products: productRes.data || [],
+        };
+
+        printDataWithProducts.push(rowWithProducts);
+      } catch (error) {
+        console.error(`鑾峰彇閿�鍞彴璐� ${row.id} 鐨勪骇鍝佹暟鎹け璐�:`, error);
+        // 鍗充娇鏌愪釜璁板綍鐨勪骇鍝佹暟鎹幏鍙栧け璐ワ紝涔熻鍖呭惈璇ヨ褰�
+        printDataWithProducts.push({
+          ...row,
+          products: [],
+        });
+      }
+    }
+
+    printData.value = printDataWithProducts;
+    console.log("鎵撳嵃鏁版嵁锛堝寘鍚骇鍝侊級:", printData.value);
+    printPreviewVisible.value = true;
+  } catch (error) {
+    console.error("鑾峰彇浜у搧鏁版嵁澶辫触:", error);
+    proxy.$modal.msgError("鑾峰彇浜у搧鏁版嵁澶辫触锛岃閲嶈瘯");
+  } finally {
+    proxy.$modal.closeLoading();
+  }
 };
 // 鎵ц鎵撳嵃
 const executePrint = () => {
-	console.log('寮�濮嬫墽琛屾墦鍗帮紝鏁版嵁鏉℃暟:', printData.value.length);
-	console.log('鎵撳嵃鏁版嵁:', printData.value);
-	
-	// 鍒涘缓涓�涓柊鐨勬墦鍗扮獥鍙�
-	const printWindow = window.open('', '_blank', 'width=800,height=600');
-	
-	// 鏋勫缓鎵撳嵃鍐呭
-	let printContent = `
-    <!DOCTYPE html>
-    <html>
-    <head>
-      <meta charset="UTF-8">
-      <title>鎵撳嵃棰勮</title>
-      <style>
-        body {
-          margin: 0;
-          padding: 0;
-          font-family: "SimSun", serif;
-          background: white;
-        }
-                                                     .print-page {
-            width: 200mm;
-            height: 75mm;
-            padding: 10mm;
-            padding-left: 20mm;
-            background: white;
-            box-sizing: border-box;
-            page-break-after: always;
-            page-break-inside: avoid;
-          }
-         .print-page:last-child {
-           page-break-after: avoid;
-         }
-        .delivery-note {
-          width: 100%;
-          height: 100%;
-          font-size: 12px;
-          line-height: 1.2;
-          display: flex;
-          flex-direction: column;
-          color: #000;
-        }
-        .header {
-          text-align: center;
-          margin-bottom: 8px;
-        }
-        .company-name {
-          font-size: 18px;
-          font-weight: bold;
-          margin-bottom: 4px;
-        }
-        .document-title {
-          font-size: 16px;
-          font-weight: bold;
-        }
-        .info-section {
-          margin-bottom: 8px;
-          display: flex;
-          justify-content: space-between;
-          align-items: center;
-        }
-        .info-row {
-          line-height: 20px;
-        }
-        .label {
-          font-weight: bold;
-          width: 60px;
-          font-size: 12px;
-        }
-        .value {
-          margin-right: 20px;
-          min-width: 80px;
-          font-size: 12px;
-        }
-                 .table-section {
-                 margin-bottom: 40px;
-          //  flex: 0.6;
-         }
-        .product-table {
-          width: 100%;
-          border-collapse: collapse;
-          border: 1px solid #000;
-        }
-                 .product-table th, .product-table td {
-           border: 1px solid #000;
-           padding: 6px;
-           text-align: center;
-           font-size: 12px;
-           line-height: 1.4;
-         }
-        .product-table th {
-          font-weight: bold;
-        }
-        .total-value {
-          font-weight: bold;
-        }
-        .footer-section {
-          margin-top: auto;
-        }
-        .footer-row {
-          display: flex;
-          margin-bottom: 3px;
-          line-height: 22px;
-          justify-content: space-between;
-        }
-        .footer-item {
-          display: flex;
-          margin-right: 20px;
-        }
-        .footer-item .label {
-          font-weight: bold;
-          width: 80px;
-          font-size: 12px;
-        }
-        .footer-item .value {
-          min-width: 80px;
-          font-size: 12px;
-        }
-        .address-item .address-value {
-          min-width: 200px;
-        }
-        @media print {
-          body {
-            margin: 0;
-            padding: 0;
-          }
-                     .print-page {
-             margin: 0;
-             padding: 10mm;
-             /* padding-left: 20mm; */
-             page-break-inside: avoid;
-             page-break-after: always;
-           }
-           .print-page:last-child {
-             page-break-after: avoid;
-           }
-        }
-      </style>
-    </head>
-    <body>
-  `;
-	
-	// 涓烘瘡鏉℃暟鎹敓鎴愭墦鍗伴〉闈�
-	printData.value.forEach((item, index) => {
-		printContent += `
-      <div class="print-page">
-        <div class="delivery-note">
-          <div class="header">
-            <div class="company-name">鑻辨辰闃查攬鏂版潗鏂欐湁闄愬叕鍙�</div>
-            <div class="document-title">闆跺敭鍙戣揣鍗�</div>
-          </div>
-          
-          <div class="info-section">
-            <div class="info-row">
-              <div>
-                <span class="label">鍙戣揣鏃ユ湡锛�</span>
-                <span class="value">${formatDate(item.createTime)}</span>
-              </div>
-              <div>
-                <span class="label">瀹㈡埛鍚嶇О锛�</span>
-                <span class="value">${item.customerName || '寮犵埍鏈�'}</span>
-              </div>
-            </div>
-            <div class="info-row">
-              <span class="label">鍗曞彿锛�</span>
-              <span class="value">${item.salesContractNo || ''}</span>
-            </div>
-          </div>
+  console.log("寮�濮嬫墽琛屾墦鍗帮紝鏁版嵁鏉℃暟:", printData.value.length);
+  console.log("鎵撳嵃鏁版嵁:", printData.value);
 
-          <div class="table-section">
-            <table class="product-table">
-              <thead>
-                <tr>
-                  <th>浜у搧鍚嶇О</th>
-                  <th>瑙勬牸鍨嬪彿</th>
-                  <th>鍗曚綅</th>
-                  <th>鍗曚环</th>
-                  <th>闆跺敭鏁伴噺</th>
-                  <th>闆跺敭閲戦</th>
-                </tr>
-              </thead>
-              <tbody>
-                ${item.products && item.products.length > 0 ?
-			item.products.map(product => `
-                    <tr>
-                      <td>${product.productCategory || ''}</td>
-                      <td>${product.specificationModel || ''}</td>
-                      <td>${product.unit || ''}</td>
-                      <td>${product.taxInclusiveUnitPrice || '0'}</td>
-                      <td>${product.quantity || '0'}</td>
-                      <td>${product.taxInclusiveTotalPrice || '0'}</td>
-                    </tr>
-                  `).join('') :
-			'<tr><td colspan="6" style="text-align: center; color: #999;">鏆傛棤浜у搧鏁版嵁</td></tr>'
-		}
-              </tbody>
-              <tfoot>
-                <tr>
-                  <td class="label">鍚堣</td>
-                  <td class="total-value"></td>
-                  <td class="total-value"></td>
-                  <td class="total-value"></td>
-                  <td class="total-value">${getTotalQuantityForPrint(item.products)}</td>
-                  <td class="total-value">${getTotalAmountForPrint(item.products)}</td>
-                </tr>
-              </tfoot>
-            </table>
-          </div>
+  // 鍒涘缓涓�涓柊鐨勬墦鍗扮獥鍙�
+  const printWindow = window.open("", "_blank", "width=800,height=600");
 
-          <div class="footer-section">
-            <div class="footer-row">
-              <div class="footer-item">
-                <span class="label">鏀惰揣鐢佃瘽锛�</span>
-                <span class="value"></span>
-              </div>
-              <div class="footer-item">
-                <span class="label">鏀惰揣浜猴細</span>
-                <span class="value"></span>
-              </div>
-              <div class="footer-item address-item">
-                <span class="label">鏀惰揣鍦板潃锛�</span>
-                <span class="value address-value"></span>
-              </div>
-            </div>
-            <div class="footer-row">
-              <div class="footer-item">
-                <span class="label">鎿嶄綔鍛橈細</span>
-                <span class="value">${userStore.nickName || '鎾曞紑鍓�'}</span>
-              </div>
-              <div class="footer-item">
-                <span class="label">鎵撳嵃鏃ユ湡锛�</span>
-                <span class="value">${formatDateTime(new Date())}</span>
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-    `;
-	});
-	
-	printContent += `
-    </body>
-    </html>
-  `;
-	
-	// 鍐欏叆鍐呭鍒版柊绐楀彛
-	printWindow.document.write(printContent);
-	printWindow.document.close();
-	
-	// 绛夊緟鍐呭鍔犺浇瀹屾垚鍚庢墦鍗�
-	printWindow.onload = () => {
-		setTimeout(() => {
-			printWindow.print();
-			printWindow.close();
-			printPreviewVisible.value = false;
-		}, 500);
-	};
+  // 鏋勫缓鎵撳嵃鍐呭
+  let printContent = `
+                                                    <!DOCTYPE html>
+                                                    <html>
+                                                    <head>
+                                                      <meta charset="UTF-8">
+                                                      <title>鎵撳嵃棰勮</title>
+                                                      <style>
+                                                        body {
+                                                          margin: 0;
+                                                          padding: 0;
+                                                          font-family: "SimSun", serif;
+                                                          background: white;
+                                                        }
+                                                                                                     .print-page {
+                                                            width: 200mm;
+                                                            height: 75mm;
+                                                            padding: 10mm;
+                                                            padding-left: 20mm;
+                                                            background: white;
+                                                            box-sizing: border-box;
+                                                            page-break-after: always;
+                                                            page-break-inside: avoid;
+                                                          }
+                                                         .print-page:last-child {
+                                                           page-break-after: avoid;
+                                                         }
+                                                        .delivery-note {
+                                                          width: 100%;
+                                                          height: 100%;
+                                                          font-size: 12px;
+                                                          line-height: 1.2;
+                                                          display: flex;
+                                                          flex-direction: column;
+                                                          color: #000;
+                                                        }
+                                                        .header {
+                                                          text-align: center;
+                                                          margin-bottom: 8px;
+                                                        }
+                                                        .company-name {
+                                                          font-size: 18px;
+                                                          font-weight: bold;
+                                                          margin-bottom: 4px;
+                                                        }
+                                                        .document-title {
+                                                          font-size: 16px;
+                                                          font-weight: bold;
+                                                        }
+                                                        .info-section {
+                                                          margin-bottom: 8px;
+                                                          display: flex;
+                                                          justify-content: space-between;
+                                                          align-items: center;
+                                                        }
+                                                        .info-row {
+                                                          line-height: 20px;
+                                                        }
+                                                        .label {
+                                                          font-weight: bold;
+                                                          width: 60px;
+                                                          font-size: 12px;
+                                                        }
+                                                        .value {
+                                                          margin-right: 20px;
+                                                          min-width: 80px;
+                                                          font-size: 12px;
+                                                        }
+                                                                 .table-section {
+                                                                 margin-bottom: 40px;
+                                                          //  flex: 0.6;
+                                                         }
+                                                        .product-table {
+                                                          width: 100%;
+                                                          border-collapse: collapse;
+                                                          border: 1px solid #000;
+                                                        }
+                                                                 .product-table th, .product-table td {
+                                                           border: 1px solid #000;
+                                                           padding: 6px;
+                                                           text-align: center;
+                                                           font-size: 12px;
+                                                           line-height: 1.4;
+                                                         }
+                                                        .product-table th {
+                                                          font-weight: bold;
+                                                        }
+                                                        .total-value {
+                                                          font-weight: bold;
+                                                        }
+                                                        .footer-section {
+                                                          margin-top: auto;
+                                                        }
+                                                        .footer-row {
+                                                          display: flex;
+                                                          margin-bottom: 3px;
+                                                          line-height: 22px;
+                                                          justify-content: space-between;
+                                                        }
+                                                        .footer-item {
+                                                          display: flex;
+                                                          margin-right: 20px;
+                                                        }
+                                                        .footer-item .label {
+                                                          font-weight: bold;
+                                                          width: 80px;
+                                                          font-size: 12px;
+                                                        }
+                                                        .footer-item .value {
+                                                          min-width: 80px;
+                                                          font-size: 12px;
+                                                        }
+                                                        .address-item .address-value {
+                                                          min-width: 200px;
+                                                        }
+                                                        @media print {
+                                                          body {
+                                                            margin: 0;
+                                                            padding: 0;
+                                                          }
+                                                                     .print-page {
+                                                             margin: 0;
+                                                             padding: 10mm;
+                                                             /* padding-left: 20mm; */
+                                                             page-break-inside: avoid;
+                                                             page-break-after: always;
+                                                           }
+                                                           .print-page:last-child {
+                                                             page-break-after: avoid;
+                                                           }
+                                                        }
+                                                      </style>
+                                                    </head>
+                                                    <body>
+                                                  `;
+
+  // 涓烘瘡鏉℃暟鎹敓鎴愭墦鍗伴〉闈�
+  printData.value.forEach((item, index) => {
+    printContent += `
+                                                      <div class="print-page">
+                                                        <div class="delivery-note">
+                                                          <div class="header">
+                                                            <div class="document-title">闆跺敭鍙戣揣鍗�</div>
+                                                          </div>
+                                                          
+                                                          <div class="info-section">
+                                                            <div class="info-row">
+                                                              <div>
+                                                                <span class="label">鍙戣揣鏃ユ湡锛�</span>
+                                                                <span class="value">${formatDate(
+        item.createTime
+    )}</span>
+                                                              </div>
+                                                              <div>
+                                                                <span class="label">瀹㈡埛鍚嶇О锛�</span>
+                                                                <span class="value">${
+        item.customerName
+    }</span>
+                                                              </div>
+                                                            </div>
+                                                            <div class="info-row">
+                                                              <span class="label">鍗曞彿锛�</span>
+                                                              <span class="value">${
+        item.salesContractNo ||
+        ""
+    }</span>
+                                                            </div>
+                                                          </div>
+
+                                                          <div class="table-section">
+                                                            <table class="product-table">
+                                                              <thead>
+                                                                <tr>
+                                                                  <th>浜у搧鍚嶇О</th>
+                                                                  <th>瑙勬牸鍨嬪彿</th>
+                                                                  <th>鍗曚綅</th>
+                                                                  <th>鍗曚环</th>
+                                                                  <th>闆跺敭鏁伴噺</th>
+                                                                  <th>闆跺敭閲戦</th>
+                                                                </tr>
+                                                              </thead>
+                                                              <tbody>
+                                                                ${
+        item.products &&
+        item.products
+            .length > 0
+            ? item.products
+                .map(
+                    product => `
+                                                                    <tr>
+                                                                      <td>${
+                        product.productCategory ||
+                        ""
+                    }</td>
+                                                                      <td>${
+                        product.specificationModel ||
+                        ""
+                    }</td>
+                                                                      <td>${
+                        product.unit ||
+                        ""
+                    }</td>
+                                                                      <td>${
+                        product.taxInclusiveUnitPrice ||
+                        "0"
+                    }</td>
+                                                                      <td>${
+                        product.quantity ||
+                        "0"
+                    }</td>
+                                                                      <td>${
+                        product.taxInclusiveTotalPrice ||
+                        "0"
+                    }</td>
+                                                                    </tr>
+                                                                  `
+                )
+                .join("")
+            : '<tr><td colspan="6" style="text-align: center; color: #999;">鏆傛棤浜у搧鏁版嵁</td></tr>'
+    }
+                                                              </tbody>
+                                                              <tfoot>
+                                                                <tr>
+                                                                  <td class="label">鍚堣</td>
+                                                                  <td class="total-value"></td>
+                                                                  <td class="total-value"></td>
+                                                                  <td class="total-value"></td>
+                                                                  <td class="total-value">${getTotalQuantityForPrint(
+        item.products
+    )}</td>
+                                                                  <td class="total-value">${getTotalAmountForPrint(
+        item.products
+    )}</td>
+                                                                </tr>
+                                                              </tfoot>
+                                                            </table>
+                                                          </div>
+
+                                                          <div class="footer-section">
+                                                            <div class="footer-row">
+                                                              <div class="footer-item">
+                                                                <span class="label">鏀惰揣鐢佃瘽锛�</span>
+                                                                <span class="value"></span>
+                                                              </div>
+                                                              <div class="footer-item">
+                                                                <span class="label">鏀惰揣浜猴細</span>
+                                                                <span class="value"></span>
+                                                              </div>
+                                                              <div class="footer-item address-item">
+                                                                <span class="label">鏀惰揣鍦板潃锛�</span>
+                                                                <span class="value address-value"></span>
+                                                              </div>
+                                                            </div>
+                                                            <div class="footer-row">
+                                                              <div class="footer-item">
+                                                                <span class="label">鎿嶄綔鍛橈細</span>
+                                                                <span class="value">${
+        userStore.nickName ||
+        "鎾曞紑鍓�"
+    }</span>
+                                                              </div>
+                                                              <div class="footer-item">
+                                                                <span class="label">鎵撳嵃鏃ユ湡锛�</span>
+                                                                <span class="value">${formatDateTime(
+        new Date()
+    )}</span>
+                                                              </div>
+                                                            </div>
+                                                          </div>
+                                                        </div>
+                                                      </div>
+                                                    `;
+  });
+
+  printContent += `
+                                                    </body>
+                                                    </html>
+                                                  `;
+
+  // 鍐欏叆鍐呭鍒版柊绐楀彛
+  printWindow.document.write(printContent);
+  printWindow.document.close();
+
+  // 绛夊緟鍐呭鍔犺浇瀹屾垚鍚庢墦鍗�
+  printWindow.onload = () => {
+    setTimeout(() => {
+      printWindow.print();
+      printWindow.close();
+      printPreviewVisible.value = false;
+    }, 500);
+  };
 };
 // 鏍煎紡鍖栨棩鏈�
-const formatDate = (dateString) => {
-	if (!dateString) return getCurrentDate();
-	const date = new Date(dateString);
-	const year = date.getFullYear();
-	const month = String(date.getMonth() + 1).padStart(2, "0");
-	const day = String(date.getDate()).padStart(2, "0");
-	return `${year}/${month}/${day}`;
+const formatDate = dateString => {
+  if (!dateString) return getCurrentDate();
+  const date = new Date(dateString);
+  const year = date.getFullYear();
+  const month = String(date.getMonth() + 1).padStart(2, "0");
+  const day = String(date.getDate()).padStart(2, "0");
+  return `${year}/${month}/${day}`;
 };
 // 鏍煎紡鍖栨棩鏈熸椂闂�
-const formatDateTime = (date) => {
-	const year = date.getFullYear();
-	const month = String(date.getMonth() + 1).padStart(2, "0");
-	const day = String(date.getDate()).padStart(2, "0");
-	const hours = String(date.getHours()).padStart(2, "0");
-	const minutes = String(date.getMinutes()).padStart(2, "0");
-	const seconds = String(date.getSeconds()).padStart(2, "0");
-	return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
+const formatDateTime = date => {
+  const year = date.getFullYear();
+  const month = String(date.getMonth() + 1).padStart(2, "0");
+  const day = String(date.getDate()).padStart(2, "0");
+  const hours = String(date.getHours()).padStart(2, "0");
+  const minutes = String(date.getMinutes()).padStart(2, "0");
+  const seconds = String(date.getSeconds()).padStart(2, "0");
+  return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
 };
 // 璁$畻浜у搧鎬绘暟閲�
-const getTotalQuantity = (products) => {
-	if (!products || products.length === 0) return '0';
-	const total = products.reduce((sum, product) => {
-		return sum + (parseFloat(product.quantity) || 0);
-	}, 0);
-	return total.toFixed(2);
+const getTotalQuantity = products => {
+  if (!products || products.length === 0) return "0";
+  const total = products.reduce((sum, product) => {
+    return sum + (parseFloat(product.quantity) || 0);
+  }, 0);
+  return total.toFixed(2);
 };
 
 // 璁$畻浜у搧鎬婚噾棰�
-const getTotalAmount = (products) => {
-	if (!products || products.length === 0) return '0';
-	const total = products.reduce((sum, product) => {
-		return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
-	}, 0);
-	return total.toFixed(2);
+const getTotalAmount = products => {
+  if (!products || products.length === 0) return "0";
+  const total = products.reduce((sum, product) => {
+    return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
+  }, 0);
+  return total.toFixed(2);
 };
 
 // 鐢ㄤ簬鎵撳嵃鐨勮绠楀嚱鏁�
-const getTotalQuantityForPrint = (products) => {
-	if (!products || products.length === 0) return '0';
-	const total = products.reduce((sum, product) => {
-		return sum + (parseFloat(product.quantity) || 0);
-	}, 0);
-	return total.toFixed(2);
+const getTotalQuantityForPrint = products => {
+  if (!products || products.length === 0) return "0";
+  const total = products.reduce((sum, product) => {
+    return sum + (parseFloat(product.quantity) || 0);
+  }, 0);
+  return total.toFixed(2);
 };
 
-const getTotalAmountForPrint = (products) => {
-	if (!products || products.length === 0) return '0';
-	const total = products.reduce((sum, product) => {
-		return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
-	}, 0);
-	return total.toFixed(2);
+const getTotalAmountForPrint = products => {
+  if (!products || products.length === 0) return "0";
+  const total = products.reduce((sum, product) => {
+    return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
+  }, 0);
+  return total.toFixed(2);
 };
 
 const mathNum = () => {
-	console.log("productForm.value", productForm.value);
-	if (!productForm.value.taxInclusiveUnitPrice) {
-		return;
-	}
-	if (!productForm.value.quantity) {
-		return;
-	}
-	// 鍚◣鎬讳环璁$畻
-	productForm.value.taxInclusiveTotalPrice =
-		proxy.calculateTaxIncludeTotalPrice(
-			productForm.value.taxInclusiveUnitPrice,
-			productForm.value.quantity
-		);
-	if (productForm.value.taxRate) {
-		// 涓嶅惈绋庢�讳环璁$畻
-		productForm.value.taxExclusiveTotalPrice =
-			proxy.calculateTaxExclusiveTotalPrice(
-				productForm.value.taxInclusiveTotalPrice,
-				productForm.value.taxRate
-			);
-	}
+  console.log("productForm.value", productForm.value);
+  if (!productForm.value.taxInclusiveUnitPrice) {
+    return;
+  }
+  if (!productForm.value.quantity) {
+    return;
+  }
+  // 鍚◣鎬讳环璁$畻
+  productForm.value.taxInclusiveTotalPrice =
+      proxy.calculateTaxIncludeTotalPrice(
+          productForm.value.taxInclusiveUnitPrice,
+          productForm.value.quantity
+      );
+  if (productForm.value.taxRate) {
+    // 涓嶅惈绋庢�讳环璁$畻
+    productForm.value.taxExclusiveTotalPrice =
+        proxy.calculateTaxExclusiveTotalPrice(
+            productForm.value.taxInclusiveTotalPrice,
+            productForm.value.taxRate
+        );
+  }
 };
 
 // 鏍规嵁鍚◣鎬讳环璁$畻鍚◣鍗曚环鍜屾暟閲�
 const calculateFromTotalPrice = () => {
-	if (isCalculating.value) return;
-	
-	const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
-	const quantity = parseFloat(productForm.value.quantity);
-	
-	if (!totalPrice || !quantity || quantity <= 0) {
-		return;
-	}
-	
-	isCalculating.value = true;
-	
-	// 璁$畻鍚◣鍗曚环 = 鍚◣鎬讳环 / 鏁伴噺
-	productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2);
-	
-	// 濡傛灉鏈夌◣鐜囷紝璁$畻涓嶅惈绋庢�讳环
-	if (productForm.value.taxRate) {
-		productForm.value.taxExclusiveTotalPrice =
-			proxy.calculateTaxExclusiveTotalPrice(
-				totalPrice,
-				productForm.value.taxRate
-			);
-	}
-	
-	isCalculating.value = false;
+  if (isCalculating.value) return;
+
+  const totalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
+  const quantity = parseFloat(productForm.value.quantity);
+
+  if (!totalPrice || !quantity || quantity <= 0) {
+    return;
+  }
+
+  isCalculating.value = true;
+
+  // 璁$畻鍚◣鍗曚环 = 鍚◣鎬讳环 / 鏁伴噺
+  productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2);
+
+  // 濡傛灉鏈夌◣鐜囷紝璁$畻涓嶅惈绋庢�讳环
+  if (productForm.value.taxRate) {
+    productForm.value.taxExclusiveTotalPrice =
+        proxy.calculateTaxExclusiveTotalPrice(
+            totalPrice,
+            productForm.value.taxRate
+        );
+  }
+
+  isCalculating.value = false;
 };
 
 // 鏍规嵁涓嶅惈绋庢�讳环璁$畻鍚◣鍗曚环鍜屾暟閲�
 const calculateFromExclusiveTotalPrice = () => {
-	if (!productForm.value.taxRate) {
-		proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
-		return;
-	}
-	if (isCalculating.value) return;
-	
-	const exclusiveTotalPrice = parseFloat(productForm.value.taxExclusiveTotalPrice);
-	const quantity = parseFloat(productForm.value.quantity);
-	const taxRate = parseFloat(productForm.value.taxRate);
-	
-	if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) {
-		return;
-	}
-	
-	isCalculating.value = true;
-	
-	// 鍏堣绠楀惈绋庢�讳环 = 涓嶅惈绋庢�讳环 / (1 - 绋庣巼/100)
-	const taxRateDecimal = taxRate / 100;
-	const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
-	productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
-	
-	// 璁$畻鍚◣鍗曚环 = 鍚◣鎬讳环 / 鏁伴噺
-	productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2);
-	
-	isCalculating.value = false;
+  if (!productForm.value.taxRate) {
+    proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+    return;
+  }
+  if (isCalculating.value) return;
+
+  const exclusiveTotalPrice = parseFloat(
+      productForm.value.taxExclusiveTotalPrice
+  );
+  const quantity = parseFloat(productForm.value.quantity);
+  const taxRate = parseFloat(productForm.value.taxRate);
+
+  if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) {
+    return;
+  }
+
+  isCalculating.value = true;
+
+  // 鍏堣绠楀惈绋庢�讳环 = 涓嶅惈绋庢�讳环 / (1 - 绋庣巼/100)
+  const taxRateDecimal = taxRate / 100;
+  const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
+  productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
+
+  // 璁$畻鍚◣鍗曚环 = 鍚◣鎬讳环 / 鏁伴噺
+  productForm.value.taxInclusiveUnitPrice = (
+      inclusiveTotalPrice / quantity
+  ).toFixed(2);
+
+  isCalculating.value = false;
 };
 
 // 鏍规嵁鏁伴噺鍙樺寲璁$畻鎬讳环
 const calculateFromQuantity = () => {
-	if (!productForm.value.taxRate) {
-		proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
-		return;
-	}
-	if (isCalculating.value) return;
-	
-	const quantity = parseFloat(productForm.value.quantity);
-	const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
-	
-	if (!quantity || quantity <= 0 || !unitPrice) {
-		return;
-	}
-	
-	isCalculating.value = true;
-	
-	// 璁$畻鍚◣鎬讳环
-	productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
-	
-	// 濡傛灉鏈夌◣鐜囷紝璁$畻涓嶅惈绋庢�讳环
-	if (productForm.value.taxRate) {
-		productForm.value.taxExclusiveTotalPrice =
-			proxy.calculateTaxExclusiveTotalPrice(
-				productForm.value.taxInclusiveTotalPrice,
-				productForm.value.taxRate
-			);
-	}
-	
-	isCalculating.value = false;
+  if (!productForm.value.taxRate) {
+    proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+    return;
+  }
+  if (isCalculating.value) return;
+
+  const quantity = parseFloat(productForm.value.quantity);
+  const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
+
+  if (!quantity || quantity <= 0 || !unitPrice) {
+    return;
+  }
+
+  isCalculating.value = true;
+
+  // 璁$畻鍚◣鎬讳环
+  productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
+
+  // 濡傛灉鏈夌◣鐜囷紝璁$畻涓嶅惈绋庢�讳环
+  if (productForm.value.taxRate) {
+    productForm.value.taxExclusiveTotalPrice =
+        proxy.calculateTaxExclusiveTotalPrice(
+            productForm.value.taxInclusiveTotalPrice,
+            productForm.value.taxRate
+        );
+  }
+
+  isCalculating.value = false;
 };
 
 // 鏍规嵁鍚◣鍗曚环鍙樺寲璁$畻鎬讳环
 const calculateFromUnitPrice = () => {
-	if (!productForm.value.taxRate) {
-		proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
-		return;
-	}
-	if (isCalculating.value) return;
-	
-	const quantity = parseFloat(productForm.value.quantity);
-	const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
-	
-	if (!quantity || quantity <= 0 || !unitPrice) {
-		return;
-	}
-	
-	isCalculating.value = true;
-	
-	// 璁$畻鍚◣鎬讳环
-	productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
-	
-	// 濡傛灉鏈夌◣鐜囷紝璁$畻涓嶅惈绋庢�讳环
-	if (productForm.value.taxRate) {
-		productForm.value.taxExclusiveTotalPrice =
-			proxy.calculateTaxExclusiveTotalPrice(
-				productForm.value.taxInclusiveTotalPrice,
-				productForm.value.taxRate
-			);
-	}
-	
-	isCalculating.value = false;
+  if (!productForm.value.taxRate) {
+    proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+    return;
+  }
+  if (isCalculating.value) return;
+
+  const quantity = parseFloat(productForm.value.quantity);
+  const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
+
+  if (!quantity || quantity <= 0 || !unitPrice) {
+    return;
+  }
+
+  isCalculating.value = true;
+
+  // 璁$畻鍚◣鎬讳环
+  productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
+
+  // 濡傛灉鏈夌◣鐜囷紝璁$畻涓嶅惈绋庢�讳环
+  if (productForm.value.taxRate) {
+    productForm.value.taxExclusiveTotalPrice =
+        proxy.calculateTaxExclusiveTotalPrice(
+            productForm.value.taxInclusiveTotalPrice,
+            productForm.value.taxRate
+        );
+  }
+
+  isCalculating.value = false;
 };
 
 // 鏍规嵁绋庣巼鍙樺寲璁$畻涓嶅惈绋庢�讳环
 const calculateFromTaxRate = () => {
-	if (!productForm.value.taxRate) {
-		proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
-		return;
-	}
-	if (isCalculating.value) return;
-	
-	const inclusiveTotalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
-	const taxRate = parseFloat(productForm.value.taxRate);
-	
-	if (!inclusiveTotalPrice || !taxRate) {
-		return;
-	}
-	
-	isCalculating.value = true;
-	
-	// 璁$畻涓嶅惈绋庢�讳环
-	productForm.value.taxExclusiveTotalPrice =
-		proxy.calculateTaxExclusiveTotalPrice(
-			inclusiveTotalPrice,
-			taxRate
-		);
-	
-	isCalculating.value = false;
+  if (!productForm.value.taxRate) {
+    proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+    return;
+  }
+  if (isCalculating.value) return;
+
+  const inclusiveTotalPrice = parseFloat(
+      productForm.value.taxInclusiveTotalPrice
+  );
+  const taxRate = parseFloat(productForm.value.taxRate);
+
+  if (!inclusiveTotalPrice || !taxRate) {
+    return;
+  }
+
+  isCalculating.value = true;
+
+  // 璁$畻涓嶅惈绋庢�讳环
+  productForm.value.taxExclusiveTotalPrice =
+      proxy.calculateTaxExclusiveTotalPrice(inclusiveTotalPrice, taxRate);
+
+  isCalculating.value = false;
 };
 /**
  * 鑾峰彇鍙戣揣鐘舵�佹枃鏈�
  * @param row 琛屾暟鎹�
  */
-const getShippingStatusText = (row) => {
-	// 濡傛灉宸插彂璐э紙鏈夊彂璐ф棩鏈熸垨杞︾墝鍙凤級锛屾樉绀�"宸插彂璐�"
-	if (row.shippingDate || row.shippingCarNumber) {
-		return '宸插彂璐�';
-	}
-	
-	// 鑾峰彇鍙戣揣鐘舵�佸瓧娈�
-	const status = row.shippingStatus;
-	
-	// 濡傛灉鐘舵�佷负绌烘垨鏈畾涔夛紝榛樿涓�"寰呭彂璐�"
-	if (status === null || status === undefined || status === '') {
-		return '寰呭彂璐�';
-	}
-	
-	// 鐘舵�佹槸瀛楃涓�
-	const statusStr = String(status).trim();
-	const statusTextMap = {
-		'寰呭彂璐�': '寰呭彂璐�',
-		'寰呭鏍�': '寰呭鏍�',
-		'瀹℃牳涓�': '瀹℃牳涓�',
-		'瀹℃牳鎷掔粷': '瀹℃牳鎷掔粷',
-		'瀹℃牳閫氳繃': '瀹℃牳閫氳繃',
-		'宸插彂璐�': '宸插彂璐�'
-	};
-	return statusTextMap[statusStr] || '寰呭彂璐�';
+const getShippingStatusText = row => {
+  // 濡傛灉宸插彂璐э紙鏈夊彂璐ф棩鏈熸垨杞︾墝鍙凤級锛屾樉绀�"宸插彂璐�"
+  // if (row.shippingDate || row.shippingCarNumber) {
+  //   return "宸插彂璐�";
+  // }
+
+  // 鑾峰彇鍙戣揣鐘舵�佸瓧娈�
+  const status = row.shippingStatus;
+
+  // 濡傛灉鐘舵�佷负绌烘垨鏈畾涔夛紝榛樿涓�"寰呭彂璐�"
+  if (status === null || status === undefined || status === "") {
+    return "寰呭彂璐�";
+  }
+
+  // 鐘舵�佹槸瀛楃涓�
+  const statusStr = String(status).trim();
+  const statusTextMap = {
+    寰呭彂璐�: "寰呭彂璐�",
+    寰呭鏍�: "寰呭鏍�",
+    瀹℃牳涓�: "瀹℃牳涓�",
+    瀹℃牳鎷掔粷: "瀹℃牳鎷掔粷",
+    瀹℃牳閫氳繃: "瀹℃牳閫氳繃",
+    宸插彂璐�: "宸插彂璐�",
+  };
+  return statusTextMap[statusStr] || "寰呭彂璐�";
 };
 
 /**
  * 鑾峰彇鍙戣揣鐘舵�佹爣绛剧被鍨嬶紙棰滆壊锛�
  * @param row 琛屾暟鎹�
  */
-const getShippingStatusType = (row) => {
-	// 濡傛灉宸插彂璐э紙鏈夊彂璐ф棩鏈熸垨杞︾墝鍙凤級锛屾樉绀虹豢鑹�
-	if (row.shippingDate || row.shippingCarNumber) {
-		return 'success';
-	}
-	
-	// 鑾峰彇鍙戣揣鐘舵�佸瓧娈�
-	const status = row.shippingStatus;
-	
-	// 濡傛灉鐘舵�佷负绌烘垨鏈畾涔夛紝榛樿涓虹伆鑹诧紙寰呭彂璐э級
-	if (status === null || status === undefined || status === '') {
-		return 'info';
-	}
-	
-	// 鐘舵�佹槸瀛楃涓�
-	const statusStr = String(status).trim();
-	const typeTextMap = {
-		'寰呭彂璐�': 'info',
-		'寰呭鏍�': 'info',
-		'瀹℃牳涓�': 'warning',
-		'瀹℃牳鎷掔粷': 'danger',
-		'瀹℃牳閫氳繃': 'success',
-		'宸插彂璐�': 'success'
-	};
-	return typeTextMap[statusStr] || 'info';
+const getShippingStatusType = row => {
+  // 濡傛灉宸插彂璐э紙鏈夊彂璐ф棩鏈熸垨杞︾墝鍙凤級锛屾樉绀虹豢鑹�
+  if (row.shippingStatus === "宸插彂璐�") {
+    return "success";
+  }
+
+  // 鑾峰彇鍙戣揣鐘舵�佸瓧娈�
+  const status = row.shippingStatus;
+
+  // 濡傛灉鐘舵�佷负绌烘垨鏈畾涔夛紝榛樿涓虹伆鑹诧紙寰呭彂璐э級
+  if (status === null || status === undefined || status === "") {
+    return "info";
+  }
+
+  // 鐘舵�佹槸瀛楃涓�
+  const statusStr = String(status).trim();
+  const typeTextMap = {
+    寰呭彂璐�: "info",
+    寰呭鏍�: "info",
+    瀹℃牳涓�: "warning",
+    瀹℃牳鎷掔粷: "danger",
+    瀹℃牳閫氳繃: "success",
+    宸插彂璐�: "success",
+  };
+  return typeTextMap[statusStr] || "info";
 };
 
 /**
@@ -2072,101 +2548,118 @@
  * 鍙湁鍦ㄤ骇鍝佺姸鎬佹槸鍏呰冻锛屽彂璐х姸鎬佹槸寰呭彂璐у拰瀹℃牳鎷掔粷鐨勬椂鍊欐墠鍙互鍙戣揣
  * @param row 琛屾暟鎹�
  */
-const canShip = (row) => {
-	// 浜у搧鐘舵�佸繀椤绘槸鍏呰冻锛坅pproveStatus === 1锛�
-	if (row.approveStatus !== 1) {
-		return false;
-	}
-	
-	// 鑾峰彇鍙戣揣鐘舵��
-	const shippingStatus = row.shippingStatus;
-	
-	// 濡傛灉宸插彂璐э紙鏈夊彂璐ф棩鏈熸垨杞︾墝鍙凤級锛屼笉鑳藉啀娆″彂璐�
-	if (row.shippingDate || row.shippingCarNumber) {
-		return false;
-	}
-	
-	// 鍙戣揣鐘舵�佸繀椤绘槸"寰呭彂璐�"鎴�"瀹℃牳鎷掔粷"
-	const statusStr = shippingStatus ? String(shippingStatus).trim() : '';
-	return statusStr === '寰呭彂璐�' || statusStr === '瀹℃牳鎷掔粷';
+const canShip = row => {
+  // 浜у搧鐘舵�佸繀椤绘槸鍏呰冻锛坅pproveStatus === 1锛�
+  if (row.approveStatus !== 1) {
+    return false;
+  }
+
+  // 鑾峰彇鍙戣揣鐘舵��
+  const shippingStatus = row.shippingStatus;
+
+  // 濡傛灉宸插彂璐э紙鏈夊彂璐ф棩鏈熸垨杞︾墝鍙凤級锛屼笉鑳藉啀娆″彂璐�
+  if (shippingStatus === "宸插彂璐�") {
+    return false;
+  }
+
+  // 鍙戣揣鐘舵�佸繀椤绘槸"寰呭彂璐�"鎴�"瀹℃牳鎷掔粷"
+  const statusStr = shippingStatus ? String(shippingStatus).trim() : "";
+  return statusStr === "寰呭彂璐�" || statusStr === "瀹℃牳鎷掔粷";
 };
 
-/**
- * 涓嬭浇鏂囦欢
- *
- * @param row 涓嬭浇鏂囦欢鐨勭浉鍏充俊鎭璞�
- */
-const fileListRef = ref(null)
-const fileListDialogVisible = ref(false)
-const downLoadFile = (row) => {
-	getSalesLedgerWithProducts({ id: row.id, type: 1 }).then((res) => {
-		if (fileListRef.value) {
-			fileListRef.value.open(res.salesLedgerFiles)
-		}
-	});
+// 鎵撳紑闄勪欢寮圭獥
+const recordId = ref(0)
+const fileDialogVisible = ref(false)
+
+// 鎵撳紑闄勪欢寮规
+const openFileDialog = async (row) => {
+  recordId.value = row.id
+  fileDialogVisible.value = true
 }
 
 // 鎵撳紑鍙戣揣寮规
-const openDeliveryForm = (row) => {
-	// 妫�鏌ユ槸鍚﹀彲浠ュ彂璐�
-	if (!canShip(row)) {
-		proxy.$modal.msgWarning("鍙湁鍦ㄤ骇鍝佺姸鎬佹槸鍏呰冻锛屽彂璐х姸鎬佹槸寰呭彂璐ф垨瀹℃牳鎷掔粷鐨勬椂鍊欐墠鍙互鍙戣揣");
-		return;
-	}
-	
-	currentDeliveryRow.value = row;
+const openDeliveryForm = async row => {
+  // 妫�鏌ユ槸鍚﹀彲浠ュ彂璐�
+  if (!canShip(row)) {
+    proxy.$modal.msgWarning(
+        "鍙湁鍦ㄤ骇鍝佺姸鎬佹槸鍏呰冻锛屽彂璐х姸鎬佹槸寰呭彂璐ф垨瀹℃牳鎷掔粷鐨勬椂鍊欐墠鍙互鍙戣揣"
+    );
+    return;
+  }
+
+  currentDeliveryRow.value = row;
+  const batchNoList = await getDeliveryBatchNoList(
+      row.productModelId || row.modelId
+  );
   deliveryForm.value = {
     type: "璐ц溅",
+    batchNo: [],
+    batchNoList,
   };
-  // 閲嶇疆瀹℃壒浜鸿妭鐐癸紙榛樿涓�涓┖鑺傜偣锛�
-  approverNodes.value = [{ id: 1, userId: null }];
-  nextApproverId = 2;
-	deliveryFormVisible.value = true;
+  deliveryFormVisible.value = true;
 };
 
 // 鎻愪氦鍙戣揣琛ㄥ崟
 const submitDelivery = () => {
-  proxy.$refs["deliveryFormRef"].validate((valid) => {
+  proxy.$refs["deliveryFormRef"].validate(valid => {
     if (valid) {
-      // 瀹℃壒浜哄繀濉牎楠岋紙鎵�鏈夎妭鐐归兘瑕侀�変汉锛�
-      const hasEmptyApprover = approverNodes.value.some(node => !node.userId);
-      if (hasEmptyApprover) {
-        proxy.$modal.msgError("璇蜂负鎵�鏈夊鎵硅妭鐐归�夋嫨瀹℃壒浜猴紒");
+      const selectedBatchRows = getSelectedDeliveryBatchRows();
+      if (selectedBatchRows.length === 0) {
+        proxy.$modal.msgWarning("璇疯嚦灏戝~鍐欎竴涓壒鍙风殑鍙戣揣鏁伴噺");
         return;
       }
-      const approveUserIds = approverNodes.value.map(node => node.userId).join(",");
+      const totalDeliveryQuantity = selectedBatchRows.reduce(
+          (sum, item) => sum + Number(item.deliveryQuantity || 0),
+          0
+      );
+      const currentRowQuantity = Number(currentDeliveryRow.value?.quantity || 0);
+      if (currentRowQuantity > 0 && totalDeliveryQuantity > currentRowQuantity) {
+        proxy.$modal.msgWarning("鎵瑰彿鍙戣揣鎬绘暟涓嶈兘瓒呰繃褰撳墠浜у搧鏁伴噺");
+        return;
+      }
       // 淇濆瓨褰撳墠灞曞紑鐨勮ID锛屼互渚垮彂璐у悗閲嶆柊鍔犺浇瀛愯〃鏍兼暟鎹�
       const currentExpandedKeys = [...expandedRowKeys.value];
       const salesLedgerId = currentDeliveryRow.value.salesLedgerId;
+      deliveryForm.value.batchNo = selectedBatchRows.map(item => item.id);
+      const productModelId = currentDeliveryRow.value.productModelId || currentDeliveryRow.value.modelId;
       addShippingInfo({
         salesLedgerId: salesLedgerId,
         salesLedgerProductId: currentDeliveryRow.value.id,
         type: deliveryForm.value.type,
-				approveUserIds,
-      })
-        .then(() => {
-          proxy.$modal.msgSuccess("鍙戣揣鎴愬姛");
-          closeDeliveryDia();
-          // 鍒锋柊涓昏〃鏁版嵁
-          getList().then(() => {
-            // 濡傛灉涔嬪墠鏈夊睍寮�鐨勮锛岄噸鏂板姞杞借繖浜涜鐨勫瓙琛ㄦ牸鏁版嵁
-            if (currentExpandedKeys.length > 0) {
-              // 浣跨敤 Promise.all 骞惰鍔犺浇鎵�鏈夊睍寮�琛岀殑瀛愯〃鏍兼暟鎹�
-              const loadPromises = currentExpandedKeys.map(ledgerId => {
-                return productList({ salesLedgerId: ledgerId, type: 1 }).then((res) => {
-                  const index = tableData.value.findIndex((item) => item.id === ledgerId);
-                  if (index > -1) {
-                    tableData.value[index].children = res.data;
+        batchNo: deliveryForm.value.batchNo,
+        batchNoDetailList: selectedBatchRows.map(item => ({
+          stockInventoryId: item.id,
+          batchNo: item.batchNo,
+          quantity: Number(item.deliveryQuantity || 0),
+          productModelId: productModelId,
+        })),
+      }).then(() => {
+        proxy.$modal.msgSuccess("鍙戣揣鎴愬姛");
+        closeDeliveryDia();
+        // 鍒锋柊涓昏〃鏁版嵁
+        getList().then(() => {
+          // 濡傛灉涔嬪墠鏈夊睍寮�鐨勮锛岄噸鏂板姞杞借繖浜涜鐨勫瓙琛ㄦ牸鏁版嵁
+          if (currentExpandedKeys.length > 0) {
+            // 浣跨敤 Promise.all 骞惰鍔犺浇鎵�鏈夊睍寮�琛岀殑瀛愯〃鏍兼暟鎹�
+            const loadPromises = currentExpandedKeys.map(ledgerId => {
+              return productList({salesLedgerId: ledgerId, type: 1}).then(
+                  res => {
+                    const index = tableData.value.findIndex(
+                        item => item.id === ledgerId
+                    );
+                    if (index > -1) {
+                      tableData.value[index].children = res.data;
+                    }
                   }
-                });
-              });
-              Promise.all(loadPromises).then(() => {
-                // 鎭㈠灞曞紑鐘舵��
-                expandedRowKeys.value = currentExpandedKeys;
-              });
-            }
-          });
-        })
+              );
+            });
+            Promise.all(loadPromises).then(() => {
+              // 鎭㈠灞曞紑鐘舵��
+              expandedRowKeys.value = currentExpandedKeys;
+            });
+          }
+        });
+      });
     }
   });
 };
@@ -2179,212 +2672,215 @@
 };
 const currentFactoryName = ref("");
 const getCurrentFactoryName = async () => {
-	let res = await userStore.getInfo();
-	currentFactoryName.value = res.user.currentFactoryName;
+  let res = await userStore.getInfo();
+  currentFactoryName.value = res.user.currentFactoryName;
 };
 onMounted(() => {
-	getList();
-	userListNoPage().then(res => {
-		userList.value = res.data;
-	})
-	getCurrentFactoryName();
+  searchForm.salesContractNo = route.query.salesContractNo;
+  getList();
+  userListNoPage().then(res => {
+    userList.value = res.data;
+  });
+  getCurrentFactoryName();
 });
 </script>
 
 <style scoped lang="scss">
 .ml-10 {
-	margin-left: 10px;
+  margin-left: 10px;
 }
 
-::v-deep .yellow {
-  background-color: #FAF0DE;
+:deep(.yellow) {
+  background-color: #faf0de;
 }
 
-::v-deep .pink {
-  background-color: #FAE1DE;
+:deep(.pink) {
+  background-color: #fae1de;
 }
 
-::v-deep .red {
-  background-color: #FAE1DE;
+:deep(.red) {
+  background-color: #fae1de;
 }
 
-::v-deep .purple{
-  background-color: #F4DEFA;
+:deep(.purple) {
+  background-color: #f4defa;
 }
 
 .table_list {
-	margin-top: unset;
+  margin-top: unset;
 }
 
 .actions {
-	display: flex;
-	justify-content: space-between;
-	margin-bottom: 10px;
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 10px;
 }
+
 .print-preview-dialog {
-	.el-dialog__body {
-		padding: 0;
-		max-height: 80vh;
-		overflow-y: auto;
-	}
+  .el-dialog__body {
+    padding: 0;
+    max-height: 80vh;
+    overflow-y: auto;
+  }
 }
 
 .print-preview-container {
-	.print-preview-header {
-		padding: 15px;
-		border-bottom: 1px solid #e4e7ed;
-		text-align: center;
-		
-		.el-button {
-			margin: 0 10px;
-		}
-	}
-	
-	.print-preview-content {
-		padding: 20px;
-		background-color: #f5f5f5;
-		min-height: 400px;
-	}
+  .print-preview-header {
+    padding: 15px;
+    border-bottom: 1px solid #e4e7ed;
+    text-align: center;
+
+    .el-button {
+      margin: 0 10px;
+    }
+  }
+
+  .print-preview-content {
+    padding: 20px;
+    background-color: #f5f5f5;
+    min-height: 400px;
+  }
 }
 
 .print-page {
-	width: 220mm;
-	height: 90mm;
-	padding: 10mm;
-	margin: 0 auto;
-	background: white;
-	box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
-	margin-bottom: 10px;
-	box-sizing: border-box;
+  width: 220mm;
+  height: 90mm;
+  padding: 10mm;
+  margin: 0 auto;
+  background: white;
+  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+  margin-bottom: 10px;
+  box-sizing: border-box;
 }
 
 .delivery-note {
-	width: 100%;
-	height: 100%;
-	font-family: "SimSun", serif;
-	font-size: 10px;
-	line-height: 1.2;
-	display: flex;
-	flex-direction: column;
+  width: 100%;
+  height: 100%;
+  font-family: "SimSun", serif;
+  font-size: 10px;
+  line-height: 1.2;
+  display: flex;
+  flex-direction: column;
 }
 
 .header {
-	text-align: center;
-	margin-bottom: 8px;
-	
-	.company-name {
-		font-size: 18px;
-		font-weight: bold;
-		margin-bottom: 4px;
-	}
-	
-	.document-title {
-		font-size: 16px;
-		font-weight: bold;
-	}
+  text-align: center;
+  margin-bottom: 8px;
+
+  .company-name {
+    font-size: 18px;
+    font-weight: bold;
+    margin-bottom: 4px;
+  }
+
+  .document-title {
+    font-size: 16px;
+    font-weight: bold;
+  }
 }
 
 .info-section {
-	margin-bottom: 8px;
-	display: flex;
-	justify-content: space-between;
-	align-items: center;
-	
-	.info-row {
-		line-height: 20px;
-		
-		.label {
-			font-weight: bold;
-			width: 60px;
-			font-size: 14px;
-		}
-		
-		.value {
-			margin-right: 20px;
-			min-width: 80px;
-			font-size: 14px;
-		}
-	}
+  margin-bottom: 8px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+
+  .info-row {
+    line-height: 20px;
+
+    .label {
+      font-weight: bold;
+      width: 60px;
+      font-size: 14px;
+    }
+
+    .value {
+      margin-right: 20px;
+      min-width: 80px;
+      font-size: 14px;
+    }
+  }
 }
 
 .table-section {
-	margin-bottom: 4px;
-	flex: 1;
-	
-	.product-table {
-		width: 100%;
-		border-collapse: collapse;
-		border: 1px solid #000;
-		
-		th, td {
-			border: 1px solid #000;
-			padding: 6px;
-			text-align: center;
-			font-size: 14px;
-			line-height: 1.4;
-		}
-		
-		th {
-			font-weight: bold;
-		}
-		
-		.total-label {
-			text-align: right;
-			font-weight: bold;
-		}
-		
-		.total-value {
-			font-weight: bold;
-		}
-	}
+  margin-bottom: 4px;
+  flex: 1;
+
+  .product-table {
+    width: 100%;
+    border-collapse: collapse;
+    border: 1px solid #000;
+
+    th,
+    td {
+      border: 1px solid #000;
+      padding: 6px;
+      text-align: center;
+      font-size: 14px;
+      line-height: 1.4;
+    }
+
+    th {
+      font-weight: bold;
+    }
+
+    .total-label {
+      text-align: right;
+      font-weight: bold;
+    }
+
+    .total-value {
+      font-weight: bold;
+    }
+  }
 }
 
 .footer-section {
-	.footer-row {
-		display: flex;
-		margin-bottom: 3px;
-		line-height: 20px;
-		justify-content: space-between;
-		
-		.footer-item {
-			display: flex;
-			margin-right: 20px;
-			
-			.label {
-				font-weight: bold;
-				width: 80px;
-				font-size: 14px;
-			}
-			
-			.value {
-				min-width: 80px;
-				font-size: 14px;
-			}
-			
-			&.address-item {
-				.address-value {
-					min-width: 200px;
-				}
-			}
-		}
-	}
+  .footer-row {
+    display: flex;
+    margin-bottom: 3px;
+    line-height: 20px;
+    justify-content: space-between;
+
+    .footer-item {
+      display: flex;
+      margin-right: 20px;
+
+      .label {
+        font-weight: bold;
+        width: 80px;
+        font-size: 14px;
+      }
+
+      .value {
+        min-width: 80px;
+        font-size: 14px;
+      }
+
+      &.address-item {
+        .address-value {
+          min-width: 200px;
+        }
+      }
+    }
+  }
 }
 
 @media print {
-	.app-container {
-		display: none;
-	}
-	
-	.print-page {
-		box-shadow: none;
-		margin: 0;
-		padding: 10mm;
-		padding-left: 20mm;
-		page-break-inside: avoid;
-		page-break-after: always;
-	}
-	.print-page:last-child {
-		page-break-after: avoid;
-	}
+  .app-container {
+    display: none;
+  }
+
+  .print-page {
+    box-shadow: none;
+    margin: 0;
+    padding: 10mm;
+    padding-left: 20mm;
+    page-break-inside: avoid;
+    page-break-after: always;
+  }
+  .print-page:last-child {
+    page-break-after: avoid;
+  }
 }
 </style>
diff --git a/src/views/salesManagement/salesQuotation/index.vue b/src/views/salesManagement/salesQuotation/index.vue
index 5bd5cef..2237b72 100644
--- a/src/views/salesManagement/salesQuotation/index.vue
+++ b/src/views/salesManagement/salesQuotation/index.vue
@@ -16,8 +16,8 @@
           </el-input>
         </el-col>
         <el-col :span="8">
-          <el-select v-model="searchForm.customer" placeholder="璇烽�夋嫨瀹㈡埛" clearable>
-						<el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.customerName">
+          <el-select v-model="searchForm.customerId" placeholder="璇烽�夋嫨瀹㈡埛" clearable>
+						<el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id">
 							{{
 								item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
 							}}
@@ -70,8 +70,8 @@
         </el-table-column>
         <el-table-column label="鎿嶄綔" width="200" fixed="right" align="center">
           <template #default="scope">
-            <el-button link type="primary" @click="handleView(scope.row)">鏌ョ湅</el-button>
             <el-button link type="primary" @click="handleEdit(scope.row)" :disabled="!['寰呭鎵�','鎷掔粷'].includes(scope.row.status)">缂栬緫</el-button>
+            <el-button link type="primary" @click="handleView(scope.row)" style="color: #67C23A">鏌ョ湅</el-button>
             <el-button link type="danger" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
           </template>
         </el-table-column>
@@ -102,19 +102,15 @@
           <div class="form-content">
             <el-row :gutter="24">
               <el-col :span="12">
-                <el-form-item label="瀹㈡埛鍚嶇О" prop="customer">
-                  <el-select v-model="form.customer" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%" @change="handleCustomerChange" clearable>
-                    <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.customerName">
-                      {{
-                        item.customerName + "鈥斺��" + item.taxpayerIdentificationNumber
-                      }}
-                    </el-option>
+                <el-form-item label="瀹㈡埛鍚嶇О" prop="customerId">
+                  <el-select v-model="form.customerId" placeholder="璇烽�夋嫨瀹㈡埛" style="width: 100%" clearable filterable>
+                    <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id"></el-option>
                   </el-select>
                 </el-form-item>
               </el-col>
               <el-col :span="12">
                 <el-form-item label="涓氬姟鍛�" prop="salesperson">
-                  <el-select v-model="form.salesperson" placeholder="璇烽�夋嫨涓氬姟鍛�" style="width: 100%" clearable>
+                  <el-select v-model="form.salesperson" placeholder="璇烽�夋嫨涓氬姟鍛�" style="width: 100%" clearable filterable>
                     <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                       :value="item.nickName" />
                   </el-select>
@@ -153,62 +149,6 @@
               <el-col :span="12">
                 <el-form-item label="浠樻鏂瑰紡" prop="paymentMethod">
                   <el-input v-model="form.paymentMethod" placeholder="璇疯緭鍏ヤ粯娆炬柟寮�" clearable />
-                </el-form-item>
-              </el-col>
-            </el-row>
-          </div>
-        </el-card>
-
-        <!-- 瀹℃壒浜轰俊鎭� -->
-        <el-card class="form-card" shadow="hover">
-          <template #header>
-            <div class="card-header-wrapper">
-              <el-icon class="card-icon"><UserFilled /></el-icon>
-              <span class="card-title">瀹℃壒浜洪�夋嫨</span>
-              <el-button type="primary" size="small" @click="addApproverNode" class="header-btn">
-                <el-icon><Plus /></el-icon>
-                鏂板鑺傜偣
-              </el-button>
-            </div>
-          </template>
-          <div class="form-content">
-            <el-row>
-              <el-col :span="24">
-                <el-form-item>
-                  <div class="approver-nodes-container">
-                    <div
-                      v-for="(node, index) in approverNodes"
-                      :key="node.id"
-                      class="approver-node-item"
-                    >
-                      <div class="approver-node-label">
-                        <span class="node-step">{{ index + 1 }}</span>
-                        <span class="node-text">瀹℃壒浜�</span>
-                        <el-icon class="arrow-icon"><ArrowRight /></el-icon>
-                      </div>
-                      <el-select
-                        v-model="node.userId"
-                        placeholder="閫夋嫨浜哄憳"
-                        class="approver-select"
-                        clearable
-                      >
-                        <el-option
-                          v-for="user in userList"
-                          :key="user.userId"
-                          :label="user.nickName"
-                          :value="user.userId"
-                        />
-                      </el-select>
-                      <el-button
-                        type="danger"
-                        size="small"
-                        :icon="Delete"
-                        @click="removeApproverNode(index)"
-                        v-if="approverNodes.length > 1"
-                        class="remove-btn"
-                      >鍒犻櫎</el-button>
-                    </div>
-                  </div>
                 </el-form-item>
               </el-col>
             </el-row>
@@ -356,25 +296,26 @@
 <script setup>
 import { ref, reactive, computed, onMounted, markRaw, shallowRef } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
-import { Search, Document, UserFilled, Box, EditPen, Plus, ArrowRight, Delete } from '@element-plus/icons-vue'
+import { Search, Document, Box, EditPen, Plus } from '@element-plus/icons-vue'
 import Pagination from '@/components/PIMTable/Pagination.vue'
 import FormDialog from '@/components/Dialog/FormDialog.vue'
 import {getQuotationList,addQuotation,updateQuotation,deleteQuotation} from '@/api/salesManagement/salesQuotation.js'
-import {userListNoPage} from "@/api/system/user.js";
-import {customerList} from "@/api/salesManagement/salesLedger.js";
 import {modelList, productTreeList} from "@/api/basicData/product.js";
+import {listCustomer} from "@/api/basicData/customer.js";
+import { userListNoPage } from "@/api/system/user.js";
 
 // 鍝嶅簲寮忔暟鎹�
 const loading = ref(false)
 const searchForm = reactive({
   quotationNo: '',
-  customer: '',
+  customerId: '',
   status: ''
 })
 
 const quotationList = ref([])
+const userList = ref([])
 const productOptions = ref([]);
-const modelOptions = ref([]);
+const modelOptions  = ref([]);
 const pagination = reactive({
   total: 3,
   currentPage: 1,
@@ -386,6 +327,7 @@
 const dialogTitle = ref('鏂板鎶ヤ环')
 const form = reactive({
   quotationNo: '',
+  customerId: undefined,
   customer: '',
   salesperson: '',
   quotationDate: '',
@@ -426,29 +368,12 @@
   })
   return r
 })
-const userList = ref([]);
 const customerOption = ref([]);
-
-// 瀹℃壒浜鸿妭鐐圭浉鍏�
-const approverNodes = ref([
-  { id: 1, userId: null }
-])
-let nextApproverId = 2
 
 const isEdit = ref(false)
 const editId = ref(null)
 const currentQuotation = ref({})
 const formRef = ref()
-
-// 娣诲姞瀹℃壒浜鸿妭鐐�
-function addApproverNode() {
-  approverNodes.value.push({ id: nextApproverId++, userId: null })
-}
-
-// 鍒犻櫎瀹℃壒浜鸿妭鐐�
-function removeApproverNode(index) {
-  approverNodes.value.splice(index, 1)
-}
 
 // 璁$畻灞炴��
 const filteredList = computed(() => {
@@ -480,26 +405,16 @@
   dialogTitle.value = '鏂板鎶ヤ环'
   isEdit.value = false
   resetForm()
-  // 閲嶇疆瀹℃壒浜鸿妭鐐�
-  approverNodes.value = [{ id: 1, userId: null }]
-  nextApproverId = 2
   dialogVisible.value = true
-	let userLists = await userListNoPage();
-	// 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
-	userList.value = (userLists.data || []).map(item => ({
-    userId: item.userId,
-    nickName: item.nickName || '',
-    userName: item.userName || ''
-  }));
-	getProductOptions();
-	customerList().then((res) => {
-		// 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
-		customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
-      id: item.id,
-      customerName: item.customerName || '',
-      taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || ''
-    }))
-	});
+  getProductOptions();
+  fetchCustomerOptions()
+}
+
+const fetchCustomerOptions = () => {
+  if (customerOption.value.length > 0) return
+  listCustomer({current: -1,size:-1, type: 0}).then((res) => {
+    customerOption.value = res.data.records;
+  });
 }
 const getProductOptions = () => {
 	// 杩斿洖 Promise锛屼究浜庣紪杈戞椂 await 纭繚鑳藉弽鏄�
@@ -627,10 +542,12 @@
   form.id = row.id || form.id || null
   // 鍏堝姞杞戒骇鍝佹爲鏁版嵁锛屽惁鍒� el-tree-select 鏃犳硶鍙嶆樉浜у搧鍚嶇О
   await getProductOptions()
+  await fetchCustomerOptions()
 
   // 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
   form.quotationNo = row.quotationNo || ''
   form.customer = row.customer || ''
+  form.customerId = row.customerId || undefined
   form.salesperson = row.salesperson || ''
   form.quotationDate = row.quotationDate || ''
   form.validDate = row.validDate || ''
@@ -683,28 +600,7 @@
   form.discountRate = row.discountRate || 0
   form.discountAmount = row.discountAmount || 0
   form.totalAmount = row.totalAmount || 0
-  
-  // 鍙嶆樉瀹℃壒浜�
-  if (row.approveUserIds) {
-    const userIds = row.approveUserIds.split(',')
-    approverNodes.value = userIds.map((userId, idx) => ({
-      id: idx + 1,
-      userId: parseInt(userId.trim())
-    }))
-    nextApproverId = userIds.length + 1
-  } else {
-    approverNodes.value = [{ id: 1, userId: null }]
-    nextApproverId = 2
-  }
-  
-  // 鍔犺浇鐢ㄦ埛鍒楄〃
-  let userLists = await userListNoPage();
-  userList.value = (userLists.data || []).map(item => ({
-    userId: item.userId,
-    nickName: item.nickName || '',
-    userName: item.userName || ''
-  }));
-  
+
   dialogVisible.value = true
 }
 
@@ -783,10 +679,6 @@
   form.totalAmount = form.subtotal + form.freight + form.otherFee - form.discountAmount
 }
 
-const handleCustomerChange = () => {
-  // 鍙互鏍规嵁瀹㈡埛淇℃伅鑷姩濉厖涓�浜涢粯璁ゅ��
-}
-
 const handleSubmit = () => {
   formRef.value.validate((valid) => {
     if (valid) {
@@ -795,22 +687,13 @@
         return
       }
 
-      // 瀹℃壒浜哄繀濉牎楠�
-      const hasEmptyApprover = approverNodes.value.some(node => !node.userId)
-      if (hasEmptyApprover) {
-        ElMessage.error('璇蜂负鎵�鏈夊鎵硅妭鐐归�夋嫨瀹℃壒浜猴紒')
-        return
-      }
-      
-      // 鏀堕泦鎵�鏈夎妭鐐圭殑瀹℃壒浜篿d
-      form.approveUserIds = approverNodes.value.map(node => node.userId).join(',')
-      
       // 璁$畻鎵�鏈変骇鍝佺殑鍗曚环鎬诲拰
       form.totalAmount = form.products.reduce((sum, product) => {
         const price = Number(product.unitPrice) || 0
         return sum + price
       }, 0)
-      
+
+      form.customer = customerOption.value.find(item => item.id === form.customerId)?.customerName || ''
       if (isEdit.value) {
         // 缂栬緫
         const index = quotationList.value.findIndex(item => item.id === editId.value)
@@ -834,7 +717,7 @@
           }
         })
       }
-      
+
     }
   })
 }
@@ -860,6 +743,7 @@
         id: item.id,
         quotationNo: item.quotationNo || '',
         customer: item.customer || '',
+        customerId: item.customerId || undefined,
         salesperson: item.salesperson || '',
         quotationDate: item.quotationDate || '',
         validDate: item.validDate || '',
@@ -888,18 +772,25 @@
       pagination.total = res.data.total
     }
   })
-	customerList().then((res) => {
-		// 鍙鍒堕渶瑕佺殑瀛楁锛岄伩鍏嶅皢缁勪欢寮曠敤鏀惧叆鍝嶅簲寮忓璞�
-		customerOption.value = (Array.isArray(res) ? res : []).map(item => ({
-      id: item.id,
-      customerName: item.customerName || '',
-      taxpayerIdentificationNumber: item.taxpayerIdentificationNumber || ''
-    }))
-	});
+	// customerList().then((res) => {
+	// 	customerOption.value = res;
+	// });
+}
+
+const getUserList = async () => {
+  try {
+    const res = await userListNoPage()
+    userList.value = Array.isArray(res?.data) ? res.data : []
+  } catch (error) {
+    userList.value = []
+    ElMessage.error('鍔犺浇涓氬姟鍛樺垪琛ㄥけ璐�')
+  }
 }
 
 onMounted(()=>{
+  getUserList()
   handleSearch()
+  fetchCustomerOptions()
 })
 </script>
 
@@ -991,71 +882,6 @@
   }
 }
 
-.approver-nodes-container {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 24px;
-  padding: 12px 0;
-}
-
-.approver-node-item {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  gap: 12px;
-  padding: 16px;
-  background: #f8f9fa;
-  border-radius: 8px;
-  border: 1px solid #e4e7ed;
-  transition: all 0.3s ease;
-  min-width: 180px;
-  
-  &:hover {
-    border-color: #409eff;
-    background: #f0f7ff;
-    box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
-  }
-}
-
-.approver-node-label {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  font-size: 14px;
-  color: #606266;
-  
-  .node-step {
-    display: inline-flex;
-    align-items: center;
-    justify-content: center;
-    width: 24px;
-    height: 24px;
-    background: #409eff;
-    color: #fff;
-    border-radius: 50%;
-    font-size: 12px;
-    font-weight: 600;
-  }
-  
-  .node-text {
-    font-weight: 500;
-  }
-  
-  .arrow-icon {
-    color: #909399;
-    font-size: 14px;
-  }
-}
-
-.approver-select {
-  width: 100%;
-  min-width: 150px;
-}
-
-.remove-btn {
-  margin-top: 4px;
-}
-
 .product-table {
   :deep(.el-table__header) {
     background-color: #f5f7fa;
@@ -1082,14 +908,4 @@
   text-align: right;
 }
 
-// 鍝嶅簲寮忎紭鍖�
-@media (max-width: 1200px) {
-  .approver-nodes-container {
-    gap: 16px;
-  }
-  
-  .approver-node-item {
-    min-width: 160px;
-  }
-}
 </style>
diff --git a/src/views/salesManagement/strategyControl/index.vue b/src/views/salesManagement/strategyControl/index.vue
index 629d255..dbdae38 100644
--- a/src/views/salesManagement/strategyControl/index.vue
+++ b/src/views/salesManagement/strategyControl/index.vue
@@ -75,7 +75,7 @@
             </el-table-column>
             <el-table-column label="鎿嶄綔" width="200" fixed="right" align="center">
               <template #default="scope">
-                <el-button link type="primary" @click="handleViewPriceStrategy(scope.row)">鏌ョ湅</el-button>
+                <el-button link type="primary" @click="handleViewPriceStrategy(scope.row)" style="color: #67C23A">鏌ョ湅</el-button>
                 <el-button link type="primary" @click="handleEditPriceStrategy(scope.row)">缂栬緫</el-button>
                 <el-button link type="danger" @click="handleDeletePriceStrategy(scope.row)">鍒犻櫎</el-button>
               </template>
@@ -254,7 +254,7 @@
             </el-table-column>
             <el-table-column label="鎿嶄綔" width="120" fixed="right" align="center">
               <template #default="scope">
-                <el-button link type="primary" @click="handleViewContract(scope.row)">鏌ョ湅璇︽儏</el-button>
+                <el-button link type="primary" @click="handleViewContract(scope.row)" style="color: #67C23A">鏌ョ湅璇︽儏</el-button>
               </template>
             </el-table-column>
           </el-table>
diff --git a/src/views/system/appVersion/index.vue b/src/views/system/appVersion/index.vue
new file mode 100644
index 0000000..6ff9ae6
--- /dev/null
+++ b/src/views/system/appVersion/index.vue
@@ -0,0 +1,270 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button type="primary" plain icon="Upload" @click="openUploadDialog">涓婁紶APK</el-button>
+      </el-col>
+    </el-row>
+
+    <el-table v-loading="loading" :data="versionList">
+      <el-table-column label="ID" prop="id" align="center" width="80"/>
+      <el-table-column label="搴旂敤鍚嶇О" prop="name" align="center" min-width="150"/>
+      <el-table-column label="鐗堟湰鍙�" prop="version" align="center" width="120"/>
+      <el-table-column label="鍒涘缓鏃堕棿" prop="createTime" align="center" width="170">
+        <template #default="scope">
+          <span>{{ parseTime(scope.row.createTime, "{y}-{m}-{d} {h}:{i}:{s}") }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="鏇存柊鏃堕棿" prop="updateTime" align="center" width="170">
+        <template #default="scope">
+          <span>{{ parseTime(scope.row.updateTime, "{y}-{m}-{d} {h}:{i}:{s}") }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="鍒涘缓浜�" prop="createUser" align="center" width="100"/>
+      <el-table-column label="鎿嶄綔" align="center" width="180" class-name="small-padding fixed-width">
+        <template #default="scope">
+          <el-button link type="primary" @click="downloadAttachment(scope.row)">涓嬭浇</el-button>
+          <el-button link type="success" @click="openQrDialog(scope.row)">鎵爜涓嬭浇</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+        v-show="total > 0"
+        :total="total"
+        v-model:page="queryParams.current"
+        v-model:limit="queryParams.size"
+        @pagination="getList"
+    />
+
+    <el-dialog title="涓婁紶APK" v-model="uploadOpen" width="560px" append-to-body @close="resetUploadForm">
+      <el-form ref="uploadRef" :model="uploadForm" :rules="uploadRules" label-width="90px">
+        <el-form-item label="搴旂敤鍚嶇О" prop="name">
+          <el-input v-model="uploadForm.name" placeholder="璇疯緭鍏ュ簲鐢ㄥ悕绉�"/>
+        </el-form-item>
+        <el-form-item label="鐗堟湰鍙�" prop="version">
+          <el-input v-model="uploadForm.version" placeholder="璇疯緭鍏ョ増鏈彿"/>
+        </el-form-item>
+        <el-form-item label="APK鏂囦欢" prop="storageBlobDTOList">
+          <FileUpload v-model:file-list="uploadForm.storageBlobDTOList" :limit="1" :file-type="['apk']"
+                      :file-size="200"/>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" :loading="uploading" @click="submitUpload">纭� 瀹�</el-button>
+          <el-button @click="uploadOpen = false">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <el-dialog
+      v-model="qrOpen"
+      title="鎵爜涓嬭浇"
+      width="460px"
+      append-to-body
+      class="download-qr-dialog"
+    >
+      <div class="download-qr-content">
+        <div class="app-meta-card">
+          <div class="meta-row">
+            <span class="meta-label">搴旂敤鍚嶇О</span>
+            <span class="meta-value">{{ qrCurrentRow?.name || "-" }}</span>
+          </div>
+          <div class="meta-row">
+            <span class="meta-label">鐗堟湰缂栧彿</span>
+            <span class="meta-value">{{ qrCurrentRow?.version || "-" }}</span>
+          </div>
+        </div>
+        <div class="qr-box">
+          <img v-if="qrCodeUrl" :src="qrCodeUrl" alt="download qr code" class="qr-image" />
+          <div class="qr-tip">璇蜂娇鐢ㄦ墜鏈烘壂鐮佷笅杞藉簲鐢�</div>
+        </div>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="qrOpen = false">鍏� 闂�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="SystemAppVersion">
+import {listAppVersion, add} from "@/api/system/appVersion";
+import FileUpload from "@/components/AttachmentUpload/file/index.vue";
+import QRCode from "qrcode";
+
+const {proxy} = getCurrentInstance();
+
+const loading = ref(false);
+const versionList = ref([]);
+const total = ref(0);
+
+const queryParams = reactive({
+  current: 1,
+  size: 10,
+});
+
+const uploadOpen = ref(false);
+const uploading = ref(false);
+const qrOpen = ref(false);
+const qrCodeUrl = ref("");
+const qrCurrentRow = ref(null);
+const uploadForm = reactive({
+  name: "",
+  version: "",
+  storageBlobDTOList: null,
+});
+
+const uploadRules = {
+  name: [{required: true, message: "璇疯緭鍏ュ簲鐢ㄥ悕绉�", trigger: "blur"}],
+  version: [{required: true, message: "璇疯緭鍏ョ増鏈彿", trigger: "blur"}],
+  storageBlobDTOList: [{required: true, message: "璇蜂笂浼燗PK鏂囦欢", trigger: "change"}],
+};
+
+function normalizeListResp(res) {
+  const data = res?.data || {};
+  const records = data.records || res?.rows || [];
+  const totalNum = Number(data.total ?? res?.total ?? 0);
+  return {
+    records: Array.isArray(records) ? records : [],
+    total: Number.isNaN(totalNum) ? 0 : totalNum,
+  };
+}
+
+function getList() {
+  loading.value = true;
+  listAppVersion(queryParams)
+      .then(res => {
+        const result = normalizeListResp(res);
+        versionList.value = result.records;
+        total.value = result.total;
+      })
+      .finally(() => {
+        loading.value = false;
+      });
+}
+
+function openUploadDialog() {
+  resetUploadForm();
+  uploadOpen.value = true;
+}
+
+function resetUploadForm() {
+  uploadForm.name = "";
+  uploadForm.version = "";
+  uploadForm.storageBlobDTOList = null;
+  proxy.resetForm("uploadRef");
+}
+
+
+function downloadAttachment(row) {
+  window.open(row.downloadURL, "_blank");
+}
+
+async function openQrDialog(row) {
+  if (!row?.downloadURL) {
+    proxy.$modal.msgError("褰撳墠璁板綍缂哄皯涓嬭浇鍦板潃锛屾棤娉曠敓鎴愪簩缁寸爜");
+    return;
+  }
+  try {
+    qrCodeUrl.value = await QRCode.toDataURL(row.downloadURL, {
+      width: 220,
+      margin: 2,
+      errorCorrectionLevel: "M",
+      color: {
+        dark: "#1f2937",
+        light: "#ffffff",
+      },
+    });
+    qrCurrentRow.value = row;
+    qrOpen.value = true;
+  } catch (error) {
+    proxy.$modal.msgError("浜岀淮鐮佺敓鎴愬け璐ワ紝璇风◢鍚庨噸璇�");
+  }
+}
+
+function submitUpload() {
+  proxy.$refs.uploadRef.validate(valid => {
+    if (!valid) return;
+    uploading.value = true;
+    add(uploadForm)
+        .then(() => {
+          proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
+          uploadOpen.value = false;
+          getList();
+        })
+        .finally(() => {
+          uploading.value = false;
+        });
+  });
+}
+
+onMounted(() => {
+  getList();
+});
+</script>
+
+<style scoped>
+.download-qr-content {
+  padding: 4px 4px 8px;
+}
+
+.app-meta-card {
+  border-radius: 10px;
+  padding: 12px 14px;
+  margin-bottom: 16px;
+  background: linear-gradient(135deg, #f0f7ff 0%, #ecfdf5 100%);
+  border: 1px solid #dbeafe;
+}
+
+.meta-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  line-height: 26px;
+}
+
+.meta-row + .meta-row {
+  margin-top: 6px;
+}
+
+.meta-label {
+  font-size: 13px;
+  color: #64748b;
+}
+
+.meta-value {
+  max-width: 260px;
+  font-size: 14px;
+  color: #0f172a;
+  font-weight: 600;
+  word-break: break-all;
+  text-align: right;
+}
+
+.qr-box {
+  border: 1px solid #e2e8f0;
+  border-radius: 12px;
+  padding: 18px 12px 14px;
+  text-align: center;
+  background: #ffffff;
+  box-shadow: 0 6px 20px rgba(15, 23, 42, 0.06);
+}
+
+.qr-image {
+  width: 220px;
+  height: 220px;
+  border-radius: 8px;
+  border: 1px solid #e5e7eb;
+  padding: 8px;
+  background: #fff;
+}
+
+.qr-tip {
+  margin-top: 10px;
+  font-size: 13px;
+  color: #64748b;
+}
+</style>
diff --git a/src/views/system/role/index.vue b/src/views/system/role/index.vue
index d960674..85ecf28 100644
--- a/src/views/system/role/index.vue
+++ b/src/views/system/role/index.vue
@@ -273,7 +273,7 @@
 /** 鏁版嵁鑼冨洿閫夐」*/
 const dataScopeOptions = ref([
   { value: "1", label: "鍏ㄩ儴鏁版嵁鏉冮檺" },
-  { value: "2", label: "鑷畾鏁版嵁鏉冮檺" },
+  // { value: "2", label: "鑷畾鏁版嵁鏉冮檺" },
   { value: "3", label: "鏈儴闂ㄦ暟鎹潈闄�" },
   { value: "4", label: "鏈儴闂ㄥ強浠ヤ笅鏁版嵁鏉冮檺" },
   { value: "5", label: "浠呮湰浜烘暟鎹潈闄�" }
diff --git a/src/views/systemArchitecture/index.vue b/src/views/systemArchitecture/index.vue
new file mode 100644
index 0000000..1eb37bb
--- /dev/null
+++ b/src/views/systemArchitecture/index.vue
@@ -0,0 +1,436 @@
+<template>
+  <div class="architecture-page">
+    <section ref="canvasRef" class="canvas">
+      <svg
+        v-if="linkPath"
+        class="link-overlay"
+        :width="svgSize.width"
+        :height="svgSize.height"
+        aria-hidden="true"
+      >
+        <defs>
+          <marker
+            id="erp-link-arrow"
+            markerWidth="10"
+            markerHeight="10"
+            refX="7"
+            refY="3.5"
+            orient="auto"
+            markerUnits="strokeWidth"
+          >
+            <path d="M0,0 L0,7 L7,3.5 z" fill="#94a3b8" />
+          </marker>
+        </defs>
+        <path
+          :d="linkPath"
+          class="link-overlay__path"
+          marker-end="url(#erp-link-arrow)"
+        />
+      </svg>
+
+      <section
+        v-for="row in architectureRows"
+        :key="row.key"
+        class="lane"
+      >
+        <aside class="lane__aside">
+          <svg-icon :icon-class="row.icon" class="lane__icon" />
+          <h2>{{ row.label }}</h2>
+          <span class="lane__arrow"></span>
+        </aside>
+
+        <div class="lane__flow">
+          <div
+            v-for="(item, index) in row.items"
+            :key="item.name"
+            class="node-wrap"
+          >
+            <article
+              class="node"
+              :class="{ 'node--accent': item.accent }"
+              :ref="setNodeRef(item)"
+            >
+              <span class="node__mark">
+                <span class="node__cap"></span>
+                <span class="node__base"></span>
+                <svg-icon :icon-class="item.icon" class="node__icon" />
+              </span>
+              <h3>{{ item.name }}</h3>
+            </article>
+            <span v-if="index < row.items.length - 1" class="flow-arrow"></span>
+          </div>
+        </div>
+      </section>
+    </section>
+  </div>
+</template>
+
+<script setup>
+import { nextTick, onBeforeUnmount, onMounted, onUpdated, ref } from 'vue'
+
+const architectureRows = [
+  {
+    key: 'basic',
+    label: '鍩虹閰嶇疆',
+    icon: 'system',
+    items: [
+      { name: '瑙掕壊鐢ㄦ埛绠$悊', icon: 'user' },
+      { name: '浜у搧缁存姢', icon: 'monitor' },
+      { name: '瀹℃壒绠$悊', icon: 'tree', accent: true }
+    ]
+  },
+  {
+    key: 'sale',
+    label: '閿�鍞�',
+    icon: 'chart',
+    items: [
+      { name: '瀹㈡埛妗f', icon: 'peoples' },
+      { name: '閿�鍞姤浠�', icon: 'form' },
+      { name: '閿�鍞彴璐�', icon: 'table' },
+      { name: '鍙戣揣鍙拌处', icon: 'clipboard', accent: true, linkSource: true },
+      { name: '閿�鍞��璐�', icon: 'nested' },
+      { name: '瀹㈡埛寰�鏉�', icon: 'money' },
+      { name: '鎸囨爣缁熻', icon: 'chart' }
+    ]
+  },
+  {
+    key: 'purchase',
+    label: '閲囪喘',
+    icon: 'shopping',
+    items: [
+      { name: '渚涘簲鍟嗘。妗�', icon: 'people' },
+      { name: '閲囪喘鍙拌处', icon: 'table', accent: true },
+      { name: '閲囪喘閫�璐�', icon: 'nested' },
+      { name: '渚涘簲鍟嗗線鏉�', icon: 'money' },
+      { name: '閲囪喘鎶ヨ〃', icon: 'chart' }
+    ]
+  },
+  {
+    key: 'produce',
+    label: '鐢熶骇',
+    icon: 'build',
+    items: [
+      { name: '宸ュ簭', icon: 'tree' },
+      { name: 'BOM', icon: 'list' },
+      { name: '宸ヨ壓璺嚎', icon: 'guide' },
+      { name: '鐢熶骇璁㈠崟', icon: 'form' },
+      { name: '鐢熶骇鎺掍骇', icon: 'date' },
+      { name: '鐢熶骇鎶ュ伐', icon: 'edit' },
+      { name: '鎶ュ伐鍙拌处', icon: 'clipboard' },
+      { name: '鐢熶骇鏍哥畻', icon: 'money', accent: true }
+    ]
+  },
+  {
+    key: 'store',
+    label: '浠撳偍鐗╂祦',
+    icon: 'redis',
+    items: [
+      { name: '鍏ュ簱绠$悊', icon: 'download', accent: true },
+      { name: '鍑哄簱绠$悊', icon: 'upload', accent: true, linkTarget: true },
+      { name: '搴撳瓨绠$悊', icon: 'redis-list' }
+    ]
+  }
+]
+
+const canvasRef = ref(null)
+const sourceNodeRef = ref(null)
+const targetNodeRef = ref(null)
+const linkPath = ref('')
+const svgSize = ref({ width: 0, height: 0 })
+let resizeObserver = null
+
+function setNodeRef(item) {
+  return (el) => {
+    if (item.linkSource) sourceNodeRef.value = el
+    if (item.linkTarget) targetNodeRef.value = el
+  }
+}
+
+function resetLine() {
+  linkPath.value = ''
+}
+
+function updateLinkLine() {
+  if (!canvasRef.value || !sourceNodeRef.value || !targetNodeRef.value || window.innerWidth <= 768) {
+    resetLine()
+    return
+  }
+
+  const canvasRect = canvasRef.value.getBoundingClientRect()
+  const sourceRect = sourceNodeRef.value.getBoundingClientRect()
+  const targetRect = targetNodeRef.value.getBoundingClientRect()
+
+  svgSize.value = {
+    width: Math.ceil(canvasRef.value.scrollWidth),
+    height: Math.ceil(canvasRef.value.scrollHeight)
+  }
+
+  const startX = sourceRect.left + sourceRect.width / 2 - canvasRect.left
+  const startY = sourceRect.bottom - canvasRect.top + 2
+  const endX = targetRect.left + targetRect.width / 2 - canvasRect.left
+  const endY = targetRect.top - canvasRect.top - 6
+  const middleY = startY + Math.max(24, (endY - startY) / 2)
+
+  linkPath.value = `M ${startX} ${startY} L ${startX} ${middleY} L ${endX} ${middleY} L ${endX} ${endY}`
+}
+
+function handleResize() {
+  requestAnimationFrame(() => {
+    requestAnimationFrame(updateLinkLine)
+  })
+}
+
+onMounted(async () => {
+  await nextTick()
+  handleResize()
+  window.addEventListener('resize', handleResize)
+  if (window.ResizeObserver && canvasRef.value) {
+    resizeObserver = new ResizeObserver(handleResize)
+    resizeObserver.observe(canvasRef.value)
+  }
+})
+
+onUpdated(() => {
+  nextTick(handleResize)
+})
+
+onBeforeUnmount(() => {
+  window.removeEventListener('resize', handleResize)
+  if (resizeObserver) resizeObserver.disconnect()
+})
+</script>
+
+<style scoped>
+.architecture-page {
+  min-height: calc(100vh - 84px);
+  padding: 18px;
+  background: #f6f7f9;
+}
+
+.canvas {
+  position: relative;
+  display: grid;
+  gap: 10px;
+}
+
+.lane {
+  display: grid;
+  grid-template-columns: 94px 1fr;
+  gap: 10px;
+  align-items: stretch;
+}
+
+.lane__aside {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 8px;
+  padding: 8px 6px;
+}
+
+.lane__icon {
+  font-size: 18px;
+  color: #2563eb;
+  flex-shrink: 0;
+}
+
+.lane__aside h2 {
+  margin: 0;
+  width: 34px;
+  font-size: 12px;
+  line-height: 1.15;
+  font-weight: 600;
+  color: #1f2937;
+  text-align: center;
+}
+
+.lane__arrow {
+  position: relative;
+  width: 16px;
+  height: 1px;
+  background: #cbd5e1;
+  flex-shrink: 0;
+}
+
+.lane__arrow::after {
+  content: '';
+  position: absolute;
+  top: 50%;
+  right: -1px;
+  width: 6px;
+  height: 6px;
+  border-top: 1px solid #cbd5e1;
+  border-right: 1px solid #cbd5e1;
+  transform: translateY(-50%) rotate(45deg);
+}
+
+.lane__flow {
+  position: relative;
+  z-index: 2;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 8px;
+  padding: 12px 14px;
+  background: #f1f3f5;
+}
+
+.node-wrap {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  position: relative;
+}
+
+.node {
+  width: 100px;
+  min-height: 72px;
+  padding: 4px 2px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  gap: 6px;
+}
+
+.node__mark {
+  position: relative;
+  width: 34px;
+  height: 28px;
+  display: flex;
+  align-items: flex-start;
+  justify-content: center;
+}
+
+.node__cap {
+  position: absolute;
+  top: 0;
+  width: 18px;
+  height: 14px;
+  border-radius: 4px 4px 3px 3px;
+  background: linear-gradient(180deg, #60a5fa, #2563eb);
+  box-shadow: 0 3px 6px rgba(37, 99, 235, 0.16);
+}
+
+.node__base {
+  position: absolute;
+  bottom: 0;
+  width: 30px;
+  height: 12px;
+  border-radius: 4px;
+  background: linear-gradient(180deg, #ffffff, #e8edf3);
+  border: 1px solid #d7dee7;
+}
+
+.node__icon {
+  position: relative;
+  z-index: 1;
+  margin-top: 2px;
+  font-size: 15px;
+  color: #ffffff;
+}
+
+.node--accent .node__cap {
+  background: linear-gradient(180deg, #2dd4bf, #0f766e);
+  box-shadow: 0 3px 6px rgba(15, 118, 110, 0.16);
+}
+
+.node h3 {
+  margin: 0;
+  font-size: 12px;
+  line-height: 1.3;
+  font-weight: 600;
+  color: #111827;
+  text-align: center;
+}
+
+.flow-arrow {
+  width: 18px;
+  height: 1px;
+  background: #b6c1ce;
+  position: relative;
+  flex-shrink: 0;
+}
+
+.flow-arrow::after {
+  content: '';
+  position: absolute;
+  top: 50%;
+  right: -1px;
+  width: 6px;
+  height: 6px;
+  border-top: 1px solid #94a3b8;
+  border-right: 1px solid #94a3b8;
+  transform: translateY(-50%) rotate(45deg);
+}
+
+.link-overlay {
+  position: absolute;
+  inset: 0;
+  z-index: 20;
+  pointer-events: none;
+  overflow: visible;
+}
+
+.link-overlay__path {
+  fill: none;
+  stroke: #94a3b8;
+  stroke-width: 1;
+  stroke-dasharray: 4 4;
+  stroke-linecap: round;
+  stroke-linejoin: round;
+}
+
+@media (max-width: 1180px) {
+  .lane {
+    grid-template-columns: 1fr;
+    gap: 6px;
+  }
+
+  .lane__aside {
+    justify-content: flex-start;
+  }
+}
+
+@media (max-width: 768px) {
+  .architecture-page {
+    padding: 12px;
+  }
+
+  .lane__flow {
+    display: grid;
+    gap: 8px;
+    padding: 12px;
+  }
+
+  .node-wrap {
+    display: grid;
+    gap: 8px;
+  }
+
+  .node {
+    width: 100%;
+    min-height: 52px;
+    flex-direction: row;
+    justify-content: flex-start;
+    gap: 10px;
+  }
+
+  .node h3 {
+    text-align: left;
+  }
+
+  .flow-arrow {
+    width: 1px;
+    height: 16px;
+    margin: 0 auto;
+  }
+
+  .flow-arrow::after {
+    top: auto;
+    bottom: -1px;
+    right: 50%;
+    transform: translateX(50%) rotate(135deg);
+  }
+}
+</style>
diff --git a/src/views/tool/build/CodeTypeDialog.vue b/src/views/tool/build/CodeTypeDialog.vue
index de0beb7..1a75789 100644
--- a/src/views/tool/build/CodeTypeDialog.vue
+++ b/src/views/tool/build/CodeTypeDialog.vue
@@ -14,8 +14,8 @@
     </el-form>
 
     <template #footer>
-      <el-button @click="onClose">鍙栨秷</el-button>
       <el-button type="primary" @click="handelConfirm">纭畾</el-button>
+      <el-button @click="onClose">鍙栨秷</el-button>
     </template>
   </el-dialog>
 </template>
diff --git a/vite.config.js b/vite.config.js
index dc687a8..03311be 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -8,11 +8,11 @@
   const { VITE_APP_ENV } = env;
   const baseUrl =
       env.VITE_APP_ENV === "development"
-          ? "http://1.15.17.182:9003"
+          ? "http://1.15.17.182:9048"
           : env.VITE_BASE_API;
   const javaUrl =
       env.VITE_APP_ENV === "development"
-          ? "http://1.15.17.182:9002"
+          ? "http://1.15.17.182:9049"
           : env.VITE_JAVA_API;
   return {
     define:{

--
Gitblit v1.9.3