This commit is contained in:
lai
2025-08-01 15:54:34 +08:00

View File

@@ -1,7 +1,8 @@
<template> <template>
<div class="dashboard-cards"> <div class="dashboard-cards">
<div style="font-size:32px; font-weight: bold; margin-bottom: 20px; text-align: center;"> <div style="font-size:32px; font-weight: bold; margin-bottom: 20px; text-align: center; position: relative;">
Dashboard Dashboard
<span class="control-tower-badge">IMS Control Tower</span>
</div> </div>
<div style="margin-bottom: 24px; text-align: right;"> <div style="margin-bottom: 24px; text-align: right;">
<label style="margin-right: 8px; font-weight: 600;">NE</label> <label style="margin-right: 8px; font-weight: 600;">NE</label>
@@ -17,16 +18,16 @@
:allow-clear="false" :allow-clear="false"
/> />
<!-- 调试信息 --> <!-- 调试信息 -->
<div style="margin-top: 8px; font-size: 12px; color: #666;"> <!-- <div style="margin-top: 8px; font-size: 12px; color: #666;">-->
WebSocket状态: {{ wsStatus }} | 数据点数量: {{ imsRealtimeRawData.length }} <!-- WebSocket状态: {{ wsStatus }} | 数据点数量: {{ imsRealtimeRawData.length }}-->
</div> <!-- </div>-->
<a-button <!-- <a-button-->
type="primary" <!-- type="primary"-->
style="margin-left: 16px;" <!-- style="margin-left: 16px;"-->
@click="testDataUpdate" <!-- @click="testDataUpdate"-->
> <!-- >-->
测试数据更新 <!-- 测试数据更新-->
</a-button> <!-- </a-button>-->
<!-- <div style="margin-top: 8px; font-size: 12px; color: #666;">--> <!-- <div style="margin-top: 8px; font-size: 12px; color: #666;">-->
<!-- WebSocket状态: {{ wsStatus }} | 数据点数量: {{ imsRealtimeRawData.length }}--> <!-- WebSocket状态: {{ wsStatus }} | 数据点数量: {{ imsRealtimeRawData.length }}-->
<!-- </div>--> <!-- </div>-->
@@ -354,7 +355,7 @@ function updateActiveCallsChart() {
const xAxisData = [1, 2, 3, 4, 5] const xAxisData = [1, 2, 3, 4, 5]
chart.setOption({ chart.setOption({
grid: { left: 0, right: 0, top: 10, bottom: 10 }, grid: { left: 0, right: 30, top: 10, bottom: 10 }, // 增加右侧边距
xAxis: { type: 'category', show: false, data: xAxisData }, xAxis: { type: 'category', show: false, data: xAxisData },
yAxis: { type: 'value', show: false }, yAxis: { type: 'value', show: false },
series: [{ series: [{
@@ -388,7 +389,7 @@ function updateActiveCallsChart() {
const latestValue = chartData[chartData.length - 1] const latestValue = chartData[chartData.length - 1]
chart.setOption({ chart.setOption({
grid: { left: 0, right: 0, top: 10, bottom: 10 }, grid: { left: 0, right: 30, top: 10, bottom: 10 }, // 增加右侧边距
xAxis: { type: 'category', show: false, data: xAxisData }, xAxis: { type: 'category', show: false, data: xAxisData },
yAxis: { type: 'value', show: false }, yAxis: { type: 'value', show: false },
series: [{ series: [{
@@ -396,45 +397,101 @@ function updateActiveCallsChart() {
type: 'line', symbol: 'none', type: 'line', symbol: 'none',
lineStyle: { width: 2, color: '#1890ff' }, // 蓝色线条表示有数据 lineStyle: { width: 2, color: '#1890ff' }, // 蓝色线条表示有数据
areaStyle: { color: 'rgba(24,144,255,0.1)' } // 淡蓝色填充 areaStyle: { color: 'rgba(24,144,255,0.1)' } // 淡蓝色填充
}], }]
graphic: [
{
type: 'text',
right: 8,
top: 8,
style: {
text: maxValue.toString(),
fontSize: 12,
fill: '#666',
fontWeight: 'bold'
}
},
{
type: 'text',
right: 8,
top: '50%',
style: {
text: latestValue.toString(),
fontSize: 12,
fill: '#1890ff',
fontWeight: 'bold'
}
},
{
type: 'text',
right: 8,
bottom: 8,
style: {
text: minValue.toString(),
fontSize: 12,
fill: '#666',
fontWeight: 'bold'
}
}
]
}) })
console.log('updateActiveCallsChart - 图表更新完成') // 在图表容器中添加数值标注
const chartContainer = callsChartRef.value
if (chartContainer) {
// 清除之前的标注
const existingLabels = chartContainer.querySelectorAll('.chart-label')
existingLabels.forEach(label => label.remove())
// 添加右侧数值标注
const maxLabel = document.createElement('div')
maxLabel.className = 'chart-label'
maxLabel.style.cssText = `
position: absolute;
right: 8px;
top: 8px;
font-size: 12px;
font-weight: bold;
color: #666;
pointer-events: none;
z-index: 10;
`
maxLabel.textContent = maxValue.toString()
const latestLabel = document.createElement('div')
latestLabel.className = 'chart-label'
latestLabel.style.cssText = `
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
font-size: 12px;
font-weight: bold;
color: #1890ff;
pointer-events: none;
z-index: 10;
`
latestLabel.textContent = latestValue.toString()
const minLabel = document.createElement('div')
minLabel.className = 'chart-label'
minLabel.style.cssText = `
position: absolute;
right: 8px;
bottom: 8px;
font-size: 12px;
font-weight: bold;
color: #666;
pointer-events: none;
z-index: 10;
`
minLabel.textContent = minValue.toString()
// 添加底部时间标注
const oldestTimeLabel = document.createElement('div')
oldestTimeLabel.className = 'chart-label'
oldestTimeLabel.style.cssText = `
position: absolute;
left: 8px;
bottom: -20px;
font-size: 11px;
color: #999;
pointer-events: none;
z-index: 10;
`
// 计算图表实际显示的最旧数据时间(基于图表数据点数量)
const displayDataLength = Math.min(imsRealtimeRawData.value.length, 30)
const oldestDisplayIndex = Math.max(0, imsRealtimeRawData.value.length - displayDataLength)
const oldestDisplayData = imsRealtimeRawData.value[oldestDisplayIndex]
if (oldestDisplayData && oldestDisplayData.timestamp) {
oldestTimeLabel.textContent = calculateRelativeTime(oldestDisplayData.timestamp)
} else {
oldestTimeLabel.textContent = '--'
}
const nowTimeLabel = document.createElement('div')
nowTimeLabel.className = 'chart-label'
nowTimeLabel.style.cssText = `
position: absolute;
right: 8px;
bottom: -20px;
font-size: 11px;
color: #999;
pointer-events: none;
z-index: 10;
`
nowTimeLabel.textContent = 'now'
chartContainer.appendChild(maxLabel)
chartContainer.appendChild(latestLabel)
chartContainer.appendChild(minLabel)
chartContainer.appendChild(oldestTimeLabel)
chartContainer.appendChild(nowTimeLabel)
}
} }
// 更新failed calls图表 // 更新failed calls图表
@@ -462,7 +519,7 @@ function updateFailedCallsChart() {
const xAxisData = [1, 2, 3, 4, 5] const xAxisData = [1, 2, 3, 4, 5]
chart.setOption({ chart.setOption({
grid: { left: 0, right: 0, top: 10, bottom: 10 }, grid: { left: 0, right: 30, top: 10, bottom: 10 }, // 增加右侧边距
xAxis: { type: 'category', show: false, data: xAxisData }, xAxis: { type: 'category', show: false, data: xAxisData },
yAxis: { type: 'value', show: false }, yAxis: { type: 'value', show: false },
series: [{ series: [{
@@ -494,7 +551,7 @@ function updateFailedCallsChart() {
const latestValue = chartData[chartData.length - 1] const latestValue = chartData[chartData.length - 1]
chart.setOption({ chart.setOption({
grid: { left: 0, right: 0, top: 10, bottom: 10 }, grid: { left: 0, right: 30, top: 10, bottom: 10 }, // 增加右侧边距
xAxis: { type: 'category', show: false, data: xAxisData }, xAxis: { type: 'category', show: false, data: xAxisData },
yAxis: { type: 'value', show: false }, yAxis: { type: 'value', show: false },
series: [{ series: [{
@@ -502,43 +559,101 @@ function updateFailedCallsChart() {
type: 'line', symbol: 'none', type: 'line', symbol: 'none',
lineStyle: { width: 2, color: '#faad14' }, // 橙色线条表示failed calls lineStyle: { width: 2, color: '#faad14' }, // 橙色线条表示failed calls
areaStyle: { color: 'rgba(250,173,20,0.1)' } // 淡橙色填充 areaStyle: { color: 'rgba(250,173,20,0.1)' } // 淡橙色填充
}], }]
graphic: [
{
type: 'text',
right: 8,
top: 8,
style: {
text: maxValue.toString(),
fontSize: 12,
fill: '#666',
fontWeight: 'bold'
}
},
{
type: 'text',
right: 8,
top: '50%',
style: {
text: latestValue.toString(),
fontSize: 12,
fill: '#faad14',
fontWeight: 'bold'
}
},
{
type: 'text',
right: 8,
bottom: 8,
style: {
text: minValue.toString(),
fontSize: 12,
fill: '#666',
fontWeight: 'bold'
}
}
]
}) })
// 在图表容器中添加数值标注
const chartContainer = failedCallsChartRef.value
if (chartContainer) {
// 清除之前的标注
const existingLabels = chartContainer.querySelectorAll('.chart-label')
existingLabels.forEach(label => label.remove())
// 添加右侧数值标注
const maxLabel = document.createElement('div')
maxLabel.className = 'chart-label'
maxLabel.style.cssText = `
position: absolute;
right: 8px;
top: 8px;
font-size: 12px;
font-weight: bold;
color: #666;
pointer-events: none;
z-index: 10;
`
maxLabel.textContent = maxValue.toString()
const latestLabel = document.createElement('div')
latestLabel.className = 'chart-label'
latestLabel.style.cssText = `
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
font-size: 12px;
font-weight: bold;
color: #faad14;
pointer-events: none;
z-index: 10;
`
latestLabel.textContent = latestValue.toString()
const minLabel = document.createElement('div')
minLabel.className = 'chart-label'
minLabel.style.cssText = `
position: absolute;
right: 8px;
bottom: 8px;
font-size: 12px;
font-weight: bold;
color: #666;
pointer-events: none;
z-index: 10;
`
minLabel.textContent = minValue.toString()
// 添加底部时间标注
const oldestTimeLabel = document.createElement('div')
oldestTimeLabel.className = 'chart-label'
oldestTimeLabel.style.cssText = `
position: absolute;
left: 8px;
bottom: -20px;
font-size: 11px;
color: #999;
pointer-events: none;
z-index: 10;
`
// 计算图表实际显示的最旧数据时间(基于图表数据点数量)
const displayDataLength = Math.min(imsRealtimeRawData.value.length, 30)
const oldestDisplayIndex = Math.max(0, imsRealtimeRawData.value.length - displayDataLength)
const oldestDisplayData = imsRealtimeRawData.value[oldestDisplayIndex]
if (oldestDisplayData && oldestDisplayData.timestamp) {
oldestTimeLabel.textContent = calculateRelativeTime(oldestDisplayData.timestamp)
} else {
oldestTimeLabel.textContent = '--'
}
const nowTimeLabel = document.createElement('div')
nowTimeLabel.className = 'chart-label'
nowTimeLabel.style.cssText = `
position: absolute;
right: 8px;
bottom: -20px;
font-size: 11px;
color: #999;
pointer-events: none;
z-index: 10;
`
nowTimeLabel.textContent = 'now'
chartContainer.appendChild(maxLabel)
chartContainer.appendChild(latestLabel)
chartContainer.appendChild(minLabel)
chartContainer.appendChild(oldestTimeLabel)
chartContainer.appendChild(nowTimeLabel)
}
} }
// 更新active registrations图表 // 更新active registrations图表
@@ -564,7 +679,7 @@ function updateActiveRegistrationsChart() {
const xAxisData = [1, 2, 3, 4, 5] const xAxisData = [1, 2, 3, 4, 5]
chart.setOption({ chart.setOption({
grid: { left: 0, right: 0, top: 10, bottom: 10 }, grid: { left: 0, right: 30, top: 10, bottom: 10 }, // 增加右侧边距
xAxis: { type: 'category', show: false, data: xAxisData }, xAxis: { type: 'category', show: false, data: xAxisData },
yAxis: { type: 'value', show: false }, yAxis: { type: 'value', show: false },
series: [{ series: [{
@@ -596,7 +711,7 @@ function updateActiveRegistrationsChart() {
const latestValue = chartData[chartData.length - 1] const latestValue = chartData[chartData.length - 1]
chart.setOption({ chart.setOption({
grid: { left: 0, right: 0, top: 10, bottom: 10 }, grid: { left: 0, right: 30, top: 10, bottom: 10 }, // 增加右侧边距
xAxis: { type: 'category', show: false, data: xAxisData }, xAxis: { type: 'category', show: false, data: xAxisData },
yAxis: { type: 'value', show: false }, yAxis: { type: 'value', show: false },
series: [{ series: [{
@@ -604,43 +719,101 @@ function updateActiveRegistrationsChart() {
type: 'line', symbol: 'none', type: 'line', symbol: 'none',
lineStyle: { width: 2, color: '#1890ff' }, // 蓝色线条表示active registrations lineStyle: { width: 2, color: '#1890ff' }, // 蓝色线条表示active registrations
areaStyle: { color: 'rgba(24,144,255,0.1)' } // 淡蓝色填充 areaStyle: { color: 'rgba(24,144,255,0.1)' } // 淡蓝色填充
}], }]
graphic: [
{
type: 'text',
right: 8,
top: 8,
style: {
text: maxValue.toString(),
fontSize: 12,
fill: '#666',
fontWeight: 'bold'
}
},
{
type: 'text',
right: 8,
top: '50%',
style: {
text: latestValue.toString(),
fontSize: 12,
fill: '#1890ff',
fontWeight: 'bold'
}
},
{
type: 'text',
right: 8,
bottom: 8,
style: {
text: minValue.toString(),
fontSize: 12,
fill: '#666',
fontWeight: 'bold'
}
}
]
}) })
// 在图表容器中添加数值标注
const chartContainer = regChartRef.value
if (chartContainer) {
// 清除之前的标注
const existingLabels = chartContainer.querySelectorAll('.chart-label')
existingLabels.forEach(label => label.remove())
// 添加右侧数值标注
const maxLabel = document.createElement('div')
maxLabel.className = 'chart-label'
maxLabel.style.cssText = `
position: absolute;
right: 8px;
top: 8px;
font-size: 12px;
font-weight: bold;
color: #666;
pointer-events: none;
z-index: 10;
`
maxLabel.textContent = maxValue.toString()
const latestLabel = document.createElement('div')
latestLabel.className = 'chart-label'
latestLabel.style.cssText = `
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
font-size: 12px;
font-weight: bold;
color: #1890ff;
pointer-events: none;
z-index: 10;
`
latestLabel.textContent = latestValue.toString()
const minLabel = document.createElement('div')
minLabel.className = 'chart-label'
minLabel.style.cssText = `
position: absolute;
right: 8px;
bottom: 8px;
font-size: 12px;
font-weight: bold;
color: #666;
pointer-events: none;
z-index: 10;
`
minLabel.textContent = minValue.toString()
// 添加底部时间标注
const oldestTimeLabel = document.createElement('div')
oldestTimeLabel.className = 'chart-label'
oldestTimeLabel.style.cssText = `
position: absolute;
left: 8px;
bottom: -20px;
font-size: 11px;
color: #999;
pointer-events: none;
z-index: 10;
`
// 计算图表实际显示的最旧数据时间(基于图表数据点数量)
const displayDataLength = Math.min(imsRealtimeRawData.value.length, 30)
const oldestDisplayIndex = Math.max(0, imsRealtimeRawData.value.length - displayDataLength)
const oldestDisplayData = imsRealtimeRawData.value[oldestDisplayIndex]
if (oldestDisplayData && oldestDisplayData.timestamp) {
oldestTimeLabel.textContent = calculateRelativeTime(oldestDisplayData.timestamp)
} else {
oldestTimeLabel.textContent = '--'
}
const nowTimeLabel = document.createElement('div')
nowTimeLabel.className = 'chart-label'
nowTimeLabel.style.cssText = `
position: absolute;
right: 8px;
bottom: -20px;
font-size: 11px;
color: #999;
pointer-events: none;
z-index: 10;
`
nowTimeLabel.textContent = 'now'
chartContainer.appendChild(maxLabel)
chartContainer.appendChild(latestLabel)
chartContainer.appendChild(minLabel)
chartContainer.appendChild(oldestTimeLabel)
chartContainer.appendChild(nowTimeLabel)
}
} }
// 更新failed registrations图表 // 更新failed registrations图表
@@ -668,7 +841,7 @@ function updateFailedRegistrationsChart() {
const xAxisData = [1, 2, 3, 4, 5] const xAxisData = [1, 2, 3, 4, 5]
chart.setOption({ chart.setOption({
grid: { left: 0, right: 0, top: 10, bottom: 10 }, grid: { left: 0, right: 30, top: 10, bottom: 10 }, // 增加右侧边距
xAxis: { type: 'category', show: false, data: xAxisData }, xAxis: { type: 'category', show: false, data: xAxisData },
yAxis: { type: 'value', show: false }, yAxis: { type: 'value', show: false },
series: [{ series: [{
@@ -700,7 +873,7 @@ function updateFailedRegistrationsChart() {
const latestValue = chartData[chartData.length - 1] const latestValue = chartData[chartData.length - 1]
chart.setOption({ chart.setOption({
grid: { left: 0, right: 0, top: 10, bottom: 10 }, grid: { left: 0, right: 30, top: 10, bottom: 10 }, // 增加右侧边距
xAxis: { type: 'category', show: false, data: xAxisData }, xAxis: { type: 'category', show: false, data: xAxisData },
yAxis: { type: 'value', show: false }, yAxis: { type: 'value', show: false },
series: [{ series: [{
@@ -708,43 +881,101 @@ function updateFailedRegistrationsChart() {
type: 'line', symbol: 'none', type: 'line', symbol: 'none',
lineStyle: { width: 2, color: '#f5222d' }, // 红色线条表示failed registrations lineStyle: { width: 2, color: '#f5222d' }, // 红色线条表示failed registrations
areaStyle: { color: 'rgba(245,34,45,0.1)' } // 淡红色填充 areaStyle: { color: 'rgba(245,34,45,0.1)' } // 淡红色填充
}], }]
graphic: [
{
type: 'text',
right: 8,
top: 8,
style: {
text: maxValue.toString(),
fontSize: 12,
fill: '#666',
fontWeight: 'bold'
}
},
{
type: 'text',
right: 8,
top: '50%',
style: {
text: latestValue.toString(),
fontSize: 12,
fill: '#f5222d',
fontWeight: 'bold'
}
},
{
type: 'text',
right: 8,
bottom: 8,
style: {
text: minValue.toString(),
fontSize: 12,
fill: '#666',
fontWeight: 'bold'
}
}
]
}) })
// 在图表容器中添加数值标注
const chartContainer = failedRegChartRef.value
if (chartContainer) {
// 清除之前的标注
const existingLabels = chartContainer.querySelectorAll('.chart-label')
existingLabels.forEach(label => label.remove())
// 添加右侧数值标注
const maxLabel = document.createElement('div')
maxLabel.className = 'chart-label'
maxLabel.style.cssText = `
position: absolute;
right: 8px;
top: 8px;
font-size: 12px;
font-weight: bold;
color: #666;
pointer-events: none;
z-index: 10;
`
maxLabel.textContent = maxValue.toString()
const latestLabel = document.createElement('div')
latestLabel.className = 'chart-label'
latestLabel.style.cssText = `
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
font-size: 12px;
font-weight: bold;
color: #f5222d;
pointer-events: none;
z-index: 10;
`
latestLabel.textContent = latestValue.toString()
const minLabel = document.createElement('div')
minLabel.className = 'chart-label'
minLabel.style.cssText = `
position: absolute;
right: 8px;
bottom: 8px;
font-size: 12px;
font-weight: bold;
color: #666;
pointer-events: none;
z-index: 10;
`
minLabel.textContent = minValue.toString()
// 添加底部时间标注
const oldestTimeLabel = document.createElement('div')
oldestTimeLabel.className = 'chart-label'
oldestTimeLabel.style.cssText = `
position: absolute;
left: 8px;
bottom: -20px;
font-size: 11px;
color: #999;
pointer-events: none;
z-index: 10;
`
// 计算图表实际显示的最旧数据时间(基于图表数据点数量)
const displayDataLength = Math.min(imsRealtimeRawData.value.length, 30)
const oldestDisplayIndex = Math.max(0, imsRealtimeRawData.value.length - displayDataLength)
const oldestDisplayData = imsRealtimeRawData.value[oldestDisplayIndex]
if (oldestDisplayData && oldestDisplayData.timestamp) {
oldestTimeLabel.textContent = calculateRelativeTime(oldestDisplayData.timestamp)
} else {
oldestTimeLabel.textContent = '--'
}
const nowTimeLabel = document.createElement('div')
nowTimeLabel.className = 'chart-label'
nowTimeLabel.style.cssText = `
position: absolute;
right: 8px;
bottom: -20px;
font-size: 11px;
color: #999;
pointer-events: none;
z-index: 10;
`
nowTimeLabel.textContent = 'now'
chartContainer.appendChild(maxLabel)
chartContainer.appendChild(latestLabel)
chartContainer.appendChild(minLabel)
chartContainer.appendChild(oldestTimeLabel)
chartContainer.appendChild(nowTimeLabel)
}
} }
// 处理IMS实时数据只存储当前选中网元 // 处理IMS实时数据只存储当前选中网元
@@ -822,7 +1053,7 @@ function handleIMSRealtimeData(res: any) {
onMounted(() => { onMounted(() => {
// 初始化所有图表为0值线 // 初始化所有图表为0值线
const defaultChartOption = { const defaultChartOption = {
grid: { left: 0, right: 0, top: 10, bottom: 10 }, grid: { left: 0, right: 30, top: 10, bottom: 10 }, // 增加右侧边距
xAxis: { type: 'category', show: false, data: [1, 2, 3, 4, 5] }, xAxis: { type: 'category', show: false, data: [1, 2, 3, 4, 5] },
yAxis: { type: 'value', show: false }, yAxis: { type: 'value', show: false },
series: [{ series: [{
@@ -848,7 +1079,7 @@ onMounted(() => {
const latestValue = mosData[mosData.length - 1] const latestValue = mosData[mosData.length - 1]
chart.setOption({ chart.setOption({
grid: { left: 0, right: 0, top: 10, bottom: 10 }, grid: { left: 0, right: 30, top: 10, bottom: 10 }, // 增加右侧边距
xAxis: { type: 'category', show: false, data: [1,2,3,4,5] }, xAxis: { type: 'category', show: false, data: [1,2,3,4,5] },
yAxis: { type: 'value', show: false }, yAxis: { type: 'value', show: false },
series: [{ series: [{
@@ -932,7 +1163,7 @@ function calculateMOValue() {
return moValue.toFixed(1) return moValue.toFixed(1)
} }
// 计算MT值 (SCSCF.06/SCSCF.07)*100 // 计算MT值 (SCSCF.07/SCSCF.08)*100
function calculateMTValue() { function calculateMTValue() {
if (imsRealtimeRawData.value.length === 0) return '-' if (imsRealtimeRawData.value.length === 0) return '-'
@@ -941,12 +1172,12 @@ function calculateMTValue() {
if (!latestData || !latestData.data) return '-' if (!latestData || !latestData.data) return '-'
const kpiEvent = latestData.data const kpiEvent = latestData.data
const scscf06 = Number(kpiEvent['SCSCF.06']) || 0
const scscf07 = Number(kpiEvent['SCSCF.07']) || 0 const scscf07 = Number(kpiEvent['SCSCF.07']) || 0
const scscf08 = Number(kpiEvent['SCSCF.08']) || 0
if (scscf07 === 0) return '-' if (scscf08 === 0) return '-'
const mtValue = (scscf06 / scscf07) * 100 const mtValue = (scscf07 / scscf08) * 100
return mtValue.toFixed(1) return mtValue.toFixed(1)
} }
@@ -1046,6 +1277,22 @@ function calculateTimeDifference(latestData: any, previousData: any) {
} }
} }
// 计算相对时间(距离现在多久)
function calculateRelativeTime(timestamp: number) {
const now = Date.now()
const diffMs = now - timestamp
const diffMinutes = Math.floor(diffMs / (1000 * 60))
const diffHours = Math.floor(diffMinutes / 60)
if (diffHours >= 1) {
return `${diffHours}h`
} else if (diffMinutes >= 1) {
return `${diffMinutes}m`
} else {
return '1m'
}
}
// 辅助函数从数据中计算MO值 // 辅助函数从数据中计算MO值
function calculateMOValueFromData(kpiEvent: any) { function calculateMOValueFromData(kpiEvent: any) {
const scscf05 = Number(kpiEvent['SCSCF.05']) || 0 const scscf05 = Number(kpiEvent['SCSCF.05']) || 0
@@ -1055,9 +1302,9 @@ function calculateMOValueFromData(kpiEvent: any) {
// 辅助函数从数据中计算MT值 // 辅助函数从数据中计算MT值
function calculateMTValueFromData(kpiEvent: any) { function calculateMTValueFromData(kpiEvent: any) {
const scscf06 = Number(kpiEvent['SCSCF.06']) || 0
const scscf07 = Number(kpiEvent['SCSCF.07']) || 0 const scscf07 = Number(kpiEvent['SCSCF.07']) || 0
return scscf07 === 0 ? '-' : (scscf06 / scscf07) * 100 const scscf08 = Number(kpiEvent['SCSCF.08']) || 0
return scscf08 === 0 ? '-' : (scscf07 / scscf08) * 100
} }
// 计算registration success值 // 计算registration success值
@@ -1527,30 +1774,30 @@ function calculateFailedRegistrationsChange() {
} }
// 测试数据更新 // 测试数据更新
function testDataUpdate() { // function testDataUpdate() {
// console.log('测试数据更新') // // console.log('测试数据更新')
//
// 创建模拟的后端KPI数据消息格式 // // 创建模拟的后端KPI数据消息格式
const mockWebSocketMessage = { // const mockWebSocketMessage = {
code: 1, // code: 1,
data: { // data: {
data: { // data: {
'SCSCF.03': Math.floor(Math.random() * 300000) + 200000, // active registrations // 'SCSCF.03': Math.floor(Math.random() * 300000) + 200000, // active registrations
'SCSCF.04': Math.floor(Math.random() * 310000) + 200000, // total registrations // 'SCSCF.04': Math.floor(Math.random() * 310000) + 200000, // total registrations
'SCSCF.05': Math.floor(Math.random() * 15000) + 10000, // MO calls // 'SCSCF.05': Math.floor(Math.random() * 15000) + 10000, // MO calls
'SCSCF.06': Math.floor(Math.random() * 16000) + 10000, // total calls // 'SCSCF.06': Math.floor(Math.random() * 16000) + 10000, // total calls
'SCSCF.07': Math.floor(Math.random() * 15000) + 10000, // successful calls // 'SCSCF.07': Math.floor(Math.random() * 15000) + 10000, // successful calls
}, // },
groupId: `10_IMS_${selectedImsNeId.value}` // groupId: `10_IMS_${selectedImsNeId.value}`
}, // },
msg: 'success' // msg: 'success'
} // }
//
// console.log('模拟WebSocket消息:', mockWebSocketMessage) // // console.log('模拟WebSocket消息:', mockWebSocketMessage)
//
// 直接调用handleIMSRealtimeData函数 // // 直接调用handleIMSRealtimeData函数
handleIMSRealtimeData(mockWebSocketMessage) // handleIMSRealtimeData(mockWebSocketMessage)
} // }
</script> </script>
<style scoped> <style scoped>
@@ -1593,12 +1840,14 @@ function testDataUpdate() {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-end; justify-content: flex-end;
margin-bottom: 25px; /* 为底部时间标注留出空间 */
} }
.mini-chart { .mini-chart {
width: 100%; width: 100%;
height: 100%; height: 100%;
margin-bottom: 0; margin-bottom: 0;
display: block; display: block;
position: relative;
} }
.card-subtext { .card-subtext {
font-size: 12px; font-size: 12px;
@@ -1679,4 +1928,30 @@ function testDataUpdate() {
[data-theme='dark'] .ims-select :deep(.ant-select-arrow) { [data-theme='dark'] .ims-select :deep(.ant-select-arrow) {
color: #cacada; color: #cacada;
} }
/* IMS Control Tower 角标样式 */
.control-tower-badge {
position: absolute;
left: 50%;
bottom: 0;
margin-left: 100px;
font-size: 12px;
font-weight: 500;
color: #1890ff;
background: linear-gradient(135deg, #e6f7ff 0%, #bae7ff 100%);
padding: 4px 12px;
border-radius: 12px;
border: 1px solid #91d5ff;
box-shadow: 0 2px 4px rgba(24, 144, 255, 0.1);
letter-spacing: 0.5px;
transform: translateY(4px);
}
/* 暗色主题适配 */
[data-theme='dark'] .control-tower-badge {
color: #69c0ff;
background: linear-gradient(135deg, #001529 0%, #002766 100%);
border-color: #1890ff;
box-shadow: 0 2px 4px rgba(105, 192, 255, 0.1);
}
</style> </style>