Files
fe.ems.vue3/src/views/dashboard/overview2/components/NeResources/index.vue
2025-08-08 19:53:36 +08:00

370 lines
8.8 KiB
Vue

<script setup lang="ts">
import { onMounted, ref, nextTick, watch } from 'vue';
import * as echarts from 'echarts/core';
import { GridComponent, TooltipComponent } from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
import { graphNodeClickID, graphNodeState } from '../../hooks/useTopology';
import useI18n from '@/hooks/useI18n';
import { markRaw } from 'vue';
// 引入液体填充图表
import 'echarts-liquidfill';
const { t } = useI18n();
echarts.use([GridComponent, TooltipComponent, CanvasRenderer]);
/**图DOM节点实例对象 */
const neResourcesDom = ref<HTMLElement | undefined>(undefined);
/**图实例对象 */
const neResourcesChart = ref<any>(null);
// 当前选中的网元ID
const currentNeId = ref('');
// 资源数据
const resourceData = ref({
neCpu: 1,
sysCpu: 1,
sysMem: 1,
sysDisk: 1,
});
// 获取颜色
function getColorByValue(value: number) {
if (value >= 70) {
return ['#f5222d', '#ff7875']; // 红色
} else if (value >= 30) {
return ['#2f54eb', '#597ef7']; // 蓝色
} else {
return ['#52c41a', '#95de64']; // 绿色
}
}
/**图数据 */
const optionData: any = {
tooltip: {
trigger: 'item',
// formatter: '{a}: {c}%',
formatter: (params: any) => {
return `${params.seriesName}: ${(params.value * 100).toFixed(2)}%`;
},
},
grid: {
top: '10%',
bottom: '5%',
left: '5%',
right: '5%',
containLabel: true,
},
series: [
{
type: 'liquidFill',
radius: '50%',
center: ['15%', '35%'],
data: [resourceData.value.neCpu / 100],
name: t('views.dashboard.overview.resources.neCpu'),
color: getColorByValue(resourceData.value.neCpu),
backgroundStyle: {
color: 'rgba(10, 60, 160, 0.1)',
},
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.neCpu')}\n${
resourceData.value.neCpu
}%`;
},
textStyle: {
fontSize: 12,
color: '#fff',
},
},
},
outline: {
show: true,
borderDistance: 2,
itemStyle: {
borderColor: '#0a3ca0',
borderWidth: 1,
},
},
},
{
type: 'liquidFill',
radius: '50%',
center: ['85%', '35%'],
data: [resourceData.value.sysCpu / 100],
name: t('views.dashboard.overview.resources.sysCpu'),
color: getColorByValue(resourceData.value.sysCpu),
backgroundStyle: {
color: 'rgba(10, 60, 160, 0.1)',
},
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.sysCpu')}\n${
resourceData.value.sysCpu
}%`;
},
textStyle: {
fontSize: 12,
color: '#fff',
},
},
},
outline: {
show: true,
borderDistance: 2,
itemStyle: {
borderColor: '#0a3ca0',
borderWidth: 1,
},
},
},
{
type: 'liquidFill',
radius: '50%',
center: ['35%', '65%'],
data: [resourceData.value.sysMem / 100],
name: t('views.dashboard.overview.resources.sysMem'),
color: getColorByValue(resourceData.value.sysMem),
backgroundStyle: {
color: 'rgba(10, 60, 160, 0.1)',
},
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.sysMem')}\n${
resourceData.value.sysMem
}%`;
},
textStyle: {
fontSize: 12,
color: '#fff',
},
},
},
outline: {
show: true,
borderDistance: 2,
itemStyle: {
borderColor: '#0a3ca0',
borderWidth: 1,
},
},
},
{
type: 'liquidFill',
radius: '50%',
center: ['65%', '65%'],
data: [resourceData.value.sysDisk / 100],
name: t('views.dashboard.overview.resources.sysDisk'),
color: getColorByValue(resourceData.value.sysDisk),
backgroundStyle: {
color: 'rgba(10, 60, 160, 0.1)',
},
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.sysDisk')}\n${
resourceData.value.sysDisk
}%`;
},
textStyle: {
fontSize: 12,
color: '#fff',
},
},
},
outline: {
show: true,
borderDistance: 2,
itemStyle: {
borderColor: '#0a3ca0',
borderWidth: 1,
},
},
},
],
};
/**图数据渲染 */
function handleRanderChart(container: HTMLElement | undefined, option: any) {
if (!container) return;
neResourcesChart.value = markRaw(echarts.init(container));
option && neResourcesChart.value.setOption(option);
// 创建 ResizeObserver 实例
var observer = new ResizeObserver(entries => {
if (neResourcesChart.value) {
neResourcesChart.value.resize();
}
});
// 监听元素大小变化
observer.observe(container);
}
function fnChangeData(data: any[], itemID: string) {
const neType = itemID.split('_')[0];
const neID = itemID.split('_')[1];
currentNeId.value = neID;
let info = data.find((item: any) => item.id === neType);
if (!info || !info.neStateMap[neID]?.online) return;
let sysCpuUsage = 0;
let nfCpuUsage = 0;
if (info.neStateMap[neID].cpu) {
nfCpuUsage = info.neStateMap[neID].cpu.nfCpuUsage;
const nfCpu = +(info.neStateMap[neID].cpu.nfCpuUsage / 100);
nfCpuUsage = +nfCpu.toFixed(2);
if (nfCpuUsage > 100) {
nfCpuUsage = 100;
}
sysCpuUsage = info.neStateMap[neID].cpu.sysCpuUsage;
let sysCpu = +(info.neStateMap[neID].cpu.sysCpuUsage / 100);
sysCpuUsage = +sysCpu.toFixed(2);
if (sysCpuUsage > 100) {
sysCpuUsage = 100;
}
}
let sysMemUsage = 0;
if (info.neStateMap[neID].mem) {
const men = info.neStateMap[neID].mem.sysMemUsage;
sysMemUsage = +(men / 100).toFixed(2);
if (sysMemUsage > 100) {
sysMemUsage = 100;
}
}
let sysDiskUsage = 0;
if (
info.neStateMap[neID].disk &&
Array.isArray(info.neStateMap[neID].disk.partitionInfo)
) {
let disks: any[] = info.neStateMap[neID].disk.partitionInfo;
disks = disks.sort((a, b) => +b.used - +a.used);
if (disks.length > 0) {
const { total, used } = disks[0];
sysDiskUsage = +((used / total) * 100).toFixed(2);
}
}
resourceData.value = {
neCpu: nfCpuUsage,
sysCpu: sysCpuUsage,
sysMem: sysMemUsage,
sysDisk: sysDiskUsage,
};
// 更新图表数据
neResourcesChart.value.setOption({
series: [
{
data: [resourceData.value.neCpu / 100],
color: getColorByValue(resourceData.value.neCpu),
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.neCpu')}\n${
resourceData.value.neCpu
}%`;
},
},
},
},
{
data: [resourceData.value.sysCpu / 100],
color: getColorByValue(resourceData.value.sysCpu),
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.sysCpu')}\n${
resourceData.value.sysCpu
}%`;
},
},
},
},
{
data: [resourceData.value.sysMem / 100],
color: getColorByValue(resourceData.value.sysMem),
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.sysMem')}\n${
resourceData.value.sysMem
}%`;
},
},
},
},
{
data: [resourceData.value.sysDisk / 100],
color: getColorByValue(resourceData.value.sysDisk),
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.sysDisk')}\n${
resourceData.value.sysDisk
}%`;
},
},
},
},
],
});
}
watch(
graphNodeState,
v => {
fnChangeData(v, graphNodeClickID.value);
},
{
deep: true,
}
);
watch(graphNodeClickID, v => {
fnChangeData(graphNodeState.value, v);
});
onMounted(() => {
nextTick(() => {
handleRanderChart(neResourcesDom.value, optionData);
});
});
</script>
<template>
<div class="resource-panel">
<div ref="neResourcesDom" class="chart"></div>
</div>
</template>
<style lang="less" scoped>
.resource-panel {
width: 100%;
height: 100%;
margin-top: -32px;
display: flex;
flex-direction: column;
.panel-title {
font-size: 14px;
color: #00fcff;
padding: 8px;
border-bottom: 1px solid rgba(10, 115, 255, 0.3);
}
.chart {
flex: 1;
width: 100%;
}
}
</style>