From fae66a2b79cbc32d6f519147e564bc2ff8954bd3 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Thu, 10 Jul 2025 10:33:26 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E5=9C=A8=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E6=8C=82=E8=BD=BD=E6=97=B6=E8=8E=B7=E5=8F=96=E7=BD=91=E5=85=83?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E5=B9=B6=E6=9B=B4=E6=96=B0=E6=8E=88=E6=9D=83?= =?UTF-8?q?=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ne/neLicense/index.vue | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/views/ne/neLicense/index.vue b/src/views/ne/neLicense/index.vue index e2007e16..2fcf3fd1 100644 --- a/src/views/ne/neLicense/index.vue +++ b/src/views/ne/neLicense/index.vue @@ -321,9 +321,28 @@ function fnRecordStateReload() { onMounted(() => { // 初始字典数据 - getDict('ne_license_status') - .then(res => { - dictStatus.value = res; + getDict('ne_license_status').then(res => { + dictStatus.value = res; + }); + // 获取网元网元列表 + useNeInfoStore() + .fnNelist() + .then(async res => { + if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) { + tableState.loading = true; + for (const row of res.data) { + if (row.neType.toUpperCase() === 'OMC') { + continue; + } + await stateNeLicense(row.neType, row.neId); + } + tableState.loading = false; + } else { + message.warning({ + content: t('common.noData'), + duration: 2, + }); + } }) .finally(() => { // 获取列表数据 From 2cdbcd6de84b29ddde1ce506c758cc8ec7502a51 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Thu, 10 Jul 2025 10:54:51 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E8=B0=83=E6=95=B4=E5=AE=89=E8=A3=85?= =?UTF-8?q?UE=5FPOOL=E6=94=B9=E5=88=B0=E5=9F=BA=E7=A1=80=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ne/neQuickSetup/components/Para5GForm.vue | 37 ++++++++----------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/src/views/ne/neQuickSetup/components/Para5GForm.vue b/src/views/ne/neQuickSetup/components/Para5GForm.vue index 1d3784b6..d77ed0f1 100644 --- a/src/views/ne/neQuickSetup/components/Para5GForm.vue +++ b/src/views/ne/neQuickSetup/components/Para5GForm.vue @@ -166,6 +166,21 @@ watch( placeholder="1-65535" > + + + + + @@ -245,28 +260,6 @@ watch( - - - - - - - Date: Thu, 10 Jul 2025 14:25:33 +0800 Subject: [PATCH 3/3] feat(dashboard): add overview2 component with images and functionality - Introduced new images for the dashboard overview (rect.png, title.png). - Created index.vue for the overview2 component, implementing various features including: - User activity tracking and statistics. - Network element state retrieval and display. - Integration of multiple sub-components (Topology, NeResources, UserActivity, IMSActivity, AlarnTypeBar, UPFFlow). - Added dropdowns for selecting UDM and NE resources. - Implemented periodic data fetching for network statistics. - Enhanced user interface with responsive design and improved styling. --- .../components/AlarnTypeBar/index.vue | 185 ++--- .../overview/components/NeResources/index.vue | 458 ++++++------ .../overview/components/Topology/index.vue | 128 +--- .../components/UserActivity/index.vue | 67 ++ src/views/dashboard/overview/css/index.css | 137 +--- .../dashboard/overview/hooks/useTopology.ts | 93 +-- src/views/dashboard/overview/hooks/useWS.ts | 4 +- src/views/dashboard/overview/index.vue | 489 ++++++------ .../components/AlarnTypeBar/index.vue | 305 ++++++++ .../components/IMSActivity/index.vue | 0 .../components/NeResources/index.vue | 352 +++++++++ .../overview2/components/Topology/index.vue | 337 +++++++++ .../overview2/components/UPFFlow/index.vue | 291 ++++++++ .../components/UserActivity/index.vue | 324 ++++++++ src/views/dashboard/overview2/css/index.css | 399 ++++++++++ .../dashboard/overview2/hooks/useTopology.ts | 197 +++++ .../overview2/hooks/useUPFTotalFlow.ts | 110 +++ .../overview2/hooks/useUserActivity.ts | 144 ++++ src/views/dashboard/overview2/hooks/useWS.ts | 224 ++++++ .../{overview => overview2}/images/bj.png | Bin .../dashboard/overview2/images/border.png | Bin 0 -> 2418 bytes .../dashboard/overview2/images/brand.png | Bin 0 -> 14403 bytes src/views/dashboard/overview2/images/line.png | Bin 0 -> 237 bytes .../images/newBrand.png | Bin src/views/dashboard/overview2/images/rect.png | Bin 0 -> 1089 bytes .../{overview => overview2}/images/title.png | Bin src/views/dashboard/overview2/index.vue | 705 ++++++++++++++++++ .../ne/neQuickSetup/components/Para5GForm.vue | 2 +- 28 files changed, 4087 insertions(+), 864 deletions(-) create mode 100644 src/views/dashboard/overview2/components/AlarnTypeBar/index.vue rename src/views/dashboard/{overview => overview2}/components/IMSActivity/index.vue (100%) create mode 100644 src/views/dashboard/overview2/components/NeResources/index.vue create mode 100644 src/views/dashboard/overview2/components/Topology/index.vue create mode 100644 src/views/dashboard/overview2/components/UPFFlow/index.vue create mode 100644 src/views/dashboard/overview2/components/UserActivity/index.vue create mode 100644 src/views/dashboard/overview2/css/index.css create mode 100644 src/views/dashboard/overview2/hooks/useTopology.ts create mode 100644 src/views/dashboard/overview2/hooks/useUPFTotalFlow.ts create mode 100644 src/views/dashboard/overview2/hooks/useUserActivity.ts create mode 100644 src/views/dashboard/overview2/hooks/useWS.ts rename src/views/dashboard/{overview => overview2}/images/bj.png (100%) create mode 100644 src/views/dashboard/overview2/images/border.png create mode 100644 src/views/dashboard/overview2/images/brand.png create mode 100644 src/views/dashboard/overview2/images/line.png rename src/views/dashboard/{overview => overview2}/images/newBrand.png (100%) create mode 100644 src/views/dashboard/overview2/images/rect.png rename src/views/dashboard/{overview => overview2}/images/title.png (100%) create mode 100644 src/views/dashboard/overview2/index.vue diff --git a/src/views/dashboard/overview/components/AlarnTypeBar/index.vue b/src/views/dashboard/overview/components/AlarnTypeBar/index.vue index 3469a235..583bfd03 100644 --- a/src/views/dashboard/overview/components/AlarnTypeBar/index.vue +++ b/src/views/dashboard/overview/components/AlarnTypeBar/index.vue @@ -129,123 +129,79 @@ function initPicture() { const optionData: EChartsOption = { title: [ { - text: 'Top3', - left: 'center', - top: '36%', + show: false, + }, + { + text: t('views.dashboard.overview.alarmTypeBar.topTitle'), textStyle: { color: '#fff', - fontSize: 16, - fontWeight: 'bold', + fontSize: '14', + fontWeight: 400, }, - }, - ], - grid: [ - { // 主图 - top: '5%', - left: '20%', - right: '10%', - height: '35%' - }, - { // Top3 top: '50%', - left: '20%', - right: '10%', - height: '30%' - } + left: '0%', + }, ], tooltip: { - - axisPointer: { type: 'shadow' }, + trigger: 'item', formatter: '{b} : {c}', }, legend: { - show: false + orient: 'vertical', + right: '2%', + top: '12%', + data: alarmTypeType.value.map((item: any) => item.name), //label数组 + textStyle: { + color: '#A7D6F4', // 设置图例文字颜色 + }, }, - xAxis: [ + grid: [ { - type: 'value', - gridIndex: 0, - show: false, + top: '60%', + left: '15%', + right: '25%', + 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: [ - // 四类型告警横向柱状图 + //饼图: { - type: 'bar', - xAxisIndex: 0, - 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]; - } - }, + type: 'pie', + radius: '35%', + color: ['#f5222d', '#fa8c16', '#fadb14', '#1677ff', '#13c2c2'], label: { show: true, - position: 'right', - color: '#fff', //淡蓝色 - fontWeight: 'bold', - fontSize: 16, + position: 'inner', formatter: (params: any) => { if (!params.value) return ''; return `${params.value}`; }, }, - data: alarmTypeType.value.map((item: any) => item.value), - zlevel: 2 + labelLine: { + show: false, + }, + center: ['35%', '25%'], + data: alarmTypeType.value, + zlevel: 2, // 设置zlevel为1,使得柱状图在下层显示 + itemStyle: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.5)', + }, }, - // Top3横向柱状图 + //柱状 { - name: 'Top3', type: 'bar', - xAxisIndex: 1, - yAxisIndex: 1, - barWidth: 18, + barWidth: 12, // 柱子宽度 + barCategoryGap: '30%', // 控制同一系列的柱间距离 + label: { + show: true, + position: 'right', // 位置 + color: '#A7D6F4', //淡蓝色 + fontSize: 14, + distance: 14, // label与柱子距离 + formatter: '{c}', + }, itemStyle: { borderRadius: [0, 20, 20, 0], // 圆角(左上、右上、右下、左下) color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [ @@ -254,19 +210,42 @@ function initPicture() { { offset: 1, color: '#2f54eb' }, ]), // 渐变 }, - label: { - show: true, - position: 'right', - color: '#fff', //淡蓝色 - fontWeight: 'bold', - fontSize: 16, - formatter: '{c}' + data: alarmTypeTypeTop.value, + }, + ], + // 柱状图设置 + xAxis: [ + { + splitLine: { + show: false, }, - data: alarmTypeTypeTop.value.map((item: any) => item.value), - zlevel: 1 - } - ] + 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.name), + axisLabel: { + color: '#A7D6F4', + fontSize: 14, + }, + }, + ], }; + fnDesign(alarmTypeBar.value, optionData); }); } diff --git a/src/views/dashboard/overview/components/NeResources/index.vue b/src/views/dashboard/overview/components/NeResources/index.vue index 669214c8..a50a6a20 100644 --- a/src/views/dashboard/overview/components/NeResources/index.vue +++ b/src/views/dashboard/overview/components/NeResources/index.vue @@ -1,19 +1,24 @@ diff --git a/src/views/dashboard/overview/components/Topology/index.vue b/src/views/dashboard/overview/components/Topology/index.vue index 1b9e04c8..12e9d763 100644 --- a/src/views/dashboard/overview/components/Topology/index.vue +++ b/src/views/dashboard/overview/components/Topology/index.vue @@ -22,25 +22,18 @@ const graphG6Dom = ref(undefined); /**图节点展示 */ const graphNodeTooltip = new Tooltip({ - offsetX: 10, + offsetX: 20, offsetY: 20, getContent(evt) { if (!evt) return t('views.monitor.topologyBuild.graphNotInfo'); - const { id, label, neState, neInfoList, neStateMap }: any = evt.item?.getModel(); - //console.log(neInfoList,neState,neInfoList); + const { id, label, neState }: any = evt.item?.getModel(); if (notNeNodes.includes(id)) { return `
${label || id}
`; } if (!neState) { return `
${label || id}
`; } - - // 获取同类型网元列表 - const sameTypeNes = neInfoList || []; - - // 如果没有网元或只有一个网元,显示原来的信息 - if (sameTypeNes.length <= 1) { - return ` + return `
-
${t('views.monitor.topology.state')}: - ${ - neState.online - ? t('views.monitor.topology.normalcy') - : t('views.monitor.topology.exceptions') - } -
-
${t('views.monitor.topology.refreshTime')}: - ${neState.refreshTime ?? '--'} -
-
========================
`; - - // 为每个同类型网元添加信息 - sameTypeNes.forEach((ne: any, index: number) => { - // 获取该网元的状态信息 - const neStateInfo = neStateMap?.[ne.neId] || - (ne.neId === neState.neId ? neState : {}); - - content += ` -
${t('views.monitor.topology.name')}:${ne.neName || id + '_' + ne.neId}
-
ID:${ne.neId || '--'}
-
IP:${neStateInfo.neIP || ne.neIP || '--'}
-
${t('views.monitor.topology.version')}: - ${neStateInfo.version || ne.version || '--'} -
-
${t('views.monitor.topology.serialNum')}: - ${neStateInfo.sn || ne.sn || '--'} -
-
${t('views.monitor.topology.expiryDate')}: - ${neStateInfo.expire || ne.expire || '--'} -
-
${t('views.monitor.topology.state')}: - ${ - neStateInfo.online !== undefined - ? (neStateInfo.online - ? t('views.monitor.topology.normalcy') - : t('views.monitor.topology.exceptions')) - : 'undefined' - } -
- ${index < sameTypeNes.length - 1 ? '
------------------------
' : ''} - `; - }); - - content += '
'; - return content; }, itemTypes: ['node'], }); - +/**图绑定事件 */ +function fnGraphEvent(graph: Graph) { + // 节点点击 + graph.on('node:click', evt => { + // 获得鼠标当前目标节点 + const node = evt.item?.getModel(); + if (node && node.id && !notNeNodes.includes(node.id)) { + graphNodeClickID.value = node.id; + } + }); +} /**图数据渲染 */ function handleRanderGraph( @@ -176,6 +122,7 @@ function handleRanderGraph( graph.data(data); graph.render(); + fnGraphEvent(graph); graphG6.value = graph; @@ -188,7 +135,7 @@ function handleRanderGraph( } graphG6.value.changeSize( entry.contentRect.width, - entry.contentRect.height - 20 + entry.contentRect.height - 30 ); graphG6.value.fitCenter(); }); @@ -203,14 +150,14 @@ function handleRanderGraph( * 获取图组数据渲染到画布 * @param reload 是否重载数据 */ - function fnGraphDataLoad(reload: boolean = false) { +function fnGraphDataLoad(reload: boolean = false) { Promise.all([ getGraphData(graphState.group), listAllNeInfo({ bandStatus: false, }), ]) - .then(resArr => { + .then(resArr => { const graphRes = resArr[0]; const neRes = resArr[1]; if ( @@ -236,45 +183,27 @@ function handleRanderGraph( if (!res) return; const { combos, edges, nodes } = res.graphData; - // 按网元类型分组 - const neTypeMap = new Map(); - res.neList.forEach(ne => { - if (!ne.neType) return; - if (!neTypeMap.has(ne.neType)) { - neTypeMap.set(ne.neType, []); - } - neTypeMap.get(ne.neType).push(ne); - }); - // 节点过滤 const nf: Record[] = nodes.filter( (node: Record) => { Reflect.set(node, 'neState', { online: false }); - Reflect.set(node, 'neStateMap', {}); // 初始化状态映射 - // 图片路径处理 if (node.img) node.img = parseBasePath(node.img); - if (node.icon.show && node.icon?.img){ + if (node.icon.show && node.icon?.img) { node.icon.img = parseBasePath(node.icon.img); } - // 遍历是否有网元数据 const nodeID: string = node.id; - - // 处理非网元节点 + const hasNe = res.neList.some(ne => { + Reflect.set(node, 'neInfo', ne.neType === nodeID ? ne : {}); + return ne.neType === nodeID; + }); + if (hasNe) { + return true; + } if (notNeNodes.includes(nodeID)) { return true; } - //(neTypeMap.get(nodeID),nodeID,node.neState) - // 处理网元节点 - if (neTypeMap.has(nodeID)) { - // all NeInfo - Reflect.set(node, 'neInfoList', neTypeMap.get(nodeID)); - - Reflect.set(node, 'neInfo', neTypeMap.get(nodeID)[0] || {}); - return true; - } - return false; } ); @@ -286,6 +215,7 @@ function handleRanderGraph( const edgeTarget: string = edge.target; const hasNeS = nf.some(n => n.id === edgeSource); const hasNeT = nf.some(n => n.id === edgeTarget); + // console.log(hasNeS, edgeSource, hasNeT, edgeTarget); if (hasNeS && hasNeT) { return true; } diff --git a/src/views/dashboard/overview/components/UserActivity/index.vue b/src/views/dashboard/overview/components/UserActivity/index.vue index 614b19a2..7bd187c1 100644 --- a/src/views/dashboard/overview/components/UserActivity/index.vue +++ b/src/views/dashboard/overview/components/UserActivity/index.vue @@ -65,6 +65,73 @@ onMounted(() => {