From 19edc651c0e4718e9ccc645d7417146953fbbd70 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 25 五月 2026 11:48:38 +0800
Subject: [PATCH] 浪潮 1.安环管理页面开发联调

---
 src/views/safetyManagement/basicInfo/index.vue        |  931 +++++++++++++++++++
 src/views/safetyManagement/employeeLearning/index.vue |  175 +-
 src/views/safetyManagement/trainingManage/index.vue   |  892 ++++++++++++++++++
 src/views/safetyManagement/inspectionReport/index.vue |  293 +++++-
 src/api/safetyManagement/employeeLearning.js          |   58 +
 src/api/safetyManagement/basicInfo.js                 |  203 ++++
 src/api/safetyManagement/inspectionReport.js          |   73 +
 src/api/safetyManagement/trainingManage.js            |  135 ++
 8 files changed, 2,553 insertions(+), 207 deletions(-)

diff --git a/src/api/safetyManagement/basicInfo.js b/src/api/safetyManagement/basicInfo.js
new file mode 100644
index 0000000..c6ba92d
--- /dev/null
+++ b/src/api/safetyManagement/basicInfo.js
@@ -0,0 +1,203 @@
+import request from '@/utils/request'
+
+// ==================== 浜哄憳妗f鎺ュ彛 ====================
+export function getPersonnelList(query) {
+  return request({
+    url: '/safety/personnel/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addPersonnel(data) {
+  return request({
+    url: '/safety/personnel/add',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updatePersonnel(data) {
+  return request({
+    url: '/safety/personnel/update',
+    method: 'put',
+    data: data
+  })
+}
+
+export function deletePersonnel(id) {
+  return request({
+    url: '/safety/personnel/delete/' + id,
+    method: 'delete'
+  })
+}
+
+export function getPersonnelDetail(id) {
+  return request({
+    url: '/safety/personnel/detail/' + id,
+    method: 'get'
+  })
+}
+
+// ==================== 璁惧璁炬柦鎺ュ彛 ====================
+export function getEquipmentList(query) {
+  return request({
+    url: '/safety/equipment/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addEquipment(data) {
+  return request({
+    url: '/safety/equipment/add',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateEquipment(data) {
+  return request({
+    url: '/safety/equipment/update',
+    method: 'put',
+    data: data
+  })
+}
+
+export function deleteEquipment(id) {
+  return request({
+    url: '/safety/equipment/delete/' + id,
+    method: 'delete'
+  })
+}
+
+export function getEquipmentDetail(id) {
+  return request({
+    url: '/safety/equipment/detail/' + id,
+    method: 'get'
+  })
+}
+
+// ==================== 浣滀笟鍖哄煙鎺ュ彛 ====================
+export function getWorkAreaList(query) {
+  return request({
+    url: '/safety/workArea/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addWorkArea(data) {
+  return request({
+    url: '/safety/workArea/add',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateWorkArea(data) {
+  return request({
+    url: '/safety/workArea/update',
+    method: 'put',
+    data: data
+  })
+}
+
+export function deleteWorkArea(id) {
+  return request({
+    url: '/safety/workArea/delete/' + id,
+    method: 'delete'
+  })
+}
+
+export function getWorkAreaDetail(id) {
+  return request({
+    url: '/safety/workArea/detail/' + id,
+    method: 'get'
+  })
+}
+
+export function getAllWorkArea() {
+  return request({
+    url: '/safety/workArea/all',
+    method: 'get'
+  })
+}
+
+// ==================== 宀椾綅椋庨櫓鎺ュ彛 ====================
+export function getRiskList(query) {
+  return request({
+    url: '/safety/risk/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addRisk(data) {
+  return request({
+    url: '/safety/risk/add',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateRisk(data) {
+  return request({
+    url: '/safety/risk/update',
+    method: 'put',
+    data: data
+  })
+}
+
+export function deleteRisk(id) {
+  return request({
+    url: '/safety/risk/delete/' + id,
+    method: 'delete'
+  })
+}
+
+export function getRiskDetail(id) {
+  return request({
+    url: '/safety/risk/detail/' + id,
+    method: 'get'
+  })
+}
+
+// ==================== 搴旀�ヨ祫婧愭帴鍙� ====================
+export function getEmergencyList(query) {
+  return request({
+    url: '/safety/emergency/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addEmergency(data) {
+  return request({
+    url: '/safety/emergency/add',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateEmergency(data) {
+  return request({
+    url: '/safety/emergency/update',
+    method: 'put',
+    data: data
+  })
+}
+
+export function deleteEmergency(id) {
+  return request({
+    url: '/safety/emergency/delete/' + id,
+    method: 'delete'
+  })
+}
+
+export function getEmergencyDetail(id) {
+  return request({
+    url: '/safety/emergency/detail/' + id,
+    method: 'get'
+  })
+}
diff --git a/src/api/safetyManagement/employeeLearning.js b/src/api/safetyManagement/employeeLearning.js
new file mode 100644
index 0000000..41fd47c
--- /dev/null
+++ b/src/api/safetyManagement/employeeLearning.js
@@ -0,0 +1,58 @@
+import request from '@/utils/request'
+
+// ==================== 瀛︿範璁板綍鎺ュ彛 ====================
+export function getLearningRecordList(query) {
+  return request({
+    url: '/safety/learning/record/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function getLearningStatistics() {
+  return request({
+    url: '/safety/learning/record/statistics',
+    method: 'get'
+  })
+}
+
+// ==================== 鑰冩牳鎶ュ憡鎺ュ彛 ====================
+export function getAssessmentReportList(query) {
+  return request({
+    url: '/safety/learning/report/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function getAssessmentReportDetail(id) {
+  return request({
+    url: '/safety/learning/report/detail/' + id,
+    method: 'get'
+  })
+}
+
+// ==================== 娴嬭瘎浠诲姟鎺ュ彛 ====================
+export function getAssessmentTaskList(query) {
+  return request({
+    url: '/safety/learning/assessment/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function startAssessment(data) {
+  return request({
+    url: '/safety/learning/assessment/start',
+    method: 'post',
+    data: data
+  })
+}
+
+export function submitAssessment(data) {
+  return request({
+    url: '/safety/learning/assessment/submit',
+    method: 'post',
+    data: data
+  })
+}
diff --git a/src/api/safetyManagement/inspectionReport.js b/src/api/safetyManagement/inspectionReport.js
new file mode 100644
index 0000000..f1fe313
--- /dev/null
+++ b/src/api/safetyManagement/inspectionReport.js
@@ -0,0 +1,73 @@
+import request from '@/utils/request'
+
+// ==================== 宸℃璁板綍鎺ュ彛 ====================
+export function getInspectionRecordList(query) {
+  return request({
+    url: '/safety/inspection/record/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addInspectionRecord(data) {
+  return request({
+    url: '/safety/inspection/record/add',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateInspectionRecord(data) {
+  return request({
+    url: '/safety/inspection/record/update',
+    method: 'put',
+    data: data
+  })
+}
+
+export function deleteInspectionRecord(id) {
+  return request({
+    url: '/safety/inspection/record/delete/' + id,
+    method: 'delete'
+  })
+}
+
+// 鍚屾宸℃鏁版嵁锛堟ā鎷熷瓙绯荤粺锛�
+export function syncInspectionData() {
+  return request({
+    url: '/safety/inspection/record/sync',
+    method: 'post'
+  })
+}
+
+// ==================== 缁熻鎶ヨ〃鎺ュ彛 ====================
+export function getTodayStatistics() {
+  return request({
+    url: '/safety/inspection/statistics/today',
+    method: 'get'
+  })
+}
+
+export function getTrendStatistics(params) {
+  return request({
+    url: '/safety/inspection/statistics/trend',
+    method: 'get',
+    params: params
+  })
+}
+
+export function getTypeStatistics(params) {
+  return request({
+    url: '/safety/inspection/statistics/type',
+    method: 'get',
+    params: params
+  })
+}
+
+export function getInspectorStatistics(params) {
+  return request({
+    url: '/safety/inspection/statistics/inspector',
+    method: 'get',
+    params: params
+  })
+}
diff --git a/src/api/safetyManagement/trainingManage.js b/src/api/safetyManagement/trainingManage.js
new file mode 100644
index 0000000..84880b1
--- /dev/null
+++ b/src/api/safetyManagement/trainingManage.js
@@ -0,0 +1,135 @@
+import request from '@/utils/request'
+
+// ==================== 鍩硅璧勬枡鎺ュ彛 ====================
+export function getMaterialList(query) {
+  return request({
+    url: '/safety/training/material/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function uploadMaterial(data) {
+  return request({
+    url: '/safety/training/material/upload',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateMaterial(data) {
+  return request({
+    url: '/safety/training/material/update',
+    method: 'put',
+    data: data
+  })
+}
+
+export function deleteMaterial(id) {
+  return request({
+    url: '/safety/training/material/delete/' + id,
+    method: 'delete'
+  })
+}
+
+export function getMaterialDetail(id) {
+  return request({
+    url: '/safety/training/material/detail/' + id,
+    method: 'get'
+  })
+}
+
+// ==================== 鍩硅璁″垝鎺ュ彛 ====================
+export function getPlanList(query) {
+  return request({
+    url: '/safety/training/plan/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addPlan(data) {
+  return request({
+    url: '/safety/training/plan/add',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updatePlan(data) {
+  return request({
+    url: '/safety/training/plan/update',
+    method: 'put',
+    data: data
+  })
+}
+
+export function deletePlan(id) {
+  return request({
+    url: '/safety/training/plan/delete/' + id,
+    method: 'delete'
+  })
+}
+
+export function getPlanDetail(id) {
+  return request({
+    url: '/safety/training/plan/detail/' + id,
+    method: 'get'
+  })
+}
+
+// ==================== 瀹屾垚璁板綍鎺ュ彛 ====================
+export function getRecordList(query) {
+  return request({
+    url: '/safety/training/record/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addRecord(data) {
+  return request({
+    url: '/safety/training/record/add',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateRecord(data) {
+  return request({
+    url: '/safety/training/record/update',
+    method: 'put',
+    data: data
+  })
+}
+
+export function deleteRecord(id) {
+  return request({
+    url: '/safety/training/record/delete/' + id,
+    method: 'delete'
+  })
+}
+
+export function getRecordDetail(id) {
+  return request({
+    url: '/safety/training/record/detail/' + id,
+    method: 'get'
+  })
+}
+
+export function exportRecord(query) {
+  return request({
+    url: '/safety/training/record/export',
+    method: 'get',
+    params: query,
+    responseType: 'blob'
+  })
+}
+
+// 鑾峰彇瀛︿範缁熻锛堢敤浜庡憳宸ュ涔犻〉闈級
+export function getLearningStatistics() {
+  return request({
+    url: '/safety/training/record/statistics',
+    method: 'get'
+  })
+}
diff --git a/src/views/safetyManagement/basicInfo/index.vue b/src/views/safetyManagement/basicInfo/index.vue
index ce0160e..a640aa5 100644
--- a/src/views/safetyManagement/basicInfo/index.vue
+++ b/src/views/safetyManagement/basicInfo/index.vue
@@ -18,7 +18,7 @@
           <div class="actions">
             <el-button type="primary" @click="addPersonnel" icon="Plus">鏂板</el-button>
           </div>
-          <PIMTable :column="personnelColumns" :tableData="personnelList" :page="personnelPage" @pagination="changePersonnelPage" />
+          <PIMTable :column="personnelColumns" :tableData="personnelList" :page="personnelPage" @pagination="changePersonnelPage" :tableLoading="personnelLoading" />
         </div>
       </el-tab-pane>
 
@@ -28,7 +28,7 @@
             <el-input v-model="equipmentFilters.name" placeholder="璇疯緭鍏ヨ澶囧悕绉�" clearable style="width: 200px" />
           </el-form-item>
           <el-form-item label="鎵�灞炲尯鍩�">
-            <el-input v-model="equipmentFilters.area" placeholder="璇疯緭鍏ユ墍灞炲尯鍩�" clearable style="width: 200px" />
+            <el-input v-model="equipmentFilters.areaName" placeholder="璇疯緭鍏ユ墍灞炲尯鍩�" clearable style="width: 200px" />
           </el-form-item>
           <el-form-item>
             <el-button type="primary" @click="getEquipmentData">鎼滅储</el-button>
@@ -39,7 +39,7 @@
           <div class="actions">
             <el-button type="primary" @click="addEquipment" icon="Plus">鏂板</el-button>
           </div>
-          <PIMTable :column="equipmentColumns" :tableData="equipmentList" :page="equipmentPage" @pagination="changeEquipmentPage" />
+          <PIMTable :column="equipmentColumns" :tableData="equipmentList" :page="equipmentPage" @pagination="changeEquipmentPage" :tableLoading="equipmentLoading" />
         </div>
       </el-tab-pane>
 
@@ -57,7 +57,7 @@
           <div class="actions">
             <el-button type="primary" @click="addArea" icon="Plus">鏂板</el-button>
           </div>
-          <PIMTable :column="areaColumns" :tableData="areaList" :page="areaPage" @pagination="changeAreaPage" />
+          <PIMTable :column="areaColumns" :tableData="areaList" :page="areaPage" @pagination="changeAreaPage" :tableLoading="areaLoading" />
         </div>
       </el-tab-pane>
 
@@ -75,7 +75,7 @@
           <div class="actions">
             <el-button type="primary" @click="addRisk" icon="Plus">鏂板</el-button>
           </div>
-          <PIMTable :column="riskColumns" :tableData="riskList" :page="riskPage" @pagination="changeRiskPage" />
+          <PIMTable :column="riskColumns" :tableData="riskList" :page="riskPage" @pagination="changeRiskPage" :tableLoading="riskLoading" />
         </div>
       </el-tab-pane>
 
@@ -93,16 +93,197 @@
           <div class="actions">
             <el-button type="primary" @click="addEmergency" icon="Plus">鏂板</el-button>
           </div>
-          <PIMTable :column="emergencyColumns" :tableData="emergencyList" :page="emergencyPage" @pagination="changeEmergencyPage" />
+          <PIMTable :column="emergencyColumns" :tableData="emergencyList" :page="emergencyPage" @pagination="changeEmergencyPage" :tableLoading="emergencyLoading" />
         </div>
       </el-tab-pane>
     </el-tabs>
+
+    <!-- 浜哄憳妗f寮圭獥 -->
+    <el-dialog :title="personnelDialog.title" v-model="personnelDialog.visible" width="600px" append-to-body>
+      <el-form ref="personnelFormRef" :model="personnelForm" :rules="personnelRules" label-width="100px">
+        <el-form-item label="濮撳悕" prop="name">
+          <el-input v-model="personnelForm.name" placeholder="璇疯緭鍏ュ鍚�" />
+        </el-form-item>
+        <el-form-item label="閮ㄩ棬" prop="dept">
+          <el-input v-model="personnelForm.dept" placeholder="璇疯緭鍏ラ儴闂�" />
+        </el-form-item>
+        <el-form-item label="宀椾綅" prop="post">
+          <el-input v-model="personnelForm.post" placeholder="璇疯緭鍏ュ矖浣�" />
+        </el-form-item>
+        <el-form-item label="鑱旂郴鏂瑰紡" prop="phone">
+          <el-input v-model="personnelForm.phone" placeholder="璇疯緭鍏ヨ仈绯绘柟寮�" />
+        </el-form-item>
+        <el-form-item label="鍏ヨ亴鏃ユ湡" prop="entryDate">
+          <el-date-picker v-model="personnelForm.entryDate" type="date" placeholder="閫夋嫨鍏ヨ亴鏃ユ湡" value-format="YYYY-MM-DD" style="width: 100%" />
+        </el-form-item>
+        <el-form-item label="鐘舵��" prop="status">
+          <el-radio-group v-model="personnelForm.status">
+            <el-radio :value="1">鍦ㄨ亴</el-radio>
+            <el-radio :value="0">绂昏亴</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="personnelForm.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="personnelDialog.visible = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="submitPersonnelForm" :loading="personnelDialog.loading">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 璁惧璁炬柦寮圭獥 -->
+    <el-dialog :title="equipmentDialog.title" v-model="equipmentDialog.visible" width="600px" append-to-body>
+      <el-form ref="equipmentFormRef" :model="equipmentForm" :rules="equipmentRules" label-width="100px">
+        <el-form-item label="璁惧鍚嶇О" prop="name">
+          <el-input v-model="equipmentForm.name" placeholder="璇疯緭鍏ヨ澶囧悕绉�" />
+        </el-form-item>
+        <el-form-item label="瑙勬牸鍨嬪彿" prop="model">
+          <el-input v-model="equipmentForm.model" placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�" />
+        </el-form-item>
+        <el-form-item label="鎵�灞炲尯鍩�" prop="areaName">
+          <el-input v-model="equipmentForm.areaName" placeholder="璇疯緭鍏ユ墍灞炲尯鍩�" />
+        </el-form-item>
+        <el-form-item label="鐘舵��" prop="status">
+          <el-radio-group v-model="equipmentForm.status">
+            <el-radio :value="1">姝e父</el-radio>
+            <el-radio :value="0">鍋滅敤</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="equipmentForm.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="equipmentDialog.visible = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="submitEquipmentForm" :loading="equipmentDialog.loading">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 浣滀笟鍖哄煙寮圭獥 -->
+    <el-dialog :title="areaDialog.title" v-model="areaDialog.visible" width="600px" append-to-body>
+      <el-form ref="areaFormRef" :model="areaForm" :rules="areaRules" label-width="100px">
+        <el-form-item label="鍖哄煙鍚嶇О" prop="name">
+          <el-input v-model="areaForm.name" placeholder="璇疯緭鍏ュ尯鍩熷悕绉�" />
+        </el-form-item>
+        <el-form-item label="浣嶇疆" prop="location">
+          <el-input v-model="areaForm.location" placeholder="璇疯緭鍏ヤ綅缃�" />
+        </el-form-item>
+        <el-form-item label="璐熻矗浜�" prop="manager">
+          <el-input v-model="areaForm.manager" placeholder="璇疯緭鍏ヨ礋璐d汉" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="areaForm.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="areaDialog.visible = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="submitAreaForm" :loading="areaDialog.loading">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 宀椾綅椋庨櫓寮圭獥 -->
+    <el-dialog :title="riskDialog.title" v-model="riskDialog.visible" width="600px" append-to-body>
+      <el-form ref="riskFormRef" :model="riskForm" :rules="riskRules" label-width="100px">
+        <el-form-item label="椋庨櫓绫诲瀷" prop="type">
+          <el-input v-model="riskForm.type" placeholder="璇疯緭鍏ラ闄╃被鍨�" />
+        </el-form-item>
+        <el-form-item label="椋庨櫓绛夌骇" prop="level">
+          <el-select v-model="riskForm.level" placeholder="璇烽�夋嫨椋庨櫓绛夌骇" style="width: 100%">
+            <el-option label="楂�" value="high" />
+            <el-option label="涓�" value="medium" />
+            <el-option label="浣�" value="low" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鍏宠仈宀椾綅" prop="post">
+          <el-input v-model="riskForm.post" placeholder="璇疯緭鍏ュ叧鑱斿矖浣�" />
+        </el-form-item>
+        <el-form-item label="椋庨櫓鎻忚堪" prop="description">
+          <el-input v-model="riskForm.description" type="textarea" :rows="3" placeholder="璇疯緭鍏ラ闄╂弿杩�" />
+        </el-form-item>
+        <el-form-item label="鎺у埗鎺柦" prop="controlMeasures">
+          <el-input v-model="riskForm.controlMeasures" type="textarea" :rows="3" placeholder="璇疯緭鍏ユ帶鍒舵帾鏂�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="riskForm.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="riskDialog.visible = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="submitRiskForm" :loading="riskDialog.loading">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 搴旀�ヨ祫婧愬脊绐� -->
+    <el-dialog :title="emergencyDialog.title" v-model="emergencyDialog.visible" width="600px" append-to-body>
+      <el-form ref="emergencyFormRef" :model="emergencyForm" :rules="emergencyRules" label-width="100px">
+        <el-form-item label="璧勬簮鍚嶇О" prop="name">
+          <el-input v-model="emergencyForm.name" placeholder="璇疯緭鍏ヨ祫婧愬悕绉�" />
+        </el-form-item>
+        <el-form-item label="璧勬簮绫诲瀷" prop="type">
+          <el-input v-model="emergencyForm.type" placeholder="璇疯緭鍏ヨ祫婧愮被鍨�" />
+        </el-form-item>
+        <el-form-item label="鏁伴噺" prop="quantity">
+          <el-input-number v-model="emergencyForm.quantity" :min="0" style="width: 100%" />
+        </el-form-item>
+        <el-form-item label="鎵�灞炲尯鍩�" prop="areaName">
+          <el-input v-model="emergencyForm.areaName" placeholder="璇疯緭鍏ユ墍灞炲尯鍩�" />
+        </el-form-item>
+        <el-form-item label="瀛樻斁浣嶇疆" prop="location">
+          <el-input v-model="emergencyForm.location" placeholder="璇疯緭鍏ュ瓨鏀句綅缃�" />
+        </el-form-item>
+        <el-form-item label="绠$悊浜�" prop="manager">
+          <el-input v-model="emergencyForm.manager" placeholder="璇疯緭鍏ョ鐞嗕汉" />
+        </el-form-item>
+        <el-form-item label="鐘舵��" prop="status">
+          <el-radio-group v-model="emergencyForm.status">
+            <el-radio :value="1">姝e父</el-radio>
+            <el-radio :value="0">缂哄け</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="emergencyForm.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="emergencyDialog.visible = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="submitEmergencyForm" :loading="emergencyDialog.loading">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { ref, reactive } from "vue";
+import { ref, reactive, onMounted } from "vue";
 import PIMTable from "@/components/PIMTable/PIMTable.vue";
+import {
+  getPersonnelList,
+  addPersonnel as addPersonnelApi,
+  updatePersonnel,
+  deletePersonnel,
+  getPersonnelDetail,
+  getEquipmentList,
+  addEquipment as addEquipmentApi,
+  updateEquipment,
+  deleteEquipment,
+  getEquipmentDetail,
+  getWorkAreaList,
+  addWorkArea as addWorkAreaApi,
+  updateWorkArea,
+  deleteWorkArea,
+  getWorkAreaDetail,
+  getRiskList,
+  addRisk as addRiskApi,
+  updateRisk,
+  deleteRisk,
+  getRiskDetail,
+  getEmergencyList,
+  addEmergency as addEmergencyApi,
+  updateEmergency,
+  deleteEmergency,
+  getEmergencyDetail
+} from "@/api/safetyManagement/basicInfo.js";
+import { ElMessage, ElMessageBox } from "element-plus";
 
 defineOptions({
   name: "鍩虹淇℃伅绠$悊",
@@ -110,88 +291,762 @@
 
 const activeTab = ref("personnel");
 
-// 浜哄憳妗f
+// ==================== 浜哄憳妗f ====================
 const personnelFilters = reactive({ name: "", dept: "" });
 const personnelList = ref([]);
 const personnelPage = reactive({ current: 1, size: 10, total: 0 });
+const personnelLoading = ref(false);
 const personnelColumns = [
   { label: "濮撳悕", prop: "name", align: "center" },
   { label: "閮ㄩ棬", prop: "dept", align: "center" },
   { label: "宀椾綅", prop: "post", align: "center" },
   { label: "鑱旂郴鏂瑰紡", prop: "phone", align: "center" },
   { label: "鍏ヨ亴鏃ユ湡", prop: "entryDate", align: "center" },
+  {
+    label: "鐘舵��",
+    prop: "status",
+    align: "center",
+    dataType: "tag",
+    formatType: (val) => (val === 1 ? "success" : "info"),
+    formatData: (val) => (val === 1 ? "鍦ㄨ亴" : "绂昏亴")
+  },
+  {
+    label: "鎿嶄綔",
+    prop: "action",
+    align: "center",
+    dataType: "action",
+    operation: [
+      { name: "缂栬緫", type: "text", clickFun: (row) => handleEditPersonnel(row) },
+      { name: "鍒犻櫎", type: "text", clickFun: (row) => handleDeletePersonnel(row) }
+    ]
+  }
 ];
 
-// 璁惧璁炬柦
-const equipmentFilters = reactive({ name: "", area: "" });
+const personnelDialog = reactive({ visible: false, title: "", loading: false });
+const personnelFormRef = ref(null);
+const personnelForm = reactive({
+  id: null,
+  name: "",
+  dept: "",
+  post: "",
+  phone: "",
+  entryDate: "",
+  status: 1,
+  remark: ""
+});
+const personnelRules = {
+  name: [{ required: true, message: "璇疯緭鍏ュ鍚�", trigger: "blur" }],
+  dept: [{ required: true, message: "璇疯緭鍏ラ儴闂�", trigger: "blur" }]
+};
+
+// ==================== 璁惧璁炬柦 ====================
+const equipmentFilters = reactive({ name: "", areaName: "" });
 const equipmentList = ref([]);
 const equipmentPage = reactive({ current: 1, size: 10, total: 0 });
+const equipmentLoading = ref(false);
 const equipmentColumns = [
   { label: "璁惧鍚嶇О", prop: "name", align: "center" },
   { label: "瑙勬牸鍨嬪彿", prop: "model", align: "center" },
-  { label: "鎵�灞炲尯鍩�", prop: "area", align: "center" },
-  { label: "鐘舵��", prop: "status", align: "center" },
+  { label: "鎵�灞炲尯鍩�", prop: "areaName", align: "center" },
+  {
+    label: "鐘舵��",
+    prop: "status",
+    align: "center",
+    dataType: "tag",
+    formatType: (val) => (val === 1 ? "success" : "danger"),
+    formatData: (val) => (val === 1 ? "姝e父" : "鍋滅敤")
+  },
+  {
+    label: "鎿嶄綔",
+    prop: "action",
+    align: "center",
+    dataType: "action",
+    operation: [
+      { name: "缂栬緫", type: "text", clickFun: (row) => handleEditEquipment(row) },
+      { name: "鍒犻櫎", type: "text", clickFun: (row) => handleDeleteEquipment(row) }
+    ]
+  }
 ];
 
-// 浣滀笟鍖哄煙
+const equipmentDialog = reactive({ visible: false, title: "", loading: false });
+const equipmentFormRef = ref(null);
+const equipmentForm = reactive({
+  id: null,
+  name: "",
+  model: "",
+  areaName: "",
+  status: 1,
+  remark: ""
+});
+const equipmentRules = {
+  name: [{ required: true, message: "璇疯緭鍏ヨ澶囧悕绉�", trigger: "blur" }]
+};
+
+// ==================== 浣滀笟鍖哄煙 ====================
 const areaFilters = reactive({ name: "" });
 const areaList = ref([]);
 const areaPage = reactive({ current: 1, size: 10, total: 0 });
+const areaLoading = ref(false);
 const areaColumns = [
   { label: "鍖哄煙鍚嶇О", prop: "name", align: "center" },
   { label: "浣嶇疆", prop: "location", align: "center" },
   { label: "璐熻矗浜�", prop: "manager", align: "center" },
+  {
+    label: "鎿嶄綔",
+    prop: "action",
+    align: "center",
+    dataType: "action",
+    operation: [
+      { name: "缂栬緫", type: "text", clickFun: (row) => handleEditArea(row) },
+      { name: "鍒犻櫎", type: "text", clickFun: (row) => handleDeleteArea(row) }
+    ]
+  }
 ];
 
-// 宀椾綅椋庨櫓
+const areaDialog = reactive({ visible: false, title: "", loading: false });
+const areaFormRef = ref(null);
+const areaForm = reactive({
+  id: null,
+  name: "",
+  location: "",
+  manager: "",
+  remark: ""
+});
+const areaRules = {
+  name: [{ required: true, message: "璇疯緭鍏ュ尯鍩熷悕绉�", trigger: "blur" }]
+};
+
+// ==================== 宀椾綅椋庨櫓 ====================
 const riskFilters = reactive({ type: "" });
 const riskList = ref([]);
 const riskPage = reactive({ current: 1, size: 10, total: 0 });
+const riskLoading = ref(false);
 const riskColumns = [
   { label: "椋庨櫓绫诲瀷", prop: "type", align: "center" },
-  { label: "椋庨櫓绛夌骇", prop: "level", align: "center" },
+  {
+    label: "椋庨櫓绛夌骇",
+    prop: "level",
+    align: "center",
+    dataType: "tag",
+    formatType: (val) => {
+      if (val === 'high') return 'danger';
+      if (val === 'medium') return 'warning';
+      return 'success';
+    },
+    formatData: (val) => {
+      const map = { high: '楂�', medium: '涓�', low: '浣�' };
+      return map[val] || val;
+    }
+  },
   { label: "鎻忚堪", prop: "description", align: "center" },
+  {
+    label: "鎿嶄綔",
+    prop: "action",
+    align: "center",
+    dataType: "action",
+    operation: [
+      { name: "缂栬緫", type: "text", clickFun: (row) => handleEditRisk(row) },
+      { name: "鍒犻櫎", type: "text", clickFun: (row) => handleDeleteRisk(row) }
+    ]
+  }
 ];
 
-// 搴旀�ヨ祫婧�
+const riskDialog = reactive({ visible: false, title: "", loading: false });
+const riskFormRef = ref(null);
+const riskForm = reactive({
+  id: null,
+  type: "",
+  level: "",
+  description: "",
+  post: "",
+  controlMeasures: "",
+  remark: ""
+});
+const riskRules = {
+  type: [{ required: true, message: "璇疯緭鍏ラ闄╃被鍨�", trigger: "blur" }],
+  level: [{ required: true, message: "璇烽�夋嫨椋庨櫓绛夌骇", trigger: "change" }]
+};
+
+// ==================== 搴旀�ヨ祫婧� ====================
 const emergencyFilters = reactive({ name: "" });
 const emergencyList = ref([]);
 const emergencyPage = reactive({ current: 1, size: 10, total: 0 });
+const emergencyLoading = ref(false);
 const emergencyColumns = [
   { label: "璧勬簮鍚嶇О", prop: "name", align: "center" },
   { label: "绫诲瀷", prop: "type", align: "center" },
   { label: "鏁伴噺", prop: "quantity", align: "center" },
   { label: "瀛樻斁浣嶇疆", prop: "location", align: "center" },
+  {
+    label: "鐘舵��",
+    prop: "status",
+    align: "center",
+    dataType: "tag",
+    formatType: (val) => (val === 1 ? "success" : "danger"),
+    formatData: (val) => (val === 1 ? "姝e父" : "缂哄け")
+  },
+  {
+    label: "鎿嶄綔",
+    prop: "action",
+    align: "center",
+    dataType: "action",
+    operation: [
+      { name: "缂栬緫", type: "text", clickFun: (row) => handleEditEmergency(row) },
+      { name: "鍒犻櫎", type: "text", clickFun: (row) => handleDeleteEmergency(row) }
+    ]
+  }
 ];
 
-const handleTabChange = () => {
-  // 鍒囨崲tab鏃跺姞杞藉搴旀暟鎹�
+const emergencyDialog = reactive({ visible: false, title: "", loading: false });
+const emergencyFormRef = ref(null);
+const emergencyForm = reactive({
+  id: null,
+  name: "",
+  type: "",
+  quantity: 0,
+  areaName: "",
+  location: "",
+  manager: "",
+  status: 1,
+  remark: ""
+});
+const emergencyRules = {
+  name: [{ required: true, message: "璇疯緭鍏ヨ祫婧愬悕绉�", trigger: "blur" }],
+  type: [{ required: true, message: "璇疯緭鍏ヨ祫婧愮被鍨�", trigger: "blur" }]
 };
 
-const getPersonnelData = () => {};
-const resetPersonnelFilters = () => { personnelFilters.name = ""; personnelFilters.dept = ""; };
-const addPersonnel = () => {};
-const changePersonnelPage = ({ page, limit }) => { personnelPage.current = page; personnelPage.size = limit; };
+// ==================== 閫氱敤鏂规硶 ====================
+const loadData = () => {
+  switch (activeTab.value) {
+    case 'personnel':
+      getPersonnelData();
+      break;
+    case 'equipment':
+      getEquipmentData();
+      break;
+    case 'workArea':
+      getAreaData();
+      break;
+    case 'risk':
+      getRiskData();
+      break;
+    case 'emergency':
+      getEmergencyData();
+      break;
+  }
+};
 
-const getEquipmentData = () => {};
-const resetEquipmentFilters = () => { equipmentFilters.name = ""; equipmentFilters.area = ""; };
-const addEquipment = () => {};
-const changeEquipmentPage = ({ page, limit }) => { equipmentPage.current = page; equipmentPage.size = limit; };
+const handleTabChange = () => {
+  loadData();
+};
 
-const getAreaData = () => {};
-const resetAreaFilters = () => { areaFilters.name = ""; };
-const addArea = () => {};
-const changeAreaPage = ({ page, limit }) => { areaPage.current = page; areaPage.size = limit; };
 
-const getRiskData = () => {};
-const resetRiskFilters = () => { riskFilters.type = ""; };
-const addRisk = () => {};
-const changeRiskPage = ({ page, limit }) => { riskPage.current = page; riskPage.size = limit; };
 
-const getEmergencyData = () => {};
-const resetEmergencyFilters = () => { emergencyFilters.name = ""; };
-const addEmergency = () => {};
-const changeEmergencyPage = ({ page, limit }) => { emergencyPage.current = page; emergencyPage.size = limit; };
+// ==================== 浜哄憳妗f鏂规硶 ====================
+const getPersonnelData = async () => {
+  personnelLoading.value = true;
+  try {
+    const res = await getPersonnelList({
+      pageNum: personnelPage.current,
+      pageSize: personnelPage.size,
+      ...personnelFilters
+    });
+    if (res.code === 200) {
+      personnelList.value = res.data.rows || res.data.records || [];
+      personnelPage.total = res.data.total || 0;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇浜哄憳妗f澶辫触');
+  } finally {
+    personnelLoading.value = false;
+  }
+};
+
+const resetPersonnelFilters = () => {
+  personnelFilters.name = "";
+  personnelFilters.dept = "";
+  personnelPage.current = 1;
+  getPersonnelData();
+};
+
+const changePersonnelPage = ({ page, limit }) => {
+  personnelPage.current = page;
+  personnelPage.size = limit;
+  getPersonnelData();
+};
+
+const resetPersonnelForm = () => {
+  personnelForm.id = null;
+  personnelForm.name = "";
+  personnelForm.dept = "";
+  personnelForm.post = "";
+  personnelForm.phone = "";
+  personnelForm.entryDate = "";
+  personnelForm.status = 1;
+  personnelForm.remark = "";
+};
+
+const addPersonnel = () => {
+  resetPersonnelForm();
+  personnelDialog.title = "鏂板浜哄憳妗f";
+  personnelDialog.visible = true;
+};
+
+const handleEditPersonnel = async (row) => {
+  resetPersonnelForm();
+  try {
+    const res = await getPersonnelDetail(row.id);
+    if (res.code === 200) {
+      Object.assign(personnelForm, res.data);
+      personnelDialog.title = "缂栬緫浜哄憳妗f";
+      personnelDialog.visible = true;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇浜哄憳妗f璇︽儏澶辫触');
+  }
+};
+
+const handleDeletePersonnel = (row) => {
+  ElMessageBox.confirm(`纭鍒犻櫎浜哄憳 "${row.name}" 鍚楋紵`, "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning"
+  }).then(async () => {
+    try {
+      const res = await deletePersonnel(row.id);
+      if (res.code === 200) {
+        ElMessage.success("鍒犻櫎鎴愬姛");
+        getPersonnelData();
+      }
+    } catch (error) {
+      ElMessage.error("鍒犻櫎澶辫触");
+    }
+  });
+};
+
+const submitPersonnelForm = async () => {
+  const valid = await personnelFormRef.value.validate().catch(() => false);
+  if (!valid) return;
+  
+  personnelDialog.loading = true;
+  try {
+    const api = personnelForm.id ? updatePersonnel : addPersonnelApi;
+    const res = await api(personnelForm);
+    if (res.code === 200) {
+      ElMessage.success(personnelForm.id ? "淇敼鎴愬姛" : "鏂板鎴愬姛");
+      personnelDialog.visible = false;
+      getPersonnelData();
+    }
+  } catch (error) {
+    ElMessage.error(personnelForm.id ? "淇敼澶辫触" : "鏂板澶辫触");
+  } finally {
+    personnelDialog.loading = false;
+  }
+};
+
+// ==================== 璁惧璁炬柦鏂规硶 ====================
+const getEquipmentData = async () => {
+  equipmentLoading.value = true;
+  try {
+    const res = await getEquipmentList({
+      pageNum: equipmentPage.current,
+      pageSize: equipmentPage.size,
+      ...equipmentFilters
+    });
+    if (res.code === 200) {
+      equipmentList.value = res.data.rows || res.data.records || [];
+      equipmentPage.total = res.data.total || 0;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇璁惧璁炬柦澶辫触');
+  } finally {
+    equipmentLoading.value = false;
+  }
+};
+
+const resetEquipmentFilters = () => {
+  equipmentFilters.name = "";
+  equipmentFilters.areaName = "";
+  equipmentPage.current = 1;
+  getEquipmentData();
+};
+
+const changeEquipmentPage = ({ page, limit }) => {
+  equipmentPage.current = page;
+  equipmentPage.size = limit;
+  getEquipmentData();
+};
+
+const resetEquipmentForm = () => {
+  equipmentForm.id = null;
+  equipmentForm.name = "";
+  equipmentForm.model = "";
+  equipmentForm.areaName = "";
+  equipmentForm.status = 1;
+  equipmentForm.remark = "";
+};
+
+const addEquipment = () => {
+  resetEquipmentForm();
+  equipmentDialog.title = "鏂板璁惧璁炬柦";
+  equipmentDialog.visible = true;
+};
+
+const handleEditEquipment = async (row) => {
+  resetEquipmentForm();
+  try {
+    const res = await getEquipmentDetail(row.id);
+    if (res.code === 200) {
+      Object.assign(equipmentForm, res.data);
+      equipmentDialog.title = "缂栬緫璁惧璁炬柦";
+      equipmentDialog.visible = true;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇璁惧璇︽儏澶辫触');
+  }
+};
+
+const handleDeleteEquipment = (row) => {
+  ElMessageBox.confirm(`纭鍒犻櫎璁惧 "${row.name}" 鍚楋紵`, "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning"
+  }).then(async () => {
+    try {
+      const res = await deleteEquipment(row.id);
+      if (res.code === 200) {
+        ElMessage.success("鍒犻櫎鎴愬姛");
+        getEquipmentData();
+      }
+    } catch (error) {
+      ElMessage.error("鍒犻櫎澶辫触");
+    }
+  });
+};
+
+const submitEquipmentForm = async () => {
+  const valid = await equipmentFormRef.value.validate().catch(() => false);
+  if (!valid) return;
+  
+  equipmentDialog.loading = true;
+  try {
+    const api = equipmentForm.id ? updateEquipment : addEquipmentApi;
+    const res = await api(equipmentForm);
+    if (res.code === 200) {
+      ElMessage.success(equipmentForm.id ? "淇敼鎴愬姛" : "鏂板鎴愬姛");
+      equipmentDialog.visible = false;
+      getEquipmentData();
+    }
+  } catch (error) {
+    ElMessage.error(equipmentForm.id ? "淇敼澶辫触" : "鏂板澶辫触");
+  } finally {
+    equipmentDialog.loading = false;
+  }
+};
+
+// ==================== 浣滀笟鍖哄煙鏂规硶 ====================
+const getAreaData = async () => {
+  areaLoading.value = true;
+  try {
+    const res = await getWorkAreaList({
+      pageNum: areaPage.current,
+      pageSize: areaPage.size,
+      ...areaFilters
+    });
+    if (res.code === 200) {
+      areaList.value = res.data.rows || res.data.records || [];
+      areaPage.total = res.data.total || 0;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇浣滀笟鍖哄煙澶辫触');
+  } finally {
+    areaLoading.value = false;
+  }
+};
+
+const resetAreaFilters = () => {
+  areaFilters.name = "";
+  areaPage.current = 1;
+  getAreaData();
+};
+
+const changeAreaPage = ({ page, limit }) => {
+  areaPage.current = page;
+  areaPage.size = limit;
+  getAreaData();
+};
+
+const resetAreaForm = () => {
+  areaForm.id = null;
+  areaForm.name = "";
+  areaForm.location = "";
+  areaForm.manager = "";
+  areaForm.remark = "";
+};
+
+const addArea = () => {
+  resetAreaForm();
+  areaDialog.title = "鏂板浣滀笟鍖哄煙";
+  areaDialog.visible = true;
+};
+
+const handleEditArea = async (row) => {
+  resetAreaForm();
+  try {
+    const res = await getWorkAreaDetail(row.id);
+    if (res.code === 200) {
+      Object.assign(areaForm, res.data);
+      areaDialog.title = "缂栬緫浣滀笟鍖哄煙";
+      areaDialog.visible = true;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇浣滀笟鍖哄煙璇︽儏澶辫触');
+  }
+};
+
+const handleDeleteArea = (row) => {
+  ElMessageBox.confirm(`纭鍒犻櫎鍖哄煙 "${row.name}" 鍚楋紵`, "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning"
+  }).then(async () => {
+    try {
+      const res = await deleteWorkArea(row.id);
+      if (res.code === 200) {
+        ElMessage.success("鍒犻櫎鎴愬姛");
+        getAreaData();
+      }
+    } catch (error) {
+      ElMessage.error("鍒犻櫎澶辫触");
+    }
+  });
+};
+
+const submitAreaForm = async () => {
+  const valid = await areaFormRef.value.validate().catch(() => false);
+  if (!valid) return;
+  
+  areaDialog.loading = true;
+  try {
+    const api = areaForm.id ? updateWorkArea : addWorkAreaApi;
+    const res = await api(areaForm);
+    if (res.code === 200) {
+      ElMessage.success(areaForm.id ? "淇敼鎴愬姛" : "鏂板鎴愬姛");
+      areaDialog.visible = false;
+      getAreaData();
+    }
+  } catch (error) {
+    ElMessage.error(areaForm.id ? "淇敼澶辫触" : "鏂板澶辫触");
+  } finally {
+    areaDialog.loading = false;
+  }
+};
+
+// ==================== 宀椾綅椋庨櫓鏂规硶 ====================
+const getRiskData = async () => {
+  riskLoading.value = true;
+  try {
+    const res = await getRiskList({
+      pageNum: riskPage.current,
+      pageSize: riskPage.size,
+      ...riskFilters
+    });
+    if (res.code === 200) {
+      riskList.value = res.data.rows || res.data.records || [];
+      riskPage.total = res.data.total || 0;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇宀椾綅椋庨櫓澶辫触');
+  } finally {
+    riskLoading.value = false;
+  }
+};
+
+const resetRiskFilters = () => {
+  riskFilters.type = "";
+  riskPage.current = 1;
+  getRiskData();
+};
+
+const changeRiskPage = ({ page, limit }) => {
+  riskPage.current = page;
+  riskPage.size = limit;
+  getRiskData();
+};
+
+const resetRiskForm = () => {
+  riskForm.id = null;
+  riskForm.type = "";
+  riskForm.level = "";
+  riskForm.description = "";
+  riskForm.post = "";
+  riskForm.controlMeasures = "";
+  riskForm.remark = "";
+};
+
+const addRisk = () => {
+  resetRiskForm();
+  riskDialog.title = "鏂板宀椾綅椋庨櫓";
+  riskDialog.visible = true;
+};
+
+const handleEditRisk = async (row) => {
+  resetRiskForm();
+  try {
+    const res = await getRiskDetail(row.id);
+    if (res.code === 200) {
+      Object.assign(riskForm, res.data);
+      riskDialog.title = "缂栬緫宀椾綅椋庨櫓";
+      riskDialog.visible = true;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇宀椾綅椋庨櫓璇︽儏澶辫触');
+  }
+};
+
+const handleDeleteRisk = (row) => {
+  ElMessageBox.confirm(`纭鍒犻櫎椋庨櫓 "${row.type}" 鍚楋紵`, "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning"
+  }).then(async () => {
+    try {
+      const res = await deleteRisk(row.id);
+      if (res.code === 200) {
+        ElMessage.success("鍒犻櫎鎴愬姛");
+        getRiskData();
+      }
+    } catch (error) {
+      ElMessage.error("鍒犻櫎澶辫触");
+    }
+  });
+};
+
+const submitRiskForm = async () => {
+  const valid = await riskFormRef.value.validate().catch(() => false);
+  if (!valid) return;
+  
+  riskDialog.loading = true;
+  try {
+    const api = riskForm.id ? updateRisk : addRiskApi;
+    const res = await api(riskForm);
+    if (res.code === 200) {
+      ElMessage.success(riskForm.id ? "淇敼鎴愬姛" : "鏂板鎴愬姛");
+      riskDialog.visible = false;
+      getRiskData();
+    }
+  } catch (error) {
+    ElMessage.error(riskForm.id ? "淇敼澶辫触" : "鏂板澶辫触");
+  } finally {
+    riskDialog.loading = false;
+  }
+};
+
+// ==================== 搴旀�ヨ祫婧愭柟娉� ====================
+const getEmergencyData = async () => {
+  emergencyLoading.value = true;
+  try {
+    const res = await getEmergencyList({
+      pageNum: emergencyPage.current,
+      pageSize: emergencyPage.size,
+      ...emergencyFilters
+    });
+    if (res.code === 200) {
+      emergencyList.value = res.data.rows || res.data.records || [];
+      emergencyPage.total = res.data.total || 0;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇搴旀�ヨ祫婧愬け璐�');
+  } finally {
+    emergencyLoading.value = false;
+  }
+};
+
+const resetEmergencyFilters = () => {
+  emergencyFilters.name = "";
+  emergencyPage.current = 1;
+  getEmergencyData();
+};
+
+const changeEmergencyPage = ({ page, limit }) => {
+  emergencyPage.current = page;
+  emergencyPage.size = limit;
+  getEmergencyData();
+};
+
+const resetEmergencyForm = () => {
+  emergencyForm.id = null;
+  emergencyForm.name = "";
+  emergencyForm.type = "";
+  emergencyForm.quantity = 0;
+  emergencyForm.areaName = "";
+  emergencyForm.location = "";
+  emergencyForm.manager = "";
+  emergencyForm.status = 1;
+  emergencyForm.remark = "";
+};
+
+const addEmergency = () => {
+  resetEmergencyForm();
+  emergencyDialog.title = "鏂板搴旀�ヨ祫婧�";
+  emergencyDialog.visible = true;
+};
+
+const handleEditEmergency = async (row) => {
+  resetEmergencyForm();
+  try {
+    const res = await getEmergencyDetail(row.id);
+    if (res.code === 200) {
+      Object.assign(emergencyForm, res.data);
+      emergencyDialog.title = "缂栬緫搴旀�ヨ祫婧�";
+      emergencyDialog.visible = true;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇搴旀�ヨ祫婧愯鎯呭け璐�');
+  }
+};
+
+const handleDeleteEmergency = (row) => {
+  ElMessageBox.confirm(`纭鍒犻櫎璧勬簮 "${row.name}" 鍚楋紵`, "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning"
+  }).then(async () => {
+    try {
+      const res = await deleteEmergency(row.id);
+      if (res.code === 200) {
+        ElMessage.success("鍒犻櫎鎴愬姛");
+        getEmergencyData();
+      }
+    } catch (error) {
+      ElMessage.error("鍒犻櫎澶辫触");
+    }
+  });
+};
+
+const submitEmergencyForm = async () => {
+  const valid = await emergencyFormRef.value.validate().catch(() => false);
+  if (!valid) return;
+  
+  emergencyDialog.loading = true;
+  try {
+    const api = emergencyForm.id ? updateEmergency : addEmergencyApi;
+    const res = await api(emergencyForm);
+    if (res.code === 200) {
+      ElMessage.success(emergencyForm.id ? "淇敼鎴愬姛" : "鏂板鎴愬姛");
+      emergencyDialog.visible = false;
+      getEmergencyData();
+    }
+  } catch (error) {
+    ElMessage.error(emergencyForm.id ? "淇敼澶辫触" : "鏂板澶辫触");
+  } finally {
+    emergencyDialog.loading = false;
+  }
+};
+
+onMounted(() => {
+  getPersonnelData();
+});
 </script>
 
 <style lang="scss" scoped>
diff --git a/src/views/safetyManagement/employeeLearning/index.vue b/src/views/safetyManagement/employeeLearning/index.vue
index 2070899..f4c6bc0 100644
--- a/src/views/safetyManagement/employeeLearning/index.vue
+++ b/src/views/safetyManagement/employeeLearning/index.vue
@@ -11,103 +11,128 @@
           <el-divider />
           <div class="stat-item">
             <span class="label">宸插畬鎴愬煿璁�</span>
-            <span class="value">{{ userInfo.completedTrainings }} 娆�</span>
+            <span class="value">{{ statistics.completedTrainings }} 娆�</span>
           </div>
           <div class="stat-item">
             <span class="label">寰呭弬鍔犲煿璁�</span>
-            <span class="value">{{ userInfo.pendingTrainings }} 娆�</span>
+            <span class="value">{{ statistics.pendingTrainings }} 娆�</span>
           </div>
           <div class="stat-item">
             <span class="label">骞冲潎鑰冩牳鍒嗘暟</span>
-            <span class="value">{{ userInfo.avgScore }} 鍒�</span>
+            <span class="value">{{ statistics.avgScore }} 鍒�</span>
           </div>
         </el-card>
       </el-col>
 
       <el-col :span="18">
-        <el-tabs v-model="activeTab" type="border-card">
-          <el-tab-pane label="鎴戠殑瀛︿範璁板綍" name="records">
-            <PIMTable :column="recordColumns" :tableData="recordList" :page="recordPage" @pagination="changeRecordPage" />
-          </el-tab-pane>
-
-          <el-tab-pane label="鑰冩牳鎶ュ憡" name="reports">
-            <PIMTable :column="reportColumns" :tableData="reportList" :page="reportPage" @pagination="changeReportPage" />
-          </el-tab-pane>
-
-          <el-tab-pane label="鍦ㄧ嚎娴嬭瘎" name="assessment">
-            <div class="assessment-list">
-              <el-card v-for="item in assessmentList" :key="item.id" class="assessment-card">
-                <div class="assessment-header">
-                  <h4>{{ item.title }}</h4>
-                  <el-tag :type="item.status === '寰呮祴璇�' ? 'warning' : 'success'">{{ item.status }}</el-tag>
-                </div>
-                <p class="assessment-desc">{{ item.description }}</p>
-                <div class="assessment-footer">
-                  <span>鎴鏃堕棿锛歿{ item.deadline }}</span>
-                  <el-button type="primary" size="small" :disabled="item.status !== '寰呮祴璇�'" @click="startAssessment(item)">
-                    {{ item.status === '寰呮祴璇�' ? '寮�濮嬫祴璇�' : '宸插畬鎴�' }}
-                  </el-button>
-                </div>
-              </el-card>
-            </div>
-          </el-tab-pane>
-        </el-tabs>
+        <el-card>
+          <template #header>
+            <span>鎴戠殑瀛︿範璁板綍</span>
+          </template>
+          <PIMTable :column="recordColumns" :tableData="recordList" :page="recordPage" @pagination="changeRecordPage" :tableLoading="recordLoading" />
+        </el-card>
       </el-col>
     </el-row>
   </div>
 </template>
 
 <script setup>
-import { ref, reactive } from "vue";
+import { ref, reactive, onMounted, computed } from "vue";
 import PIMTable from "@/components/PIMTable/PIMTable.vue";
+import useUserStore from "@/store/modules/user";
+import {
+  getRecordList
+} from "@/api/safetyManagement/trainingManage.js";
+import { ElMessage } from "element-plus";
 
 defineOptions({
   name: "鍛樺伐瀛︿範",
 });
 
-const activeTab = ref("records");
+const userStore = useUserStore();
 
-const userInfo = reactive({
-  name: "寮犱笁",
-  dept: "鐢熶骇閮�",
-  post: "鎿嶄綔宸�",
-  avatar: "",
-  completedTrainings: 12,
-  pendingTrainings: 3,
-  avgScore: 85,
+const userInfo = computed(() => ({
+  name: userStore.name || "-",
+  dept: userStore.currentFactoryName || "-",
+  post: userStore.roleName || "-",
+  avatar: userStore.avatar || "",
+}));
+
+const statistics = reactive({
+  completedTrainings: 0,
+  pendingTrainings: 0,
+  avgScore: 0,
 });
 
-// 瀛︿範璁板綍
+// 瀛︿範璁板綍锛堟潵鑷煿璁畬鎴愯褰曪級
 const recordList = ref([]);
 const recordPage = reactive({ current: 1, size: 10, total: 0 });
+const recordLoading = ref(false);
 const recordColumns = [
   { label: "鍩硅鍐呭", prop: "content", align: "center" },
-  { label: "鍩硅鏃堕棿", prop: "trainingTime", align: "center" },
-  { label: "鍩硅鏃堕暱", prop: "duration", align: "center" },
+  { label: "瀹屾垚鏃堕棿", prop: "completeTime", align: "center" },
+  { label: "瀛︿範鏃堕暱", prop: "duration", align: "center" },
   { label: "鍩硅鏂瑰紡", prop: "method", align: "center" },
-  { label: "瀹屾垚鐘舵��", prop: "status", align: "center" },
+  { label: "鍒嗘暟", prop: "score", align: "center" },
+  {
+    label: "鑰冩牳缁撴灉",
+    prop: "result",
+    align: "center",
+    dataType: "tag",
+    formatType: (val) => {
+      if (val === '閫氳繃') return 'success';
+      if (val === '鏈�氳繃') return 'danger';
+      return 'info';
+    },
+    formatData: (val) => val || '-'
+  }
 ];
 
-// 鑰冩牳鎶ュ憡
-const reportList = ref([]);
-const reportPage = reactive({ current: 1, size: 10, total: 0 });
-const reportColumns = [
-  { label: "鑰冩牳鍚嶇О", prop: "name", align: "center" },
-  { label: "鑰冩牳鏃堕棿", prop: "assessTime", align: "center" },
-  { label: "寰楀垎", prop: "score", align: "center" },
-  { label: "绛夌骇", prop: "grade", align: "center" },
-  { label: "鏄惁鍚堟牸", prop: "qualified", align: "center" },
-];
 
-// 鍦ㄧ嚎娴嬭瘎
-const assessmentList = ref([
-  { id: 1, title: "瀹夊叏鎿嶄綔瑙勭▼娴嬭瘎", description: "鑰冩牳鍛樺伐瀵瑰畨鍏ㄦ搷浣滆绋嬬殑鎺屾彙绋嬪害", deadline: "2026-05-30", status: "寰呮祴璇�" },
-  { id: 2, title: "搴旀�ュ缃兘鍔涙祴璇�", description: "鑰冩牳鍛樺伐搴旀�ュ缃兘鍔�", deadline: "2026-06-15", status: "宸插畬鎴�" },
-]);
 
-const changeRecordPage = ({ page, limit }) => { recordPage.current = page; recordPage.size = limit; };
-const changeReportPage = ({ page, limit }) => { reportPage.current = page; reportPage.size = limit; };
-const startAssessment = (item) => {};
+// 璁$畻缁熻鏁版嵁
+const calculateStatistics = (records) => {
+  const completedTrainings = records.filter(r => r.status === 1).length;
+  const pendingTrainings = records.filter(r => r.status === 0).length;
+  const scores = records.filter(r => r.score > 0).map(r => r.score);
+  const avgScore = scores.length > 0 ? (scores.reduce((a, b) => a + b, 0) / scores.length).toFixed(1) : 0;
+
+  statistics.completedTrainings = completedTrainings;
+  statistics.pendingTrainings = pendingTrainings;
+  statistics.avgScore = avgScore;
+};
+
+// 瀛︿範璁板綍鏂规硶锛堟煡璇㈠綋鍓嶇敤鎴风殑瀹屾垚璁板綍锛�
+const getRecordData = async () => {
+  recordLoading.value = true;
+  try {
+    const res = await getRecordList({
+      pageNum: recordPage.current,
+      pageSize: recordPage.size,
+      employeeId: userStore.userId // 鍙煡璇㈠綋鍓嶇敤鎴风殑鏁版嵁
+    });
+    if (res.code === 200) {
+      recordList.value = res.data.rows || res.data.records || [];
+      recordPage.total = res.data.total || 0;
+      // 鏍规嵁鏌ヨ鍒扮殑鏁版嵁璁$畻缁熻
+      calculateStatistics(recordList.value);
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇瀛︿範璁板綍澶辫触');
+  } finally {
+    recordLoading.value = false;
+  }
+};
+
+const changeRecordPage = ({ page, limit }) => {
+  recordPage.current = page;
+  recordPage.size = limit;
+  getRecordData();
+};
+
+onMounted(() => {
+  getRecordData();
+});
 </script>
 
 <style lang="scss" scoped>
@@ -136,30 +161,4 @@
   }
 }
 
-.assessment-list {
-  .assessment-card {
-    margin-bottom: 15px;
-    .assessment-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      h4 {
-        margin: 0;
-      }
-    }
-    .assessment-desc {
-      color: #666;
-      margin: 10px 0;
-    }
-    .assessment-footer {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      span {
-        color: #999;
-        font-size: 12px;
-      }
-    }
-  }
-}
 </style>
diff --git a/src/views/safetyManagement/inspectionReport/index.vue b/src/views/safetyManagement/inspectionReport/index.vue
index 91cc4e4..6ef624c 100644
--- a/src/views/safetyManagement/inspectionReport/index.vue
+++ b/src/views/safetyManagement/inspectionReport/index.vue
@@ -79,112 +79,305 @@
         </el-form-item>
         <el-form-item label="鐘舵��">
           <el-select v-model="filters.status" placeholder="璇烽�夋嫨" clearable style="width: 150px">
-            <el-option label="姝e父" value="normal" />
-            <el-option label="寮傚父" value="abnormal" />
-            <el-option label="婕忔" value="missed" />
-            <el-option label="鏈墽琛�" value="unexecuted" />
+            <el-option label="姝e父" :value="0" />
+            <el-option label="寮傚父" :value="1" />
+            <el-option label="婕忔" :value="2" />
+            <el-option label="鏈墽琛�" :value="3" />
           </el-select>
         </el-form-item>
         <el-form-item>
           <el-button type="primary" @click="getData">鎼滅储</el-button>
           <el-button @click="resetFilters">閲嶇疆</el-button>
+          <el-button type="success" @click="handleSyncData" icon="Refresh">鍚屾鏁版嵁</el-button>
         </el-form-item>
       </el-form>
-      <PIMTable :column="columns" :tableData="dataList" :page="pagination" @pagination="changePage" />
+      <PIMTable :column="columns" :tableData="dataList" :page="pagination" @pagination="changePage" :tableLoading="tableLoading" />
     </el-card>
   </div>
 </template>
 
 <script setup>
-import { ref, reactive, onMounted } from "vue";
+import { ref, reactive, onMounted, nextTick } from "vue";
 import PIMTable from "@/components/PIMTable/PIMTable.vue";
 import * as echarts from "echarts";
+import {
+  getInspectionRecordList,
+  getTodayStatistics,
+  getTrendStatistics,
+  getTypeStatistics,
+  syncInspectionData
+} from "@/api/safetyManagement/inspectionReport.js";
+import { ElMessage } from "element-plus";
+import dayjs from "dayjs";
 
 defineOptions({
   name: "宸℃鏁版嵁鎶ヨ〃",
 });
 
 const todayStats = reactive({
-  completionRate: 95,
-  inspectorCount: 8,
-  abnormalCount: 2,
-  missedCount: 1,
+  completionRate: 0,
+  inspectorCount: 0,
+  abnormalCount: 0,
+  missedCount: 0,
 });
 
 const filters = reactive({
   dateRange: [],
   inspector: "",
-  status: "",
+  status: null,
 });
 
 const dataList = ref([]);
 const pagination = reactive({ current: 1, size: 10, total: 0 });
+const tableLoading = ref(false);
 
 const columns = [
   { label: "宸℃鏃堕棿", prop: "inspectionTime", align: "center" },
   { label: "宸℃浜哄憳", prop: "inspector", align: "center" },
   { label: "宸℃鍖哄煙", prop: "area", align: "center" },
-  { label: "宸℃绫诲瀷", prop: "type", align: "center" },
-  { label: "鐘舵��", prop: "status", align: "center" },
+  {
+    label: "宸℃绫诲瀷",
+    prop: "type",
+    align: "center",
+    formatData: (val) => {
+      const typeMap = {
+        'daily': '鏃ュ父宸℃',
+        'equipment': '璁惧宸℃',
+        'fire': '娑堥槻宸℃',
+        'night': '澶滈棿宸℃',
+        'safety': '瀹夊叏宸℃',
+        'environment': '鐜宸℃'
+      };
+      return typeMap[val] || val;
+    }
+  },
+  {
+    label: "鐘舵��",
+    prop: "status",
+    align: "center",
+    dataType: "tag",
+    formatType: (val) => {
+      if (val === 0) return 'success';
+      if (val === 1) return 'danger';
+      if (val === 2) return 'warning';
+      return 'info';
+    },
+    formatData: (val) => {
+      const statusMap = { 0: '姝e父', 1: '寮傚父', 2: '婕忔', 3: '鏈墽琛�' };
+      return statusMap[val] || val;
+    }
+  },
   { label: "寮傚父鎯呭喌", prop: "abnormalDesc", align: "center" },
 ];
 
 const trendChartRef = ref(null);
 const typeChartRef = ref(null);
+let trendChart = null;
+let typeChart = null;
 
 onMounted(() => {
-  initTrendChart();
-  initTypeChart();
+  getTodayStats();
+  getData();
+  nextTick(() => {
+    initTrendChart();
+    initTypeChart();
+  });
 });
 
-const initTrendChart = () => {
-  const chart = echarts.init(trendChartRef.value);
-  const option = {
-    xAxis: {
-      type: "category",
-      data: ["鍛ㄤ竴", "鍛ㄤ簩", "鍛ㄤ笁", "鍛ㄥ洓", "鍛ㄤ簲", "鍛ㄥ叚", "鍛ㄦ棩"],
-    },
-    yAxis: { type: "value" },
-    series: [
-      { name: "宸插畬鎴�", type: "line", data: [120, 132, 101, 134, 90, 230, 210], smooth: true },
-      { name: "鏈畬鎴�", type: "line", data: [20, 12, 21, 14, 30, 20, 10], smooth: true },
-    ],
-    legend: { data: ["宸插畬鎴�", "鏈畬鎴�"], bottom: 0 },
-    grid: { left: "3%", right: "4%", bottom: "10%", containLabel: true },
-  };
-  chart.setOption(option);
+// 鑾峰彇浠婃棩缁熻
+const getTodayStats = async () => {
+  try {
+    const res = await getTodayStatistics();
+    if (res.code === 200) {
+      Object.assign(todayStats, res.data);
+    }
+  } catch (error) {
+    console.error('鑾峰彇浠婃棩缁熻澶辫触', error);
+  }
 };
 
-const initTypeChart = () => {
-  const chart = echarts.init(typeChartRef.value);
-  const option = {
-    series: [
-      {
-        type: "pie",
-        radius: ["40%", "70%"],
-        data: [
-          { value: 335, name: "璁惧宸℃" },
-          { value: 310, name: "瀹夊叏宸℃" },
-          { value: 234, name: "鐜宸℃" },
-          { value: 135, name: "娑堥槻宸℃" },
-        ],
+// 鍒濆鍖栬秼鍔垮浘琛�
+const initTrendChart = async () => {
+  if (!trendChartRef.value) return;
+
+  trendChart = echarts.init(trendChartRef.value);
+
+  try {
+    const endDate = dayjs().format('YYYY-MM-DD');
+    const startDate = dayjs().subtract(6, 'day').format('YYYY-MM-DD');
+    const res = await getTrendStatistics({ startDate, endDate });
+
+    let dates = [];
+    let completedData = [];
+    let abnormalData = [];
+    let missedData = [];
+    let unexecutedData = [];
+
+    if (res.code === 200 && Array.isArray(res.data)) {
+      // 鎸夋棩鏈熸帓搴�
+      const sortedData = res.data.sort((a, b) => a.statDate.localeCompare(b.statDate));
+      dates = sortedData.map(item => dayjs(item.statDate).format('MM-DD'));
+      completedData = sortedData.map(item => item.completedCount || 0);
+      abnormalData = sortedData.map(item => item.abnormalCount || 0);
+      missedData = sortedData.map(item => item.missedCount || 0);
+      unexecutedData = sortedData.map(item => item.unexecutedCount || 0);
+    } else {
+      // 浣跨敤榛樿鏁版嵁
+      for (let i = 6; i >= 0; i--) {
+        dates.push(dayjs().subtract(i, 'day').format('MM-DD'));
+      }
+      completedData = [2, 1, 2, 2, 1, 4, 3];
+      abnormalData = [1, 0, 0, 1, 0, 1, 0];
+      missedData = [0, 0, 1, 0, 0, 1, 0];
+      unexecutedData = [0, 1, 0, 0, 1, 0, 1];
+    }
+
+    const option = {
+      tooltip: { trigger: 'axis' },
+      xAxis: {
+        type: "category",
+        data: dates,
       },
-    ],
-    legend: { orient: "vertical", left: "left" },
-  };
-  chart.setOption(option);
+      yAxis: { type: "value" },
+      series: [
+        { name: "宸插畬鎴�", type: "line", data: completedData, smooth: true },
+        { name: "寮傚父", type: "line", data: abnormalData, smooth: true },
+        { name: "婕忔", type: "line", data: missedData, smooth: true },
+        { name: "鏈墽琛�", type: "line", data: unexecutedData, smooth: true },
+      ],
+      legend: { data: ["宸插畬鎴�", "寮傚父", "婕忔", "鏈墽琛�"], bottom: 0 },
+      grid: { left: "3%", right: "4%", bottom: "15%", containLabel: true },
+    };
+    trendChart.setOption(option);
+  } catch (error) {
+    console.error('鑾峰彇瓒嬪娍鏁版嵁澶辫触', error);
+  }
 };
 
-const getData = () => {};
+// 绫诲瀷鏄犲皠
+const typeMap = {
+  'daily': '鏃ュ父宸℃',
+  'equipment': '璁惧宸℃',
+  'fire': '娑堥槻宸℃',
+  'night': '澶滈棿宸℃',
+  'safety': '瀹夊叏宸℃',
+  'environment': '鐜宸℃'
+};
+
+// 鍒濆鍖栫被鍨嬪浘琛�
+const initTypeChart = async () => {
+  if (!typeChartRef.value) return;
+
+  typeChart = echarts.init(typeChartRef.value);
+
+  try {
+    const endDate = dayjs().format('YYYY-MM-DD');
+    const startDate = dayjs().subtract(30, 'day').format('YYYY-MM-DD');
+    const res = await getTypeStatistics({ startDate, endDate });
+
+    let pieData = [];
+
+    if (res.code === 200 && Array.isArray(res.data)) {
+      pieData = res.data.map(item => ({
+        value: item.count || 0,
+        name: typeMap[item.type] || item.type
+      }));
+    } else {
+      // 浣跨敤榛樿鏁版嵁
+      pieData = [
+        { value: 8, name: "鏃ュ父宸℃" },
+        { value: 4, name: "璁惧宸℃" },
+        { value: 3, name: "娑堥槻宸℃" },
+        { value: 3, name: "澶滈棿宸℃" },
+      ];
+    }
+
+    const option = {
+      tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
+      series: [
+        {
+          type: "pie",
+          radius: ["40%", "70%"],
+          data: pieData,
+          label: {
+            formatter: '{b}\n{c}娆� ({d}%)'
+          }
+        },
+      ],
+      legend: { orient: "vertical", left: "left" },
+    };
+    typeChart.setOption(option);
+  } catch (error) {
+    console.error('鑾峰彇绫诲瀷缁熻澶辫触', error);
+  }
+};
+
+// 鑾峰彇宸℃璁板綍鍒楄〃
+const getData = async () => {
+  tableLoading.value = true;
+  try {
+    const params = {
+      pageNum: pagination.current,
+      pageSize: pagination.size,
+      inspector: filters.inspector,
+      status: filters.status,
+    };
+    
+    if (filters.dateRange && filters.dateRange.length === 2) {
+      params.startDate = filters.dateRange[0];
+      params.endDate = filters.dateRange[1];
+    }
+    
+    const res = await getInspectionRecordList(params);
+    if (res.code === 200) {
+      dataList.value = res.data.rows || res.data.records || [];
+      pagination.total = res.data.total || 0;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇宸℃璁板綍澶辫触');
+  } finally {
+    tableLoading.value = false;
+  }
+};
+
 const resetFilters = () => {
   filters.dateRange = [];
   filters.inspector = "";
-  filters.status = "";
+  filters.status = null;
+  pagination.current = 1;
+  getData();
 };
+
 const changePage = ({ page, limit }) => {
   pagination.current = page;
   pagination.size = limit;
+  getData();
 };
+
+// 鍚屾宸℃鏁版嵁
+const handleSyncData = async () => {
+  try {
+    const res = await syncInspectionData();
+    if (res.code === 200) {
+      ElMessage.success(`鍚屾鎴愬姛锛屾柊澧� ${res.data?.count || 0} 鏉¤褰昤);
+      // 鍒锋柊鍒楄〃鍜岀粺璁�
+      getData();
+      getTodayStats();
+      initTrendChart();
+      initTypeChart();
+    } else {
+      ElMessage.warning(res.msg || '鍚屾澶辫触');
+    }
+  } catch (error) {
+    ElMessage.error(error.response?.data?.msg || '鍚屾澶辫触');
+  }
+};
+
+// 绐楀彛澶у皬鏀瑰彉鏃堕噸鏂版覆鏌撳浘琛�
+window.addEventListener('resize', () => {
+  trendChart && trendChart.resize();
+  typeChart && typeChart.resize();
+});
 </script>
 
 <style lang="scss" scoped>
diff --git a/src/views/safetyManagement/trainingManage/index.vue b/src/views/safetyManagement/trainingManage/index.vue
index 3c37b2f..873f862 100644
--- a/src/views/safetyManagement/trainingManage/index.vue
+++ b/src/views/safetyManagement/trainingManage/index.vue
@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <el-tabs v-model="activeTab" type="border-card">
+    <el-tabs v-model="activeTab" type="border-card" @tab-change="handleTabChange">
       <el-tab-pane label="鍩硅璧勬枡" name="materials">
         <el-form :model="materialFilters" :inline="true">
           <el-form-item label="璧勬枡鍚嶇О">
@@ -8,10 +8,17 @@
           </el-form-item>
           <el-form-item label="璧勬枡绫诲瀷">
             <el-select v-model="materialFilters.type" placeholder="璇烽�夋嫨" clearable style="width: 150px">
-              <el-option label="鍒跺害" value="system" />
-              <el-option label="璇句欢" value="courseware" />
-              <el-option label="瑙嗛" value="video" />
-              <el-option label="妗堜緥" value="case" />
+              <el-option label="PDF" value="PDF" />
+              <el-option label="鍒跺害" value="鍒跺害" />
+              <el-option label="璇句欢" value="璇句欢" />
+              <el-option label="瑙嗛" value="瑙嗛" />
+              <el-option label="妗堜緥" value="妗堜緥" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="鐘舵��">
+            <el-select v-model="materialFilters.status" placeholder="璇烽�夋嫨" clearable style="width: 150px">
+              <el-option label="鍚敤" :value="1" />
+              <el-option label="鍋滅敤" :value="0" />
             </el-select>
           </el-form-item>
           <el-form-item>
@@ -21,9 +28,9 @@
         </el-form>
         <div class="table_list">
           <div class="actions">
-            <el-button type="primary" @click="uploadMaterial" icon="Upload">涓婁紶璧勬枡</el-button>
+            <el-button type="primary" @click="openMaterialDialog" icon="Upload">涓婁紶璧勬枡</el-button>
           </div>
-          <PIMTable :column="materialColumns" :tableData="materialList" :page="materialPage" @pagination="changeMaterialPage" />
+          <PIMTable :column="materialColumns" :tableData="materialList" :page="materialPage" @pagination="changeMaterialPage" :tableLoading="materialLoading" />
         </div>
       </el-tab-pane>
 
@@ -35,6 +42,20 @@
           <el-form-item label="宀椾綅">
             <el-input v-model="planFilters.post" placeholder="璇疯緭鍏ュ矖浣�" clearable style="width: 200px" />
           </el-form-item>
+          <el-form-item label="鍩硅绛夌骇">
+            <el-select v-model="planFilters.level" placeholder="璇烽�夋嫨" clearable style="width: 150px">
+              <el-option label="涓�绾�" value="涓�绾�" />
+              <el-option label="浜岀骇" value="浜岀骇" />
+              <el-option label="涓夌骇" value="涓夌骇" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="鐘舵��">
+            <el-select v-model="planFilters.status" placeholder="璇烽�夋嫨" clearable style="width: 150px">
+              <el-option label="寰呮墽琛�" :value="0" />
+              <el-option label="鎵ц涓�" :value="1" />
+              <el-option label="宸插畬鎴�" :value="2" />
+            </el-select>
+          </el-form-item>
           <el-form-item>
             <el-button type="primary" @click="getPlanData">鎼滅储</el-button>
             <el-button @click="resetPlanFilters">閲嶇疆</el-button>
@@ -42,9 +63,9 @@
         </el-form>
         <div class="table_list">
           <div class="actions">
-            <el-button type="primary" @click="addPlan" icon="Plus">鍒跺畾璁″垝</el-button>
+            <el-button type="primary" @click="openPlanDialog" icon="Plus">鍒跺畾璁″垝</el-button>
           </div>
-          <PIMTable :column="planColumns" :tableData="planList" :page="planPage" @pagination="changePlanPage" />
+          <PIMTable :column="planColumns" :tableData="planList" :page="planPage" @pagination="changePlanPage" :tableLoading="planLoading" />
         </div>
       </el-tab-pane>
 
@@ -53,22 +74,232 @@
           <el-form-item label="鍛樺伐濮撳悕">
             <el-input v-model="recordFilters.employeeName" placeholder="璇疯緭鍏ュ憳宸ュ鍚�" clearable style="width: 200px" />
           </el-form-item>
+          <el-form-item label="鐘舵��">
+            <el-select v-model="recordFilters.status" placeholder="璇烽�夋嫨" clearable style="width: 150px">
+              <el-option label="宸插畬鎴�" :value="1" />
+              <el-option label="鏈畬鎴�" :value="0" />
+            </el-select>
+          </el-form-item>
           <el-form-item>
             <el-button type="primary" @click="getRecordData">鎼滅储</el-button>
             <el-button @click="resetRecordFilters">閲嶇疆</el-button>
+            <el-button type="primary" @click="openRecordDialog" icon="Plus">鏂板</el-button>
+            <el-button type="success" @click="exportRecords" icon="Download">瀵煎嚭</el-button>
           </el-form-item>
         </el-form>
         <div class="table_list">
-          <PIMTable :column="recordColumns" :tableData="recordList" :page="recordPage" @pagination="changeRecordPage" />
+          <PIMTable :column="recordColumns" :tableData="recordList" :page="recordPage" @pagination="changeRecordPage" :tableLoading="recordLoading" />
         </div>
       </el-tab-pane>
     </el-tabs>
+
+    <!-- 鍩硅璧勬枡寮圭獥 -->
+    <el-dialog :title="materialDialog.title" v-model="materialDialog.visible" width="600px" append-to-body>
+      <el-form ref="materialFormRef" :model="materialForm" :rules="materialRules" label-width="100px">
+        <el-form-item label="璧勬枡鍚嶇О" prop="name">
+          <el-input v-model="materialForm.name" placeholder="璇疯緭鍏ヨ祫鏂欏悕绉�" />
+        </el-form-item>
+        <el-form-item label="璧勬枡绫诲瀷" prop="type">
+          <el-select v-model="materialForm.type" placeholder="璇烽�夋嫨璧勬枡绫诲瀷" style="width: 100%">
+            <el-option label="PDF" value="PDF" />
+            <el-option label="鍒跺害" value="鍒跺害" />
+            <el-option label="璇句欢" value="璇句欢" />
+            <el-option label="瑙嗛" value="瑙嗛" />
+            <el-option label="妗堜緥" value="妗堜緥" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鏂囦欢涓婁紶" prop="fileUrl" v-if="!materialForm.id">
+          <el-upload
+            ref="uploadRef"
+            action="/dev-api/common/upload"
+            :headers="uploadHeaders"
+            :on-success="handleUploadSuccess"
+            :on-error="handleUploadError"
+            :before-upload="beforeUpload"
+            :limit="1"
+          >
+            <el-button type="primary">閫夋嫨鏂囦欢</el-button>
+            <template #tip>
+              <div class="el-upload__tip">鏀寔 PDF銆乄ord銆佽棰戠瓑鏍煎紡</div>
+            </template>
+          </el-upload>
+        </el-form-item>
+        <el-form-item label="鏂囦欢澶у皬" prop="fileSize" v-if="materialForm.fileSize">
+          <el-input v-model="materialForm.fileSize" disabled />
+        </el-form-item>
+        <el-form-item label="鐘舵��" prop="status">
+          <el-radio-group v-model="materialForm.status">
+            <el-radio :value="1">鍚敤</el-radio>
+            <el-radio :value="0">鍋滅敤</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="materialForm.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="materialDialog.visible = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="submitMaterialForm" :loading="materialDialog.loading">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 鍩硅璁″垝寮圭獥 -->
+    <el-dialog :title="planDialog.title" v-model="planDialog.visible" width="700px" append-to-body>
+      <el-form ref="planFormRef" :model="planForm" :rules="planRules" label-width="100px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="璁″垝骞村害" prop="year">
+              <el-date-picker v-model="planForm.year" type="year" placeholder="閫夋嫨骞村害" value-format="YYYY" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="閫傜敤宀椾綅" prop="post">
+              <el-input v-model="planForm.post" placeholder="璇疯緭鍏ラ�傜敤宀椾綅" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鍩硅绛夌骇" prop="level">
+              <el-select v-model="planForm.level" placeholder="璇烽�夋嫨鍩硅绛夌骇" style="width: 100%">
+                <el-option label="涓�绾�" value="涓�绾�" />
+                <el-option label="浜岀骇" value="浜岀骇" />
+                <el-option label="涓夌骇" value="涓夌骇" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="璁″垝璇炬椂" prop="hours">
+              <el-input-number v-model="planForm.hours" :min="1" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="鍩硅鍐呭" prop="content">
+          <el-input v-model="planForm.content" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ煿璁唴瀹�" />
+        </el-form-item>
+        <el-form-item label="鍩硅璧勬枡" prop="materialIds">
+          <el-select v-model="planForm.materialIds" multiple placeholder="璇烽�夋嫨鍩硅璧勬枡" style="width: 100%">
+            <el-option v-for="item in materialOptions" :key="item.id" :label="item.name" :value="item.id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鐘舵��" prop="status">
+          <el-radio-group v-model="planForm.status">
+            <el-radio :value="0">寰呮墽琛�</el-radio>
+            <el-radio :value="1">鎵ц涓�</el-radio>
+            <el-radio :value="2">宸插畬鎴�</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="planForm.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="planDialog.visible = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="submitPlanForm" :loading="planDialog.loading">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 瀹屾垚璁板綍寮圭獥 -->
+    <el-dialog :title="recordDialog.title" v-model="recordDialog.visible" width="700px" append-to-body>
+      <el-form ref="recordFormRef" :model="recordForm" :rules="recordRules" label-width="100px">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鍩硅璁″垝" prop="planId">
+              <el-select v-model="recordForm.planId" placeholder="璇烽�夋嫨鍩硅璁″垝" style="width: 100%" @change="handlePlanChange">
+                <el-option v-for="item in planOptions" :key="item.id" :label="item.content" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鍛樺伐濮撳悕" prop="employeeId">
+              <el-select v-model="recordForm.employeeId" placeholder="璇烽�夋嫨鍛樺伐" style="width: 100%" @change="handleEmployeeChange">
+                <el-option v-for="item in employeeOptions" :key="item.userId" :label="item.userName" :value="item.userId" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="瀹屾垚鏃堕棿" prop="completeTime">
+              <el-date-picker v-model="recordForm.completeTime" type="datetime" placeholder="閫夋嫨瀹屾垚鏃堕棿" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="瀛︿範鏃堕暱" prop="duration">
+              <el-input-number v-model="recordForm.duration" :min="0" :precision="1" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鍩硅鏂瑰紡" prop="method">
+              <el-select v-model="recordForm.method" placeholder="璇烽�夋嫨鍩硅鏂瑰紡" style="width: 100%">
+                <el-option label="绾夸笅鎺堣" value="绾夸笅鎺堣" />
+                <el-option label="绾夸笂瀛︿範" value="绾夸笂瀛︿範" />
+                <el-option label="瀹炴搷婕旂粌" value="瀹炴搷婕旂粌" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鑰冩牳鍒嗘暟" prop="score">
+              <el-input-number v-model="recordForm.score" :min="0" :max="100" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="鑰冩牳缁撴灉" prop="result">
+              <el-select v-model="recordForm.result" placeholder="璇烽�夋嫨鑰冩牳缁撴灉" style="width: 100%">
+                <el-option label="閫氳繃" value="閫氳繃" />
+                <el-option label="鏈�氳繃" value="鏈�氳繃" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鐘舵��" prop="status">
+              <el-radio-group v-model="recordForm.status">
+                <el-radio :value="1">宸插畬鎴�</el-radio>
+                <el-radio :value="0">鏈畬鎴�</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="recordForm.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="recordDialog.visible = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="submitRecordForm" :loading="recordDialog.loading">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { ref, reactive } from "vue";
+import { ref, reactive, onMounted } from "vue";
 import PIMTable from "@/components/PIMTable/PIMTable.vue";
+import {
+  getMaterialList,
+  uploadMaterial as uploadMaterialApi,
+  updateMaterial,
+  deleteMaterial,
+  getMaterialDetail,
+  getPlanList,
+  addPlan as addPlanApi,
+  updatePlan,
+  deletePlan,
+  getPlanDetail,
+  getRecordList,
+  addRecord as addRecordApi,
+  updateRecord,
+  deleteRecord,
+  getRecordDetail,
+  exportRecord
+} from "@/api/safetyManagement/trainingManage.js";
+import { listUser } from "@/api/system/user";
+import { getToken } from "@/utils/auth";
+import { ElMessage, ElMessageBox } from "element-plus";
 
 defineOptions({
   name: "鍩硅绠$悊",
@@ -76,54 +307,653 @@
 
 const activeTab = ref("materials");
 
-// 鍩硅璧勬枡
-const materialFilters = reactive({ name: "", type: "" });
+// ==================== 鍩硅璧勬枡 ====================
+const materialFilters = reactive({ name: "", type: "", status: null });
 const materialList = ref([]);
 const materialPage = reactive({ current: 1, size: 10, total: 0 });
+const materialLoading = ref(false);
 const materialColumns = [
   { label: "璧勬枡鍚嶇О", prop: "name", align: "center" },
-  { label: "绫诲瀷", prop: "type", align: "center" },
+  {
+    label: "绫诲瀷",
+    prop: "type",
+    align: "center",
+    dataType: "tag",
+    formatType: () => "primary",
+    formatData: (val) => val || '-'
+  },
   { label: "涓婁紶浜�", prop: "uploader", align: "center" },
   { label: "涓婁紶鏃堕棿", prop: "uploadTime", align: "center" },
   { label: "鏂囦欢澶у皬", prop: "fileSize", align: "center" },
+  {
+    label: "鐘舵��",
+    prop: "status",
+    align: "center",
+    dataType: "tag",
+    formatType: (val) => (val === 1 ? "success" : "info"),
+    formatData: (val) => (val === 1 ? "鍚敤" : "鍋滅敤")
+  },
+  {
+    label: "鎿嶄綔",
+    prop: "action",
+    align: "center",
+    dataType: "action",
+    operation: [
+      { name: "缂栬緫", type: "text", clickFun: (row) => handleEditMaterial(row) },
+      { name: "涓嬭浇", type: "text", clickFun: (row) => handleDownload(row) },
+      { name: "鍒犻櫎", type: "text", clickFun: (row) => handleDeleteMaterial(row) }
+    ]
+  }
 ];
 
-// 鍩硅璁″垝
-const planFilters = reactive({ year: "", post: "" });
+const materialDialog = reactive({ visible: false, title: "", loading: false });
+const materialFormRef = ref(null);
+const uploadRef = ref(null);
+const materialForm = reactive({
+  id: null,
+  name: "",
+  type: "",
+  fileUrl: "",
+  fileSize: "",
+  status: 1,
+  remark: ""
+});
+const materialRules = {
+  name: [{ required: true, message: "璇疯緭鍏ヨ祫鏂欏悕绉�", trigger: "blur" }],
+  type: [{ required: true, message: "璇烽�夋嫨璧勬枡绫诲瀷", trigger: "change" }]
+};
+
+const uploadHeaders = reactive({
+  Authorization: "Bearer " + getToken()
+});
+
+// ==================== 鍩硅璁″垝 ====================
+const planFilters = reactive({ year: "", post: "", level: "", status: null });
 const planList = ref([]);
 const planPage = reactive({ current: 1, size: 10, total: 0 });
+const planLoading = ref(false);
 const planColumns = [
   { label: "璁″垝骞村害", prop: "year", align: "center" },
   { label: "宀椾綅", prop: "post", align: "center" },
-  { label: "灞傜骇", prop: "level", align: "center" },
+  { label: "鍩硅绛夌骇", prop: "level", align: "center" },
   { label: "鍩硅鍐呭", prop: "content", align: "center" },
   { label: "璁″垝璇炬椂", prop: "hours", align: "center" },
+  {
+    label: "鐘舵��",
+    prop: "status",
+    align: "center",
+    dataType: "tag",
+    formatType: (val) => {
+      if (val === 0) return 'info';
+      if (val === 1) return 'warning';
+      return 'success';
+    },
+    formatData: (val) => {
+      const statusMap = { 0: '寰呮墽琛�', 1: '鎵ц涓�', 2: '宸插畬鎴�' };
+      return statusMap[val] || val;
+    }
+  },
+  {
+    label: "鎿嶄綔",
+    prop: "action",
+    align: "center",
+    dataType: "action",
+    operation: [
+      { name: "缂栬緫", type: "text", clickFun: (row) => handleEditPlan(row) },
+      { name: "鍒犻櫎", type: "text", clickFun: (row) => handleDeletePlan(row) }
+    ]
+  }
 ];
 
-// 瀹屾垚璁板綍
-const recordFilters = reactive({ employeeName: "" });
+const planDialog = reactive({ visible: false, title: "", loading: false });
+const planFormRef = ref(null);
+const planForm = reactive({
+  id: null,
+  year: "",
+  post: "",
+  level: "",
+  content: "",
+  hours: 1,
+  materialIds: [],
+  status: 0,
+  remark: ""
+});
+const planRules = {
+  year: [{ required: true, message: "璇烽�夋嫨璁″垝骞村害", trigger: "change" }],
+  post: [{ required: true, message: "璇疯緭鍏ラ�傜敤宀椾綅", trigger: "blur" }],
+  level: [{ required: true, message: "璇烽�夋嫨鍩硅绛夌骇", trigger: "change" }],
+  content: [{ required: true, message: "璇疯緭鍏ュ煿璁唴瀹�", trigger: "blur" }],
+  hours: [{ required: true, message: "璇疯緭鍏ヨ鍒掕鏃�", trigger: "blur" }]
+};
+const materialOptions = ref([]);
+
+// ==================== 瀹屾垚璁板綍 ====================
+const recordFilters = reactive({ employeeName: "", status: null });
 const recordList = ref([]);
 const recordPage = reactive({ current: 1, size: 10, total: 0 });
+const recordLoading = ref(false);
 const recordColumns = [
   { label: "鍛樺伐濮撳悕", prop: "employeeName", align: "center" },
   { label: "鍩硅鍐呭", prop: "content", align: "center" },
   { label: "瀹屾垚鏃堕棿", prop: "completeTime", align: "center" },
-  { label: "鑰冩牳缁撴灉", prop: "result", align: "center" },
+  { label: "瀛︿範鏃堕暱", prop: "duration", align: "center" },
+  { label: "鍩硅鏂瑰紡", prop: "method", align: "center" },
+  { label: "鍒嗘暟", prop: "score", align: "center" },
+  {
+    label: "鑰冩牳缁撴灉",
+    prop: "result",
+    align: "center",
+    dataType: "tag",
+    formatType: (val) => {
+      if (val === '浼樼' || val === '閫氳繃') return 'success';
+      if (val === '鑹ソ') return 'primary';
+      if (val === '鍚堟牸') return 'warning';
+      return 'danger';
+    },
+    formatData: (val) => val || '-'
+  },
+  {
+    label: "鎿嶄綔",
+    prop: "action",
+    align: "center",
+    dataType: "action",
+    operation: [
+      { name: "缂栬緫", type: "text", clickFun: (row) => handleEditRecord(row) },
+      { name: "鍒犻櫎", type: "text", clickFun: (row) => handleDeleteRecord(row) }
+    ]
+  }
 ];
 
-const getMaterialData = () => {};
-const resetMaterialFilters = () => { materialFilters.name = ""; materialFilters.type = ""; };
-const uploadMaterial = () => {};
-const changeMaterialPage = ({ page, limit }) => { materialPage.current = page; materialPage.size = limit; };
+const recordDialog = reactive({ visible: false, title: "", loading: false });
+const recordFormRef = ref(null);
+const recordForm = reactive({
+  id: null,
+  planId: null,
+  employeeId: null,
+  employeeName: "",
+  content: "",
+  completeTime: "",
+  duration: 0,
+  method: "",
+  score: 0,
+  result: "",
+  status: 1,
+  remark: ""
+});
+const recordRules = {
+  planId: [{ required: true, message: "璇烽�夋嫨鍩硅璁″垝", trigger: "change" }],
+  employeeId: [{ required: true, message: "璇烽�夋嫨鍛樺伐", trigger: "change" }],
+  completeTime: [{ required: true, message: "璇烽�夋嫨瀹屾垚鏃堕棿", trigger: "change" }]
+};
+const planOptions = ref([]);
+const employeeOptions = ref([]);
 
-const getPlanData = () => {};
-const resetPlanFilters = () => { planFilters.year = ""; planFilters.post = ""; };
-const addPlan = () => {};
-const changePlanPage = ({ page, limit }) => { planPage.current = page; planPage.size = limit; };
+// ==================== 閫氱敤鏂规硶 ====================
+const loadData = () => {
+  switch (activeTab.value) {
+    case 'materials':
+      getMaterialData();
+      break;
+    case 'plans':
+      getPlanData();
+      break;
+    case 'records':
+      getRecordData();
+      break;
+  }
+};
 
-const getRecordData = () => {};
-const resetRecordFilters = () => { recordFilters.employeeName = ""; };
-const changeRecordPage = ({ page, limit }) => { recordPage.current = page; recordPage.size = limit; };
+const handleTabChange = () => {
+  loadData();
+};
+
+// ==================== 鍩硅璧勬枡鏂规硶 ====================
+const getMaterialData = async () => {
+  materialLoading.value = true;
+  try {
+    const res = await getMaterialList({
+      pageNum: materialPage.current,
+      pageSize: materialPage.size,
+      ...materialFilters
+    });
+    if (res.code === 200) {
+      materialList.value = res.data.records || res.data.rows || [];
+      materialPage.total = res.data.total || 0;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇鍩硅璧勬枡澶辫触');
+  } finally {
+    materialLoading.value = false;
+  }
+};
+
+const resetMaterialFilters = () => {
+  materialFilters.name = "";
+  materialFilters.type = "";
+  materialFilters.status = null;
+  materialPage.current = 1;
+  getMaterialData();
+};
+
+const changeMaterialPage = ({ page, limit }) => {
+  materialPage.current = page;
+  materialPage.size = limit;
+  getMaterialData();
+};
+
+const resetMaterialForm = () => {
+  materialForm.id = null;
+  materialForm.name = "";
+  materialForm.type = "";
+  materialForm.fileUrl = "";
+  materialForm.fileSize = "";
+  materialForm.status = 1;
+  materialForm.remark = "";
+  if (uploadRef.value) {
+    uploadRef.value.clearFiles();
+  }
+};
+
+const openMaterialDialog = () => {
+  resetMaterialForm();
+  materialDialog.title = "涓婁紶鍩硅璧勬枡";
+  materialDialog.visible = true;
+};
+
+const handleEditMaterial = async (row) => {
+  resetMaterialForm();
+  try {
+    const res = await getMaterialDetail(row.id);
+    if (res.code === 200) {
+      Object.assign(materialForm, res.data);
+      materialDialog.title = "缂栬緫鍩硅璧勬枡";
+      materialDialog.visible = true;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇璧勬枡璇︽儏澶辫触');
+  }
+};
+
+const handleUploadSuccess = (response) => {
+  if (response.code === 200) {
+    materialForm.fileUrl = response.url;
+    materialForm.fileSize = formatFileSize(response.file?.size || 0);
+    ElMessage.success('鏂囦欢涓婁紶鎴愬姛');
+  } else {
+    ElMessage.error(response.msg || '涓婁紶澶辫触');
+  }
+};
+
+const handleUploadError = () => {
+  ElMessage.error('鏂囦欢涓婁紶澶辫触');
+};
+
+const beforeUpload = (file) => {
+  const maxSize = 50 * 1024 * 1024; // 50MB
+  if (file.size > maxSize) {
+    ElMessage.error('鏂囦欢澶у皬涓嶈兘瓒呰繃 50MB');
+    return false;
+  }
+  return true;
+};
+
+const formatFileSize = (size) => {
+  if (size < 1024) return size + ' B';
+  if (size < 1024 * 1024) return (size / 1024).toFixed(2) + ' KB';
+  if (size < 1024 * 1024 * 1024) return (size / (1024 * 1024)).toFixed(2) + ' MB';
+  return (size / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
+};
+
+const submitMaterialForm = async () => {
+  const valid = await materialFormRef.value.validate().catch(() => false);
+  if (!valid) return;
+  
+  if (!materialForm.id && !materialForm.fileUrl) {
+    ElMessage.error('璇蜂笂浼犳枃浠�');
+    return;
+  }
+  
+  materialDialog.loading = true;
+  try {
+    const api = materialForm.id ? updateMaterial : uploadMaterialApi;
+    const res = await api(materialForm);
+    if (res.code === 200) {
+      ElMessage.success(materialForm.id ? '淇敼鎴愬姛' : '涓婁紶鎴愬姛');
+      materialDialog.visible = false;
+      getMaterialData();
+    }
+  } catch (error) {
+    ElMessage.error(materialForm.id ? '淇敼澶辫触' : '涓婁紶澶辫触');
+  } finally {
+    materialDialog.loading = false;
+  }
+};
+
+const handleDownload = (row) => {
+  if (row.fileUrl) {
+    // 澶勭悊URL鏍煎紡闂
+    let url = row.fileUrl;
+    // 淇绫讳技 http://host:portupload/ 鐨勯敊璇牸寮�
+    url = url.replace(/^(http:\/\/[^/]+:\d+)(upload\/)/, '$1/$2');
+    // 濡傛灉涓嶆槸瀹屾暣URL锛屾坊鍔犲墠缂�
+    if (!url.startsWith('http')) {
+      url = '/dev-api/' + url.replace(/^\//, '');
+    }
+    window.open(url, '_blank');
+  } else {
+    ElMessage.warning('鏂囦欢鍦板潃涓嶅瓨鍦�');
+  }
+};
+
+const handleDeleteMaterial = (row) => {
+  ElMessageBox.confirm(`纭鍒犻櫎璧勬枡 "${row.name}" 鍚楋紵`, "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning"
+  }).then(async () => {
+    try {
+      const res = await deleteMaterial(row.id);
+      if (res.code === 200) {
+        ElMessage.success("鍒犻櫎鎴愬姛");
+        getMaterialData();
+      }
+    } catch (error) {
+      ElMessage.error("鍒犻櫎澶辫触");
+    }
+  });
+};
+
+// ==================== 鍩硅璁″垝鏂规硶 ====================
+const getPlanData = async () => {
+  planLoading.value = true;
+  try {
+    const res = await getPlanList({
+      pageNum: planPage.current,
+      pageSize: planPage.size,
+      ...planFilters
+    });
+    if (res.code === 200) {
+      planList.value = res.data.records || res.data.rows || [];
+      planPage.total = res.data.total || 0;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇鍩硅璁″垝澶辫触');
+  } finally {
+    planLoading.value = false;
+  }
+};
+
+const resetPlanFilters = () => {
+  planFilters.year = "";
+  planFilters.post = "";
+  planFilters.level = "";
+  planFilters.status = null;
+  planPage.current = 1;
+  getPlanData();
+};
+
+const changePlanPage = ({ page, limit }) => {
+  planPage.current = page;
+  planPage.size = limit;
+  getPlanData();
+};
+
+const loadMaterialOptions = async () => {
+  try {
+    const res = await getMaterialList({ pageNum: 1, pageSize: 1000 });
+    if (res.code === 200) {
+      materialOptions.value = res.data.records || res.data.rows || [];
+    }
+  } catch (error) {
+    console.error('鍔犺浇鍩硅璧勬枡閫夐」澶辫触', error);
+  }
+};
+
+const resetPlanForm = () => {
+  planForm.id = null;
+  planForm.year = "";
+  planForm.post = "";
+  planForm.level = "";
+  planForm.content = "";
+  planForm.hours = 1;
+  planForm.materialIds = [];
+  planForm.status = 0;
+  planForm.remark = "";
+};
+
+const openPlanDialog = () => {
+  resetPlanForm();
+  loadMaterialOptions();
+  planDialog.title = "鍒跺畾鍩硅璁″垝";
+  planDialog.visible = true;
+};
+
+const handleEditPlan = async (row) => {
+  resetPlanForm();
+  loadMaterialOptions();
+  try {
+    const res = await getPlanDetail(row.id);
+    if (res.code === 200) {
+      Object.assign(planForm, res.data);
+      // 灏� materialIds 瀛楃涓茶浆鎹负鏁扮粍
+      if (planForm.materialIds && typeof planForm.materialIds === 'string') {
+        planForm.materialIds = planForm.materialIds.split(',').map(id => parseInt(id));
+      }
+      planDialog.title = "缂栬緫鍩硅璁″垝";
+      planDialog.visible = true;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇璁″垝璇︽儏澶辫触');
+  }
+};
+
+const submitPlanForm = async () => {
+  const valid = await planFormRef.value.validate().catch(() => false);
+  if (!valid) return;
+  
+  planDialog.loading = true;
+  try {
+    const submitData = { ...planForm };
+    // 灏� materialIds 鏁扮粍杞崲涓洪�楀彿鍒嗛殧鐨勫瓧绗︿覆
+    if (Array.isArray(submitData.materialIds)) {
+      submitData.materialIds = submitData.materialIds.join(',');
+    }
+    const api = planForm.id ? updatePlan : addPlanApi;
+    const res = await api(submitData);
+    if (res.code === 200) {
+      ElMessage.success(planForm.id ? '淇敼鎴愬姛' : '鏂板鎴愬姛');
+      planDialog.visible = false;
+      getPlanData();
+    }
+  } catch (error) {
+    ElMessage.error(planForm.id ? '淇敼澶辫触' : '鏂板澶辫触');
+  } finally {
+    planDialog.loading = false;
+  }
+};
+
+const handleDeletePlan = (row) => {
+  ElMessageBox.confirm(`纭鍒犻櫎璇ュ煿璁鍒掑悧锛焋, "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning"
+  }).then(async () => {
+    try {
+      const res = await deletePlan(row.id);
+      if (res.code === 200) {
+        ElMessage.success("鍒犻櫎鎴愬姛");
+        getPlanData();
+      }
+    } catch (error) {
+      ElMessage.error("鍒犻櫎澶辫触");
+    }
+  });
+};
+
+// ==================== 瀹屾垚璁板綍鏂规硶 ====================
+const getRecordData = async () => {
+  recordLoading.value = true;
+  try {
+    const res = await getRecordList({
+      pageNum: recordPage.current,
+      pageSize: recordPage.size,
+      ...recordFilters
+    });
+    if (res.code === 200) {
+      recordList.value = res.data.records || res.data.rows || [];
+      recordPage.total = res.data.total || 0;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇瀹屾垚璁板綍澶辫触');
+  } finally {
+    recordLoading.value = false;
+  }
+};
+
+const resetRecordFilters = () => {
+  recordFilters.employeeName = "";
+  recordFilters.status = null;
+  recordPage.current = 1;
+  getRecordData();
+};
+
+const changeRecordPage = ({ page, limit }) => {
+  recordPage.current = page;
+  recordPage.size = limit;
+  getRecordData();
+};
+
+const exportRecords = async () => {
+  try {
+    const res = await exportRecord(recordFilters);
+    // 澶勭悊鏂囦欢涓嬭浇
+    const blob = new Blob([res]);
+    const link = document.createElement('a');
+    link.href = URL.createObjectURL(blob);
+    link.download = '鍩硅瀹屾垚璁板綍.xlsx';
+    link.click();
+    ElMessage.success('瀵煎嚭鎴愬姛');
+  } catch (error) {
+    ElMessage.error('瀵煎嚭澶辫触');
+  }
+};
+
+// 瀹屾垚璁板綍琛ㄥ崟鏂规硶
+const resetRecordForm = () => {
+  recordForm.id = null;
+  recordForm.planId = null;
+  recordForm.employeeId = null;
+  recordForm.employeeName = "";
+  recordForm.content = "";
+  recordForm.completeTime = "";
+  recordForm.duration = 0;
+  recordForm.method = "";
+  recordForm.score = 0;
+  recordForm.result = "";
+  recordForm.status = 1;
+  recordForm.remark = "";
+};
+
+// 鍔犺浇鍩硅璁″垝閫夐」
+const loadPlanOptions = async () => {
+  try {
+    const res = await getPlanList({ pageNum: 1, pageSize: 1000 });
+    if (res.code === 200) {
+      planOptions.value = res.data.records || res.data.rows || [];
+    }
+  } catch (error) {
+    console.error('鍔犺浇鍩硅璁″垝澶辫触', error);
+  }
+};
+
+// 鍔犺浇鍛樺伐閫夐」锛堜粠鐢ㄦ埛绠$悊鑾峰彇锛�
+const loadEmployeeOptions = async () => {
+  try {
+    const res = await listUser({ pageNum: 1, pageSize: 1000 });
+    // 鐢ㄦ埛绠$悊鎺ュ彛鐩存帴杩斿洖 rows锛屾病鏈� code 瀛楁
+    employeeOptions.value = res.rows || [];
+  } catch (error) {
+    console.error('鍔犺浇鍛樺伐鍒楄〃澶辫触', error);
+  }
+};
+
+const handlePlanChange = (val) => {
+  const selectedPlan = planOptions.value.find(item => item.id === val);
+  if (selectedPlan) {
+    recordForm.content = selectedPlan.content;
+  }
+};
+
+const handleEmployeeChange = (val) => {
+  const selectedEmployee = employeeOptions.value.find(item => item.userId === val);
+  if (selectedEmployee) {
+    recordForm.employeeName = selectedEmployee.userName;
+  }
+};
+
+const openRecordDialog = () => {
+  resetRecordForm();
+  loadPlanOptions();
+  loadEmployeeOptions();
+  recordDialog.title = "鏂板瀹屾垚璁板綍";
+  recordDialog.visible = true;
+};
+
+const handleEditRecord = async (row) => {
+  resetRecordForm();
+  try {
+    const res = await getRecordDetail(row.id);
+    if (res.code === 200) {
+      Object.assign(recordForm, res.data);
+      recordDialog.title = "缂栬緫瀹屾垚璁板綍";
+      recordDialog.visible = true;
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇璁板綍璇︽儏澶辫触');
+  }
+};
+
+const submitRecordForm = async () => {
+  const valid = await recordFormRef.value.validate().catch(() => false);
+  if (!valid) return;
+
+  recordDialog.loading = true;
+  try {
+    const api = recordForm.id ? updateRecord : addRecordApi;
+    const res = await api(recordForm);
+    if (res.code === 200) {
+      ElMessage.success(recordForm.id ? '淇敼鎴愬姛' : '鏂板鎴愬姛');
+      recordDialog.visible = false;
+      getRecordData();
+    }
+  } catch (error) {
+    ElMessage.error(recordForm.id ? '淇敼澶辫触' : '鏂板澶辫触');
+  } finally {
+    recordDialog.loading = false;
+  }
+};
+
+const handleDeleteRecord = (row) => {
+  ElMessageBox.confirm(`纭鍒犻櫎璇ュ畬鎴愯褰曞悧锛焋, "鎻愮ず", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning"
+  }).then(async () => {
+    try {
+      const res = await deleteRecord(row.id);
+      if (res.code === 200) {
+        ElMessage.success("鍒犻櫎鎴愬姛");
+        getRecordData();
+      }
+    } catch (error) {
+      ElMessage.error("鍒犻櫎澶辫触");
+    }
+  });
+};
+
+onMounted(() => {
+  getMaterialData();
+});
 </script>
 
 <style lang="scss" scoped>

--
Gitblit v1.9.3