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

View File

@@ -18,23 +18,25 @@ import {
nodeImageAnimateState,
nodeRectAnimateState,
} from '@/views/monitor/topologyBuild/hooks/registerNode';
import { graphState, graphNodeClickID } from '../../hooks/useTopology';
import useI18n from '@/hooks/useI18n';
const { t } = useI18n();
// const { graphState } = useTopology();
/**图DOM节点实例对象 */
const graphG6Dom = ref<HTMLElement | undefined>(undefined);
/**图状态 */
const graphState = reactive<Record<string, any>>({
/**当前图组名 */
group: '5GC System Architecture2',
/**图数据 */
data: {
combos: [],
edges: [],
nodes: [],
},
});
// const graphState = reactive<Record<string, any>>({
// /**当前图组名 */
// group: '5GC System Architecture2',
// /**图数据 */
// data: {
// combos: [],
// edges: [],
// nodes: [],
// },
// });
/**非网元元素 */
const notNeNodes = ['5GC', 'DN', 'UE', 'Base'];
@@ -42,52 +44,6 @@ const notNeNodes = ['5GC', 'DN', 'UE', 'Base'];
/**图实例对象 */
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({
offsetX: 10,
@@ -140,6 +96,18 @@ const graphNodeTooltip = new Tooltip({
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() {
// 边
@@ -170,6 +138,8 @@ function handleRanderGraph(
height: clientHeight - 36,
fitCenter: true,
fitView: true,
fitViewPadding: [40],
autoPaint: true,
modes: {
default: [
'drag-combo',
@@ -184,7 +154,7 @@ function handleRanderGraph(
fill: 'transparent',
},
},
plugins: [graphNodeMenu, graphNodeTooltip],
plugins: [graphNodeTooltip],
animate: true, // 是否使用动画过度,默认为 false
animateCfg: {
duration: 500, // Number一次动画的时长
@@ -194,8 +164,24 @@ function handleRanderGraph(
graph.data(data);
graph.render();
fnGraphEvent(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;
}
@@ -233,7 +219,6 @@ function fnGraphDataLoad(reload: boolean = false) {
}
})
.then(res => {
console.log('fnGraphDataLoad', res);
if (!res) return;
const { combos, edges, nodes } = res.graphData;
@@ -255,7 +240,6 @@ function fnGraphDataLoad(reload: boolean = false) {
return false;
}
);
console.log(nf);
// 边过滤
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(() => {

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

View File

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

View File

@@ -326,10 +326,10 @@ function fnModalCancel() {
<a-input-number
v-model:value="comboState.form.style.fillOpacity"
style="width: 100%"
:min="0.1"
:min="0"
:max="1"
:step="0.1"
placeholder="<=100"
placeholder="<=1"
></a-input-number>
</a-form-item>
</a-col>
@@ -849,7 +849,10 @@ function fnModalCancel() {
</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-form-item
:label="t('views.monitor.topologyBuild.nodeFormClipTypeCircle')"
@@ -867,7 +870,10 @@ function fnModalCancel() {
</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-form-item
:label="

View File

@@ -182,7 +182,6 @@ export function nodeRectAnimateState() {
if (value) {
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 { fill, lineWidth, stroke, radius } = model.style;
const fillColor = fill || stroke;