This commit is contained in:
2024-01-25 17:41:41 +08:00
7 changed files with 253 additions and 217 deletions

View File

@@ -1,12 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, onBeforeUnmount } from 'vue'; import { onMounted, ref, onBeforeUnmount, nextTick } from 'vue';
import * as echarts from 'echarts/core'; import * as echarts from 'echarts/core';
import { TooltipComponent } from 'echarts/components'; import { TooltipComponent } from 'echarts/components';
import { GaugeChart } from 'echarts/charts'; import { GaugeChart } from 'echarts/charts';
import { CanvasRenderer } from 'echarts/renderers'; import { CanvasRenderer } from 'echarts/renderers';
import { graphNodeClickID, graphNodeState } from '../../hooks/useTopology';
import useI18n from '@/hooks/useI18n'; import useI18n from '@/hooks/useI18n';
import { GaugeSeriesOption } from 'echarts/types/dist/echarts'; import { GaugeSeriesOption } from 'echarts/types/dist/echarts';
import { watch } from 'vue';
const { t } = useI18n(); const { t } = useI18n();
echarts.use([TooltipComponent, GaugeChart, CanvasRenderer]); echarts.use([TooltipComponent, GaugeChart, CanvasRenderer]);
@@ -27,8 +28,8 @@ const optionData: EChartsOption = {
{ {
name: '系统内存', name: '系统内存',
type: 'gauge', type: 'gauge',
center: ['30%', '25%'], center: ['15%', '45%'],
radius: '40%', radius: '65%',
startAngle: 90, startAngle: 90,
endAngle: -270, endAngle: -270,
min: 1, min: 1,
@@ -38,7 +39,7 @@ const optionData: EChartsOption = {
}, },
progress: { progress: {
show: true, show: true,
width: 10, width: 20,
overlap: true, overlap: true,
roundCap: true, roundCap: true,
clip: false, clip: false,
@@ -48,7 +49,7 @@ const optionData: EChartsOption = {
}, },
axisLine: { axisLine: {
lineStyle: { lineStyle: {
width: 10, width: 20,
}, },
}, },
axisTick: { axisTick: {
@@ -65,8 +66,8 @@ const optionData: EChartsOption = {
}, },
title: { title: {
show: true, show: true,
offsetCenter: [0, -5], offsetCenter: [0, '-10%'],
fontSize: 10, fontSize: 15,
color: '#fafafa', color: '#fafafa',
}, },
detail: { detail: {
@@ -74,8 +75,8 @@ const optionData: EChartsOption = {
width: '60%', width: '60%',
lineHeight: 40, lineHeight: 40,
borderRadius: 8, borderRadius: 8,
offsetCenter: [0, 10], offsetCenter: [0, '30%'],
fontSize: 10, fontSize: 15,
fontWeight: 'bolder', fontWeight: 'bolder',
formatter: '{value}%', formatter: '{value}%',
color: 'inherit', color: 'inherit',
@@ -83,15 +84,15 @@ const optionData: EChartsOption = {
data: [ data: [
{ {
name: '系统内存', name: '系统内存',
value: 20, value: 1,
}, },
], ],
}, },
{ {
name: '系统CPU', name: '系统CPU',
type: 'gauge', type: 'gauge',
center: ['70%', '25%'], center: ['50%', '45%'],
radius: '40%', radius: '65%',
startAngle: 90, startAngle: 90,
endAngle: -270, endAngle: -270,
min: 1, min: 1,
@@ -101,7 +102,7 @@ const optionData: EChartsOption = {
}, },
progress: { progress: {
show: true, show: true,
width: 10, width: 20,
overlap: true, overlap: true,
roundCap: true, roundCap: true,
clip: false, clip: false,
@@ -111,7 +112,7 @@ const optionData: EChartsOption = {
}, },
axisLine: { axisLine: {
lineStyle: { lineStyle: {
width: 10, width: 20,
}, },
}, },
axisTick: { axisTick: {
@@ -128,8 +129,8 @@ const optionData: EChartsOption = {
}, },
title: { title: {
show: true, show: true,
offsetCenter: [0, -5], offsetCenter: [0, '-10%'],
fontSize: 10, fontSize: 15,
color: '#fafafa', color: '#fafafa',
}, },
detail: { detail: {
@@ -137,8 +138,8 @@ const optionData: EChartsOption = {
width: '60%', width: '60%',
lineHeight: 40, lineHeight: 40,
borderRadius: 8, borderRadius: 8,
offsetCenter: [0, 10], offsetCenter: [0, '30%'],
fontSize: 10, fontSize: 15,
fontWeight: 'bolder', fontWeight: 'bolder',
formatter: '{value}%', formatter: '{value}%',
color: 'inherit', color: 'inherit',
@@ -153,8 +154,8 @@ const optionData: EChartsOption = {
{ {
name: '网元内存', name: '网元内存',
type: 'gauge', type: 'gauge',
center: ['30%', '75%'], center: ['85%', '45%'],
radius: '40%', radius: '65%',
startAngle: 90, startAngle: 90,
endAngle: -270, endAngle: -270,
min: 1, min: 1,
@@ -164,7 +165,7 @@ const optionData: EChartsOption = {
}, },
progress: { progress: {
show: true, show: true,
width: 10, width: 20,
overlap: true, overlap: true,
roundCap: true, roundCap: true,
clip: false, clip: false,
@@ -174,7 +175,7 @@ const optionData: EChartsOption = {
}, },
axisLine: { axisLine: {
lineStyle: { lineStyle: {
width: 10, width: 20,
}, },
}, },
axisTick: { axisTick: {
@@ -191,8 +192,8 @@ const optionData: EChartsOption = {
}, },
title: { title: {
show: true, show: true,
offsetCenter: [0, -5], offsetCenter: [0, '-10%'],
fontSize: 10, fontSize: 15,
color: '#fafafa', color: '#fafafa',
}, },
detail: { detail: {
@@ -200,71 +201,8 @@ const optionData: EChartsOption = {
width: '60%', width: '60%',
lineHeight: 40, lineHeight: 40,
borderRadius: 8, borderRadius: 8,
offsetCenter: [0, 10], offsetCenter: [0, '30%'],
fontSize: 10, fontSize: 15,
fontWeight: 'bolder',
formatter: '{value}%',
color: 'inherit',
},
data: [
{
name: '系统内存',
value: 20,
},
],
},
{
name: '网元CPU',
type: 'gauge',
center: ['70%', '75%'],
radius: '40%',
startAngle: 90,
endAngle: -270,
min: 1,
max: 100,
itemStyle: {
color: '#1890ff',
},
progress: {
show: true,
width: 10,
overlap: true,
roundCap: true,
clip: false,
},
pointer: {
show: false,
},
axisLine: {
lineStyle: {
width: 10,
},
},
axisTick: {
show: false,
},
splitLine: {
show: false,
},
axisLabel: {
show: false,
},
anchor: {
show: false,
},
title: {
show: true,
offsetCenter: [0, -5],
fontSize: 10,
color: '#fafafa',
},
detail: {
valueAnimation: true,
width: '60%',
lineHeight: 40,
borderRadius: 8,
offsetCenter: [0, 10],
fontSize: 10,
fontWeight: 'bolder', fontWeight: 'bolder',
formatter: '{value}%', formatter: '{value}%',
color: 'inherit', color: 'inherit',
@@ -285,71 +223,151 @@ function handleRanderChart(
option: EChartsOption option: EChartsOption
) { ) {
if (!container) return; if (!container) return;
const { clientHeight, clientWidth } = container; neResourcesChart.value = echarts.init(container, 'light');
neResourcesChart.value = echarts.init(container, 'light', {
width: clientWidth,
height: clientHeight - 36,
});
option && neResourcesChart.value.setOption(option); option && neResourcesChart.value.setOption(option);
// 创建 ResizeObserver 实例
var observer = new ResizeObserver(function (entries) {
neResourcesChart.value.resize();
});
// 监听元素大小变化
observer.observe(container);
} }
onMounted(() => { function fnChangeData(data: any[], itemID: string) {
setInterval(function () { let info = data.find((item: any) => item.id === itemID);
const random = +(Math.random() * 90).toFixed(2); if (!info.neState.online) {
const random2 = +(Math.random() * 90).toFixed(2); info = data.find((item: any) => item.id === 'OMC');
const random3 = +(Math.random() * 90).toFixed(2); graphNodeClickID.value = 'OMC';
const random4 = +(Math.random() * 90).toFixed(2); }
let sysMemUsage = 0;
let sysCpuUsage = 0;
let nfCpuUsage = 0;
if (info.neState.cpu) {
nfCpuUsage = +(info.neState.cpu.nfCpuUsage / 100).toFixed(2);
sysCpuUsage = +(info.neState.cpu.sysCpuUsage / 100).toFixed(2);
}
if (info.neState.mem) {
let men = info.neState.mem.sysMemUsage;
if (men > 1000) {
men = +(men / 100).toFixed(2);
}
sysMemUsage = men;
}
function colorStyle(v: number): string {
let color = '#1890ff'; let color = '#1890ff';
if (random < 30) { if (v < 30) {
color = '#73d13d'; color = '#73d13d';
} }
if (random > 60) { if (v > 60) {
color = '#ff4d4f'; color = '#ff4d4f';
} }
neResourcesChart.value.setOption({ return color;
series: [ }
{
data: [
{
name: '系统内存',
value: random,
itemStyle: {
color: color,
shadowColor: color,
},
},
],
},
{
data: [
{
name: '系统CPU',
value: random2,
},
],
},
{
data: [
{
name: '网元内存',
value: random3,
},
],
},
{
data: [
{
name: '网元CPU',
value: random4,
},
],
},
],
});
}, 2000);
handleRanderChart(neResourcesDom.value, optionData); neResourcesChart.value.setOption({
series: [
{
data: [
{
name: '系统内存',
value: sysMemUsage,
itemStyle: {
color: colorStyle(sysMemUsage),
shadowColor: colorStyle(sysMemUsage),
},
},
],
},
{
data: [
{
name: '系统CPU',
value: sysCpuUsage,
itemStyle: {
color: colorStyle(sysCpuUsage),
shadowColor: colorStyle(sysCpuUsage),
},
},
],
},
{
data: [
{
name: '网元CPU',
value: nfCpuUsage,
itemStyle: {
color: colorStyle(nfCpuUsage),
shadowColor: colorStyle(nfCpuUsage),
},
},
],
},
],
});
}
watch(
graphNodeState,
v => {
fnChangeData(v, graphNodeClickID.value);
},
{
deep: true,
}
);
watch(graphNodeClickID, v => {
fnChangeData(graphNodeState.value, v);
});
onMounted(() => {
// setInterval(function () {
// neResourcesChart.value.setOption({
// series: [
// {
// data: [
// {
// name: '系统内存',
// value: sysMemUsage,
// itemStyle: {
// color: colorStyle(sysMemUsage),
// shadowColor: colorStyle(sysMemUsage),
// },
// },
// ],
// },
// {
// data: [
// {
// name: '系统CPU',
// value: sysCpuUsage,
// itemStyle: {
// color: colorStyle(sysCpuUsage),
// shadowColor: colorStyle(sysCpuUsage),
// },
// },
// ],
// },
// {
// data: [
// {
// name: '网元CPU',
// value: nfCpuUsage,
// itemStyle: {
// color: colorStyle(nfCpuUsage),
// shadowColor: colorStyle(nfCpuUsage),
// },
// },
// ],
// },
// ],
// });
// }, 2000);
nextTick(() => {
handleRanderChart(neResourcesDom.value, optionData);
});
}); });
onBeforeUnmount(() => {}); onBeforeUnmount(() => {});

View File

@@ -18,23 +18,25 @@ import {
nodeImageAnimateState, nodeImageAnimateState,
nodeRectAnimateState, nodeRectAnimateState,
} from '@/views/monitor/topologyBuild/hooks/registerNode'; } from '@/views/monitor/topologyBuild/hooks/registerNode';
import { graphState, graphNodeClickID } from '../../hooks/useTopology';
import useI18n from '@/hooks/useI18n'; import useI18n from '@/hooks/useI18n';
const { t } = useI18n(); const { t } = useI18n();
// const { graphState } = useTopology();
/**图DOM节点实例对象 */ /**图DOM节点实例对象 */
const graphG6Dom = ref<HTMLElement | undefined>(undefined); const graphG6Dom = ref<HTMLElement | undefined>(undefined);
/**图状态 */ /**图状态 */
const graphState = reactive<Record<string, any>>({ // const graphState = reactive<Record<string, any>>({
/**当前图组名 */ // /**当前图组名 */
group: '5GC System Architecture2', // group: '5GC System Architecture2',
/**图数据 */ // /**图数据 */
data: { // data: {
combos: [], // combos: [],
edges: [], // edges: [],
nodes: [], // nodes: [],
}, // },
}); // });
/**非网元元素 */ /**非网元元素 */
const notNeNodes = ['5GC', 'DN', 'UE', 'Base']; const notNeNodes = ['5GC', 'DN', 'UE', 'Base'];
@@ -42,52 +44,6 @@ const notNeNodes = ['5GC', 'DN', 'UE', 'Base'];
/**图实例对象 */ /**图实例对象 */
const graphG6 = ref<any>(null); const graphG6 = ref<any>(null);
/**图节点右击菜单 */
const graphNodeMenu = new Menu({
offsetX: 6,
offseY: 10,
itemTypes: ['node'],
getContent(evt) {
if (!evt) return t('views.monitor.topologyBuild.graphNotInfo');
const { id, label, neState }: any = evt.item?.getModel();
if (notNeNodes.includes(id)) {
return `<div><span>${label || id}</span></div>`;
}
if (!neState) {
return `<div><span>${label || id}</span></div>`;
}
return `
<div
style="
display: flex;
flex-direction: column;
width: 140px;
"
>
<h3 style="margin-bottom: 8px">
${t('views.monitor.topology.name')}:
${neState.neName ?? '--'}
</h3>
<div id="restart" style="cursor: pointer; margin-bottom: 4px">
> ${t('views.configManage.neManage.restart')}
</div>
<div id="stop" style="cursor: pointer; margin-bottom: 4px;">
> ${t('views.configManage.neManage.stop')}
</div>
<div id="log" style="cursor: pointer; margin-bottom: 4px;">
> ${t('views.monitor.topology.viewLogFile')}
</div>
</div>
`;
},
handleMenuClick(target, item) {
const { neInfo }: any = item?.getModel();
const { neName, neType, neId } = neInfo;
const targetId = target.id;
console.log(targetId);
},
});
/**图节点展示 */ /**图节点展示 */
const graphNodeTooltip = new Tooltip({ const graphNodeTooltip = new Tooltip({
offsetX: 10, offsetX: 10,
@@ -140,6 +96,18 @@ const graphNodeTooltip = new Tooltip({
itemTypes: ['node'], itemTypes: ['node'],
}); });
/**图绑定事件 */
function fnGraphEvent(graph: Graph) {
// 节点点击
graph.on('node:click', evt => {
// 获得鼠标当前目标节点
const node = evt.item?.getModel();
if (node && node.id) {
graphNodeClickID.value = node.id;
}
});
}
/**注册自定义边或节点 */ /**注册自定义边或节点 */
function registerEdgeNode() { function registerEdgeNode() {
// 边 // 边
@@ -170,6 +138,8 @@ function handleRanderGraph(
height: clientHeight - 36, height: clientHeight - 36,
fitCenter: true, fitCenter: true,
fitView: true, fitView: true,
fitViewPadding: [40],
autoPaint: true,
modes: { modes: {
default: [ default: [
'drag-combo', 'drag-combo',
@@ -184,7 +154,7 @@ function handleRanderGraph(
fill: 'transparent', fill: 'transparent',
}, },
}, },
plugins: [graphNodeMenu, graphNodeTooltip], plugins: [graphNodeTooltip],
animate: true, // 是否使用动画过度,默认为 false animate: true, // 是否使用动画过度,默认为 false
animateCfg: { animateCfg: {
duration: 500, // Number一次动画的时长 duration: 500, // Number一次动画的时长
@@ -194,8 +164,24 @@ function handleRanderGraph(
graph.data(data); graph.data(data);
graph.render(); graph.render();
fnGraphEvent(graph);
graphG6.value = graph; graphG6.value = graph;
// 创建 ResizeObserver 实例
var observer = new ResizeObserver(function (entries) {
// 当元素大小发生变化时触发回调函数
entries.forEach(function (entry) {
graphG6.value.changeSize(
entry.contentRect.width,
entry.contentRect.height - 30
);
graphG6.value.fitCenter();
});
});
// 监听元素大小变化
observer.observe(container);
return graph; return graph;
} }
@@ -233,7 +219,6 @@ function fnGraphDataLoad(reload: boolean = false) {
} }
}) })
.then(res => { .then(res => {
console.log('fnGraphDataLoad', res);
if (!res) return; if (!res) return;
const { combos, edges, nodes } = res.graphData; const { combos, edges, nodes } = res.graphData;
@@ -255,7 +240,6 @@ function fnGraphDataLoad(reload: boolean = false) {
return false; return false;
} }
); );
console.log(nf);
// 边过滤 // 边过滤
const ef: Record<string, any>[] = edges.filter( const ef: Record<string, any>[] = edges.filter(
@@ -396,7 +380,7 @@ async function fnGetState() {
} }
} }
stateTimeout.value = setTimeout(() => fnGetState(), 30_000); stateTimeout.value = setTimeout(() => fnGetState(), 5_000);
} }
onMounted(() => { onMounted(() => {

View File

@@ -0,0 +1,26 @@
import { computed, reactive, ref } from 'vue';
/**图状态 */
export const graphState = reactive<Record<string, any>>({
/**当前图组名 */
group: '5GC System Architecture3',
/**图数据 */
data: {
combos: [],
edges: [],
nodes: [],
},
});
/**图点击选择 */
export const graphNodeClickID = ref<string>('UPF');
/**图节点网元信息状态 */
export const graphNodeState = computed(() =>
graphState.data.nodes.map((item: any) => ({
id: item.id,
label: item.label,
neInfo: item.neInfo,
neState: item.neState,
}))
);

View File

@@ -7,7 +7,7 @@ import CDR from './components/CDR/index.vue';
import AlarnDayLine from './components/AlarnDayLine/index.vue'; import AlarnDayLine from './components/AlarnDayLine/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 { graphNodeClickID } from './hooks/useTopology';
import { useFullscreen } from '@vueuse/core'; import { useFullscreen } from '@vueuse/core';
import useAppStore from '@/store/modules/app'; import useAppStore from '@/store/modules/app';
const appStore = useAppStore(); const appStore = useAppStore();
@@ -141,7 +141,8 @@ onMounted(() => {});
<div class="resources panel"> <div class="resources panel">
<div class="inner"> <div class="inner">
<h3> <h3>
<DashboardOutlined style="color: #68d8fe" />&nbsp;&nbsp; 资源情况 <DashboardOutlined style="color: #68d8fe" />&nbsp;&nbsp;
资源情况{{ graphNodeClickID }}
</h3> </h3>
<div class="chart"> <div class="chart">
<NeResources /> <NeResources />

View File

@@ -29,7 +29,7 @@ const graphG6Dom = ref<HTMLElement | undefined>(undefined);
/**图状态 */ /**图状态 */
const graphState = reactive<Record<string, any>>({ const graphState = reactive<Record<string, any>>({
/**当前图组名 */ /**当前图组名 */
group: '5GC System Architecture2', group: '5GC System Architecture3',
/**图数据 */ /**图数据 */
data: { data: {
combos: [], combos: [],
@@ -182,6 +182,8 @@ function handleRanderGraph(
height: clientHeight, height: clientHeight,
fitCenter: true, fitCenter: true,
fitView: true, fitView: true,
fitViewPadding: [40],
autoPaint: true,
modes: { modes: {
default: [ default: [
'drag-combo', 'drag-combo',

View File

@@ -238,7 +238,7 @@ function fnModalCancel() {
</a-form-item> </a-form-item>
<!-- 圆形尺寸 --> <!-- 圆形尺寸 -->
<a-row :gutter="16" v-if="comboState.form.type.startsWith('circle') "> <a-row :gutter="16" v-if="comboState.form.type.startsWith('circle')">
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
:label="t('views.monitor.topologyBuild.comboFormSize')" :label="t('views.monitor.topologyBuild.comboFormSize')"
@@ -267,7 +267,7 @@ function fnModalCancel() {
</a-row> </a-row>
<!-- 矩形尺寸 --> <!-- 矩形尺寸 -->
<a-row :gutter="16" v-if="comboState.form.type.startsWith('rect') "> <a-row :gutter="16" v-if="comboState.form.type.startsWith('rect')">
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
:label="t('views.monitor.topologyBuild.comboFormSize')" :label="t('views.monitor.topologyBuild.comboFormSize')"
@@ -326,10 +326,10 @@ function fnModalCancel() {
<a-input-number <a-input-number
v-model:value="comboState.form.style.fillOpacity" v-model:value="comboState.form.style.fillOpacity"
style="width: 100%" style="width: 100%"
:min="0.1" :min="0"
:max="1" :max="1"
:step="0.1" :step="0.1"
placeholder="<=100" placeholder="<=1"
></a-input-number> ></a-input-number>
</a-form-item> </a-form-item>
</a-col> </a-col>
@@ -849,7 +849,10 @@ function fnModalCancel() {
</a-row> </a-row>
<!-- 剪裁圆形 --> <!-- 剪裁圆形 -->
<a-row :gutter="16" v-if="nodeState.form.clipCfg.type.startsWith('circle') "> <a-row
:gutter="16"
v-if="nodeState.form.clipCfg.type.startsWith('circle')"
>
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
:label="t('views.monitor.topologyBuild.nodeFormClipTypeCircle')" :label="t('views.monitor.topologyBuild.nodeFormClipTypeCircle')"
@@ -867,7 +870,10 @@ function fnModalCancel() {
</a-row> </a-row>
<!-- 剪裁矩形 --> <!-- 剪裁矩形 -->
<a-row :gutter="16" v-if="nodeState.form.clipCfg.type.startsWith('rect')"> <a-row
:gutter="16"
v-if="nodeState.form.clipCfg.type.startsWith('rect')"
>
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
:label=" :label="
@@ -976,7 +982,7 @@ function fnModalCancel() {
<template <template
v-if=" v-if="
nodeState.form.icon && nodeState.form.icon &&
!nodeState.form.type.startsWith('rect') && !nodeState.form.type.startsWith('rect') &&
!nodeState.form.type.startsWith('image') !nodeState.form.type.startsWith('image')
" "
> >

View File

@@ -182,7 +182,6 @@ export function nodeRectAnimateState() {
if (value) { if (value) {
if (Array.isArray(backArr) && backArr.length === 3) return; if (Array.isArray(backArr) && backArr.length === 3) return;
console.log(value, '==', keyShape.cfg.animating);
const size = Array.isArray(model?.size) ? model?.size : [40, 40]; const size = Array.isArray(model?.size) ? model?.size : [40, 40];
const { fill, lineWidth, stroke, radius } = model.style; const { fill, lineWidth, stroke, radius } = model.style;
const fillColor = fill || stroke; const fillColor = fill || stroke;