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