zouyu
2026-05-26 b8da78824e4c67632abb65302f01ccf74d5a1096
src/views/safetyManagement/inspectionReport/index.vue
@@ -79,112 +79,305 @@
        </el-form-item>
        <el-form-item label="状态">
          <el-select v-model="filters.status" placeholder="请选择" clearable style="width: 150px">
            <el-option label="正常" value="normal" />
            <el-option label="异常" value="abnormal" />
            <el-option label="漏检" value="missed" />
            <el-option label="未执行" value="unexecuted" />
            <el-option label="正常" :value="0" />
            <el-option label="异常" :value="1" />
            <el-option label="漏检" :value="2" />
            <el-option label="未执行" :value="3" />
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="getData">搜索</el-button>
          <el-button @click="resetFilters">重置</el-button>
          <el-button type="success" @click="handleSyncData" icon="Refresh">同步数据</el-button>
        </el-form-item>
      </el-form>
      <PIMTable :column="columns" :tableData="dataList" :page="pagination" @pagination="changePage" />
      <PIMTable :column="columns" :tableData="dataList" :page="pagination" @pagination="changePage" :tableLoading="tableLoading" />
    </el-card>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { ref, reactive, onMounted, nextTick } from "vue";
import PIMTable from "@/components/PIMTable/PIMTable.vue";
import * as echarts from "echarts";
import {
  getInspectionRecordList,
  getTodayStatistics,
  getTrendStatistics,
  getTypeStatistics,
  syncInspectionData
} from "@/api/safetyManagement/inspectionReport.js";
import { ElMessage } from "element-plus";
import dayjs from "dayjs";
defineOptions({
  name: "巡检数据报表",
});
const todayStats = reactive({
  completionRate: 95,
  inspectorCount: 8,
  abnormalCount: 2,
  missedCount: 1,
  completionRate: 0,
  inspectorCount: 0,
  abnormalCount: 0,
  missedCount: 0,
});
const filters = reactive({
  dateRange: [],
  inspector: "",
  status: "",
  status: null,
});
const dataList = ref([]);
const pagination = reactive({ current: 1, size: 10, total: 0 });
const tableLoading = ref(false);
const columns = [
  { label: "巡检时间", prop: "inspectionTime", align: "center" },
  { label: "巡检人员", prop: "inspector", align: "center" },
  { label: "巡检区域", prop: "area", align: "center" },
  { label: "巡检类型", prop: "type", align: "center" },
  { label: "状态", prop: "status", align: "center" },
  {
    label: "巡检类型",
    prop: "type",
    align: "center",
    formatData: (val) => {
      const typeMap = {
        'daily': '日常巡检',
        'equipment': '设备巡检',
        'fire': '消防巡检',
        'night': '夜间巡检',
        'safety': '安全巡检',
        'environment': '环境巡检'
      };
      return typeMap[val] || val;
    }
  },
  {
    label: "状态",
    prop: "status",
    align: "center",
    dataType: "tag",
    formatType: (val) => {
      if (val === 0) return 'success';
      if (val === 1) return 'danger';
      if (val === 2) return 'warning';
      return 'info';
    },
    formatData: (val) => {
      const statusMap = { 0: '正常', 1: '异常', 2: '漏检', 3: '未执行' };
      return statusMap[val] || val;
    }
  },
  { label: "异常情况", prop: "abnormalDesc", align: "center" },
];
const trendChartRef = ref(null);
const typeChartRef = ref(null);
let trendChart = null;
let typeChart = null;
onMounted(() => {
  initTrendChart();
  initTypeChart();
  getTodayStats();
  getData();
  nextTick(() => {
    initTrendChart();
    initTypeChart();
  });
});
const initTrendChart = () => {
  const chart = echarts.init(trendChartRef.value);
  const option = {
    xAxis: {
      type: "category",
      data: ["周一", "周二", "周三", "周四", "周五", "周六", "周日"],
    },
    yAxis: { type: "value" },
    series: [
      { name: "已完成", type: "line", data: [120, 132, 101, 134, 90, 230, 210], smooth: true },
      { name: "未完成", type: "line", data: [20, 12, 21, 14, 30, 20, 10], smooth: true },
    ],
    legend: { data: ["已完成", "未完成"], bottom: 0 },
    grid: { left: "3%", right: "4%", bottom: "10%", containLabel: true },
  };
  chart.setOption(option);
// 获取今日统计
const getTodayStats = async () => {
  try {
    const res = await getTodayStatistics();
    if (res.code === 200) {
      Object.assign(todayStats, res.data);
    }
  } catch (error) {
    console.error('获取今日统计失败', error);
  }
};
const initTypeChart = () => {
  const chart = echarts.init(typeChartRef.value);
  const option = {
    series: [
      {
        type: "pie",
        radius: ["40%", "70%"],
        data: [
          { value: 335, name: "设备巡检" },
          { value: 310, name: "安全巡检" },
          { value: 234, name: "环境巡检" },
          { value: 135, name: "消防巡检" },
        ],
// 初始化趋势图表
const initTrendChart = async () => {
  if (!trendChartRef.value) return;
  trendChart = echarts.init(trendChartRef.value);
  try {
    const endDate = dayjs().format('YYYY-MM-DD');
    const startDate = dayjs().subtract(6, 'day').format('YYYY-MM-DD');
    const res = await getTrendStatistics({ startDate, endDate });
    let dates = [];
    let completedData = [];
    let abnormalData = [];
    let missedData = [];
    let unexecutedData = [];
    if (res.code === 200 && Array.isArray(res.data)) {
      // 按日期排序
      const sortedData = res.data.sort((a, b) => a.statDate.localeCompare(b.statDate));
      dates = sortedData.map(item => dayjs(item.statDate).format('MM-DD'));
      completedData = sortedData.map(item => item.completedCount || 0);
      abnormalData = sortedData.map(item => item.abnormalCount || 0);
      missedData = sortedData.map(item => item.missedCount || 0);
      unexecutedData = sortedData.map(item => item.unexecutedCount || 0);
    } else {
      // 使用默认数据
      for (let i = 6; i >= 0; i--) {
        dates.push(dayjs().subtract(i, 'day').format('MM-DD'));
      }
      completedData = [2, 1, 2, 2, 1, 4, 3];
      abnormalData = [1, 0, 0, 1, 0, 1, 0];
      missedData = [0, 0, 1, 0, 0, 1, 0];
      unexecutedData = [0, 1, 0, 0, 1, 0, 1];
    }
    const option = {
      tooltip: { trigger: 'axis' },
      xAxis: {
        type: "category",
        data: dates,
      },
    ],
    legend: { orient: "vertical", left: "left" },
  };
  chart.setOption(option);
      yAxis: { type: "value" },
      series: [
        { name: "已完成", type: "line", data: completedData, smooth: true },
        { name: "异常", type: "line", data: abnormalData, smooth: true },
        { name: "漏检", type: "line", data: missedData, smooth: true },
        { name: "未执行", type: "line", data: unexecutedData, smooth: true },
      ],
      legend: { data: ["已完成", "异常", "漏检", "未执行"], bottom: 0 },
      grid: { left: "3%", right: "4%", bottom: "15%", containLabel: true },
    };
    trendChart.setOption(option);
  } catch (error) {
    console.error('获取趋势数据失败', error);
  }
};
const getData = () => {};
// 类型映射
const typeMap = {
  'daily': '日常巡检',
  'equipment': '设备巡检',
  'fire': '消防巡检',
  'night': '夜间巡检',
  'safety': '安全巡检',
  'environment': '环境巡检'
};
// 初始化类型图表
const initTypeChart = async () => {
  if (!typeChartRef.value) return;
  typeChart = echarts.init(typeChartRef.value);
  try {
    const endDate = dayjs().format('YYYY-MM-DD');
    const startDate = dayjs().subtract(30, 'day').format('YYYY-MM-DD');
    const res = await getTypeStatistics({ startDate, endDate });
    let pieData = [];
    if (res.code === 200 && Array.isArray(res.data)) {
      pieData = res.data.map(item => ({
        value: item.count || 0,
        name: typeMap[item.type] || item.type
      }));
    } else {
      // 使用默认数据
      pieData = [
        { value: 8, name: "日常巡检" },
        { value: 4, name: "设备巡检" },
        { value: 3, name: "消防巡检" },
        { value: 3, name: "夜间巡检" },
      ];
    }
    const option = {
      tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
      series: [
        {
          type: "pie",
          radius: ["40%", "70%"],
          data: pieData,
          label: {
            formatter: '{b}\n{c}次 ({d}%)'
          }
        },
      ],
      legend: { orient: "vertical", left: "left" },
    };
    typeChart.setOption(option);
  } catch (error) {
    console.error('获取类型统计失败', error);
  }
};
// 获取巡检记录列表
const getData = async () => {
  tableLoading.value = true;
  try {
    const params = {
      pageNum: pagination.current,
      pageSize: pagination.size,
      inspector: filters.inspector,
      status: filters.status,
    };
    if (filters.dateRange && filters.dateRange.length === 2) {
      params.startDate = filters.dateRange[0];
      params.endDate = filters.dateRange[1];
    }
    const res = await getInspectionRecordList(params);
    if (res.code === 200) {
      dataList.value = res.data.rows || res.data.records || [];
      pagination.total = res.data.total || 0;
    }
  } catch (error) {
    ElMessage.error('获取巡检记录失败');
  } finally {
    tableLoading.value = false;
  }
};
const resetFilters = () => {
  filters.dateRange = [];
  filters.inspector = "";
  filters.status = "";
  filters.status = null;
  pagination.current = 1;
  getData();
};
const changePage = ({ page, limit }) => {
  pagination.current = page;
  pagination.size = limit;
  getData();
};
// 同步巡检数据
const handleSyncData = async () => {
  try {
    const res = await syncInspectionData();
    if (res.code === 200) {
      ElMessage.success(`同步成功,新增 ${res.data?.count || 0} 条记录`);
      // 刷新列表和统计
      getData();
      getTodayStats();
      initTrendChart();
      initTypeChart();
    } else {
      ElMessage.warning(res.msg || '同步失败');
    }
  } catch (error) {
    ElMessage.error(error.response?.data?.msg || '同步失败');
  }
};
// 窗口大小改变时重新渲染图表
window.addEventListener('resize', () => {
  trendChart && trendChart.resize();
  typeChart && typeChart.resize();
});
</script>
<style lang="scss" scoped>