From 3ce715a4e66955eac86dcd2f5c8afcfdf8becb6d Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期四, 29 十月 2026 16:46:47 +0800
Subject: [PATCH] Merge origin/dev_项目管理 into dev_项目管理

---
 src/views/collaborativeApproval/notificationManagement/summary/index.vue                          |    6 
 src/views/index.vue                                                                               |    4 
 src/views/reportAnalysis/safetyDashboard/index.vue                                                |  256 +++++
 src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js |   10 
 src/views/qualityManagement/rawMaterialInspection/index.vue                                       |    5 
 src/views/procurementManagement/thePaymentLedger/index.vue                                        |    3 
 src/views/personnelManagement/contractManagement/index.vue                                        |   10 
 src/views/reportAnalysis/safetyDashboard/components/center-top.vue                                |  432 ++++++++
 src/views/personnelManagement/employeeRecord/components/EducationWorkSection.vue                  |    3 
 src/views/safeProduction/safetyTrainingAssessment/detail.vue                                      |    7 
 src/views/reportAnalysis/safetyDashboard/components/center-bottom.vue                             |  217 ++++
 multiple/assets/favicon/ZXDZfavicon.ico                                                           |    0 
 multiple/assets/logo/JMGCLogo.png                                                                 |    0 
 multiple/assets/favicon/OLDXCLfavicon.ico                                                         |    0 
 src/views/equipmentManagement/upkeep/index.vue                                                    |    6 
 src/views/qualityManagement/processInspection/components/formDia.vue                              |    1 
 src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue                      |    6 
 multiple/assets/favicon/ZGLTico.ico                                                               |    0 
 src/views/salesManagement/strategyControl/index.vue                                               |    2 
 src/views/productionPlan/productionPlan/components/PIMTable.vue                                   |   11 
 multiple/assets/favicon/JMGCfavicon.ico                                                           |    0 
 src/assets/logo/SHYY.png                                                                          |    0 
 src/views/reportAnalysis/safetyDashboard/components/left-bottom.vue                               |  206 ++++
 src/views/financialManagement/payable/reconciliation.vue                                          |   54 
 src/views/financialManagement/payable/purchaseIn.vue                                              |    2 
 src/views/reportAnalysis/safetyDashboard/components/right-bottom.vue                              |  275 +++++
 src/main.js                                                                                       |   17 
 src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue                      |    6 
 src/views/qualityManagement/rawMaterialInspection/components/formDia.vue                          |    1 
 src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceListSearch.js      |    1 
 src/utils/index.js                                                                                |    9 
 src/views/reportAnalysis/safetyDashboard/components/left-top.vue                                  |  241 ++++
 src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue                        |    6 
 multiple/config.json                                                                              |  410 +++++++
 src/views/productionManagement/workOrder/index.vue                                                |   10 
 src/views/system/appVersion/index.vue                                                             |    4 
 src/utils/ruoyi.js                                                                                |    2 
 multiple/assets/favicon/THZSfavicon.ico                                                           |    0 
 src/views/salesManagement/salesLedger/index.vue                                                   |    2 
 src/api/reportAnalysis/safetyDashboard.js                                                         |   60 +
 src/views/officeProcessAutomation/HrManage/regular-apply/index.vue                                |    8 
 src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalModuleRegistry.js          |    8 
 multiple/assets/favicon/YXHJfavicon.ico                                                           |    0 
 multiple/assets/logo/ZXDZLogo.png                                                                 |    0 
 multiple/assets/logo/THZSLogo.png                                                                 |    0 
 src/views/financialManagement/receivable/reconciliation.vue                                       |   49 
 src/views/inventoryManagement/stockManagement/Record.vue                                          |    6 
 src/views/inventoryManagement/stockManagement/Unqualified.vue                                     |    6 
 src/views/officeProcessAutomation/HrManage/staff-archive/components/EducationWorkSection.vue      |    3 
 src/views/qualityManagement/afterSalesTraceability/index.vue                                      |    2 
 src/views/equipmentManagement/spareParts/index.vue                                                |    7 
 src/views/officeProcessAutomation/AttendManage/out-apply/index.vue                                |  174 +++
 src/views/qualityManagement/finalInspection/components/formDia.vue                                |    1 
 src/components/PIMTable/PIMTable.vue                                                              |   11 
 src/views/inventoryManagement/stockManagement/Qualified.vue                                       |    6 
 src/views/equipmentManagement/inspectionManagement/index.vue                                      |    2 
 src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js            |    2 
 multiple/assets/logo/YXHJLogo.png                                                                 |    0 
 multiple/assets/logo/ZGLTLogo.png                                                                 |    0 
 src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js      |    9 
 src/utils/dateDisplay.js                                                                          |   32 
 src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue                                |    6 
 src/views/reportAnalysis/safetyDashboard/components/right-top.vue                                 |  275 +++++
 multiple/assets/logo/SHYY.png                                                                     |    0 
 multiple/assets/logo/OLDXCLLogo.png                                                               |    0 
 src/views/personnelManagement/monthlyStatistics/index.vue                                         |  103 ++
 src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue                            |    2 
 src/views/officeProcessAutomation/ApproveManage/approve-template/index.vue                        |   29 
 68 files changed, 2,908 insertions(+), 118 deletions(-)

diff --git a/multiple/assets/favicon/JMGCfavicon.ico b/multiple/assets/favicon/JMGCfavicon.ico
new file mode 100644
index 0000000..e02efaa
--- /dev/null
+++ b/multiple/assets/favicon/JMGCfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/OLDXCLfavicon.ico b/multiple/assets/favicon/OLDXCLfavicon.ico
new file mode 100644
index 0000000..0e416fe
--- /dev/null
+++ b/multiple/assets/favicon/OLDXCLfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/THZSfavicon.ico b/multiple/assets/favicon/THZSfavicon.ico
new file mode 100644
index 0000000..ebca650
--- /dev/null
+++ b/multiple/assets/favicon/THZSfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/YXHJfavicon.ico b/multiple/assets/favicon/YXHJfavicon.ico
new file mode 100644
index 0000000..335b60d
--- /dev/null
+++ b/multiple/assets/favicon/YXHJfavicon.ico
Binary files differ
diff --git a/multiple/assets/favicon/ZGLTico.ico b/multiple/assets/favicon/ZGLTico.ico
new file mode 100644
index 0000000..5783eb7
--- /dev/null
+++ b/multiple/assets/favicon/ZGLTico.ico
Binary files differ
diff --git a/multiple/assets/favicon/ZXDZfavicon.ico b/multiple/assets/favicon/ZXDZfavicon.ico
new file mode 100644
index 0000000..9f92295
--- /dev/null
+++ b/multiple/assets/favicon/ZXDZfavicon.ico
Binary files differ
diff --git a/multiple/assets/logo/JMGCLogo.png b/multiple/assets/logo/JMGCLogo.png
new file mode 100644
index 0000000..2e72b39
--- /dev/null
+++ b/multiple/assets/logo/JMGCLogo.png
Binary files differ
diff --git a/multiple/assets/logo/OLDXCLLogo.png b/multiple/assets/logo/OLDXCLLogo.png
new file mode 100644
index 0000000..61dc9b5
--- /dev/null
+++ b/multiple/assets/logo/OLDXCLLogo.png
Binary files differ
diff --git "a/src/assets/logo/\344\270\212\346\265\267\351\203\242\346\230\261\347\275\221\347\273\234\347\247\221\346\212\200\346\234\211\351\231\220\345\205\254\345\217\270.png" b/multiple/assets/logo/SHYY.png
similarity index 100%
copy from "src/assets/logo/\344\270\212\346\265\267\351\203\242\346\230\261\347\275\221\347\273\234\347\247\221\346\212\200\346\234\211\351\231\220\345\205\254\345\217\270.png"
copy to multiple/assets/logo/SHYY.png
Binary files differ
diff --git a/multiple/assets/logo/THZSLogo.png b/multiple/assets/logo/THZSLogo.png
new file mode 100644
index 0000000..9d444ca
--- /dev/null
+++ b/multiple/assets/logo/THZSLogo.png
Binary files differ
diff --git a/multiple/assets/logo/YXHJLogo.png b/multiple/assets/logo/YXHJLogo.png
new file mode 100644
index 0000000..b01f29c
--- /dev/null
+++ b/multiple/assets/logo/YXHJLogo.png
Binary files differ
diff --git a/multiple/assets/logo/ZGLTLogo.png b/multiple/assets/logo/ZGLTLogo.png
new file mode 100644
index 0000000..aab2ca4
--- /dev/null
+++ b/multiple/assets/logo/ZGLTLogo.png
Binary files differ
diff --git a/multiple/assets/logo/ZXDZLogo.png b/multiple/assets/logo/ZXDZLogo.png
new file mode 100644
index 0000000..4e84696
--- /dev/null
+++ b/multiple/assets/logo/ZXDZLogo.png
Binary files differ
diff --git a/multiple/config.json b/multiple/config.json
index c5fdd28..7fcbfb2 100644
--- a/multiple/config.json
+++ b/multiple/config.json
@@ -12,8 +12,350 @@
       "VITE_BASE_API": "http://1.15.17.182:9048",
       "VITE_JAVA_API": "http://1.15.17.182:9049"
     },
-    "logo": "logo/XDRJ.png",
+    "logo": "logo/SHYY.png",
     "favicon": "favicon/favicon.ico"
+  },
+  "BTYX": {
+    "env": {
+      "VITE_APP_TITLE": "娌冲崡甯お浼橀�夐鍝佹湁闄愬叕鍙�",
+      "VITE_BASE_API": "http://1.15.17.182:9056",
+      "VITE_JAVA_API": "http://1.15.17.182:9057"
+    },
+    "logo": "logo/BTYXLogo.png",
+    "favicon": "favicon/BTYXfavicon.ico"
+  },
+  "ZXZN": {
+    "env": {
+      "VITE_APP_TITLE": "娌冲崡鏅鸿姱鏅鸿兘鏈哄櫒浜烘湁闄愬叕鍙�",
+      "VITE_BASE_API": "http://127.0.0.1:9001",
+      "VITE_JAVA_API": "http://127.0.0.1:9000"
+    },
+    "logo": "logo/ZXZNLogo.png",
+    "favicon": "favicon/ZXZNfavicon.ico"
+  },
+  "SDTX": {
+    "env": {
+      "VITE_APP_TITLE": "娌冲崡鍠勯紟閫氫俊绉戞妧鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.213.156.184:9001",
+      "VITE_JAVA_API": "http://36.213.156.184:9000"
+    },
+    "logo": "logo/SDTXLogo.png",
+    "favicon": "favicon/SDTXfavicon.ico"
+  },
+  "QXY": {
+    "env": {
+      "VITE_APP_TITLE": "寮轰俊瀹囩數鍣ㄧ鐞嗙郴缁�",
+      "VITE_BASE_API": "http://36.134.154.10:9001",
+      "VITE_JAVA_API": "http://36.134.154.10:9000"
+    },
+    "logo": "logo/QXYLogo.png",
+    "favicon": "favicon/QXYfavicon.ico"
+  },
+  "HQJC": {
+    "env": {
+      "VITE_APP_TITLE": "鍗庡己寤烘潗绠$悊绯荤粺",
+      "VITE_BASE_API": "http://36.134.77.64:9001",
+      "VITE_JAVA_API": "http://36.134.77.64:9000"
+    },
+    "logo": "logo/HQJCLogo.png",
+    "favicon": "favicon/HQJCfavicon.ico"
+  },
+  "XCDQ": {
+    "env": {
+      "VITE_APP_TITLE": "鏃櫒鐢靛櫒绠$悊绯荤粺",
+      "VITE_BASE_API": "http://36.133.45.183:9001",
+      "VITE_JAVA_API": "http://36.133.45.183:9002"
+    },
+    "logo": "logo/XCDQLogo.png",
+    "favicon": "favicon/XCDQfavicon.ico"
+  },
+  "BWSM": {
+    "env": {
+      "VITE_APP_TITLE": "鍏淮鍟嗚锤绠$悊绯荤粺",
+      "VITE_BASE_API": "http://1.15.17.182:9070",
+      "VITE_JAVA_API": "http://1.15.17.182:9069"
+    },
+    "logo": "logo/BWSMLogo.png",
+    "favicon": "favicon/BWSMfavicon.ico"
+  },
+  "CKGM": {
+    "env": {
+      "VITE_APP_TITLE": "瀹稿悍宸ヨ锤绠$悊绯荤粺",
+      "VITE_BASE_API": "http://1.15.17.182:9072",
+      "VITE_JAVA_API": "http://1.15.17.182:9071"
+    },
+    "logo": "logo/CKGMLogo.png",
+    "favicon": "favicon/CKGMfavicon.ico"
+  },
+  "RFSY": {
+    "env": {
+      "VITE_APP_TITLE": "鐟炰赴鐩涗笟绠$悊绯荤粺",
+      "VITE_BASE_API": "http://1.15.17.182:9076",
+      "VITE_JAVA_API": "http://1.15.17.182:9075"
+    },
+    "logo": "logo/RFSYLogo.png",
+    "favicon": "favicon/RFSYfavicon.ico"
+  },
+  "ZQSY": {
+    "env": {
+      "VITE_APP_TITLE": "娉芥穱瀹炰笟",
+      "VITE_BASE_API": "http://36.213.128.159:9000",
+      "VITE_JAVA_API": "http://36.213.128.159:9001"
+    },
+    "logo": "logo/ZQSYLogo.png",
+    "favicon": "favicon/ZQSYfavicon.ico"
+  },
+  "JXJH": {
+    "env": {
+      "VITE_APP_TITLE": "娴氬幙姹熸捣姘存偿鍒跺搧鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.139.201.20:9000",
+      "VITE_JAVA_API": "http://36.139.201.20:9001"
+    },
+    "logo": "logo/JXJHLogo.png",
+    "favicon": "favicon/JXJHfavicon.ico"
+  },
+  "YTJZ": {
+    "env": {
+      "VITE_APP_TITLE": "璞嘲寤虹瓚鏉愭枡鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.139.201.181:9000",
+      "VITE_JAVA_API": "http://36.139.201.181:9001"
+    },
+    "logo": "logo/YTJZLogo.png",
+    "favicon": "favicon/YTJZfavicon.ico"
+  },
+  "HYLQ": {
+    "env": {
+      "VITE_APP_TITLE": "鑸�歌矾妗ュ伐绋嬫湁闄愬叕鍙�",
+      "VITE_BASE_API": "http://36.139.202.111:9000",
+      "VITE_JAVA_API": "http://36.139.202.111:9001"
+    },
+    "logo": "logo/HYLQLogo.png",
+    "favicon": "favicon/HYLQfavicon.ico"
+  },
+  "HJGC": {
+    "env": {
+      "VITE_APP_TITLE": "鐜宸ョ▼绠$悊绯荤粺",
+      "VITE_BASE_API": "http://36.139.202.111:9014",
+      "VITE_JAVA_API": "http://36.139.202.111:9013"
+    },
+    "logo": "logo/HJGCLogo.png",
+    "favicon": "favicon/HJGCfavicon.ico"
+  },
+  "QYHY": {
+    "env": {
+      "VITE_APP_TITLE": "濂囩紭鎭掕壓绠$悊绯荤粺",
+      "VITE_BASE_API": "http://36.139.202.111:9012",
+      "VITE_JAVA_API": "http://36.139.202.111:9011"
+    },
+    "logo": "logo/QYHYLogo.png",
+    "favicon": "favicon/QYHYfavicon.ico"
+  },
+  "QXY": {
+    "env": {
+      "VITE_APP_TITLE": "寮轰俊瀹囩數鍣ㄤ簯涓绘満",
+      "VITE_BASE_API": "http://36.134.154.10:9000",
+      "VITE_JAVA_API": "http://36.134.154.10:9001"
+    },
+    "logo": "logo/QXYLogo.png",
+    "favicon": "favicon/QXYfavicon.ico"
+  },
+  "HYJC": {
+    "env": {
+      "VITE_APP_TITLE": "鎭掓磱寤烘潗",
+      "VITE_BASE_API": "http://36.138.94.178:9000",
+      "VITE_JAVA_API": "http://36.138.94.178:9001"
+    },
+    "logo": "logo/HYJCLogo.png",
+    "favicon": "favicon/HYJCfavicon.ico"
+  },
+  "JHY": {
+    "env": {
+      "VITE_APP_TITLE": "灞辫タ鐪佹绀惧幙鏅嬪拰鍥鍝佹湁闄愬叕鍙�",
+      "VITE_BASE_API": "http://223.15.233.27:9001",
+      "VITE_JAVA_API": "http://223.15.233.27:9002"
+    },
+    "logo": "logo/JHYLogo.png",
+    "favicon": "favicon/JHYfavicon.ico"
+  },
+  "XCDQ": {
+    "env": {
+      "VITE_APP_TITLE": "鏃櫒鐢靛櫒绠$悊绯荤粺",
+      "VITE_BASE_API": "http://36.133.45.183:9001",
+      "VITE_JAVA_API": "http://36.133.45.183:9002"
+    },
+    "logo": "logo/XCDQLogo.png",
+    "favicon": "favicon/XCDQfavicon.ico"
+  },
+  "XHKS": {
+    "env": {
+      "VITE_APP_TITLE": "閼摟鐭垮北绠$悊绯荤粺",
+      "VITE_BASE_API": "http://36.133.45.183:9078",
+      "VITE_JAVA_API": "http://36.133.45.183:9077"
+    },
+    "logo": "logo/XHKSLogo.png",
+    "favicon": "favicon/XHKSfavicon.ico"
+  },
+  "QFSW": {
+    "env": {
+      "VITE_APP_TITLE": "涔句赴鐢熺墿绠$悊绯荤粺",
+      "VITE_BASE_API": "http://36.133.45.183:9080",
+      "VITE_JAVA_API": "http://36.133.45.183:9079"
+    },
+    "logo": "logo/QFSWLogo.png",
+    "favicon": "favicon/QFSWfavicon.ico"
+  },
+  "ZDJC": {
+    "env": {
+      "VITE_APP_TITLE": "姝e痉鏂板瀷寤烘潗绠$悊绯荤粺",
+      "VITE_BASE_API": "http://36.133.45.183:9082",
+      "VITE_JAVA_API": "http://36.133.45.183:9081"
+    },
+    "logo": "logo/ZDJCLogo.png",
+    "favicon": "favicon/ZDJCfavicon.ico"
+  },
+  "LSY": {
+    "env": {
+      "VITE_APP_TITLE": "灞辫タ鏋楁槆鐒卞伐璐告湁闄愬叕鍙�",
+      "VITE_BASE_API": "http://36.133.45.183:9084",
+      "VITE_JAVA_API": "http://36.133.45.183:9083"
+    },
+    "logo": "logo/LSYLogo.png",
+    "favicon": "favicon/LSYfavicon.ico"
+  },
+  "HTHB": {
+    "env": {
+      "VITE_APP_TITLE": "灞辫タ瀹忚吘鏂板瀷鐜繚绉戞妧鏈夐檺璐d换鍏徃",
+      "VITE_BASE_API": "http://36.133.45.183:9090",
+      "VITE_JAVA_API": "http://36.133.45.183:9089"
+    },
+    "logo": "logo/HTHBLogo.png",
+    "favicon": "favicon/HTHBfavicon.ico"
+  },
+  "YSS": {
+    "env": {
+      "VITE_APP_TITLE": "灞辫タ缇跨鏅熷啘鐢熺墿绉戞妧鍙戝睍鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.133.45.183:9086",
+      "VITE_JAVA_API": "http://36.133.45.183:9085"
+    },
+    "logo": "logo/YSSLogo.png",
+    "favicon": "favicon/YSSfavicon.ico"
+  },
+  "KYHG": {
+    "env": {
+      "VITE_APP_TITLE": "灞辫タ鍧ゆ簮鍖栧伐鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.137.13.29:9001",
+      "VITE_JAVA_API": "http://36.137.13.29:9000"
+    },
+    "logo": "logo/KYHGLogo.png",
+    "favicon": "favicon/KYHGfavicon.ico"
+  },
+  "JXSM": {
+    "env": {
+      "VITE_APP_TITLE": "瑗勫灒鍘挎磥閼晢璐告湁闄愬叕鍙�",
+      "VITE_BASE_API": "http://36.134.76.148:9001",
+      "VITE_JAVA_API": "http://36.134.76.148:9002"
+    },
+    "logo": "logo/JXSMLogo.png",
+    "favicon": "favicon/JXSMico.ico"
+  },
+  "DCHX": {
+    "env": {
+      "VITE_APP_TITLE": "灞辫タ澶ф垚鍖栫氦鑲′唤鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.134.76.148:9088",
+      "VITE_JAVA_API": "http://36.134.76.148:9087"
+    },
+    "logo": "logo/DCHXLogo.png",
+    "favicon": "favicon/DCHXfavicon.ico"
+  },
+  "KHYY": {
+    "env": {
+      "VITE_APP_TITLE": "闀挎不甯傚潳鐨撴恫鍘嬪埗閫犳湁闄愬叕鍙�",
+      "VITE_BASE_API": "http://36.134.76.148:9094",
+      "VITE_JAVA_API": "http://36.134.76.148:9093"
+    },
+    "logo": "logo/KHYYLogo.png",
+    "favicon": "favicon/KHYYfavicon.ico"
+  },
+  "XLGS": {
+    "env": {
+      "VITE_APP_TITLE": "闀挎不甯傞懌娴緵姘磋澶囨湁闄愬叕鍙�",
+      "VITE_BASE_API": "http://36.134.76.148:9092",
+      "VITE_JAVA_API": "http://36.134.76.148:9091"
+    },
+    "logo": "logo/XLGSLogo.png",
+    "favicon": "favicon/XLGSfavicon.ico"
+  },
+  "NY": {
+    "env": {
+      "VITE_APP_TITLE": "灞辫タ鐪佽棰㈡柊鏉愭枡鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.134.76.148:9092",
+      "VITE_JAVA_API": "http://36.134.76.148:9091"
+    },
+    "logo": "logo/NYLogo.png",
+    "favicon": "favicon/NYfavicon.ico"
+  },
+  "JFHY": {
+    "env": {
+      "VITE_APP_TITLE": "灞辫タ閲戠鎭掕繙鍟嗚锤鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.134.76.148:9100",
+      "VITE_JAVA_API": "http://36.134.76.148:9099"
+    },
+    "logo": "logo/JFHYLogo.png",
+    "favicon": "favicon/JFHYfavicon.ico"
+  },
+  "BTW": {
+    "env": {
+      "VITE_APP_TITLE": "灞辫タ姣旂壒缁寸鎶�鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.134.76.148:9102",
+      "VITE_JAVA_API": "http://36.134.76.148:9101"
+    },
+    "logo": "logo/BTWLogo.png",
+    "favicon": "favicon/BTWfavicon.ico"
+  },
+  "BLSGR": {
+    "env": {
+      "VITE_APP_TITLE": "瀹佸甯冮噷鏂牸灏旂緤缁掑埗鍝佹湁闄愬叕鍙�",
+      "VITE_BASE_API": "http://36.134.76.148:9102",
+      "VITE_JAVA_API": "http://36.134.76.148:9101"
+    },
+    "logo": "logo/BLSGRLogo.png",
+    "favicon": "favicon/BLSGRfavicon.ico"
+  },
+  "SDZZ": {
+    "env": {
+      "VITE_APP_TITLE": "骞抽仴鍘跨洓杈鹃摳閫犲巶",
+      "VITE_BASE_API": "http://36.134.76.148:9102",
+      "VITE_JAVA_API": "http://36.134.76.148:9101"
+    },
+    "logo": "logo/SDZZLogo.png",
+    "favicon": "favicon/SDZZfavicon.ico"
+  },
+  "QLMC": {
+    "env": {
+      "VITE_APP_TITLE": "绁佽繛鐗у満淇℃伅绠$悊绯荤粺",
+      "VITE_BASE_API": "http://114.132.189.42:9066",
+      "VITE_JAVA_API": "http://114.132.189.42:9060"
+    },
+    "logo": "logo/QLMCLogo.png",
+    "favicon": "favicon/QLMCico.ico"
+  },
+  "HYZC": {
+    "env": {
+      "VITE_APP_TITLE": "灞辫タ鍗庝嚎浼楁垚寤烘潗鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.213.180.217:9009",
+      "VITE_JAVA_API": "http://36.213.180.217:9008"
+    },
+    "logo": "logo/HYZCLogo.png",
+    "favicon": "favicon/HYZCfavicon.ico"
+  },
+  "OLD": {
+    "env": {
+      "VITE_APP_TITLE": "闈掓捣娆ц幈寰锋柊鏉愭枡绉戞妧鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://223.221.37.139:9000",
+      "VITE_JAVA_API": "http://223.221.37.139:9000"
+    },
+    "logo": "logo/OLDLogo.png",
+    "favicon": "favicon/OLD.ico"
   },
   "XJGM": {
     "env": {
@@ -24,7 +366,69 @@
     "logo": "logo/XJGMLogo.png",
     "favicon": "favicon/XJGM.ico"
   },
-
+  "RBHB": {
+    "env": {
+      "VITE_APP_TITLE": "鍏嬫媺鐜涗緷铻嶉偊鐜繚绉戞妧鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.213.180.217:9017",
+      "VITE_JAVA_API": "http://36.213.180.217:9018"
+    },
+    "logo": "logo/RBHBLogo.png",
+    "favicon": "favicon/RBHB.ico"
+  },
+  "OLDXCL": {
+    "env": {
+      "VITE_APP_TITLE": "闈掓捣娆ц幈寰锋柊鏉愭枡绉戞妧鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.213.90.123:9524",
+      "VITE_JAVA_API": "http://36.213.90.123:9525"
+    },
+    "logo": "logo/OLDXCLLogo.png",
+    "favicon": "favicon/OLDXCLfavicon.ico"
+  },
+    "YXHJ": {
+    "env": {
+      "VITE_APP_TITLE": "闈掓捣褰︾ゥ鐜绉戞妧鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.213.90.123:9526",
+      "VITE_JAVA_API": "http://36.213.90.123:9527"
+    },
+    "logo": "logo/YXHJLogo.png",
+    "favicon": "favicon/YXHJfavicon.ico"
+  },
+    "ZXDZ": {
+    "env": {
+      "VITE_APP_TITLE": "闈掓捣鍑嗘槦鐢靛瓙琛″櫒鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.213.90.123:9528",
+      "VITE_JAVA_API": "http://36.213.90.123:9529"
+    },
+    "logo": "logo/ZXDZLogo.png",
+    "favicon": "favicon/ZXDZfavicon.ico"
+  },
+    "THZS": {
+    "env": {
+      "VITE_APP_TITLE": "闈掓捣鐗瑰紭鍐嶇敓鐗╄祫鍥炴敹鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.213.90.123:9530",
+      "VITE_JAVA_API": "http://36.213.90.123:9531"
+    },
+    "logo": "logo/THZSLogo.png",
+    "favicon": "favicon/THZSfavicon.ico"
+  },
+    "JMGC": {
+    "env": {
+      "VITE_APP_TITLE": "闈掓捣閿︽芳宸ョ▼鏈烘鏈夐檺鍏徃",
+      "VITE_BASE_API": "http://36.213.90.123:9532",
+      "VITE_JAVA_API": "http://36.213.90.123:9533"
+    },
+    "logo": "logo/JMGCLogo.png",
+    "favicon": "favicon/JMGCfavicon.ico"
+  },
+  "ZGLT": {
+    "env": {
+      "VITE_APP_TITLE": "涓浗鑱旈��",
+      "VITE_BASE_API": "http://36.213.180.217:9017",
+      "VITE_JAVA_API": "http://36.213.180.217:9018"
+    },
+    "logo": "logo/ZGLTLogo.png",
+    "favicon": "favicon/ZGLTico.ico"
+  },
   "logo": "/src/assets/logo/logo.png",
   "favicon": "/public/favicon.ico"
-}
+}
\ No newline at end of file
diff --git a/src/api/reportAnalysis/safetyDashboard.js b/src/api/reportAnalysis/safetyDashboard.js
new file mode 100644
index 0000000..8309c9d
--- /dev/null
+++ b/src/api/reportAnalysis/safetyDashboard.js
@@ -0,0 +1,60 @@
+import request from '@/utils/request'
+
+// 椤堕儴鎬昏
+export function getSafetyOverview() {
+  return request({
+    url: '/safeDashboard/getOverview',
+    method: 'get'
+  })
+}
+
+// 鏈�鏂板畨鍏ㄥ姩鎬�
+export function getLatestSafeEvents(params) {
+  return request({
+    url: '/safeDashboard/getLatestEvents',
+    method: 'get',
+    params
+  })
+}
+
+// 闅愭偅绫诲瀷鍒嗗竷
+export function getHiddenTypeDistribution() {
+  return request({
+    url: '/safeDashboard/getHiddenTypeDistribution',
+    method: 'get'
+  })
+}
+
+// 杩�6涓湀闅愭偅瓒嬪娍
+export function getHiddenTrend() {
+  return request({
+    url: '/safeDashboard/getHiddenTrend',
+    method: 'get'
+  })
+}
+
+// 鍗遍櫓婧愰闄╃瓑绾у垎甯�
+export function getHazardRiskDistribution() {
+  return request({
+    url: '/safeDashboard/getHazardRiskDistribution',
+    method: 'get'
+  })
+}
+
+// 鍗遍櫓婧愮被鍨嬫帓鍚�
+export function getHazardTypeRanking(params) {
+  return request({
+    url: '/safeDashboard/getHazardTypeRanking',
+    method: 'get',
+    params
+  })
+}
+
+// 闅愭偅鏁存敼璐d换浜烘帓鍚�
+export function getHiddenRectifyRanking(params) {
+  return request({
+    url: '/safeDashboard/getHiddenRectifyRanking',
+    method: 'get',
+    params
+  })
+}
diff --git "a/src/assets/logo/\344\270\212\346\265\267\351\203\242\346\230\261\347\275\221\347\273\234\347\247\221\346\212\200\346\234\211\351\231\220\345\205\254\345\217\270.png" b/src/assets/logo/SHYY.png
similarity index 100%
rename from "src/assets/logo/\344\270\212\346\265\267\351\203\242\346\230\261\347\275\221\347\273\234\347\247\221\346\212\200\346\234\211\351\231\220\345\205\254\345\217\270.png"
rename to src/assets/logo/SHYY.png
Binary files differ
diff --git a/src/components/PIMTable/PIMTable.vue b/src/components/PIMTable/PIMTable.vue
index d4104f0..7940f1a 100644
--- a/src/components/PIMTable/PIMTable.vue
+++ b/src/components/PIMTable/PIMTable.vue
@@ -179,15 +179,19 @@
              class="cell link"
              style="width: 100%"
              @click="goLink(scope.row, item.linkMethod)">
-          <span v-if="!item.formatData">{{ scope.row[item.prop] }}</span>
+          <span v-if="!item.formatData">{{
+            toDateOnly(scope.row[item.prop])
+          }}</span>
         </div>
         <!-- 榛樿绾睍绀烘暟鎹� -->
         <div v-else
              class="cell"
              style="width: 100%">
-          <span v-if="!item.formatData">{{ scope.row[item.prop] }}</span>
+          <span v-if="!item.formatData">{{
+            toDateOnly(scope.row[item.prop])
+          }}</span>
           <span v-else>{{
-            formatters(scope.row[item.prop], item.formatData)
+            toDateOnly(formatters(scope.row[item.prop], item.formatData))
           }}</span>
         </div>
       </template>
@@ -205,6 +209,7 @@
   import pagination from "./Pagination.vue";
   import { computed, ref, inject, getCurrentInstance } from "vue";
   import { ElMessage } from "element-plus";
+  import { toDateOnly } from "@/utils/dateDisplay";
 
   // 鑾峰彇鍏ㄥ眬鐨� uploadHeader
   const { proxy } = getCurrentInstance();
diff --git a/src/main.js b/src/main.js
index 025ff14..d0282b7 100644
--- a/src/main.js
+++ b/src/main.js
@@ -58,6 +58,7 @@
 import PageHeader from "@/components/PageHeader/index.vue";
 
 import { getToken } from "@/utils/auth";
+import { toDateOnly } from "@/utils/dateDisplay";
 import {
   calculateTaxExclusiveTotalPrice,
   summarizeTable,
@@ -80,6 +81,7 @@
 app.config.globalProperties.addDateRange = addDateRange;
 app.config.globalProperties.selectDictLabel = selectDictLabel;
 app.config.globalProperties.selectDictLabels = selectDictLabels;
+app.config.globalProperties.formatDateOnly = toDateOnly;
 app.config.globalProperties.javaApi = __BASE_API__;
 app.config.globalProperties.HaveJson = (val) => {
   return JSON.parse(JSON.stringify(val));
@@ -116,4 +118,19 @@
 });
 app._context.components.ElDialog.props.closeOnClickModal.default = false;
 
+// 鍒楄〃鏃堕棿缁熶竴鍙睍绀哄勾鏈堟棩锛氭湭鍗曠嫭閰嶇疆 formatter 鐨勮〃鏍煎垪璧拌繖閲�
+const tableColumnFormatter =
+  app._context.components.ElTableColumn?.props?.formatter;
+if (tableColumnFormatter) {
+  const formatCell = (row, column, cellValue) => toDateOnly(cellValue);
+  if (typeof tableColumnFormatter === "function") {
+    app._context.components.ElTableColumn.props.formatter = {
+      type: Function,
+      default: formatCell,
+    };
+  } else {
+    tableColumnFormatter.default = formatCell;
+  }
+}
+
 app.mount("#app");
diff --git a/src/utils/dateDisplay.js b/src/utils/dateDisplay.js
new file mode 100644
index 0000000..db7deb4
--- /dev/null
+++ b/src/utils/dateDisplay.js
@@ -0,0 +1,32 @@
+/**
+ * 鍒楄〃灞曠ず鐢ㄧ殑鏃ユ湡澶勭悊锛氬甫鏃跺垎绉掔殑鏃ユ湡鏃堕棿鍙繚鐣欏勾鏈堟棩锛�
+ * 鏈潵灏辨槸鏃ユ湡銆佹椂闂存垨鍏跺畠鍐呭鐨勫�煎師鏍疯繑鍥炪��
+ */
+const DATE_TIME_PATTERN =
+  /^(\d{4})[-/骞碷(\d{1,2})[-/鏈圿(\d{1,2})鏃�?[ T]\d{1,2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?$/;
+
+function pad2(value) {
+  return String(value).padStart(2, "0");
+}
+
+export function toDateOnly(value) {
+  if (value == null || value === "") return value;
+
+  if (value instanceof Date) {
+    if (Number.isNaN(value.getTime())) return value;
+    return `${value.getFullYear()}-${pad2(value.getMonth() + 1)}-${pad2(
+      value.getDate()
+    )}`;
+  }
+
+  if (typeof value !== "string") {
+    // 瀵硅薄/鏁扮粍鎸夎〃鏍煎師鏈夋柟寮忚浆鎴愭枃鏈紝閬垮厤鎶婇潪瀛楃涓茬洿鎺ヤ氦缁欐覆鏌撳眰
+    return typeof value === "object" ? String(value) : value;
+  }
+
+  const text = value.trim();
+  const matched = text.match(DATE_TIME_PATTERN);
+  if (!matched) return value;
+
+  return `${matched[1]}-${pad2(matched[2])}-${pad2(matched[3])}`;
+}
diff --git a/src/utils/index.js b/src/utils/index.js
index 809593f..0c443c7 100644
--- a/src/utils/index.js
+++ b/src/utils/index.js
@@ -12,14 +12,7 @@
       ? "0" + (date.getMonth() + 1)
       : date.getMonth() + 1;
   var day = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
-  var hours = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
-  var minutes =
-    date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
-  var seconds =
-    date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
-  return (
-    year + "-" + month + "-" + day + " " + hours + ":" + minutes + ":" + seconds
-  );
+  return year + "-" + month + "-" + day;
 }
 
 /**
diff --git a/src/utils/ruoyi.js b/src/utils/ruoyi.js
index 0d72c1a..2db8af1 100644
--- a/src/utils/ruoyi.js
+++ b/src/utils/ruoyi.js
@@ -8,7 +8,7 @@
   if (arguments.length === 0 || !time) {
     return null
   }
-  const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
+  const format = pattern || '{y}-{m}-{d}'
   let date
   if (typeof time === 'object') {
     date = time
diff --git a/src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue b/src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue
index 11d1774..817e08d 100644
--- a/src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue
+++ b/src/views/collaborativeApproval/notificationManagement/meetDraft/index.vue
@@ -430,7 +430,11 @@
 // 鏍煎紡鍖栨棩鏈熸椂闂�
 const formatDateTime = (dateTime) => {
   if (!dateTime) return ''
-  return dateTime.replace(' ', '\n')
+  const matched = String(dateTime).match(
+    /^(\d{4})[-/](\d{1,2})[-/](\d{1,2})/
+  )
+  if (!matched) return dateTime
+  return `${matched[1]}-${matched[2].padStart(2, '0')}-${matched[3].padStart(2, '0')}`
 }
 
 // 椤甸潰鍔犺浇鏃惰幏鍙栨暟鎹�
diff --git a/src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue b/src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue
index dea9495..de0dc2a 100644
--- a/src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue
+++ b/src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue
@@ -305,7 +305,11 @@
 // 鏍煎紡鍖栨棩鏈熸椂闂�
 const formatDateTime = (dateTime) => {
   if (!dateTime) return ''
-  return dateTime.replace(' ', '\n')
+  const matched = String(dateTime).match(
+    /^(\d{4})[-/](\d{1,2})[-/](\d{1,2})/
+  )
+  if (!matched) return dateTime
+  return `${matched[1]}-${matched[2].padStart(2, '0')}-${matched[3].padStart(2, '0')}`
 }
 
 // 鎻愪氦瀹℃壒
diff --git a/src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue b/src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue
index a3ae223..63ebb0c 100644
--- a/src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue
+++ b/src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue
@@ -302,7 +302,11 @@
 // 鏍煎紡鍖栨棩鏈熸椂闂�
 const formatDateTime = (dateTime) => {
   if (!dateTime) return ''
-  return dateTime.replace(' ', '\n')
+  const matched = String(dateTime).match(
+    /^(\d{4})[-/](\d{1,2})[-/](\d{1,2})/
+  )
+  if (!matched) return dateTime
+  return `${matched[1]}-${matched[2].padStart(2, '0')}-${matched[3].padStart(2, '0')}`
 }
 
 // 鎻愪氦鍙戝竷
diff --git a/src/views/collaborativeApproval/notificationManagement/summary/index.vue b/src/views/collaborativeApproval/notificationManagement/summary/index.vue
index 9f3fc7f..4c63ed0 100644
--- a/src/views/collaborativeApproval/notificationManagement/summary/index.vue
+++ b/src/views/collaborativeApproval/notificationManagement/summary/index.vue
@@ -332,7 +332,11 @@
 // 鏍煎紡鍖栨棩鏈熸椂闂�
 const formatDateTime = (dateTime) => {
   if (!dateTime) return ''
-  return dateTime.replace(' ', '\n')
+  const matched = String(dateTime).match(
+    /^(\d{4})[-/](\d{1,2})[-/](\d{1,2})/
+  )
+  if (!matched) return dateTime
+  return `${matched[1]}-${matched[2].padStart(2, '0')}-${matched[3].padStart(2, '0')}`
 }
 
 // 椤甸潰鍔犺浇鏃惰幏鍙栨暟鎹�
diff --git a/src/views/equipmentManagement/inspectionManagement/index.vue b/src/views/equipmentManagement/inspectionManagement/index.vue
index 8c0d52c..e50e8fc 100644
--- a/src/views/equipmentManagement/inspectionManagement/index.vue
+++ b/src/views/equipmentManagement/inspectionManagement/index.vue
@@ -197,7 +197,7 @@
       formatData: cell => {
         if (!cell) return "-";
         try {
-          return dayjs(cell).format("YYYY-MM-DD HH:mm:ss");
+          return dayjs(cell).format("YYYY-MM-DD");
         } catch {
           return cell;
         }
diff --git a/src/views/equipmentManagement/spareParts/index.vue b/src/views/equipmentManagement/spareParts/index.vue
index 8abe35d..9f5d8fe 100644
--- a/src/views/equipmentManagement/spareParts/index.vue
+++ b/src/views/equipmentManagement/spareParts/index.vue
@@ -388,13 +388,14 @@
 const resetQuery = () => {
   queryParams.name = '';
   pagination.current = 1;
+  pagination.size = 10;
   fetchListData();
 }
 
 // 鍒嗛〉澶у皬鏀瑰彉
-const handleSizeChange = (size) => {
-  pagination.size = size;
-  pagination.current = 1;
+const handleSizeChange = ({ page, limit }) => {
+  pagination.current = page;
+  pagination.size = limit;
   fetchListData();
 }
 
diff --git a/src/views/equipmentManagement/upkeep/index.vue b/src/views/equipmentManagement/upkeep/index.vue
index 560b70d..b473fc1 100644
--- a/src/views/equipmentManagement/upkeep/index.vue
+++ b/src/views/equipmentManagement/upkeep/index.vue
@@ -335,8 +335,7 @@
       prop: "registrationDate",
       label: "鐧昏鏃ユ湡",
       minWidth: 100,
-      formatData: cell =>
-        cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-",
+      formatData: cell => (cell ? dayjs(cell).format("YYYY-MM-DD") : "-"),
     },
     {
       fixed: "right",
@@ -417,8 +416,7 @@
       label: "瀹為檯淇濆吇鏃ユ湡",
       align: "center",
       prop: "maintenanceActuallyTime",
-      formatData: cell =>
-        cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-",
+      formatData: cell => (cell ? dayjs(cell).format("YYYY-MM-DD") : "-"),
     },
     {
       label: "淇濆吇缁撴灉",
diff --git a/src/views/financialManagement/payable/purchaseIn.vue b/src/views/financialManagement/payable/purchaseIn.vue
index b105859..bfdbc91 100644
--- a/src/views/financialManagement/payable/purchaseIn.vue
+++ b/src/views/financialManagement/payable/purchaseIn.vue
@@ -58,7 +58,7 @@
         }"
                 @pagination="changePage">
         <template #inboundDate="{ row }">
-          {{ row.inboundDate ?? row.InboundDate ?? "" }}
+          {{ formatDateOnly(row.inboundDate ?? row.InboundDate ?? "") }}
         </template>
       </PIMTable>
     </div>
diff --git a/src/views/financialManagement/payable/reconciliation.vue b/src/views/financialManagement/payable/reconciliation.vue
index a3b8495..3ef6a81 100644
--- a/src/views/financialManagement/payable/reconciliation.vue
+++ b/src/views/financialManagement/payable/reconciliation.vue
@@ -11,10 +11,19 @@
           />
         </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 label="瀵硅处鏃ユ湡:">
+        <el-date-picker
+          v-model="filters.dateRange"
+          type="daterange"
+          placeholder="璇烽�夋嫨瀵硅处鏃ユ湡"
+          value-format="YYYY-MM-DD"
+          format="YYYY-MM-DD"
+          start-placeholder="寮�濮嬫棩鏈�"
+          end-placeholder="缁撴潫鏃ユ湡"
+          range-separator="鑷�"
+          clearable
+          style="width: 300px;"
+        />
       </el-form-item>
       <el-form-item>
         <el-button type="primary" @click="onSearch">鎼滅储</el-button>
@@ -66,7 +75,7 @@
     <FormDialog title="瀵硅处鏄庣粏" v-model="detailDialogVisible" width="900px" @confirm="printDetail" @cancel="detailDialogVisible = false" operationType="detail">
       <div class="statement-header">
         <h3>{{ currentSupplier }} 搴斾粯瀵硅处鍗�</h3>
-        <p>瀵硅处鏈熼棿: {{ currentPeriod }}</p>
+        <p>瀵硅处鏃ユ湡: {{ currentPeriod }}</p>
       </div>
       <el-table :data="detailData" border style="width: 100%" v-loading="detailLoading">
         <el-table-column prop="date" label="鏃ユ湡" width="120" />
@@ -124,12 +133,13 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="瀵硅处鏈堜唤" prop="statementMonth">
+            <el-form-item label="瀵硅处鏃ユ湡" prop="statementMonth">
               <el-date-picker
                 v-model="generateForm.statementMonth"
-                type="month"
-                placeholder="閫夋嫨鏈堜唤"
-                value-format="YYYY-MM"
+                type="date"
+                placeholder="閫夋嫨瀵硅处鏃ユ湡"
+                value-format="YYYY-MM-DD"
+                format="YYYY-MM-DD"
                 style="width: 100%;"
                 @change="onStatementMonthChange"
               />
@@ -139,7 +149,7 @@
       </el-form>
 
       <div v-if="statementDetailLoaded" class="purchase-section">
-        <div v-if="purchaseData.length > 0" class="section-title">鏈湀閲囪喘鏁版嵁</div>
+        <div v-if="purchaseData.length > 0" class="section-title">瀵硅处鏃ユ湡閲囪喘鏁版嵁</div>
         <el-table
           v-if="purchaseData.length > 0"
           ref="purchaseTableRef"
@@ -165,7 +175,7 @@
           </el-table-column>
           <el-table-column prop="remark" label="澶囨敞" />
         </el-table>
-        <el-empty v-else description="璇ヤ緵搴斿晢鏈湀鏆傛棤鏄庣粏鏁版嵁" :image-size="80" />
+        <el-empty v-else description="璇ヤ緵搴斿晢褰撴棩鏆傛棤鏄庣粏鏁版嵁" :image-size="80" />
 
         <div class="summary-row">
           <span>鏈熷垵浣欓: <strong class="text-primary">楼{{ formatMoney(generateForm.openingBalance) }}</strong></span>
@@ -176,7 +186,7 @@
       </div>
 
       <div v-else-if="generateForm.supplierId && generateForm.statementMonth && !purchaseLoading" class="empty-tip">
-        <el-empty description="璇ヤ緵搴斿晢鏈湀鏆傛棤閲囪喘鏁版嵁" />
+        <el-empty description="璇ヤ緵搴斿晢褰撴棩鏆傛棤閲囪喘鏁版嵁" />
       </div>
 
       <template #footer>
@@ -209,8 +219,7 @@
 
 const filters = reactive({
   supplierId: "",
-  startMonth: "",
-  endMonth: "",
+  dateRange: [],
 });
 
 const pagination = reactive({
@@ -222,7 +231,7 @@
 const columns = [
   { label: "瀵硅处鍗曞彿", prop: "statementNumber", width: "150" },
   { label: "渚涘簲鍟�", prop: "supplierName", width: "180" },
-  { label: "瀵硅处鏈熼棿", prop: "statementMonth", width: "150" },
+  { label: "瀵硅处鏃ユ湡", prop: "statementMonth", width: "150" },
   { label: "鏈熷垵浣欓", prop: "openingBalance", dataType: "slot", slot: "openingBalance" },
   { label: "鏈湡搴斾粯", prop: "currentPlan", dataType: "slot", slot: "currentPlan" },
   { label: "鏈湡浠樻", prop: "currentActually", dataType: "slot", slot: "currentActually" },
@@ -358,13 +367,9 @@
   if (filters.supplierId) {
     result.customerId = filters.supplierId;
   }
-  if (filters.startMonth && filters.endMonth && filters.startMonth === filters.endMonth) {
-    result.statementMonth = filters.startMonth;
-  } else if (filters.startMonth) {
-    result.startMonth = filters.startMonth;
-  }
-  if (filters.endMonth && filters.startMonth !== filters.endMonth) {
-    result.endMonth = filters.endMonth;
+  if (Array.isArray(filters.dateRange) && filters.dateRange.length === 2) {
+    result.startDate = filters.dateRange[0];
+    result.endDate = filters.dateRange[1];
   }
   return result;
 };
@@ -466,8 +471,7 @@
 
 const resetFilters = () => {
   filters.supplierId = "";
-  filters.startMonth = "";
-  filters.endMonth = "";
+  filters.dateRange = [];
   pagination.currentPage = 1;
   getTableData();
 };
@@ -671,7 +675,7 @@
 const viewDetail = (row) => {
   const partnerId = row.customerId ?? row.supplierId;
   if (!partnerId || !row.statementMonth) {
-    ElMessage.warning("缂哄皯渚涘簲鍟嗘垨瀵硅处鏈堜唤锛屾棤娉曟煡璇㈡槑缁�");
+    ElMessage.warning("缂哄皯渚涘簲鍟嗘垨瀵硅处鏃ユ湡锛屾棤娉曟煡璇㈡槑缁�");
     return;
   }
 
diff --git a/src/views/financialManagement/receivable/reconciliation.vue b/src/views/financialManagement/receivable/reconciliation.vue
index 03400ef..0ef037a 100644
--- a/src/views/financialManagement/receivable/reconciliation.vue
+++ b/src/views/financialManagement/receivable/reconciliation.vue
@@ -6,10 +6,19 @@
           <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :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 label="瀵硅处鏃ユ湡:">
+        <el-date-picker
+          v-model="filters.dateRange"
+          type="daterange"
+          placeholder="璇烽�夋嫨瀵硅处鏃ユ湡"
+          value-format="YYYY-MM-DD"
+          format="YYYY-MM-DD"
+          start-placeholder="寮�濮嬫棩鏈�"
+          end-placeholder="缁撴潫鏃ユ湡"
+          range-separator="鑷�"
+          clearable
+          style="width: 300px;"
+        />
       </el-form-item>
       <el-form-item>
         <el-button type="primary" @click="getTableData">鎼滅储</el-button>
@@ -62,7 +71,7 @@
     <FormDialog title="瀵硅处鏄庣粏" v-model="detailDialogVisible" width="900px" @confirm="printDetail" @cancel="detailDialogVisible = false" operationType="detail">
       <div class="statement-header">
         <h3>{{ currentCustomer }} 搴旀敹瀵硅处鍗�</h3>
-        <p>瀵硅处鏈熼棿: {{ currentPeriod }}</p>
+        <p>瀵硅处鏃ユ湡: {{ currentPeriod }}</p>
       </div>
       <el-table :data="detailData" border style="width: 100%" v-loading="detailLoading">
         <el-table-column prop="date" label="鏃ユ湡" width="120" />
@@ -114,15 +123,15 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="瀵硅处鏈堜唤" prop="statementMonth">
-              <el-date-picker v-model="generateForm.statementMonth" type="month" placeholder="閫夋嫨鏈堜唤" value-format="YYYY-MM" style="width: 100%;" @change="onStatementMonthChange" />
+            <el-form-item label="瀵硅处鏃ユ湡" prop="statementMonth">
+              <el-date-picker v-model="generateForm.statementMonth" type="date" placeholder="閫夋嫨瀵硅处鏃ユ湡" value-format="YYYY-MM-DD" format="YYYY-MM-DD" style="width: 100%;" @change="onStatementMonthChange" />
             </el-form-item>
           </el-col>
         </el-row>
       </el-form>
 
       <div v-if="statementDetailLoaded" class="sales-section">
-        <div v-if="salesData.length > 0" class="section-title">鏈湀閿�鍞暟鎹�</div>
+        <div v-if="salesData.length > 0" class="section-title">瀵硅处鏃ユ湡閿�鍞暟鎹�</div>
         <el-table
           v-if="salesData.length > 0"
           ref="salesTableRef"
@@ -148,7 +157,7 @@
           </el-table-column>
           <el-table-column prop="remark" label="澶囨敞" />
         </el-table>
-        <el-empty v-else description="璇ュ鎴锋湰鏈堟殏鏃犳槑缁嗘暟鎹�" :image-size="80" />
+        <el-empty v-else description="璇ュ鎴峰綋鏃ユ殏鏃犳槑缁嗘暟鎹�" :image-size="80" />
 
         <div class="summary-row">
           <span>鏈熷垵浣欓: <strong class="text-primary">楼{{ formatMoney(generateForm.openingBalance) }}</strong></span>
@@ -159,7 +168,7 @@
       </div>
 
       <div v-else-if="generateForm.customerId && generateForm.statementMonth && !salesLoading" class="empty-tip">
-        <el-empty description="璇ュ鎴锋湰鏈堟殏鏃犻攢鍞暟鎹�" />
+        <el-empty description="璇ュ鎴峰綋鏃ユ殏鏃犻攢鍞暟鎹�" />
       </div>
 
       <template #footer>
@@ -192,8 +201,7 @@
 
 const filters = reactive({
   customerId: "",
-  startMonth: "",
-  endMonth: "",
+  dateRange: [],
 });
 
 const pagination = reactive({
@@ -205,7 +213,7 @@
 const columns = [
   { label: "瀵硅处鍗曞彿", prop: "statementNumber", width: "150" },
   { label: "瀹㈡埛鍚嶇О", prop: "customerName", width: "180" },
-  { label: "瀵硅处鏈熼棿", prop: "statementMonth", width: "150" },
+  { label: "瀵硅处鏃ユ湡", prop: "statementMonth", width: "150" },
   { label: "鏈熷垵浣欓", prop: "openingBalance", dataType: "slot", slot: "openingBalance" },
   { label: "鏈湡搴旀敹", prop: "currentPlan", dataType: "slot", slot: "currentPlan" },
   { label: "鏈湡鏀舵", prop: "currentActually", dataType: "slot", slot: "currentActually" },
@@ -341,13 +349,9 @@
   if (filters.customerId) {
     result.customerId = filters.customerId;
   }
-  if (filters.startMonth && filters.endMonth && filters.startMonth === filters.endMonth) {
-    result.statementMonth = filters.startMonth;
-  } else if (filters.startMonth) {
-    result.startMonth = filters.startMonth;
-  }
-  if (filters.endMonth && filters.startMonth !== filters.endMonth) {
-    result.endMonth = filters.endMonth;
+  if (Array.isArray(filters.dateRange) && filters.dateRange.length === 2) {
+    result.startDate = filters.dateRange[0];
+    result.endDate = filters.dateRange[1];
   }
   return result;
 };
@@ -444,8 +448,7 @@
 
 const resetFilters = () => {
   filters.customerId = "";
-  filters.startMonth = "";
-  filters.endMonth = "";
+  filters.dateRange = [];
   pagination.currentPage = 1;
   getTableData();
 };
@@ -645,7 +648,7 @@
 
 const viewDetail = (row) => {
   if (!row.customerId || !row.statementMonth) {
-    ElMessage.warning("缂哄皯瀹㈡埛鎴栧璐︽湀浠斤紝鏃犳硶鏌ヨ鏄庣粏");
+    ElMessage.warning("缂哄皯瀹㈡埛鎴栧璐︽棩鏈燂紝鏃犳硶鏌ヨ鏄庣粏");
     return;
   }
 
diff --git a/src/views/index.vue b/src/views/index.vue
index 5a76883..2fcfde8 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -222,7 +222,7 @@
             <li v-for="item in todoList" :key="item.id" class="todo-item">
               <div class="todo-head">
                 <span class="todo-no">寰呭姙缂栧彿锛歿{ item.approveId }}</span>
-                <span class="todo-time">{{ item.approveTime }}</span>
+                <span class="todo-time">{{ formatDateOnly(item.approveTime) }}</span>
               </div>
               <div class="todo-desc">閮ㄩ棬锛歿{ item.approveDeptName }}</div>
               <div class="todo-desc">浜嬬敱锛歿{ item.approveReason }}</div>
@@ -289,7 +289,7 @@
               </div>
               <div class="plan-meta">
                 <span>璁″垝 {{ item.planQty }}</span>
-                <span>浜ゆ湡 {{ item.deliveryDate }}</span>
+                <span>浜ゆ湡 {{ formatDateOnly(item.deliveryDate) }}</span>
               </div>
             </li>
           </ul>
diff --git a/src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue b/src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue
index a835ef4..2a47012 100644
--- a/src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue
+++ b/src/views/inventoryManagement/stockManagement/BatchNoQtyDetail.vue
@@ -54,7 +54,11 @@
             label="鏈�杩戞洿鏂版椂闂�"
             prop="updateTime"
             show-overflow-tooltip
-          />
+          >
+            <template #default="scope">
+              {{ formatDateOnly(scope.row.updateTime || scope.row.createTime) }}
+            </template>
+          </el-table-column>
           <el-table-column fixed="right" label="鎿嶄綔" min-width="180" align="center">
             <template #default="scope">
               <el-button
diff --git a/src/views/inventoryManagement/stockManagement/Qualified.vue b/src/views/inventoryManagement/stockManagement/Qualified.vue
index 4286772..022c9a3 100644
--- a/src/views/inventoryManagement/stockManagement/Qualified.vue
+++ b/src/views/inventoryManagement/stockManagement/Qualified.vue
@@ -31,7 +31,11 @@
         <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 label="鏈�杩戞洿鏂版椂闂�" prop="updateTime" show-overflow-tooltip>
+          <template #default="scope">
+            {{ formatDateOnly(scope.row.updateTime || scope.row.createTime) }}
+          </template>
+        </el-table-column>
         <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.unLockedQuantity === 0">棰嗙敤</el-button>
diff --git a/src/views/inventoryManagement/stockManagement/Record.vue b/src/views/inventoryManagement/stockManagement/Record.vue
index 9ee214c..71a4d32 100644
--- a/src/views/inventoryManagement/stockManagement/Record.vue
+++ b/src/views/inventoryManagement/stockManagement/Record.vue
@@ -107,7 +107,11 @@
                          show-overflow-tooltip />
         <el-table-column label="鏈�杩戞洿鏂版椂闂�"
                          prop="updateTime"
-                         show-overflow-tooltip />
+                         show-overflow-tooltip>
+          <template #default="scope">
+            {{ formatDateOnly(scope.row.updateTime || scope.row.createTime) }}
+          </template>
+        </el-table-column>
         <el-table-column fixed="right"
                          label="鎿嶄綔"
                          min-width="80"
diff --git a/src/views/inventoryManagement/stockManagement/Unqualified.vue b/src/views/inventoryManagement/stockManagement/Unqualified.vue
index 55662be..ab57dff 100644
--- a/src/views/inventoryManagement/stockManagement/Unqualified.vue
+++ b/src/views/inventoryManagement/stockManagement/Unqualified.vue
@@ -26,7 +26,11 @@
         <el-table-column label="搴撳瓨鏁伴噺" prop="qualitity" show-overflow-tooltip />
         <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 label="鏈�杩戞洿鏂版椂闂�" prop="updateTime" show-overflow-tooltip>
+          <template #default="scope">
+            {{ formatDateOnly(scope.row.updateTime || scope.row.createTime) }}
+          </template>
+        </el-table-column>
         <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.unLockedQuantity === 0">棰嗙敤</el-button>
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js
index d6bec18..50d67ca 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceFormConfigTable.js
@@ -87,7 +87,15 @@
   const leadingCols = moduleKey && INSTANCE_NO_SEARCH_MODULE_KEYS.has(moduleKey) ? [INSTANCE_NO_TABLE_COLUMN] : [];
 
   return computed(() => {
-    const formCols = getFormConfigFieldColumns(tableDataRef.value?.[0], { excludeKeys });
+    // 鍥哄畾鍒楀凡灞曠ず鐨勫瓧娈典笉鍐嶇敱 formConfig 鍔ㄦ�佸垪閲嶅鐢熸垚
+    const fixedProps = new Set(
+      [...beforeFormColumns, ...extraColumns, ...afterFormColumns]
+        .map(c => c?.prop)
+        .filter(Boolean)
+    );
+    const formCols = getFormConfigFieldColumns(tableDataRef.value?.[0], {
+      excludeKeys: new Set([...(excludeKeys || []), ...fixedProps]),
+    });
     return [
       ...leadingCols,
       ...beforeFormColumns,
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceListSearch.js b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceListSearch.js
index 80dda5c..7504167 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceListSearch.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalInstanceListSearch.js
@@ -8,6 +8,7 @@
   APPROVAL_MODULE_KEYS.WORK_HANDOVER,
   APPROVAL_MODULE_KEYS.LEAVE,
   APPROVAL_MODULE_KEYS.OVERTIME,
+  APPROVAL_MODULE_KEYS.OUT_OFFICE,
 ]);
 
 export const INSTANCE_NO_TABLE_COLUMN = {
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalModuleRegistry.js b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalModuleRegistry.js
index fd56eca..ed61187 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalModuleRegistry.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-shared/approvalModuleRegistry.js
@@ -9,6 +9,7 @@
   WORK_HANDOVER: "work_handover",
   LEAVE: "leave",
   OVERTIME: "overtime",
+  OUT_OFFICE: "out_office",
   TRAVEL_REIMBURSE: "travel_reimburse",
   COST_REIMBURSE: "cost_reimburse",
   ENTERPRISE_NEWS: "enterprise_news",
@@ -21,6 +22,7 @@
   [APPROVAL_MODULE_KEYS.WORK_HANDOVER]: 13,
   [APPROVAL_MODULE_KEYS.LEAVE]: 14,
   [APPROVAL_MODULE_KEYS.OVERTIME]: 15,
+  [APPROVAL_MODULE_KEYS.OUT_OFFICE]: 1,
   [APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE]: 16,
   [APPROVAL_MODULE_KEYS.COST_REIMBURSE]: 17,
   [APPROVAL_MODULE_KEYS.ENTERPRISE_NEWS]: 18,
@@ -63,6 +65,12 @@
     businessType: APPROVAL_BUSINESS_TYPE[APPROVAL_MODULE_KEYS.OVERTIME],
     typeLabels: ["鍔犵彮", "鍔犵彮鐢宠", "鍔犵彮瀹℃壒"],
   },
+  [APPROVAL_MODULE_KEYS.OUT_OFFICE]: {
+    label: "鍏嚭鐢宠",
+    approvalType: "out_office",
+    businessType: APPROVAL_BUSINESS_TYPE[APPROVAL_MODULE_KEYS.OUT_OFFICE],
+    typeLabels: ["鍏嚭", "鍏嚭鐢宠", "鍏嚭绠$悊"],
+  },
   [APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE]: {
     label: "宸梾鎶ラ攢",
     approvalType: "travel_reimburse",
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js b/src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js
index 727f896..ff02425 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/approveTemplateConstants.js
@@ -159,16 +159,16 @@
   if (val == null || val === "") return "";
   if (Array.isArray(val) && val.length >= 3) {
     const [y, m, d, h = 0, min = 0, s = 0] = val;
-    return dayjs(new Date(y, m - 1, d, h, min, s)).format("YYYY-MM-DD HH:mm:ss");
+    return dayjs(new Date(y, m - 1, d, h, min, s)).format("YYYY-MM-DD");
   }
   if (typeof val === "number") {
     const d = val > 1e12 ? dayjs(val) : dayjs.unix(val);
-    return d.isValid() ? d.format("YYYY-MM-DD HH:mm:ss") : "";
+    return d.isValid() ? d.format("YYYY-MM-DD") : "";
   }
   const s = String(val).trim();
   if (!s) return "";
   const parsed = dayjs(s.includes("T") ? s : s.replace(/-/g, "/"));
-  return parsed.isValid() ? parsed.format("YYYY-MM-DD HH:mm:ss") : s;
+  return parsed.isValid() ? parsed.format("YYYY-MM-DD") : s;
 }
 
 export function formatDisplayTime(val) {
@@ -270,6 +270,9 @@
   };
   const kw = (searchForm?.keyword || "").trim();
   if (kw) params.templateName = kw;
+  if (searchForm?.businessType != null && searchForm.businessType !== "") {
+    params.businessType = searchForm.businessType;
+  }
   if (searchForm?.enabledOnly) params.enabled = "1";
   return params;
 }
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/index.vue b/src/views/officeProcessAutomation/ApproveManage/approve-template/index.vue
index 0a495c6..55ec8be 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/index.vue
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/index.vue
@@ -26,6 +26,22 @@
 
         />
 
+        <span class="search_title" style="margin-left: 12px">妯℃澘绫诲瀷锛�</span>
+        <el-select
+          v-model="searchForm.businessType"
+          placeholder="璇烽�夋嫨妯℃澘绫诲瀷"
+          clearable
+          filterable
+          style="width: 180px"
+          @change="handleQuery"
+        >
+          <el-option
+            v-for="opt in templateTypeOptions"
+            :key="`search-biz-type-${opt.value}`"
+            :label="opt.label"
+            :value="opt.value"
+          />
+        </el-select>
         <el-checkbox v-model="searchForm.enabledOnly" class="ml12" @change="handleQuery">
 
           浠呮樉绀哄惎鐢�
@@ -422,6 +438,7 @@
 import { ElMessage } from "element-plus";
 
 import { computed, nextTick, onMounted, ref } from "vue";
+import { useRoute } from "vue-router";
 
 import { userListNoPageByTenantId } from "@/api/system/user.js";
 
@@ -495,6 +512,8 @@
 } = useApproveTemplate();
 
 
+
+const route = useRoute();
 
 const flowUserOptions = ref([]);
 
@@ -614,11 +633,17 @@
 
 
 
-onMounted(() => {
+onMounted(async () => {
 
   loadUsers();
 
-  loadTemplateTypeOptions();
+  const queryBusinessType = route.query.businessType;
+
+  if (queryBusinessType != null && queryBusinessType !== "") {
+    searchForm.businessType = Number(queryBusinessType);
+  }
+
+  await loadTemplateTypeOptions();
 
   fetchTemplateList();
 
diff --git a/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js b/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js
index 79e96bc..d01534c 100644
--- a/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js
+++ b/src/views/officeProcessAutomation/ApproveManage/approve-template/useApproveTemplate.js
@@ -49,6 +49,7 @@
 
   const searchForm = reactive({
     keyword: "",
+    businessType: "",
     enabledOnly: false,
   });
 
@@ -176,6 +177,7 @@
 
   function resetSearch() {
     searchForm.keyword = "";
+    searchForm.businessType = "";
     searchForm.enabledOnly = false;
     handleQuery();
   }
diff --git a/src/views/officeProcessAutomation/AttendManage/out-apply/index.vue b/src/views/officeProcessAutomation/AttendManage/out-apply/index.vue
new file mode 100644
index 0000000..c434a20
--- /dev/null
+++ b/src/views/officeProcessAutomation/AttendManage/out-apply/index.vue
@@ -0,0 +1,174 @@
+<!--OA妯″潡锛氬叕鍑虹敵璇�-->
+<template>
+  <div class="app-container">
+    <div class="search_form mb20">
+      <div>
+        <span class="search_title">瀹℃壒鍗曞彿锛�</span>
+        <el-input
+          v-model="searchForm.instanceNo"
+          style="width: 220px"
+          placeholder="璇疯緭鍏ュ鎵瑰崟鍙�"
+          clearable
+          @keyup.enter="onSearch"
+        />
+        <span class="search_title" style="margin-left: 12px">鐢宠浜猴細</span>
+        <el-input
+          v-model="searchForm.applicantKeyword"
+          style="width: 220px"
+          placeholder="濮撳悕鎴栫紪鍙�"
+          clearable
+          :prefix-icon="Search"
+          @keyup.enter="onSearch"
+        />
+        <el-button type="primary" style="margin-left: 10px" @click="onSearch">鎼滅储</el-button>
+        <el-button @click="resetSearch">閲嶇疆</el-button>
+      </div>
+      <div class="search_actions">
+        <el-button type="primary" @click="openAddWithTemplate">鏂板鍏嚭鐢宠</el-button>
+      </div>
+    </div>
+
+    <div class="table_list">
+      <PIMTable
+        rowKey="id"
+        :column="tableColumn"
+        :tableData="tableData"
+        :page="page"
+        :isSelection="false"
+        :tableLoading="tableLoading"
+        @pagination="onPagination"
+        :total="page.total"
+      />
+    </div>
+
+    <ApprovalInstanceSubmitDialog
+      v-model="submitDialog.visible"
+      :title="submitDialogTitle"
+      :form="submitForm"
+      :rules="submitFormRules"
+      :fields="submitFormFields"
+      :active-template="activeTemplate"
+      :user-options="flowUserOptions"
+      :is-edit="isSubmitEdit"
+      :saving="submitSaving"
+      :form-ref="submitFormRef"
+      @submit="onSubmit"
+    />
+
+    <ApprovalTemplateBindDialog
+      v-model:visible="templateBindVisible"
+      :module-key="APPROVAL_MODULE_KEYS.OUT_OFFICE"
+      skip-form-confirm
+      @confirm="onTemplateBound"
+      @closed="onTemplateBindClosed"
+    />
+
+    <ApprovalInstanceDetailDialog
+      v-model="detailDialog.visible"
+      title="鍏嚭鐢宠璇︽儏"
+      :row="detailRow"
+      @edit="openEditFromDetail"
+    />
+  </div>
+</template>
+
+<script setup>
+import { Search } from "@element-plus/icons-vue";
+import { ElMessage } from "element-plus";
+import { onMounted, reactive } from "vue";
+import ApprovalInstanceDetailDialog from "../../ApproveManage/approve-shared/components/ApprovalInstanceDetailDialog.vue";
+import ApprovalInstanceSubmitDialog from "../../ApproveManage/approve-shared/components/ApprovalInstanceSubmitDialog.vue";
+import ApprovalTemplateBindDialog from "../../ApproveManage/approve-shared/components/ApprovalTemplateBindDialog.vue";
+import { buildInstanceTableColumns } from "../../ApproveManage/approve-shared/approvalInstanceFormConfigTable.js";
+import { APPROVAL_MODULE_KEYS } from "../../ApproveManage/approve-shared/approvalModuleRegistry.js";
+import { useApprovalInstanceModule } from "../../ApproveManage/approve-shared/useApprovalInstanceModule.js";
+import { useFlowUserOptions } from "../../ApproveManage/approve-shared/useFlowUserOptions.js";
+
+const searchForm = reactive({
+  instanceNo: "",
+  applicantKeyword: "",
+});
+
+const mod = useApprovalInstanceModule({
+  moduleKey: APPROVAL_MODULE_KEYS.OUT_OFFICE,
+});
+
+const {
+  tableData,
+  tableLoading,
+  page,
+  detailDialog,
+  detailRow,
+  submitDialog,
+  submitForm,
+  submitFormRef,
+  submitSaving,
+  isSubmitEdit,
+  activeTemplate,
+  submitFormFields,
+  submitFormRules,
+  submitDialogTitle,
+  templateBindVisible,
+  handleQuery,
+  initModuleList,
+  pagination,
+  openAddWithTemplate,
+  onTemplateBound,
+  onTemplateBindClosed,
+  openEditFromDetail,
+  submitInstanceForm,
+  buildTableActions,
+} = mod;
+
+const { flowUserOptions, loadFlowUsers } = useFlowUserOptions();
+
+const tableColumn = buildInstanceTableColumns(tableData, buildTableActions, {
+  moduleKey: APPROVAL_MODULE_KEYS.OUT_OFFICE,
+});
+
+function onSearch() {
+  handleQuery(searchForm);
+}
+
+function resetSearch() {
+  searchForm.instanceNo = "";
+  searchForm.applicantKeyword = "";
+  onSearch();
+}
+
+function onPagination(obj) {
+  pagination(obj, searchForm);
+}
+
+async function onSubmit() {
+  const ok = await submitInstanceForm({ skipValidate: true });
+  if (ok) ElMessage.success(isSubmitEdit.value ? "淇敼鎴愬姛" : "鎻愪氦鎴愬姛");
+}
+
+onMounted(async () => {
+  loadFlowUsers();
+  await initModuleList(searchForm);
+});
+</script>
+
+<style scoped>
+.mb20 {
+  margin-bottom: 20px;
+}
+.search_form {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px;
+}
+.search_actions {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+.search_title {
+  font-size: 14px;
+  color: var(--el-text-color-regular);
+}
+</style>
diff --git a/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue b/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue
index df70f4f..07f33e9 100644
--- a/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue
+++ b/src/views/officeProcessAutomation/HrManage/regular-apply/index.vue
@@ -130,6 +130,14 @@
 
 const tableColumn = buildInstanceTableColumns(tableData, buildTableActions, {
   moduleKey: APPROVAL_MODULE_KEYS.REGULAR,
+  beforeFormColumns: [
+    {
+      label: "鐢宠浜�",
+      prop: "applicantName",
+      width: 120,
+      showOverflowTooltip: true,
+    },
+  ],
 });
 
 function onSearch() {
diff --git a/src/views/officeProcessAutomation/HrManage/staff-archive/components/EducationWorkSection.vue b/src/views/officeProcessAutomation/HrManage/staff-archive/components/EducationWorkSection.vue
index c1470e7..eee2d9c 100644
--- a/src/views/officeProcessAutomation/HrManage/staff-archive/components/EducationWorkSection.vue
+++ b/src/views/officeProcessAutomation/HrManage/staff-archive/components/EducationWorkSection.vue
@@ -17,6 +17,9 @@
               clearable
               style="width: 100%"
             >
+              <el-option label="鍒濅腑" value="middle_school" />
+              <el-option label="楂樹腑" value="high_school" />
+              <el-option label="涓笓" value="technical_secondary" />
               <el-option label="涓笓鍙婁互涓�" value="secondary" />
               <el-option label="澶т笓" value="junior_college" />
               <el-option label="鏈" value="bachelor" />
diff --git a/src/views/personnelManagement/contractManagement/index.vue b/src/views/personnelManagement/contractManagement/index.vue
index bde498a..6e42c8b 100644
--- a/src/views/personnelManagement/contractManagement/index.vue
+++ b/src/views/personnelManagement/contractManagement/index.vue
@@ -169,16 +169,6 @@
       prop: "phone",
       width: 150,
     },
-    {
-      label: "绱ф�ヨ仈绯讳汉",
-      prop: "emergencyContact",
-      width: 120,
-    },
-    {
-      label: "绱ф�ヨ仈绯讳汉鐢佃瘽",
-      prop: "emergencyContactPhone",
-      width: 150,
-    },
     // {
     //   label: "鍚堝悓骞撮檺",
     //   prop: "contractTerm",
diff --git a/src/views/personnelManagement/employeeRecord/components/EducationWorkSection.vue b/src/views/personnelManagement/employeeRecord/components/EducationWorkSection.vue
index c1470e7..eee2d9c 100644
--- a/src/views/personnelManagement/employeeRecord/components/EducationWorkSection.vue
+++ b/src/views/personnelManagement/employeeRecord/components/EducationWorkSection.vue
@@ -17,6 +17,9 @@
               clearable
               style="width: 100%"
             >
+              <el-option label="鍒濅腑" value="middle_school" />
+              <el-option label="楂樹腑" value="high_school" />
+              <el-option label="涓笓" value="technical_secondary" />
               <el-option label="涓笓鍙婁互涓�" value="secondary" />
               <el-option label="澶т笓" value="junior_college" />
               <el-option label="鏈" value="bachelor" />
diff --git a/src/views/personnelManagement/monthlyStatistics/index.vue b/src/views/personnelManagement/monthlyStatistics/index.vue
index f581070..54a4918 100644
--- a/src/views/personnelManagement/monthlyStatistics/index.vue
+++ b/src/views/personnelManagement/monthlyStatistics/index.vue
@@ -93,6 +93,64 @@
       @close="auditDialogVisible = false"
       @success="handleAuditSuccess"
     />
+    <el-dialog v-model="salaryDetailVisible" title="浜哄憳宸ヨ祫鏄庣粏" width="1100px">
+      <div class="salary-detail-title">
+        {{ salaryDetailRow.salaryTitle || "-" }}锛坽{ salaryDetailRow.salaryMonth || "-" }}锛�
+      </div>
+      <el-table :data="salaryDetailRow.staffSalaryDetailList || []" border style="width: 100%">
+        <el-table-column prop="staffName" label="濮撳悕" width="90" />
+        <el-table-column prop="deptName" label="閮ㄩ棬" width="110" />
+        <el-table-column prop="postName" label="宀椾綅" width="110" />
+        <el-table-column prop="basicSalary" label="鍩烘湰宸ヨ祫" width="100" align="right" />
+        <el-table-column prop="pieceSalary" label="璁′欢宸ヨ祫" width="100" align="right" />
+        <el-table-column prop="otherIncome" label="鍏朵粬鏀跺叆" width="100" align="right" />
+        <el-table-column prop="socialPersonal" label="绀句繚(涓汉)" width="100" align="right" />
+        <el-table-column prop="fundPersonal" label="鍏Н閲�(涓汉)" width="110" align="right" />
+        <el-table-column prop="salaryTax" label="涓◣" width="90" align="right" />
+        <el-table-column prop="grossSalary" label="搴斿彂宸ヨ祫" width="110" align="right">
+          <template #default="{ row }">{{ formatAmount(row.grossSalary) }}</template>
+        </el-table-column>
+        <el-table-column prop="deductSalary" label="鎵f鍚堣" width="100" align="right" />
+        <el-table-column prop="netSalary" label="瀹炲彂宸ヨ祫" width="110" align="right">
+          <template #default="{ row }">{{ formatAmount(row.netSalary) }}</template>
+        </el-table-column>
+      </el-table>
+      <template #footer>
+        <el-button @click="salaryDetailVisible = false">鍏抽棴</el-button>
+      </template>
+    </el-dialog>
+    <el-dialog v-model="issueRecordVisible" title="鍙戞斁璁板綍" width="820px" append-to-body>
+      <template v-if="issueRecordRow">
+        <el-descriptions :column="2" border>
+          <el-descriptions-item label="宸ヨ祫涓婚">{{ issueRecordRow.salaryTitle }}</el-descriptions-item>
+          <el-descriptions-item label="宸ヨ祫鏈堜唤">{{ issueRecordRow.salaryMonth }}</el-descriptions-item>
+          <el-descriptions-item label="宸ヨ祫鎬婚">{{ formatAmount(issueRecordRow.totalSalary) }}</el-descriptions-item>
+          <el-descriptions-item label="鍙戞斁閾惰">{{ issueRecordRow.payBank || "-" }}</el-descriptions-item>
+          <el-descriptions-item label="鍙戞斁鏃堕棿">{{ issueRecordRow.payTime || "-" }}</el-descriptions-item>
+          <el-descriptions-item label="瀹℃牳浜�">{{ issueRecordRow.auditUserName || "-" }}</el-descriptions-item>
+          <el-descriptions-item label="澶囨敞" :span="2">{{ issueRecordRow.remark || "-" }}</el-descriptions-item>
+        </el-descriptions>
+        <div class="issue-record-title">鍙戞斁鏄庣粏锛坽{ issueRecordDetails.length }} 浜猴級</div>
+        <el-table :data="issueRecordDetails" border size="small" max-height="360">
+          <el-table-column prop="staffName" label="濮撳悕" width="90" />
+          <el-table-column prop="deptName" label="閮ㄩ棬" width="110" />
+          <el-table-column prop="postName" label="宀椾綅" width="110" />
+          <el-table-column label="搴斿彂宸ヨ祫" width="110" align="right">
+            <template #default="{ row }">{{ formatAmount(row.grossSalary) }}</template>
+          </el-table-column>
+          <el-table-column label="鎵f鍚堣" width="110" align="right">
+            <template #default="{ row }">{{ formatAmount(row.deductSalary) }}</template>
+          </el-table-column>
+          <el-table-column label="瀹炲彂宸ヨ祫" width="110" align="right">
+            <template #default="{ row }">{{ formatAmount(row.netSalary) }}</template>
+          </el-table-column>
+          <el-table-column prop="remark" label="澶囨敞" show-overflow-tooltip />
+        </el-table>
+      </template>
+      <template #footer>
+        <el-button @click="issueRecordVisible = false">鍏抽棴</el-button>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -149,6 +207,7 @@
   },
   { label: "宸ヨ祫鎬婚", prop: "totalSalary", width: 120 },
   { label: "鏀粯閾惰", prop: "payBank", width: 120 },
+  { label: "鍙戞斁鏃堕棿", prop: "payTime", width: 160, formatData: (v) => v || "-" },
   { label: "瀹℃牳浜�", prop: "auditUserName", width: 110 },
   { label: "澶囨敞", prop: "remark", minWidth: 120 },
   {
@@ -156,8 +215,13 @@
     label: "鎿嶄綔",
     align: "center",
     fixed: "right",
-    width: 180,
+    width: 240,
     operation: [
+      {
+        name: "鏌ョ湅鏄庣粏",
+        type: "text",
+        clickFun: (row) => openSalaryDetail(row),
+      },
       {
         name: "缂栬緫",
         type: "text",
@@ -180,6 +244,12 @@
         type: "text",
         disabled: (row) => Number(row?.status) !== 4,
         clickFun: (row) => openIssue(row),
+      },
+      {
+        name: "鍙戞斁璁板綍",
+        type: "text",
+        disabled: (row) => Number(row?.status) !== 5,
+        clickFun: (row) => openIssueRecord(row),
       },
     ],
   },
@@ -209,6 +279,28 @@
 const auditDialogVisible = ref(false);
 const auditRow = ref(null);
 const auditDiaRef = ref(null);
+const salaryDetailVisible = ref(false);
+const salaryDetailRow = ref({});
+const issueRecordVisible = ref(false);
+const issueRecordRow = ref(null);
+
+const issueRecordDetails = computed(() =>
+  Array.isArray(issueRecordRow.value?.staffSalaryDetailList)
+    ? issueRecordRow.value.staffSalaryDetailList
+    : []
+);
+
+const formatAmount = (v) => (v == null || v === "" ? "-" : Number(v).toFixed(2));
+
+const openIssueRecord = (row) => {
+  issueRecordRow.value = row || null;
+  issueRecordVisible.value = true;
+};
+
+const openSalaryDetail = (row) => {
+  salaryDetailRow.value = row || {};
+  salaryDetailVisible.value = true;
+};
 
 const issueBankOptions = computed(() => {
   const options = Array.isArray(bankSetting.value?.options) ? bankSetting.value.options : [];
@@ -410,4 +502,13 @@
 .table_list {
   margin-top: 20px;
 }
+.salary-detail-title {
+  margin-bottom: 12px;
+  font-weight: 600;
+}
+.issue-record-title {
+  margin: 16px 0 8px;
+  font-weight: 600;
+  font-size: 14px;
+}
 </style>
\ No newline at end of file
diff --git a/src/views/procurementManagement/thePaymentLedger/index.vue b/src/views/procurementManagement/thePaymentLedger/index.vue
index 4031ea7..dae778f 100644
--- a/src/views/procurementManagement/thePaymentLedger/index.vue
+++ b/src/views/procurementManagement/thePaymentLedger/index.vue
@@ -55,8 +55,7 @@
   {
     label: "鐧昏鏃ユ湡",
     prop: "registrationtDate",
-    formatData: cell =>
-      cell ? dayjs(cell).format("YYYY-MM-DD HH:mm:ss") : "-",
+    formatData: cell => (cell ? dayjs(cell).format("YYYY-MM-DD") : "-"),
   },
 ]);
 const tableData = ref([]);
diff --git a/src/views/productionManagement/workOrder/index.vue b/src/views/productionManagement/workOrder/index.vue
index d3b0f93..7b93c29 100644
--- a/src/views/productionManagement/workOrder/index.vue
+++ b/src/views/productionManagement/workOrder/index.vue
@@ -12,6 +12,15 @@
                     prefix-icon="Search" />
         </div>
         <div class="search-item">
+          <span class="search_title">鐢熶骇璁㈠崟鍙凤細</span>
+          <el-input v-model="searchForm.productOrderNpsNo"
+                    style="width: 240px"
+                    placeholder="璇疯緭鍏�"
+                    @change="handleQuery"
+                    clearable
+                    prefix-icon="Search" />
+        </div>
+        <div class="search-item">
           <el-button type="primary"
                      @click="handleQuery">鎼滅储</el-button>
         </div>
@@ -467,6 +476,7 @@
   const data = reactive({
     searchForm: {
       workOrderNo: "",
+      productOrderNpsNo: "",
     },
   });
   const { searchForm } = toRefs(data);
diff --git a/src/views/productionPlan/productionPlan/components/PIMTable.vue b/src/views/productionPlan/productionPlan/components/PIMTable.vue
index 5f5eea5..06cfb80 100644
--- a/src/views/productionPlan/productionPlan/components/PIMTable.vue
+++ b/src/views/productionPlan/productionPlan/components/PIMTable.vue
@@ -183,16 +183,20 @@
              class="cell link"
              style="width: 100%"
              @click="goLink(scope.row, item.linkMethod)">
-          <span v-if="!item.formatData">{{ scope.row[item.prop] }}</span>
+          <span v-if="!item.formatData">{{
+            toDateOnly(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-if="!item.formatData">{{
+            toDateOnly(scope.row[item.prop])
+          }}</span>
           <span v-else>{{
-            formatters(scope.row[item.prop], item.formatData)
+            toDateOnly(formatters(scope.row[item.prop], item.formatData))
           }}</span>
         </div>
       </template>
@@ -210,6 +214,7 @@
   import pagination from "../../../../components/PIMTable/Pagination.vue";
   import { ref, inject, getCurrentInstance } from "vue";
   import { ElMessage } from "element-plus";
+  import { toDateOnly } from "@/utils/dateDisplay";
 
   // 鑾峰彇鍏ㄥ眬鐨� uploadHeader
   const { proxy } = getCurrentInstance();
diff --git a/src/views/qualityManagement/afterSalesTraceability/index.vue b/src/views/qualityManagement/afterSalesTraceability/index.vue
index 8cd4856..ca133a0 100644
--- a/src/views/qualityManagement/afterSalesTraceability/index.vue
+++ b/src/views/qualityManagement/afterSalesTraceability/index.vue
@@ -123,7 +123,7 @@
       <el-table-column label="鑱旂郴鏂瑰紡" prop="contact" width="140" />
       <el-table-column label="鍙嶉鏃堕棿" prop="feedbackTime" width="160">
         <template #default="scope">
-          <span>{{ scope.row.feedbackTime }}</span>
+          <span>{{ formatDateOnly(scope.row.feedbackTime) }}</span>
         </template>
       </el-table-column>
       <el-table-column label="闂鎻忚堪" prop="problemDesc" show-overflow-tooltip />
diff --git a/src/views/qualityManagement/finalInspection/components/formDia.vue b/src/views/qualityManagement/finalInspection/components/formDia.vue
index 94801ff..ee21d5e 100644
--- a/src/views/qualityManagement/finalInspection/components/formDia.vue
+++ b/src/views/qualityManagement/finalInspection/components/formDia.vue
@@ -173,6 +173,7 @@
                 :column="tableColumn"
                 :tableData="tableData"
                 :tableLoading="tableLoading"
+                :isShowPagination="false"
                 height="400">
         <template #slot="{ row }">
           <el-input v-model="row.testValue"
diff --git a/src/views/qualityManagement/processInspection/components/formDia.vue b/src/views/qualityManagement/processInspection/components/formDia.vue
index 370b5f6..82d4ac3 100644
--- a/src/views/qualityManagement/processInspection/components/formDia.vue
+++ b/src/views/qualityManagement/processInspection/components/formDia.vue
@@ -188,6 +188,7 @@
                 :column="tableColumn"
                 :tableData="tableData"
                 :tableLoading="tableLoading"
+                :isShowPagination="false"
                 height="400">
         <template #slot="{ row }">
           <el-input v-model="row.testValue"
diff --git a/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue b/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
index 595bf80..9df0185 100644
--- a/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
+++ b/src/views/qualityManagement/rawMaterialInspection/components/formDia.vue
@@ -190,6 +190,7 @@
                 :column="tableColumn"
                 :tableData="tableData"
                 :tableLoading="tableLoading"
+                :isShowPagination="false"
                 height="400">
         <template #slot="{ row }">
           <el-input v-model="row.testValue"
diff --git a/src/views/qualityManagement/rawMaterialInspection/index.vue b/src/views/qualityManagement/rawMaterialInspection/index.vue
index 6d2acd5..2998f3a 100644
--- a/src/views/qualityManagement/rawMaterialInspection/index.vue
+++ b/src/views/qualityManagement/rawMaterialInspection/index.vue
@@ -197,11 +197,6 @@
       width: 120,
     },
     {
-      label: "妫�娴嬪崟浣�",
-      prop: "checkCompany",
-      width: 120,
-    },
-    {
       label: "妫�娴嬬粨鏋�",
       prop: "checkResult",
       dataType: "tag",
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue b/src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue
index a7d0174..a092cd8 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue
@@ -97,7 +97,7 @@
               >
                 <div class="todo-title">鐢宠绫诲瀷锛歿{ item.approveTypeName }}</div>
                 <div class="todo-division">鐢宠閮ㄩ棬锛歿{ item.approveDeptName }}</div>
-                <div class="todo-time">{{ item.approveTime }}</div>
+                <div class="todo-time">{{ formatDateOnly(item.approveTime) }}</div>
               </div>
               
             </div>
diff --git a/src/views/reportAnalysis/safetyDashboard/components/center-bottom.vue b/src/views/reportAnalysis/safetyDashboard/components/center-bottom.vue
new file mode 100644
index 0000000..9d8036b
--- /dev/null
+++ b/src/views/reportAnalysis/safetyDashboard/components/center-bottom.vue
@@ -0,0 +1,217 @@
+<template>
+  <div>
+    <PanelHeader title="鍗遍櫓婧愰闄╃瓑绾у垎甯�" />
+    <div class="panel-item-customers">
+      <div class="pie-chart-wrapper">
+        <div class="pie-background" :style="{ backgroundImage: `url(${roseBorderImg})` }"></div>
+        <Echarts
+          ref="echartsRef"
+          :chartStyle="chartStyle"
+          :legend="pieLegend"
+          :series="pieSeries"
+          :tooltip="pieTooltip"
+          :color="pieColors"
+          :options="pieOptions"
+          style="height: 320px"
+        />
+        <div v-if="showEmpty" class="empty-tip">鏆傛棤鏁版嵁</div>
+      </div>
+      <span v-if="refreshFailed" class="refresh-error">鍒锋柊澶辫触</span>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, inject, watch } from 'vue'
+import { getHazardRiskDistribution } from '@/api/reportAnalysis/safetyDashboard.js'
+import PanelHeader from '@/views/reportAnalysis/dataDashboard/components/PanelHeader.vue'
+import Echarts from '@/components/Echarts/echarts.vue'
+import roseBorderImg from '@/assets/BI/鐜懓鍥捐竟妗�.png'
+
+const echartsRef = ref(null)
+const riskRows = ref([])
+const loaded = ref(false)
+const loading = ref(false)
+const refreshFailed = ref(false)
+
+const pieColors = ['#ff5676', '#ffce64', '#43e8fc', '#82BAFF', '#D1E4F5']
+
+const chartStyle = {
+  width: '100%',
+  height: '100%'
+}
+
+const total = computed(() =>
+  riskRows.value.reduce((sum, item) => sum + (Number(item.value) || 0), 0)
+)
+
+const showEmpty = computed(() => loaded.value && !loading.value && total.value === 0)
+
+const objectData = computed(() => {
+  const map = {}
+  riskRows.value.forEach((item) => {
+    map[item.name] = item
+  })
+  return map
+})
+
+const pieLegend = computed(() => ({
+  orient: 'vertical',
+  top: 'center',
+  left: '50%',
+  itemGap: 30,
+  icon: 'circle',
+  data: riskRows.value.map((item, idx) => ({
+    name: item.name,
+    icon: 'circle',
+    textStyle: {
+      fontSize: 18,
+      color: pieColors[idx % pieColors.length]
+    }
+  })),
+  formatter: function (name) {
+    const item = objectData.value[name]
+    if (!item) return name
+    return `{title|${name}}{value|${item.value}}{unit|澶剗{percent|${item.rate ?? '0.00'}}{unit|%}`
+  },
+  textStyle: {
+    rich: {
+      value: {
+        color: '#43e8fc',
+        fontSize: 18,
+        fontWeight: 600,
+        padding: [0, 10, 0, 30]
+      },
+      unit: {
+        color: '#82baff',
+        fontSize: 14,
+        fontWeight: 600,
+        padding: [0, 50, 0, 0]
+      },
+      percent: {
+        color: '#43e8fc',
+        fontSize: 18,
+        fontWeight: 600,
+        padding: [0, 10, 0, 0]
+      },
+      title: {
+        fontSize: 18,
+        padding: [0, 0, 0, 0]
+      }
+    }
+  }
+}))
+
+const pieTooltip = {
+  trigger: 'item',
+  formatter: '{a} <br/>{b}锛歿c} 澶勶紙{d}%锛�'
+}
+
+const pieSeries = computed(() => [
+  {
+    name: '鍗遍櫓婧愰闄╃瓑绾�',
+    type: 'pie',
+    radius: '70%',
+    center: ['20%', '50%'],
+    itemStyle: {
+      borderColor: '#0a1c3a',
+      borderWidth: 5
+    },
+    label: {
+      show: false
+    },
+    minAngle: 15,
+    data: riskRows.value.map((item) => ({
+      name: item.name,
+      value: Number(item.value) || 0,
+      rate: item.rate
+    })),
+    animationType: 'scale',
+    animationEasing: 'elasticOut'
+  }
+])
+
+const pieOptions = {
+  backgroundColor: 'transparent',
+  textStyle: { color: '#B8C8E0' }
+}
+
+const loadData = async () => {
+  if (loading.value) return
+  loading.value = true
+  try {
+    const res = await getHazardRiskDistribution()
+    riskRows.value = Array.isArray(res?.data) ? res.data : []
+    refreshFailed.value = false
+    loaded.value = true
+  } catch (e) {
+    console.error('鑾峰彇鍗遍櫓婧愰闄╃瓑绾у垎甯冨け璐�:', e)
+    refreshFailed.value = true
+  } finally {
+    loading.value = false
+  }
+}
+
+const safetyDashboardRefreshTick = inject('safetyDashboardRefreshTick', null)
+if (safetyDashboardRefreshTick) {
+  watch(safetyDashboardRefreshTick, () => {
+    loadData()
+  })
+}
+
+onMounted(() => {
+  loadData()
+})
+</script>
+
+<style scoped>
+.panel-item-customers {
+  position: relative;
+  border: 1px solid #1a58b0;
+  padding: 18px;
+  width: 100%;
+  height: 370px;
+  box-sizing: border-box;
+}
+
+.pie-chart-wrapper {
+  position: relative;
+  width: 100%;
+  height: 320px;
+  background: transparent;
+}
+
+.pie-background {
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  transform: translate(-113%, -50%);
+  width: 360px;
+  height: 360px;
+  background-size: contain;
+  background-position: center;
+  background-repeat: no-repeat;
+  z-index: 1;
+  pointer-events: none;
+}
+
+.empty-tip {
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: 44%;
+  width: 40%;
+  text-align: center;
+  font-size: 15px;
+  color: #8aa4c8;
+  pointer-events: none;
+}
+
+.refresh-error {
+  position: absolute;
+  right: 16px;
+  top: 8px;
+  font-size: 12px;
+  color: #ff9a9a;
+}
+</style>
diff --git a/src/views/reportAnalysis/safetyDashboard/components/center-top.vue b/src/views/reportAnalysis/safetyDashboard/components/center-top.vue
new file mode 100644
index 0000000..278490a
--- /dev/null
+++ b/src/views/reportAnalysis/safetyDashboard/components/center-top.vue
@@ -0,0 +1,432 @@
+<template>
+  <div class="center-top">
+    <div class="stats-cards">
+      <div class="stat-card">
+        <img src="@/assets/BI/icon@2x.png" alt="鍥炬爣" class="card-icon" />
+        <div class="card-content">
+          <span class="card-label">闅愭偅鎬绘暟</span>
+          <span class="card-value">{{ overviewValue(overview.hiddenTotal) }}</span>
+          <div class="card-sub">
+            <span>寰呮暣鏀� {{ overviewValue(overview.hiddenPending) }}</span>
+            <span class="card-sub-divider">/</span>
+            <span>宸叉暣鏀� {{ overviewValue(overview.hiddenRectified) }}</span>
+          </div>
+        </div>
+      </div>
+      <div class="stat-card">
+        <img src="@/assets/BI/icon@2x.png" alt="鍥炬爣" class="card-icon" />
+        <div class="card-content">
+          <span class="card-label">鍗遍櫓婧愭�绘暟</span>
+          <span class="card-value">{{ overviewValue(overview.hazardTotal) }}</span>
+          <div class="card-sub">
+            <span>閲嶅ぇ椋庨櫓 {{ overviewValue(overview.hazardMajor) }}</span>
+          </div>
+        </div>
+      </div>
+      <div class="stat-card">
+        <img src="@/assets/BI/icon@2x.png" alt="鍥炬爣" class="card-icon" />
+        <div class="card-content">
+          <span class="card-label">浜嬫晠鎬绘暟</span>
+          <span class="card-value">{{ overviewValue(overview.accidentTotal) }}</span>
+          <div class="card-sub">
+            <span>鏈勾 {{ overviewValue(overview.accidentThisYear) }}</span>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="event-info">
+      <div class="event-header">
+        <img src="@/assets/BI/shijianmingxiicon@2x.png" alt="鍥炬爣" class="event-icon" />
+        <span class="event-title">瀹夊叏鍔ㄦ��</span>
+        <span v-if="refreshFailed" class="refresh-error">鍒锋柊澶辫触</span>
+      </div>
+
+      <div class="metrics-row">
+        <div class="metric" v-for="item in metrics" :key="item.label">
+          <span class="metric-value">{{ item.value }}</span>
+          <span class="metric-label">{{ item.label }}</span>
+        </div>
+      </div>
+
+      <div class="event-content">
+        <ul class="event-list" v-if="displayEvents.length > 0" ref="listRef">
+          <li v-for="(item, index) in displayEvents" :key="index">
+            <div class="event-line">
+              <span class="event-cat" :class="catClass(item.category)">{{ item.category }}</span>
+              <span class="event-name">{{ item.title }}</span>
+              <span class="event-status">{{ item.statusText }}</span>
+            </div>
+            <div class="event-meta">
+              <span>{{ item.location || '鈥�' }}</span>
+              <span>{{ item.eventTime }}</span>
+            </div>
+          </li>
+        </ul>
+        <div v-else class="event-empty">
+          {{ eventsFailed ? '鏁版嵁鍔犺浇澶辫触锛�60绉掑悗鑷姩閲嶈瘯' : '鏆傛棤瀹夊叏鍔ㄦ��' }}
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, onBeforeUnmount, nextTick, inject, watch } from 'vue'
+import {
+  getSafetyOverview,
+  getLatestSafeEvents
+} from '@/api/reportAnalysis/safetyDashboard.js'
+
+const overview = ref({
+  hiddenTotal: 0,
+  hiddenPending: 0,
+  hiddenRectified: 0,
+  hazardTotal: 0,
+  hazardMajor: 0,
+  accidentTotal: 0,
+  accidentThisYear: 0,
+  trainingCount: 0,
+  trainingPersonTimes: 0,
+  planCount: 0,
+  certCount: 0
+})
+const overviewLoaded = ref(false)
+const overviewFailed = ref(false)
+
+const events = ref([])
+const eventsFailed = ref(false)
+
+const listRef = ref(null)
+const loading = ref(false)
+
+const refreshFailed = computed(() => overviewFailed.value || eventsFailed.value)
+
+const overviewValue = (val) => (overviewLoaded.value ? Number(val) || 0 : '--')
+
+const metrics = computed(() => [
+  { label: '鍩硅鍦烘', value: overviewValue(overview.value.trainingCount) },
+  { label: '鍙傝浜烘', value: overviewValue(overview.value.trainingPersonTimes) },
+  { label: '搴旀�ラ妗�', value: overviewValue(overview.value.planCount) },
+  { label: '瀹夊叏璧勮川', value: overviewValue(overview.value.certCount) }
+])
+
+const displayEvents = computed(() => {
+  if (events.value.length >= 4) {
+    return [...events.value, ...events.value]
+  }
+  return events.value
+})
+
+const catClass = (category) => {
+  if (category === '浜嬫晠') return 'cat-accident'
+  if (category === '鍗遍櫓鐗╂枡棰嗙敤') return 'cat-material'
+  return 'cat-hidden'
+}
+
+let rafId = null
+
+const stopScroll = () => {
+  if (rafId) {
+    cancelAnimationFrame(rafId)
+    rafId = null
+  }
+}
+
+const startScroll = () => {
+  stopScroll()
+  nextTick(() => {
+    const el = listRef.value
+    if (!el || events.value.length < 4) return
+    const half = el.scrollHeight / 2
+    if (half <= el.clientHeight) return
+    let position = 0
+    const step = () => {
+      position += 0.4
+      if (position >= half) {
+        position = 0
+        el.scrollTop = 0
+      } else {
+        el.scrollTop = position
+      }
+      rafId = requestAnimationFrame(step)
+    }
+    rafId = requestAnimationFrame(step)
+  })
+}
+
+const loadOverview = async () => {
+  try {
+    const res = await getSafetyOverview()
+    overview.value = {
+      hiddenTotal: 0,
+      hiddenPending: 0,
+      hiddenRectified: 0,
+      hazardTotal: 0,
+      hazardMajor: 0,
+      accidentTotal: 0,
+      accidentThisYear: 0,
+      trainingCount: 0,
+      trainingPersonTimes: 0,
+      planCount: 0,
+      certCount: 0,
+      ...(res?.data || {})
+    }
+    overviewLoaded.value = true
+    overviewFailed.value = false
+  } catch (e) {
+    console.error('鑾峰彇瀹夊叏鎬昏澶辫触:', e)
+    overviewFailed.value = true
+  }
+}
+
+const loadEvents = async () => {
+  try {
+    const res = await getLatestSafeEvents({ limit: 10 })
+    stopScroll()
+    events.value = Array.isArray(res?.data) ? res.data : []
+    eventsFailed.value = false
+    startScroll()
+  } catch (e) {
+    console.error('鑾峰彇鏈�鏂板畨鍏ㄥ姩鎬佸け璐�:', e)
+    eventsFailed.value = true
+  }
+}
+
+const refreshData = async () => {
+  if (loading.value) return
+  loading.value = true
+  await Promise.allSettled([loadOverview(), loadEvents()])
+  loading.value = false
+}
+
+const safetyDashboardRefreshTick = inject('safetyDashboardRefreshTick', null)
+if (safetyDashboardRefreshTick) {
+  watch(safetyDashboardRefreshTick, () => {
+    refreshData()
+  })
+}
+
+onMounted(() => {
+  refreshData()
+})
+
+onBeforeUnmount(() => {
+  stopScroll()
+})
+</script>
+
+<style scoped>
+.center-top {
+  flex: 1;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+}
+
+.stats-cards {
+  display: flex;
+  gap: 30px;
+}
+
+.stat-card {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  background-image: url('@/assets/BI/border@2x.png');
+  background-size: 100% 100%;
+  background-position: center;
+  background-repeat: no-repeat;
+  height: 142px;
+}
+
+.card-icon {
+  width: 100px;
+  height: 100px;
+  margin: 20px 20px 0 10px;
+}
+
+.card-content {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+.card-value {
+  font-weight: 500;
+  font-size: 40px;
+  background: linear-gradient(360deg, #008bfd 0%, #ffffff 100%);
+  -webkit-background-clip: text;
+  -webkit-text-fill-color: transparent;
+  background-clip: text;
+}
+
+.card-label {
+  font-weight: 400;
+  font-size: 19px;
+  color: rgba(208, 231, 255, 0.7);
+}
+
+.card-sub {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  font-size: 14px;
+  color: #d0e7ff;
+  opacity: 0.9;
+}
+
+.card-sub-divider {
+  opacity: 0.5;
+}
+
+.event-info {
+  flex: 1;
+  min-height: 220px;
+  display: flex;
+  flex-direction: column;
+  background-image: url('@/assets/BI/shijianmingchengbeijing@2x.png');
+  background-size: 100% 100%;
+  background-position: center;
+  background-repeat: no-repeat;
+  padding: 12px 20px 18px;
+}
+
+.event-header {
+  display: flex;
+  align-items: center;
+  position: relative;
+}
+
+.event-icon {
+  width: 40px;
+  height: 40px;
+}
+
+.event-title {
+  font-weight: 500;
+  font-size: 18px;
+  color: #fffffe;
+  line-height: 30px;
+}
+
+.refresh-error {
+  margin-left: 12px;
+  font-size: 12px;
+  color: #ff9a9a;
+}
+
+.metrics-row {
+  display: flex;
+  gap: 12px;
+  margin: 8px 0 12px;
+}
+
+.metric {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 4px;
+  padding: 8px 0;
+  border-radius: 6px;
+  background: rgba(24, 93, 190, 0.18);
+  border: 1px solid rgba(64, 140, 235, 0.25);
+}
+
+.metric-value {
+  font-size: 22px;
+  font-weight: 600;
+  color: #43e8fc;
+}
+
+.metric-label {
+  font-size: 13px;
+  color: rgba(208, 231, 255, 0.75);
+}
+
+.event-content {
+  flex: 1;
+  min-height: 0;
+  overflow: hidden;
+}
+
+.event-list {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+  height: 100%;
+  overflow: hidden;
+}
+
+.event-list li {
+  padding: 8px 12px;
+  margin-bottom: 8px;
+  border-radius: 6px;
+  background: rgba(24, 93, 190, 0.12);
+}
+
+.event-line {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+
+.event-cat {
+  flex: none;
+  padding: 1px 8px;
+  border-radius: 10px;
+  font-size: 12px;
+  line-height: 18px;
+}
+
+.cat-hidden {
+  color: #4ee4ff;
+  background: rgba(78, 228, 255, 0.15);
+}
+
+.cat-accident {
+  color: #ff7f97;
+  background: rgba(255, 127, 151, 0.15);
+}
+
+.cat-material {
+  color: #ffce64;
+  background: rgba(255, 206, 100, 0.15);
+}
+
+.event-name {
+  flex: 1;
+  min-width: 0;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  font-size: 15px;
+  color: #fffffe;
+}
+
+.event-status {
+  flex: none;
+  font-size: 13px;
+  color: rgba(208, 231, 255, 0.75);
+}
+
+.event-meta {
+  display: flex;
+  justify-content: space-between;
+  margin-top: 4px;
+  padding-left: 2px;
+  font-size: 12px;
+  color: rgba(184, 200, 224, 0.7);
+}
+
+.event-empty {
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 15px;
+  color: #8aa4c8;
+}
+</style>
diff --git a/src/views/reportAnalysis/safetyDashboard/components/left-bottom.vue b/src/views/reportAnalysis/safetyDashboard/components/left-bottom.vue
new file mode 100644
index 0000000..6266f83
--- /dev/null
+++ b/src/views/reportAnalysis/safetyDashboard/components/left-bottom.vue
@@ -0,0 +1,206 @@
+<template>
+  <div>
+    <PanelHeader title="杩�6涓湀闅愭偅鎺掓煡涓庢暣鏀硅秼鍔�" />
+    <div class="panel-item-customers">
+      <Echarts
+        ref="chart"
+        :chartStyle="chartStyle"
+        :grid="grid"
+        :legend="legend"
+        :series="series"
+        :tooltip="tooltip"
+        :xAxis="xAxis"
+        :yAxis="yAxis"
+        :options="chartOptions"
+        style="height: 380px"
+      />
+      <div v-if="showEmpty" class="empty-tip">杩�6涓湀鏆傛棤闅愭偅鏁版嵁</div>
+      <span v-if="refreshFailed" class="refresh-error">鍒锋柊澶辫触</span>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, inject, watch } from 'vue'
+import Echarts from '@/components/Echarts/echarts.vue'
+import PanelHeader from '@/views/reportAnalysis/dataDashboard/components/PanelHeader.vue'
+import { getHiddenTrend } from '@/api/reportAnalysis/safetyDashboard.js'
+
+const chart = ref(null)
+const trendData = ref([])
+const loaded = ref(false)
+const loading = ref(false)
+const refreshFailed = ref(false)
+
+const chartStyle = {
+  width: '100%',
+  height: '100%'
+}
+
+const chartOptions = {
+  backgroundColor: 'transparent',
+  textStyle: { color: '#B8C8E0' }
+}
+
+const showEmpty = computed(() => {
+  if (!loaded.value || loading.value) return false
+  return trendData.value.every(
+    (item) => !Number(item.reportedCount) && !Number(item.completedCount)
+  )
+})
+
+const categories = computed(() =>
+  trendData.value.map((item) => {
+    const month = String(item.month || '')
+    return month.length >= 7 ? `${month.slice(5)}鏈坄 : month
+  })
+)
+
+const series = computed(() => [
+  {
+    name: '涓婃姤鏁�',
+    type: 'bar',
+    barWidth: 18,
+    itemStyle: {
+      color: {
+        type: 'linear',
+        x: 0,
+        y: 1,
+        x2: 0,
+        y2: 0,
+        colorStops: [
+          { offset: 0, color: 'rgba(0,164,237,0)' },
+          { offset: 1, color: '#4EE4FF' }
+        ]
+      }
+    },
+    data: trendData.value.map((item) => Number(item.reportedCount) || 0)
+  },
+  {
+    name: '鏁存敼瀹屾垚鏁�',
+    type: 'bar',
+    barWidth: 18,
+    itemStyle: {
+      color: {
+        type: 'linear',
+        x: 0,
+        y: 1,
+        x2: 0,
+        y2: 0,
+        colorStops: [
+          { offset: 0, color: 'rgba(38,255,203,0)' },
+          { offset: 1, color: '#26FFCB' }
+        ]
+      }
+    },
+    data: trendData.value.map((item) => Number(item.completedCount) || 0)
+  }
+])
+
+const legend = {
+  show: true,
+  right: '4%',
+  top: '0%',
+  itemWidth: 10,
+  itemHeight: 10,
+  icon: 'circle',
+  textStyle: { color: '#B8C8E0', fontSize: 13 },
+  data: ['涓婃姤鏁�', '鏁存敼瀹屾垚鏁�']
+}
+
+const grid = {
+  left: '3%',
+  right: '4%',
+  bottom: '3%',
+  top: '18%',
+  containLabel: true
+}
+
+const tooltip = {
+  trigger: 'axis',
+  axisPointer: { type: 'shadow' },
+  formatter: function (params) {
+    let result = `${params[0].axisValueLabel}<br/>`
+    params.forEach((item) => {
+      result += `<div>${item.marker} ${item.seriesName}锛�${item.value} 椤�</div>`
+    })
+    return result
+  }
+}
+
+const xAxis = computed(() => [
+  {
+    type: 'category',
+    axisTick: { show: false },
+    axisLine: { lineStyle: { color: 'rgba(255,255,255,0.2)' } },
+    axisLabel: { color: '#B8C8E0' },
+    data: categories.value
+  }
+])
+
+const yAxis = [
+  {
+    type: 'value',
+    minInterval: 1,
+    axisLabel: { color: '#B8C8E0' },
+    splitLine: { lineStyle: { color: 'rgba(255,255,255,0.08)' } }
+  }
+]
+
+const loadData = async () => {
+  if (loading.value) return
+  loading.value = true
+  try {
+    const res = await getHiddenTrend()
+    trendData.value = Array.isArray(res?.data) ? res.data : []
+    refreshFailed.value = false
+    loaded.value = true
+  } catch (e) {
+    console.error('鑾峰彇闅愭偅瓒嬪娍澶辫触:', e)
+    refreshFailed.value = true
+  } finally {
+    loading.value = false
+  }
+}
+
+const safetyDashboardRefreshTick = inject('safetyDashboardRefreshTick', null)
+if (safetyDashboardRefreshTick) {
+  watch(safetyDashboardRefreshTick, () => {
+    loadData()
+  })
+}
+
+onMounted(() => {
+  loadData()
+})
+</script>
+
+<style scoped>
+.panel-item-customers {
+  position: relative;
+  border: 1px solid #1a58b0;
+  padding: 18px;
+  width: 100%;
+  height: 478px;
+  box-sizing: border-box;
+}
+
+.empty-tip {
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: 50%;
+  text-align: center;
+  font-size: 16px;
+  color: #8aa4c8;
+  pointer-events: none;
+}
+
+.refresh-error {
+  position: absolute;
+  right: 16px;
+  top: 8px;
+  font-size: 12px;
+  color: #ff9a9a;
+}
+</style>
diff --git a/src/views/reportAnalysis/safetyDashboard/components/left-top.vue b/src/views/reportAnalysis/safetyDashboard/components/left-top.vue
new file mode 100644
index 0000000..7699bb7
--- /dev/null
+++ b/src/views/reportAnalysis/safetyDashboard/components/left-top.vue
@@ -0,0 +1,241 @@
+<template>
+  <div>
+    <PanelHeader title="闅愭偅绫诲瀷鍒嗗竷" />
+    <div class="panel-item-customers">
+      <div class="pie-chart-wrapper" ref="pieWrapperRef">
+        <div class="pie-background" ref="pieBackgroundRef"></div>
+        <Echarts
+          ref="chart"
+          :chartStyle="chartStyle"
+          :legend="pieLegend"
+          :series="pieSeries"
+          :tooltip="pieTooltip"
+          :color="pieColors"
+          :options="pieOptions"
+          style="height: 100%"
+        />
+        <div v-if="showEmpty" class="empty-tip">鏆傛棤鏁版嵁</div>
+      </div>
+      <span v-if="refreshFailed" class="refresh-error">鍒锋柊澶辫触</span>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, onBeforeUnmount, inject, watch } from 'vue'
+import Echarts from '@/components/Echarts/echarts.vue'
+import PanelHeader from '@/views/reportAnalysis/dataDashboard/components/PanelHeader.vue'
+import { useChartBackground } from '@/hooks/useChartBackground.js'
+import { getHiddenTypeDistribution } from '@/api/reportAnalysis/safetyDashboard.js'
+
+const pieWrapperRef = ref(null)
+const pieBackgroundRef = ref(null)
+const chart = ref(null)
+
+const dataList = ref([])
+const loaded = ref(false)
+const loading = ref(false)
+const refreshFailed = ref(false)
+
+const pieColors = ['#26FFCB', '#24CBFF', '#35FBF4', '#2651FF', '#D1E4F5', '#5782F7', '#2F67EF', '#82BAFF']
+
+const showEmpty = computed(() => loaded.value && !loading.value && dataList.value.length === 0)
+
+const dotRich = pieColors.reduce((acc, color, idx) => {
+  acc[`dot${idx}`] = {
+    width: 8,
+    height: 8,
+    borderRadius: 8,
+    backgroundColor: color,
+    align: 'center'
+  }
+  return acc
+}, {})
+
+const pieLegend = {
+  show: false,
+  data: []
+}
+
+const pieTooltip = {
+  trigger: 'item',
+  formatter: function (params) {
+    return `${params.name}锛�${params.value} 椤癸紙${params.percent}%锛塦
+  }
+}
+
+const pieSeries = computed(() => {
+  if (!dataList.value.length) return []
+  return [
+    {
+      name: '闅愭偅绫诲瀷',
+      type: 'pie',
+      radius: ['35%', '55%'],
+      center: ['50%', '50%'],
+      label: {
+        show: true,
+        position: 'outside',
+        color: '#fff',
+        fontSize: 12,
+        lineHeight: 18,
+        rich: {
+          ...dotRich,
+          parent: { fontSize: 14, fontWeight: 600, color: '#fff', lineHeight: 20, overflow: 'break' },
+          child: { fontSize: 12, color: '#fff', lineHeight: 18 }
+        },
+        formatter: function (params) {
+          const children = params?.data?.children || []
+          const parentName = params?.data?.name || ''
+          const parentValue = Number(params?.data?.value) || 0
+          const dotKey = `dot${(params?.dataIndex || 0) % pieColors.length}`
+          const parentLine = `{${dotKey}|} {parent|${parentName} (${parentValue}椤�)}`
+          if (!children.length) return parentLine
+          const childLines = children.map(
+            (c) => `{child|${c.name} ${Number(c.value) || 0}椤箎`
+          )
+          return [parentLine, ...childLines].join('\n')
+        }
+      },
+      labelLine: {
+        show: true,
+        length: 20,
+        length2: 20,
+        lineStyle: {
+          color: '#B8C8E0'
+        }
+      },
+      itemStyle: {
+        color: function (params) {
+          return pieColors[params.dataIndex % pieColors.length]
+        }
+      },
+      data: dataList.value
+    },
+    {
+      type: 'pie',
+      radius: ['35%', '40%'],
+      center: ['50%', '50%'],
+      silent: true,
+      label: { show: false },
+      labelLine: { show: false },
+      itemStyle: {
+        color: 'rgba(0, 127, 255, 0.25)'
+      },
+      data: [1]
+    }
+  ]
+})
+
+const chartStyle = {
+  width: '100%',
+  height: '126%'
+}
+
+const pieOptions = {
+  backgroundColor: 'transparent',
+  textStyle: { color: '#B8C8E0' }
+}
+
+const { adjustBackgroundPosition, init: initBackground, cleanup: cleanupBackground } = useChartBackground({
+  wrapperRef: pieWrapperRef,
+  backgroundRef: pieBackgroundRef,
+  offsetX: '-51.5%',
+  offsetY: '-39%',
+  watchData: dataList
+})
+
+const loadData = async () => {
+  if (loading.value) return
+  loading.value = true
+  try {
+    const res = await getHiddenTypeDistribution()
+    const items = res?.data || []
+    dataList.value = items.map((it) => ({
+      name: it.name || '鏈煡绫诲瀷',
+      value: Number(it.value) || 0,
+      rate: it.rate,
+      children: Array.isArray(it.children) ? it.children.map((c) => ({
+        name: c.name,
+        value: Number(c.value) || 0,
+        rate: c.rate
+      })) : []
+    }))
+    refreshFailed.value = false
+    loaded.value = true
+    adjustBackgroundPosition()
+  } catch (e) {
+    console.error('鑾峰彇闅愭偅绫诲瀷鍒嗗竷澶辫触:', e)
+    refreshFailed.value = true
+  } finally {
+    loading.value = false
+  }
+}
+
+const safetyDashboardRefreshTick = inject('safetyDashboardRefreshTick', null)
+if (safetyDashboardRefreshTick) {
+  watch(safetyDashboardRefreshTick, () => {
+    loadData()
+  })
+}
+
+onMounted(() => {
+  loadData()
+  initBackground()
+})
+
+onBeforeUnmount(() => {
+  cleanupBackground()
+})
+</script>
+
+<style scoped>
+.panel-item-customers {
+  position: relative;
+  border: 1px solid #1a58b0;
+  padding: 18px;
+  width: 100%;
+  height: 420px;
+  box-sizing: border-box;
+}
+
+.pie-chart-wrapper {
+  position: relative;
+  width: 100%;
+  height: 320px;
+  background: transparent;
+}
+
+.pie-background {
+  position: absolute;
+  width: 360px;
+  height: 360px;
+  background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
+  background-size: contain;
+  background-position: center;
+  background-repeat: no-repeat;
+  z-index: 1;
+  pointer-events: none;
+  left: 50%;
+  top: 50%;
+  transform: translate(-51.5%, -39%);
+}
+
+.empty-tip {
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: 48%;
+  text-align: center;
+  font-size: 16px;
+  color: #8aa4c8;
+  pointer-events: none;
+}
+
+.refresh-error {
+  position: absolute;
+  right: 16px;
+  top: 8px;
+  font-size: 12px;
+  color: #ff9a9a;
+}
+</style>
diff --git a/src/views/reportAnalysis/safetyDashboard/components/right-bottom.vue b/src/views/reportAnalysis/safetyDashboard/components/right-bottom.vue
new file mode 100644
index 0000000..984d728
--- /dev/null
+++ b/src/views/reportAnalysis/safetyDashboard/components/right-bottom.vue
@@ -0,0 +1,275 @@
+<template>
+  <div>
+    <PanelHeader title="闅愭偅鏁存敼璐d换浜烘帓鍚�" />
+    <div class="panel-item-customers">
+      <Echarts
+        ref="chart"
+        :chartStyle="chartStyle"
+        :grid="grid"
+        :series="series"
+        :tooltip="tooltip"
+        :xAxis="xAxis"
+        :yAxis="yAxis"
+        :options="chartOptions"
+        style="height: 390px"
+      />
+      <div v-if="showEmpty" class="empty-tip">鏆傛棤鏁版嵁</div>
+      <span v-if="refreshFailed" class="refresh-error">鍒锋柊澶辫触</span>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, inject, watch } from 'vue'
+import Echarts from '@/components/Echarts/echarts.vue'
+import PanelHeader from '@/views/reportAnalysis/dataDashboard/components/PanelHeader.vue'
+import { getHiddenRectifyRanking } from '@/api/reportAnalysis/safetyDashboard.js'
+
+const chart = ref(null)
+const rows = ref([])
+const loaded = ref(false)
+const loading = ref(false)
+const refreshFailed = ref(false)
+
+const aircraft =
+  'path://M107.000,71.000 C104.936,71.000 102.665,70.806 100.273,70.467 C94.592,76.922 86.275,81.000 77.000,81.000 C70.794,81.000 65.020,79.170 60.172,76.029 C66.952,74.165 72.647,69.714 76.173,63.817 C69.821,61.362 64.063,58.593 60.000,56.039 L60.000,52.813 C70.456,53.950 80.723,55.000 83.000,55.000 C88.972,55.000 93.000,53.723 93.000,50.000 C93.000,47.071 89.222,45.000 83.000,45.000 C80.723,45.000 70.456,46.050 60.000,47.187 L60.000,43.989 C64.057,41.431 69.807,38.644 76.168,36.173 C72.641,30.281 66.948,25.834 60.172,23.971 C65.020,20.830 70.794,19.000 77.000,19.000 C86.270,19.000 94.584,23.074 100.265,29.524 C102.647,29.191 104.918,29.000 107.000,29.000 C129.644,29.000 148.000,50.000 148.000,50.000 C148.000,50.000 129.644,71.000 107.000,71.000 ZM113.000,38.000 C106.373,38.000 101.000,43.373 101.000,50.000 C101.000,56.627 106.373,62.000 113.000,62.000 C119.627,62.000 125.000,56.627 125.000,50.000 C125.000,43.373 119.627,38.000 113.000,38.000 ZM113.000,56.000 C109.686,56.000 107.000,53.314 107.000,50.000 C107.000,46.686 109.686,44.000 113.000,44.000 C116.314,44.000 119.000,46.686 119.000,50.000 C119.000,53.314 116.314,56.000 113.000,56.000 ZM110.500,19.000 C109.567,19.000 108.763,18.483 108.334,17.726 C100.231,9.857 89.187,5.000 77.000,5.000 C64.813,5.000 53.769,9.857 45.666,17.726 C45.237,18.483 44.433,19.000 43.500,19.000 C42.119,19.000 41.000,17.881 41.000,16.500 C41.000,15.847 41.256,15.259 41.665,14.813 L41.575,14.718 C50.629,5.628 63.156,-0.000 77.000,-0.000 C90.844,-0.000 103.371,5.628 112.425,14.718 L112.335,14.813 C112.744,15.259 113.000,15.847 113.000,16.500 C113.000,17.881 111.881,19.000 110.500,19.000 ZM53.000,49.484 C61.406,48.626 77.810,47.000 81.345,47.000 C87.353,47.000 91.000,48.243 91.000,50.000 C91.000,52.234 87.111,53.000 81.345,53.000 C77.810,53.000 61.406,51.374 53.000,50.516 L53.000,49.484 ZM53.000,47.000 L9.000,50.000 L53.000,53.000 L53.000,56.000 L-0.000,50.000 L53.000,44.000 L53.000,47.000 ZM43.500,81.000 C44.433,81.000 45.237,81.517 45.666,82.274 C53.769,90.143 64.813,95.000 77.000,95.000 C89.187,95.000 100.231,90.143 108.334,82.274 C108.763,81.517 109.567,81.000 110.500,81.000 C111.881,81.000 113.000,82.119 113.000,83.500 C113.000,84.153 112.744,84.741 112.335,85.187 L112.425,85.282 C103.371,94.372 90.844,100.000 77.000,100.000 C63.156,100.000 50.629,94.372 41.575,85.282 L41.665,85.187 C41.256,84.741 41.000,84.153 41.000,83.500 C41.000,82.119 42.119,81.000 43.500,81.000 Z'
+
+const color = {
+  0: '#ff5676',
+  1: '#ffd83e',
+  2: '#fbff94',
+  3: '#7daeff'
+}
+
+const chartStyle = {
+  width: '100%',
+  height: '100%'
+}
+
+const chartOptions = {
+  backgroundColor: 'transparent',
+  textStyle: { color: '#B8C8E0' }
+}
+
+const sortedRows = computed(() =>
+  [...rows.value].sort((a, b) => (Number(a.value) || 0) - (Number(b.value) || 0))
+)
+
+const showEmpty = computed(
+  () => loaded.value && !loading.value && rows.value.length === 0
+)
+
+const dataNum = computed(() =>
+  sortedRows.value.map((item, i) => {
+    const index = sortedRows.value.length - i - 1
+    const isTop3 = index < 3
+    return {
+      value: Number(item.value) || 0,
+      itemStyle: {
+        color: {
+          type: 'linear',
+          x: 1,
+          y: 0,
+          x2: 0,
+          y2: 0,
+          colorStops: [
+            { offset: 0, color: isTop3 ? '#ffdae1' : '#ecf3ff' },
+            { offset: 0.07, color: isTop3 ? color[index] : color[3] },
+            { offset: 1, color: isTop3 ? 'rgba(255, 86, 118, .1)' : 'rgba(125,174,255, .1)' }
+          ],
+          global: false
+        },
+        barBorderRadius: [0, 20, 20, 0]
+      },
+      symbol: isTop3 ? aircraft : 'none',
+      symbolPosition: 'end',
+      symbolSize: [30, 25],
+      symbolOffset: [35, 0]
+    }
+  })
+)
+
+const bgData = computed(() => {
+  const maxValue = Math.max(1, ...dataNum.value.map((v) => v.value))
+  return dataNum.value.map(() => maxValue * 1.25)
+})
+
+const tooltip = computed(() => ({
+  trigger: 'axis',
+  textStyle: { fontSize: '100%' },
+  formatter: function (params) {
+    const index = params[0]?.dataIndex ?? 0
+    const row = sortedRows.value[index] || {}
+    return `${params[0]?.axisValueLabel || ''}<br/>宸叉暣鏀癸細${row.value ?? 0} 椤�<br/>鍗犳瘮锛�${row.rate ?? '0.00'}%`
+  }
+}))
+
+const grid = { top: 0, left: '22%', right: '12%', bottom: 0 }
+
+const xAxis = [
+  {
+    splitLine: { show: false },
+    axisLine: { show: false },
+    axisLabel: { show: false },
+    axisTick: { show: false }
+  }
+]
+
+const yAxis = computed(() => [
+  {
+    type: 'category',
+    data: sortedRows.value.map((item) => item.name),
+    axisLabel: {
+      formatter: (value) => {
+        if (!value) return ''
+        const maxLen = 6
+        if (value.length <= maxLen) return `{a|${value}}`
+        const lines = []
+        for (let i = 0; i < value.length; i += maxLen) {
+          lines.push(value.slice(i, i + maxLen))
+        }
+        return lines.map((line) => `{a|${line}}`).join('\n')
+      },
+      rich: {
+        a: {
+          width: 120,
+          fontSize: 14,
+          color: '#fff',
+          padding: [5, 4, 5, 0],
+          align: 'right'
+        }
+      }
+    },
+    axisLine: { show: false },
+    axisTick: { show: false },
+    splitLine: { show: false }
+  },
+  {
+    type: 'category',
+    data: sortedRows.value.map((item) => Number(item.value) || 0),
+    axisLabel: {
+      formatter: (params, index) => {
+        const row = sortedRows.value[index] || {}
+        const rank = sortedRows.value.length - index
+        const isTop3 = rank < 4
+        return `{a${isTop3 ? rank : ''}|${row.rate ?? '0.00'} }{b${isTop3 ? rank : ''}|%}`
+      },
+      rich: {
+        a: { fontSize: 18, color: '#98bfff', verticalAlign: 'bottom' },
+        a1: { fontSize: 18, color: '#ff7f97', verticalAlign: 'bottom' },
+        a2: { fontSize: 18, color: '#ffce64', verticalAlign: 'bottom' },
+        a3: { fontSize: 18, color: '#e8ed66', verticalAlign: 'bottom' },
+        b: { fontSize: 12, color: '#98bfff', verticalAlign: 'bottom' },
+        b1: { fontSize: 12, color: '#ff7f97', verticalAlign: 'bottom' },
+        b2: { fontSize: 12, color: '#ffce64', verticalAlign: 'bottom' },
+        b3: { fontSize: 12, color: '#e8ed66', verticalAlign: 'bottom' }
+      }
+    },
+    axisLine: { show: false },
+    axisTick: { show: false },
+    splitLine: { show: false }
+  }
+])
+
+const series = computed(() => [
+  {
+    name: '宸叉暣鏀�',
+    z: 6,
+    type: 'pictorialBar',
+    data: dataNum.value
+  },
+  {
+    name: '鑳屾櫙',
+    z: 6,
+    type: 'bar',
+    barWidth: 22,
+    tooltip: { show: false },
+    itemStyle: {
+      color: 'rgba(255,255,255,.1)',
+      barBorderRadius: [0, 20, 20, 0]
+    },
+    data: bgData.value
+  },
+  {
+    name: '宸叉暣鏀规笎鍙�',
+    type: 'bar',
+    barWidth: 22,
+    barGap: '-100%',
+    tooltip: { show: false },
+    itemStyle: {
+      color: {
+        type: 'linear',
+        x: 1,
+        y: 0,
+        x2: 0,
+        y2: 0,
+        colorStops: [
+          { offset: 0, color: 'rgba(255, 218, 220)' },
+          { offset: 0.07, color: 'rgba(255, 86, 118)' },
+          { offset: 1, color: 'rgba(255, 86, 118, 0)' }
+        ],
+        global: false
+      },
+      barBorderRadius: [0, 20, 20, 0]
+    },
+    data: dataNum.value
+  }
+])
+
+const loadData = async () => {
+  if (loading.value) return
+  loading.value = true
+  try {
+    const res = await getHiddenRectifyRanking({ limit: 5 })
+    rows.value = Array.isArray(res?.data) ? res.data : []
+    refreshFailed.value = false
+    loaded.value = true
+  } catch (e) {
+    console.error('鑾峰彇闅愭偅鏁存敼璐d换浜烘帓鍚嶅け璐�:', e)
+    refreshFailed.value = true
+  } finally {
+    loading.value = false
+  }
+}
+
+const safetyDashboardRefreshTick = inject('safetyDashboardRefreshTick', null)
+if (safetyDashboardRefreshTick) {
+  watch(safetyDashboardRefreshTick, () => {
+    loadData()
+  })
+}
+
+onMounted(() => {
+  loadData()
+})
+</script>
+
+<style scoped>
+.panel-item-customers {
+  position: relative;
+  border: 1px solid #1a58b0;
+  padding: 18px;
+  width: 100%;
+  height: 449px;
+  box-sizing: border-box;
+}
+
+.empty-tip {
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: 50%;
+  text-align: center;
+  font-size: 16px;
+  color: #8aa4c8;
+  pointer-events: none;
+}
+
+.refresh-error {
+  position: absolute;
+  right: 16px;
+  top: 8px;
+  font-size: 12px;
+  color: #ff9a9a;
+}
+</style>
diff --git a/src/views/reportAnalysis/safetyDashboard/components/right-top.vue b/src/views/reportAnalysis/safetyDashboard/components/right-top.vue
new file mode 100644
index 0000000..b08c194
--- /dev/null
+++ b/src/views/reportAnalysis/safetyDashboard/components/right-top.vue
@@ -0,0 +1,275 @@
+<template>
+  <div>
+    <PanelHeader title="鍗遍櫓婧愮被鍨嬫帓鍚�" />
+    <div class="panel-item-customers">
+      <Echarts
+        ref="chart"
+        :chartStyle="chartStyle"
+        :grid="grid"
+        :series="series"
+        :tooltip="tooltip"
+        :xAxis="xAxis"
+        :yAxis="yAxis"
+        :options="chartOptions"
+        style="height: 390px"
+      />
+      <div v-if="showEmpty" class="empty-tip">鏆傛棤鏁版嵁</div>
+      <span v-if="refreshFailed" class="refresh-error">鍒锋柊澶辫触</span>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, inject, watch } from 'vue'
+import Echarts from '@/components/Echarts/echarts.vue'
+import PanelHeader from '@/views/reportAnalysis/dataDashboard/components/PanelHeader.vue'
+import { getHazardTypeRanking } from '@/api/reportAnalysis/safetyDashboard.js'
+
+const chart = ref(null)
+const rows = ref([])
+const loaded = ref(false)
+const loading = ref(false)
+const refreshFailed = ref(false)
+
+const aircraft =
+  'path://M107.000,71.000 C104.936,71.000 102.665,70.806 100.273,70.467 C94.592,76.922 86.275,81.000 77.000,81.000 C70.794,81.000 65.020,79.170 60.172,76.029 C66.952,74.165 72.647,69.714 76.173,63.817 C69.821,61.362 64.063,58.593 60.000,56.039 L60.000,52.813 C70.456,53.950 80.723,55.000 83.000,55.000 C88.972,55.000 93.000,53.723 93.000,50.000 C93.000,47.071 89.222,45.000 83.000,45.000 C80.723,45.000 70.456,46.050 60.000,47.187 L60.000,43.989 C64.057,41.431 69.807,38.644 76.168,36.173 C72.641,30.281 66.948,25.834 60.172,23.971 C65.020,20.830 70.794,19.000 77.000,19.000 C86.270,19.000 94.584,23.074 100.265,29.524 C102.647,29.191 104.918,29.000 107.000,29.000 C129.644,29.000 148.000,50.000 148.000,50.000 C148.000,50.000 129.644,71.000 107.000,71.000 ZM113.000,38.000 C106.373,38.000 101.000,43.373 101.000,50.000 C101.000,56.627 106.373,62.000 113.000,62.000 C119.627,62.000 125.000,56.627 125.000,50.000 C125.000,43.373 119.627,38.000 113.000,38.000 ZM113.000,56.000 C109.686,56.000 107.000,53.314 107.000,50.000 C107.000,46.686 109.686,44.000 113.000,44.000 C116.314,44.000 119.000,46.686 119.000,50.000 C119.000,53.314 116.314,56.000 113.000,56.000 ZM110.500,19.000 C109.567,19.000 108.763,18.483 108.334,17.726 C100.231,9.857 89.187,5.000 77.000,5.000 C64.813,5.000 53.769,9.857 45.666,17.726 C45.237,18.483 44.433,19.000 43.500,19.000 C42.119,19.000 41.000,17.881 41.000,16.500 C41.000,15.847 41.256,15.259 41.665,14.813 L41.575,14.718 C50.629,5.628 63.156,-0.000 77.000,-0.000 C90.844,-0.000 103.371,5.628 112.425,14.718 L112.335,14.813 C112.744,15.259 113.000,15.847 113.000,16.500 C113.000,17.881 111.881,19.000 110.500,19.000 ZM53.000,49.484 C61.406,48.626 77.810,47.000 81.345,47.000 C87.353,47.000 91.000,48.243 91.000,50.000 C91.000,52.234 87.111,53.000 81.345,53.000 C77.810,53.000 61.406,51.374 53.000,50.516 L53.000,49.484 ZM53.000,47.000 L9.000,50.000 L53.000,53.000 L53.000,56.000 L-0.000,50.000 L53.000,44.000 L53.000,47.000 ZM43.500,81.000 C44.433,81.000 45.237,81.517 45.666,82.274 C53.769,90.143 64.813,95.000 77.000,95.000 C89.187,95.000 100.231,90.143 108.334,82.274 C108.763,81.517 109.567,81.000 110.500,81.000 C111.881,81.000 113.000,82.119 113.000,83.500 C113.000,84.153 112.744,84.741 112.335,85.187 L112.425,85.282 C103.371,94.372 90.844,100.000 77.000,100.000 C63.156,100.000 50.629,94.372 41.575,85.282 L41.665,85.187 C41.256,84.741 41.000,84.153 41.000,83.500 C41.000,82.119 42.119,81.000 43.500,81.000 Z'
+
+const color = {
+  0: '#ff5676',
+  1: '#ffd83e',
+  2: '#fbff94',
+  3: '#7daeff'
+}
+
+const chartStyle = {
+  width: '100%',
+  height: '100%'
+}
+
+const chartOptions = {
+  backgroundColor: 'transparent',
+  textStyle: { color: '#B8C8E0' }
+}
+
+const sortedRows = computed(() =>
+  [...rows.value].sort((a, b) => (Number(a.value) || 0) - (Number(b.value) || 0))
+)
+
+const showEmpty = computed(
+  () => loaded.value && !loading.value && rows.value.length === 0
+)
+
+const dataNum = computed(() =>
+  sortedRows.value.map((item, i) => {
+    const index = sortedRows.value.length - i - 1
+    const isTop3 = index < 3
+    return {
+      value: Number(item.value) || 0,
+      itemStyle: {
+        color: {
+          type: 'linear',
+          x: 1,
+          y: 0,
+          x2: 0,
+          y2: 0,
+          colorStops: [
+            { offset: 0, color: isTop3 ? '#ffdae1' : '#ecf3ff' },
+            { offset: 0.07, color: isTop3 ? color[index] : color[3] },
+            { offset: 1, color: isTop3 ? 'rgba(255, 86, 118, .1)' : 'rgba(125,174,255, .1)' }
+          ],
+          global: false
+        },
+        barBorderRadius: [0, 20, 20, 0]
+      },
+      symbol: isTop3 ? aircraft : 'none',
+      symbolPosition: 'end',
+      symbolSize: [30, 25],
+      symbolOffset: [35, 0]
+    }
+  })
+)
+
+const bgData = computed(() => {
+  const maxValue = Math.max(1, ...dataNum.value.map((v) => v.value))
+  return dataNum.value.map(() => maxValue * 1.25)
+})
+
+const tooltip = computed(() => ({
+  trigger: 'axis',
+  textStyle: { fontSize: '100%' },
+  formatter: function (params) {
+    const index = params[0]?.dataIndex ?? 0
+    const row = sortedRows.value[index] || {}
+    return `${params[0]?.axisValueLabel || ''}<br/>鏁伴噺锛�${row.value ?? 0} 澶�<br/>鍗犳瘮锛�${row.rate ?? '0.00'}%`
+  }
+}))
+
+const grid = { top: 0, left: '22%', right: '12%', bottom: 0 }
+
+const xAxis = [
+  {
+    splitLine: { show: false },
+    axisLine: { show: false },
+    axisLabel: { show: false },
+    axisTick: { show: false }
+  }
+]
+
+const yAxis = computed(() => [
+  {
+    type: 'category',
+    data: sortedRows.value.map((item) => item.name),
+    axisLabel: {
+      formatter: (value) => {
+        if (!value) return ''
+        const maxLen = 6
+        if (value.length <= maxLen) return `{a|${value}}`
+        const lines = []
+        for (let i = 0; i < value.length; i += maxLen) {
+          lines.push(value.slice(i, i + maxLen))
+        }
+        return lines.map((line) => `{a|${line}}`).join('\n')
+      },
+      rich: {
+        a: {
+          width: 120,
+          fontSize: 14,
+          color: '#fff',
+          padding: [5, 4, 5, 0],
+          align: 'right'
+        }
+      }
+    },
+    axisLine: { show: false },
+    axisTick: { show: false },
+    splitLine: { show: false }
+  },
+  {
+    type: 'category',
+    data: sortedRows.value.map((item) => Number(item.value) || 0),
+    axisLabel: {
+      formatter: (params, index) => {
+        const row = sortedRows.value[index] || {}
+        const rank = sortedRows.value.length - index
+        const isTop3 = rank < 4
+        return `{a${isTop3 ? rank : ''}|${row.rate ?? '0.00'} }{b${isTop3 ? rank : ''}|%}`
+      },
+      rich: {
+        a: { fontSize: 18, color: '#98bfff', verticalAlign: 'bottom' },
+        a1: { fontSize: 18, color: '#ff7f97', verticalAlign: 'bottom' },
+        a2: { fontSize: 18, color: '#ffce64', verticalAlign: 'bottom' },
+        a3: { fontSize: 18, color: '#e8ed66', verticalAlign: 'bottom' },
+        b: { fontSize: 12, color: '#98bfff', verticalAlign: 'bottom' },
+        b1: { fontSize: 12, color: '#ff7f97', verticalAlign: 'bottom' },
+        b2: { fontSize: 12, color: '#ffce64', verticalAlign: 'bottom' },
+        b3: { fontSize: 12, color: '#e8ed66', verticalAlign: 'bottom' }
+      }
+    },
+    axisLine: { show: false },
+    axisTick: { show: false },
+    splitLine: { show: false }
+  }
+])
+
+const series = computed(() => [
+  {
+    name: '鏁伴噺',
+    z: 6,
+    type: 'pictorialBar',
+    data: dataNum.value
+  },
+  {
+    name: '鑳屾櫙',
+    z: 6,
+    type: 'bar',
+    barWidth: 22,
+    tooltip: { show: false },
+    itemStyle: {
+      color: 'rgba(255,255,255,.1)',
+      barBorderRadius: [0, 20, 20, 0]
+    },
+    data: bgData.value
+  },
+  {
+    name: '鏁伴噺娓愬彉',
+    type: 'bar',
+    barWidth: 22,
+    barGap: '-100%',
+    tooltip: { show: false },
+    itemStyle: {
+      color: {
+        type: 'linear',
+        x: 1,
+        y: 0,
+        x2: 0,
+        y2: 0,
+        colorStops: [
+          { offset: 0, color: 'rgba(255, 218, 220)' },
+          { offset: 0.07, color: 'rgba(255, 86, 118)' },
+          { offset: 1, color: 'rgba(255, 86, 118, 0)' }
+        ],
+        global: false
+      },
+      barBorderRadius: [0, 20, 20, 0]
+    },
+    data: dataNum.value
+  }
+])
+
+const loadData = async () => {
+  if (loading.value) return
+  loading.value = true
+  try {
+    const res = await getHazardTypeRanking({ limit: 8 })
+    rows.value = Array.isArray(res?.data) ? res.data : []
+    refreshFailed.value = false
+    loaded.value = true
+  } catch (e) {
+    console.error('鑾峰彇鍗遍櫓婧愮被鍨嬫帓鍚嶅け璐�:', e)
+    refreshFailed.value = true
+  } finally {
+    loading.value = false
+  }
+}
+
+const safetyDashboardRefreshTick = inject('safetyDashboardRefreshTick', null)
+if (safetyDashboardRefreshTick) {
+  watch(safetyDashboardRefreshTick, () => {
+    loadData()
+  })
+}
+
+onMounted(() => {
+  loadData()
+})
+</script>
+
+<style scoped>
+.panel-item-customers {
+  position: relative;
+  border: 1px solid #1a58b0;
+  padding: 18px;
+  width: 100%;
+  height: 449px;
+  box-sizing: border-box;
+}
+
+.empty-tip {
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: 50%;
+  text-align: center;
+  font-size: 16px;
+  color: #8aa4c8;
+  pointer-events: none;
+}
+
+.refresh-error {
+  position: absolute;
+  right: 16px;
+  top: 8px;
+  font-size: 12px;
+  color: #ff9a9a;
+}
+</style>
diff --git a/src/views/reportAnalysis/safetyDashboard/index.vue b/src/views/reportAnalysis/safetyDashboard/index.vue
new file mode 100644
index 0000000..119dabb
--- /dev/null
+++ b/src/views/reportAnalysis/safetyDashboard/index.vue
@@ -0,0 +1,256 @@
+<template>
+  <div class="scale-container">
+    <div class="data-dashboard" :style="{ transform: `scale(${scaleRatio})` }">
+      <button class="fullscreen-btn" @click="toggleFullscreen" :title="isFullscreen ? '閫�鍑哄叏灞�' : '鍏ㄥ睆鏄剧ず'">
+        <svg v-if="!isFullscreen" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+          <path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/>
+        </svg>
+        <svg v-else width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+          <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
+        </svg>
+      </button>
+
+      <div class="dashboard-header">
+        <div class="factory-name">瀹夊叏鏁版嵁椹鹃┒鑸�</div>
+      </div>
+
+      <div class="dashboard-content">
+        <div class="left-panel">
+          <LeftTop />
+          <LeftBottom />
+        </div>
+
+        <div class="center-panel">
+          <CenterTop />
+          <CenterBottom />
+        </div>
+
+        <div class="right-panel">
+          <RightTop />
+          <RightBottom />
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onBeforeUnmount, nextTick, provide } from 'vue'
+import LeftTop from './components/left-top.vue'
+import LeftBottom from './components/left-bottom.vue'
+import CenterTop from './components/center-top.vue'
+import CenterBottom from './components/center-bottom.vue'
+import RightTop from './components/right-top.vue'
+import RightBottom from './components/right-bottom.vue'
+
+const isFullscreen = ref(false)
+const scaleRatio = ref(1)
+const designWidth = 1920
+const designHeight = 1080
+
+const DASHBOARD_REFRESH_MS = 60 * 1000
+const safetyDashboardRefreshTick = ref(0)
+provide('safetyDashboardRefreshTick', safetyDashboardRefreshTick)
+let dashboardPollTimer = null
+
+const calculateScale = () => {
+  const container = document.querySelector('.scale-container')
+  if (!container) return
+  const rect = container.getBoundingClientRect?.()
+  const containerWidth = container.clientWidth || rect?.width || window.innerWidth
+  const containerHeight = container.clientHeight || rect?.height || window.innerHeight
+  const scaleX = containerWidth / designWidth
+  const scaleY = containerHeight / designHeight
+  scaleRatio.value = Math.min(scaleX, scaleY)
+}
+
+const handleResize = () => {
+  setTimeout(() => {
+    calculateScale()
+  }, 100)
+}
+
+const toggleFullscreen = () => {
+  const element = document.querySelector('.scale-container')
+  if (!element) return
+  if (!isFullscreen.value) {
+    if (element.requestFullscreen) {
+      element.requestFullscreen()
+    } else if (element.webkitRequestFullscreen) {
+      element.webkitRequestFullscreen()
+    } else if (element.msRequestFullscreen) {
+      element.msRequestFullscreen()
+    }
+  } else {
+    if (document.exitFullscreen) {
+      document.exitFullscreen()
+    } else if (document.webkitExitFullscreen) {
+      document.webkitExitFullscreen()
+    } else if (document.msExitFullscreen) {
+      document.msExitFullscreen()
+    }
+  }
+}
+
+const handleFullscreenChange = () => {
+  const fullscreenElement =
+    document.fullscreenElement ||
+    document.webkitFullscreenElement ||
+    document.msFullscreenElement
+  isFullscreen.value = !!(fullscreenElement && fullscreenElement.classList.contains('scale-container'))
+  setTimeout(() => {
+    calculateScale()
+  }, 200)
+}
+
+onMounted(() => {
+  nextTick(() => {
+    calculateScale()
+  })
+  window.addEventListener('resize', handleResize)
+  window.addEventListener('fullscreenchange', handleFullscreenChange)
+  window.addEventListener('webkitfullscreenchange', handleFullscreenChange)
+  window.addEventListener('MSFullscreenChange', handleFullscreenChange)
+  dashboardPollTimer = setInterval(() => {
+    safetyDashboardRefreshTick.value++
+  }, DASHBOARD_REFRESH_MS)
+})
+
+onBeforeUnmount(() => {
+  if (dashboardPollTimer) {
+    clearInterval(dashboardPollTimer)
+    dashboardPollTimer = null
+  }
+  window.removeEventListener('resize', handleResize)
+  window.removeEventListener('fullscreenchange', handleFullscreenChange)
+  window.removeEventListener('webkitfullscreenchange', handleFullscreenChange)
+  window.removeEventListener('MSFullscreenChange', handleFullscreenChange)
+})
+</script>
+
+<style scoped>
+.scale-container {
+  position: relative;
+  width: 100%;
+  height: calc(100vh - 84px);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: #000;
+  overflow: hidden;
+}
+
+.data-dashboard {
+  position: relative;
+  width: 1920px;
+  height: 1080px;
+  background-image: url("@/assets/BI/backImage@2x.png");
+  background-size: cover;
+  background-position: center;
+  background-repeat: no-repeat;
+  transform-origin: center center;
+}
+
+.scale-container:fullscreen {
+  width: 100vw;
+  height: 100vh;
+  margin: 0;
+  padding: 0;
+  background-color: #000;
+  z-index: 9999;
+}
+
+.scale-container:-webkit-full-screen {
+  width: 100vw;
+  height: 100vh;
+  margin: 0;
+  padding: 0;
+  background-color: #000;
+  z-index: 9999;
+}
+
+.scale-container:-ms-fullscreen {
+  width: 100vw;
+  height: 100vh;
+  margin: 0;
+  padding: 0;
+  background-color: #000;
+  z-index: 9999;
+}
+
+.dashboard-header {
+  position: relative;
+  z-index: 1;
+  height: 86px;
+  background-image: url("@/assets/BI/biaoti.png");
+  background-size: cover;
+  background-repeat: no-repeat;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.factory-name {
+  font-weight: 600;
+  font-size: 46px;
+  color: #FFFFFF;
+  top: 16px;
+  position: absolute;
+  letter-spacing: 4px;
+}
+
+.fullscreen-btn {
+  position: absolute;
+  top: 10px;
+  left: 20px;
+  width: 40px;
+  height: 40px;
+  background: rgba(0, 20, 60, 0.8);
+  border: 1px solid rgba(0, 212, 255, 0.3);
+  border-radius: 6px;
+  color: #00d4ff;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  transition: all 0.3s;
+  z-index: 10000;
+}
+
+.fullscreen-btn:hover {
+  background: rgba(0, 30, 90, 0.9);
+  border-color: rgba(0, 212, 255, 0.5);
+}
+
+.dashboard-content {
+  position: relative;
+  z-index: 1;
+  display: flex;
+  gap: 30px;
+  padding: 0 30px;
+  height: calc(100% - 86px);
+  overflow: hidden;
+}
+
+.left-panel,
+.center-panel,
+.right-panel {
+  overflow: hidden;
+}
+
+.left-panel,
+.right-panel {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  gap: 24px;
+  width: 520px;
+}
+
+.center-panel {
+  flex: 1.5;
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+}
+</style>
diff --git a/src/views/safeProduction/safetyTrainingAssessment/detail.vue b/src/views/safeProduction/safetyTrainingAssessment/detail.vue
index 9e3af6b..ce627f0 100644
--- a/src/views/safeProduction/safetyTrainingAssessment/detail.vue
+++ b/src/views/safeProduction/safetyTrainingAssessment/detail.vue
@@ -155,9 +155,10 @@
   const getList = () => {
     tableLoading.value = true;
     userListNoPage({}).then(res => {
-      console.log("res", res.data);
-      tableData.value = res.data;
-      tableDataCopy.value = res.data;
+      // 瓒呯骇绠$悊鍛樿处鍙蜂笉鏄煿璁璞★紝涓嶅嚭鐜板湪鍩硅璁板綍浜哄憳鍒楄〃閲�
+      const list = (res.data || []).filter(item => item.userId !== 1);
+      tableData.value = list;
+      tableDataCopy.value = list;
       if (tableData.value.length > 0) {
         customerId.value = tableData.value[0].userId;
         receiptPaymentList(customerId.value);
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index bc5c55a..2c876b6 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -146,7 +146,7 @@
                 <template #default="scope">
                   <div>
                     <div v-if="scope.row.shippingDate">
-                      {{ scope.row.shippingDate }}
+                      {{ formatDateOnly(scope.row.shippingDate) }}
                     </div>
                     <el-tag v-else type="info">- </el-tag>
                   </div>
diff --git a/src/views/salesManagement/strategyControl/index.vue b/src/views/salesManagement/strategyControl/index.vue
index dbdae38..837ccf9 100644
--- a/src/views/salesManagement/strategyControl/index.vue
+++ b/src/views/salesManagement/strategyControl/index.vue
@@ -63,7 +63,7 @@
             </el-table-column>
             <el-table-column prop="validPeriod" label="鏈夋晥鏈�" width="200">
               <template #default="scope">
-                {{ scope.row.startDate }} 鑷� {{ scope.row.endDate }}
+                {{ formatDateOnly(scope.row.startDate) }} 鑷� {{ formatDateOnly(scope.row.endDate) }}
               </template>
             </el-table-column>
             <el-table-column prop="status" label="鐘舵��" width="80">
diff --git a/src/views/system/appVersion/index.vue b/src/views/system/appVersion/index.vue
index 6ff9ae6..5980ce9 100644
--- a/src/views/system/appVersion/index.vue
+++ b/src/views/system/appVersion/index.vue
@@ -12,12 +12,12 @@
       <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>
+          <span>{{ parseTime(scope.row.createTime) }}</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>
+          <span>{{ parseTime(scope.row.updateTime) }}</span>
         </template>
       </el-table-column>
       <el-table-column label="鍒涘缓浜�" prop="createUser" align="center" width="100"/>

--
Gitblit v1.9.3