gongchunyi
2026-08-12 fc62e83f73b4b8ff4714f995da4eafbb7f525d1a
src/views/equipmentManagement/inspectionManagement/index.vue
@@ -24,7 +24,6 @@
                           :label="tab.label"
                           :value="tab.name" />
        </el-radio-group>
        <!-- 操作按钮区 -->
        <el-space v-if="activeRadio !== 'task'">
          <el-button type="primary"
                     :icon="Plus"
@@ -38,7 +37,9 @@
          <el-button @click="handleOut">导出</el-button>
        </el-space>
      </div>
      <div>
      <!-- 巡检任务 -->
      <div v-show="activeRadio === 'taskManage'">
        <PIMTable :table-loading="tableLoading"
                  :table-data="tableData"
                  :column="tableColumns"
@@ -52,11 +53,10 @@
                  total: total,
                  layout: 'total, sizes, prev, pager, next, jumper'
                }"
                  :table-style="{ width: '100%', height: 'calc(100vh - 23em)' }">
                  height="calc(100vh - 23em)"
                  :table-style="{ width: '100%' }">
          <template #inspector="{ row }">
            <div class="person-tags">
              <!-- 调试信息,上线时删除 -->
              <!-- {{ console.log('inspector data:', row.inspector) }} -->
              <template v-if="row.inspector && row.inspector.length > 0">
                <el-tag v-for="(person, index) in row.inspector"
                        :key="index"
@@ -66,8 +66,7 @@
                  {{ person }}
                </el-tag>
              </template>
              <span v-else
                    class="no-data">--</span>
              <span v-else class="no-data">--</span>
            </div>
          </template>
          <template #isEnabled="{ row }">
@@ -78,48 +77,124 @@
          </template>
        </PIMTable>
      </div>
      <!-- 巡检记录 - 日期分组折叠 -->
      <div v-show="activeRadio === 'task'">
        <el-table
          :data="groupedInspectionList"
          border
          v-loading="tableLoading"
          :expand-row-keys="inspectionExpandedRowKeys"
          :row-key="(row) => row.date"
          @expand-change="onInspectionExpandChange"
          height="calc(100vh - 23em)"
        >
          <el-table-column type="expand">
            <template #default="props">
              <el-table :data="props.row.children" border row-key="id">
                <el-table-column label="巡检任务名称" prop="taskName" align="center" min-width="180" />
                <el-table-column label="巡检项目" prop="inspectionProject" align="center" min-width="160" />
                <el-table-column label="巡检人" align="center" min-width="150">
                  <template #default="{ row }">
                    <template v-if="row.inspector && row.inspector.length > 0">
                      <el-tag v-for="(p, i) in row.inspector" :key="i" size="small" type="primary" class="person-tag">{{ p }}</el-tag>
                    </template>
                    <span v-else>--</span>
                  </template>
                </el-table-column>
                <el-table-column label="巡检状态" align="center" width="100">
                  <template #default="{ row }">
                    <el-tag :type="row.status === 1 ? 'success' : 'warning'" size="small">
                      {{ row.status === 1 ? '已完成' : '待巡检' }}
                    </el-tag>
                  </template>
                </el-table-column>
                <el-table-column label="巡检结果" prop="inspectionResult" align="center" min-width="120" />
                <el-table-column label="备注" prop="remarks" align="center" min-width="150" />
                <el-table-column label="异常描述" prop="abnormalDescription" align="center" min-width="150" />
                <el-table-column label="操作" align="center" width="200">
                  <template #default="{ row }">
                    <el-button type="primary" link @click="openUploadDialog(row)">上传</el-button>
                    <el-button type="success" link @click="viewFile(row)">查看附件</el-button>
                    <el-button type="warning" link v-if="row.status === 0" @click="handleSingleComplete(row)">巡检完成</el-button>
                  </template>
                </el-table-column>
              </el-table>
            </template>
          </el-table-column>
          <el-table-column label="巡检日期" prop="date" align="center" />
          <el-table-column label="巡检记录数" prop="count" align="center" />
          <el-table-column label="待巡检数" align="center" width="120">
            <template #default="{ row }">
              {{ row.children.filter(c => c.status === 0).length }}
            </template>
          </el-table-column>
          <el-table-column label="操作" align="center" width="200">
            <template #default="{ row }">
              <el-button
                type="primary"
                size="small"
                :disabled="!row.children.some(c => c.status === 0)"
                @click="handleBatchInspection(row.date)"
              >
                一键巡检完成
              </el-button>
            </template>
          </el-table-column>
        </el-table>
        <Pagination
          v-show="total > 0"
          :total="total"
          layout="total, sizes, prev, pager, next, jumper"
          :page="pageNum"
          :limit="pageSize"
          @pagination="handlePagination"
        />
      </div>
    </el-card>
    <form-dia ref="formDia"
              @closeDia="handleQuery"></form-dia>
    <view-files ref="viewFiles"></view-files>
    <upload-files ref="uploadFiles"
                  @success="handleQuery"
                  @closeDia="handleQuery"></upload-files>
  </div>
</template>
<script setup>
  import { Delete, Plus } from "@element-plus/icons-vue";
  import { onMounted, ref, reactive, getCurrentInstance, nextTick } from "vue";
  import { ElMessageBox } from "element-plus";
  import { onMounted, ref, reactive, getCurrentInstance, nextTick, computed } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import dayjs from "dayjs";
  // 组件引入
  import PIMTable from "@/components/PIMTable/PIMTable.vue";
  import FormDia from "@/views/equipmentManagement/inspectionManagement/components/formDia.vue";
  import UploadFiles from "@/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue";
  import ViewFiles from "@/views/equipmentManagement/inspectionManagement/components/viewFiles.vue";
  // 接口引入
  import {
    delTimingTask,
    inspectionTaskList,
    timingTaskList,
    completeInspection,
    batchCompleteInspection,
  } from "@/api/inspectionManagement/index.js";
  import Pagination from "@/components/PIMTable/Pagination.vue";
  // 全局变量
  const { proxy } = getCurrentInstance();
  const formDia = ref();
  const viewFiles = ref();
  const uploadFiles = ref();
  // 查询参数
  const queryParams = reactive({
    taskName: "",
  });
  const queryParams = reactive({ taskName: "" });
  // 单选框配置
  const activeRadio = ref("taskManage");
  const radios = reactive([
    { name: "taskManage", label: "定时任务管理" },
    { name: "task", label: "定时任务记录" },
    { name: "taskManage", label: "巡检任务" },
    { name: "task", label: "巡检记录" },
  ]);
  // 表格数据
  const selectedRows = ref([]);
  const tableData = ref([]);
  const operationsArr = ref([]);
@@ -127,296 +202,165 @@
  const tableLoading = ref(false);
  const total = ref(0);
  const pageNum = ref(1);
  const pageSize = ref(10);
  const pageSize = ref(100);
  // 列配置
  const columns = ref([
    { prop: "taskName", label: "巡检任务名称", minWidth: 160 },
    { prop: "inspectionProject", label: "巡检项目", minWidth: 150 },
    { prop: "remarks", label: "备注", minWidth: 150 },
    { prop: "inspector", label: "执行巡检人", minWidth: 150, slot: "inspector" },
    { prop: "taskName", label: "巡检任务名称", minWidth: 200 },
    { prop: "inspectionProject", label: "巡检项目", minWidth: 180 },
    { prop: "remarks", label: "备注", minWidth: 180 },
    { prop: "inspector", label: "执行巡检人", minWidth: 180, slot: "inspector" },
    { prop: "isEnabled", label: "是否启用", minWidth: 100, dataType: "slot", slot: "isEnabled" },
    {
      prop: "isEnabled",
      label: "是否启用",
      minWidth: 100,
      dataType: "slot",
      slot: "isEnabled",
      prop: "frequencyType", label: "频次", minWidth: 120,
      formatData: params => ({ DAILY: "每日", WEEKLY: "每周", MONTHLY: "每月", QUARTERLY: "季度" }[params] || ""),
    },
    {
      prop: "frequencyType",
      label: "频次",
      minWidth: 150,
      // formatter: (_, __, val) => ({
      //   DAILY: "每日",
      //   WEEKLY: "每周",
      //   MONTHLY: "每月",
      //   QUARTERLY: "季度"
      // }[val] || "")
      formatData: params => {
        return params === "DAILY"
          ? "每日"
          : params === "WEEKLY"
          ? "每周"
          : params === "MONTHLY"
          ? "每月"
          : params === "QUARTERLY"
          ? "季度"
          : "";
      },
    },
    {
      prop: "frequencyDetail",
      label: "开始日期与时间",
      minWidth: 150,
      prop: "frequencyDetail", label: "开始日期与时间", minWidth: 200,
      formatter: (row, column, cellValue) => {
        // 先判断是否是字符串
        if (typeof cellValue !== "string") return "";
        let val = cellValue;
        const replacements = {
          MON: "周一",
          TUE: "周二",
          WED: "周三",
          THU: "周四",
          FRI: "周五",
          SAT: "周六",
          SUN: "周日",
        };
        // 使用正则一次性替换所有匹配项
        return val.replace(
          /MON|TUE|WED|THU|FRI|SAT|SUN/g,
          match => replacements[match]
        );
        return cellValue.replace(/MON|TUE|WED|THU|FRI|SAT|SUN/g, match => ({ MON: "周一", TUE: "周二", WED: "周三", THU: "周四", FRI: "周五", SAT: "周六", SUN: "周日" }[match]));
      },
    },
    { prop: "registrant", label: "登记人", minWidth: 100 },
    { prop: "createTime", label: "登记日期", minWidth: 100 },
    { prop: "registrant", label: "登记人", minWidth: 120 },
    {
      prop: "inspectionResult",
      label: "巡检结果",
      minWidth: 100,
      dataType: "tag",
      formatData: val => {
        return val == 1 ? "正常" : "异常";
      },
      formatType: val => {
        return val == 1 ? "success" : "danger";
      },
      prop: "createTime", label: "登记日期", minWidth: 180,
      formatData: cell => { if (!cell) return "-"; try { return dayjs(cell).format("YYYY-MM-DD HH:mm:ss"); } catch { return cell; } },
    },
    { prop: "abnormalDescription", label: "异常描述", minWidth: 100 },
    { prop: "abnormalDescription", label: "异常描述", minWidth: 150 },
  ]);
  // 操作列配置
  const getOperationColumn = operations => {
    if (!operations || operations.length === 0) return null;
    const operationConfig = {
      label: "操作",
      width: 130,
      fixed: "right",
      dataType: "action",
      operation: operations
        .map(op => {
          switch (op) {
            case "edit":
              return {
                name: "编辑",
                clickFun: handleAdd,
                color: "#409EFF",
              };
            case "viewFile":
              return {
                name: "查看附件",
                clickFun: viewFile,
                color: "#67C23A",
              };
            default:
              return null;
          }
        })
        .filter(Boolean),
    return {
      label: "操作", width: operations.length > 1 ? 180 : 130, fixed: "right", align: "center", dataType: "action",
      operation: operations.map(op => {
        switch (op) {
          case "edit": return { name: "编辑", clickFun: handleAdd, color: "#409EFF" };
          case "upload": return { name: "上传", clickFun: openUploadDialog, color: "#409EFF" };
          case "viewFile": return { name: "查看附件", clickFun: viewFile, color: "#67C23A" };
          default: return null;
        }
      }).filter(Boolean),
    };
    return operationConfig;
  };
  onMounted(() => {
    radioChange("taskManage");
  });
  // 巡检记录 - 日期分组
  const inspectionExpandedRowKeys = ref([]);
  // 单选变化
  const groupInspectionByDate = (list) => {
    const map = {};
    list.forEach(item => {
      const date = item.dateStr || (item.createTime ? dayjs(item.createTime).format('YYYY-MM-DD') : '未知日期');
      if (!map[date]) map[date] = [];
      map[date].push(item);
    });
    return Object.entries(map).map(([date, children]) => ({ date, children, count: children.length }));
  };
  const groupedInspectionList = computed(() => groupInspectionByDate(tableData.value));
  const onInspectionExpandChange = (row, expandedRows) => {
    inspectionExpandedRowKeys.value = expandedRows.map(r => r.date);
  };
  // 单条巡检完成
  const handleSingleComplete = async (row) => {
    try {
      await ElMessageBox.confirm('确认将该巡检记录标记为完成?', '提示', { type: 'warning' });
      const res = await completeInspection(row.id);
      if (res.code === 200) {
        ElMessage.success('巡检完成');
        getList();
      } else {
        ElMessage.error(res.msg || '操作失败');
      }
    } catch { /* 取消 */ }
  };
  // 一键巡检完成
  const handleBatchInspection = async (dateStr) => {
    try {
      await ElMessageBox.confirm(`确认将 ${dateStr} 的所有待巡检记录标记为完成?`, '提示', { type: 'warning' });
      const res = await batchCompleteInspection(dateStr);
      if (res.code === 200) {
        ElMessage.success(res.msg || '批量完成成功');
        getList();
      } else {
        ElMessage.error(res.msg || '操作失败');
      }
    } catch { /* 取消 */ }
  };
  onMounted(() => { radioChange("taskManage"); });
  const radioChange = value => {
    inspectionExpandedRowKeys.value = [];
    if (value === "taskManage") {
      const operationColumn = getOperationColumn(["edit"]);
      tableColumns.value = [
        ...columns.value,
        ...(operationColumn ? [operationColumn] : []),
      ];
      tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])];
      operationsArr.value = ["edit"];
    } else if (value === "task") {
      const operationColumn = getOperationColumn(["viewFile"]);
      // 定时任务记录不展示"是否启用"列
      const operationColumn = getOperationColumn(["upload", "viewFile"]);
      const taskColumns = columns.value.filter(col => col.prop !== "isEnabled");
      tableColumns.value = [
        ...taskColumns,
        ...(operationColumn ? [operationColumn] : []),
      ];
      operationsArr.value = ["viewFile"];
      tableColumns.value = [...taskColumns, ...(operationColumn ? [operationColumn] : [])];
      operationsArr.value = ["upload", "viewFile"];
    }
    pageNum.value = 1;
    pageSize.value = 10;
    getList();
  };
  // 查询操作
  const handleQuery = () => {
    pageNum.value = 1;
    pageSize.value = 10;
    getList();
  };
  // 分页处理
  const handlePagination = val => {
    pageNum.value = val.page;
    pageSize.value = val.limit;
    getList();
  };
  // 获取列表数据
  const handleQuery = () => { pageNum.value = 1; getList(); };
  const handlePagination = val => { pageNum.value = val.page; pageSize.value = val.limit; getList(); };
  const getList = () => {
    tableLoading.value = true;
    const params = {
      ...queryParams,
      size: pageSize.value,
      current: pageNum.value,
    };
    let apiCall;
    if (activeRadio.value === "task") {
      apiCall = inspectionTaskList(params);
    } else {
      apiCall = timingTaskList(params);
    }
    apiCall
      .then(res => {
        const rawData = res.data.records || [];
        // 处理 inspector 字段,将字符串转换为数组(适用于所有情况)
        tableData.value = rawData.map(item => {
          const processedItem = { ...item };
          // 处理 inspector 字段
          if (processedItem.inspector) {
            if (typeof processedItem.inspector === "string") {
              // 字符串按逗号分割
              processedItem.inspector = processedItem.inspector
                .split(",")
                .map(s => s.trim())
                .filter(s => s);
            } else if (!Array.isArray(processedItem.inspector)) {
              // 非数组转为数组
              processedItem.inspector = [processedItem.inspector];
            }
          } else {
            // 空值设为空数组
            processedItem.inspector = [];
    const params = { ...queryParams, size: pageSize.value, current: pageNum.value };
    const apiCall = activeRadio.value === "task" ? inspectionTaskList(params) : timingTaskList(params);
    apiCall.then(res => {
      const rawData = res.data.records || [];
      tableData.value = rawData.map(item => {
        const processedItem = { ...item };
        if (processedItem.inspector) {
          if (typeof processedItem.inspector === "string") {
            processedItem.inspector = processedItem.inspector.split(",").map(s => s.trim()).filter(s => s);
          } else if (!Array.isArray(processedItem.inspector)) {
            processedItem.inspector = [processedItem.inspector];
          }
          return processedItem;
        });
        total.value = res.data.total || 0;
      })
      .finally(() => {
        tableLoading.value = false;
      });
  };
  // 重置查询
  const resetQuery = () => {
    for (const key in queryParams) {
      if (!["pageNum", "pageSize"].includes(key)) {
        queryParams[key] = "";
      }
    }
    handleQuery();
  };
  // 新增 / 编辑
  const handleAdd = row => {
    const type = row ? "edit" : "add";
    nextTick(() => {
      formDia.value?.openDialog(type, row);
    });
  };
  // 查看附件
  const viewFile = row => {
    nextTick(() => {
      viewFiles.value?.openDialog(row);
    });
  };
  // 删除操作
  const handleDelete = () => {
    if (!selectedRows.value.length) {
      proxy.$modal.msgWarning("请选择要删除的数据");
      return;
    }
    const deleteIds = selectedRows.value.map(item => item.id);
    proxy.$modal
      .confirm("是否确认删除所选数据项?")
      .then(() => {
        return delTimingTask(deleteIds);
      })
      .then(() => {
        proxy.$modal.msgSuccess("删除成功");
        handleQuery();
      })
      .catch(() => {});
  };
  // 多选变更
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
  };
  // 导出
  const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        // 根据当前选中的标签页调用不同的导出接口
        if (activeRadio.value === "taskManage") {
          // 定时任务管理
          proxy.download("/timingTask/export", {}, "定时任务管理.xlsx");
        } else if (activeRadio.value === "task") {
          // 定时任务记录
          proxy.download("/inspectionTask/export", {}, "定时任务记录.xlsx");
        } else {
          processedItem.inspector = [];
        }
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
        return processedItem;
      });
      total.value = res.data.total || 0;
    }).finally(() => { tableLoading.value = false; });
  };
  const resetQuery = () => { queryParams.taskName = ""; handleQuery(); };
  const handleAdd = row => { nextTick(() => { formDia.value?.openDialog(row ? "edit" : "add", row); }); };
  const viewFile = row => { nextTick(() => { viewFiles.value?.openDialog(row); }); };
  const openUploadDialog = row => { nextTick(() => { uploadFiles.value?.openDialog(row); }); };
  const handleDelete = () => {
    if (!selectedRows.value.length) { proxy.$modal.msgWarning("请选择要删除的数据"); return; }
    proxy.$modal.confirm("是否确认删除所选数据项?").then(() => delTimingTask(selectedRows.value.map(item => item.id)))
      .then(() => { proxy.$modal.msgSuccess("删除成功"); handleQuery(); }).catch(() => {});
  };
  const handleSelectionChange = selection => { selectedRows.value = selection; };
  const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", { confirmButtonText: "确认", cancelButtonText: "取消", type: "warning" })
      .then(() => {
        if (activeRadio.value === "taskManage") proxy.download("/timingTask/export", {}, "巡检任务.xlsx");
        else proxy.download("/inspectionTask/export", {}, "巡检记录.xlsx");
      }).catch(() => { proxy.$modal.msg("已取消"); });
  };
</script>
<style scoped>
  .person-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
  }
  .person-tag {
    margin-right: 4px;
    margin-bottom: 2px;
  }
  .no-data {
    color: #909399;
    font-size: 14px;
  }
</style>
  .person-tags { display: flex; flex-wrap: wrap; gap: 4px; }
  .person-tag { margin-right: 4px; margin-bottom: 2px; }
  .no-data { color: #909399; font-size: 14px; }
</style>