修复首页“设备使用频次”功能X轴显示错误 #29

Merged
dyf merged 1 commits from liubiao/dyf-vue-ui:main into main 2026-02-26 09:07:43 +08:00

View File

@ -5,19 +5,27 @@
<h2>数据总览</h2>
<div class="data-item">
<div class="data_bck">
<div class="number"><span>{{ DataOverview.devicesNumber }}</span> </div>
<div class="number">
<span>{{ DataOverview.devicesNumber }}</span>
</div>
<div class="title_number">设备数量</div>
</div>
<div class="data_green">
<div class="number"><span>{{ DataOverview.equipmentOnline }}</span> </div>
<div class="number">
<span>{{ DataOverview.equipmentOnline }}</span>
</div>
<div class="title_number">在线设备</div>
</div>
<div class="data_orgine">
<div class="number"><span>{{ DataOverview.binding }}</span> </div>
<div class="number">
<span>{{ DataOverview.binding }}</span>
</div>
<div class="title_number">已绑定设备</div>
</div>
<div class="data_red">
<div class="number"><span>{{ DataOverview.equipmentAbnormal }}</span> </div>
<div class="number">
<span>{{ DataOverview.equipmentAbnormal }}</span>
</div>
<div class="title_number">异常设备</div>
</div>
</div>
@ -28,10 +36,8 @@
<div class="content-row">
<h2>设备分类</h2>
<div class="card-header">
<div v-for="(item, index) in deviceList" :key="index" class="progress-item"
style="display: inline-block; margin-right: 40px;">
<el-progress :stroke-width="7" type="circle" :width="100"
:percentage="item.total === 0 ? 0 : (item.current / item.total) * 100">
<div v-for="(item, index) in deviceList" :key="index" class="progress-item" style="display: inline-block; margin-right: 40px">
<el-progress :stroke-width="7" type="circle" :width="100" :percentage="item.total === 0 ? 0 : (item.current / item.total) * 100">
<template #default>
<div class="progress-text">
<span class="current">{{ item.current }}</span>
@ -77,20 +83,12 @@
<div class="card-header">
<h2>设备使用频次</h2>
<div class="chart-controls">
<el-select v-model="deviceType" placeholder="设备类型" style="width: 150px;"
@change="handleDeviceTypeChange">
<el-option v-for="item in deviceTypeOptions" :key="item.value" :label="item.typeName"
:value="item.deviceTypeId" />
<el-select v-model="deviceType" placeholder="设备类型" style="width: 150px" @change="handleDeviceTypeChange">
<el-option v-for="item in deviceTypeOptions" :key="item.value" :label="item.typeName" :value="item.deviceTypeId" />
</el-select>
<div class="tab-group">
<div class="tab-item" :class="{ 'tab-item--active': activeTab === '1' }"
@click="updateFrequencyChart('1')">
近半年
</div>
<div class="tab-item" :class="{ 'tab-item--active': activeTab === '2' }"
@click="updateFrequencyChart('2')">
近一年
</div>
<div class="tab-item" :class="{ 'tab-item--active': activeTab === '1' }" @click="updateFrequencyChart('1')">近半年</div>
<div class="tab-item" :class="{ 'tab-item--active': activeTab === '2' }" @click="updateFrequencyChart('2')">近一年</div>
</div>
</div>
</div>
@ -141,8 +139,8 @@
</template>
<script setup name="Index" lang="ts">
import api from '@/api/home/index'
import { DataOverviewType } from '@/api/home/types'
import api from '@/api/home/index';
import { DataOverviewType } from '@/api/home/types';
import router from '@/router';
import * as echarts from 'echarts'; // 引入ECharts核心库
import apiTypeAll from '@/api/equipmentManagement/device/index';
@ -153,15 +151,15 @@ const DataOverview = ref<DataOverviewType>({
equipmentAbnormal: 0
});
const deviceTypeOptions = ref([]); //设备类型
const deviceType = ref()
const deviceType = ref();
const activeTab = ref('1');
const alarmsData = ref()
const alarmsData = ref();
// ---------------------- 基础数据 ----------------------
// 设备分类数据
const deviceList = ref([
{ name: "4G设备", current: 0, total: 0 },
{ name: "蓝牙设备", current: 0, total: 0 },
{ name: "4G&蓝牙设备", current: 0, total: 0 },
{ name: '4G设备', current: 0, total: 0 },
{ name: '蓝牙设备', current: 0, total: 0 },
{ name: '4G&蓝牙设备', current: 0, total: 0 }
]);
// ---------------------- 图表Ref用于挂载图表实例 ----------------------
const frequencyChartRef = ref<HTMLDivElement | null>(null); // 设备使用频次折线图
@ -193,7 +191,7 @@ const initFrequencyChart = async (range: any = '1', deviceTypeId: any) => {
try {
let data = {
deviceTypeId: deviceTypeId
}
};
const res = await api.getEquipmentUsageData(range, data);
const monthData = res.data[0] || {};
const monthKeys = ['m1', 'm2', 'm3', 'm4', 'm5', 'm6', 'm7', 'm8', 'm9', 'm10', 'm11', 'm12'];
@ -202,23 +200,24 @@ const initFrequencyChart = async (range: any = '1', deviceTypeId: any) => {
let filteredKeys, filteredNames, yAxisData;
const today = new Date();
const currentMonth = today.getMonth();
if (range === '1') {
let mm = 6;
if (range === '2') {
mm = 12;
}
const result = [];
for (let i = 0; i < 6; i++) {
for (let i = 0; i < mm; i++) {
const targetMonth = (currentMonth - i + 12) % 12;
result.push(targetMonth);
}
const recent6Months = result.reverse();
const recentMonths = result.reverse();
// 匹配接口字段和名称
filteredKeys = recent6Months.map(monthIndex => monthKeys[monthIndex]);
filteredNames = recent6Months.map(monthIndex => monthNames[monthIndex]);
yAxisData = filteredKeys.map(key => monthData[key] || 0);
} else {
// 近一年全部12个月1月→12月
filteredKeys = monthKeys;
filteredNames = monthNames;
yAxisData = filteredKeys.map(key => monthData[key] || 0);
}
filteredKeys = recentMonths.map((monthIndex) => monthKeys[monthIndex]);
filteredNames = recentMonths.map((monthIndex) => monthNames[monthIndex]);
yAxisData = filteredKeys.map((key) => monthData[key] || 0);
const chartData = {
xAxis: filteredNames,
yAxis: yAxisData,
@ -258,7 +257,10 @@ const initFrequencyChart = async (range: any = '1', deviceTypeId: any) => {
areaStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(64, 158, 255, 0.3)' },
{ offset: 1, color: 'rgba(64, 158, 255, 0)' }
@ -267,21 +269,22 @@ const initFrequencyChart = async (range: any = '1', deviceTypeId: any) => {
},
markPoint: {
data: chartData.peak.value
? [{
? [
{
name: chartData.peak.name,
value: chartData.peak.value,
xAxis: chartData.xAxis.indexOf(chartData.peak.month),
yAxis: chartData.peak.value,
itemStyle: { color: '#409eff' }
}]
}
]
: []
}
}
]
};
frequencyChartInstance.setOption(option);
} catch (error) {
}
} catch (error) {}
};
/**
* 2. 报警环形图(今日报警处理占比)
@ -292,7 +295,7 @@ const initAlarmRingChart = async () => {
try {
const res = await api.getAlarmInformation({});
const { alarmsTotalToday = 0, processingAlarmToday = 0 } = res.data || {};
alarmsData.value = res.data || '0'
alarmsData.value = res.data || '0';
alarmRingChartInstance = echarts.init(alarmRingChartRef.value);
const option = {
tooltip: {
@ -312,16 +315,13 @@ const initAlarmRingChart = async () => {
label: {
show: true,
position: 'center',
formatter: [
'{valueStyle|' + alarmsTotalToday + '/' + processingAlarmToday + '}',
'{textStyle|今日报警/处理}'
].join('\n'), // 换行
formatter: ['{valueStyle|' + alarmsTotalToday + '/' + processingAlarmToday + '}', '{textStyle|今日报警/处理}'].join('\n'), // 换行
// 关键:配置 rich 定义样式
rich: {
valueStyle: {
color: '#333', // 数字颜色
fontSize: 18, // 数字字号
fontWeight: 'bold',// 数字加粗
fontWeight: 'bold', // 数字加粗
lineHeight: 24 // 行高(控制与下一行间距)
},
textStyle: {
@ -338,9 +338,8 @@ const initAlarmRingChart = async () => {
show: false // 隐藏标签连接线
},
data: [
{
value:processingAlarmToday ,
value: processingAlarmToday,
name: '已处理',
itemStyle: { color: '#07BE75' }
},
@ -348,17 +347,15 @@ const initAlarmRingChart = async () => {
value: alarmsTotalToday,
name: '报警',
itemStyle: { color: '#F65757' }
},
}
]
}
]
};
alarmRingChartInstance.setOption(option);
// 报警柱状图
initAlarmBarChart()
} catch (error) {
}
initAlarmBarChart();
} catch (error) {}
};
/**
@ -373,8 +370,8 @@ const initAlarmBarChart = () => {
{ name: '电子围栏', field: 'fenceElectronic' } // fenceElectronic
];
const alarmTypes = alarmTypeMap.map(item => item.name);
const alarmCounts = alarmTypeMap.map(item => {
const alarmTypes = alarmTypeMap.map((item) => item.name);
const alarmCounts = alarmTypeMap.map((item) => {
const value = alarmsData.value[item.field]; // 提取对应字段值
console.log(`${item.name}数值:`, value); // 打印每个类型的数值
return value;
@ -429,19 +426,18 @@ const updateFrequencyChart = (tabValue: any) => {
if (frequencyChartInstance) {
frequencyChartInstance.dispose();
}
deviceType.value=''
deviceType.value = '';
initFrequencyChart(tabValue, '');
};
const handleDeviceTypeChange = (all) => {
initFrequencyChart(activeTab.value, all);
};
// 首页统计接口
const getData = async () => {
// 设备总览
api.getDataOverview({}).then(res => {
DataOverview.value = res.data
})
api.getDataOverview({}).then((res) => {
DataOverview.value = res.data;
});
// 设备分类
try {
const res = await api.getEquipmentClassification({});
@ -450,48 +446,42 @@ const getData = async () => {
// 映射数据current 为各类型设备数量total 为总设备数6
deviceList.value = [
{
name: "4G设备",
name: '4G设备',
current: equipment4G,
total: total
},
{
name: "蓝牙设备",
name: '蓝牙设备',
current: deviceBluetooth,
total: total
},
{
name: "4G&蓝牙设备",
name: '4G&蓝牙设备',
current: devices4GAndBluetooth,
total: total
},
}
];
} catch (error) {
console.log('获取设备分类数据失败:', error);
}
// 设备类型
apiTypeAll.deviceTypeAll().then(res => {
apiTypeAll
.deviceTypeAll()
.then((res) => {
if (res.code == 200) {
const originalData = Array.isArray(res.data) ? res.data : [];
deviceTypeOptions.value = [{ typeName: '全部', deviceTypeId: ''}].concat(originalData);
deviceTypeOptions.value = [{ typeName: '全部', deviceTypeId: '' }].concat(originalData);
}
}).catch(err => {
})
.catch((err) => {});
};
// ---------------------- 生命周期钩子(初始化/销毁图表) ----------------------
onMounted(() => {
// 页面加载时初始化所有图表
initFrequencyChart('1', '');
initAlarmRingChart();
getData()
getData();
// 监听窗口 resize自动调整图表大小
window.addEventListener('resize', () => {
@ -506,7 +496,7 @@ onUnmounted(() => {
frequencyChartInstance?.dispose();
alarmRingChartInstance?.dispose();
//alarmBarChartInstance?.dispose();
window.removeEventListener('resize', () => { });
window.removeEventListener('resize', () => {});
});
</script>
@ -528,7 +518,7 @@ onUnmounted(() => {
.data_green,
.data_orgine,
.data_red {
width:23%;
width: 23%;
height: 135px;
border-radius: 10px;
position: relative;
@ -556,7 +546,7 @@ onUnmounted(() => {
}
.number {
padding-top:30px;
padding-top: 30px;
font-size: 18px;
span {
@ -675,7 +665,6 @@ onUnmounted(() => {
padding: 16px;
height: 360px; // 固定图表卡片高度,避免布局错乱
.card-body {
height: calc(100% - 40px); // 卡片内容区高度减去header高度
}
@ -713,7 +702,7 @@ onUnmounted(() => {
}
.stat.green {
color: #07BE75;
color: #07be75;
}
.label {