feat:关键指标loading和title优化
This commit is contained in:
@@ -1045,7 +1045,7 @@ export default {
|
||||
unitSelect:'To better display the image, the same unit needs to be selected. The current unit is:',
|
||||
avg:'(average)',
|
||||
total:'(total)',
|
||||
ago1:'Past 1 day value',
|
||||
ago1:'Past 24 hrs value',
|
||||
ago7:'Past 7 days value',
|
||||
ago30:'Past 30 days value',
|
||||
},
|
||||
@@ -1079,6 +1079,7 @@ export default {
|
||||
"changeBar":"Change to Bar Charts",
|
||||
"chooseShowMetrics":"Select the metric you want to display",
|
||||
"chooseMetrics":"Select an indicator",
|
||||
"tips":"The percentages and rates are averages,the counts are statistical values.",
|
||||
},
|
||||
voiceOverView:{
|
||||
"voiceTitle":"Voice Calls Dashboard",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted, nextTick, computed, h } from 'vue';
|
||||
|
||||
|
||||
import * as echarts from 'echarts/core';
|
||||
import {
|
||||
GridComponent,
|
||||
@@ -20,7 +22,7 @@ import type { Dayjs } from 'dayjs';
|
||||
import dayjs from 'dayjs';
|
||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||
import { generateColorRGBA } from '@/utils/generate-utils';
|
||||
import { LineOutlined } from '@ant-design/icons-vue';
|
||||
import { LineOutlined, InfoCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { TableColumnType } from 'ant-design-vue';
|
||||
import useNeInfoStore from '@/store/modules/neinfo';
|
||||
const { t, currentLocale } = useI18n();
|
||||
@@ -200,7 +202,6 @@ const fetchNeList = async () => {
|
||||
}
|
||||
});
|
||||
|
||||
console.log('网元列表获取成功:', neList.value);
|
||||
} else {
|
||||
message.warning({
|
||||
content: t('common.noData'),
|
||||
@@ -223,12 +224,9 @@ const fnRealTimeSwitch = (bool: boolean) => {
|
||||
if (!ws.value) {
|
||||
ws.value = new WS();
|
||||
}
|
||||
// 只清空图表数据,不影响表格统计数据
|
||||
chartData.value = [];
|
||||
|
||||
// 初始化统计数据表格
|
||||
fnInitKpiStatsData();
|
||||
|
||||
tableLoading.value = true;
|
||||
const options: OptionsType = {
|
||||
url: '/ws',
|
||||
params: {
|
||||
@@ -244,7 +242,6 @@ const fnRealTimeSwitch = (bool: boolean) => {
|
||||
} else if (ws.value) {
|
||||
ws.value.close();
|
||||
ws.value = null;
|
||||
tableLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -259,7 +256,6 @@ const wsMessage = (res: Record<string, any>) => {
|
||||
}
|
||||
const { code, data } = res;
|
||||
if (code === RESULT_CODE_ERROR || !data?.groupId) {
|
||||
tableLoading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -281,11 +277,8 @@ const wsMessage = (res: Record<string, any>) => {
|
||||
newData[key] = Number(kpiEvent[kpiId]) || 0;
|
||||
}
|
||||
|
||||
// 更新数据
|
||||
// 更新图表数据(只影响图表,不影响表格)
|
||||
updateChartData(newData);
|
||||
if (tableLoading.value) {
|
||||
tableLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 添加数据处理函数
|
||||
@@ -318,7 +311,6 @@ const fetchChartData = async () => {
|
||||
updateChart();
|
||||
return;
|
||||
}
|
||||
tableLoading.value = true;
|
||||
rangeLoading.value = true;
|
||||
try {
|
||||
const [startTime, endTime] = dateRange.value;
|
||||
@@ -389,12 +381,10 @@ const fetchChartData = async () => {
|
||||
});
|
||||
|
||||
updateChart();
|
||||
updateKpiStats();
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch chart data:', error);
|
||||
message.error(t('common.getInfoFail'));
|
||||
} finally {
|
||||
tableLoading.value = false;
|
||||
rangeLoading.value = false;
|
||||
}
|
||||
};
|
||||
@@ -533,9 +523,9 @@ const themeObserver = new MutationObserver(() => {
|
||||
chart!.setOption(option, true);
|
||||
// 强制重新渲染
|
||||
chart!.resize();
|
||||
// 更新统计数据
|
||||
// 更新统计数据表格(不重新获取数据)
|
||||
nextTick(() => {
|
||||
updateKpiStats();
|
||||
fnInitKpiStatsData();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -684,15 +674,15 @@ const updateChart = () => {
|
||||
//钩子函数
|
||||
onMounted(async () => {
|
||||
try {
|
||||
// 获取网元列表
|
||||
// 获取网元列表和指标信息(这两个很快)
|
||||
await fetchNeList();
|
||||
// 获取所有网元的指标
|
||||
await fetchSpecificKPI();
|
||||
await nextTick();
|
||||
|
||||
// 初始化统计数据表格
|
||||
// 立即初始化表格结构,显示标题行(空的统计值)
|
||||
fnInitKpiStatsData();
|
||||
|
||||
// 初始化图表
|
||||
const container = document.getElementById('chartContainer');
|
||||
if (container && !chart) {
|
||||
chart = echarts.init(container);
|
||||
@@ -717,11 +707,18 @@ onMounted(async () => {
|
||||
} else {
|
||||
console.error('Chart container not found');
|
||||
}
|
||||
|
||||
// 添加主题观察器
|
||||
themeObserver.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['data-theme'],
|
||||
});
|
||||
|
||||
// 异步获取统计数据(这个慢,放在最后)
|
||||
nextTick(() => {
|
||||
fnGetKpiStatsData();
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize:', error);
|
||||
message.error(t('common.initFail'));
|
||||
@@ -777,7 +774,7 @@ const fetchSpecificKPI = async () => {
|
||||
if (kpiColumns.value.length === 0) {
|
||||
console.warn('No KPIs found');
|
||||
} else {
|
||||
console.log(`Found ${kpiColumns.value.length} total KPIs`);
|
||||
// console.log(`Found ${kpiColumns.value.length} total KPIs`);
|
||||
}
|
||||
|
||||
return kpiColumns.value;
|
||||
@@ -842,8 +839,7 @@ const updateChartData = (newData: ChartDataItem) => {
|
||||
chart.setOption(option);
|
||||
});
|
||||
|
||||
// 更新统计数据 - 重新获取完整的30天数据
|
||||
fnGetKpiStatsData();
|
||||
// 实时数据只更新图表,不影响统计表格数据
|
||||
} catch (error) {
|
||||
console.error('Failed to update chart:', error);
|
||||
}
|
||||
@@ -876,9 +872,9 @@ function fnInitKpiStatsData() {
|
||||
kpiStats.value.push({
|
||||
kpiId: `${kpiId}_${ne.neId}`,
|
||||
title: `${kpi.title}(${ne.neId})`,
|
||||
last1Day: '-', // 默认值,显示加载中状态
|
||||
last7Days: '-',
|
||||
last30Days: '-',
|
||||
last1Day: '', // 空白显示,loading状态表示正在获取数据
|
||||
last7Days: '',
|
||||
last30Days: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -891,6 +887,7 @@ async function fnGetKpiStatsData() {
|
||||
return;
|
||||
}
|
||||
|
||||
tableLoading.value = true;
|
||||
const now = new Date();
|
||||
const now_ms = now.getTime();
|
||||
const day1_start = now_ms - (1 * 24 * 60 * 60 * 1000); // 1天前
|
||||
@@ -898,10 +895,11 @@ async function fnGetKpiStatsData() {
|
||||
const day30_start = now_ms - (30 * 24 * 60 * 60 * 1000); // 30天前
|
||||
|
||||
try {
|
||||
// 为每个网元的每个指标分别获取近30天的数据
|
||||
// 创建所有网元的并发请求
|
||||
const requests = [];
|
||||
|
||||
for (const neType of ALL_NE_TYPES) {
|
||||
for (const ne of neList.value[neType]) {
|
||||
// 请求近30天的数据
|
||||
const params = {
|
||||
neType,
|
||||
neId: ne.neId,
|
||||
@@ -913,79 +911,103 @@ async function fnGetKpiStatsData() {
|
||||
kpiIds: TARGET_KPI_IDS[neType].join(','),
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await listKPIData(params);
|
||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||
// 为每个指标计算统计值
|
||||
for (const kpiId of TARGET_KPI_IDS[neType]) {
|
||||
const kpi = kpiColumns.value.find(col => col.kpiId === kpiId);
|
||||
if (!kpi) continue;
|
||||
// 添加到并发请求数组
|
||||
requests.push(
|
||||
listKPIData(params).then(res => ({
|
||||
neType,
|
||||
neId: ne.neId,
|
||||
success: res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data),
|
||||
data: res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data) ? res.data : []
|
||||
})).catch(error => {
|
||||
console.error(`获取网元${ne.neId}统计数据失败:`, error);
|
||||
return {
|
||||
neType,
|
||||
neId: ne.neId,
|
||||
success: false,
|
||||
data: []
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const key = `${kpiId}_${ne.neId}`;
|
||||
// 并发执行所有请求
|
||||
const results = await Promise.all(requests);
|
||||
|
||||
// 根据时间范围筛选非零数据
|
||||
const data1Day = res.data.filter((item: any) => {
|
||||
const itemTime = Number(item.timeGroup);
|
||||
const value = item[kpiId] ? Number(item[kpiId]) : 0;
|
||||
return itemTime >= day1_start && value !== 0;
|
||||
});
|
||||
// 处理所有结果
|
||||
results.forEach(result => {
|
||||
const { neType, neId, success, data } = result;
|
||||
|
||||
const data7Days = res.data.filter((item: any) => {
|
||||
const itemTime = Number(item.timeGroup);
|
||||
const value = item[kpiId] ? Number(item[kpiId]) : 0;
|
||||
return itemTime >= day7_start && value !== 0;
|
||||
});
|
||||
if (success && data.length > 0) {
|
||||
// 为每个指标计算统计值
|
||||
for (const kpiId of TARGET_KPI_IDS[neType as NeType]) {
|
||||
const kpi = kpiColumns.value.find(col => col.kpiId === kpiId);
|
||||
if (!kpi) continue;
|
||||
|
||||
const data30Days = res.data.filter((item: any) => {
|
||||
const itemTime = Number(item.timeGroup);
|
||||
const value = item[kpiId] ? Number(item[kpiId]) : 0;
|
||||
return itemTime >= day30_start && value !== 0;
|
||||
});
|
||||
const key = `${kpiId}_${neId}`;
|
||||
|
||||
// 计算统计值(只对非零数据进行计算,关键指标多为次数类使用累计值)
|
||||
const calculateValue = (dataArray: any[]) => {
|
||||
if (dataArray.length === 0) return 0;
|
||||
// 根据时间范围筛选非零数据
|
||||
const data1Day = data.filter((item: any) => {
|
||||
const itemTime = Number(item.timeGroup);
|
||||
const value = item[kpiId] ? Number(item[kpiId]) : 0;
|
||||
return itemTime >= day1_start && value !== 0;
|
||||
});
|
||||
|
||||
const values = dataArray.map((item: any) => Number(item[kpiId]));
|
||||
// 关键指标多为次数类,使用累计值
|
||||
return Number(values.reduce((sum, val) => sum + val, 0).toFixed(2));
|
||||
};
|
||||
const data7Days = data.filter((item: any) => {
|
||||
const itemTime = Number(item.timeGroup);
|
||||
const value = item[kpiId] ? Number(item[kpiId]) : 0;
|
||||
return itemTime >= day7_start && value !== 0;
|
||||
});
|
||||
|
||||
// 更新对应的统计数据
|
||||
const statsIndex = kpiStats.value.findIndex((item: any) => item.kpiId === key);
|
||||
if (statsIndex !== -1) {
|
||||
kpiStats.value[statsIndex].last1Day = calculateValue(data1Day);
|
||||
kpiStats.value[statsIndex].last7Days = calculateValue(data7Days);
|
||||
kpiStats.value[statsIndex].last30Days = calculateValue(data30Days);
|
||||
}
|
||||
}
|
||||
const data30Days = data.filter((item: any) => {
|
||||
const itemTime = Number(item.timeGroup);
|
||||
const value = item[kpiId] ? Number(item[kpiId]) : 0;
|
||||
return itemTime >= day30_start && value !== 0;
|
||||
});
|
||||
|
||||
// 计算统计值(只对非零数据进行计算,关键指标多为次数类使用累计值)
|
||||
const calculateValue = (dataArray: any[]) => {
|
||||
if (dataArray.length === 0) return 0;
|
||||
|
||||
const values = dataArray.map((item: any) => Number(item[kpiId]));
|
||||
// 关键指标多为次数类,使用累计值
|
||||
return Number(values.reduce((sum, val) => sum + val, 0).toFixed(2));
|
||||
};
|
||||
|
||||
// 更新对应的统计数据
|
||||
const statsIndex = kpiStats.value.findIndex((item: any) => item.kpiId === key);
|
||||
if (statsIndex !== -1) {
|
||||
kpiStats.value[statsIndex].last1Day = calculateValue(data1Day);
|
||||
kpiStats.value[statsIndex].last7Days = calculateValue(data7Days);
|
||||
kpiStats.value[statsIndex].last30Days = calculateValue(data30Days);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`获取网元${ne.neId}统计数据失败:`, error);
|
||||
// 如果获取失败,保持默认值
|
||||
for (const kpiId of TARGET_KPI_IDS[neType]) {
|
||||
const key = `${kpiId}_${ne.neId}`;
|
||||
const statsIndex = kpiStats.value.findIndex((item: any) => item.kpiId === key);
|
||||
if (statsIndex !== -1) {
|
||||
kpiStats.value[statsIndex].last1Day = '-';
|
||||
kpiStats.value[statsIndex].last7Days = '-';
|
||||
kpiStats.value[statsIndex].last30Days = '-';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 如果获取失败,保持空白显示
|
||||
for (const kpiId of TARGET_KPI_IDS[neType as NeType]) {
|
||||
const key = `${kpiId}_${neId}`;
|
||||
const statsIndex = kpiStats.value.findIndex((item: any) => item.kpiId === key);
|
||||
if (statsIndex !== -1) {
|
||||
kpiStats.value[statsIndex].last1Day = '';
|
||||
kpiStats.value[statsIndex].last7Days = '';
|
||||
kpiStats.value[statsIndex].last30Days = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 更新图表显示
|
||||
updateChartLegendSelect();
|
||||
} catch (error) {
|
||||
console.error('获取统计数据失败:', error);
|
||||
// 如果获取失败,保持默认值
|
||||
// 如果获取失败,保持空白显示
|
||||
for (const statsItem of kpiStats.value) {
|
||||
statsItem.last1Day = '-';
|
||||
statsItem.last7Days = '-';
|
||||
statsItem.last30Days = '-';
|
||||
statsItem.last1Day = '';
|
||||
statsItem.last7Days = '';
|
||||
statsItem.last30Days = '';
|
||||
}
|
||||
} finally {
|
||||
tableLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -996,11 +1018,8 @@ const updateKpiStats = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化统计数据
|
||||
// 只初始化统计数据表格结构,不获取数据
|
||||
fnInitKpiStatsData();
|
||||
|
||||
// 获取统计数据
|
||||
fnGetKpiStatsData();
|
||||
};
|
||||
|
||||
// 添加表列定义
|
||||
@@ -1029,19 +1048,24 @@ const statsColumns: TableColumnType<KPIStats>[] = [
|
||||
width: '40%',
|
||||
},
|
||||
{
|
||||
title: t('views.perfManage.customTarget.ago1'),
|
||||
title: () => h('div', { style: { display: 'flex', alignItems: 'right', gap: '4px' } }, [
|
||||
h('span', t('views.perfManage.customTarget.ago1')),
|
||||
h(InfoCircleOutlined, {
|
||||
title: t('views.perfManage.kpiOverView.tips'),
|
||||
}),
|
||||
]),
|
||||
dataIndex: 'last1Day',
|
||||
key: 'last1Day',
|
||||
width: '20%',
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
customRender: ({ record }: { record: KPIStats }) => {
|
||||
const value = record.last1Day;
|
||||
// 如果是默认值,直接显示
|
||||
if (value === '-') {
|
||||
return value;
|
||||
// 如果是空值,直接显示空白
|
||||
if (value === '' || value === null || value === undefined) {
|
||||
return '';
|
||||
}
|
||||
// 关键指标多为次数类,使用累计值
|
||||
return `${value} ${t('views.perfManage.customTarget.total')}`;
|
||||
return `${value} `;
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1052,12 +1076,12 @@ const statsColumns: TableColumnType<KPIStats>[] = [
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
customRender: ({ record }: { record: KPIStats }) => {
|
||||
const value = record.last7Days;
|
||||
// 如果是默认值,直接显示
|
||||
if (value === '-') {
|
||||
return value;
|
||||
// 如果是空值,直接显示空白
|
||||
if (value === '' || value === null || value === undefined) {
|
||||
return '';
|
||||
}
|
||||
// 关键指标多为次数类,使用累计值
|
||||
return `${value} ${t('views.perfManage.customTarget.total')}`;
|
||||
return `${value} `;
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1068,12 +1092,12 @@ const statsColumns: TableColumnType<KPIStats>[] = [
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
customRender: ({ record }: { record: KPIStats }) => {
|
||||
const value = record.last30Days;
|
||||
// 如果是默认值,直接显示
|
||||
if (value === '-') {
|
||||
return value;
|
||||
// 如果是空值,直接显示空白
|
||||
if (value === '' || value === null || value === undefined) {
|
||||
return '';
|
||||
}
|
||||
// 关键指标多为次数类,使用累计值
|
||||
return `${value} ${t('views.perfManage.customTarget.total')}`;
|
||||
return `${value} `;
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1179,6 +1203,9 @@ const tableRowConfig = computed(() => {
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
height: calc(100vh - 200px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
[data-theme='light'] .chart-wrapper {
|
||||
@@ -1190,8 +1217,10 @@ const tableRowConfig = computed(() => {
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
height: 400px;
|
||||
height: 450px;
|
||||
width: 100%;
|
||||
min-height: 400px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
|
||||
Reference in New Issue
Block a user