gongchunyi
2 天以前 06661fa6408ac922959dc6d79dab41596acdb375
fix: 安全设施巡检流程调整
已修改2个文件
850 ■■■■ 文件已修改
src/components/PIMTable/PIMTable.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/safeProduction/safetyFacility/index.vue 842 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/PIMTable/PIMTable.vue
@@ -333,6 +333,7 @@
  const uploadRefs = ref([]);
  const currentFiles = ref({});
  const uploadKeys = ref({});
  const parentMethods = inject("parentMethods", {});
  const indexMethod = index => {
    return (props.page.current - 1) * props.page.size + index + 1;
@@ -342,6 +343,10 @@
  const goLink = (row, linkMethod) => {
    if (!linkMethod) {
      return ElMessage.warning("请配置 link 事件");
    }
    if (typeof linkMethod === "function") {
      linkMethod(row);
      return;
    }
    const parentMethod = getParentMethod(linkMethod);
    if (typeof parentMethod === "function") {
@@ -353,8 +358,7 @@
  // 获取父组件方法(示例实现)
  const getParentMethod = methodName => {
    const parentMethods = inject("parentMethods", {});
    return parentMethods[methodName];
    return parentMethods?.[methodName];
  };
  const dataTypeFn = (val, format) => {
src/views/safeProduction/safetyFacility/index.vue
@@ -1,10 +1,12 @@
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" @tab-click="handleTabClick">
  <div class="app-container safety-facility-page">
    <el-tabs v-model="activeTab"
             class="facility-tabs"
             @tab-click="handleTabClick">
      <!-- 安全设施台账 -->
      <el-tab-pane label="安全设施台账" name="ledger">
        <div class="search_form mb20">
          <div>
        <div class="facility-toolbar">
          <div class="toolbar-filters">
            <span class="search_title">设施编号:</span>
            <el-input v-model="ledgerSearchForm.facilityCode"
                      style="width: 200px"
@@ -39,12 +41,16 @@
              <el-option label="报废" value="报废" />
            </el-select>
            <el-button type="primary"
                       @click="handleLedgerQuery"
                       style="margin-left: 10px">
                       @click="handleLedgerQuery">
              搜索
            </el-button>
            <el-button @click="resetLedgerQuery">重置</el-button>
          </div>
          <div>
          <div class="toolbar-actions">
            <el-button type="danger"
                       plain
                       :disabled="ledgerSelectedIds.length === 0"
                       @click="handleBatchDeleteLedger">批量删除</el-button>
            <el-button type="primary"
                       @click="openLedgerForm('add')">新增设施</el-button>
          </div>
@@ -65,8 +71,8 @@
      <!-- 安全设施巡检 -->
      <el-tab-pane label="安全设施巡检" name="inspection">
        <div class="search_form mb20">
          <div>
        <div class="facility-toolbar">
          <div class="toolbar-filters">
            <span class="search_title">巡检编号:</span>
            <el-input v-model="inspectionSearchForm.inspectionCode"
                      style="width: 200px"
@@ -83,10 +89,10 @@
              <el-option label="已巡检" value="已巡检" />
            </el-select>
            <el-button type="primary"
                       @click="handleInspectionQuery"
                       style="margin-left: 10px">
                       @click="handleInspectionQuery">
              搜索
            </el-button>
            <el-button @click="resetInspectionQuery">重置</el-button>
          </div>
        </div>
@@ -104,8 +110,8 @@
      <!-- 整改跟踪 -->
      <el-tab-pane label="整改跟踪" name="rectification">
        <div class="search_form mb20">
          <div>
        <div class="facility-toolbar">
          <div class="toolbar-filters">
            <span class="search_title">状态:</span>
            <el-select v-model="rectificationSearchForm.status"
                       clearable
@@ -117,10 +123,10 @@
              <el-option label="已验收" value="已验收" />
            </el-select>
            <el-button type="primary"
                       @click="handleRectificationQuery"
                       style="margin-left: 10px">
                       @click="handleRectificationQuery">
              搜索
            </el-button>
            <el-button @click="resetRectificationQuery">重置</el-button>
          </div>
        </div>
@@ -145,7 +151,8 @@
      <el-form ref="ledgerFormRef"
               :model="ledgerForm"
               :rules="ledgerRules"
               label-width="120px">
               label-width="120px"
               :disabled="isLedgerView">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="设施编号"
@@ -255,9 +262,10 @@
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
          <el-button v-if="!isLedgerView"
                     type="primary"
                     @click="submitLedgerForm">确定</el-button>
          <el-button @click="ledgerDialogVisible = false">取消</el-button>
          <el-button @click="ledgerDialogVisible = false">{{ isLedgerView ? '关闭' : '取消' }}</el-button>
        </span>
      </template>
    </el-dialog>
@@ -265,44 +273,54 @@
    <!-- 巡检表单弹窗 -->
    <el-dialog v-model="inspectionDialogVisible"
               :title="inspectionDialogTitle"
               width="600px"
               width="720px"
               class="facility-form-dialog"
               :close-on-click-modal="false">
      <el-form ref="inspectionFormRef"
               :model="inspectionForm"
               :rules="inspectionRules"
               label-width="120px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="巡检编号"
                          prop="inspectionCode">
              <el-input v-model="inspectionForm.inspectionCode"
                        placeholder="请输入巡检编号"
                        :disabled="isInspectionView" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="巡检类型"
                          prop="inspectionType">
              <el-select v-model="inspectionForm.inspectionType"
                         placeholder="请选择巡检类型"
                         style="width: 100%"
                         :disabled="isInspectionView">
                <el-option label="定期巡检" value="定期巡检" />
                <el-option label="临时巡检" value="临时巡检" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="计划巡检时间"
                      prop="planTime">
          <el-date-picker v-model="inspectionForm.planTime"
                          type="datetime"
                          placeholder="请选择计划巡检时间"
                          format="YYYY-MM-DD HH:mm:ss"
                          value-format="YYYY-MM-DD HH:mm:ss"
                          style="width: 100%"
                          :disabled="isInspectionView" />
        </el-form-item>
               label-position="top"
               class="inspection-form">
        <div v-if="inspectionForm.facilityName || inspectionForm.facilityCode"
             class="dialog-summary">
          <div class="summary-item">
            <span>设施名称</span>
            <strong>{{ inspectionForm.facilityName || '-' }}</strong>
          </div>
          <div class="summary-item">
            <span>设施编号</span>
            <strong>{{ inspectionForm.facilityCode || '-' }}</strong>
          </div>
        </div>
        <div class="inspection-form-grid">
          <el-form-item label="巡检编号"
                        prop="inspectionCode">
            <el-input v-model="inspectionForm.inspectionCode"
                      placeholder="请输入巡检编号"
                      :disabled="isInspectionBaseReadonly" />
          </el-form-item>
          <el-form-item label="巡检类型"
                        prop="inspectionType">
            <el-select v-model="inspectionForm.inspectionType"
                       placeholder="请选择巡检类型"
                       style="width: 100%"
                       :disabled="isInspectionBaseReadonly">
              <el-option label="定期巡检" value="定期巡检" />
              <el-option label="临时巡检" value="临时巡检" />
            </el-select>
          </el-form-item>
          <el-form-item label="计划巡检时间"
                        prop="planTime"
                        class="form-grid-full">
            <el-date-picker v-model="inspectionForm.planTime"
                            type="datetime"
                            placeholder="请选择计划巡检时间"
                            format="YYYY-MM-DD HH:mm:ss"
                            value-format="YYYY-MM-DD HH:mm:ss"
                            style="width: 100%"
                            :disabled="isInspectionBaseReadonly" />
          </el-form-item>
        </div>
        <template v-if="inspectionDialogType !== 'add'">
          <el-form-item label="检查结果"
                        prop="checkResult">
@@ -311,7 +329,8 @@
              <el-radio label="异常">异常</el-radio>
            </el-radio-group>
          </el-form-item>
          <el-form-item label="检查说明">
          <el-form-item label="检查说明"
                        prop="checkDesc">
            <el-input v-model="inspectionForm.checkDesc"
                      type="textarea"
                      :rows="3"
@@ -343,47 +362,111 @@
               :close-on-click-modal="false">
      <el-form ref="rectificationFormRef"
               :model="rectificationForm"
               :rules="rectificationRules"
               label-width="120px">
        <template v-if="rectificationDialogTitle !== '验收'">
          <el-form-item label="问题描述">
            <el-input v-model="rectificationForm.problemDesc"
                      type="textarea"
                      :rows="3"
                      placeholder="请输入问题描述"
                      :disabled="isRectificationView" />
          </el-form-item>
          <el-form-item label="问题等级">
            <el-select v-model="rectificationForm.problemLevel"
                       placeholder="请选择问题等级"
                       style="width: 100%"
                       :disabled="isRectificationView">
              <el-option label="一般" value="一般" />
              <el-option label="重大" value="重大" />
            </el-select>
          </el-form-item>
          <el-form-item label="计划整改时间">
            <el-date-picker v-model="rectificationForm.planTime"
                            type="datetime"
                            placeholder="请选择计划整改时间"
                            format="YYYY-MM-DD HH:mm:ss"
                            value-format="YYYY-MM-DD HH:mm:ss"
                            style="width: 100%"
                            :disabled="isRectificationView" />
          </el-form-item>
        </template>
        <el-form-item label="整改说明" v-if="rectificationDialogTitle === '整改'">
        <div v-if="rectificationForm.facilityName || rectificationForm.inspectionCode"
             class="dialog-summary">
          <div class="summary-item">
            <span>设施名称</span>
            <strong>{{ rectificationForm.facilityName || '-' }}</strong>
          </div>
          <div class="summary-item">
            <span>巡检编号</span>
            <strong>{{ rectificationForm.inspectionCode || '-' }}</strong>
          </div>
        </div>
        <el-form-item label="问题描述"
                      :prop="isRectificationCreate ? 'problemDesc' : undefined">
          <el-input v-model="rectificationForm.problemDesc"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入问题描述"
                    :disabled="!isRectificationCreate" />
        </el-form-item>
        <el-form-item v-if="rectificationInspectionImageUrls.length"
                      label="巡检异常图片">
          <div class="rectification-image-list">
            <el-image v-for="(url, index) in rectificationInspectionImageUrls"
                      :key="`${url}-${index}`"
                      :src="url"
                      :preview-src-list="rectificationInspectionImageUrls"
                      :initial-index="index"
                      fit="cover"
                      class="rectification-image" />
          </div>
        </el-form-item>
        <el-form-item label="问题等级"
                      :prop="isRectificationCreate ? 'problemLevel' : undefined">
          <el-select v-model="rectificationForm.problemLevel"
                     placeholder="请选择问题等级"
                     style="width: 100%"
                     :disabled="!isRectificationCreate">
            <el-option label="一般" value="一般" />
            <el-option label="重大" value="重大" />
          </el-select>
        </el-form-item>
        <el-form-item label="计划整改时间"
                      :prop="isRectificationCreate ? 'planTime' : undefined">
          <el-date-picker v-model="rectificationForm.planTime"
                          type="datetime"
                          placeholder="请选择计划整改时间"
                          format="YYYY-MM-DD HH:mm:ss"
                          value-format="YYYY-MM-DD HH:mm:ss"
                          style="width: 100%"
                          :disabled="!isRectificationCreate" />
        </el-form-item>
        <el-form-item label="整改责任人"
                      :prop="isRectificationCreate ? 'rectifyUserId' : undefined">
          <el-select v-if="isRectificationCreate"
                     v-model="rectificationForm.rectifyUserId"
                     placeholder="请选择整改责任人"
                     filterable
                     clearable
                     style="width: 100%">
            <el-option v-for="item in userList"
                       :key="item.userId"
                       :label="item.nickName"
                       :value="item.userId" />
          </el-select>
          <el-input v-else
                    :model-value="rectificationForm.rectifyUserName || getUserName(rectificationForm.rectifyUserId) || '-'"
                    disabled />
        </el-form-item>
        <el-form-item v-if="showVerifyUser"
                      label="验收人"
                      :prop="isRectificationFix ? 'verifyUserId' : undefined">
          <el-select v-if="isRectificationFix"
                     v-model="rectificationForm.verifyUserId"
                     placeholder="请选择验收人"
                     filterable
                     clearable
                     style="width: 100%">
            <el-option v-for="item in userList"
                       :key="item.userId"
                       :label="item.nickName"
                       :value="item.userId" />
          </el-select>
          <el-input v-else
                    :model-value="rectificationForm.verifyUserName || getUserName(rectificationForm.verifyUserId) || '-'"
                    disabled />
        </el-form-item>
        <el-form-item v-if="showRectifyDesc"
                      label="整改说明"
                      :prop="isRectificationFix ? 'rectifyDesc' : undefined">
          <el-input v-model="rectificationForm.rectifyDesc"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入整改说明"
                    :disabled="isRectificationView" />
                    :disabled="!isRectificationFix" />
        </el-form-item>
        <el-form-item label="验收说明" v-if="rectificationDialogTitle === '验收'">
        <el-form-item v-if="showVerifyDesc"
                      label="验收说明"
                      :prop="isRectificationVerify ? 'verifyDesc' : undefined">
          <el-input v-model="rectificationForm.verifyDesc"
                    type="textarea"
                    :rows="3"
                    placeholder="请输入验收说明"
                    :disabled="isRectificationView" />
                    :disabled="!isRectificationVerify" />
        </el-form-item>
      </el-form>
      <template #footer>
@@ -400,10 +483,12 @@
<script setup>
import { Search } from "@element-plus/icons-vue";
import { onMounted, ref, reactive, toRefs, computed } from "vue";
import { onMounted, ref, reactive, toRefs, computed, provide } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import PIMTable from "@/components/PIMTable/PIMTable.vue";
import ImageUpload from "@/components/AttachmentUpload/image/index.vue";
import { userListNoPage } from "@/api/system/user.js";
import useUserStore from "@/store/modules/user";
import {
  getFacilityLedgerList,
  addFacilityLedger,
@@ -418,6 +503,10 @@
  updateFacilityRectification
} from "@/api/safeProduction/safetyFacility";
const userStore = useUserStore();
const userList = ref([]);
const currentUserId = computed(() => Number(userStore.id) || null);
// 表单验证规则
const ledgerRules = {
  facilityCode: [{ required: true, message: "请输入设施编号", trigger: "blur" }],
@@ -427,7 +516,20 @@
};
const inspectionRules = {
  inspectionCode: [{ required: true, message: "请输入巡检编号", trigger: "blur" }]
  inspectionCode: [{ required: true, message: "请输入巡检编号", trigger: "blur" }],
  inspectionType: [{ required: true, message: "请选择巡检类型", trigger: "change" }],
  planTime: [{ required: true, message: "请选择计划巡检时间", trigger: "change" }],
  checkResult: [{ required: true, message: "请选择检查结果", trigger: "change" }]
};
const rectificationRules = {
  problemDesc: [{ required: true, message: "请输入问题描述", trigger: "blur" }],
  problemLevel: [{ required: true, message: "请选择问题等级", trigger: "change" }],
  planTime: [{ required: true, message: "请选择计划整改时间", trigger: "change" }],
  rectifyUserId: [{ required: true, message: "请选择整改责任人", trigger: "change" }],
  verifyUserId: [{ required: true, message: "请选择验收人", trigger: "change" }],
  rectifyDesc: [{ required: true, message: "请输入整改说明", trigger: "blur" }],
  verifyDesc: [{ required: true, message: "请输入验收说明", trigger: "blur" }]
};
// 响应式数据
@@ -454,7 +556,7 @@
  inspectionTableData: [],
  inspectionForm: {
    id: null, facilityId: null, inspectionCode: "", inspectionType: "定期巡检",
    planTime: "", checkResult: "", checkDesc: "", storageBlobDTOs: []
    facilityCode: "", facilityName: "", planTime: "", checkResult: "", checkDesc: "", storageBlobDTOs: []
  },
  inspectionDialogVisible: false,
  inspectionDialogTitle: "",
@@ -466,7 +568,10 @@
  rectificationTableData: [],
  rectificationForm: {
    id: null, inspectionId: null, facilityId: null, problemDesc: "",
    problemLevel: "", planTime: "", rectifyDesc: "", verifyDesc: ""
    problemLevel: "", rectifyUserId: "", rectifyUserName: "", verifyUserId: "",
    verifyUserName: "", facilityName: "", facilityCode: "", inspectionCode: "",
    planTime: "", actualTime: "", rectifyDesc: "", verifyTime: "", verifyDesc: "",
    status: "", storageBlobVOs: []
  },
  rectificationDialogVisible: false,
  rectificationDialogTitle: "",
@@ -487,8 +592,41 @@
const ledgerFormRef = ref();
const inspectionFormRef = ref();
const rectificationFormRef = ref();
const isLedgerView = computed(() => ledgerDialogType.value === "view");
const isInspectionView = computed(() => inspectionDialogType.value === "view");
const isInspectionBaseReadonly = computed(() => inspectionDialogType.value !== "add");
const isRectificationView = computed(() => rectificationDialogType.value === "view");
const isRectificationCreate = computed(() => rectificationDialogType.value === "add");
const isRectificationFix = computed(() => rectificationDialogType.value === "do");
const isRectificationVerify = computed(() => rectificationDialogType.value === "verify");
const showRectifyDesc = computed(
  () =>
    isRectificationFix.value ||
    isRectificationVerify.value ||
    (isRectificationView.value && !!rectificationForm.value.rectifyDesc)
);
const showVerifyDesc = computed(
  () =>
    isRectificationVerify.value ||
    (isRectificationView.value &&
      (rectificationForm.value.status === "已验收" || !!rectificationForm.value.verifyDesc))
);
const showVerifyUser = computed(
  () =>
    isRectificationFix.value ||
    isRectificationVerify.value ||
    isRectificationView.value ||
    !!rectificationForm.value.verifyUserId
);
const getAttachmentUrl = item => {
  if (!item) return "";
  if (typeof item === "string") return item;
  return item.url || item.previewURL || item.previewUrl || item.downloadURL || "";
};
const rectificationInspectionImageUrls = computed(() => {
  const list = rectificationForm.value.storageBlobVOs || rectificationForm.value.storageBlobDTOs || [];
  return list.map(getAttachmentUrl).filter(Boolean);
});
// 自动计算下次检查时间
const computedNextCheckTime = computed(() => {
@@ -499,44 +637,134 @@
  return date.toISOString().split("T")[0];
});
const padTime = value => String(value).padStart(2, "0");
const formatDateTime = (date = new Date()) => {
  const target = date instanceof Date ? date : new Date(date);
  return `${target.getFullYear()}-${padTime(target.getMonth() + 1)}-${padTime(target.getDate())} ${padTime(target.getHours())}:${padTime(target.getMinutes())}:${padTime(target.getSeconds())}`;
};
const formatCompactDateTime = (date = new Date()) => {
  const target = date instanceof Date ? date : new Date(date);
  return `${target.getFullYear()}${padTime(target.getMonth() + 1)}${padTime(target.getDate())}${padTime(target.getHours())}${padTime(target.getMinutes())}`;
};
const addDaysDateTime = days => {
  const target = new Date();
  target.setDate(target.getDate() + days);
  target.setHours(18, 0, 0, 0);
  return formatDateTime(target);
};
const getDefaultPlanTime = row => {
  if (row?.nextCheckTime) return `${row.nextCheckTime} 09:00:00`;
  return formatDateTime();
};
const buildInspectionCode = row => {
  const code = row?.facilityCode || "FAC";
  return `XJ-${code}-${formatCompactDateTime()}`;
};
const getUserName = userId => {
  const user = userList.value.find(item => Number(item.userId) === Number(userId));
  return user?.nickName || "";
};
const isScrappedFacility = row => row?.status === "报废";
const isPendingInspection = row => row?.status === "待巡检";
const isCheckedInspection = row => row?.status === "已巡检";
const isAbnormalInspection = row => row?.checkResult === "异常";
const isPendingRectification = row => row?.status === "待整改" || row?.status === "整改中";
const isRectified = row => row?.status === "已整改";
const isNotAssignedVerifier = row =>
  row?.verifyUserId && Number(row.verifyUserId) !== Number(currentUserId.value);
provide("parentMethods", {
  openLedgerDetail: row => openLedgerForm("view", row)
});
// 设施台账表格列配置
const ledgerTableColumn = ref([
  { label: "设施编号", prop: "facilityCode", showOverflowTooltip: true, minWidth: 120 },
  { label: "设施名称", prop: "facilityName", showOverflowTooltip: true, minWidth: 150 },
  { label: "设施类型", prop: "facilityType", showOverflowTooltip: true, minWidth: 100 },
  { label: "规格型号", prop: "facilitySpec", showOverflowTooltip: true, minWidth: 120 },
  { label: "安装位置", prop: "installLocation", showOverflowTooltip: true, minWidth: 150 },
  { label: "安装时间", prop: "installTime", minWidth: 120 },
  { label: "下次检查时间", prop: "nextCheckTime", minWidth: 120 },
  {
    label: "状态", prop: "status", minWidth: 80, dataType: "tag",
    label: "设施编号",
    prop: "facilityCode",
    dataType: "link",
    linkMethod: "openLedgerDetail",
    showOverflowTooltip: true,
    minWidth: 120,
    align: "center",
  },
  { label: "设施名称", prop: "facilityName", showOverflowTooltip: true, minWidth: 150 , align: "center"},
  { label: "设施类型", prop: "facilityType", showOverflowTooltip: true, minWidth: 120 , align: "center"},
  { label: "规格型号", prop: "facilitySpec", showOverflowTooltip: true, minWidth: 120 , align: "center"},
  { label: "安装位置", prop: "installLocation", showOverflowTooltip: true, minWidth: 150 , align: "center"},
  { label: "安装时间", prop: "installTime", minWidth: 120 , align: "center"},
  { label: "下次检查时间", prop: "nextCheckTime", minWidth: 120 , align: "center"},
  {
    label: "状态", prop: "status", minWidth: 100, dataType: "tag", align: "center",
    formatType: params => {
      const map = { 正常: "success", 异常: "danger", 报废: "info" };
      return map[params] || "info";
    }
  },
  {
    dataType: "action", label: "操作", align: "center", fixed: "right", width: 200,
    dataType: "action", label: "操作", align: "center", fixed: "right", width: 190,
    operation: [
      { name: "编辑", type: "text", clickFun: row => openLedgerForm("edit", row) },
      { name: "发起巡检", type: "text", clickFun: row => openInspectionForm("add", row) },
      { name: "删除", type: "text", style: { color: "#F56C6C" }, clickFun: row => handleDeleteLedger(row) }
      {
        name: "发起巡检",
        type: "text",
        show: row => !isScrappedFacility(row),
        clickFun: row => openInspectionForm("add", row)
      },
      { name: "删除", type: "text", clickFun: row => handleDeleteLedger(row) }
    ]
  }
]);
// 巡检记录表格列配置
const inspectionTableColumn = ref([
  { label: "巡检编号", prop: "inspectionCode", showOverflowTooltip: true, minWidth: 150 },
  { label: "设施名称", prop: "facilityName", showOverflowTooltip: true, minWidth: 150 },
  { label: "设施编号", prop: "facilityCode", showOverflowTooltip: true, minWidth: 120 },
  { label: "巡检类型", prop: "inspectionType", minWidth: 100 },
  { label: "计划巡检时间", prop: "planTime", minWidth: 180 },
  { label: "巡检人", prop: "inspectorName", minWidth: 100 },
  { label: "实际巡检时间", prop: "actualTime", minWidth: 180 },
  {
    label: "巡检编号",
    width: 200,
    align: "center",
    prop: "inspectionCode",
    showOverflowTooltip: true,
    minWidth: 150
  },
  {
    label: "设施名称",
    prop: "facilityName",
    showOverflowTooltip: true,
    minWidth: 150
  },
  {
    label: "设施编号",
    prop: "facilityCode",
    showOverflowTooltip: true,
    minWidth: 120
  },
  {
    label: "巡检类型",
    prop: "inspectionType",
    minWidth: 100
  },
  {
    label: "计划巡检时间",
    prop: "planTime",
    minWidth: 180
  },
  {
    label: "巡检人",
    prop: "inspectorName",
    minWidth: 100
  },
  {
    label: "实际巡检时间",
    prop: "actualTime",
    minWidth: 180
  },
  {
    label: "检查结果", prop: "checkResult", minWidth: 100, dataType: "tag",
    formatType: params => params === "正常" ? "success" : "danger"
    formatData: params => params || "未填写",
    formatType: params => {
      const map = { 正常: "success", 异常: "danger", 未填写: "info" };
      return map[params] || "info";
    }
  },
  {
    label: "状态", prop: "status", minWidth: 100, dataType: "tag",
@@ -548,25 +776,26 @@
      {
        name: "巡检", type: "text",
        clickFun: row => openInspectionForm("do", row),
        show: row => row.status === "待巡检"
        show: isPendingInspection
      },
      {
        name: "查看", type: "text",
        clickFun: row => openInspectionForm("view", row),
        show: row => row.status !== "待巡检"
        show: isCheckedInspection
      },
      {
        name: "整改", type: "text",
        clickFun: row => openRectificationForm("add", row),
        show: row => row.checkResult === "异常"
        clickFun: row => openRectificationFromInspection(row),
        show: row => isCheckedInspection(row) && isAbnormalInspection(row)
      },
      { name: "删除", type: "text", style: { color: "#F56C6C" }, clickFun: row => handleDeleteInspection(row), disabled: row => row.status !== "待巡检" }
      { name: "删除", type: "text", clickFun: row => handleDeleteInspection(row), show: isPendingInspection }
    ]
  }
]);
// 整改跟踪表格列配置
const rectificationTableColumn = ref([
  { label: "巡检编号", prop: "inspectionCode", showOverflowTooltip: true, minWidth: 150 },
  { label: "设施名称", prop: "facilityName", showOverflowTooltip: true, minWidth: 150 },
  { label: "问题描述", prop: "problemDesc", showOverflowTooltip: true, minWidth: 200 },
  {
@@ -575,6 +804,7 @@
  },
  { label: "整改责任人", prop: "rectifyUserName", minWidth: 120 },
  { label: "计划整改时间", prop: "planTime", minWidth: 180 },
  { label: "实际整改时间", prop: "actualTime", minWidth: 180 },
  {
    label: "状态", prop: "status", minWidth: 100, dataType: "tag",
    formatType: params => {
@@ -583,18 +813,20 @@
    }
  },
  { label: "验收人", prop: "verifyUserName", minWidth: 120 },
  { label: "验收时间", prop: "verifyTime", minWidth: 180 },
  {
    dataType: "action", label: "操作", align: "center", fixed: "right", width: 200,
    dataType: "action", label: "操作", align: "center", fixed: "right", width: 180,
    operation: [
      {
        name: "整改", type: "text",
        clickFun: row => openRectificationForm("do", row),
        show: row => row.status === "待整改" || row.status === "整改中"
        show: isPendingRectification
      },
      {
        name: "验收", type: "text",
        clickFun: row => openRectificationForm("verify", row),
        show: row => row.status === "已整改"
        show: isRectified,
        disabled: isNotAssignedVerifier
      },
      { name: "查看", type: "text", clickFun: row => openRectificationForm("view", row) }
    ]
@@ -606,6 +838,7 @@
  getLedgerList();
  getInspectionList();
  getRectificationList();
  getUserList();
});
// 标签页切换
@@ -615,6 +848,12 @@
  else if (activeTab.value === "rectification") getRectificationList();
};
const getUserList = () => {
  userListNoPage().then(res => {
    userList.value = res.data || [];
  });
};
// ============ 设施台账方法 ============
const handleLedgerQuery = () => {
@@ -622,13 +861,23 @@
  getLedgerList();
};
const resetLedgerQuery = () => {
  Object.assign(ledgerSearchForm.value, {
    facilityCode: "",
    facilityName: "",
    facilityType: "",
    status: ""
  });
  handleLedgerQuery();
};
const getLedgerList = () => {
  ledgerTableLoading.value = true;
  getFacilityLedgerList({ ...ledgerPage.value, ...ledgerSearchForm.value })
    .then(res => {
      ledgerTableLoading.value = false;
      ledgerTableData.value = res.data.records;
      ledgerPage.value.total = res.data.total;
      ledgerTableData.value = res.data?.records || [];
      ledgerPage.value.total = res.data?.total || 0;
    })
    .catch(() => { ledgerTableLoading.value = false; });
};
@@ -652,6 +901,9 @@
      installLocation: "", installTime: "", productionDate: "", validPeriod: null,
      status: "正常", remark: ""
    });
  } else if (type === "view") {
    ledgerDialogTitle.value = "设施详情";
    Object.assign(ledgerForm.value, { ...row });
  } else {
    ledgerDialogTitle.value = "编辑设施";
    Object.assign(ledgerForm.value, { ...row });
@@ -692,6 +944,27 @@
  }).catch(() => {});
};
const handleBatchDeleteLedger = () => {
  if (ledgerSelectedIds.value.length === 0) {
    ElMessage.warning("请选择要删除的设施");
    return;
  }
  ElMessageBox.confirm("确认删除选中的设施吗?已产生巡检记录的设施不会被后端允许删除。", "批量删除", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning"
  }).then(async () => {
    const res = await deleteFacilityLedger(ledgerSelectedIds.value);
    if (res.code === 200) {
      ElMessage.success("删除成功");
      ledgerSelectedIds.value = [];
      getLedgerList();
    } else {
      ElMessage.error(res.msg || "删除失败");
    }
  }).catch(() => {});
};
// ============ 巡检记录方法 ============
const handleInspectionQuery = () => {
@@ -699,13 +972,21 @@
  getInspectionList();
};
const resetInspectionQuery = () => {
  Object.assign(inspectionSearchForm.value, {
    inspectionCode: "",
    status: ""
  });
  handleInspectionQuery();
};
const getInspectionList = () => {
  inspectionTableLoading.value = true;
  getFacilityInspectionList({ ...inspectionPage.value, ...inspectionSearchForm.value })
    .then(res => {
      inspectionTableLoading.value = false;
      inspectionTableData.value = res.data.records;
      inspectionPage.value.total = res.data.total;
      inspectionTableData.value = res.data?.records || [];
      inspectionPage.value.total = res.data?.total || 0;
    })
    .catch(() => { inspectionTableLoading.value = false; });
};
@@ -721,14 +1002,30 @@
  if (type === "add") {
    inspectionDialogTitle.value = "发起巡检";
    Object.assign(inspectionForm.value, {
      id: null, facilityId: row.id, inspectionCode: "", inspectionType: "定期巡检",
      planTime: "", checkResult: "", checkDesc: "", storageBlobDTOs: []
      id: null,
      facilityId: row.id,
      facilityCode: row.facilityCode,
      facilityName: row.facilityName,
      inspectionCode: buildInspectionCode(row),
      inspectionType: "定期巡检",
      planTime: getDefaultPlanTime(row),
      checkResult: "",
      checkDesc: "",
      storageBlobDTOs: []
    });
  } else if (type === "do") {
    inspectionDialogTitle.value = "巡检";
    Object.assign(inspectionForm.value, {
      id: row.id, facilityId: row.facilityId, inspectionCode: row.inspectionCode,
      inspectionType: row.inspectionType, planTime: row.planTime, checkResult: "", checkDesc: "", storageBlobDTOs: []
      id: row.id,
      facilityId: row.facilityId,
      facilityCode: row.facilityCode,
      facilityName: row.facilityName,
      inspectionCode: row.inspectionCode,
      inspectionType: row.inspectionType,
      planTime: row.planTime,
      checkResult: "",
      checkDesc: "",
      storageBlobDTOs: []
    });
  } else {
    inspectionDialogTitle.value = "查看巡检";
@@ -740,20 +1037,42 @@
const submitInspectionForm = async () => {
  try {
    await inspectionFormRef.value.validate();
    if (inspectionDialogTitle.value === "巡检") {
    if (inspectionDialogType.value === "do") {
      if (!inspectionForm.value.checkResult) {
        ElMessage.warning("请选择检查结果");
        return;
      }
      if (inspectionForm.value.checkResult === "异常" && !inspectionForm.value.checkDesc) {
        ElMessage.warning("检查结果异常时请填写检查说明");
        return;
      }
      if (inspectionForm.value.checkResult === "异常" && !hasAttachments(inspectionForm.value.storageBlobDTOs)) {
        ElMessage.warning("检查结果异常时请上传现场照片");
        return;
      }
      inspectionForm.value.status = "已巡检";
      inspectionForm.value.actualTime = new Date().toISOString().replace("T", " ").substring(0, 19);
      inspectionForm.value.actualTime = formatDateTime();
    }
    const api = inspectionForm.value.id ? updateFacilityInspection : addFacilityInspection;
    const res = await api({ ...inspectionForm.value });
    if (res.code === 200) {
      ElMessage.success("操作成功");
      inspectionDialogVisible.value = false;
      if (inspectionDialogType.value === "add") {
        activeTab.value = "inspection";
      }
      getInspectionList();
      if (inspectionDialogType.value === "do" && inspectionForm.value.checkResult === "异常") {
        ElMessageBox.confirm("本次巡检结果为异常,是否立即创建整改跟踪?", "创建整改", {
          confirmButtonText: "创建整改",
          cancelButtonText: "稍后处理",
          type: "warning"
        }).then(() => {
          openRectificationFromInspection({ ...inspectionForm.value });
        }).catch(() => {});
      }
    } else {
      ElMessage.error(res.msg || "操作失败");
    }
  } catch (error) {
    console.error("表单验证失败:", error);
@@ -783,13 +1102,20 @@
  getRectificationList();
};
const resetRectificationQuery = () => {
  Object.assign(rectificationSearchForm.value, {
    status: ""
  });
  handleRectificationQuery();
};
const getRectificationList = () => {
  rectificationTableLoading.value = true;
  getFacilityRectificationList({ ...rectificationPage.value, ...rectificationSearchForm.value })
    .then(res => {
      rectificationTableLoading.value = false;
      rectificationTableData.value = res.data.records;
      rectificationPage.value.total = res.data.total;
      rectificationTableData.value = res.data?.records || [];
      rectificationPage.value.total = res.data?.total || 0;
    })
    .catch(() => { rectificationTableLoading.value = false; });
};
@@ -800,33 +1126,117 @@
  getRectificationList();
};
const getExistingRectification = async inspectionId => {
  if (!inspectionId) return null;
  const res = await getFacilityRectificationList({ current: 1, size: 1, inspectionId });
  return res.data?.records?.[0] || null;
};
const getRectificationDialogType = row => {
  if (isPendingRectification(row)) return "do";
  if (isRectified(row)) return "verify";
  return "view";
};
const openRectificationFromInspection = async row => {
  const existing = await getExistingRectification(row.id);
  activeTab.value = "rectification";
  if (existing) {
    ElMessage.info("该巡检已存在整改记录,已为你打开对应处理窗口");
    existing.inspectionCode = existing.inspectionCode || row.inspectionCode;
    existing.facilityCode = existing.facilityCode || row.facilityCode;
    existing.facilityName = existing.facilityName || row.facilityName;
    openRectificationForm(getRectificationDialogType(existing), existing);
    return;
  }
  openRectificationForm("add", row);
};
const openRectificationForm = (type, row = null) => {
  rectificationDialogType.value = type;
  if (type === "add") {
    rectificationDialogTitle.value = "新增整改";
    Object.assign(rectificationForm.value, {
      id: null, inspectionId: row.id, facilityId: row.facilityId,
      problemDesc: "", problemLevel: "", planTime: "", rectifyDesc: "", verifyDesc: ""
      id: null,
      inspectionId: row.id,
      facilityId: row.facilityId,
      facilityName: row.facilityName || "",
      facilityCode: row.facilityCode || "",
      inspectionCode: row.inspectionCode || "",
      problemDesc: row.checkDesc || "",
      problemLevel: "一般",
      rectifyUserId: currentUserId.value || "",
      rectifyUserName: getUserName(currentUserId.value),
      verifyUserId: "",
      verifyUserName: "",
      planTime: addDaysDateTime(1),
      actualTime: "",
      rectifyDesc: "",
      verifyTime: "",
      verifyDesc: "",
      status: "待整改",
      storageBlobVOs: row.storageBlobVOs || row.storageBlobDTOs || []
    });
  } else if (type === "do") {
    rectificationDialogTitle.value = "整改";
    Object.assign(rectificationForm.value, {
      id: row.id, inspectionId: row.inspectionId, facilityId: row.facilityId,
      problemDesc: row.problemDesc, problemLevel: row.problemLevel, planTime: row.planTime || "",
      rectifyDesc: "", verifyDesc: ""
      ...row,
      storageBlobVOs: row.storageBlobVOs || row.storageBlobDTOs || [],
      rectifyDesc: row.rectifyDesc || "",
      verifyDesc: row.verifyDesc || ""
    });
  } else if (type === "verify") {
    rectificationDialogTitle.value = "验收";
    Object.assign(rectificationForm.value, {
      id: row.id, inspectionId: row.inspectionId, facilityId: row.facilityId,
      problemDesc: row.problemDesc, problemLevel: row.problemLevel,
      rectifyDesc: row.rectifyDesc, verifyDesc: ""
      ...row,
      storageBlobVOs: row.storageBlobVOs || row.storageBlobDTOs || [],
      verifyDesc: row.verifyDesc || ""
    });
  } else {
    rectificationDialogTitle.value = "查看整改";
    Object.assign(rectificationForm.value, { ...row });
    Object.assign(rectificationForm.value, {
      ...row,
      storageBlobVOs: row.storageBlobVOs || row.storageBlobDTOs || []
    });
  }
  rectificationDialogVisible.value = true;
};
const buildRectificationPayload = () => {
  const {
    id,
    inspectionId,
    facilityId,
    problemDesc,
    problemLevel,
    problemImage,
    rectifyUserId,
    planTime,
    actualTime,
    rectifyDesc,
    rectifyImage,
    status,
    verifyUserId,
    verifyTime,
    verifyDesc
  } = rectificationForm.value;
  return {
    id,
    inspectionId,
    facilityId,
    problemDesc,
    problemLevel,
    problemImage,
    rectifyUserId,
    planTime,
    actualTime,
    rectifyDesc,
    rectifyImage,
    status,
    verifyUserId,
    verifyTime,
    verifyDesc
  };
};
const submitRectificationForm = async () => {
@@ -834,27 +1244,91 @@
    rectificationDialogVisible.value = false;
    return;
  }
  if (rectificationDialogTitle.value === "整改") {
  try {
    await rectificationFormRef.value.validate();
  } catch (error) {
    console.error("表单验证失败:", error);
    return;
  }
  if (rectificationDialogType.value === "do") {
    rectificationForm.value.status = "已整改";
    rectificationForm.value.actualTime = new Date().toISOString().replace("T", " ").substring(0, 19);
  } else if (rectificationDialogTitle.value === "验收") {
    rectificationForm.value.actualTime = formatDateTime();
  } else if (rectificationDialogType.value === "verify") {
    rectificationForm.value.status = "已验收";
    rectificationForm.value.verifyTime = new Date().toISOString().replace("T", " ").substring(0, 19);
    rectificationForm.value.verifyTime = formatDateTime();
  }
  const api = rectificationForm.value.id ? updateFacilityRectification : addFacilityRectification;
  const res = await api({ ...rectificationForm.value });
  const res = await api(buildRectificationPayload());
  if (res.code === 200) {
    ElMessage.success("操作成功");
    rectificationDialogVisible.value = false;
    getRectificationList();
    getInspectionList();
  } else {
    ElMessage.error(res.msg || "操作失败");
  }
};
</script>
<style scoped>
.safety-facility-page {
  padding-top: 4px;
}
.facility-tabs {
  width: 100%;
}
.facility-tabs :deep(.el-tabs__header) {
  margin-bottom: 18px;
}
.facility-tabs :deep(.el-tabs__nav-wrap::after) {
  height: 1px;
  background-color: #d8e2ef;
}
.facility-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 14px 18px;
  min-height: 44px;
  margin-bottom: 18px;
}
.toolbar-filters,
.toolbar-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 12px;
}
.toolbar-actions {
  margin-left: auto;
}
.table_list {
  padding-top: 6px;
}
.table_list :deep(.link) {
  color: var(--el-color-primary);
  cursor: pointer;
  font-weight: 600;
}
.table_list :deep(.link:hover) {
  text-decoration: underline;
}
.search_title {
  font-size: 14px;
  font-weight: 600;
  color: #606266;
  white-space: nowrap;
}
.mb20 {
@@ -864,4 +1338,90 @@
.ml10 {
  margin-left: 10px;
}
.toolbar-filters .ml10 {
  margin-left: 0;
}
.facility-form-dialog :deep(.el-dialog__body) {
  padding: 24px 32px 10px;
}
.facility-form-dialog :deep(.el-dialog__footer) {
  padding: 8px 32px 26px;
}
.dialog-summary {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 20px;
  padding: 14px 18px;
  border: 1px solid #e4eaf3;
  border-radius: 6px;
  background: #f7faff;
}
.summary-item {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.summary-item span {
  flex: 0 0 auto;
  color: #7a8599;
}
.summary-item strong {
  min-width: 0;
  overflow: hidden;
  color: #25324b;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.inspection-form-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(220px, 0.8fr);
  column-gap: 20px;
}
.inspection-form-grid .form-grid-full {
  grid-column: 1 / -1;
}
.inspection-form :deep(.el-form-item) {
  margin-bottom: 20px;
}
.inspection-form :deep(.el-form-item__label) {
  margin-bottom: 8px;
  padding: 0;
  font-weight: 600;
  color: #25324b;
  line-height: 1.2;
}
.rectification-image-list {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.rectification-image {
  width: 96px;
  height: 96px;
  border: 1px solid #dbe4f0;
  border-radius: 6px;
  background: #f8fafc;
}
@media (max-width: 900px) {
  .inspection-form-grid,
  .dialog-summary {
    grid-template-columns: 1fr;
  }
}
</style>