Merge branch 'main-v2' of http://192.168.2.166:3180/OMC/ems_frontend_vue3 into main-v2
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user