فهرست منبع

//更新初始化数据

wyj0522 2 هفته پیش
والد
کامیت
5056bed098

+ 1 - 2
gm-base-ser/src/main/java/com/goomood/phm/controller/FaultStatisticsController.java

@@ -5,7 +5,6 @@ import com.goomood.common.core.domain.AjaxResult;
 import com.goomood.phm.service.FaultMonitorService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
@@ -18,7 +17,7 @@ import java.util.Map;
  */
 @Api(tags = "故障统计API")
 @RestController
-@RequestMapping("/phm/statistics")
+@RequestMapping("/statistics")
 public class FaultStatisticsController extends BaseController {
 
     @Resource

+ 80 - 4
gm-base-ser/src/main/java/com/goomood/phm/domain/FaultMonitorA.java

@@ -5,9 +5,8 @@ import java.util.Date;
 
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
-import com.goomood.common.core.domain.BaseEntity;
+import com.goomood.common.annotation.Excel;
 import lombok.Data;
-import nonapi.io.github.classgraph.json.Id;
 
 import java.io.Serializable;
 
@@ -16,208 +15,285 @@ import java.io.Serializable;
  */
 @Data
 @TableName("fault_monitor")
-public class FaultMonitorA extends BaseEntity implements Serializable {
+public class FaultMonitorA  implements Serializable {
 
     private static final long serialVersionUID = 1L;
 
     /** id */
-    @Id
     private Long id;
 
     /** 日期 */
     @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "日期", width = 30, dateFormat = "yyyy-MM-dd")
     private Date incidentDate;
 
     /** 战区 */
+    @Excel(name = "战区")
     private String militaryRegion;
 
     /** 军 */
+    @Excel(name = "军")
     private String army;
 
     /** 师 */
+    @Excel(name = "师")
     private String division;
 
     /** 团 */
+    @Excel(name = "团")
     private String regiment;
 
     /** 中队 */
+    @Excel(name = "中队")
     private String squadron;
 
     /** 飞机号 */
+    @Excel(name = "飞机号")
     private String aircraftNo;
 
     /** 机型 */
+    @Excel(name = "机型")
     private String aircraftType;
 
     /** 出厂号码 */
+    @Excel(name = "出厂号码")
     private String factoryNo;
 
     /** 机件类型 */
+    @Excel(name = "机件类型")
     private String partCategory;
 
     /** 机件号 */
+    @Excel(name = "机件号")
     private String partNo;
 
     /** 机件型别 */
+    @Excel(name = "机件型别")
     private String partModel;
 
     /** 机件名称 */
+    @Excel(name = "机件名称")
     private String partName;
 
     /** 起落 */
+    @Excel(name = "起落")
     private Long landingCount;
 
     /** 专业 */
+    @Excel(name = "专业")
     private String specialty;
 
     /** 故障件名称 */
+    @Excel(name = "故障件名称")
     private String faultyPartName;
 
     /** 故障件型别 */
+    @Excel(name = "故障件型别")
     private String faultyPartModel;
 
     /** 故障件号码 */
+    @Excel(name = "故障件号码")
     private String faultyPartNo;
 
     /** 故障件所属发动机型别 */
+    @Excel(name = "故障件所属发动机型别")
     private String engineType;
 
     /** 故障件所属发动机号码 */
+    @Excel(name = "故障件所属发动机号码")
     private String engineNo;
 
     /** 故障件所属发动机序号 */
+    @Excel(name = "故障件所属发动机序号")
     private String engineSerialNo;
 
     /** 故障件位置 */
+    @Excel(name = "故障件位置")
     private String faultLocation;
 
     /** 故障现象 */
+    @Excel(name = "故障现象")
     private String faultDescription;
 
     /** 故障发生地点 */
+    @Excel(name = "故障发生地点")
     private String faultLocationSite;
 
     /** 发现时机 */
+    @Excel(name = "发现时机")
     private String detectionTiming;
 
     /** 系统 */
+    @Excel(name = "系统")
     private String useSystem;
 
     /** 计时类型 */
+    @Excel(name = "计时类型")
     private String timingType;
 
     /** 故障件制造厂 */
+    @Excel(name = "故障件制造厂")
     private String manufacturer;
 
     /** 故障件翻修厂 */
+    @Excel(name = "故障件翻修厂")
     private String repairFacility;
 
     /** 故障件装本机工作时次 */
+
     @JsonFormat(pattern = "HH:mm:ss")
+    @Excel(name = "故障件装本机工作时次", width = 30, dateFormat = "HH:mm:ss")
     private Time workHoursCurrent;
 
     /** 故障件总工作时次 */
     @JsonFormat(pattern = "HH:mm:ss")
+    @Excel(name = "故障件总工作时次", width = 30, dateFormat = "HH:mm:ss")
     private Time workHoursTotal;
 
     /** 故障件修后时次 */
     @JsonFormat(pattern = "HH:mm:ss")
+    @Excel(name = "故障件修后时次", width = 30, dateFormat = "HH:mm:ss")
     private Time workHoursAfterRepair;
 
     /** 故换件型别 */
+    @Excel(name = "故换件型别")
     private String replacementPartModel;
 
     /** 故换件号码 */
+    @Excel(name = "故换件号码")
     private String replacementPartNo;
 
     /** 故换件制造厂 */
+    @Excel(name = "故换件制造厂")
     private String replacementManufacturer;
 
     /** 故换件总工作时次 */
     @JsonFormat(pattern = "HH:mm:ss")
+    @Excel(name = "故换件总工作时次", width = 30, dateFormat = "HH:mm:ss")
     private Time replacementWorkHoursTotal;
 
     /** 故换件翻修厂 */
+    @Excel(name = "故换件翻修厂")
     private String replacementRepairFacility;
 
     /** 故换件修后时次 */
     @JsonFormat(pattern = "HH:mm:ss")
+    @Excel(name = "故换件修后时次", width = 30, dateFormat = "HH:mm:ss")
     private Time replacementHoursAfterRepair;
 
     /** 故障失常码 */
+    @Excel(name = "故障失常码")
     private String faultCode;
 
     /** 故障件装机日期 */
     @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "故障件装机日期", width = 30, dateFormat = "yyyy-MM-dd")
+
     private Date installationDate;
 
     /** 故障原因 */
+    @Excel(name = "故障原因")
     private String faultCause;
 
     /** 故障性质 */
+    @Excel(name = "故障性质")
     private String faultNature;
 
     /** 故障责任 */
+    @Excel(name = "故障责任")
     private String faultResponsibility;
 
     /** 故障后果 */
+    @Excel(name = "故障后果")
     private String faultConsequence;
 
     /** 处理意见 */
+    @Excel(name = "处理意见")
     private String handlingSuggestion;
 
     /** 影响次数 */
+    @Excel(name = "影响次数")
     private Long impactCount;
 
     /** 误飞次数 */
+    @Excel(name = "误飞次数")
     private Long missedFlightCount;
 
     /** 判明方法 */
+    @Excel(name = "判明方法")
     private String detectionMethod;
 
     /** 发现人 */
+    @Excel(name = "发现人")
     private String discoverer;
 
     /** 排除方法 */
+    @Excel(name = "排除方法")
     private String resolutionMethod;
 
     /** 排故人 */
+    @Excel(name = "排故人")
     private String maintainer;
 
     /** 审核人 */
+    @Excel(name = "审核人")
     private String reviewer;
 
     /** 审核时间 */
     @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "审核时间", width = 30, dateFormat = "yyyy-MM-dd")
     private Date reviewTime;
 
     /** 排除日期 */
     @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "排除日期", width = 30, dateFormat = "yyyy-MM-dd")
     private Date resolutionDate;
 
     /** 排故工时 */
     @JsonFormat(pattern = "HH:mm:ss")
+    @Excel(name = "排故工时", width = 30, dateFormat = "HH:mm:ss")
     private Time maintenanceHours;
 
     /** 排故人数 */
+    @Excel(name = "排故人数")
     private Long maintenanceStaffCount;
 
     /** 排故时间 */
     @JsonFormat(pattern = "HH:mm:ss")
+    @Excel(name = "排故时间", width = 30, dateFormat = "HH:mm:ss")
     private Time maintenanceTime;
 
     /** 是否立功 */
+    @Excel(name = "是否立功", readConverterExp = "fault_yes=是,fault_no=否")
     private String meritAwarded;
 
     /** 需要试飞 */
+    @Excel(name = "需要试飞", readConverterExp = "fault_yes=是,fault_no=否")
     private String testFlightRequired;
 
     /** 备注 */
+    @Excel(name = "备注")
     private String remarks;
 
     /** 文件路径 */
     private String filePath;
 
     /** 监控状态 */
+    @Excel(name = "监控状态")
     private String status;
+    /** 创建者 */
+    private String createBy;
+
+    /** 创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    /** 更新者 */
+    private String updateBy;
+
+    /** 更新时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    private Date updateTime;
+
 }    

+ 3 - 11
gm-base-ser/src/main/java/com/goomood/phm/mapper/FaultMonitorAMapper.java

@@ -1,6 +1,8 @@
 package com.goomood.phm.mapper;
 
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.goomood.phm.domain.FaultMonitor;
+import com.goomood.phm.domain.FaultMonitorA;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
@@ -11,16 +13,6 @@ import java.util.List;
  * 故障监控Mapper接口
  */
 @Mapper
-public interface FaultMonitorAMapper {
+public interface FaultMonitorAMapper extends BaseMapper<FaultMonitorA> {
 
-    /**
-     * 根据日期范围查询故障记录
-     * @param startDate 开始日期
-     * @param endDate 结束日期
-     * @return 故障记录列表
-     */
-    List<FaultMonitor> findByIncidentDateBetween(
-            @Param("startDate") Date startDate,
-            @Param("endDate") Date endDate
-    );
 }    

+ 62 - 57
gm-base-ser/src/main/java/com/goomood/phm/service/impl/FaultMonitorServiceAImpl.java

@@ -1,6 +1,7 @@
 package com.goomood.phm.service.impl;
 
-import com.goomood.phm.domain.FaultMonitor;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.goomood.phm.domain.FaultMonitorA;
 import com.goomood.phm.mapper.FaultMonitorAMapper;
 import com.goomood.phm.service.FaultMonitorService;
 import org.springframework.stereotype.Service;
@@ -18,85 +19,89 @@ import java.util.stream.Collectors;
 public class FaultMonitorServiceAImpl implements FaultMonitorService {
 
     @Resource
-    private FaultMonitorAMapper faultMonitorMapper;
+    private FaultMonitorAMapper faultMonitorAMapper;
 
     @Override
     public Map<String, Long> getTop5FaultyPartsNextMonthInThreeYears() {
-        // 计算关键日期
         LocalDate currentDate = LocalDate.now();
         LocalDate nextMonth = currentDate.plusMonths(1);
         int targetMonth = nextMonth.getMonthValue();
-        
+
         // 查询近三年数据
-        List<FaultMonitor> faultMonitors = faultMonitorMapper.findByIncidentDateBetween(
-            toDate(currentDate.minusYears(3)),
-            toDate(currentDate)
-        );
-        
+        QueryWrapper<FaultMonitorA> queryWrapper = new QueryWrapper<>();
+        queryWrapper.between("incident_date",
+                toDate(currentDate.minusYears(3)),
+                toDate(currentDate));
+
+        List<FaultMonitorA> faultMonitors = faultMonitorAMapper.selectList(queryWrapper);
+
         return faultMonitors.stream()
-            .filter(fault -> isTargetMonth(fault.getIncidentDate(), targetMonth))
-            .filter(this::isValidFaultPart)
-            .collect(Collectors.groupingBy(
-                FaultMonitor::getFaultyPartName,
-                Collectors.counting()
-            ))
-            .entrySet().stream()
-            .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
-            .limit(5)
-            .collect(Collectors.toMap(
-                Map.Entry::getKey,
-                Map.Entry::getValue,
-                (e1, e2) -> e1,
-                LinkedHashMap::new
-            ));
+                .filter(fault -> isTargetMonth(fault.getIncidentDate(), targetMonth))
+                .filter(this::isValidFaultPart)
+                .collect(Collectors.groupingBy(
+                        FaultMonitorA::getFaultyPartName,
+                        Collectors.counting()
+                ))
+                .entrySet().stream()
+                .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
+                .limit(5)
+                .collect(Collectors.toMap(
+                        Map.Entry::getKey,
+                        Map.Entry::getValue,
+                        (e1, e2) -> e1,
+                        LinkedHashMap::new
+                ));
     }
 
     @Override
     public Map<String, Long> getTop5FaultyPartsInSixMonths() {
         LocalDate currentDate = LocalDate.now();
-        
-        List<FaultMonitor> faultMonitors = faultMonitorMapper.findByIncidentDateBetween(
-            toDate(currentDate.minusMonths(6)),
-            toDate(currentDate)
-        );
-        
+
+        // 使用MyBatis-Plus查询近半年数据
+        QueryWrapper<FaultMonitorA> queryWrapper = new QueryWrapper<>();
+        queryWrapper.between("incident_date",
+                toDate(currentDate.minusMonths(6)),
+                toDate(currentDate));
+
+        List<FaultMonitorA> faultMonitors = faultMonitorAMapper.selectList(queryWrapper);
+
         return faultMonitors.stream()
-            .filter(this::isValidFaultPart)
-            .collect(Collectors.groupingBy(
-                FaultMonitor::getFaultyPartName,
-                Collectors.counting()
-            ))
-            .entrySet().stream()
-            .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
-            .limit(5)
-            .collect(Collectors.toMap(
-                Map.Entry::getKey,
-                Map.Entry::getValue,
-                (e1, e2) -> e1,
-                LinkedHashMap::new
-            ));
-    }
-    
-    // 辅助方法:Date转LocalDate
-    private LocalDate toLocalDate(Date date) {
-        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+                .filter(this::isValidFaultPart)
+                .collect(Collectors.groupingBy(
+                        FaultMonitorA::getFaultyPartName,
+                        Collectors.counting()
+                ))
+                .entrySet().stream()
+                .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
+                .limit(5)
+                .collect(Collectors.toMap(
+                        Map.Entry::getKey,
+                        Map.Entry::getValue,
+                        (e1, e2) -> e1,
+                        LinkedHashMap::new
+                ));
     }
-    
+
     // 辅助方法:LocalDate转Date
     private Date toDate(LocalDate localDate) {
         return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
     }
-    
+
     // 检查是否为目标月份的故障
     private boolean isTargetMonth(Date date, int targetMonth) {
         if (date == null) return false;
         return toLocalDate(date).getMonthValue() == targetMonth;
     }
-    
+
+    // 辅助方法:Date转LocalDate
+    private LocalDate toLocalDate(Date date) {
+        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+    }
+
     // 验证故障部件有效性
-    private boolean isValidFaultPart(FaultMonitor fault) {
-        return fault != null 
-            && fault.getFaultyPartName() != null 
-            && !fault.getFaultyPartName().trim().isEmpty();
+    private boolean isValidFaultPart(FaultMonitorA fault) {
+        return fault != null
+                && fault.getFaultyPartName() != null
+                && !fault.getFaultyPartName().trim().isEmpty();
     }
-}    
+}

+ 0 - 12
gm-base-ser/src/main/resources/mapper/fault/FaultMonitorAMapper.xml

@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
-"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.goomood.phm.mapper.FaultMonitorAMapper">
-
-    <!-- 根据日期范围查询故障记录 -->
-    <select id="findByIncidentDateBetween" resultType="com.goomood.phm.domain.FaultMonitorA">
-        SELECT * FROM fault_monitor
-        WHERE incident_date BETWEEN #{startDate} AND #{endDate}
-    </select>
-
-</mapper>    

+ 17 - 0
gm-web/src/api/faultStatistics/index.js

@@ -0,0 +1,17 @@
+import request from '@/utils/request'
+
+// 获取近三年下月故障最多的5类部件
+export function getTop5NextMonth() {
+  return request({
+    url: '/statistics/top5NextMonth',
+    method: 'get'
+  })
+}
+
+// 获取近半年故障最多的5类部件
+export function getTop5SixMonths() {
+  return request({
+    url: '/statistics/top5SixMonths',
+    method: 'get'
+  })
+}

+ 30 - 0
gm-web/src/utils/dataUtils.js

@@ -0,0 +1,30 @@
+/**
+ * 格式化日期
+ * @param {Date} date - 日期对象
+ * @param {String} format - 格式字符串(如 'yyyy-MM-dd HH:mm:ss')
+ * @returns {String} 格式化后的日期
+ */
+export function formatDate(date, format) {
+  if (!date) return ''
+  if (!(date instanceof Date)) {
+    date = new Date(date)
+  }
+  const o = {
+    'M+': date.getMonth() + 1, // 月份
+    'd+': date.getDate(), // 日
+    'H+': date.getHours(), // 小时
+    'm+': date.getMinutes(), // 分
+    's+': date.getSeconds(), // 秒
+    'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
+    'S': date.getMilliseconds() // 毫秒
+  }
+  if (/(y+)/.test(format)) {
+    format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
+  }
+  for (const k in o) {
+    if (new RegExp('(' + k + ')').test(format)) {
+      format = format.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
+    }
+  }
+  return format
+}

+ 335 - 0
gm-web/src/views/FaultStatisticsBoard.vue

@@ -0,0 +1,335 @@
+<template>
+  <div class="fault-statistics-board">
+    <!-- 看板标题 -->
+    <div class="board-header">
+      <h2>故障部件统计综合看板</h2>
+      <el-tag type="info" size="small">{{ updateTime }}</el-tag>
+    </div>
+
+    <!-- 数据卡片区域 -->
+    <div class="statistic-cards">
+      <el-card class="stat-card">
+        <div slot="header">近三年下月故障总次数</div>
+        <div class="stat-value">{{ totalNextMonthFaults }}</div>
+        <div class="stat-desc">统计周期:{{ threeYearsRange }}</div>
+      </el-card>
+
+      <el-card class="stat-card">
+        <div slot="header">近半年故障总次数</div>
+        <div class="stat-value">{{ totalSixMonthsFaults }}</div>
+        <div class="stat-desc">统计周期:{{ sixMonthsRange }}</div>
+      </el-card>
+    </div>
+
+    <!-- 图表区域(响应式布局) -->
+    <div class="charts-container">
+      <!-- 近三年下月故障部件TOP5 -->
+      <el-card class="chart-card">
+        <div slot="header">近三年下月故障最多的5类部件</div>
+        <div class="chart-wrapper">
+          <div ref="nextMonthChart" class="chart-dom"></div>
+        </div>
+      </el-card>
+
+      <!-- 近半年故障部件TOP5 -->
+      <el-card class="chart-card">
+        <div slot="header">近半年故障最多的5类部件</div>
+        <div class="chart-wrapper">
+          <div ref="sixMonthsChart" class="chart-dom"></div>
+        </div>
+      </el-card>
+    </div>
+
+<!--    &lt;!&ndash; 加载状态 &ndash;&gt;-->
+<!--    <el-loading-->
+<!--      v-loading="loading"-->
+<!--      text="正在加载统计数据..."-->
+<!--      background="rgba(255, 255, 255, 0.8)"-->
+<!--    ></el-loading>-->
+  </div>
+</template>
+
+<script>
+import * as echarts from 'echarts'
+import { getTop5NextMonth, getTop5SixMonths } from '@/api/faultStatistics' // 引入API
+import { formatDate } from '@/utils/dataUtils' // 日期工具函数
+
+export default {
+  name: 'FaultStatisticsBoard',
+  data() {
+    return {
+      // 数据
+      nextMonthData: [], // 近三年下月TOP5数据
+      sixMonthsData: [], // 近半年TOP5数据
+      totalNextMonthFaults: 0, // 近三年下月总故障数
+      totalSixMonthsFaults: 0, // 近半年总故障数
+
+      // 时间信息
+      updateTime: '', // 数据更新时间
+      threeYearsRange: '', // 近三年时间范围
+      sixMonthsRange: '', // 近半年时间范围
+
+      // 状态
+      loading: true,
+      // 图表实例
+      nextMonthChartIns: null,
+      sixMonthsChartIns: null
+    }
+  },
+  mounted() {
+    // 初始化图表
+    this.initCharts()
+    // 加载数据
+    this.loadStatisticsData()
+    // 监听窗口大小变化,重绘图表
+    window.addEventListener('resize', this.handleResize)
+  },
+  beforeDestroy() {
+    // 销毁图表实例,释放资源
+    if (this.nextMonthChartIns) this.nextMonthChartIns.dispose()
+    if (this.sixMonthsChartIns) this.sixMonthsChartIns.dispose()
+    window.removeEventListener('resize', this.handleResize)
+  },
+  methods: {
+    // 初始化图表实例
+    initCharts() {
+      this.nextMonthChartIns = echarts.init(this.$refs.nextMonthChart)
+      this.sixMonthsChartIns = echarts.init(this.$refs.sixMonthsChart)
+    },
+
+    // 加载统计数据
+    async loadStatisticsData() {
+      this.loading = true
+      try {
+        // 并行请求两个接口
+        const [nextMonthRes, sixMonthsRes] = await Promise.all([
+          getTop5NextMonth(),
+          getTop5SixMonths()
+        ])
+
+        // 处理近三年下月数据
+        this.nextMonthData = nextMonthRes.data || []
+        this.totalNextMonthFaults = this.nextMonthData.reduce(
+          (sum, item) => sum + item.count, 0
+        )
+
+        // 处理近半年数据
+        this.sixMonthsData = sixMonthsRes.data || []
+        this.totalSixMonthsFaults = this.sixMonthsData.reduce(
+          (sum, item) => sum + item.count, 0
+        )
+
+        // 设置时间信息
+        this.updateTime = formatDate(new Date(), 'yyyy-MM-dd HH:mm:ss')
+        this.setTimeRangeText()
+
+        // 渲染图表
+        this.renderNextMonthChart()
+        this.renderSixMonthsChart()
+
+      } catch (error) {
+        this.$message.error('数据加载失败:' + (error.message || '未知错误'))
+      } finally {
+        this.loading = false
+      }
+    },
+
+    // 设置时间范围文本
+    setTimeRangeText() {
+      const now = new Date()
+      // 近三年范围:例如 2022-08 至 2025-08
+      const threeYearsAgo = new Date(now.getFullYear() - 3, now.getMonth(), 1)
+      this.threeYearsRange = `${formatDate(threeYearsAgo, 'yyyy-MM')} 至 ${formatDate(now, 'yyyy-MM')}`
+
+      // 近半年范围:例如 2025-02 至 2025-08
+      const sixMonthsAgo = new Date(now.getFullYear(), now.getMonth() - 5, 1)
+      this.sixMonthsRange = `${formatDate(sixMonthsAgo, 'yyyy-MM')} 至 ${formatDate(now, 'yyyy-MM')}`
+    },
+
+    // 渲染近三年下月故障部件图表
+    renderNextMonthChart() {
+      const parts = this.nextMonthData.map(item => item.partName)
+      const counts = this.nextMonthData.map(item => item.count)
+
+      this.nextMonthChartIns.setOption({
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: { type: 'shadow' },
+          formatter: '{b}: {c} 次' // 提示框格式:部件名: 次数
+        },
+        grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
+        xAxis: {
+          type: 'value',
+          name: '故障次数',
+          axisLabel: { formatter: '{value}' }
+        },
+        yAxis: {
+          type: 'category',
+          data: parts,
+          axisLabel: {
+            // 部件名过长时自动换行
+            interval: 0,
+            formatter: function(value) {
+              return value.length > 6 ? value.substring(0, 6) + '...' : value
+            }
+          }
+        },
+        series: [{
+          name: '故障次数',
+          type: 'bar',
+          data: counts,
+          itemStyle: {
+            // 柱状图颜色渐变
+            color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
+              { offset: 0, color: '#409EFF' },
+              { offset: 1, color: '#67C23A' }
+            ])
+          },
+          label: {
+            show: true,
+            position: 'right',
+            formatter: '{c} 次'
+          }
+        }]
+      })
+    },
+
+    // 渲染近半年故障部件图表
+    renderSixMonthsChart() {
+      const parts = this.sixMonthsData.map(item => item.partName)
+      const counts = this.sixMonthsData.map(item => item.count)
+
+      this.sixMonthsChartIns.setOption({
+        tooltip: {
+          trigger: 'item',
+          formatter: '{a} <br/>{b}: {c} 次 ({d}%)'
+        },
+        legend: {
+          orient: 'vertical',
+          left: 10,
+          data: parts
+        },
+        series: [{
+          name: '故障部件',
+          type: 'pie',
+          radius: ['40%', '70%'], // 环形图
+          avoidLabelOverlap: false,
+          itemStyle: {
+            borderRadius: 4,
+            borderColor: '#fff',
+            borderWidth: 2
+          },
+          label: {
+            show: false,
+            position: 'center'
+          },
+          emphasis: {
+            label: {
+              show: true,
+              fontSize: '16',
+              fontWeight: 'bold'
+            }
+          },
+          labelLine: {
+            show: false
+          },
+          data: this.sixMonthsData.map((item, index) => ({
+            name: parts[index],
+            value: item.count
+          }))
+        }]
+      })
+    },
+
+    // 窗口大小变化时重绘图表
+    handleResize() {
+      this.nextMonthChartIns.resize()
+      this.sixMonthsChartIns.resize()
+    }
+  }
+}
+</script>
+
+<style scoped>
+.fault-statistics-board {
+  padding: 20px;
+  background-color: #f5f7fa;
+  min-height: 100%;
+}
+
+.board-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+}
+
+.statistic-cards {
+  display: flex;
+  gap: 20px;
+  margin-bottom: 20px;
+  flex-wrap: wrap;
+}
+
+.stat-card {
+  flex: 1;
+  min-width: 280px;
+}
+
+.stat-value {
+  font-size: 28px;
+  font-weight: bold;
+  color: #1890ff;
+  margin: 15px 0;
+}
+
+.stat-desc {
+  color: #666;
+  font-size: 14px;
+}
+
+.charts-container {
+  display: flex;
+  gap: 20px;
+  flex-wrap: wrap;
+}
+
+.chart-card {
+  flex: 1;
+  min-width: 450px;
+  height: 450px;
+}
+
+.chart-wrapper {
+  width: 100%;
+  height: calc(100% - 44px); /* 减去卡片头部高度 */
+}
+
+.chart-dom {
+  width: 100%;
+  height: 100%;
+}
+
+/* 响应式调整 */
+@media (max-width: 992px) {
+  .charts-container {
+    flex-direction: column;
+  }
+  .chart-card {
+    height: 400px;
+  }
+}
+
+@media (max-width: 576px) {
+  .chart-card {
+    min-width: 100%;
+    height: 350px;
+  }
+  .statistic-cards {
+    flex-direction: column;
+  }
+  .stat-card {
+    min-width: 100%;
+  }
+}
+</style>

+ 8 - 1
gm-web/src/views/index.vue

@@ -21,6 +21,11 @@
           <KnowledgeExtraction/>
         </div>
       </el-tab-pane>
+      <el-tab-pane label="故障统计" name="faultMonitor">
+        <div class="tab-content_app">
+         <fault-statistics-board/>
+        </div>
+      </el-tab-pane>
     </el-tabs>
   </div>
 </template>
@@ -30,13 +35,15 @@ import ChatComponent from './ChatComponent.vue';
 import FileView from '@/views/file'
 import KnowledgeExtraction from '@/views/knowledgeExtraction/index.vue'
 import CatAi from '@/views/chat/catAi.vue'
+import FaultStatisticsBoard from '@/views/FaultStatisticsBoard.vue'
 export default {
   name: 'MainPage',
   components: {
     ChatComponent,
     FileView,
     KnowledgeExtraction,
-    CatAi
+    CatAi,
+    FaultStatisticsBoard
   },
   data() {
     return {

+ 0 - 1
gm-web/src/views/knowledgeExtraction/index.vue

@@ -520,7 +520,6 @@ export default {
   },
   methods: {
     init() {
-      download(2)
       this.getTaskListAPI()
       this.getAtlasFileAPI()
       this.getEntityClassAPI()