feat:综合仪表盘修改迁移以及版本兼容修复

This commit is contained in:
zhongzm
2025-07-18 17:27:12 +08:00
parent 8b6805fdb0
commit 96c1f54b71
11 changed files with 584 additions and 276 deletions

View File

@@ -29,6 +29,7 @@
"crypto-js": "4.2.0", "crypto-js": "4.2.0",
"dayjs": "1.11.13", "dayjs": "1.11.13",
"echarts": "5.6.0", "echarts": "5.6.0",
"echarts-liquidfill": "^3.1.0",
"file-saver": "2.0.5", "file-saver": "2.0.5",
"grid-layout-plus": "1.0.6", "grid-layout-plus": "1.0.6",
"intl-tel-input": "25.2.0", "intl-tel-input": "25.2.0",

34
src/api/neUser/base5G.ts Normal file
View File

@@ -0,0 +1,34 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
/**
* 查询列表
* @param query 查询参数
* @returns object
*/
export async function listBase5G(query: Record<string, any>) {
const result = await request({
url: `/api/rest/ueManagement/v1/elementType/${query.neType.toLowerCase()}/objectType/nbInfo`,
method: 'GET',
params: query,
});
let data: DataList = {
total: 0,
rows: [],
code: result.code,
msg: result.msg,
};
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
const rows = parseObjLineToHump(result.data.data);
data.total = rows.length;
data.rows = rows;
}
// 模拟数据
// data.rows = [{"address":"192.168.1.137:38412","id":"217","name":"attach-enb-100000-20","ueNum":0}]
// data.rows = [{address: "192.168.8.223", id: 257, name: "SmallCell", ueNum: 0}]
return data;
}

66
src/api/neUser/ims.ts Normal file
View File

@@ -0,0 +1,66 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
/**
* 查询列表
* @param query 查询参数
* @returns object
*/
export async function listUEInfoByIMS(query: Record<string, any>) {
query.nbId = query.id;
const result = await request({
url: '/api/rest/ueManagement/v1/elementType/ims/objectType/ueInfo',
method: 'GET',
params: query,
});
let data: DataList = {
total: 0,
rows: [],
code: result.code,
msg: result.msg,
};
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
const rows = parseObjLineToHump(result.data.data);
data.total = rows.length;
data.rows = rows;
}
// 测试数据
// data.rows = [
// {
// activeTime: '2023-11-29 17:04:54',
// barring: 0,
// impu: 'sip:12307551232@ims.mnc000.mcc460.3gppnetwork.org',
// imsi: '460001230000002',
// msisdn: '12307551232',
// regState: 1,
// },
// ];
return data;
}
/**
* 首页查询IMS在线用户数
* @param query 查询参数
* @returns neId
*/
export async function listUENumByIMS(neId: String) {
const result = await request({
url: `/api/rest/ueManagement/v1/elementType/ims/objectType/ueNum?neId=${neId}`,
method: 'GET',
});
if (result.code === RESULT_CODE_SUCCESS) {
let num = result.data['ueNum'] || 0;
if (num === 0) {
num = result.data.data['ueNum'] || 0;
}
return Object.assign(result, { data: num });
}
// 模拟数据
// { "ueNum": 0 }
// result.data = 0
return result;
}

113
src/api/neUser/smf.ts Normal file
View File

@@ -0,0 +1,113 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch';
/**
* 查询列表
* @param query 查询参数
* @returns object
*/
export async function listUEInfoBySMF(query: Record<string, any>) {
query.nbId = query.id;
const result = await request({
url: '/api/rest/ueManagement/v1/elementType/smf/objectType/ueInfo',
method: 'GET',
params: query,
});
let data: DataList = {
total: 0,
rows: [],
code: result.code,
msg: result.msg,
};
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && result.data) {
if (result.data.total && result.data.data) {
data.total = result.data.total;
data.rows = result.data.data;
} else {
Object.assign(data, {
total: result.data.length,
rows: result.data,
});
}
}
// 模拟数据
// data.code = RESULT_CODE_SUCCESS;
// data.total = 2;
// data.rows = [
// {
// imsi: 'imsi-460000100000090',
// msisdn: 'msisdn-12307550090',
// pduSessionInfo: [
// {
// activeTime: '2024-06-19 14:35:26',
// dnn: 'ims',
// ipv4: '10.10.48.8',
// ipv6: '',
// pduSessionID: 6,
// ranN3IP: '192.168.1.137',
// sstSD: '1-000001',
// tai: '46000-001124',
// upState: 'Active',
// upfN3IP: '192.168.1.161',
// },
// {
// activeTime: '2024-06-19 14:35:26',
// dnn: 'cmnet',
// ipv4: '10.10.48.9',
// ipv6: '2001:4860:4860::/64',
// pduSessionID: 7,
// ranN3IP: '192.168.1.137',
// sstSD: '1-000001',
// tai: '46000-001124',
// upState: 'Active',
// upfN3IP: '192.168.1.161',
// },
// ],
// ratType: 'NR',
// },
// {
// imsi: 'imsi-460602072701180',
// msisdn: 'msisdn-123460600080',
// pduSessionInfo: [
// {
// activeTime: '2024-06-19 14:31:09',
// dnn: 'cmnet',
// ipv4: '10.10.48.4',
// ipv6: '',
// pduSessionID: 5,
// ranN3IP: '192.168.8.223',
// sstSD: '1-000001',
// tai: '46060-0001',
// upState: 'Active',
// upfN3IP: '192.168.1.161',
// },
// ],
// ratType: 'EUTRAN',
// },
// ];
return data;
}
/**
* 首页查询SMF在线用户数
* @param query 查询参数
* @returns neId
*/
export async function listUENumBySMF(neId: String) {
const result = await request({
url: `/api/rest/ueManagement/v1/elementType/smf/objectType/ueNum?neId=${neId}`,
method: 'GET',
});
if (result.code === RESULT_CODE_SUCCESS) {
return Object.assign(result, {
data: result.data.data['ueNum'],
});
}
// 模拟数据
// { "data": { "ueNum": 0 } }
// result.data = 0
return result;
}

View File

@@ -368,10 +368,13 @@ export default {
imsUeNum: "VoNR/VoLTE", imsUeNum: "VoNR/VoLTE",
smfUeNum: "Data Sessions", smfUeNum: "Data Sessions",
gnbBase: "Online gNodeB", gnbBase: "Online gNodeB",
gnbSumBase: "Total gNodeB",
enbBase: "Online eNodeB", enbBase: "Online eNodeB",
enbSumBase: "Total eNodeB",
gnbUeNum:'5G Active Users', gnbUeNum:'5G Active Users',
enbUeNum:'4G Active Users', enbUeNum:'4G Active Users',
baseTitle:'Online Information', baseTitle:'Online Information',
nodeBInfo: 'NodeB Information',
}, },
upfFlow:{ upfFlow:{
title: "UPF Throughput", title: "UPF Throughput",
@@ -401,6 +404,7 @@ export default {
}, },
userActivity: { userActivity: {
title: "User Activity", title: "User Activity",
imsTitle: "IMS Activity",
type: "Type", type: "Type",
duration: "Duration", duration: "Duration",
caller: "Caller", caller: "Caller",

View File

@@ -368,10 +368,13 @@ export default {
imsUeNum: "IMS 会话数", imsUeNum: "IMS 会话数",
smfUeNum: "Data 会话数", smfUeNum: "Data 会话数",
gnbBase: "5G 基站数", gnbBase: "5G 基站数",
gnbSumBase: "5G 基站总数",
gnbUeNum:'5G 用户数', gnbUeNum:'5G 用户数',
enbBase: "4G 基站数", enbBase: "4G 基站数",
enbSumBase: "4G 基站总数",
enbUeNum:'4G 用户数', enbUeNum:'4G 用户数',
baseTitle:'在线信息', baseTitle:'在线信息',
nodeBInfo: '基站信息',
}, },
upfFlow:{ upfFlow:{
title: "用户面吞吐量", title: "用户面吞吐量",
@@ -401,6 +404,7 @@ export default {
}, },
userActivity: { userActivity: {
title: "用户活动", title: "用户活动",
imsTitle: "IMS 活动",
type: "类型", type: "类型",
duration: "时长", duration: "时长",
caller: "主叫", caller: "主叫",

View File

@@ -0,0 +1,93 @@
import { defineStore } from 'pinia';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { listAllNeInfo } from '@/api/ne/neInfo';
import { parseDataToOptions } from '@/utils/parse-tree-utils';
import { getNePerformanceList } from '@/api/perfManage/taskManage';
/**网元信息类型 */
type NeInfo = {
/**网元列表 */
neList: Record<string, any>[];
/**级联options树结构 */
neCascaderOptions: Record<string, any>[];
/**选择器单级父类型 */
neSelectOtions: Record<string, any>[];
/**性能测量数据集 */
perMeasurementList: Record<string, any>[];
};
const useNeInfoStore = defineStore('neinfo', {
state: (): NeInfo => ({
neList: [],
neCascaderOptions: [],
neSelectOtions: [],
perMeasurementList: [],
}),
getters: {
/**
* 获取级联options树结构
* @param state 内部属性不用传入
* @returns 级联options
*/
getNeCascaderOptions(state) {
return state.neCascaderOptions;
},
/**
* 选择器单级父类型
* @param state 内部属性不用传入
* @returns 级联options
*/
getNeSelectOtions(state) {
return state.neSelectOtions;
},
},
actions: {
// 刷新网元列表
async fnRefreshNelist() {
this.neList = [];
const res = await this.fnNelist();
return res;
},
// 获取网元列表
async fnNelist() {
// 有数据不请求
if (this.neList.length > 0) {
return { code: 1, data: this.neList, msg: 'success' };
}
const res = await listAllNeInfo({
bandStatus: false,
});
if (res.code === RESULT_CODE_SUCCESS) {
// 原始列表
this.neList = JSON.parse(JSON.stringify(res.data));
// 转级联数据
const options = parseDataToOptions(
res.data,
'neType',
'neName',
'neId'
);
this.neCascaderOptions = options;
// 转选择器单级父类型
this.neSelectOtions = options.map(item => item);
}
return res;
},
// 获取性能测量数据集列表
async fnNeTaskPerformance() {
// 有数据不请求
if (this.perMeasurementList.length > 0) {
return { code: 1, data: this.perMeasurementList, msg: 'success' };
}
const res = await getNePerformanceList();
if (res.code === RESULT_CODE_SUCCESS) {
this.perMeasurementList = res.data;
}
return res;
},
},
});
export default useNeInfoStore;

View File

@@ -78,9 +78,9 @@ const alarmTypeType = ref<any>([
/**告警类型Top数据 */ /**告警类型Top数据 */
const alarmTypeTypeTop = ref<any>([ const alarmTypeTypeTop = ref<any>([
{ neType: 'AMF', total: 0 }, { name: 'AMF', value: 0 },
{ neType: 'UDM', total: 0 }, { name: 'UDM', value: 0 },
{ neType: 'SMF', total: 0 }, { name: 'SMF', value: 0 },
]); ]);
// //
@@ -92,7 +92,7 @@ function initPicture() {
if (res0.code === RESULT_CODE_SUCCESS && Array.isArray(res0.data)) { if (res0.code === RESULT_CODE_SUCCESS && Array.isArray(res0.data)) {
for (const item of res0.data) { for (const item of res0.data) {
let index = 0; let index = 0;
switch (item.severity) { switch (item.name) {
case 'Critical': case 'Critical':
index = 0; index = 0;
break; break;
@@ -109,7 +109,7 @@ function initPicture() {
// index = 4; // index = 4;
// break; // break;
} }
alarmTypeType.value[index].value = Number(item.total); alarmTypeType.value[index].value = Number(item.value);
} }
} }
} }
@@ -119,7 +119,7 @@ function initPicture() {
alarmTypeTypeTop.value = alarmTypeTypeTop.value alarmTypeTypeTop.value = alarmTypeTypeTop.value
.concat(res1.data) .concat(res1.data)
.sort((a: any, b: any) => { .sort((a: any, b: any) => {
return b.total - a.total; return b.value - a.value;
}) })
.slice(0, 3); .slice(0, 3);
} }
@@ -129,79 +129,123 @@ function initPicture() {
const optionData: EChartsOption = { const optionData: EChartsOption = {
title: [ title: [
{ {
show: false, text: 'Top3',
}, left: 'center',
{ top: '36%',
text: t('views.dashboard.overview.alarmTypeBar.topTitle'),
textStyle: { textStyle: {
color: '#fff', color: '#fff',
fontSize: '14', fontSize: 16,
fontWeight: 400, fontWeight: 'bold',
}, },
top: '50%',
left: '0%',
}, },
], ],
grid: [
{ // 主图
top: '5%',
left: '20%',
right: '10%',
height: '35%'
},
{ // Top3
top: '50%',
left: '20%',
right: '10%',
height: '30%'
}
],
tooltip: { tooltip: {
trigger: 'item',
axisPointer: { type: 'shadow' },
formatter: '{b} : {c}', formatter: '{b} : {c}',
}, },
legend: { legend: {
orient: 'vertical', show: false
right: '2%',
top: '12%',
data: alarmTypeType.value.map((item: any) => item.name), //label数组
textStyle: {
color: '#A7D6F4', // 设置图例文字颜色
}, },
}, xAxis: [
grid: [
{ {
top: '60%', type: 'value',
left: '15%', gridIndex: 0,
right: '25%', show: false,
bottom: '10%',
}, },
{
type: 'value',
gridIndex: 1,
show: false,
}
],
yAxis: [
{
type: 'category',
gridIndex: 0,
data: alarmTypeType.value.map((item: any) => item.name),
axisLabel: { color: '#fff', fontSize: 14,fontWeight: 'bold' },
axisLine: { show: false },
axisTick: { show: false },
inverse: true
},
{
type: 'category',
gridIndex: 1,
data: alarmTypeTypeTop.value.map((item: any) => item.name),
axisLabel: { color: '#fff', fontSize: 14,fontWeight: 'bold' },
axisLine: { show: false },
axisTick: { show: false },
inverse: true
}
], ],
series: [ series: [
//饼图: // 四类型告警横向柱状图
{ {
type: 'pie', type: 'bar',
radius: '35%', xAxisIndex: 0,
color: ['#f5222d', '#fa8c16', '#fadb14', '#1677ff', '#13c2c2'], yAxisIndex: 0,
barWidth: 18,
itemStyle: {
borderRadius: [0, 8, 8, 0],
color: function (params: any) {
// 渐变色
const colorArr = [
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#f5222d' },
{ offset: 1, color: '#fa8c16' }
]),
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#fa8c16' },
{ offset: 1, color: '#fadb14' }
]),
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#fadb14' },
{ offset: 1, color: '#1677ff' }
]),
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#1677ff' },
{ offset: 1, color: '#00fcff' }
])
];
return colorArr[params.dataIndex] || colorArr[3];
}
},
label: { label: {
show: true, show: true,
position: 'inner', position: 'right',
color: '#fff', //淡蓝色
fontWeight: 'bold',
fontSize: 16,
formatter: (params: any) => { formatter: (params: any) => {
if (!params.value) return ''; if (!params.value) return '';
return `${params.value}`; return `${params.value}`;
}, },
}, },
labelLine: { data: alarmTypeType.value.map((item: any) => item.value),
show: false, zlevel: 2
}, },
center: ['35%', '25%'], // Top3横向柱状图
data: alarmTypeType.value,
zlevel: 2, // 设置zlevel为1使得柱状图在下层显示
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
//柱状
{ {
name: 'Top3',
type: 'bar', type: 'bar',
barWidth: 12, // 柱子宽度 xAxisIndex: 1,
barCategoryGap: '30%', // 控制同一系列的柱间距离 yAxisIndex: 1,
label: { barWidth: 18,
show: true,
position: 'right', // 位置
color: '#A7D6F4', //淡蓝色
fontSize: 14,
distance: 14, // label与柱子距离
formatter: '{c}',
},
itemStyle: { itemStyle: {
borderRadius: [0, 20, 20, 0], // 圆角(左上、右上、右下、左下) borderRadius: [0, 20, 20, 0], // 圆角(左上、右上、右下、左下)
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [ color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
@@ -210,42 +254,19 @@ function initPicture() {
{ offset: 1, color: '#2f54eb' }, { offset: 1, color: '#2f54eb' },
]), // 渐变 ]), // 渐变
}, },
data: alarmTypeTypeTop.value.map((item: any) => item.total), label: {
show: true,
position: 'right',
color: '#fff', //淡蓝色
fontWeight: 'bold',
fontSize: 16,
formatter: '{c}'
}, },
], data: alarmTypeTypeTop.value.map((item: any) => item.value),
// 柱状图设置 zlevel: 1
xAxis: [ }
{ ]
splitLine: {
show: false,
},
type: 'value',
show: false,
},
],
yAxis: [
{
splitLine: {
show: false,
},
axisLine: {
//y轴
show: false,
},
type: 'category',
axisTick: {
show: false,
},
inverse: true,
data: alarmTypeTypeTop.value.map((item: any) => item.neType),
axisLabel: {
color: '#A7D6F4',
fontSize: 14,
},
},
],
}; };
fnDesign(alarmTypeBar.value, optionData); fnDesign(alarmTypeBar.value, optionData);
}); });
} }

View File

@@ -7,7 +7,7 @@ import { graphNodeClickID, graphNodeState } from '../../hooks/useTopology';
import useI18n from '@/hooks/useI18n'; import useI18n from '@/hooks/useI18n';
import { markRaw } from 'vue'; import { markRaw } from 'vue';
// 引入液体填充图表 // 引入液体填充图表
// import 'echarts-liquidfill'; import 'echarts-liquidfill';
const { t } = useI18n(); const { t } = useI18n();

View File

@@ -203,7 +203,7 @@ function handleRanderGraph(
* 获取图组数据渲染到画布 * 获取图组数据渲染到画布
* @param reload 是否重载数据 * @param reload 是否重载数据
*/ */
function fnGraphDataLoad(reload: boolean = false) { function fnGraphDataLoad(reload: boolean = false) {
Promise.all([ Promise.all([
getGraphData(graphState.group), getGraphData(graphState.group),
listAllNeInfo({ listAllNeInfo({

View File

@@ -10,25 +10,33 @@ import UserActivity from './components/UserActivity/index.vue';
import IMSActivity from './components/IMSActivity/index.vue'; import IMSActivity from './components/IMSActivity/index.vue';
import AlarnTypeBar from './components/AlarnTypeBar/index.vue'; import AlarnTypeBar from './components/AlarnTypeBar/index.vue';
import UPFFlow from './components/UPFFlow/index.vue'; import UPFFlow from './components/UPFFlow/index.vue';
import { listIMSSessionNum } from '@/api/neData/ims';
import { listUDMSub } from '@/api/neData/udm_sub'; import { listUDMSub } from '@/api/neData/udm_sub';
import { listAMFNblist } from '@/api/neData/amf'; import { listUENumBySMF } from '@/api/neUser/smf';
import { listMMENblist } from '@/api/neData/mme'; import { listUENumByIMS } from '@/api/neUser/ims';
import { listSMFSubNum } from '@/api/neData/smf'; import { listBase5G } from '@/api/neUser/base5G';
import { graphNodeClickID, graphState, notNeNodes } from './hooks/useTopology'; import {
graphNodeClickID,
graphState,
notNeNodes,
graphNodeStateNum,
neStateRequestMap,
} from './hooks/useTopology';
import { upfTotalFlow, upfTFActive } from './hooks/useUPFTotalFlow'; import { upfTotalFlow, upfTFActive } from './hooks/useUPFTotalFlow';
import { useFullscreen } from '@vueuse/core'; import { useFullscreen } from '@vueuse/core';
import useWS from './hooks/useWS'; import useWS from './hooks/useWS';
import useAppStore from '@/store/modules/app'; import useAppStore from '@/store/modules/app';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import useNeListStore from '@/store/modules/ne_list'; import useNeInfoStore from '@/store/modules/neinfo';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { upfWhoId } from './hooks/useWS'; import { upfWhoId } from './hooks/useWS';
import { listAMFNbStatelist } from '@/api/neData/amf'; import {
listAMFNbStatelist,
} from '@/api/neData/amf';
import { listMMENbStatelist } from '@/api/neData/mme'; import { listMMENbStatelist } from '@/api/neData/mme';
const neInfoStore = useNeListStore();
const neInfoStore = useNeInfoStore();
const router = useRouter(); const router = useRouter();
const appStore = useAppStore(); const appStore = useAppStore();
const { t } = useI18n(); const { t } = useI18n();
@@ -66,6 +74,7 @@ let skimState: SkimStateType = reactive({
enbUeNum: 0, enbUeNum: 0,
gNbSumNum: 0, gNbSumNum: 0,
eNbSumNum: 0, eNbSumNum: 0,
}); });
/**网元参数 */ /**网元参数 */
@@ -88,10 +97,13 @@ function fnGetNeState() {
for (const node of graphState.data.nodes) { for (const node of graphState.data.nodes) {
if (notNeNodes.includes(node.id)) continue; if (notNeNodes.includes(node.id)) continue;
const neInfoList = node.neInfoList || []; const neInfoList = node.neInfoList || [];
if (neInfoList.length === 0) continue; if (neInfoList.length === 0) continue;
for (const neInfo of neInfoList) { for (const neInfo of neInfoList) {
if (!neInfo.neType || !neInfo.neId) continue; if (!neInfo.neType || !neInfo.neId) continue;
wsSend({ wsSend({
@@ -108,6 +120,7 @@ function fnGetNeState() {
/**获取概览信息 */ /**获取概览信息 */
async function fnGetSkim() { async function fnGetSkim() {
let tempGnbSumNum = 0; let tempGnbSumNum = 0;
let tempEnbSumNum = 0; let tempEnbSumNum = 0;
@@ -122,10 +135,18 @@ async function fnGetSkim() {
// (skimState.udmSubNum += res.total), // (skimState.udmSubNum += res.total),
// }, // },
// ], // ],
[
'UDM',
{
request: (neId: string) => listUDMSub({ neId: neId, pageNum: 1, pageSize: 1 }),
process: (res: any) =>
res.code === RESULT_CODE_SUCCESS && (skimState.udmSubNum = res.total),
},
],
[ [
'SMF', 'SMF',
{ {
request: (neId: string) => listSMFSubNum(neId), request: (neId: string) => listUENumBySMF(neId),
process: (res: any) => process: (res: any) =>
res.code === RESULT_CODE_SUCCESS && (skimState.smfUeNum += res.data), res.code === RESULT_CODE_SUCCESS && (skimState.smfUeNum += res.data),
}, },
@@ -133,7 +154,7 @@ async function fnGetSkim() {
[ [
'IMS', 'IMS',
{ {
request: (neId: string) => listIMSSessionNum(neId), request: (neId: string) => listUENumByIMS(neId),
process: (res: any) => process: (res: any) =>
res.code === RESULT_CODE_SUCCESS && (skimState.imsUeNum += res.data), res.code === RESULT_CODE_SUCCESS && (skimState.imsUeNum += res.data),
}, },
@@ -141,7 +162,7 @@ async function fnGetSkim() {
[ [
'AMF', 'AMF',
{ {
request: (neId: string) => listAMFNblist({ neId }), request: (neId: string) => listBase5G({ neType: 'AMF', neId }),
process: async (res: any, neId: any) => { process: async (res: any, neId: any) => {
if (res.code === RESULT_CODE_SUCCESS) { if (res.code === RESULT_CODE_SUCCESS) {
skimState.gnbNum += res.total; skimState.gnbNum += res.total;
@@ -150,12 +171,10 @@ async function fnGetSkim() {
0 0
); );
const amfNbRes = await listAMFNbStatelist({ neId }); const amfNbRes = await listAMFNbStatelist({ neId });
if ( if (amfNbRes.code === RESULT_CODE_SUCCESS && Array.isArray(amfNbRes.data)) {
amfNbRes.code === RESULT_CODE_SUCCESS &&
Array.isArray(amfNbRes.data)
) {
// skimState.gNbSumNum += amfNbRes.data.length; // skimState.gNbSumNum += amfNbRes.data.length;
tempGnbSumNum += amfNbRes.data.length; tempGnbSumNum += amfNbRes.data.length;
} }
} }
}, },
@@ -164,7 +183,7 @@ async function fnGetSkim() {
[ [
'MME', 'MME',
{ {
request: (neId: string) => listMMENblist({ neId }), request: (neId: string) => listBase5G({ neType: 'MME', neId }),
process: async (res: any, neId: any) => { process: async (res: any, neId: any) => {
if (res.code === RESULT_CODE_SUCCESS) { if (res.code === RESULT_CODE_SUCCESS) {
skimState.enbNum += res.total; skimState.enbNum += res.total;
@@ -174,13 +193,11 @@ async function fnGetSkim() {
); );
const mmeNbRes = await listMMENbStatelist({ neId }); const mmeNbRes = await listMMENbStatelist({ neId });
if ( if (mmeNbRes.code === RESULT_CODE_SUCCESS && Array.isArray(mmeNbRes.data)) {
mmeNbRes.code === RESULT_CODE_SUCCESS &&
Array.isArray(mmeNbRes.data)
) {
// skimState.eNbSumNum += mmeNbRes.data.length; // skimState.eNbSumNum += mmeNbRes.data.length;
tempEnbSumNum += mmeNbRes.data.length; tempEnbSumNum += mmeNbRes.data.length;
} }
} }
}, },
}, },
@@ -207,7 +224,7 @@ async function fnGetSkim() {
// 重置 // 重置
Object.assign(skimState, { Object.assign(skimState, {
// udmSubNum: 0, udmSubNum: 0,
smfUeNum: 0, smfUeNum: 0,
imsUeNum: 0, imsUeNum: 0,
gnbNum: 0, gnbNum: 0,
@@ -231,13 +248,31 @@ async function fnGetSkim() {
skimState.eNbSumNum = tempEnbSumNum; skimState.eNbSumNum = tempEnbSumNum;
// UDM // UDM
listUDMSub({ neId: udmNeId.value, pageNum: 1, pageSize: 1 }).then(res => { // UDM - 使用await确保同步处理
if (res.code === RESULT_CODE_SUCCESS) { // try {
skimState.udmSubNum = res.total; // const udmRes = await listUDMSub({ neId: udmNeId.value, pageNum: 1, pageSize: 1 });
} // if (udmRes.code === RESULT_CODE_SUCCESS) {
}); // skimState.udmSubNum = udmRes.total;
// } else {
// skimState.udmSubNum = 0;
// }
// } catch (error) {
// skimState.udmSubNum = 0;
// }
// UDM
// listUDMSub({ neId: udmNeId.value, pageNum: 1, pageSize: 1 }).then(res => {
// if (res.code === RESULT_CODE_SUCCESS) {
// skimState.udmSubNum = res.total;
// } else {
// skimState.udmSubNum = 0;
// }
// }).catch(() => {
// skimState.udmSubNum = 0;
// });
} }
/**初始数据函数 */ /**初始数据函数 */
function loadData() { function loadData() {
fnGetNeState(); // 获取网元状态 fnGetNeState(); // 获取网元状态
@@ -306,13 +341,22 @@ let udmOtions = ref<Record<string, any>[]>([]);
let onlineOtions = ref<Record<string, any>[]>([]); let onlineOtions = ref<Record<string, any>[]>([]);
/**用户数量-选择UDM */ /**用户数量-选择UDM */
function fnSelectUDM(e: any) { async function fnSelectUDM(e: any) {
udmNeId.value = e.key; udmNeId.value = e.key;
listUDMSub({ neId: udmNeId.value, pageNum: 1, pageSize: 1 }).then(res => { try {
const res = await listUDMSub({ neId: udmNeId.value, pageNum: 1, pageSize: 1 });
// listUDMSub({ neId: udmNeId.value, pageNum: 1, pageSize: 1 }).then(res => {
if (res.code === RESULT_CODE_SUCCESS) { if (res.code === RESULT_CODE_SUCCESS) {
skimState.udmSubNum = res.total; skimState.udmSubNum = res.total;
}else{
skimState.udmSubNum = 0;
}
// }).catch(() => {
// skimState.udmSubNum = 0;
// });
} catch (error) {
skimState.udmSubNum = 0;
} }
});
} }
/**资源控制-选择NE */ /**资源控制-选择NE */
function fnSelectNeRe(e: any) { function fnSelectNeRe(e: any) {
@@ -351,26 +395,8 @@ onMounted(() => {
// UDM // UDM
let arr1: Record<string, any>[] = []; let arr1: Record<string, any>[] = [];
res.data.forEach((v: any) => { res.data.forEach((v: any) => {
if ( if (v.status && ['UDM', 'UPF', 'AUSF', 'PCF', 'SMF', 'AMF', 'OMC', 'SMSC', 'IMS', 'MME'].includes(v.neType)) {
v.status && onlineArr.push({ value: v.neType + '_' + v.neId, label: v.neName, rmUid: v.rmUid });
[
'UDM',
'UPF',
'AUSF',
'PCF',
'SMF',
'AMF',
'OMC',
'SMSC',
'IMS',
'MME',
].includes(v.neType)
) {
onlineArr.push({
value: v.neType + '_' + v.neId,
label: v.neName,
rmUid: v.rmUid,
});
} }
if (v.neType === 'UDM') { if (v.neType === 'UDM') {
arr1.push({ value: v.neId, label: v.neName, rmUid: v.rmUid }); arr1.push({ value: v.neId, label: v.neName, rmUid: v.rmUid });
@@ -378,9 +404,17 @@ onMounted(() => {
}); });
udmOtions.value = arr1; udmOtions.value = arr1;
onlineOtions.value = onlineArr; onlineOtions.value = onlineArr;
// if (arr1.length > 0) {
// fnSelectUDM({ key: arr1[0].value });
// }
// 确保设置正确的udmNeId
if (arr1.length > 0) { if (arr1.length > 0) {
fnSelectUDM({ key: arr1[0].value }); udmNeId.value = arr1[0].value;
} }
// 移除单独的fnSelectUDM调用让fnGetSkim统一处理
// if (arr1.length > 0) {
// fnSelectUDM({ key: arr1[0].value });
// }
if (onlineArr.length > 0) { if (onlineArr.length > 0) {
fnSelectNeRe({ key: onlineArr[0].value }); fnSelectNeRe({ key: onlineArr[0].value });
@@ -427,11 +461,7 @@ onBeforeUnmount(() => {
<template> <template>
<div class="viewport" ref="viewportDom"> <div class="viewport" ref="viewportDom">
<div class="brand"> <div class="brand">
<div <div class="brand-title" @click="toggle" :title="t('views.dashboard.overview.fullscreen')">
class="brand-title"
@click="toggle"
:title="t('views.dashboard.overview.fullscreen')"
>
{{ t('views.dashboard.overview.title') }} {{ t('views.dashboard.overview.title') }}
<FullscreenExitOutlined v-if="isFullscreen" /> <FullscreenExitOutlined v-if="isFullscreen" />
<FullscreenOutlined v-else /> <FullscreenOutlined v-else />
@@ -440,6 +470,7 @@ onBeforeUnmount(() => {
</div> </div>
<div class="column"> <div class="column">
<div class="skim panel"> <div class="skim panel">
<div class="inner"> <div class="inner">
<h3 class="leftright"> <h3 class="leftright">
@@ -449,32 +480,20 @@ onBeforeUnmount(() => {
</span> </span>
</h3> </h3>
<div class="data"> <div class="data">
<div <div class="item toRouter" :title="t('views.dashboard.overview.toRouter')">
class="item toRouter"
:title="t('views.dashboard.overview.toRouter')"
>
<div @click="fnToRouter('Sub_2010')"> <div @click="fnToRouter('Sub_2010')">
<UserOutlined <UserOutlined style="color: #4096ff; margin-right: 8px; font-size: 1.1rem" />
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
/>
{{ skimState.udmSubNum }} {{ skimState.udmSubNum }}
</div> </div>
<span> <span>
<a-dropdown <a-dropdown :trigger="['click']" :get-Popup-Container="getPopupContainer">
:trigger="['click']"
:get-Popup-Container="getPopupContainer"
>
<div class="toDeep-text"> <div class="toDeep-text">
{{ t('views.dashboard.overview.skim.users') }} {{ t('views.dashboard.overview.skim.users') }}
<DownOutlined style="margin-left: 12px; font-size: 12px" /> <DownOutlined style="margin-left: 12px; font-size: 12px" />
</div> </div>
<template #overlay> <template #overlay>
<a-menu @click="fnSelectUDM"> <a-menu @click="fnSelectUDM">
<a-menu-item <a-menu-item v-for="v in udmOtions" :key="v.value" :disabled="udmNeId === v.value">
v-for="v in udmOtions"
:key="v.value"
:disabled="udmNeId === v.value"
>
{{ v.label }} {{ v.label }}
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
@@ -482,13 +501,8 @@ onBeforeUnmount(() => {
</a-dropdown> </a-dropdown>
</span> </span>
</div> </div>
<div <div class="item toRouter" @click="fnToRouter('Ims_2080')" :title="t('views.dashboard.overview.toRouter')"
class="item toRouter" style="margin: 0 12px" v-perms:has="['dashboard:overview:imsUeNum']">
@click="fnToRouter('Ims_2080')"
:title="t('views.dashboard.overview.toRouter')"
style="margin: 0 12px"
v-perms:has="['dashboard:overview:imsUeNum']"
>
<div> <div>
<img :src="svgUserIMS" style="width: 18px; margin-right: 8px" /> <img :src="svgUserIMS" style="width: 18px; margin-right: 8px" />
{{ skimState.imsUeNum }} {{ skimState.imsUeNum }}
@@ -497,12 +511,8 @@ onBeforeUnmount(() => {
{{ t('views.dashboard.overview.skim.imsUeNum') }} {{ t('views.dashboard.overview.skim.imsUeNum') }}
</span> </span>
</div> </div>
<div <div class="item toRouter" @click="fnToRouter('Ue_2081')" :title="t('views.dashboard.overview.toRouter')"
class="item toRouter" v-perms:has="['dashboard:overview:smfUeNum']">
@click="fnToRouter('Ue_2081')"
:title="t('views.dashboard.overview.toRouter')"
v-perms:has="['dashboard:overview:smfUeNum']"
>
<div> <div>
<img :src="svgUserSMF" style="width: 18px; margin-right: 8px" /> <img :src="svgUserSMF" style="width: 18px; margin-right: 8px" />
{{ skimState.smfUeNum }} {{ skimState.smfUeNum }}
@@ -515,13 +525,11 @@ onBeforeUnmount(() => {
</div> </div>
</div> </div>
<!--告警统计--> <!--告警统计-->
<div class="alarmType panel"> <div class="alarmType panel">
<div class="inner"> <div class="inner">
<h3 <h3 class="toRouter leftright" :title="t('views.dashboard.overview.toRouter')">
class="toRouter leftright"
:title="t('views.dashboard.overview.toRouter')"
>
<span class="title" @click="fnToRouter('HistoryAlarm_2097')"> <span class="title" @click="fnToRouter('HistoryAlarm_2097')">
<PieChartOutlined style="color: #68d8fe" />&nbsp;&nbsp; <PieChartOutlined style="color: #68d8fe" />&nbsp;&nbsp;
{{ t('views.dashboard.overview.alarmTypeBar.alarmSum') }} {{ t('views.dashboard.overview.alarmTypeBar.alarmSum') }}
@@ -550,6 +558,7 @@ onBeforeUnmount(() => {
</div> </div>
<div class="column" style="flex: 4; margin: 1.333rem 0.833rem 0"> <div class="column" style="flex: 4; margin: 1.333rem 0.833rem 0">
<!-- 实时流量 --> <!-- 实时流量 -->
<div class="upfFlow panel"> <div class="upfFlow panel">
<div class="inner"> <div class="inner">
@@ -557,9 +566,10 @@ onBeforeUnmount(() => {
<span class="title"> <span class="title">
<div class="toRouter" @click="fnToRouter('GoldTarget_2104')" :title="t('views.dashboard.overview.toRouter')"> <div class="toRouter" @click="fnToRouter('GoldTarget_2104')" :title="t('views.dashboard.overview.toRouter')">
<AreaChartOutlined style="color: #68d8fe" />&nbsp;&nbsp; <AreaChartOutlined style="color: #68d8fe" />&nbsp;&nbsp;
{{ t('views.dashboard.overview.upfFlow.title') }} {{
</div> t('views.dashboard.overview.upfFlow.title')
&nbsp;&nbsp;&nbsp;&nbsp; }}
</div>&nbsp;&nbsp;&nbsp;&nbsp;
<a-dropdown <a-dropdown
:trigger="['click']" :trigger="['click']"
:get-Popup-Container="getPopupContainer" :get-Popup-Container="getPopupContainer"
@@ -576,7 +586,11 @@ onBeforeUnmount(() => {
</a-menu> </a-menu>
</template> </template>
</a-dropdown> </a-dropdown>
</span> </span>
</h3> </h3>
<div class="chart"> <div class="chart">
@@ -588,11 +602,8 @@ onBeforeUnmount(() => {
<!-- 网络拓扑 --> <!-- 网络拓扑 -->
<div class="topology panel"> <div class="topology panel">
<div class="inner"> <div class="inner">
<h3 <h3 class="toRouter centerStyle" @click="fnToRouter('TopologyArchitecture_2128')"
class="toRouter centerStyle" :title="t('views.dashboard.overview.toRouter')">
@click="fnToRouter('TopologyArchitecture_2128')"
:title="t('views.dashboard.overview.toRouter')"
>
<span class="title"> <span class="title">
<ApartmentOutlined style="color: #68d8fe" />&nbsp;&nbsp; <ApartmentOutlined style="color: #68d8fe" />&nbsp;&nbsp;
{{ t('views.dashboard.overview.topology.title') }} {{ t('views.dashboard.overview.topology.title') }}
@@ -612,47 +623,30 @@ onBeforeUnmount(() => {
<h3 class="leftright"> <h3 class="leftright">
<span class="title"> <span class="title">
<GlobalOutlined style="color: #68d8fe" />&nbsp;&nbsp; <GlobalOutlined style="color: #68d8fe" />&nbsp;&nbsp;
{{ t('views.dashboard.overview.skim.nodeBInfo') }} {{t('views.dashboard.overview.skim.nodeBInfo')}}
</span> </span>
</h3> </h3>
<div class="data" style="margin-top: 20px"> <div class="data" style="margin-top: 20px;">
<div <div class="item toRouter" @click="fnToRouter('BaseStation_2096', { neType: 'AMF' })"
class="item toRouter" :title="t('views.dashboard.overview.toRouter')">
@click="fnToRouter('BaseStation_2096', { neType: 'AMF' })"
:title="t('views.dashboard.overview.toRouter')"
>
<div style="align-items: flex-start"> <div style="align-items: flex-start">
<img <img :src="svgBase" style="width: 18px; margin-right: 8px; height: 2rem" />
:src="svgBase"
style="width: 18px; margin-right: 8px; height: 2rem"
/>
{{ skimState.gNbSumNum }} {{ skimState.gNbSumNum }}
</div> </div>
<span>{{ t('views.dashboard.overview.skim.gnbSumBase') }}</span> <span>{{ t('views.dashboard.overview.skim.gnbSumBase') }}</span>
</div> </div>
<div <div class="item toRouter" @click="fnToRouter('BaseStation_2096', { neType: 'AMF' })"
class="item toRouter" :title="t('views.dashboard.overview.toRouter')">
@click="fnToRouter('BaseStation_2096', { neType: 'AMF' })"
:title="t('views.dashboard.overview.toRouter')"
>
<div style="align-items: flex-start"> <div style="align-items: flex-start">
<img <img :src="svgBase" style="width: 18px; margin-right: 8px; height: 2rem" />
:src="svgBase"
style="width: 18px; margin-right: 8px; height: 2rem"
/>
{{ skimState.gnbNum }} {{ skimState.gnbNum }}
</div> </div>
<span>{{ t('views.dashboard.overview.skim.gnbBase') }}</span> <span>{{ t('views.dashboard.overview.skim.gnbBase') }}</span>
</div> </div>
<div <div class="item toRouter" @click="fnToRouter('BaseStation_2096', { neType: 'AMF' })"
class="item toRouter" :title="t('views.dashboard.overview.toRouter')">
@click="fnToRouter('BaseStation_2096', { neType: 'AMF' })"
:title="t('views.dashboard.overview.toRouter')"
>
<div style="align-items: flex-start"> <div style="align-items: flex-start">
<UserOutlined <UserOutlined style="color: #4096ff; margin-right: 8px; font-size: 1.1rem" />
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
/>
{{ skimState.gnbUeNum }} {{ skimState.gnbUeNum }}
</div> </div>
<span>{{ t('views.dashboard.overview.skim.gnbUeNum') }}</span> <span>{{ t('views.dashboard.overview.skim.gnbUeNum') }}</span>
@@ -663,45 +657,29 @@ onBeforeUnmount(() => {
<div class="skim panel base" v-perms:has="['dashboard:overview:enbBase']"> <div class="skim panel base" v-perms:has="['dashboard:overview:enbBase']">
<div class="inner"> <div class="inner">
<h3></h3> <h3>
<div class="data" style="margin-top: 40px"> </h3>
<div <div class="data" style="margin-top: 40px;">
class="item toRouter" <div class="item toRouter" @click="fnToRouter('BaseStation_2096', { neType: 'MME' })"
@click="fnToRouter('BaseStation_2096', { neType: 'MME' })" :title="t('views.dashboard.overview.toRouter')">
:title="t('views.dashboard.overview.toRouter')"
>
<div style="align-items: flex-start"> <div style="align-items: flex-start">
<img <img :src="svgBase" style="width: 18px; margin-right: 8px; height: 2rem" />
:src="svgBase"
style="width: 18px; margin-right: 8px; height: 2rem"
/>
{{ skimState.eNbSumNum }} {{ skimState.eNbSumNum }}
</div> </div>
<span>{{ t('views.dashboard.overview.skim.enbSumBase') }}</span> <span>{{ t('views.dashboard.overview.skim.enbSumBase') }}</span>
</div> </div>
<div <div class="item toRouter" @click="fnToRouter('BaseStation_2096', { neType: 'MME' })"
class="item toRouter" :title="t('views.dashboard.overview.toRouter')">
@click="fnToRouter('BaseStation_2096', { neType: 'MME' })"
:title="t('views.dashboard.overview.toRouter')"
>
<div style="align-items: flex-start"> <div style="align-items: flex-start">
<img <img :src="svgBase" style="width: 18px; margin-right: 8px; height: 2rem" />
:src="svgBase"
style="width: 18px; margin-right: 8px; height: 2rem"
/>
{{ skimState.enbNum }} {{ skimState.enbNum }}
</div> </div>
<span>{{ t('views.dashboard.overview.skim.enbBase') }}</span> <span>{{ t('views.dashboard.overview.skim.enbBase') }}</span>
</div> </div>
<div <div class="item toRouter" @click="fnToRouter('BaseStation_2096', { neType: 'MME' })"
class="item toRouter" :title="t('views.dashboard.overview.toRouter')">
@click="fnToRouter('BaseStation_2096', { neType: 'MME' })"
:title="t('views.dashboard.overview.toRouter')"
>
<div style="align-items: flex-start"> <div style="align-items: flex-start">
<UserOutlined <UserOutlined style="color: #4096ff; margin-right: 8px; font-size: 1.1rem" />
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
/>
{{ skimState.enbUeNum }} {{ skimState.enbUeNum }}
</div> </div>
<span>{{ t('views.dashboard.overview.skim.enbUeNum') }}</span> <span>{{ t('views.dashboard.overview.skim.enbUeNum') }}</span>
@@ -710,21 +688,17 @@ onBeforeUnmount(() => {
</div> </div>
</div> </div>
<!-- 资源情况 --> <!-- 资源情况 -->
<div class="resources panel"> <div class="resources panel">
<div class="inner"> <div class="inner">
<h3 class="resources leftright"> <h3 class="resources leftright">
<span class="title"> <span class="title">
<DashboardOutlined <DashboardOutlined style="color: #68d8fe;font-size: 20px;" />&nbsp;&nbsp;
style="color: #68d8fe; font-size: 20px" <div style="margin-left: -3px"> {{ t('views.dashboard.overview.resources.title') }}</div>
/>&nbsp;&nbsp; <a-dropdown :trigger="['click']" :get-Popup-Container="getPopupContainer">
<div style="margin-left: -3px">
{{ t('views.dashboard.overview.resources.title') }}
</div>
<a-dropdown
:trigger="['click']"
:get-Popup-Container="getPopupContainer"
>
<div class="toDeep-text"> <div class="toDeep-text">
{{ graphNodeClickID }} {{ graphNodeClickID }}
<DownOutlined style="margin-left: -2px; font-size: 12px" /> <DownOutlined style="margin-left: -2px; font-size: 12px" />
@@ -750,10 +724,8 @@ onBeforeUnmount(() => {
<div class="inner"> <div class="inner">
<h3 class="leftright"> <h3 class="leftright">
<span class="title"> <span class="title">
<WhatsAppOutlined <WhatsAppOutlined style="color: #68d8fe;font-size: 20px;" />&nbsp;&nbsp; {{
style="color: #68d8fe; font-size: 20px" t('views.dashboard.overview.userActivity.imsTitle') }}
/>&nbsp;&nbsp;
{{ t('views.dashboard.overview.userActivity.imsTitle') }}
</span> </span>
</h3> </h3>
<div class="chart"> <div class="chart">
@@ -773,7 +745,7 @@ onBeforeUnmount(() => {
} }
.toDeep :deep(.ant-select-selector) { .toDeep :deep(.ant-select-selector) {
background-color: #050f23; background-color: #050F23;
border: none; border: none;
} }