feat:关键仪表盘显示指标修改
This commit is contained in:
@@ -22,6 +22,7 @@ import type { Dayjs } from 'dayjs';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
import { generateColorRGBA } from '@/utils/generate-utils';
|
import { generateColorRGBA } from '@/utils/generate-utils';
|
||||||
|
import { parseSizeFromKbs } from '@/utils/parse-utils';
|
||||||
import { LineOutlined, InfoCircleOutlined, EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons-vue';
|
import { LineOutlined, InfoCircleOutlined, EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons-vue';
|
||||||
import { TableColumnType } from 'ant-design-vue';
|
import { TableColumnType } from 'ant-design-vue';
|
||||||
import useNeInfoStore from '@/store/modules/neinfo';
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
@@ -168,7 +169,8 @@ const TARGET_KPI_IDS: Record<NeType, string[]> = {
|
|||||||
// SMSC: ['SMSC.A.01', 'SMSC.A.02', 'SMSC.A.03'],
|
// SMSC: ['SMSC.A.01', 'SMSC.A.02', 'SMSC.A.03'],
|
||||||
};
|
};
|
||||||
|
|
||||||
const KPI_TITLE: Record<string, string> = {
|
// 原始KPI标题(用于内部计算)
|
||||||
|
const BASE_KPI_TITLE: Record<string, string> = {
|
||||||
'AMF.02': '5G Registration Request',
|
'AMF.02': '5G Registration Request',
|
||||||
'AMF.03': '5G Registration Success',
|
'AMF.03': '5G Registration Success',
|
||||||
'UPF.04': 'UPF Downlink Throughput',
|
'UPF.04': 'UPF Downlink Throughput',
|
||||||
@@ -183,6 +185,143 @@ const KPI_TITLE: Record<string, string> = {
|
|||||||
'MME.A.02':'4G Registration Success',
|
'MME.A.02':'4G Registration Success',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 计算型KPI定义
|
||||||
|
interface CalculatedKPI {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
numerator: string; // 分子指标ID
|
||||||
|
denominator: string; // 分母指标ID
|
||||||
|
isPercentage: boolean; // 是否为百分比
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算型KPI配置
|
||||||
|
const CALCULATED_KPIS: Record<NeType, CalculatedKPI[]> = {
|
||||||
|
AMF: [
|
||||||
|
{
|
||||||
|
id: 'AMF_5G_REG_SUCCESS_RATE',
|
||||||
|
title: '5G Registration Success Rate',
|
||||||
|
numerator: 'AMF.03',
|
||||||
|
denominator: 'AMF.02',
|
||||||
|
isPercentage: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
UPF: [
|
||||||
|
{
|
||||||
|
id: 'UPF.04',
|
||||||
|
title: 'UPF Downlink Throughput',
|
||||||
|
numerator: 'UPF.04',
|
||||||
|
denominator: '',
|
||||||
|
isPercentage: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'UPF.05',
|
||||||
|
title: 'UPF Uplink Throughput',
|
||||||
|
numerator: 'UPF.05',
|
||||||
|
denominator: '',
|
||||||
|
isPercentage: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
IMS: [
|
||||||
|
{
|
||||||
|
id: 'IMS_REG_SUCCESS_RATE',
|
||||||
|
title: 'IMS Registration Success Rate',
|
||||||
|
numerator: 'SCSCF.03',
|
||||||
|
denominator: 'SCSCF.04',
|
||||||
|
isPercentage: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'MO_CALL_SUCCESS_RATE',
|
||||||
|
title: 'MO Call Success Rate',
|
||||||
|
numerator: 'SCSCF.05',
|
||||||
|
denominator: 'SCSCF.06',
|
||||||
|
isPercentage: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'MT_CALL_SUCCESS_RATE',
|
||||||
|
title: 'MT Call Success Rate',
|
||||||
|
numerator: 'SCSCF.07',
|
||||||
|
denominator: 'SCSCF.08',
|
||||||
|
isPercentage: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
MME: [
|
||||||
|
{
|
||||||
|
id: 'MME_4G_REG_SUCCESS_RATE',
|
||||||
|
title: '4G Registration Success Rate',
|
||||||
|
numerator: 'MME.A.02',
|
||||||
|
denominator: 'MME.A.01',
|
||||||
|
isPercentage: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// 显示用的KPI标题
|
||||||
|
const KPI_TITLE: Record<string, string> = {};
|
||||||
|
// 初始化显示标题
|
||||||
|
Object.values(CALCULATED_KPIS).flat().forEach(kpi => {
|
||||||
|
KPI_TITLE[kpi.id] = kpi.title;
|
||||||
|
});
|
||||||
|
|
||||||
|
// UPF吞吐量指标ID列表
|
||||||
|
const UPF_THROUGHPUT_KPIS = ['UPF.04', 'UPF.05'];
|
||||||
|
|
||||||
|
// 判断是否为UPF吞吐量指标
|
||||||
|
const isUPFThroughputKPI = (kpiId: string): boolean => {
|
||||||
|
return UPF_THROUGHPUT_KPIS.includes(kpiId);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化UPF吞吐量数据 - 根据数据来源使用不同的时间间隔
|
||||||
|
const formatUPFThroughput = (value: number, timeInterval: number = 900): number => {
|
||||||
|
if (value === 0 || value === null || value === undefined) return 0;
|
||||||
|
// 使用parseSizeFromKbs函数进行格式化,转换为Mbps
|
||||||
|
const [formattedValue] = parseSizeFromKbs(value, timeInterval);
|
||||||
|
return Number(formattedValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 判断当前选中的指标中是否包含UPF吞吐量指标
|
||||||
|
const hasUPFThroughputKPIs = (): boolean => {
|
||||||
|
return selectedKPIs.value.some(kpiId => isUPFThroughputKPI(kpiId));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算KPI值的函数
|
||||||
|
const calculateKPIValue = (kpi: CalculatedKPI, rawData: Record<string, any>): number => {
|
||||||
|
if (!kpi.denominator) {
|
||||||
|
// 单一指标,如UPF吞吐量
|
||||||
|
const value = Number(rawData[kpi.numerator]) || 0;
|
||||||
|
return isUPFThroughputKPI(kpi.id) ? formatUPFThroughput(value, 900) : value;
|
||||||
|
} else {
|
||||||
|
// 计算型指标(百分比)
|
||||||
|
const numerator = Number(rawData[kpi.numerator]) || 0;
|
||||||
|
const denominator = Number(rawData[kpi.denominator]) || 0;
|
||||||
|
|
||||||
|
if (denominator === 0) return 0;
|
||||||
|
|
||||||
|
const percentage = (numerator / denominator) * 100;
|
||||||
|
return Number(percentage.toFixed(2));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取网元类型的所有计算型KPI
|
||||||
|
const getCalculatedKPIsForNeType = (neType: NeType): CalculatedKPI[] => {
|
||||||
|
return CALCULATED_KPIS[neType] || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取Y轴单位
|
||||||
|
const getYAxisUnit = (): string => {
|
||||||
|
const allKPIs = Object.values(CALCULATED_KPIS).flat();
|
||||||
|
const hasUPF = allKPIs.some(kpi => isUPFThroughputKPI(kpi.id));
|
||||||
|
const hasPercentage = allKPIs.some(kpi => kpi.isPercentage);
|
||||||
|
|
||||||
|
if (hasUPF && hasPercentage) {
|
||||||
|
return ''; // 混合单位时不显示
|
||||||
|
} else if (hasUPF) {
|
||||||
|
return '(Mbps)';
|
||||||
|
} else if (hasPercentage) {
|
||||||
|
return '(%)';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
// 添加网元信息 store
|
// 添加网元信息 store
|
||||||
const neInfoStore = useNeInfoStore();
|
const neInfoStore = useNeInfoStore();
|
||||||
|
|
||||||
@@ -284,10 +423,19 @@ const wsMessage = (res: Record<string, any>) => {
|
|||||||
date: kpiEvent.timeGroup?.toString() || Date.now().toString(),
|
date: kpiEvent.timeGroup?.toString() || Date.now().toString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// 为每个网元的每个指标添加数据
|
// 为每个网元的每个计算型指标添加数据
|
||||||
for (const kpiId of TARGET_KPI_IDS[neType as NeType] || []) {
|
const calculatedKPIs = getCalculatedKPIsForNeType(neType as NeType);
|
||||||
const key = `${kpiId}_${neId}`;
|
for (const kpi of calculatedKPIs) {
|
||||||
newData[key] = Number(kpiEvent[kpiId]) || 0;
|
const key = `${kpi.id}_${neId}`;
|
||||||
|
|
||||||
|
if (isUPFThroughputKPI(kpi.id)) {
|
||||||
|
// UPF吞吐量指标使用1分钟间隔
|
||||||
|
const rawValue = Number(kpiEvent[kpi.numerator]) || 0;
|
||||||
|
newData[key] = formatUPFThroughput(rawValue, 60);
|
||||||
|
} else {
|
||||||
|
// 其他计算型指标
|
||||||
|
newData[key] = calculateKPIValue(kpi, kpiEvent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新图表数据(只影响图表,不影响表格)
|
// 更新图表数据(只影响图表,不影响表格)
|
||||||
@@ -381,12 +529,21 @@ const fetchChartData = async () => {
|
|||||||
.sort((a, b) => Number(a.timeGroup) - Number(b.timeGroup))
|
.sort((a, b) => Number(a.timeGroup) - Number(b.timeGroup))
|
||||||
.map(item => {
|
.map(item => {
|
||||||
const dataItem: ChartDataItem = { date: item.timeGroup.toString() };
|
const dataItem: ChartDataItem = { date: item.timeGroup.toString() };
|
||||||
// 为每个网元的每个指标添加数据
|
// 为每个网元的每个计算型指标添加数据
|
||||||
for (const neType of ALL_NE_TYPES) {
|
for (const neType of ALL_NE_TYPES) {
|
||||||
for (const ne of neList.value[neType]) {
|
for (const ne of neList.value[neType]) {
|
||||||
for (const kpiId of TARGET_KPI_IDS[neType]) {
|
const calculatedKPIs = getCalculatedKPIsForNeType(neType);
|
||||||
const key = `${kpiId}_${ne.neId}`;
|
for (const kpi of calculatedKPIs) {
|
||||||
dataItem[key] = Number(item[kpiId]) || 0;
|
const key = `${kpi.id}_${ne.neId}`;
|
||||||
|
|
||||||
|
if (isUPFThroughputKPI(kpi.id)) {
|
||||||
|
// UPF吞吐量指标使用15分钟间隔
|
||||||
|
const rawValue = Number(item[kpi.numerator]) || 0;
|
||||||
|
dataItem[key] = formatUPFThroughput(rawValue, 900);
|
||||||
|
} else {
|
||||||
|
// 其他计算型指标
|
||||||
|
dataItem[key] = calculateKPIValue(kpi, item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -497,6 +654,15 @@ const themeObserver = new MutationObserver(() => {
|
|||||||
color: splitLineColor,
|
color: splitLineColor,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// 根据指标类型显示单位
|
||||||
|
name: getYAxisUnit(),
|
||||||
|
nameTextStyle: {
|
||||||
|
fontSize: 12,
|
||||||
|
padding: [0, 0, 0, 0],
|
||||||
|
color: document.documentElement.getAttribute('data-theme') === 'dark'
|
||||||
|
? '#CACADA'
|
||||||
|
: '#333',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
show: false,
|
show: false,
|
||||||
@@ -550,11 +716,9 @@ const updateChart = () => {
|
|||||||
const series = [];
|
const series = [];
|
||||||
for (const neType of ALL_NE_TYPES) {
|
for (const neType of ALL_NE_TYPES) {
|
||||||
for (const ne of neList.value[neType]) {
|
for (const ne of neList.value[neType]) {
|
||||||
for (const kpiId of TARGET_KPI_IDS[neType]) {
|
const calculatedKPIs = getCalculatedKPIsForNeType(neType);
|
||||||
const kpi = kpiColumns.value.find(col => col.kpiId === kpiId);
|
for (const kpi of calculatedKPIs) {
|
||||||
if (!kpi) continue;
|
const key = `${kpi.id}_${ne.neId}`;
|
||||||
|
|
||||||
const key = `${kpiId}_${ne.neId}`;
|
|
||||||
const color = kpiColors.get(key) || generateColorRGBA();
|
const color = kpiColors.get(key) || generateColorRGBA();
|
||||||
kpiColors.set(key, color);
|
kpiColors.set(key, color);
|
||||||
|
|
||||||
@@ -670,6 +834,15 @@ const updateChart = () => {
|
|||||||
color: getSplitLineColor(),
|
color: getSplitLineColor(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// 根据指标类型显示单位
|
||||||
|
name: getYAxisUnit(),
|
||||||
|
nameTextStyle: {
|
||||||
|
fontSize: 12,
|
||||||
|
padding: [0, 0, 0, 0],
|
||||||
|
color: document.documentElement.getAttribute('data-theme') === 'dark'
|
||||||
|
? '#CACADA'
|
||||||
|
: '#333',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
series: series, //配置数据
|
series: series, //配置数据
|
||||||
};
|
};
|
||||||
@@ -741,7 +914,7 @@ onMounted(async () => {
|
|||||||
// 存储指标列信
|
// 存储指标列信
|
||||||
const kpiColumns = ref<KPIColumn[]>([]);
|
const kpiColumns = ref<KPIColumn[]>([]);
|
||||||
// 添加选中指标的的状态
|
// 添加选中指标的的状态
|
||||||
const selectedKPIs = ref<string[]>(Object.values(TARGET_KPI_IDS).flat());
|
const selectedKPIs = ref<string[]>(Object.values(CALCULATED_KPIS).flat().map(kpi => kpi.id));
|
||||||
|
|
||||||
// 获取网元指标
|
// 获取网元指标
|
||||||
const fetchSpecificKPI = async () => {
|
const fetchSpecificKPI = async () => {
|
||||||
@@ -753,36 +926,24 @@ const fetchSpecificKPI = async () => {
|
|||||||
try {
|
try {
|
||||||
let allKPIs: KPIColumn[] = [];
|
let allKPIs: KPIColumn[] = [];
|
||||||
|
|
||||||
// 获取所有网元的指标
|
// 为每个网元类型创建计算型KPI列
|
||||||
for (const neType of ALL_NE_TYPES) {
|
for (const neType of ALL_NE_TYPES) {
|
||||||
const res = await getKPITitle(neType.toUpperCase());
|
const calculatedKPIs = getCalculatedKPIsForNeType(neType);
|
||||||
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
for (const kpi of calculatedKPIs) {
|
||||||
const titleIDs = Object.values(TARGET_KPI_IDS).flat();
|
allKPIs.push({
|
||||||
// 只获取 TARGET_KPI_IDS 中定义的指标
|
title: kpi.title,
|
||||||
const filteredKPIs = res.data
|
dataIndex: kpi.id,
|
||||||
.filter(item => TARGET_KPI_IDS[neType].includes(item.kpiId))
|
key: kpi.id,
|
||||||
.map(item => {
|
kpiId: kpi.id,
|
||||||
let title = item[`${language}Title`];
|
neType: neType,
|
||||||
if (titleIDs.includes(item.kpiId)) {
|
});
|
||||||
title = KPI_TITLE[item.kpiId] || title;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
title: title,
|
|
||||||
dataIndex: item.kpiId,
|
|
||||||
key: item.kpiId,
|
|
||||||
kpiId: item.kpiId,
|
|
||||||
neType: neType,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
allKPIs = [...allKPIs, ...filteredKPIs];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kpiColumns.value = allKPIs;
|
kpiColumns.value = allKPIs;
|
||||||
// 直接使用重要指标
|
// 使用计算型指标
|
||||||
selectedKPIs.value = Object.values(TARGET_KPI_IDS).flat();
|
selectedKPIs.value = Object.values(CALCULATED_KPIS).flat().map(kpi => kpi.id);
|
||||||
|
|
||||||
if (kpiColumns.value.length === 0) {
|
if (kpiColumns.value.length === 0) {
|
||||||
console.warn('No KPIs found');
|
console.warn('No KPIs found');
|
||||||
@@ -837,13 +998,12 @@ const updateChartData = (newData: ChartDataItem) => {
|
|||||||
},
|
},
|
||||||
series: ALL_NE_TYPES.flatMap(type =>
|
series: ALL_NE_TYPES.flatMap(type =>
|
||||||
neList.value[type].map(ne =>
|
neList.value[type].map(ne =>
|
||||||
TARGET_KPI_IDS[type].map(kpiId => {
|
getCalculatedKPIsForNeType(type).map(kpi => {
|
||||||
const kpi = kpiColumns.value.find(col => col.kpiId === kpiId);
|
const key = `${kpi.id}_${ne.neId}`;
|
||||||
const key = `${kpiId}_${ne.neId}`;
|
|
||||||
return {
|
return {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: chartData.value.map(item => item[key] || 0),
|
data: chartData.value.map(item => item[key] || 0),
|
||||||
name: `${kpi?.title || kpiId}(${ne.neName})`,
|
name: `${kpi.title}(${ne.neName})`,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@@ -878,12 +1038,10 @@ function fnInitKpiStatsData() {
|
|||||||
|
|
||||||
for (const neType of ALL_NE_TYPES) {
|
for (const neType of ALL_NE_TYPES) {
|
||||||
for (const ne of neList.value[neType]) {
|
for (const ne of neList.value[neType]) {
|
||||||
for (const kpiId of TARGET_KPI_IDS[neType]) {
|
const calculatedKPIs = getCalculatedKPIsForNeType(neType);
|
||||||
const kpi = kpiColumns.value.find(col => col.kpiId === kpiId);
|
for (const kpi of calculatedKPIs) {
|
||||||
if (!kpi) continue;
|
|
||||||
|
|
||||||
kpiStats.value.push({
|
kpiStats.value.push({
|
||||||
kpiId: `${kpiId}_${ne.neId}`,
|
kpiId: `${kpi.id}_${ne.neId}`,
|
||||||
title: `${kpi.title}(${ne.neName})`,
|
title: `${kpi.title}(${ne.neName})`,
|
||||||
last1Day: '', // 空白显示,loading状态表示正在获取数据
|
last1Day: '', // 空白显示,loading状态表示正在获取数据
|
||||||
last7Days: '',
|
last7Days: '',
|
||||||
@@ -952,39 +1110,86 @@ async function fnGetKpiStatsData() {
|
|||||||
const { neType, neId, success, data } = result;
|
const { neType, neId, success, data } = result;
|
||||||
|
|
||||||
if (success && data.length > 0) {
|
if (success && data.length > 0) {
|
||||||
// 为每个指标计算统计值
|
// 为每个计算型指标计算统计值
|
||||||
for (const kpiId of TARGET_KPI_IDS[neType as NeType]) {
|
const calculatedKPIs = getCalculatedKPIsForNeType(neType as NeType);
|
||||||
const kpi = kpiColumns.value.find(col => col.kpiId === kpiId);
|
for (const kpi of calculatedKPIs) {
|
||||||
if (!kpi) continue;
|
const key = `${kpi.id}_${neId}`;
|
||||||
|
|
||||||
const key = `${kpiId}_${neId}`;
|
// 根据时间范围筛选有效数据(非零数据)
|
||||||
|
|
||||||
// 根据时间范围筛选非零数据
|
|
||||||
const data1Day = data.filter((item: any) => {
|
const data1Day = data.filter((item: any) => {
|
||||||
const itemTime = Number(item.timeGroup);
|
const itemTime = Number(item.timeGroup);
|
||||||
const value = item[kpiId] ? Number(item[kpiId]) : 0;
|
if (itemTime < day1_start) return false;
|
||||||
return itemTime >= day1_start && value !== 0;
|
|
||||||
|
// 检查相关指标是否有有效数值
|
||||||
|
if (kpi.isPercentage) {
|
||||||
|
// 百分比指标:分母必须大于0
|
||||||
|
const denominator = Number(item[kpi.denominator]) || 0;
|
||||||
|
return denominator > 0;
|
||||||
|
} else {
|
||||||
|
// 其他指标:数值必须大于0
|
||||||
|
const value = Number(item[kpi.numerator]) || 0;
|
||||||
|
return value > 0;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const data7Days = data.filter((item: any) => {
|
const data7Days = data.filter((item: any) => {
|
||||||
const itemTime = Number(item.timeGroup);
|
const itemTime = Number(item.timeGroup);
|
||||||
const value = item[kpiId] ? Number(item[kpiId]) : 0;
|
if (itemTime < day7_start) return false;
|
||||||
return itemTime >= day7_start && value !== 0;
|
|
||||||
|
// 检查相关指标是否有有效数值
|
||||||
|
if (kpi.isPercentage) {
|
||||||
|
// 百分比指标:分母必须大于0
|
||||||
|
const denominator = Number(item[kpi.denominator]) || 0;
|
||||||
|
return denominator > 0;
|
||||||
|
} else {
|
||||||
|
// 其他指标:数值必须大于0
|
||||||
|
const value = Number(item[kpi.numerator]) || 0;
|
||||||
|
return value > 0;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const data30Days = data.filter((item: any) => {
|
const data30Days = data.filter((item: any) => {
|
||||||
const itemTime = Number(item.timeGroup);
|
const itemTime = Number(item.timeGroup);
|
||||||
const value = item[kpiId] ? Number(item[kpiId]) : 0;
|
if (itemTime < day30_start) return false;
|
||||||
return itemTime >= day30_start && value !== 0;
|
|
||||||
|
// 检查相关指标是否有有效数值
|
||||||
|
if (kpi.isPercentage) {
|
||||||
|
// 百分比指标:分母必须大于0
|
||||||
|
const denominator = Number(item[kpi.denominator]) || 0;
|
||||||
|
return denominator > 0;
|
||||||
|
} else {
|
||||||
|
// 其他指标:数值必须大于0
|
||||||
|
const value = Number(item[kpi.numerator]) || 0;
|
||||||
|
return value > 0;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算统计值(只对非零数据进行计算,关键指标多为次数类使用累计值)
|
// 计算统计值(传入的数据已经是有效数据)
|
||||||
const calculateValue = (dataArray: any[]) => {
|
const calculateValue = (dataArray: any[]) => {
|
||||||
if (dataArray.length === 0) return 0;
|
if (dataArray.length === 0) return 0;
|
||||||
|
|
||||||
const values = dataArray.map((item: any) => Number(item[kpiId]));
|
if (isUPFThroughputKPI(kpi.id)) {
|
||||||
// 关键指标多为次数类,使用累计值
|
// UPF吞吐量指标:先格式化每个数据点,然后计算平均值
|
||||||
return Number(values.reduce((sum, val) => sum + val, 0).toFixed(2));
|
const values = dataArray.map((item: any) => Number(item[kpi.numerator]) || 0);
|
||||||
|
const formattedValues = values.map(val => formatUPFThroughput(val, 900));
|
||||||
|
const average = formattedValues.reduce((sum, val) => sum + val, 0) / formattedValues.length;
|
||||||
|
return Number(average.toFixed(2));
|
||||||
|
} else if (kpi.isPercentage) {
|
||||||
|
// 百分比指标:计算平均成功率(数据已经过滤,分母都大于0)
|
||||||
|
const percentages = dataArray.map((item: any) => {
|
||||||
|
const numerator = Number(item[kpi.numerator]) || 0;
|
||||||
|
const denominator = Number(item[kpi.denominator]) || 0;
|
||||||
|
return (numerator / denominator) * 100;
|
||||||
|
});
|
||||||
|
|
||||||
|
const average = percentages.reduce((sum, val) => sum + val, 0) / percentages.length;
|
||||||
|
return Number(average.toFixed(2));
|
||||||
|
} else {
|
||||||
|
// 其他指标:累计值(数据已经过滤,都是非零值)
|
||||||
|
const values = dataArray.map((item: any) => Number(item[kpi.numerator]) || 0);
|
||||||
|
const sum = values.reduce((sum, val) => sum + val, 0);
|
||||||
|
return Number(sum.toFixed(2));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更新对应的统计数据
|
// 更新对应的统计数据
|
||||||
@@ -997,8 +1202,9 @@ async function fnGetKpiStatsData() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 如果获取失败,保持空白显示
|
// 如果获取失败,保持空白显示
|
||||||
for (const kpiId of TARGET_KPI_IDS[neType as NeType]) {
|
const calculatedKPIs = getCalculatedKPIsForNeType(neType as NeType);
|
||||||
const key = `${kpiId}_${neId}`;
|
for (const kpi of calculatedKPIs) {
|
||||||
|
const key = `${kpi.id}_${neId}`;
|
||||||
const statsIndex = kpiStats.value.findIndex((item: any) => item.kpiId === key);
|
const statsIndex = kpiStats.value.findIndex((item: any) => item.kpiId === key);
|
||||||
if (statsIndex !== -1) {
|
if (statsIndex !== -1) {
|
||||||
kpiStats.value[statsIndex].last1Day = '';
|
kpiStats.value[statsIndex].last1Day = '';
|
||||||
@@ -1077,8 +1283,18 @@ const statsColumns: TableColumnType<KPIStats>[] = [
|
|||||||
if (value === '' || value === null || value === undefined) {
|
if (value === '' || value === null || value === undefined) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
// 关键指标多为次数类,使用累计值
|
// 根据指标类型显示不同单位
|
||||||
return `${value} `;
|
const kpiId = record.kpiId.split('_')[0];
|
||||||
|
const isUPFThroughput = isUPFThroughputKPI(kpiId);
|
||||||
|
const isPercentage = Object.values(CALCULATED_KPIS).flat().find(kpi => kpi.id === kpiId)?.isPercentage;
|
||||||
|
|
||||||
|
if (isUPFThroughput) {
|
||||||
|
return `${value} Mbps`;
|
||||||
|
} else if (isPercentage) {
|
||||||
|
return `${value}%`;
|
||||||
|
} else {
|
||||||
|
return `${value}`;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1093,8 +1309,18 @@ const statsColumns: TableColumnType<KPIStats>[] = [
|
|||||||
if (value === '' || value === null || value === undefined) {
|
if (value === '' || value === null || value === undefined) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
// 关键指标多为次数类,使用累计值
|
// 根据指标类型显示不同单位
|
||||||
return `${value} `;
|
const kpiId = record.kpiId.split('_')[0];
|
||||||
|
const isUPFThroughput = isUPFThroughputKPI(kpiId);
|
||||||
|
const isPercentage = Object.values(CALCULATED_KPIS).flat().find(kpi => kpi.id === kpiId)?.isPercentage;
|
||||||
|
|
||||||
|
if (isUPFThroughput) {
|
||||||
|
return `${value} Mbps`;
|
||||||
|
} else if (isPercentage) {
|
||||||
|
return `${value}%`;
|
||||||
|
} else {
|
||||||
|
return `${value}`;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1109,8 +1335,18 @@ const statsColumns: TableColumnType<KPIStats>[] = [
|
|||||||
if (value === '' || value === null || value === undefined) {
|
if (value === '' || value === null || value === undefined) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
// 关键指标多为次数类,使用累计值
|
// 根据指标类型显示不同单位
|
||||||
return `${value} `;
|
const kpiId = record.kpiId.split('_')[0];
|
||||||
|
const isUPFThroughput = isUPFThroughputKPI(kpiId);
|
||||||
|
const isPercentage = Object.values(CALCULATED_KPIS).flat().find(kpi => kpi.id === kpiId)?.isPercentage;
|
||||||
|
|
||||||
|
if (isUPFThroughput) {
|
||||||
|
return `${value} Mbps`;
|
||||||
|
} else if (isPercentage) {
|
||||||
|
return `${value}%`;
|
||||||
|
} else {
|
||||||
|
return `${value}`;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user