From 7354f8e5dbb2a3e0612aebf94ad92513a418ea2c Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 21 五月 2026 17:01:53 +0800
Subject: [PATCH] 浪潮 1.安环管理前端四个页面构建开发

---
 src/views/safetyManagement/basicInfo/index.vue        |  203 +++++++++++++++
 src/views/safetyManagement/employeeLearning/index.vue |  165 ++++++++++++
 src/views/safetyManagement/trainingManage/index.vue   |  135 ++++++++++
 src/views/safetyManagement/inspectionReport/index.vue |  233 +++++++++++++++++
 4 files changed, 736 insertions(+), 0 deletions(-)

diff --git a/src/views/safetyManagement/basicInfo/index.vue b/src/views/safetyManagement/basicInfo/index.vue
new file mode 100644
index 0000000..ce0160e
--- /dev/null
+++ b/src/views/safetyManagement/basicInfo/index.vue
@@ -0,0 +1,203 @@
+<template>
+  <div class="app-container">
+    <el-tabs v-model="activeTab" type="border-card" @tab-change="handleTabChange">
+      <el-tab-pane label="浜哄憳妗f" name="personnel">
+        <el-form :model="personnelFilters" :inline="true">
+          <el-form-item label="濮撳悕">
+            <el-input v-model="personnelFilters.name" placeholder="璇疯緭鍏ュ鍚�" clearable style="width: 200px" />
+          </el-form-item>
+          <el-form-item label="閮ㄩ棬">
+            <el-input v-model="personnelFilters.dept" placeholder="璇疯緭鍏ラ儴闂�" clearable style="width: 200px" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="getPersonnelData">鎼滅储</el-button>
+            <el-button @click="resetPersonnelFilters">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+        <div class="table_list">
+          <div class="actions">
+            <el-button type="primary" @click="addPersonnel" icon="Plus">鏂板</el-button>
+          </div>
+          <PIMTable :column="personnelColumns" :tableData="personnelList" :page="personnelPage" @pagination="changePersonnelPage" />
+        </div>
+      </el-tab-pane>
+
+      <el-tab-pane label="璁惧璁炬柦" name="equipment">
+        <el-form :model="equipmentFilters" :inline="true">
+          <el-form-item label="璁惧鍚嶇О">
+            <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-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="getEquipmentData">鎼滅储</el-button>
+            <el-button @click="resetEquipmentFilters">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+        <div class="table_list">
+          <div class="actions">
+            <el-button type="primary" @click="addEquipment" icon="Plus">鏂板</el-button>
+          </div>
+          <PIMTable :column="equipmentColumns" :tableData="equipmentList" :page="equipmentPage" @pagination="changeEquipmentPage" />
+        </div>
+      </el-tab-pane>
+
+      <el-tab-pane label="浣滀笟鍖哄煙" name="workArea">
+        <el-form :model="areaFilters" :inline="true">
+          <el-form-item label="鍖哄煙鍚嶇О">
+            <el-input v-model="areaFilters.name" placeholder="璇疯緭鍏ュ尯鍩熷悕绉�" clearable style="width: 200px" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="getAreaData">鎼滅储</el-button>
+            <el-button @click="resetAreaFilters">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+        <div class="table_list">
+          <div class="actions">
+            <el-button type="primary" @click="addArea" icon="Plus">鏂板</el-button>
+          </div>
+          <PIMTable :column="areaColumns" :tableData="areaList" :page="areaPage" @pagination="changeAreaPage" />
+        </div>
+      </el-tab-pane>
+
+      <el-tab-pane label="宀椾綅椋庨櫓" name="risk">
+        <el-form :model="riskFilters" :inline="true">
+          <el-form-item label="椋庨櫓绫诲瀷">
+            <el-input v-model="riskFilters.type" placeholder="璇疯緭鍏ラ闄╃被鍨�" clearable style="width: 200px" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="getRiskData">鎼滅储</el-button>
+            <el-button @click="resetRiskFilters">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+        <div class="table_list">
+          <div class="actions">
+            <el-button type="primary" @click="addRisk" icon="Plus">鏂板</el-button>
+          </div>
+          <PIMTable :column="riskColumns" :tableData="riskList" :page="riskPage" @pagination="changeRiskPage" />
+        </div>
+      </el-tab-pane>
+
+      <el-tab-pane label="搴旀�ヨ祫婧�" name="emergency">
+        <el-form :model="emergencyFilters" :inline="true">
+          <el-form-item label="璧勬簮鍚嶇О">
+            <el-input v-model="emergencyFilters.name" placeholder="璇疯緭鍏ヨ祫婧愬悕绉�" clearable style="width: 200px" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="getEmergencyData">鎼滅储</el-button>
+            <el-button @click="resetEmergencyFilters">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+        <div class="table_list">
+          <div class="actions">
+            <el-button type="primary" @click="addEmergency" icon="Plus">鏂板</el-button>
+          </div>
+          <PIMTable :column="emergencyColumns" :tableData="emergencyList" :page="emergencyPage" @pagination="changeEmergencyPage" />
+        </div>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive } from "vue";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
+
+defineOptions({
+  name: "鍩虹淇℃伅绠$悊",
+});
+
+const activeTab = ref("personnel");
+
+// 浜哄憳妗f
+const personnelFilters = reactive({ name: "", dept: "" });
+const personnelList = ref([]);
+const personnelPage = reactive({ current: 1, size: 10, total: 0 });
+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" },
+];
+
+// 璁惧璁炬柦
+const equipmentFilters = reactive({ name: "", area: "" });
+const equipmentList = ref([]);
+const equipmentPage = reactive({ current: 1, size: 10, total: 0 });
+const equipmentColumns = [
+  { label: "璁惧鍚嶇О", prop: "name", align: "center" },
+  { label: "瑙勬牸鍨嬪彿", prop: "model", align: "center" },
+  { label: "鎵�灞炲尯鍩�", prop: "area", align: "center" },
+  { label: "鐘舵��", prop: "status", align: "center" },
+];
+
+// 浣滀笟鍖哄煙
+const areaFilters = reactive({ name: "" });
+const areaList = ref([]);
+const areaPage = reactive({ current: 1, size: 10, total: 0 });
+const areaColumns = [
+  { label: "鍖哄煙鍚嶇О", prop: "name", align: "center" },
+  { label: "浣嶇疆", prop: "location", align: "center" },
+  { label: "璐熻矗浜�", prop: "manager", align: "center" },
+];
+
+// 宀椾綅椋庨櫓
+const riskFilters = reactive({ type: "" });
+const riskList = ref([]);
+const riskPage = reactive({ current: 1, size: 10, total: 0 });
+const riskColumns = [
+  { label: "椋庨櫓绫诲瀷", prop: "type", align: "center" },
+  { label: "椋庨櫓绛夌骇", prop: "level", align: "center" },
+  { label: "鎻忚堪", prop: "description", align: "center" },
+];
+
+// 搴旀�ヨ祫婧�
+const emergencyFilters = reactive({ name: "" });
+const emergencyList = ref([]);
+const emergencyPage = reactive({ current: 1, size: 10, total: 0 });
+const emergencyColumns = [
+  { label: "璧勬簮鍚嶇О", prop: "name", align: "center" },
+  { label: "绫诲瀷", prop: "type", align: "center" },
+  { label: "鏁伴噺", prop: "quantity", align: "center" },
+  { label: "瀛樻斁浣嶇疆", prop: "location", align: "center" },
+];
+
+const handleTabChange = () => {
+  // 鍒囨崲tab鏃跺姞杞藉搴旀暟鎹�
+};
+
+const getPersonnelData = () => {};
+const resetPersonnelFilters = () => { personnelFilters.name = ""; personnelFilters.dept = ""; };
+const addPersonnel = () => {};
+const changePersonnelPage = ({ page, limit }) => { personnelPage.current = page; personnelPage.size = limit; };
+
+const getEquipmentData = () => {};
+const resetEquipmentFilters = () => { equipmentFilters.name = ""; equipmentFilters.area = ""; };
+const addEquipment = () => {};
+const changeEquipmentPage = ({ page, limit }) => { equipmentPage.current = page; equipmentPage.size = limit; };
+
+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; };
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  margin-bottom: 15px;
+  display: flex;
+  justify-content: flex-end;
+}
+</style>
diff --git a/src/views/safetyManagement/employeeLearning/index.vue b/src/views/safetyManagement/employeeLearning/index.vue
new file mode 100644
index 0000000..2070899
--- /dev/null
+++ b/src/views/safetyManagement/employeeLearning/index.vue
@@ -0,0 +1,165 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="20">
+      <el-col :span="6">
+        <el-card class="user-card">
+          <div class="user-info">
+            <el-avatar :size="80" :src="userInfo.avatar" />
+            <h3>{{ userInfo.name }}</h3>
+            <p>{{ userInfo.dept }} - {{ userInfo.post }}</p>
+          </div>
+          <el-divider />
+          <div class="stat-item">
+            <span class="label">宸插畬鎴愬煿璁�</span>
+            <span class="value">{{ userInfo.completedTrainings }} 娆�</span>
+          </div>
+          <div class="stat-item">
+            <span class="label">寰呭弬鍔犲煿璁�</span>
+            <span class="value">{{ userInfo.pendingTrainings }} 娆�</span>
+          </div>
+          <div class="stat-item">
+            <span class="label">骞冲潎鑰冩牳鍒嗘暟</span>
+            <span class="value">{{ userInfo.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-col>
+    </el-row>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive } from "vue";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
+
+defineOptions({
+  name: "鍛樺伐瀛︿範",
+});
+
+const activeTab = ref("records");
+
+const userInfo = reactive({
+  name: "寮犱笁",
+  dept: "鐢熶骇閮�",
+  post: "鎿嶄綔宸�",
+  avatar: "",
+  completedTrainings: 12,
+  pendingTrainings: 3,
+  avgScore: 85,
+});
+
+// 瀛︿範璁板綍
+const recordList = ref([]);
+const recordPage = reactive({ current: 1, size: 10, total: 0 });
+const recordColumns = [
+  { label: "鍩硅鍐呭", prop: "content", align: "center" },
+  { label: "鍩硅鏃堕棿", prop: "trainingTime", align: "center" },
+  { label: "鍩硅鏃堕暱", prop: "duration", align: "center" },
+  { label: "鍩硅鏂瑰紡", prop: "method", align: "center" },
+  { label: "瀹屾垚鐘舵��", prop: "status", align: "center" },
+];
+
+// 鑰冩牳鎶ュ憡
+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) => {};
+</script>
+
+<style lang="scss" scoped>
+.user-card {
+  .user-info {
+    text-align: center;
+    h3 {
+      margin: 15px 0 5px;
+    }
+    p {
+      color: #666;
+      font-size: 14px;
+    }
+  }
+  .stat-item {
+    display: flex;
+    justify-content: space-between;
+    padding: 10px 0;
+    .label {
+      color: #666;
+    }
+    .value {
+      font-weight: bold;
+      color: #409eff;
+    }
+  }
+}
+
+.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
new file mode 100644
index 0000000..91cc4e4
--- /dev/null
+++ b/src/views/safetyManagement/inspectionReport/index.vue
@@ -0,0 +1,233 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="20" class="stat-cards">
+      <el-col :span="6">
+        <el-card>
+          <div class="stat-item">
+            <div class="stat-icon" style="background: #67c23a;"><el-icon><CircleCheck /></el-icon></div>
+            <div class="stat-info">
+              <span class="stat-value">{{ todayStats.completionRate }}%</span>
+              <span class="stat-label">褰撴棩宸℃瀹屾垚鐜�</span>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="6">
+        <el-card>
+          <div class="stat-item">
+            <div class="stat-icon" style="background: #409eff;"><el-icon><User /></el-icon></div>
+            <div class="stat-info">
+              <span class="stat-value">{{ todayStats.inspectorCount }}</span>
+              <span class="stat-label">宸℃浜哄憳鏁�</span>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="6">
+        <el-card>
+          <div class="stat-item">
+            <div class="stat-icon" style="background: #e6a23c;"><el-icon><Warning /></el-icon></div>
+            <div class="stat-info">
+              <span class="stat-value">{{ todayStats.abnormalCount }}</span>
+              <span class="stat-label">寮傚父璁板綍鏁�</span>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col :span="6">
+        <el-card>
+          <div class="stat-item">
+            <div class="stat-icon" style="background: #f56c6c;"><el-icon><CircleClose /></el-icon></div>
+            <div class="stat-info">
+              <span class="stat-value">{{ todayStats.missedCount }}</span>
+              <span class="stat-label">婕忔璁板綍鏁�</span>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20" class="chart-row">
+      <el-col :span="12">
+        <el-card>
+          <template #header>
+            <span>宸℃浠诲姟鎵ц瓒嬪娍</span>
+          </template>
+          <div ref="trendChartRef" style="height: 300px;"></div>
+        </el-card>
+      </el-col>
+      <el-col :span="12">
+        <el-card>
+          <template #header>
+            <span>宸℃绫诲瀷涓庨鐜囧垎甯�</span>
+          </template>
+          <div ref="typeChartRef" style="height: 300px;"></div>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <el-card class="table-card">
+      <template #header>
+        <span>宸℃璁板綍鏄庣粏</span>
+      </template>
+      <el-form :model="filters" :inline="true">
+        <el-form-item label="宸℃鏃ユ湡">
+          <el-date-picker v-model="filters.dateRange" type="daterange" range-separator="鑷�" start-placeholder="寮�濮嬫棩鏈�" end-placeholder="缁撴潫鏃ユ湡" value-format="YYYY-MM-DD" />
+        </el-form-item>
+        <el-form-item label="宸℃浜哄憳">
+          <el-input v-model="filters.inspector" placeholder="璇疯緭鍏ュ贰妫�浜哄憳" clearable style="width: 200px" />
+        </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-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="getData">鎼滅储</el-button>
+          <el-button @click="resetFilters">閲嶇疆</el-button>
+        </el-form-item>
+      </el-form>
+      <PIMTable :column="columns" :tableData="dataList" :page="pagination" @pagination="changePage" />
+    </el-card>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
+import * as echarts from "echarts";
+
+defineOptions({
+  name: "宸℃鏁版嵁鎶ヨ〃",
+});
+
+const todayStats = reactive({
+  completionRate: 95,
+  inspectorCount: 8,
+  abnormalCount: 2,
+  missedCount: 1,
+});
+
+const filters = reactive({
+  dateRange: [],
+  inspector: "",
+  status: "",
+});
+
+const dataList = ref([]);
+const pagination = reactive({ current: 1, size: 10, total: 0 });
+
+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: "abnormalDesc", align: "center" },
+];
+
+const trendChartRef = ref(null);
+const typeChartRef = ref(null);
+
+onMounted(() => {
+  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 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: "娑堥槻宸℃" },
+        ],
+      },
+    ],
+    legend: { orient: "vertical", left: "left" },
+  };
+  chart.setOption(option);
+};
+
+const getData = () => {};
+const resetFilters = () => {
+  filters.dateRange = [];
+  filters.inspector = "";
+  filters.status = "";
+};
+const changePage = ({ page, limit }) => {
+  pagination.current = page;
+  pagination.size = limit;
+};
+</script>
+
+<style lang="scss" scoped>
+.stat-cards {
+  margin-bottom: 20px;
+  .stat-item {
+    display: flex;
+    align-items: center;
+    .stat-icon {
+      width: 60px;
+      height: 60px;
+      border-radius: 50%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      color: #fff;
+      font-size: 28px;
+      margin-right: 15px;
+    }
+    .stat-info {
+      display: flex;
+      flex-direction: column;
+      .stat-value {
+        font-size: 24px;
+        font-weight: bold;
+        color: #333;
+      }
+      .stat-label {
+        font-size: 14px;
+        color: #666;
+        margin-top: 5px;
+      }
+    }
+  }
+}
+
+.chart-row {
+  margin-bottom: 20px;
+}
+
+.table-card {
+  :deep(.el-card__header) {
+    font-weight: bold;
+  }
+}
+</style>
diff --git a/src/views/safetyManagement/trainingManage/index.vue b/src/views/safetyManagement/trainingManage/index.vue
new file mode 100644
index 0000000..3c37b2f
--- /dev/null
+++ b/src/views/safetyManagement/trainingManage/index.vue
@@ -0,0 +1,135 @@
+<template>
+  <div class="app-container">
+    <el-tabs v-model="activeTab" type="border-card">
+      <el-tab-pane label="鍩硅璧勬枡" name="materials">
+        <el-form :model="materialFilters" :inline="true">
+          <el-form-item label="璧勬枡鍚嶇О">
+            <el-input v-model="materialFilters.name" placeholder="璇疯緭鍏ヨ祫鏂欏悕绉�" clearable style="width: 200px" />
+          </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-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="getMaterialData">鎼滅储</el-button>
+            <el-button @click="resetMaterialFilters">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+        <div class="table_list">
+          <div class="actions">
+            <el-button type="primary" @click="uploadMaterial" icon="Upload">涓婁紶璧勬枡</el-button>
+          </div>
+          <PIMTable :column="materialColumns" :tableData="materialList" :page="materialPage" @pagination="changeMaterialPage" />
+        </div>
+      </el-tab-pane>
+
+      <el-tab-pane label="鍩硅璁″垝" name="plans">
+        <el-form :model="planFilters" :inline="true">
+          <el-form-item label="璁″垝骞村害">
+            <el-date-picker v-model="planFilters.year" type="year" placeholder="閫夋嫨骞村害" value-format="YYYY" style="width: 150px" />
+          </el-form-item>
+          <el-form-item label="宀椾綅">
+            <el-input v-model="planFilters.post" placeholder="璇疯緭鍏ュ矖浣�" clearable style="width: 200px" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="getPlanData">鎼滅储</el-button>
+            <el-button @click="resetPlanFilters">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+        <div class="table_list">
+          <div class="actions">
+            <el-button type="primary" @click="addPlan" icon="Plus">鍒跺畾璁″垝</el-button>
+          </div>
+          <PIMTable :column="planColumns" :tableData="planList" :page="planPage" @pagination="changePlanPage" />
+        </div>
+      </el-tab-pane>
+
+      <el-tab-pane label="瀹屾垚璁板綍" name="records">
+        <el-form :model="recordFilters" :inline="true">
+          <el-form-item label="鍛樺伐濮撳悕">
+            <el-input v-model="recordFilters.employeeName" placeholder="璇疯緭鍏ュ憳宸ュ鍚�" clearable style="width: 200px" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="getRecordData">鎼滅储</el-button>
+            <el-button @click="resetRecordFilters">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+        <div class="table_list">
+          <PIMTable :column="recordColumns" :tableData="recordList" :page="recordPage" @pagination="changeRecordPage" />
+        </div>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive } from "vue";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
+
+defineOptions({
+  name: "鍩硅绠$悊",
+});
+
+const activeTab = ref("materials");
+
+// 鍩硅璧勬枡
+const materialFilters = reactive({ name: "", type: "" });
+const materialList = ref([]);
+const materialPage = reactive({ current: 1, size: 10, total: 0 });
+const materialColumns = [
+  { label: "璧勬枡鍚嶇О", prop: "name", align: "center" },
+  { label: "绫诲瀷", prop: "type", align: "center" },
+  { label: "涓婁紶浜�", prop: "uploader", align: "center" },
+  { label: "涓婁紶鏃堕棿", prop: "uploadTime", align: "center" },
+  { label: "鏂囦欢澶у皬", prop: "fileSize", align: "center" },
+];
+
+// 鍩硅璁″垝
+const planFilters = reactive({ year: "", post: "" });
+const planList = ref([]);
+const planPage = reactive({ current: 1, size: 10, total: 0 });
+const planColumns = [
+  { label: "璁″垝骞村害", prop: "year", align: "center" },
+  { label: "宀椾綅", prop: "post", align: "center" },
+  { label: "灞傜骇", prop: "level", align: "center" },
+  { label: "鍩硅鍐呭", prop: "content", align: "center" },
+  { label: "璁″垝璇炬椂", prop: "hours", align: "center" },
+];
+
+// 瀹屾垚璁板綍
+const recordFilters = reactive({ employeeName: "" });
+const recordList = ref([]);
+const recordPage = reactive({ current: 1, size: 10, total: 0 });
+const recordColumns = [
+  { label: "鍛樺伐濮撳悕", prop: "employeeName", align: "center" },
+  { label: "鍩硅鍐呭", prop: "content", align: "center" },
+  { label: "瀹屾垚鏃堕棿", prop: "completeTime", align: "center" },
+  { label: "鑰冩牳缁撴灉", prop: "result", align: "center" },
+];
+
+const getMaterialData = () => {};
+const resetMaterialFilters = () => { materialFilters.name = ""; materialFilters.type = ""; };
+const uploadMaterial = () => {};
+const changeMaterialPage = ({ page, limit }) => { materialPage.current = page; materialPage.size = limit; };
+
+const getPlanData = () => {};
+const resetPlanFilters = () => { planFilters.year = ""; planFilters.post = ""; };
+const addPlan = () => {};
+const changePlanPage = ({ page, limit }) => { planPage.current = page; planPage.size = limit; };
+
+const getRecordData = () => {};
+const resetRecordFilters = () => { recordFilters.employeeName = ""; };
+const changeRecordPage = ({ page, limit }) => { recordPage.current = page; recordPage.size = limit; };
+</script>
+
+<style lang="scss" scoped>
+.actions {
+  margin-bottom: 15px;
+  display: flex;
+  justify-content: flex-end;
+}
+</style>

--
Gitblit v1.9.3