liding
2026-06-29 33d5f98d85539f4762d9122c85f5d8ad4b480e9f
refactor: 将巡检模块从设备管理移到安全生产模块下

- 线路巡检 (lineInspection)
- 安全设施巡检 (safetyFacility)
- 隐患上报 (hazardReport)
已添加6个文件
1486 ■■■■■ 文件已修改
src/api/safeProduction/hazardReport.js 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/safeProduction/lineInspection.js 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/safeProduction/safetyFacility.js 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safeProduction/hazardReport/index.vue 529 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safeProduction/lineInspection/index.vue 374 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safeProduction/safetyFacility/index.vue 439 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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',
  });
}
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',
  });
}
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',
  });
}
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="整改责任人" 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="请输入隐患描述" />
        </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="整改责任人">
          <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="整改责任人">{{ 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: '请输入隐患描述', 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('确认删除该隐患记录吗?', '提示', {
      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>
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 === '正常' ? '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="正常">正常</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="请输入隐患描述" />
        </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>
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="正常" 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="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="责任人" 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="责任部门">
          <el-input v-model="form.responsibilityDept" placeholder="请输入责任部门" />
        </el-form-item>
        <el-form-item label="责任人">
          <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="正常">正常</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: '正常',
  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 = {
    '正常': '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: '正常',
    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>