forked from dyf/dyf-vue-ui
修复首页“设备使用频次”功能X轴显示错误
This commit is contained in:
@ -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') {
|
||||
const result = [];
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const targetMonth = (currentMonth - i + 12) % 12;
|
||||
result.push(targetMonth);
|
||||
}
|
||||
const recent6Months = 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);
|
||||
|
||||
let mm = 6;
|
||||
if (range === '2') {
|
||||
mm = 12;
|
||||
}
|
||||
const result = [];
|
||||
for (let i = 0; i < mm; i++) {
|
||||
const targetMonth = (currentMonth - i + 12) % 12;
|
||||
result.push(targetMonth);
|
||||
}
|
||||
const recentMonths = result.reverse();
|
||||
|
||||
|
||||
// 匹配接口字段和名称
|
||||
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' }
|
||||
}]
|
||||
? [
|
||||
{
|
||||
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,22 +315,19 @@ 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',// 数字加粗
|
||||
lineHeight: 24 // 行高(控制与下一行间距)
|
||||
color: '#333', // 数字颜色
|
||||
fontSize: 18, // 数字字号
|
||||
fontWeight: 'bold', // 数字加粗
|
||||
lineHeight: 24 // 行高(控制与下一行间距)
|
||||
},
|
||||
textStyle: {
|
||||
color: 'rgba(56, 64, 79, 0.6)', // 文字颜色(可自定义)
|
||||
fontSize: 14, // 文字字号
|
||||
lineHeight: 20 // 文字行高
|
||||
fontSize: 14, // 文字字号
|
||||
lineHeight: 20 // 文字行高
|
||||
}
|
||||
},
|
||||
fontSize: 16,
|
||||
@ -338,27 +338,24 @@ const initAlarmRingChart = async () => {
|
||||
show: false // 隐藏标签连接线
|
||||
},
|
||||
data: [
|
||||
|
||||
{
|
||||
value:processingAlarmToday ,
|
||||
value: processingAlarmToday,
|
||||
name: '已处理',
|
||||
itemStyle: { color: '#07BE75' }
|
||||
},
|
||||
{
|
||||
{
|
||||
value: alarmsTotalToday,
|
||||
name: '报警',
|
||||
itemStyle: { color: '#F65757' }
|
||||
},
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
alarmRingChartInstance.setOption(option);
|
||||
// 报警柱状图
|
||||
initAlarmBarChart()
|
||||
} catch (error) {
|
||||
}
|
||||
initAlarmBarChart();
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -367,21 +364,21 @@ const initAlarmRingChart = async () => {
|
||||
const initAlarmBarChart = () => {
|
||||
if (!alarmBarChartRef.value) return;
|
||||
const alarmTypeMap = [
|
||||
{ name: '强制报警', field: 'alarmForced' }, // alarmForced
|
||||
{ name: '撞击闯入', field: 'intrusionImpact' }, // intrusionImpact
|
||||
{ name: '自动报警', field: 'alarmAuto' }, // alarmAuto
|
||||
{ name: '电子围栏', field: 'fenceElectronic' } // fenceElectronic
|
||||
{ name: '强制报警', field: 'alarmForced' }, // alarmForced
|
||||
{ name: '撞击闯入', field: 'intrusionImpact' }, // intrusionImpact
|
||||
{ name: '自动报警', field: 'alarmAuto' }, // alarmAuto
|
||||
{ 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;
|
||||
});
|
||||
const commonGradient = new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(246, 87, 87, 1)' }, // 渐变起点:#F65757(不透明)
|
||||
{ offset: 1, color: 'rgba(224, 52, 52, 0)' } // 渐变终点:#E03434(全透明)
|
||||
{ offset: 0, color: 'rgba(246, 87, 87, 1)' }, // 渐变起点:#F65757(不透明)
|
||||
{ offset: 1, color: 'rgba(224, 52, 52, 0)' } // 渐变终点:#E03434(全透明)
|
||||
]);
|
||||
const option = {
|
||||
tooltip: {
|
||||
@ -413,8 +410,8 @@ const initAlarmBarChart = () => {
|
||||
data: alarmCounts,
|
||||
barWidth: '20%',
|
||||
itemStyle: {
|
||||
color: commonGradient, // 所有柱子共用同一渐变色
|
||||
borderRadius: 4 // 统一4px圆角
|
||||
color: commonGradient, // 所有柱子共用同一渐变色
|
||||
borderRadius: 4 // 统一4px圆角
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -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 => {
|
||||
if (res.code == 200) {
|
||||
const originalData = Array.isArray(res.data) ? res.data : [];
|
||||
deviceTypeOptions.value = [{ typeName: '全部', deviceTypeId: ''}].concat(originalData);
|
||||
}
|
||||
}).catch(err => {
|
||||
|
||||
})
|
||||
apiTypeAll
|
||||
.deviceTypeAll()
|
||||
.then((res) => {
|
||||
if (res.code == 200) {
|
||||
const originalData = Array.isArray(res.data) ? res.data : [];
|
||||
deviceTypeOptions.value = [{ typeName: '全部', deviceTypeId: '' }].concat(originalData);
|
||||
}
|
||||
})
|
||||
.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 {
|
||||
|
||||
Reference in New Issue
Block a user