From 33d5f98d85539f4762d9122c85f5d8ad4b480e9f Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期一, 29 六月 2026 11:52:53 +0800
Subject: [PATCH] refactor: 将巡检模块从设备管理移到安全生产模块下

---
 src/api/safeProduction/safetyFacility.js          |   42 +
 src/api/safeProduction/lineInspection.js          |   42 +
 src/views/safeProduction/hazardReport/index.vue   |  529 +++++++++++++++++++++
 src/views/safeProduction/lineInspection/index.vue |  374 ++++++++++++++
 src/views/safeProduction/safetyFacility/index.vue |  439 +++++++++++++++++
 src/api/safeProduction/hazardReport.js            |   60 ++
 6 files changed, 1,486 insertions(+), 0 deletions(-)

diff --git a/src/api/safeProduction/hazardReport.js b/src/api/safeProduction/hazardReport.js
new file mode 100644
index 0000000..178d9fc
--- /dev/null
+++ b/src/api/safeProduction/hazardReport.js
@@ -0,0 +1,60 @@
+import request from "@/utils/request";
+
+/**
+ * 闅愭偅涓婃姤API鎺ュ彛
+ */
+
+// 鑾峰彇闅愭偅涓婃姤鍒楄〃
+export function getHazardReportList(params) {
+  return request({
+    url: "/device/hazardReport/list",
+    method: "get",
+    params,
+  });
+}
+
+// 鏂板闅愭偅涓婃姤
+export function addHazardReport(data) {
+  return request({
+    url: "/device/hazardReport/add",
+    method: "post",
+    data,
+  });
+}
+
+// 璁板綍鏁存敼缁撴灉
+export function rectifyHazardReport(data) {
+  return request({
+    url: "/device/hazardReport/rectify",
+    method: "post",
+    data,
+  });
+}
+
+// 璁板綍楠屾敹缁撴灉
+export function acceptHazardReport(data) {
+  return request({
+    url: "/device/hazardReport/accept",
+    method: "post",
+    data,
+  });
+}
+
+// 鍒犻櫎闅愭偅涓婃姤璁板綍
+export function deleteHazardReport(ids) {
+  return request({
+    url: "/device/hazardReport/delete",
+    method: "delete",
+    data: ids,
+  });
+}
+
+// 瀵煎嚭闅愭偅涓婃姤璁板綍
+export function exportHazardReport(params) {
+  return request({
+    url: "/device/hazardReport/export",
+    method: "post",
+    params,
+    responseType: 'blob',
+  });
+}
diff --git a/src/api/safeProduction/lineInspection.js b/src/api/safeProduction/lineInspection.js
new file mode 100644
index 0000000..94dd2e3
--- /dev/null
+++ b/src/api/safeProduction/lineInspection.js
@@ -0,0 +1,42 @@
+import request from "@/utils/request";
+
+/**
+ * 绾胯矾宸℃API鎺ュ彛
+ */
+
+// 鑾峰彇绾胯矾宸℃鍒楄〃
+export function getLineInspectionList(params) {
+  return request({
+    url: "/device/lineInspection/list",
+    method: "get",
+    params,
+  });
+}
+
+// 鏂板鎴栦慨鏀圭嚎璺贰妫�璁板綍
+export function addOrEditLineInspection(data) {
+  return request({
+    url: "/device/lineInspection/addOrEdit",
+    method: "post",
+    data,
+  });
+}
+
+// 鍒犻櫎绾胯矾宸℃璁板綍
+export function deleteLineInspection(ids) {
+  return request({
+    url: "/device/lineInspection/delete",
+    method: "delete",
+    data: ids,
+  });
+}
+
+// 瀵煎嚭绾胯矾宸℃璁板綍
+export function exportLineInspection(params) {
+  return request({
+    url: "/device/lineInspection/export",
+    method: "post",
+    params,
+    responseType: 'blob',
+  });
+}
diff --git a/src/api/safeProduction/safetyFacility.js b/src/api/safeProduction/safetyFacility.js
new file mode 100644
index 0000000..c5a7254
--- /dev/null
+++ b/src/api/safeProduction/safetyFacility.js
@@ -0,0 +1,42 @@
+import request from "@/utils/request";
+
+/**
+ * 瀹夊叏璁炬柦宸℃API鎺ュ彛
+ */
+
+// 鑾峰彇瀹夊叏璁炬柦鍒楄〃
+export function getSafetyFacilityList(params) {
+  return request({
+    url: "/device/safetyFacility/list",
+    method: "get",
+    params,
+  });
+}
+
+// 鏂板鎴栦慨鏀瑰畨鍏ㄨ鏂�
+export function addOrEditSafetyFacility(data) {
+  return request({
+    url: "/device/safetyFacility/addOrEdit",
+    method: "post",
+    data,
+  });
+}
+
+// 鍒犻櫎瀹夊叏璁炬柦
+export function deleteSafetyFacility(ids) {
+  return request({
+    url: "/device/safetyFacility/delete",
+    method: "delete",
+    data: ids,
+  });
+}
+
+// 瀵煎嚭瀹夊叏璁炬柦璁板綍
+export function exportSafetyFacility(params) {
+  return request({
+    url: "/device/safetyFacility/export",
+    method: "post",
+    params,
+    responseType: 'blob',
+  });
+}
diff --git a/src/views/safeProduction/hazardReport/index.vue b/src/views/safeProduction/hazardReport/index.vue
new file mode 100644
index 0000000..26e652f
--- /dev/null
+++ b/src/views/safeProduction/hazardReport/index.vue
@@ -0,0 +1,529 @@
+<template>
+  <div class="hazard-report">
+    <!-- 鎼滅储鍖哄煙 -->
+    <div class="search-area">
+      <el-form :inline="true" :model="searchForm">
+        <el-form-item label="涓婃姤绫诲瀷">
+          <el-select v-model="searchForm.reportType" placeholder="璇烽�夋嫨绫诲瀷" clearable>
+            <el-option label="绾胯矾宸℃" value="绾胯矾宸℃" />
+            <el-option label="瀹夊叏璁炬柦宸℃" value="瀹夊叏璁炬柦宸℃" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="涓ラ噸绋嬪害">
+          <el-select v-model="searchForm.severity" placeholder="璇烽�夋嫨绋嬪害" clearable>
+            <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="searchForm.rectificationStatus" placeholder="璇烽�夋嫨鐘舵��" clearable>
+            <el-option label="寰呮暣鏀�" value="寰呮暣鏀�" />
+            <el-option label="鏁存敼涓�" value="鏁存敼涓�" />
+            <el-option label="宸插畬鎴�" value="宸插畬鎴�" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="涓婃姤浜�">
+          <el-input v-model="searchForm.reporter" placeholder="璇疯緭鍏ヤ笂鎶ヤ汉" clearable />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleSearch">鎼滅储</el-button>
+          <el-button @click="resetSearch">閲嶇疆</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <!-- 鎿嶄綔鎸夐挳 -->
+    <div class="actions">
+      <el-button type="primary" @click="handleAdd">涓婃姤闅愭偅</el-button>
+      <el-button type="success" @click="handleExport">瀵煎嚭</el-button>
+    </div>
+
+    <!-- 闅愭偅鍒楄〃 -->
+    <el-table :data="reportList" style="width: 100%; margin-top: 10px;" border>
+      <el-table-column prop="reportType" label="涓婃姤绫诲瀷" width="150" />
+      <el-table-column prop="facilityName" label="鍏宠仈璁炬柦" width="150" />
+      <el-table-column prop="hazardDescription" label="闅愭偅鎻忚堪" min-width="200" show-overflow-tooltip />
+      <el-table-column prop="severity" label="涓ラ噸绋嬪害" width="120">
+        <template #default="{ row }">
+          <el-tag :type="getSeverityType(row.severity)">
+            {{ row.severity }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="reporter" label="涓婃姤浜�" width="120" />
+      <el-table-column prop="reportTime" label="涓婃姤鏃堕棿" width="180" />
+      <el-table-column prop="rectificationStatus" label="鏁存敼鐘舵��" width="120">
+        <template #default="{ row }">
+          <el-tag :type="getRectificationStatusType(row.rectificationStatus)">
+            {{ row.rectificationStatus }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="rectificationPerson" label="鏁存敼璐d换浜�" width="120" />
+      <el-table-column label="鎿嶄綔" width="280" fixed="right">
+        <template #default="{ row }">
+          <el-button
+            v-if="row.rectificationStatus === '寰呮暣鏀�'"
+            type="warning"
+            link
+            @click="handleRectify(row)"
+          >
+            鏁存敼
+          </el-button>
+          <el-button
+            v-if="row.rectificationStatus === '宸插畬鎴�' && !row.acceptancePerson"
+            type="success"
+            link
+            @click="handleAccept(row)"
+          >
+            楠屾敹
+          </el-button>
+          <el-button type="primary" link @click="handleView(row)">鏌ョ湅</el-button>
+          <el-button type="danger" link @click="handleDelete(row)">鍒犻櫎</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 鍒嗛〉 -->
+    <div class="pagination">
+      <el-pagination
+        v-model:current-page="pagination.currentPage"
+        v-model:page-size="pagination.pageSize"
+        :page-sizes="[10, 20, 50, 100]"
+        :total="pagination.total"
+        layout="total, sizes, prev, pager, next, jumper"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+
+    <!-- 涓婃姤闅愭偅瀵硅瘽妗� -->
+    <el-dialog title="涓婃姤闅愭偅" v-model="showAddDialog" width="600px">
+      <el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px">
+        <el-form-item label="涓婃姤绫诲瀷" prop="reportType">
+          <el-select v-model="addForm.reportType" placeholder="璇烽�夋嫨绫诲瀷">
+            <el-option label="绾胯矾宸℃" value="绾胯矾宸℃" />
+            <el-option label="瀹夊叏璁炬柦宸℃" value="瀹夊叏璁炬柦宸℃" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鍏宠仈璁炬柦">
+          <el-select v-model="addForm.facilityId" placeholder="璇烽�夋嫨璁炬柦" clearable>
+            <el-option
+              v-for="item in facilityOptions"
+              :key="item.id"
+              :label="item.facilityName"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="闅愭偅鎻忚堪" prop="hazardDescription">
+          <el-input type="textarea" v-model="addForm.hazardDescription" placeholder="璇疯緭鍏ラ殣鎮f弿杩�" />
+        </el-form-item>
+        <el-form-item label="涓ラ噸绋嬪害" prop="severity">
+          <el-select v-model="addForm.severity" placeholder="璇烽�夋嫨绋嬪害">
+            <el-option label="杞诲井" value="杞诲井" />
+            <el-option label="涓�鑸�" value="涓�鑸�" />
+            <el-option label="涓ラ噸" value="涓ラ噸" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="涓婃姤浜�" prop="reporter">
+          <el-input v-model="addForm.reporter" placeholder="璇疯緭鍏ヤ笂鎶ヤ汉" />
+        </el-form-item>
+        <el-form-item label="澶囨敞">
+          <el-input type="textarea" v-model="addForm.remarks" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary" @click="submitAddForm">纭畾</el-button>
+          <el-button @click="showAddDialog = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+
+    <!-- 鏁存敼瀵硅瘽妗� -->
+    <el-dialog title="璁板綍鏁存敼缁撴灉" v-model="showRectifyDialog" width="500px">
+      <el-form :model="rectifyForm" label-width="100px">
+        <el-form-item label="闅愭偅鎻忚堪">
+          <el-input type="textarea" v-model="rectifyForm.hazardDescription" disabled />
+        </el-form-item>
+        <el-form-item label="鏁存敼璐d换浜�">
+          <el-input v-model="rectifyForm.rectificationPerson" placeholder="璇疯緭鍏ユ暣鏀硅矗浠讳汉" />
+        </el-form-item>
+        <el-form-item label="鏁存敼鎻忚堪">
+          <el-input type="textarea" v-model="rectifyForm.rectificationDescription" placeholder="璇疯緭鍏ユ暣鏀规帾鏂�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary" @click="submitRectify">纭畾</el-button>
+          <el-button @click="showRectifyDialog = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+
+    <!-- 楠屾敹瀵硅瘽妗� -->
+    <el-dialog title="璁板綍楠屾敹缁撴灉" v-model="showAcceptDialog" width="500px">
+      <el-form :model="acceptForm" label-width="100px">
+        <el-form-item label="鏁存敼鎻忚堪">
+          <el-input type="textarea" v-model="acceptForm.rectificationDescription" disabled />
+        </el-form-item>
+        <el-form-item label="楠屾敹浜�">
+          <el-input v-model="acceptForm.acceptancePerson" placeholder="璇疯緭鍏ラ獙鏀朵汉" />
+        </el-form-item>
+        <el-form-item label="楠屾敹缁撴灉">
+          <el-radio-group v-model="acceptForm.acceptanceResult">
+            <el-radio label="閫氳繃">閫氳繃</el-radio>
+            <el-radio label="涓嶉�氳繃">涓嶉�氳繃</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary" @click="submitAccept">纭畾</el-button>
+          <el-button @click="showAcceptDialog = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+
+    <!-- 鏌ョ湅璇︽儏瀵硅瘽妗� -->
+    <el-dialog title="闅愭偅璇︽儏" v-model="showViewDialog" width="600px">
+      <el-descriptions :column="2" border>
+        <el-descriptions-item label="涓婃姤绫诲瀷">{{ viewData.reportType }}</el-descriptions-item>
+        <el-descriptions-item label="鍏宠仈璁炬柦">{{ viewData.facilityName }}</el-descriptions-item>
+        <el-descriptions-item label="闅愭偅鎻忚堪" :span="2">{{ viewData.hazardDescription }}</el-descriptions-item>
+        <el-descriptions-item label="涓ラ噸绋嬪害">{{ viewData.severity }}</el-descriptions-item>
+        <el-descriptions-item label="涓婃姤浜�">{{ viewData.reporter }}</el-descriptions-item>
+        <el-descriptions-item label="涓婃姤鏃堕棿">{{ viewData.reportTime }}</el-descriptions-item>
+        <el-descriptions-item label="鏁存敼鐘舵��">{{ viewData.rectificationStatus }}</el-descriptions-item>
+        <el-descriptions-item label="鏁存敼璐d换浜�">{{ viewData.rectificationPerson }}</el-descriptions-item>
+        <el-descriptions-item label="鏁存敼鏃堕棿">{{ viewData.rectificationTime }}</el-descriptions-item>
+        <el-descriptions-item label="鏁存敼鎻忚堪" :span="2">{{ viewData.rectificationDescription }}</el-descriptions-item>
+        <el-descriptions-item label="楠屾敹浜�">{{ viewData.acceptancePerson }}</el-descriptions-item>
+        <el-descriptions-item label="楠屾敹鏃堕棿">{{ viewData.acceptanceTime }}</el-descriptions-item>
+        <el-descriptions-item label="楠屾敹缁撴灉">{{ viewData.acceptanceResult }}</el-descriptions-item>
+      </el-descriptions>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="showViewDialog = false">鍏抽棴</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import {
+  getHazardReportList,
+  addHazardReport,
+  rectifyHazardReport,
+  acceptHazardReport,
+  deleteHazardReport,
+  exportHazardReport
+} from '@/api/safeProduction/hazardReport'
+import { getSafetyFacilityList } from '@/api/equipmentManagement/safetyFacility'
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+  reportType: '',
+  severity: '',
+  rectificationStatus: '',
+  reporter: ''
+})
+
+// 鍒嗛〉鏁版嵁
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0
+})
+
+// 鍒楄〃鏁版嵁
+const reportList = ref([])
+
+// 璁炬柦閫夐」
+const facilityOptions = ref([])
+
+// 瀵硅瘽妗嗙姸鎬�
+const showAddDialog = ref(false)
+const showRectifyDialog = ref(false)
+const showAcceptDialog = ref(false)
+const showViewDialog = ref(false)
+
+// 涓婃姤琛ㄥ崟
+const addForm = reactive({
+  reportType: '',
+  facilityId: null,
+  hazardDescription: '',
+  severity: '',
+  reporter: '',
+  remarks: ''
+})
+
+// 涓婃姤琛ㄥ崟楠岃瘉瑙勫垯
+const addFormRules = reactive({
+  reportType: [{ required: true, message: '璇烽�夋嫨涓婃姤绫诲瀷', trigger: 'change' }],
+  hazardDescription: [{ required: true, message: '璇疯緭鍏ラ殣鎮f弿杩�', trigger: 'blur' }],
+  severity: [{ required: true, message: '璇烽�夋嫨涓ラ噸绋嬪害', trigger: 'change' }],
+  reporter: [{ required: true, message: '璇疯緭鍏ヤ笂鎶ヤ汉', trigger: 'blur' }]
+})
+
+const addFormRef = ref(null)
+
+// 鏁存敼琛ㄥ崟
+const rectifyForm = reactive({
+  id: null,
+  hazardDescription: '',
+  rectificationPerson: '',
+  rectificationDescription: ''
+})
+
+// 楠屾敹琛ㄥ崟
+const acceptForm = reactive({
+  id: null,
+  rectificationDescription: '',
+  acceptancePerson: '',
+  acceptanceResult: '閫氳繃'
+})
+
+// 鏌ョ湅鏁版嵁
+const viewData = ref({})
+
+// 鑾峰彇涓ラ噸绋嬪害鏍囩绫诲瀷
+const getSeverityType = (severity) => {
+  const map = {
+    '杞诲井': 'info',
+    '涓�鑸�': 'warning',
+    '涓ラ噸': 'danger'
+  }
+  return map[severity] || 'info'
+}
+
+// 鑾峰彇鏁存敼鐘舵�佹爣绛剧被鍨�
+const getRectificationStatusType = (status) => {
+  const map = {
+    '寰呮暣鏀�': 'danger',
+    '鏁存敼涓�': 'warning',
+    '宸插畬鎴�': 'success'
+  }
+  return map[status] || 'info'
+}
+
+// 鑾峰彇闅愭偅鍒楄〃
+const fetchList = async () => {
+  try {
+    const params = {
+      ...searchForm,
+      current: pagination.currentPage,
+      size: pagination.pageSize
+    }
+    const res = await getHazardReportList(params)
+    if (res.code === 200) {
+      reportList.value = res.data.records || []
+      pagination.total = res.data.total || 0
+    } else {
+      ElMessage.error(res.message || '鑾峰彇鍒楄〃澶辫触')
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇鍒楄〃澶辫触')
+  }
+}
+
+// 鑾峰彇璁炬柦鍒楄〃
+const fetchFacilityOptions = async () => {
+  try {
+    const res = await getSafetyFacilityList({ size: 1000 })
+    if (res.code === 200) {
+      facilityOptions.value = res.data.records || []
+    }
+  } catch (error) {
+    console.error('鑾峰彇璁炬柦鍒楄〃澶辫触')
+  }
+}
+
+// 鎼滅储
+const handleSearch = () => {
+  pagination.currentPage = 1
+  fetchList()
+}
+
+// 閲嶇疆鎼滅储
+const resetSearch = () => {
+  Object.assign(searchForm, {
+    reportType: '',
+    severity: '',
+    rectificationStatus: '',
+    reporter: ''
+  })
+  handleSearch()
+}
+
+// 涓婃姤闅愭偅
+const handleAdd = () => {
+  Object.assign(addForm, {
+    reportType: '',
+    facilityId: null,
+    hazardDescription: '',
+    severity: '',
+    reporter: '',
+    remarks: ''
+  })
+  showAddDialog.value = true
+}
+
+// 鏁存敼
+const handleRectify = (row) => {
+  Object.assign(rectifyForm, {
+    id: row.id,
+    hazardDescription: row.hazardDescription,
+    rectificationPerson: '',
+    rectificationDescription: ''
+  })
+  showRectifyDialog.value = true
+}
+
+// 楠屾敹
+const handleAccept = (row) => {
+  Object.assign(acceptForm, {
+    id: row.id,
+    rectificationDescription: row.rectificationDescription,
+    acceptancePerson: '',
+    acceptanceResult: '閫氳繃'
+  })
+  showAcceptDialog.value = true
+}
+
+// 鏌ョ湅璇︽儏
+const handleView = (row) => {
+  viewData.value = { ...row }
+  showViewDialog.value = true
+}
+
+// 鍒犻櫎
+const handleDelete = async (row) => {
+  try {
+    await ElMessageBox.confirm('纭鍒犻櫎璇ラ殣鎮h褰曞悧锛�', '鎻愮ず', {
+      type: 'warning'
+    })
+    const res = await deleteHazardReport([row.id])
+    if (res.code === 200) {
+      ElMessage.success('鍒犻櫎鎴愬姛')
+      fetchList()
+    } else {
+      ElMessage.error(res.message || '鍒犻櫎澶辫触')
+    }
+  } catch (error) {
+    // 鍙栨秷鍒犻櫎
+  }
+}
+
+// 鎻愪氦涓婃姤琛ㄥ崟
+const submitAddForm = async () => {
+  if (!addFormRef.value) return
+  try {
+    await addFormRef.value.validate()
+    const res = await addHazardReport(addForm)
+    if (res.code === 200) {
+      ElMessage.success('涓婃姤鎴愬姛')
+      showAddDialog.value = false
+      fetchList()
+    } else {
+      ElMessage.error(res.message || '涓婃姤澶辫触')
+    }
+  } catch (error) {
+    ElMessage.error('璇峰~鍐欏畬鏁磋〃鍗曚俊鎭�')
+  }
+}
+
+// 鎻愪氦鏁存敼
+const submitRectify = async () => {
+  try {
+    const res = await rectifyHazardReport(rectifyForm)
+    if (res.code === 200) {
+      ElMessage.success('鏁存敼璁板綍鎴愬姛')
+      showRectifyDialog.value = false
+      fetchList()
+    } else {
+      ElMessage.error(res.message || '鎿嶄綔澶辫触')
+    }
+  } catch (error) {
+    ElMessage.error('鎿嶄綔澶辫触')
+  }
+}
+
+// 鎻愪氦楠屾敹
+const submitAccept = async () => {
+  try {
+    const res = await acceptHazardReport(acceptForm)
+    if (res.code === 200) {
+      ElMessage.success('楠屾敹璁板綍鎴愬姛')
+      showAcceptDialog.value = false
+      fetchList()
+    } else {
+      ElMessage.error(res.message || '鎿嶄綔澶辫触')
+    }
+  } catch (error) {
+    ElMessage.error('鎿嶄綔澶辫触')
+  }
+}
+
+// 瀵煎嚭
+const handleExport = async () => {
+  try {
+    const res = await exportHazardReport(searchForm)
+    const blob = new Blob([res])
+    const url = window.URL.createObjectURL(blob)
+    const link = document.createElement('a')
+    link.href = url
+    link.download = '闅愭偅涓婃姤璁板綍.xlsx'
+    link.click()
+    window.URL.revokeObjectURL(url)
+    ElMessage.success('瀵煎嚭鎴愬姛')
+  } catch (error) {
+    ElMessage.error('瀵煎嚭澶辫触')
+  }
+}
+
+// 鍒嗛〉澶у皬鏀瑰彉
+const handleSizeChange = (val) => {
+  pagination.pageSize = val
+  fetchList()
+}
+
+// 褰撳墠椤垫敼鍙�
+const handleCurrentChange = (val) => {
+  pagination.currentPage = val
+  fetchList()
+}
+
+onMounted(() => {
+  fetchList()
+  fetchFacilityOptions()
+})
+</script>
+
+<style scoped>
+.hazard-report {
+  padding: 20px;
+}
+
+.search-area {
+  background: #f5f7fa;
+  padding: 20px;
+  border-radius: 4px;
+  margin-bottom: 15px;
+}
+
+.actions {
+  margin-bottom: 15px;
+}
+
+.pagination {
+  margin-top: 20px;
+  display: flex;
+  justify-content: flex-end;
+}
+</style>
diff --git a/src/views/safeProduction/lineInspection/index.vue b/src/views/safeProduction/lineInspection/index.vue
new file mode 100644
index 0000000..45358e9
--- /dev/null
+++ b/src/views/safeProduction/lineInspection/index.vue
@@ -0,0 +1,374 @@
+<template>
+  <div class="line-inspection">
+    <!-- 鎼滅储鍖哄煙 -->
+    <div class="search-area">
+      <el-form :inline="true" :model="searchForm">
+        <el-form-item label="浠诲姟鍚嶇О">
+          <el-input v-model="searchForm.taskName" placeholder="璇疯緭鍏ヤ换鍔″悕绉�" clearable />
+        </el-form-item>
+        <el-form-item label="绾胯矾鍚嶇О">
+          <el-input v-model="searchForm.routeName" placeholder="璇疯緭鍏ョ嚎璺悕绉�" clearable />
+        </el-form-item>
+        <el-form-item label="宸℃浜�">
+          <el-input v-model="searchForm.inspector" placeholder="璇疯緭鍏ュ贰妫�浜�" clearable />
+        </el-form-item>
+        <el-form-item label="鐘舵��">
+          <el-select v-model="searchForm.status" placeholder="璇烽�夋嫨鐘舵��" clearable>
+            <el-option label="寰呭贰妫�" value="寰呭贰妫�" />
+            <el-option label="杩涜涓�" value="杩涜涓�" />
+            <el-option label="宸插畬鎴�" value="宸插畬鎴�" />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleSearch">鎼滅储</el-button>
+          <el-button @click="resetSearch">閲嶇疆</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <!-- 鎿嶄綔鎸夐挳 -->
+    <div class="actions">
+      <el-button type="primary" @click="handleAdd">鏂板宸℃</el-button>
+      <el-button type="success" @click="handleExport">瀵煎嚭</el-button>
+    </div>
+
+    <!-- 宸℃鍒楄〃 -->
+    <el-table :data="inspectionList" style="width: 100%; margin-top: 10px;" border>
+      <el-table-column prop="taskName" label="浠诲姟鍚嶇О" width="180" />
+      <el-table-column prop="routeName" label="绾胯矾鍚嶇О" width="180" />
+      <el-table-column prop="deviceName" label="鍏宠仈璁惧" width="150" />
+      <el-table-column prop="inspector" label="宸℃浜�" width="120" />
+      <el-table-column prop="inspectionTime" label="宸℃鏃堕棿" width="180" />
+      <el-table-column prop="inspectionResult" label="宸℃缁撴灉" width="120">
+        <template #default="{ row }">
+          <el-tag :type="row.inspectionResult === '姝e父' ? 'success' : 'danger'">
+            {{ row.inspectionResult || '鏈贰妫�' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="status" label="鐘舵��" width="120">
+        <template #default="{ row }">
+          <el-tag :type="getStatusType(row.status)">
+            {{ row.status }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="鎿嶄綔" width="200" fixed="right">
+        <template #default="{ row }">
+          <el-button type="primary" link @click="handleEdit(row)">缂栬緫</el-button>
+          <el-button type="danger" link @click="handleDelete(row)">鍒犻櫎</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 鍒嗛〉 -->
+    <div class="pagination">
+      <el-pagination
+        v-model:current-page="pagination.currentPage"
+        v-model:page-size="pagination.pageSize"
+        :page-sizes="[10, 20, 50, 100]"
+        :total="pagination.total"
+        layout="total, sizes, prev, pager, next, jumper"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+
+    <!-- 鏂板/缂栬緫瀵硅瘽妗� -->
+    <el-dialog :title="dialogTitle" v-model="showDialog" width="600px">
+      <el-form :model="form" :rules="formRules" ref="formRef" label-width="100px">
+        <el-form-item label="浠诲姟鍚嶇О" prop="taskName">
+          <el-input v-model="form.taskName" placeholder="璇疯緭鍏ヤ换鍔″悕绉�" />
+        </el-form-item>
+        <el-form-item label="绾胯矾鍚嶇О" prop="routeName">
+          <el-input v-model="form.routeName" placeholder="璇疯緭鍏ョ嚎璺悕绉�" />
+        </el-form-item>
+        <el-form-item label="鍏宠仈璁惧">
+          <el-select v-model="form.deviceId" placeholder="璇烽�夋嫨璁惧" clearable>
+            <el-option
+              v-for="item in deviceOptions"
+              :key="item.id"
+              :label="item.deviceName"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="宸℃浜�" prop="inspector">
+          <el-input v-model="form.inspector" placeholder="璇疯緭鍏ュ贰妫�浜�" />
+        </el-form-item>
+        <el-form-item label="宸℃鏃堕棿">
+          <el-date-picker
+            v-model="form.inspectionTime"
+            type="datetime"
+            placeholder="璇烽�夋嫨宸℃鏃堕棿"
+            format="YYYY-MM-DD HH:mm:ss"
+            value-format="YYYY-MM-DD HH:mm:ss"
+          />
+        </el-form-item>
+        <el-form-item label="宸℃缁撴灉">
+          <el-radio-group v-model="form.inspectionResult">
+            <el-radio label="姝e父">姝e父</el-radio>
+            <el-radio label="寮傚父">寮傚父</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="闅愭偅鎻忚堪" v-if="form.inspectionResult === '寮傚父'">
+          <el-input type="textarea" v-model="form.hazardDescription" placeholder="璇疯緭鍏ラ殣鎮f弿杩�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞">
+          <el-input type="textarea" v-model="form.remarks" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary" @click="submitForm">纭畾</el-button>
+          <el-button @click="showDialog = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import {
+  getLineInspectionList,
+  addOrEditLineInspection,
+  deleteLineInspection,
+  exportLineInspection
+} from '@/api/safeProduction/lineInspection'
+import { getDeviceLedger } from '@/api/equipmentManagement/ledger'
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+  taskName: '',
+  routeName: '',
+  inspector: '',
+  status: ''
+})
+
+// 鍒嗛〉鏁版嵁
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0
+})
+
+// 鍒楄〃鏁版嵁
+const inspectionList = ref([])
+
+// 璁惧閫夐」
+const deviceOptions = ref([])
+
+// 瀵硅瘽妗嗙姸鎬�
+const showDialog = ref(false)
+const dialogTitle = ref('鏂板宸℃')
+
+// 琛ㄥ崟鏁版嵁
+const form = reactive({
+  id: null,
+  taskName: '',
+  routeName: '',
+  deviceId: null,
+  inspector: '',
+  inspectionTime: '',
+  inspectionResult: '',
+  hazardDescription: '',
+  remarks: ''
+})
+
+// 琛ㄥ崟楠岃瘉瑙勫垯
+const formRules = reactive({
+  taskName: [{ required: true, message: '璇疯緭鍏ヤ换鍔″悕绉�', trigger: 'blur' }],
+  routeName: [{ required: true, message: '璇疯緭鍏ョ嚎璺悕绉�', trigger: 'blur' }],
+  inspector: [{ required: true, message: '璇疯緭鍏ュ贰妫�浜�', trigger: 'blur' }]
+})
+
+const formRef = ref(null)
+
+// 鑾峰彇鐘舵�佹爣绛剧被鍨�
+const getStatusType = (status) => {
+  const map = {
+    '寰呭贰妫�': 'info',
+    '杩涜涓�': 'warning',
+    '宸插畬鎴�': 'success'
+  }
+  return map[status] || 'info'
+}
+
+// 鑾峰彇宸℃鍒楄〃
+const fetchList = async () => {
+  try {
+    const params = {
+      ...searchForm,
+      current: pagination.currentPage,
+      size: pagination.pageSize
+    }
+    const res = await getLineInspectionList(params)
+    if (res.code === 200) {
+      inspectionList.value = res.data.records || []
+      pagination.total = res.data.total || 0
+    } else {
+      ElMessage.error(res.message || '鑾峰彇鍒楄〃澶辫触')
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇鍒楄〃澶辫触')
+  }
+}
+
+// 鑾峰彇璁惧鍒楄〃
+const fetchDeviceOptions = async () => {
+  try {
+    const res = await getDeviceLedger()
+    if (res.code === 200) {
+      deviceOptions.value = res.data || []
+    }
+  } catch (error) {
+    console.error('鑾峰彇璁惧鍒楄〃澶辫触')
+  }
+}
+
+// 鎼滅储
+const handleSearch = () => {
+  pagination.currentPage = 1
+  fetchList()
+}
+
+// 閲嶇疆鎼滅储
+const resetSearch = () => {
+  Object.assign(searchForm, {
+    taskName: '',
+    routeName: '',
+    inspector: '',
+    status: ''
+  })
+  handleSearch()
+}
+
+// 鏂板
+const handleAdd = () => {
+  dialogTitle.value = '鏂板宸℃'
+  Object.assign(form, {
+    id: null,
+    taskName: '',
+    routeName: '',
+    deviceId: null,
+    inspector: '',
+    inspectionTime: '',
+    inspectionResult: '',
+    hazardDescription: '',
+    remarks: ''
+  })
+  showDialog.value = true
+}
+
+// 缂栬緫
+const handleEdit = (row) => {
+  dialogTitle.value = '缂栬緫宸℃'
+  Object.assign(form, {
+    id: row.id,
+    taskName: row.taskName,
+    routeName: row.routeName,
+    deviceId: row.deviceId,
+    inspector: row.inspector,
+    inspectionTime: row.inspectionTime,
+    inspectionResult: row.inspectionResult,
+    hazardDescription: row.hazardDescription,
+    remarks: row.remarks
+  })
+  showDialog.value = true
+}
+
+// 鍒犻櫎
+const handleDelete = async (row) => {
+  try {
+    await ElMessageBox.confirm('纭鍒犻櫎璇ュ贰妫�璁板綍鍚楋紵', '鎻愮ず', {
+      type: 'warning'
+    })
+    const res = await deleteLineInspection([row.id])
+    if (res.code === 200) {
+      ElMessage.success('鍒犻櫎鎴愬姛')
+      fetchList()
+    } else {
+      ElMessage.error(res.message || '鍒犻櫎澶辫触')
+    }
+  } catch (error) {
+    // 鍙栨秷鍒犻櫎
+  }
+}
+
+// 鎻愪氦琛ㄥ崟
+const submitForm = async () => {
+  if (!formRef.value) return
+  try {
+    await formRef.value.validate()
+    const res = await addOrEditLineInspection(form)
+    if (res.code === 200) {
+      ElMessage.success('鎿嶄綔鎴愬姛')
+      showDialog.value = false
+      fetchList()
+    } else {
+      ElMessage.error(res.message || '鎿嶄綔澶辫触')
+    }
+  } catch (error) {
+    ElMessage.error('璇峰~鍐欏畬鏁磋〃鍗曚俊鎭�')
+  }
+}
+
+// 瀵煎嚭
+const handleExport = async () => {
+  try {
+    const res = await exportLineInspection(searchForm)
+    const blob = new Blob([res])
+    const url = window.URL.createObjectURL(blob)
+    const link = document.createElement('a')
+    link.href = url
+    link.download = '绾胯矾宸℃璁板綍.xlsx'
+    link.click()
+    window.URL.revokeObjectURL(url)
+    ElMessage.success('瀵煎嚭鎴愬姛')
+  } catch (error) {
+    ElMessage.error('瀵煎嚭澶辫触')
+  }
+}
+
+// 鍒嗛〉澶у皬鏀瑰彉
+const handleSizeChange = (val) => {
+  pagination.pageSize = val
+  fetchList()
+}
+
+// 褰撳墠椤垫敼鍙�
+const handleCurrentChange = (val) => {
+  pagination.currentPage = val
+  fetchList()
+}
+
+onMounted(() => {
+  fetchList()
+  fetchDeviceOptions()
+})
+</script>
+
+<style scoped>
+.line-inspection {
+  padding: 20px;
+}
+
+.search-area {
+  background: #f5f7fa;
+  padding: 20px;
+  border-radius: 4px;
+  margin-bottom: 15px;
+}
+
+.actions {
+  margin-bottom: 15px;
+}
+
+.pagination {
+  margin-top: 20px;
+  display: flex;
+  justify-content: flex-end;
+}
+</style>
diff --git a/src/views/safeProduction/safetyFacility/index.vue b/src/views/safeProduction/safetyFacility/index.vue
new file mode 100644
index 0000000..02a0342
--- /dev/null
+++ b/src/views/safeProduction/safetyFacility/index.vue
@@ -0,0 +1,439 @@
+<template>
+  <div class="safety-facility">
+    <!-- 鎼滅储鍖哄煙 -->
+    <div class="search-area">
+      <el-form :inline="true" :model="searchForm">
+        <el-form-item label="璁炬柦鍚嶇О">
+          <el-input v-model="searchForm.facilityName" placeholder="璇疯緭鍏ヨ鏂藉悕绉�" clearable />
+        </el-form-item>
+        <el-form-item label="璁炬柦绫诲瀷">
+          <el-select v-model="searchForm.facilityType" placeholder="璇烽�夋嫨绫诲瀷" clearable>
+            <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="searchForm.status" placeholder="璇烽�夋嫨鐘舵��" clearable>
+            <el-option label="姝e父" value="姝e父" />
+            <el-option label="寮傚父" value="寮傚父" />
+            <el-option label="鍋滅敤" value="鍋滅敤" />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleSearch">鎼滅储</el-button>
+          <el-button @click="resetSearch">閲嶇疆</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <!-- 鎿嶄綔鎸夐挳 -->
+    <div class="actions">
+      <el-button type="primary" @click="handleAdd">鏂板璁炬柦</el-button>
+      <el-button type="success" @click="handleExport">瀵煎嚭</el-button>
+    </div>
+
+    <!-- 璁炬柦鍒楄〃 -->
+    <el-table :data="facilityList" style="width: 100%; margin-top: 10px;" border>
+      <el-table-column prop="facilityName" label="璁炬柦鍚嶇О" width="180" />
+      <el-table-column prop="facilityType" label="璁炬柦绫诲瀷" width="120" />
+      <el-table-column prop="location" label="鎵�鍦ㄤ綅缃�" width="200" />
+      <el-table-column prop="deviceName" label="鍏宠仈璁惧" width="150" />
+      <el-table-column prop="status" label="鐘舵��" width="100">
+        <template #default="{ row }">
+          <el-tag :type="getFacilityStatusType(row.status)">
+            {{ row.status }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="lastInspectionTime" label="涓婃宸℃" width="180" />
+      <el-table-column prop="nextInspectionTime" label="涓嬫宸℃" width="180" />
+      <el-table-column prop="responsibilityPerson" label="璐d换浜�" width="120" />
+      <el-table-column label="鎿嶄綔" width="250" fixed="right">
+        <template #default="{ row }">
+          <el-button type="primary" link @click="handleEdit(row)">缂栬緫</el-button>
+          <el-button type="warning" link @click="handleInspection(row)">宸℃</el-button>
+          <el-button type="danger" link @click="handleDelete(row)">鍒犻櫎</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 鍒嗛〉 -->
+    <div class="pagination">
+      <el-pagination
+        v-model:current-page="pagination.currentPage"
+        v-model:page-size="pagination.pageSize"
+        :page-sizes="[10, 20, 50, 100]"
+        :total="pagination.total"
+        layout="total, sizes, prev, pager, next, jumper"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+
+    <!-- 鏂板/缂栬緫瀵硅瘽妗� -->
+    <el-dialog :title="dialogTitle" v-model="showDialog" width="600px">
+      <el-form :model="form" :rules="formRules" ref="formRef" label-width="100px">
+        <el-form-item label="璁炬柦鍚嶇О" prop="facilityName">
+          <el-input v-model="form.facilityName" placeholder="璇疯緭鍏ヨ鏂藉悕绉�" />
+        </el-form-item>
+        <el-form-item label="璁炬柦绫诲瀷" prop="facilityType">
+          <el-select v-model="form.facilityType" placeholder="璇烽�夋嫨绫诲瀷">
+            <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="location">
+          <el-input v-model="form.location" placeholder="璇疯緭鍏ユ墍鍦ㄤ綅缃�" />
+        </el-form-item>
+        <el-form-item label="鍏宠仈璁惧">
+          <el-select v-model="form.deviceId" placeholder="璇烽�夋嫨璁惧" clearable>
+            <el-option
+              v-for="item in deviceOptions"
+              :key="item.id"
+              :label="item.deviceName"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="璐d换閮ㄩ棬">
+          <el-input v-model="form.responsibilityDept" placeholder="璇疯緭鍏ヨ矗浠婚儴闂�" />
+        </el-form-item>
+        <el-form-item label="璐d换浜�">
+          <el-input v-model="form.responsibilityPerson" placeholder="璇疯緭鍏ヨ矗浠讳汉" />
+        </el-form-item>
+        <el-form-item label="涓嬫宸℃">
+          <el-date-picker
+            v-model="form.nextInspectionTime"
+            type="datetime"
+            placeholder="璇烽�夋嫨涓嬫宸℃鏃堕棿"
+            format="YYYY-MM-DD HH:mm:ss"
+            value-format="YYYY-MM-DD HH:mm:ss"
+          />
+        </el-form-item>
+        <el-form-item label="澶囨敞">
+          <el-input type="textarea" v-model="form.remarks" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary" @click="submitForm">纭畾</el-button>
+          <el-button @click="showDialog = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+
+    <!-- 宸℃璁板綍瀵硅瘽妗� -->
+    <el-dialog title="璁板綍宸℃缁撴灉" v-model="showInspectionDialog" width="500px">
+      <el-form :model="inspectionForm" label-width="100px">
+        <el-form-item label="璁炬柦鍚嶇О">
+          <el-input v-model="inspectionForm.facilityName" disabled />
+        </el-form-item>
+        <el-form-item label="宸℃缁撴灉">
+          <el-radio-group v-model="inspectionForm.status">
+            <el-radio label="姝e父">姝e父</el-radio>
+            <el-radio label="寮傚父">寮傚父</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="宸℃璇存槑" v-if="inspectionForm.status === '寮傚父'">
+          <el-input type="textarea" v-model="inspectionForm.description" placeholder="璇疯緭鍏ュ紓甯歌鏄�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary" @click="submitInspection">纭畾</el-button>
+          <el-button @click="showInspectionDialog = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import {
+  getSafetyFacilityList,
+  addOrEditSafetyFacility,
+  deleteSafetyFacility,
+  exportSafetyFacility
+} from '@/api/safeProduction/safetyFacility'
+import { getDeviceLedger } from '@/api/equipmentManagement/ledger'
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+  facilityName: '',
+  facilityType: '',
+  status: ''
+})
+
+// 鍒嗛〉鏁版嵁
+const pagination = reactive({
+  currentPage: 1,
+  pageSize: 10,
+  total: 0
+})
+
+// 鍒楄〃鏁版嵁
+const facilityList = ref([])
+
+// 璁惧閫夐」
+const deviceOptions = ref([])
+
+// 瀵硅瘽妗嗙姸鎬�
+const showDialog = ref(false)
+const dialogTitle = ref('鏂板璁炬柦')
+
+// 宸℃瀵硅瘽妗嗙姸鎬�
+const showInspectionDialog = ref(false)
+
+// 琛ㄥ崟鏁版嵁
+const form = reactive({
+  id: null,
+  facilityName: '',
+  facilityType: '',
+  location: '',
+  deviceId: null,
+  responsibilityDept: '',
+  responsibilityPerson: '',
+  nextInspectionTime: '',
+  remarks: ''
+})
+
+// 宸℃琛ㄥ崟鏁版嵁
+const inspectionForm = reactive({
+  id: null,
+  facilityName: '',
+  status: '姝e父',
+  description: ''
+})
+
+// 琛ㄥ崟楠岃瘉瑙勫垯
+const formRules = reactive({
+  facilityName: [{ required: true, message: '璇疯緭鍏ヨ鏂藉悕绉�', trigger: 'blur' }],
+  facilityType: [{ required: true, message: '璇烽�夋嫨璁炬柦绫诲瀷', trigger: 'change' }],
+  location: [{ required: true, message: '璇疯緭鍏ユ墍鍦ㄤ綅缃�', trigger: 'blur' }]
+})
+
+const formRef = ref(null)
+
+// 鑾峰彇鐘舵�佹爣绛剧被鍨�
+const getFacilityStatusType = (status) => {
+  const map = {
+    '姝e父': 'success',
+    '寮傚父': 'danger',
+    '鍋滅敤': 'info'
+  }
+  return map[status] || 'info'
+}
+
+// 鑾峰彇璁炬柦鍒楄〃
+const fetchList = async () => {
+  try {
+    const params = {
+      ...searchForm,
+      current: pagination.currentPage,
+      size: pagination.pageSize
+    }
+    const res = await getSafetyFacilityList(params)
+    if (res.code === 200) {
+      facilityList.value = res.data.records || []
+      pagination.total = res.data.total || 0
+    } else {
+      ElMessage.error(res.message || '鑾峰彇鍒楄〃澶辫触')
+    }
+  } catch (error) {
+    ElMessage.error('鑾峰彇鍒楄〃澶辫触')
+  }
+}
+
+// 鑾峰彇璁惧鍒楄〃
+const fetchDeviceOptions = async () => {
+  try {
+    const res = await getDeviceLedger()
+    if (res.code === 200) {
+      deviceOptions.value = res.data || []
+    }
+  } catch (error) {
+    console.error('鑾峰彇璁惧鍒楄〃澶辫触')
+  }
+}
+
+// 鎼滅储
+const handleSearch = () => {
+  pagination.currentPage = 1
+  fetchList()
+}
+
+// 閲嶇疆鎼滅储
+const resetSearch = () => {
+  Object.assign(searchForm, {
+    facilityName: '',
+    facilityType: '',
+    status: ''
+  })
+  handleSearch()
+}
+
+// 鏂板
+const handleAdd = () => {
+  dialogTitle.value = '鏂板璁炬柦'
+  Object.assign(form, {
+    id: null,
+    facilityName: '',
+    facilityType: '',
+    location: '',
+    deviceId: null,
+    responsibilityDept: '',
+    responsibilityPerson: '',
+    nextInspectionTime: '',
+    remarks: ''
+  })
+  showDialog.value = true
+}
+
+// 缂栬緫
+const handleEdit = (row) => {
+  dialogTitle.value = '缂栬緫璁炬柦'
+  Object.assign(form, {
+    id: row.id,
+    facilityName: row.facilityName,
+    facilityType: row.facilityType,
+    location: row.location,
+    deviceId: row.deviceId,
+    responsibilityDept: row.responsibilityDept,
+    responsibilityPerson: row.responsibilityPerson,
+    nextInspectionTime: row.nextInspectionTime,
+    remarks: row.remarks
+  })
+  showDialog.value = true
+}
+
+// 宸℃
+const handleInspection = (row) => {
+  Object.assign(inspectionForm, {
+    id: row.id,
+    facilityName: row.facilityName,
+    status: '姝e父',
+    description: ''
+  })
+  showInspectionDialog.value = true
+}
+
+// 鍒犻櫎
+const handleDelete = async (row) => {
+  try {
+    await ElMessageBox.confirm('纭鍒犻櫎璇ヨ鏂藉悧锛�', '鎻愮ず', {
+      type: 'warning'
+    })
+    const res = await deleteSafetyFacility([row.id])
+    if (res.code === 200) {
+      ElMessage.success('鍒犻櫎鎴愬姛')
+      fetchList()
+    } else {
+      ElMessage.error(res.message || '鍒犻櫎澶辫触')
+    }
+  } catch (error) {
+    // 鍙栨秷鍒犻櫎
+  }
+}
+
+// 鎻愪氦琛ㄥ崟
+const submitForm = async () => {
+  if (!formRef.value) return
+  try {
+    await formRef.value.validate()
+    const res = await addOrEditSafetyFacility(form)
+    if (res.code === 200) {
+      ElMessage.success('鎿嶄綔鎴愬姛')
+      showDialog.value = false
+      fetchList()
+    } else {
+      ElMessage.error(res.message || '鎿嶄綔澶辫触')
+    }
+  } catch (error) {
+    ElMessage.error('璇峰~鍐欏畬鏁磋〃鍗曚俊鎭�')
+  }
+}
+
+// 鎻愪氦宸℃缁撴灉
+const submitInspection = async () => {
+  try {
+    const data = {
+      id: inspectionForm.id,
+      status: inspectionForm.status,
+      lastInspectionTime: new Date().toISOString()
+    }
+    const res = await addOrEditSafetyFacility(data)
+    if (res.code === 200) {
+      ElMessage.success('宸℃璁板綍鎴愬姛')
+      showInspectionDialog.value = false
+      fetchList()
+    } else {
+      ElMessage.error(res.message || '鎿嶄綔澶辫触')
+    }
+  } catch (error) {
+    ElMessage.error('鎿嶄綔澶辫触')
+  }
+}
+
+// 瀵煎嚭
+const handleExport = async () => {
+  try {
+    const res = await exportSafetyFacility(searchForm)
+    const blob = new Blob([res])
+    const url = window.URL.createObjectURL(blob)
+    const link = document.createElement('a')
+    link.href = url
+    link.download = '瀹夊叏璁炬柦鍙拌处.xlsx'
+    link.click()
+    window.URL.revokeObjectURL(url)
+    ElMessage.success('瀵煎嚭鎴愬姛')
+  } catch (error) {
+    ElMessage.error('瀵煎嚭澶辫触')
+  }
+}
+
+// 鍒嗛〉澶у皬鏀瑰彉
+const handleSizeChange = (val) => {
+  pagination.pageSize = val
+  fetchList()
+}
+
+// 褰撳墠椤垫敼鍙�
+const handleCurrentChange = (val) => {
+  pagination.currentPage = val
+  fetchList()
+}
+
+onMounted(() => {
+  fetchList()
+  fetchDeviceOptions()
+})
+</script>
+
+<style scoped>
+.safety-facility {
+  padding: 20px;
+}
+
+.search-area {
+  background: #f5f7fa;
+  padding: 20px;
+  border-radius: 4px;
+  margin-bottom: 15px;
+}
+
+.actions {
+  margin-bottom: 15px;
+}
+
+.pagination {
+  margin-top: 20px;
+  display: flex;
+  justify-content: flex-end;
+}
+</style>

--
Gitblit v1.9.3