|
|
@@ -0,0 +1,779 @@
|
|
|
+<template>
|
|
|
+ <div class="app-container">
|
|
|
+ <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
|
|
|
+ <el-form-item label="统计方式" prop="remark">
|
|
|
+ <el-select
|
|
|
+ v-model="queryParams.remark"
|
|
|
+ placeholder="请选择统计方式"
|
|
|
+ clearable
|
|
|
+ style="width: 150px;"
|
|
|
+ @change="handleRemarkChange"
|
|
|
+ >
|
|
|
+ <el-option label="按月" value="month"></el-option>
|
|
|
+ <el-option label="按天" value="day"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="选择月份" prop="dataDate" v-if="queryParams.remark === 'month'">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="queryParams.dataDate"
|
|
|
+ type="month"
|
|
|
+ placeholder="请选择月份"
|
|
|
+ style="width: 150px;"
|
|
|
+ value-format="YYYY-MM"
|
|
|
+ @change="handleDateChange"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="选择日期" prop="dataDate" v-if="queryParams.remark === 'day'">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="queryParams.dataDate"
|
|
|
+ type="date"
|
|
|
+ placeholder="请选择日期"
|
|
|
+ style="width: 150px;"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ @change="handleDateChange"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="机台号" prop="deviceId">
|
|
|
+ <el-input
|
|
|
+ v-model="queryParams.deviceId"
|
|
|
+ placeholder="请输入机台号"
|
|
|
+ clearable
|
|
|
+ @input="handleDeviceIdChange"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="分析结果" prop="result">
|
|
|
+ <el-select
|
|
|
+ v-model="queryParams.result"
|
|
|
+ placeholder="请选择分析结果"
|
|
|
+ clearable
|
|
|
+ style="width: 150px;"
|
|
|
+ @change="handleResultChange"
|
|
|
+ >
|
|
|
+ <el-option label="平稳" value="平稳"></el-option>
|
|
|
+ <el-option label="上升" value="上升"></el-option>
|
|
|
+ <el-option label="明显上升" value="明显上升"></el-option>
|
|
|
+ <el-option label="下降" value="下降"></el-option>
|
|
|
+ <el-option label="明显下降" value="明显下降"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" icon="Search" @click="handleFilter">搜索</el-button>
|
|
|
+ <el-button icon="Refresh" @click="resetFilter">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <!-- 双图表展示 -->
|
|
|
+ <el-row :gutter="15" style="margin-bottom: 20px;" v-if="statistics">
|
|
|
+ <!-- 趋势分析南丁格尔图 -->
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-card>
|
|
|
+ <div ref="trendChartRef" style="width: 100%; height: 250px;"></div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <!-- 当月停机趋势折线图 -->
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-card>
|
|
|
+ <div ref="deviceChartRef" style="width: 100%; height: 250px;"></div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-table v-loading="loading" :data="filteredData" @selection-change="handleSelectionChange">
|
|
|
+ <el-table-column type="selection" width="55" align="center"/>
|
|
|
+ <!-- <el-table-column label="ID" align="center" prop="id" />-->
|
|
|
+ <el-table-column label="机台号" align="center" prop="deviceId"/>
|
|
|
+ <el-table-column label="数据量" align="center" prop="dataNum"/>
|
|
|
+ <el-table-column label="斜率" align="center" prop="slope">
|
|
|
+ <template #default="scope">
|
|
|
+ {{ (scope.row.slope).toFixed(2) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="标准差" align="center" prop="stdDev">
|
|
|
+ <template #default="scope">
|
|
|
+ {{ (scope.row.stdDev).toFixed(2) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="趋势分析" align="center" prop="result">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-link type="primary" @click="handleResultClick(scope.row)">{{ scope.row.result }}</el-link>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <!-- 图表弹窗 -->
|
|
|
+ <el-dialog :title="'设备[' + currentRow.deviceId + ']分析结果详情'" v-model="chartOpen" width="800px"
|
|
|
+ append-to-body>
|
|
|
+ <div style="text-align: center; margin: 20px 0;">
|
|
|
+ <p>设备名称: {{ currentRow.deviceName }}</p>
|
|
|
+ <p>分析结果: {{ currentRow.result }}</p>
|
|
|
+ </div>
|
|
|
+ <div v-if="currentRow.list && currentRow.list.length > 0">
|
|
|
+ <!-- 使用ECharts展示折线图 -->
|
|
|
+ <div ref="chartRef" style="width: 100%; height: 400px;"></div>
|
|
|
+ </div>
|
|
|
+ <div v-else>
|
|
|
+ <p style="text-align: center;">暂无详细数据</p>
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button @click="closeChart">关 闭</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup name="CalcStop">
|
|
|
+import {gzStop} from "@/api/calc/calcStop";
|
|
|
+import {getCurrentInstance, nextTick, reactive, ref, toRefs} from 'vue';
|
|
|
+import * as echarts from 'echarts';
|
|
|
+
|
|
|
+const {proxy} = getCurrentInstance();
|
|
|
+
|
|
|
+const calcStopList = ref([]);
|
|
|
+const filteredData = ref([]);
|
|
|
+const open = ref(false);
|
|
|
+const chartOpen = ref(false); // 添加图表弹窗状态
|
|
|
+const loading = ref(true);
|
|
|
+const showSearch = ref(true);
|
|
|
+const ids = ref([]);
|
|
|
+const single = ref(true);
|
|
|
+const multiple = ref(true);
|
|
|
+const total = ref(0);
|
|
|
+const title = ref("");
|
|
|
+const currentRow = ref({}); // 添加当前行数据
|
|
|
+const chartRef = ref(null);
|
|
|
+const statistics = ref(null); // 添加统计数据
|
|
|
+const trendChartRef = ref(null); // 趋势图容器引用
|
|
|
+const deviceChartRef = ref(null); // 设备统计图容器引用
|
|
|
+const monthList = ref([]); // 专门用于存储月度数据
|
|
|
+let chartInstance = null;
|
|
|
+let trendChartInstance = null; // 趋势图实例
|
|
|
+let deviceChartInstance = null; // 设备统计图实例
|
|
|
+
|
|
|
+// 计算默认日期(当前时间减去31小时)
|
|
|
+const getDefaultDate = () => {
|
|
|
+ const now = new Date();
|
|
|
+ const defaultDate = new Date(now.getTime() - 31 * 60 * 60 * 1000);
|
|
|
+ return defaultDate.toISOString().split('T')[0]; // 格式化为 YYYY-MM-DD
|
|
|
+};
|
|
|
+
|
|
|
+// 获取当前年月
|
|
|
+const getCurrentMonth = () => {
|
|
|
+ const now = new Date();
|
|
|
+ const year = now.getFullYear();
|
|
|
+ const month = (now.getMonth() + 1).toString().padStart(2, '0');
|
|
|
+ return `${year}-${month}`;
|
|
|
+};
|
|
|
+
|
|
|
+const data = reactive({
|
|
|
+ form: {},
|
|
|
+ queryParams: {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 1000,
|
|
|
+ deviceId: null,
|
|
|
+ dataDate: getDefaultDate(),
|
|
|
+ hour: null,
|
|
|
+ startTime: getDefaultDate(),
|
|
|
+ endTime: getDefaultDate(),
|
|
|
+ stopType: 6,
|
|
|
+ result: "", // 设置默认选中值
|
|
|
+ remark: "day", // 统计方式: month-按月, day-按天,默认按天
|
|
|
+ selectedMonth: null, // 选择的月份(已废弃,使用dataDate替代)
|
|
|
+ selectedDate: null // 选择的日期(已废弃,使用dataDate替代)
|
|
|
+ },
|
|
|
+ rules: {}
|
|
|
+});
|
|
|
+
|
|
|
+const {queryParams, form, rules} = toRefs(data);
|
|
|
+
|
|
|
+/** 查询停机数据统计列表 */
|
|
|
+function getList() {
|
|
|
+ loading.value = true;
|
|
|
+ // 根据统计方式设置查询参数
|
|
|
+ if (queryParams.value.remark === 'month' && queryParams.value.dataDate) {
|
|
|
+ // 按月查询
|
|
|
+ queryParams.value.startTime = queryParams.value.dataDate + "-01";
|
|
|
+ const year = parseInt(queryParams.value.dataDate.split("-")[0]);
|
|
|
+ const month = parseInt(queryParams.value.dataDate.split("-")[1]);
|
|
|
+ const lastDay = new Date(year, month, 0).getDate();
|
|
|
+ queryParams.value.endTime = queryParams.value.dataDate + "-" + (lastDay < 10 ? "0" + lastDay : lastDay);
|
|
|
+ } else if (queryParams.value.remark === 'day') {
|
|
|
+ // 按天查询,如果未选择日期则使用默认日期
|
|
|
+ if (!queryParams.value.dataDate) {
|
|
|
+ const defaultDate = getDefaultDate();
|
|
|
+ queryParams.value.dataDate = defaultDate;
|
|
|
+ }
|
|
|
+ queryParams.value.startTime = queryParams.value.dataDate;
|
|
|
+ queryParams.value.endTime = queryParams.value.dataDate;
|
|
|
+ }
|
|
|
+
|
|
|
+ gzStop(queryParams.value).then(response => {
|
|
|
+ calcStopList.value = response.data;
|
|
|
+ filteredData.value = response.data; // 初始化过滤数据
|
|
|
+ statistics.value = response.statistics; // 保存统计数据
|
|
|
+ monthList.value = response.monthList;
|
|
|
+ loading.value = false;
|
|
|
+ // 在数据加载完成后绘制图表
|
|
|
+ nextTick(() => {
|
|
|
+ drawTrendChart();
|
|
|
+ drawDeviceChart();
|
|
|
+ });
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// 统计方式变化处理
|
|
|
+function handleRemarkChange() {
|
|
|
+ // 清空之前选择的日期
|
|
|
+ queryParams.value.dataDate = null;
|
|
|
+ // 重置其他查询条件
|
|
|
+ queryParams.value.deviceId = null;
|
|
|
+ queryParams.value.result = "";
|
|
|
+
|
|
|
+ // 如果切换到按天统计,设置默认日期
|
|
|
+ if (queryParams.value.remark === 'day') {
|
|
|
+ const defaultDate = getDefaultDate();
|
|
|
+ queryParams.value.dataDate = defaultDate;
|
|
|
+ queryParams.value.startTime = defaultDate;
|
|
|
+ queryParams.value.endTime = defaultDate;
|
|
|
+ }
|
|
|
+ // 如果切换到按月统计,设置默认月份
|
|
|
+ else if (queryParams.value.remark === 'month') {
|
|
|
+ const currentMonth = getCurrentMonth();
|
|
|
+ queryParams.value.dataDate = currentMonth;
|
|
|
+ queryParams.value.startTime = currentMonth + "-01";
|
|
|
+ // 设置为该月的最后一天作为结束时间
|
|
|
+ const year = parseInt(currentMonth.split("-")[0]);
|
|
|
+ const month = parseInt(currentMonth.split("-")[1]);
|
|
|
+ const lastDay = new Date(year, month, 0).getDate();
|
|
|
+ queryParams.value.endTime = currentMonth + "-" + (lastDay < 10 ? "0" + lastDay : lastDay);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 统计方式改变后自动查询
|
|
|
+ handleFilter();
|
|
|
+}
|
|
|
+
|
|
|
+// 月份选择变化处理
|
|
|
+function handleMonthChange() {
|
|
|
+ if (queryParams.value.dataDate && queryParams.value.remark === 'month') {
|
|
|
+ // 设置为该月的第一天作为开始时间
|
|
|
+ queryParams.value.startTime = queryParams.value.dataDate + "-01";
|
|
|
+ // 设置为该月的最后一天作为结束时间
|
|
|
+ const year = parseInt(queryParams.value.dataDate.split("-")[0]);
|
|
|
+ const month = parseInt(queryParams.value.dataDate.split("-")[1]);
|
|
|
+ const lastDay = new Date(year, month, 0).getDate();
|
|
|
+ queryParams.value.endTime = queryParams.value.dataDate + "-" + (lastDay < 10 ? "0" + lastDay : lastDay);
|
|
|
+ } else {
|
|
|
+ queryParams.value.startTime = null;
|
|
|
+ queryParams.value.endTime = null;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 日期选择变化处理
|
|
|
+function handleDateChange() {
|
|
|
+ if (queryParams.value.dataDate && queryParams.value.remark === 'day') {
|
|
|
+ queryParams.value.startTime = queryParams.value.dataDate;
|
|
|
+ queryParams.value.endTime = queryParams.value.dataDate;
|
|
|
+ } else if (queryParams.value.dataDate && queryParams.value.remark === 'month') {
|
|
|
+ // 设置为该月的第一天作为开始时间
|
|
|
+ queryParams.value.startTime = queryParams.value.dataDate + "-01";
|
|
|
+ // 设置为该月的最后一天作为结束时间
|
|
|
+ const year = parseInt(queryParams.value.dataDate.split("-")[0]);
|
|
|
+ const month = parseInt(queryParams.value.dataDate.split("-")[1]);
|
|
|
+ const lastDay = new Date(year, month, 0).getDate();
|
|
|
+ queryParams.value.endTime = queryParams.value.dataDate + "-" + (lastDay < 10 ? "0" + lastDay : lastDay);
|
|
|
+ } else {
|
|
|
+ queryParams.value.startTime = null;
|
|
|
+ queryParams.value.endTime = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 日期改变后自动查询
|
|
|
+ handleFilter();
|
|
|
+}
|
|
|
+
|
|
|
+// 分析天数变化处理
|
|
|
+function handleDaysChange() {
|
|
|
+ getList(); // 分析天数变化时重新从后端获取数据
|
|
|
+}
|
|
|
+
|
|
|
+// 绘制趋势分析图(南丁格尔图)
|
|
|
+function drawTrendChart() {
|
|
|
+ if (!trendChartRef.value || !statistics.value) return;
|
|
|
+
|
|
|
+ // 销毁之前的实例
|
|
|
+ if (trendChartInstance) {
|
|
|
+ trendChartInstance.dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化新的实例
|
|
|
+ trendChartInstance = echarts.init(trendChartRef.value);
|
|
|
+
|
|
|
+ // 准备数据
|
|
|
+ const trendData = Object.entries(statistics.value.trendCounts).map(([name, value]) => ({
|
|
|
+ name,
|
|
|
+ value
|
|
|
+ }));
|
|
|
+
|
|
|
+ // 计算总数量,用于百分比计算
|
|
|
+ const totalValue = trendData.reduce((sum, item) => sum + item.value, 0);
|
|
|
+
|
|
|
+ // 定义颜色调色板,使颜色更加协调
|
|
|
+ const colorPalette = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de'];
|
|
|
+
|
|
|
+ // 配置图表选项
|
|
|
+ const option = {
|
|
|
+ color: colorPalette,
|
|
|
+ title: {
|
|
|
+ text: '趋势分析统计',
|
|
|
+ left: 'center'
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item',
|
|
|
+ formatter: '{a} <br/>{b} : {c} ({d}%)'
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ bottom: 10,
|
|
|
+ left: 'center',
|
|
|
+ data: trendData.map(item => item.name)
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '趋势分析',
|
|
|
+ type: 'pie',
|
|
|
+ radius: [30, 80],
|
|
|
+ center: ['50%', '50%'],
|
|
|
+ roseType: 'radius',
|
|
|
+ itemStyle: {
|
|
|
+ borderRadius: 5,
|
|
|
+ borderColor: '#fff',
|
|
|
+ borderWidth: 2
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ formatter: (params) => {
|
|
|
+ const percentage = totalValue > 0 ? ((params.value / totalValue) * 100).toFixed(2) : 0;
|
|
|
+ return `{name|${params.name}}\n{value|${params.value}} {percentage|${percentage}%}`;
|
|
|
+ },
|
|
|
+ rich: {
|
|
|
+ name: {
|
|
|
+ fontSize: 12,
|
|
|
+ color: '#666',
|
|
|
+ lineHeight: 14
|
|
|
+ },
|
|
|
+ value: {
|
|
|
+ fontSize: 14,
|
|
|
+ fontWeight: 'bold',
|
|
|
+ color: '#333',
|
|
|
+ lineHeight: 16
|
|
|
+ },
|
|
|
+ percentage: {
|
|
|
+ fontSize: 12,
|
|
|
+ color: '#999',
|
|
|
+ lineHeight: 14
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ labelLine: {
|
|
|
+ length: 5,
|
|
|
+ length2: 10
|
|
|
+ },
|
|
|
+ data: trendData
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '设备总数',
|
|
|
+ type: 'pie',
|
|
|
+ radius: [0, 30],
|
|
|
+ center: ['50%', '50%'],
|
|
|
+ itemStyle: {
|
|
|
+ color: '#5470c6'
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ position: 'center',
|
|
|
+ formatter: '设备总数\n{c}',
|
|
|
+ fontSize: 16,
|
|
|
+ fontWeight: 'bold',
|
|
|
+ lineHeight: 20,
|
|
|
+ rich: {
|
|
|
+ a: {
|
|
|
+ fontSize: 16,
|
|
|
+ color: '#333'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data: [{value: statistics.value.totalDevices, name: '设备总数'}]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ // 设置配置项
|
|
|
+ trendChartInstance.setOption(option);
|
|
|
+}
|
|
|
+
|
|
|
+// 绘制设备统计图(当月停机趋势折线图)
|
|
|
+function drawDeviceChart() {
|
|
|
+ if (!deviceChartRef.value) return;
|
|
|
+
|
|
|
+ // 销毁之前的实例
|
|
|
+ if (deviceChartInstance) {
|
|
|
+ deviceChartInstance.dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化新的实例
|
|
|
+ deviceChartInstance = echarts.init(deviceChartRef.value);
|
|
|
+
|
|
|
+ // 准备数据 - 使用专门定义的monthList
|
|
|
+ let dates = [];
|
|
|
+ let times = [];
|
|
|
+ console.error(monthList.value)
|
|
|
+ // 获取monthList数据
|
|
|
+ if (monthList.value && Array.isArray(monthList.value) && monthList.value.length > 0) {
|
|
|
+ dates = monthList.value.map(item => {
|
|
|
+ // 确保item和item.date存在
|
|
|
+ return item && item.date ? item.date : '';
|
|
|
+ }).filter(date => date !== ''); // 过滤掉空值
|
|
|
+
|
|
|
+ times = monthList.value.map(item => {
|
|
|
+ // 确保item和item.times存在
|
|
|
+ return item && typeof item.times !== 'undefined' ? item.times : 0;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果没有数据,显示提示信息
|
|
|
+ if (dates.length === 0) {
|
|
|
+ deviceChartInstance.clear();
|
|
|
+ deviceChartInstance.setOption({
|
|
|
+ title: {
|
|
|
+ text: '暂无数据',
|
|
|
+ left: 'center',
|
|
|
+ top: 'center'
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 配置图表选项
|
|
|
+ const option = {
|
|
|
+ title: {
|
|
|
+ text: '当月行走速度异常趋势',
|
|
|
+ left: 'center'
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ formatter: (params) => {
|
|
|
+ // 格式化日期显示,只显示日期部分
|
|
|
+ const date = params[0].axisValue.split(' ')[0];
|
|
|
+ return `${date}<br/>异常次数: ${params[0].data}`;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: dates.map(date => date.split(' ')[0]), // 只显示日期部分
|
|
|
+ name: '日期'
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ name: '异常次数'
|
|
|
+ },
|
|
|
+ series: [{
|
|
|
+ data: times,
|
|
|
+ type: 'line',
|
|
|
+ smooth: true,
|
|
|
+ itemStyle: {
|
|
|
+ color: '#5470c6'
|
|
|
+ },
|
|
|
+ areaStyle: {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ {offset: 0, color: 'rgba(84, 112, 198, 0.3)'},
|
|
|
+ {offset: 1, color: 'rgba(84, 112, 198, 0.05)'}
|
|
|
+ ])
|
|
|
+ },
|
|
|
+ markPoint: {
|
|
|
+ data: [
|
|
|
+ {type: 'max', name: '最大值'},
|
|
|
+ {type: 'min', name: '最小值'}
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ markLine: {
|
|
|
+ data: [
|
|
|
+ {type: 'average', name: '平均值'}
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ }]
|
|
|
+ };
|
|
|
+
|
|
|
+ // 设置配置项
|
|
|
+ deviceChartInstance.setOption(option);
|
|
|
+}
|
|
|
+
|
|
|
+// 计算统计数据
|
|
|
+function calculateStatistics(data) {
|
|
|
+ if (!data || !Array.isArray(data)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算设备总数
|
|
|
+ const totalDevices = data.length;
|
|
|
+
|
|
|
+ // 统计各趋势的数量
|
|
|
+ const trendCounts = {
|
|
|
+ "": 0,
|
|
|
+ "上升": 0,
|
|
|
+ "平稳": 0,
|
|
|
+ "下降": 0,
|
|
|
+ "明显下降": 0
|
|
|
+ };
|
|
|
+
|
|
|
+ data.forEach(item => {
|
|
|
+ if (trendCounts.hasOwnProperty(item.result)) {
|
|
|
+ trendCounts[item.result]++;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 只有在没有从后端获取到统计数据时才使用计算的统计数据
|
|
|
+ if (!statistics.value) {
|
|
|
+ statistics.value = {
|
|
|
+ totalDevices,
|
|
|
+ trendCounts
|
|
|
+ };
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 机台号变化处理
|
|
|
+function handleDeviceIdChange() {
|
|
|
+ filterData();
|
|
|
+}
|
|
|
+
|
|
|
+// 分析结果变化处理
|
|
|
+function handleResultChange() {
|
|
|
+ filterData();
|
|
|
+}
|
|
|
+
|
|
|
+/** 过滤操作 */
|
|
|
+function handleFilter() {
|
|
|
+ getList();
|
|
|
+}
|
|
|
+
|
|
|
+/** 重置过滤操作 */
|
|
|
+function resetFilter() {
|
|
|
+ proxy.resetForm("queryRef");
|
|
|
+ queryParams.value.deviceId = null;
|
|
|
+ queryParams.value.result = ""; // 重置时也保持默认选中
|
|
|
+ queryParams.value.remark = 'day'; // 重置为按天统计
|
|
|
+ queryParams.value.dataDate = getDefaultDate(); // 重置为默认日期
|
|
|
+ queryParams.value.startTime = getDefaultDate(); // 重置为默认日期
|
|
|
+ queryParams.value.endTime = getDefaultDate(); // 重置为默认日期
|
|
|
+ filterData(); // 改为调用filterData而不是getList()
|
|
|
+}
|
|
|
+
|
|
|
+// 过滤数据
|
|
|
+function filterData() {
|
|
|
+ if (!calcStopList.value || !Array.isArray(calcStopList.value)) {
|
|
|
+ filteredData.value = [];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果没有过滤条件,返回所有数据
|
|
|
+ if (!queryParams.value.deviceId && !queryParams.value.result) {
|
|
|
+ filteredData.value = calcStopList.value;
|
|
|
+ // 根据完整数据重新计算统计数据
|
|
|
+ calculateStatistics(calcStopList.value);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ filteredData.value = calcStopList.value.filter(item => {
|
|
|
+ // 根据机台号过滤(数字模糊匹配)
|
|
|
+ if (queryParams.value.deviceId &&
|
|
|
+ item.deviceId &&
|
|
|
+ item.deviceId.toString().indexOf(queryParams.value.deviceId) === -1) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据分析结果过滤
|
|
|
+ if (queryParams.value.result &&
|
|
|
+ item.result !== queryParams.value.result) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 根据过滤后的数据重新计算统计数据
|
|
|
+ calculateStatistics(filteredData.value);
|
|
|
+}
|
|
|
+
|
|
|
+/** 处理分析结果点击事件 */
|
|
|
+function handleResultClick(row) {
|
|
|
+ currentRow.value = row;
|
|
|
+ chartOpen.value = true;
|
|
|
+
|
|
|
+ // 在弹窗打开后初始化图表
|
|
|
+ setTimeout(() => {
|
|
|
+ initChart();
|
|
|
+ }, 100);
|
|
|
+}
|
|
|
+
|
|
|
+// 初始化图表
|
|
|
+function initChart() {
|
|
|
+ if (chartRef.value) {
|
|
|
+ // 销毁之前的实例
|
|
|
+ if (chartInstance) {
|
|
|
+ chartInstance.dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化新的实例
|
|
|
+ chartInstance = echarts.init(chartRef.value);
|
|
|
+
|
|
|
+ // 准备数据
|
|
|
+ let dates = currentRow.value.list.map(item => item.date);
|
|
|
+ const times = currentRow.value.list.map(item => item.times);
|
|
|
+
|
|
|
+ // 如果是按月统计,只显示日期部分,不显示时间
|
|
|
+ if (queryParams.value.remark === 'month') {
|
|
|
+ dates = dates.map(date => {
|
|
|
+ // 只保留日期部分 YYYY-MM-DD
|
|
|
+ if (date.includes(' ')) {
|
|
|
+ return date.split(' ')[0];
|
|
|
+ }
|
|
|
+ return date;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 配置图表选项
|
|
|
+ const option = {
|
|
|
+ title: {
|
|
|
+ text: '异常次数趋势图',
|
|
|
+ left: 'center'
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ formatter: function (params) {
|
|
|
+ // 如果是按月统计,提示框中也只显示日期
|
|
|
+ if (queryParams.value.remark === 'month') {
|
|
|
+ const date = params[0].axisValue;
|
|
|
+ const count = params[0].data;
|
|
|
+ return `${date}<br />次数: ${count}`;
|
|
|
+ }
|
|
|
+ return params[0].axisValue + '<br />' + params[0].seriesName + ': ' + params[0].data;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: dates,
|
|
|
+ name: '日期'
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ name: '次数(次)'
|
|
|
+ },
|
|
|
+ series: [{
|
|
|
+ data: times,
|
|
|
+ type: 'line',
|
|
|
+ smooth: true,
|
|
|
+ markPoint: {
|
|
|
+ data: [
|
|
|
+ {type: 'max', name: '最大值'},
|
|
|
+ {type: 'min', name: '最小值'}
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ markLine: {
|
|
|
+ data: [
|
|
|
+ {type: 'average', name: '平均值'}
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ }]
|
|
|
+ };
|
|
|
+
|
|
|
+ // 设置配置项
|
|
|
+ chartInstance.setOption(option);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 关闭图表弹窗 */
|
|
|
+function closeChart() {
|
|
|
+ chartOpen.value = false;
|
|
|
+ // 销毁图表实例
|
|
|
+ if (chartInstance) {
|
|
|
+ chartInstance.dispose();
|
|
|
+ chartInstance = null;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 监听窗口大小变化,自动调整图表大小
|
|
|
+const handleResize = () => {
|
|
|
+ if (trendChartInstance) {
|
|
|
+ trendChartInstance.resize();
|
|
|
+ }
|
|
|
+ if (deviceChartInstance) {
|
|
|
+ deviceChartInstance.resize();
|
|
|
+ }
|
|
|
+ if (chartInstance) {
|
|
|
+ chartInstance.resize();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ getList();
|
|
|
+ window.addEventListener('resize', handleResize);
|
|
|
+});
|
|
|
+
|
|
|
+onBeforeUnmount(() => {
|
|
|
+ window.removeEventListener('resize', handleResize);
|
|
|
+ // 销毁图表实例
|
|
|
+ if (trendChartInstance) {
|
|
|
+ trendChartInstance.dispose();
|
|
|
+ }
|
|
|
+ if (deviceChartInstance) {
|
|
|
+ deviceChartInstance.dispose();
|
|
|
+ }
|
|
|
+ if (chartInstance) {
|
|
|
+ chartInstance.dispose();
|
|
|
+ }
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.statistic-card {
|
|
|
+ margin-bottom: 20px;
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ border: 1px solid #ebeef5;
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.statistic-container {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ flex-wrap: wrap;
|
|
|
+}
|
|
|
+
|
|
|
+.statistic-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin-right: 20px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.statistic-label {
|
|
|
+ font-weight: bold;
|
|
|
+ margin-right: 5px;
|
|
|
+ color: #606266;
|
|
|
+}
|
|
|
+
|
|
|
+.statistic-value {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #409eff;
|
|
|
+}
|
|
|
+
|
|
|
+.statistic-divider {
|
|
|
+ width: 1px;
|
|
|
+ height: 20px;
|
|
|
+ background-color: #dcdfe6;
|
|
|
+ margin-right: 20px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+</style>
|