gongchunyi
6 天以前 6f1acfd2086a1f8c2cb80afd3db4e534e44d545e
src/views/safeProduction/emergencyPlanReview/index.vue
@@ -1,6 +1,6 @@
<template>
  <div class="app-container">
    <div class="search_form">
    <div class="search_form mb20">
      <div>
        <span class="search_title">应急预案名称:</span>
        <el-input v-model="searchForm.planName"
@@ -52,17 +52,18 @@
      <el-form ref="formRef"
               :model="form"
               :rules="rules"
               label-width="120px">
               label-position="top"
               label-width="150px">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="应急预案编码"
            <el-form-item label="应急预案编码:"
                          prop="planCode">
              <el-input v-model="form.planCode"
                        placeholder="请输入应急预案编码" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="应急预案名称"
            <el-form-item label="应急预案名称:"
                          prop="planName">
              <el-input v-model="form.planName"
                        placeholder="请输入应急预案名称" />
@@ -99,7 +100,7 @@
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="预案类型"
            <el-form-item label="预案类型:"
                          prop="planType">
              <el-select v-model="form.planType"
                         placeholder="请选择预案类型"
@@ -112,24 +113,25 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="备注"
            <el-form-item label="备注:"
                          prop="remark">
              <el-input v-model="form.remark"
                        placeholder="请输入备注" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="适用范围"
        <el-form-item label="适用范围:"
                      prop="applyScope">
          <el-checkbox-group v-model="form.applyScope">
            <el-checkbox label="all">全体员工</el-checkbox>
            <el-checkbox label="manager">管理层</el-checkbox>
            <el-checkbox label="hr">人事部门</el-checkbox>
            <el-checkbox label="finance">财务部门</el-checkbox>
            <el-checkbox label="tech">技术部门</el-checkbox>
          <el-checkbox-group v-model="form.applyScope"
                             class="apply-scope-checkbox-group">
            <el-checkbox v-for="dept in applyScopeOptions"
                         :key="dept.deptId"
                         :value="String(dept.deptId)">
              {{ dept.deptName }}
            </el-checkbox>
          </el-checkbox-group>
        </el-form-item>
        <el-form-item label="应急处置步骤"
        <el-form-item label="应急处置步骤:"
                      prop="execSteps">
          <div class="exec-steps-container"
               style="width:100%">
@@ -164,9 +166,9 @@
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary"
                     @click="submitForm">确定</el-button>
          <el-button @click="dialogVisible = false">取消</el-button>
        </span>
      </template>
    </el-dialog>
@@ -192,7 +194,7 @@
            {{ currentKnowledge.coreResponsorUserName }}
          </el-descriptions-item>
          <el-descriptions-item label="预案类型">
            <el-tag type="warning"> {{ currentKnowledge.planType }}</el-tag>
            <el-tag type="warning"> {{ emergencyPlanTypeLabel(currentKnowledge.planType) }}</el-tag>
          </el-descriptions-item>
          <el-descriptions-item label="备注">
            {{ currentKnowledge.remark }}
@@ -201,11 +203,11 @@
        <div class="detail-section">
          <h4>适用范围</h4>
          <div class="key-points">
            <el-tag v-for="(point, index) in currentKnowledge.applyScope.split(',')"
                    :key="index"
            <el-tag v-for="(scope, index) in getApplyScopeTags(currentKnowledge)"
                    :key="`${scope.value}-${index}`"
                    type="primary"
                    style="margin-right: 8px; margin-bottom: 8px;">
              {{ getApplyScopeLabel(point.trim()) }}
              {{ scope.label }}
            </el-tag>
          </div>
        </div>
@@ -216,7 +218,7 @@
              <div v-for="(step, index) in JSON.parse(currentKnowledge.execSteps)"
                   :key="index"
                   class="exec-step-view">
                <span class="step-number">{{ index + 1 }}.</span>
                <!-- <span class="step-number">{{ index + 1 }}.</span> -->
                <span class="step-title">{{ step.step }}:</span>
                <span>{{ step.description }}</span>
              </div>
@@ -249,6 +251,7 @@
  import { ElMessage, ElMessageBox } from "element-plus";
  import PIMTable from "@/components/PIMTable/PIMTable.vue";
  import { userListNoPage } from "@/api/system/user.js";
  import { listDept } from "@/api/system/dept.js";
  import {
    safeContingencyPlanListPage,
    safeContingencyPlanAdd,
@@ -325,37 +328,59 @@
  // 表单引用
  const formRef = ref();
  const execStepsList = ref([]);
  const applyScopeOptions = ref([]);
  const applyScopeNameMap = computed(() => {
    return applyScopeOptions.value.reduce((map, dept) => {
      map[String(dept.deptId)] = dept.deptName;
      return map;
    }, {});
  });
  // 表格列配置
  const tableColumn = ref([
    {
      label: "应急预案编码",
      prop: "planCode",
      minWidth: 200,
      align: "center",
      showOverflowTooltip: true,
    },
    {
      label: "应急预案名称",
      prop: "planName",
      minWidth: 240,
      align: "center",
      showOverflowTooltip: true,
    },
    {
      label: "发布生效时间",
      prop: "publishTime",
      minWidth: 160,
      align: "center",
      showOverflowTooltip: true,
    },
    {
      label: "核心责任人",
      prop: "coreResponsorUserName",
      minWidth: 140,
      align: "center",
      showOverflowTooltip: true,
    },
    {
      label: "预案类型",
      prop: "planType",
      minWidth: 160,
      align: "center",
      showOverflowTooltip: true,
      formatData: params => {
        return emergencyPlanTypeLabel(params);
      },
    },
    {
      label: "备注",
      prop: "remark",
      minWidth: 220,
      align: "center",
      showOverflowTooltip: true,
    },
    {
@@ -363,7 +388,7 @@
      label: "操作",
      align: "center",
      fixed: "right",
      width: 200,
      width: 120,
      operation: [
        {
          name: "编辑",
@@ -388,6 +413,7 @@
    userListNoPage().then(res => {
      userList.value = res.data;
    });
    loadApplyScopeOptions();
    getList();
    startAutoRefresh();
  });
@@ -422,6 +448,22 @@
      execStepsList.value = [];
    }
  };
  const loadApplyScopeOptions = () => {
    listDept({ status: "0" }).then(res => {
      applyScopeOptions.value = res.data || [];
    });
  };
  const parseApplyScope = scope => {
    if (Array.isArray(scope)) {
      return scope.map(item => String(item)).filter(Boolean);
    }
    return String(scope || "")
      .split(",")
      .map(item => item.trim())
      .filter(Boolean);
  };
  // 开始自动刷新
  const startAutoRefresh = () => {
    setInterval(() => {
@@ -441,7 +483,7 @@
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        page.total = res.data.total;
        page.value.total = res.data.total;
      })
      .catch(err => {
        tableLoading.value = false;
@@ -452,7 +494,7 @@
  const pagination = obj => {
    page.value.current = obj.page;
    page.value.size = obj.limit;
    handleQuery();
    getList();
  };
  // 选择变化处理
@@ -467,6 +509,7 @@
      dialogTitle.value = "新增应急预案";
      // 重置表单
      Object.assign(form.value, {
        id: undefined,
        planCode: "", // 预案编号
        applyScope: [], // 适用范围
        planType: "", // 预案类型
@@ -483,7 +526,7 @@
      Object.assign(form.value, {
        id: row.id,
        planCode: row.planCode, // 预案编号
        applyScope: row.applyScope ? row.applyScope.split(",") : [], // 适用范围
        applyScope: parseApplyScope(row.applyScope), // 适用范围
        planType: row.planType, // 预案类型
        planName: row.planName, // 预案名称
        publishTime: row.publishTime, // 发布时间
@@ -503,14 +546,13 @@
    viewDialogVisible.value = true;
  };
  const getApplyScopeLabel = scope => {
    const scopeMap = {
      all: "全体员工",
      manager: "管理层",
      hr: "人事部门",
      finance: "财务部门",
      tech: "技术部门",
    };
    return scopeMap[scope] || scope;
    return applyScopeNameMap.value[String(scope)] || scope;
  };
  const getApplyScopeTags = row => {
    return parseApplyScope(row?.applyScope).map((scope, index) => ({
      value: scope,
      label: getApplyScopeLabel(scope),
    }));
  };
  // 获取类型标签类型
@@ -569,20 +611,17 @@
    };
    return timeMap[efficiency] || "未知";
  };
  const emergencyPlanTypeOptions = computed(() => [
    {
      value: "预防性应急预案",
      label: "预防性应急预案",
    },
    {
      value: "应急处理预案",
      label: "应急处理预案",
    },
    {
      value: "恢复性应急预案",
      label: "恢复性应急预案",
    },
  ]);
  const { proxy } = getCurrentInstance();
  const { emergency_plan_type } = proxy.useDict("emergency_plan_type");
  const emergencyPlanTypeOptions = computed(
    () => emergency_plan_type?.value || []
  );
  const emergencyPlanTypeLabel = val => {
    const item = emergencyPlanTypeOptions.value.find(
      i => String(i.value) === String(val)
    );
    return item ? item.label : val;
  };
  // 提交应急预案表单
  const submitForm = async () => {
    try {
@@ -601,12 +640,14 @@
        }
      }
      // 将应急处置步骤转换为JSON字符串
      form.value.execSteps = JSON.stringify(execStepsList.value);
      const submitData = {
        ...form.value,
        applyScope: form.value.applyScope.join(","),
        execSteps: JSON.stringify(execStepsList.value),
      };
      if (dialogType.value === "add") {
        // 新增应急预案
        form.value.applyScope = form.value.applyScope.join(",");
        safeContingencyPlanAdd({ ...form.value })
        safeContingencyPlanAdd(submitData)
          .then(res => {
            if (res.code == 200) {
              ElMessage.success("添加成功");
@@ -618,8 +659,7 @@
            ElMessage.error(err.msg);
          });
      } else {
        form.value.applyScope = form.value.applyScope.join(",");
        safeContingencyPlanUpdate({ ...form.value })
        safeContingencyPlanUpdate(submitData)
          .then(res => {
            if (res.code == 200) {
              ElMessage.success("更新成功");
@@ -664,8 +704,6 @@
  };
  // 导出
  const { proxy } = getCurrentInstance();
  const { knowledge_type } = proxy.useDict("knowledge_type");
  // 字典工具
  const knowledgeTypeOptions = computed(() => knowledge_type?.value || []);
@@ -741,6 +779,16 @@
    gap: 8px;
  }
  .apply-scope-checkbox-group {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 16px;
  }
  .apply-scope-checkbox-group :deep(.el-checkbox) {
    margin-right: 0;
  }
  .usage-stats {
    margin-top: 16px;
  }
@@ -766,45 +814,83 @@
  .exec-steps-container {
    border: 1px solid #e4e7ed;
    border-radius: 4px;
    padding: 15px;
    border-radius: 8px;
    padding: 20px;
    background-color: #f9fafc;
    margin-top: 10px;
  }
  .exec-step-item {
    margin-bottom: 10px;
    padding: 10px;
    margin-bottom: 12px;
    padding: 12px;
    background-color: #ffffff;
    border: 1px solid #e4e7ed;
    border-radius: 4px;
    border-radius: 6px;
    transition: all 0.3s ease;
  }
  .exec-step-item:hover {
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    border-color: #c6e2ff;
  }
  .step-header {
    display: flex;
    align-items: flex-start;
    flex-direction: column;
    gap: 8px;
  }
  .exec-step-view {
    margin-bottom: 8px;
    padding-left: 20px;
    margin-bottom: 16px;
    padding-left: 24px;
    position: relative;
    line-height: 1.6;
  }
  .exec-step-view::before {
    content: "";
    position: absolute;
    left: 10px;
    top: 20px;
    bottom: -16px;
    width: 2px;
    background-color: #eaeaea;
  }
  .exec-step-view:last-child::before {
    display: none;
  }
  .step-number {
    position: absolute;
    left: 0;
    top: 0;
    width: 20px;
    height: 20px;
    line-height: 20px;
    text-align: center;
    font-weight: bold;
    color: #409eff;
    color: #ffffff;
    background-color: #409eff;
    border-radius: 50%;
    font-size: 12px;
    z-index: 1;
  }
  .step-title {
    font-weight: bold;
    margin-right: 5px;
    font-weight: 600;
    margin-right: 8px;
    color: #395a9c;
  }
  .no-data {
    color: #909399;
    font-style: italic;
    text-align: center;
    padding: 20px;
    background-color: #f8f9fa;
    border-radius: 4px;
    margin-top: 10px;
  }
</style>