style: 修复样式
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
<script setup lang="ts">
|
||||
import * as echarts from 'echarts/core';
|
||||
import {
|
||||
TitleComponent,
|
||||
TitleComponentOption,
|
||||
TooltipComponent,
|
||||
TooltipComponentOption,
|
||||
GridComponent,
|
||||
GridComponentOption,
|
||||
LegendComponent,
|
||||
LegendComponentOption,
|
||||
} from 'echarts/components';
|
||||
import {
|
||||
PieChart,
|
||||
PieSeriesOption,
|
||||
BarChart,
|
||||
BarSeriesOption,
|
||||
} from 'echarts/charts';
|
||||
import { LabelLayout } from 'echarts/features';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
|
||||
import { markRaw, onMounted, ref } from 'vue';
|
||||
import { origGet, top3Sel } from '@/api/faultManage/actAlarm';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
echarts.use([
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
LegendComponent,
|
||||
PieChart,
|
||||
BarChart,
|
||||
CanvasRenderer,
|
||||
LabelLayout,
|
||||
]);
|
||||
|
||||
type EChartsOption = echarts.ComposeOption<
|
||||
| TitleComponentOption
|
||||
| TooltipComponentOption
|
||||
| GridComponentOption
|
||||
| LegendComponentOption
|
||||
| PieSeriesOption
|
||||
| BarSeriesOption
|
||||
>;
|
||||
|
||||
/**图DOM节点实例对象 */
|
||||
const alarmTypeBar = ref<HTMLElement | undefined>(undefined);
|
||||
|
||||
/**图实例对象 */
|
||||
const alarmTypeBarChart = ref<any>(null);
|
||||
|
||||
/**告警类型数据 */
|
||||
const alarmTypeType = ref<any>([
|
||||
{
|
||||
value: 0,
|
||||
name: t('views.index.Critical'),
|
||||
},
|
||||
{
|
||||
value: 0,
|
||||
name: t('views.index.Major'),
|
||||
},
|
||||
{
|
||||
value: 0,
|
||||
name: t('views.index.Minor'),
|
||||
},
|
||||
{
|
||||
value: 0,
|
||||
name: t('views.index.Warning'),
|
||||
},
|
||||
// {
|
||||
// value: 0,
|
||||
// name: t('views.index.Event'),
|
||||
// },
|
||||
]);
|
||||
|
||||
/**告警类型Top数据 */
|
||||
const alarmTypeTypeTop = ref<any>([
|
||||
{ name: 'AMF', value: 0 },
|
||||
{ name: 'UDM', value: 0 },
|
||||
{ name: 'SMF', value: 0 },
|
||||
]);
|
||||
|
||||
//
|
||||
function initPicture() {
|
||||
Promise.allSettled([origGet(), top3Sel()])
|
||||
.then(resArr => {
|
||||
if (resArr[0].status === 'fulfilled') {
|
||||
const res0 = resArr[0].value;
|
||||
if (res0.code === RESULT_CODE_SUCCESS && Array.isArray(res0.data)) {
|
||||
for (const item of res0.data) {
|
||||
let index = 0;
|
||||
switch (item.name) {
|
||||
case 'Critical':
|
||||
index = 0;
|
||||
break;
|
||||
case 'Major':
|
||||
index = 1;
|
||||
break;
|
||||
case 'Minor':
|
||||
index = 2;
|
||||
break;
|
||||
case 'Warning':
|
||||
index = 3;
|
||||
break;
|
||||
// case 'Event':
|
||||
// index = 4;
|
||||
// break;
|
||||
}
|
||||
alarmTypeType.value[index].value = Number(item.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (resArr[1].status === 'fulfilled') {
|
||||
const res1 = resArr[1].value;
|
||||
if (res1.code === RESULT_CODE_SUCCESS && Array.isArray(res1.data)) {
|
||||
alarmTypeTypeTop.value = alarmTypeTypeTop.value
|
||||
.concat(res1.data)
|
||||
.sort((a: any, b: any) => {
|
||||
return b.value - a.value;
|
||||
})
|
||||
.slice(0, 3);
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
const optionData: EChartsOption = {
|
||||
title: [
|
||||
{
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
text: t('views.dashboard.overview.alarmTypeBar.topTitle'),
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: '14',
|
||||
fontWeight: 400,
|
||||
},
|
||||
top: '50%',
|
||||
left: '0%',
|
||||
},
|
||||
],
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b} : {c}',
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
right: '2%',
|
||||
top: '12%',
|
||||
data: alarmTypeType.value.map((item: any) => item.name), //label数组
|
||||
textStyle: {
|
||||
color: '#A7D6F4', // 设置图例文字颜色
|
||||
},
|
||||
},
|
||||
grid: [
|
||||
{
|
||||
top: '60%',
|
||||
left: '15%',
|
||||
right: '25%',
|
||||
bottom: '10%',
|
||||
},
|
||||
],
|
||||
series: [
|
||||
//饼图:
|
||||
{
|
||||
type: 'pie',
|
||||
radius: '35%',
|
||||
color: ['#f5222d', '#fa8c16', '#fadb14', '#1677ff', '#13c2c2'],
|
||||
label: {
|
||||
show: true,
|
||||
position: 'inner',
|
||||
formatter: (params: any) => {
|
||||
if (!params.value) return '';
|
||||
return `${params.value}`;
|
||||
},
|
||||
},
|
||||
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)',
|
||||
},
|
||||
},
|
||||
//柱状
|
||||
{
|
||||
type: 'bar',
|
||||
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, [
|
||||
{ offset: 0, color: '#f0f5ff' },
|
||||
{ offset: 0.5, color: '#adc6ff' },
|
||||
{ offset: 1, color: '#2f54eb' },
|
||||
]), // 渐变
|
||||
},
|
||||
data: alarmTypeTypeTop.value,
|
||||
},
|
||||
],
|
||||
// 柱状图设置
|
||||
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.name),
|
||||
axisLabel: {
|
||||
color: '#A7D6F4',
|
||||
fontSize: 14,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
fnDesign(alarmTypeBar.value, optionData);
|
||||
});
|
||||
}
|
||||
|
||||
function fnDesign(container: HTMLElement | undefined, option: any) {
|
||||
if (!container) return;
|
||||
|
||||
alarmTypeBarChart.value = markRaw(echarts.init(container, 'light'));
|
||||
option && alarmTypeBarChart.value.setOption(option);
|
||||
|
||||
// 创建 ResizeObserver 实例
|
||||
var observer = new ResizeObserver(entries => {
|
||||
if (alarmTypeBarChart.value) {
|
||||
alarmTypeBarChart.value.resize();
|
||||
}
|
||||
});
|
||||
// 监听元素大小变化
|
||||
observer.observe(container);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initPicture();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="alarmTypeBar" class="chart-container"></div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.chart-container {
|
||||
/* 设置图表容器大小和位置 */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,368 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, nextTick, watch } from 'vue';
|
||||
import * as echarts from 'echarts/core';
|
||||
import { GridComponent, GridComponentOption } from 'echarts/components';
|
||||
import {
|
||||
BarChart,
|
||||
BarSeriesOption,
|
||||
PictorialBarChart,
|
||||
PictorialBarSeriesOption,
|
||||
} from 'echarts/charts';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import { graphNodeClickID, graphNodeState } from '../../hooks/useTopology';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import { markRaw } from 'vue';
|
||||
const { t } = useI18n();
|
||||
|
||||
echarts.use([GridComponent, BarChart, PictorialBarChart, CanvasRenderer]);
|
||||
|
||||
type EChartsOption = echarts.ComposeOption<
|
||||
GridComponentOption | BarSeriesOption | PictorialBarSeriesOption
|
||||
>;
|
||||
|
||||
/**图DOM节点实例对象 */
|
||||
const neResourcesDom = ref<HTMLElement | undefined>(undefined);
|
||||
|
||||
/**图实例对象 */
|
||||
const neResourcesChart = ref<any>(null);
|
||||
|
||||
// 类别
|
||||
const category = ref<any>([
|
||||
{
|
||||
name: t('views.dashboard.overview.resources.sysDisk'),
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
name: t('views.dashboard.overview.resources.sysMem'),
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
name: t('views.dashboard.overview.resources.sysCpu'),
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
name: t('views.dashboard.overview.resources.neCpu'),
|
||||
value: 1,
|
||||
},
|
||||
]);
|
||||
|
||||
// 数据总数
|
||||
const total = 100;
|
||||
|
||||
/**图数据 */
|
||||
const optionData: EChartsOption = {
|
||||
xAxis: {
|
||||
max: total,
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
show: false,
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: '1%', // 设置条形图的边距
|
||||
bottom: '12%',
|
||||
left: '25%',
|
||||
right: '25%',
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
inverse: false,
|
||||
data: category.value,
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
// 内
|
||||
type: 'bar',
|
||||
barWidth: 10,
|
||||
legendHoverLink: false,
|
||||
silent: true,
|
||||
itemStyle: {
|
||||
color: function (params) {
|
||||
// 红色
|
||||
if (params.value && +params.value >= 70) {
|
||||
return new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: '#fff1f0' },
|
||||
{ offset: 0.5, color: '#ffa39e' },
|
||||
{ offset: 1, color: '#f5222d' },
|
||||
]);
|
||||
}
|
||||
// 蓝色
|
||||
if (params.value && +params.value >= 30) {
|
||||
return new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: '#f0f5ff' },
|
||||
{ offset: 0.5, color: '#adc6ff' },
|
||||
{ offset: 1, color: '#2f54eb' },
|
||||
]);
|
||||
}
|
||||
// 绿色
|
||||
return new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: '#f6ffed' },
|
||||
{ offset: 0.5, color: '#b7eb8f' },
|
||||
{ offset: 1, color: '#52c41a' },
|
||||
]);
|
||||
},
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'left',
|
||||
formatter: '{b}: ',
|
||||
fontSize: 15,
|
||||
color: '#fff',
|
||||
},
|
||||
data: category.value,
|
||||
z: 1,
|
||||
animationEasing: 'elasticOut',
|
||||
},
|
||||
{
|
||||
// 分隔
|
||||
type: 'pictorialBar',
|
||||
itemStyle: {
|
||||
color: '#0a3ca0',
|
||||
},
|
||||
symbolRepeat: 'fixed',
|
||||
symbolMargin: 6,
|
||||
symbol: 'rect',
|
||||
symbolClip: true,
|
||||
symbolSize: [1, 12],
|
||||
symbolPosition: 'start',
|
||||
symbolOffset: [0, -1],
|
||||
symbolBoundingData: total,
|
||||
data: category.value,
|
||||
z: 2,
|
||||
animationEasing: 'elasticOut',
|
||||
},
|
||||
{
|
||||
// 外边框
|
||||
type: 'pictorialBar',
|
||||
symbol: 'rect',
|
||||
symbolBoundingData: total,
|
||||
itemStyle: {
|
||||
color: 'transparent',
|
||||
},
|
||||
label: {
|
||||
formatter: params => {
|
||||
var text = `{a| ${params.value}%} `;
|
||||
if (params.value && +params.value >= 70) {
|
||||
text = `{c| ${params.value}%} `;
|
||||
} else if (params.value && +params.value >= 30) {
|
||||
text = `{b| ${params.value}%} `;
|
||||
}
|
||||
return text;
|
||||
},
|
||||
rich: {
|
||||
a: {
|
||||
color: '#52c41a', // 绿
|
||||
fontSize: 16,
|
||||
},
|
||||
b: {
|
||||
color: '#2f54eb', // 蓝
|
||||
fontSize: 16,
|
||||
},
|
||||
c: {
|
||||
color: '#f5222d', // 红
|
||||
fontSize: 16,
|
||||
},
|
||||
f: {
|
||||
color: '#ffffff', // 默认
|
||||
fontSize: 16,
|
||||
},
|
||||
},
|
||||
position: 'right',
|
||||
distance: 0, // 向右偏移位置
|
||||
show: true,
|
||||
},
|
||||
data: category.value,
|
||||
z: 0,
|
||||
animationEasing: 'elasticOut',
|
||||
},
|
||||
{
|
||||
name: '外框',
|
||||
type: 'bar',
|
||||
barGap: '-120%', // 设置外框粗细
|
||||
data: [total, total, total],
|
||||
barWidth: 14,
|
||||
itemStyle: {
|
||||
color: 'transparent', // 填充色
|
||||
borderColor: '#0a3ca0', // 边框色
|
||||
borderWidth: 1, // 边框宽度
|
||||
borderRadius: 1, //圆角半径
|
||||
},
|
||||
label: {
|
||||
// 标签显示位置
|
||||
show: false,
|
||||
position: 'top', // insideTop 或者横向的 insideLeft
|
||||
},
|
||||
z: 0,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/**图数据渲染 */
|
||||
function handleRanderChart(
|
||||
container: HTMLElement | undefined,
|
||||
option: EChartsOption
|
||||
) {
|
||||
if (!container) return;
|
||||
neResourcesChart.value = markRaw(echarts.init(container, 'light'));
|
||||
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) {
|
||||
let info = data.find((item: any) => item.id === itemID);
|
||||
if (!info || !info.neState.online) return;
|
||||
// if (!info.neState.online) {
|
||||
// info = data.find((item: any) => item.id === itemID);
|
||||
// graphNodeClickID.value = itemID;
|
||||
// }
|
||||
// console.log(info.id);
|
||||
// console.log(info.neState.cpu.nfCpuUsage);
|
||||
// console.log(info.neState.cpu.sysCpuUsage);
|
||||
// console.log(info.neState.mem);
|
||||
// console.log(info.neState.disk);
|
||||
let sysCpuUsage = 0;
|
||||
let nfCpuUsage = 0;
|
||||
if (info.neState.cpu) {
|
||||
nfCpuUsage = info.neState.cpu.nfCpuUsage;
|
||||
const nfCpu = +(info.neState.cpu.nfCpuUsage / 100);
|
||||
nfCpuUsage = +nfCpu.toFixed(2);
|
||||
if (nfCpuUsage > 100) {
|
||||
nfCpuUsage = 100;
|
||||
}
|
||||
|
||||
sysCpuUsage = info.neState.cpu.sysCpuUsage;
|
||||
let sysCpu = +(info.neState.cpu.sysCpuUsage / 100);
|
||||
sysCpuUsage = +sysCpu.toFixed(2);
|
||||
if (sysCpuUsage > 100) {
|
||||
sysCpuUsage = 100;
|
||||
}
|
||||
}
|
||||
|
||||
let sysMemUsage = 0;
|
||||
if (info.neState.mem) {
|
||||
const men = info.neState.mem.sysMemUsage;
|
||||
sysMemUsage = +(men / 100).toFixed(2);
|
||||
if (sysMemUsage > 100) {
|
||||
sysMemUsage = 100;
|
||||
}
|
||||
}
|
||||
|
||||
let sysDiskUsage = 0;
|
||||
if (info.neState.disk && Array.isArray(info.neState.disk.partitionInfo)) {
|
||||
let disks: any[] = info.neState.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);
|
||||
}
|
||||
}
|
||||
|
||||
category.value[0].value = sysDiskUsage;
|
||||
category.value[1].value = sysMemUsage;
|
||||
category.value[2].value = sysCpuUsage;
|
||||
category.value[3].value = nfCpuUsage;
|
||||
neResourcesChart.value.setOption({
|
||||
series: [
|
||||
{
|
||||
data: category.value,
|
||||
},
|
||||
{
|
||||
data: category.value,
|
||||
},
|
||||
{
|
||||
data: category.value,
|
||||
},
|
||||
{
|
||||
data: category.value,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
graphNodeState,
|
||||
v => {
|
||||
fnChangeData(v, graphNodeClickID.value);
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(graphNodeClickID, v => {
|
||||
fnChangeData(graphNodeState.value, v);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
// setInterval(function () {
|
||||
// var ndata = [
|
||||
// {
|
||||
// name: '系统内存',
|
||||
// value: Math.round(Math.random() * 100),
|
||||
// },
|
||||
// {
|
||||
// name: '系统CPU',
|
||||
// value: Math.round(Math.random() * 100),
|
||||
// },
|
||||
// {
|
||||
// name: '网元CPU',
|
||||
// value: Math.round(Math.random() * 100),
|
||||
// },
|
||||
// ];
|
||||
// neResourcesChart.value.setOption({
|
||||
// series: [
|
||||
// {
|
||||
// data: ndata,
|
||||
// },
|
||||
// {
|
||||
// data: ndata,
|
||||
// },
|
||||
// {
|
||||
// data: ndata,
|
||||
// },
|
||||
// ],
|
||||
// });
|
||||
// }, 2000);
|
||||
nextTick(() => {
|
||||
handleRanderChart(neResourcesDom.value, optionData);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="neResourcesDom" class="chart"></div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,267 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
import { listAllNeInfo } from '@/api/ne/neInfo';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import { getGraphData } from '@/api/monitor/topology';
|
||||
import { Graph, GraphData, Tooltip } from '@antv/g6';
|
||||
import { parseBasePath } from '@/plugins/file-static-url';
|
||||
import { edgeLineAnimateState } from '@/views/monitor/topologyBuild/hooks/registerEdge';
|
||||
import { nodeImageAnimateState } from '@/views/monitor/topologyBuild/hooks/registerNode';
|
||||
import {
|
||||
graphG6,
|
||||
graphState,
|
||||
graphNodeClickID,
|
||||
notNeNodes,
|
||||
} from '../../hooks/useTopology';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
const { t } = useI18n();
|
||||
|
||||
/**图DOM节点实例对象 */
|
||||
const graphG6Dom = ref<HTMLElement | undefined>(undefined);
|
||||
|
||||
/**图节点展示 */
|
||||
const graphNodeTooltip = new Tooltip({
|
||||
offsetX: 20,
|
||||
offsetY: 20,
|
||||
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: 200px;
|
||||
"
|
||||
>
|
||||
<div><strong>${t('views.monitor.topology.state')}:</strong><span>
|
||||
${
|
||||
neState.online
|
||||
? t('views.monitor.topology.normalcy')
|
||||
: t('views.monitor.topology.exceptions')
|
||||
}
|
||||
</span></div>
|
||||
<div><strong>${t('views.monitor.topology.refreshTime')}:</strong><span>
|
||||
${neState.refreshTime ?? '--'}
|
||||
</span></div>
|
||||
<div>========================</div>
|
||||
<div><strong>ID:</strong><span>${neState.neId}</span></div>
|
||||
<div><strong>${t('views.monitor.topology.name')}:</strong><span>
|
||||
${neState.neName ?? '--'}
|
||||
</span></div>
|
||||
<div><strong>IP:</strong><span>${neState.neIP}</span></div>
|
||||
<div><strong>${t('views.monitor.topology.version')}:</strong><span>
|
||||
${neState.version ?? '--'}
|
||||
</span></div>
|
||||
<div><strong>${t('views.monitor.topology.serialNum')}:</strong><span>
|
||||
${neState.sn ?? '--'}
|
||||
</span></div>
|
||||
<div><strong>${t('views.monitor.topology.expiryDate')}:</strong><span>
|
||||
${neState.expire ?? '--'}
|
||||
</span></div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
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(
|
||||
container: HTMLElement | undefined,
|
||||
data: GraphData
|
||||
) {
|
||||
if (!container) return;
|
||||
const { clientHeight, clientWidth } = container;
|
||||
|
||||
edgeLineAnimateState();
|
||||
nodeImageAnimateState();
|
||||
|
||||
const graph = new Graph({
|
||||
container: container,
|
||||
width: clientWidth,
|
||||
height: clientHeight - 36,
|
||||
fitCenter: true,
|
||||
fitView: true,
|
||||
fitViewPadding: [20],
|
||||
autoPaint: true,
|
||||
modes: {
|
||||
default: ['drag-canvas', 'zoom-canvas'],
|
||||
},
|
||||
groupByTypes: false,
|
||||
nodeStateStyles: {
|
||||
selected: {
|
||||
fill: 'transparent',
|
||||
},
|
||||
},
|
||||
plugins: [graphNodeTooltip],
|
||||
animate: true, // 是否使用动画过度,默认为 false
|
||||
animateCfg: {
|
||||
duration: 500, // Number,一次动画的时长
|
||||
easing: 'linearEasing', // String,动画函数
|
||||
},
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
|
||||
fnGraphEvent(graph);
|
||||
|
||||
graphG6.value = graph;
|
||||
|
||||
// 创建 ResizeObserver 实例
|
||||
var observer = new ResizeObserver(function (entries) {
|
||||
// 当元素大小发生变化时触发回调函数
|
||||
entries.forEach(function (entry) {
|
||||
if (!graphG6.value) {
|
||||
return;
|
||||
}
|
||||
graphG6.value.changeSize(
|
||||
entry.contentRect.width,
|
||||
entry.contentRect.height - 30
|
||||
);
|
||||
graphG6.value.fitCenter();
|
||||
});
|
||||
});
|
||||
// 监听元素大小变化
|
||||
observer.observe(container);
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图组数据渲染到画布
|
||||
* @param reload 是否重载数据
|
||||
*/
|
||||
function fnGraphDataLoad(reload: boolean = false) {
|
||||
Promise.all([
|
||||
getGraphData(graphState.group),
|
||||
listAllNeInfo({
|
||||
bandStatus: false,
|
||||
}),
|
||||
])
|
||||
.then(resArr => {
|
||||
const graphRes = resArr[0];
|
||||
const neRes = resArr[1];
|
||||
if (
|
||||
graphRes.code === RESULT_CODE_SUCCESS &&
|
||||
Array.isArray(graphRes.data.nodes) &&
|
||||
graphRes.data.nodes.length > 0 &&
|
||||
neRes.code === RESULT_CODE_SUCCESS &&
|
||||
Array.isArray(neRes.data) &&
|
||||
neRes.data.length > 0
|
||||
) {
|
||||
return {
|
||||
graphData: graphRes.data,
|
||||
neList: neRes.data,
|
||||
};
|
||||
} else {
|
||||
message.warning({
|
||||
content: t('views.monitor.topology.noData'),
|
||||
duration: 5,
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
if (!res) return;
|
||||
const { combos, edges, nodes } = res.graphData;
|
||||
|
||||
// 节点过滤
|
||||
const nf: Record<string, any>[] = nodes.filter(
|
||||
(node: Record<string, any>) => {
|
||||
Reflect.set(node, 'neState', { online: false });
|
||||
// 图片路径处理
|
||||
if (node.img) node.img = parseBasePath(node.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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
// 边过滤
|
||||
const ef: Record<string, any>[] = edges.filter(
|
||||
(edge: Record<string, any>) => {
|
||||
const edgeSource: string = edge.source;
|
||||
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;
|
||||
}
|
||||
if (hasNeS && notNeNodes.includes(edgeTarget)) {
|
||||
return true;
|
||||
}
|
||||
if (hasNeT && notNeNodes.includes(edgeSource)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
// 分组过滤
|
||||
combos.forEach((combo: Record<string, any>) => {
|
||||
const comboChildren: Record<string, any>[] = combo.children;
|
||||
combo.children = comboChildren.filter(c => nf.some(n => n.id === c.id));
|
||||
return combo;
|
||||
});
|
||||
|
||||
// 图数据
|
||||
graphState.data = { combos, edges: ef, nodes: nf };
|
||||
})
|
||||
.finally(() => {
|
||||
if (graphState.data.length < 0) return;
|
||||
// 重载数据
|
||||
if (reload) {
|
||||
graphG6.value.read(graphState.data);
|
||||
} else {
|
||||
handleRanderGraph(graphG6Dom.value, graphState.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fnGraphDataLoad(false);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="graphG6Dom" class="chart"></div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,290 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import { listKPIData } from '@/api/perfManage/goldTarget';
|
||||
import * as echarts from 'echarts/core';
|
||||
import {
|
||||
TooltipComponent,
|
||||
TooltipComponentOption,
|
||||
GridComponent,
|
||||
GridComponentOption,
|
||||
LegendComponent,
|
||||
LegendComponentOption,
|
||||
} from 'echarts/components';
|
||||
import { LineChart, LineSeriesOption } from 'echarts/charts';
|
||||
import { UniversalTransition } from 'echarts/features';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import { markRaw } from 'vue';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import { parseDateToStr } from '@/utils/date-utils';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
import { upfFlowData, upfFlowParse } from '../../hooks/useUPFTotalFlow';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
echarts.use([
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
LegendComponent,
|
||||
LineChart,
|
||||
CanvasRenderer,
|
||||
UniversalTransition,
|
||||
]);
|
||||
|
||||
type EChartsOption = echarts.ComposeOption<
|
||||
| TooltipComponentOption
|
||||
| GridComponentOption
|
||||
| LegendComponentOption
|
||||
| LineSeriesOption
|
||||
>;
|
||||
|
||||
/**图DOM节点实例对象 */
|
||||
const upfFlow = ref<HTMLElement | undefined>(undefined);
|
||||
|
||||
/**图实例对象 */
|
||||
const upfFlowChart = ref<any>(null);
|
||||
|
||||
function fnDesign(container: HTMLElement | undefined, option: EChartsOption) {
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
if (!upfFlowChart.value) {
|
||||
upfFlowChart.value = markRaw(echarts.init(container, 'light'));
|
||||
}
|
||||
|
||||
option && upfFlowChart.value.setOption(option);
|
||||
|
||||
// 创建 ResizeObserver 实例
|
||||
var observer = new ResizeObserver(entries => {
|
||||
if (upfFlowChart.value) {
|
||||
upfFlowChart.value.resize();
|
||||
}
|
||||
});
|
||||
// 监听元素大小变化
|
||||
observer.observe(container);
|
||||
}
|
||||
|
||||
//渲染速率图
|
||||
function handleRanderChart() {
|
||||
const { lineXTime, lineYUp, lineYDown } = upfFlowData.value;
|
||||
|
||||
var yAxisSeries: any = [
|
||||
{
|
||||
name: t('views.dashboard.overview.upfFlow.up'),
|
||||
type: 'line',
|
||||
color: 'rgba(250, 219, 20)',
|
||||
smooth: true,
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(250, 219, 20, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(250, 219, 20, 0.5)',
|
||||
},
|
||||
]),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||
shadowBlur: 10,
|
||||
},
|
||||
symbol: 'circle',
|
||||
symbolSize: 5,
|
||||
formatter: '{b}',
|
||||
data: lineYUp,
|
||||
},
|
||||
{
|
||||
name: t('views.dashboard.overview.upfFlow.down'),
|
||||
type: 'line',
|
||||
color: 'rgba(92, 123, 217)',
|
||||
smooth: true,
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(92, 123, 217, .5)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(92, 123, 217, 0.5)',
|
||||
},
|
||||
]),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||
shadowBlur: 10,
|
||||
},
|
||||
symbol: 'circle',
|
||||
symbolSize: 5,
|
||||
formatter: '{b}',
|
||||
data: lineYDown,
|
||||
},
|
||||
];
|
||||
|
||||
const optionData: EChartsOption = {
|
||||
tooltip: {
|
||||
show: true, //是否显示提示框组件
|
||||
trigger: 'axis',
|
||||
//formatter:'{a0}:{c0}<br>{a1}:{c1}'
|
||||
formatter: function (param: any) {
|
||||
var tip = '';
|
||||
if (param !== null && param.length > 0) {
|
||||
tip += param[0].name + '<br />';
|
||||
for (var i = 0; i < param.length; i++) {
|
||||
tip +=
|
||||
param[i].marker +
|
||||
param[i].seriesName +
|
||||
': ' +
|
||||
param[i].value +
|
||||
'<br />';
|
||||
}
|
||||
}
|
||||
return tip;
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: yAxisSeries.map((s: any) => s.name),
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
color: 'rgb(0,253,255,0.6)',
|
||||
},
|
||||
left: 'center', // 设置图例居中
|
||||
},
|
||||
grid: {
|
||||
top: '14%',
|
||||
left: '4%',
|
||||
right: '4%',
|
||||
bottom: '12%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: lineXTime,
|
||||
axisLabel: {
|
||||
formatter: function (params: any) {
|
||||
return params.split(' ')[1];
|
||||
},
|
||||
fontSize: 14,
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: 'rgb(0,253,255,0.6)',
|
||||
},
|
||||
},
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
name: '(Mbps)',
|
||||
nameTextStyle: {
|
||||
fontSize: 12, // 设置文字距离x轴的距离
|
||||
padding: [0, -10, 0, 0], // 设置名称在x轴方向上的偏移
|
||||
},
|
||||
type: 'value',
|
||||
// splitNumber: 4,
|
||||
min: 0,
|
||||
//max: 300,
|
||||
axisLabel: {
|
||||
formatter: '{value}',
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: 'rgb(23,255,243,0.3)',
|
||||
},
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: 'rgb(0,253,255,0.6)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
series: yAxisSeries,
|
||||
};
|
||||
fnDesign(upfFlow.value, optionData);
|
||||
}
|
||||
|
||||
/**查询初始UPF数据 */
|
||||
function fnGetInitData() {
|
||||
// 查询5分钟前的
|
||||
const nowDate = new Date().getTime();
|
||||
|
||||
listKPIData({
|
||||
neType: 'UPF',
|
||||
neId: '001',
|
||||
startTime: nowDate - 5 * 60 * 1000,
|
||||
endTime: nowDate,
|
||||
interval: 5, // 5秒
|
||||
sortField: 'timeGroup',
|
||||
sortOrder: 'asc',
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||
for (const item of res.data) {
|
||||
upfFlowParse(item);
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
handleRanderChart();
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => upfFlowData.value,
|
||||
v => {
|
||||
if (upfFlowChart.value == null) return;
|
||||
upfFlowChart.value.setOption({
|
||||
xAxis: {
|
||||
data: v.lineXTime,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: v.lineYUp,
|
||||
},
|
||||
{
|
||||
data: v.lineYDown,
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
fnGetInitData();
|
||||
|
||||
// setInterval(() => {
|
||||
// upfFlowData.value.lineXTime.push(parseDateToStr(new Date()));
|
||||
// const upN3 = parseSizeFromKbs(+145452, 5);
|
||||
// upfFlowData.value.lineYUp.push(upN3[0]);
|
||||
// const downN6 = parseSizeFromKbs(+232343, 5);
|
||||
// upfFlowData.value.lineYDown.push(downN6[0]);
|
||||
|
||||
// upfFlowChart.value.setOption({
|
||||
// xAxis: {
|
||||
// data: upfFlowData.value.lineXTime,
|
||||
// },
|
||||
// series: [
|
||||
// {
|
||||
// data: upfFlowData.value.lineYUp,
|
||||
// },
|
||||
// {
|
||||
// data: upfFlowData.value.lineYDown,
|
||||
// },
|
||||
// ],
|
||||
// });
|
||||
// }, 5000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="upfFlow" class="chart-container"></div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.chart-container {
|
||||
/* 设置图表容器大小和位置 */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,370 @@
|
||||
<script setup lang="ts">
|
||||
import { parseDuration, parseDateToStr } from '@/utils/date-utils';
|
||||
import { eventData, eventId } from '../../hooks/useUserActivity';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import useDictStore from '@/store/modules/dict';
|
||||
import { onMounted, reactive } from 'vue';
|
||||
const { t } = useI18n();
|
||||
const { getDict } = useDictStore();
|
||||
|
||||
/**字典数据 */
|
||||
let dict: {
|
||||
/**CDR SIP响应代码类别类型 */
|
||||
cdrSipCode: DictType[];
|
||||
/**CDR 呼叫类型 */
|
||||
cdrCallType: DictType[];
|
||||
/**UE 事件认证代码类型 */
|
||||
ueAauthCode: DictType[];
|
||||
/**UE 事件类型 */
|
||||
ueEventType: DictType[];
|
||||
/**UE 事件CM状态 */
|
||||
ueEventCmState: DictType[];
|
||||
} = reactive({
|
||||
cdrSipCode: [],
|
||||
cdrCallType: [],
|
||||
ueAauthCode: [],
|
||||
ueEventType: [],
|
||||
ueEventCmState: [],
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
// 初始字典数据
|
||||
Promise.allSettled([
|
||||
getDict('cdr_sip_code'),
|
||||
getDict('cdr_call_type'),
|
||||
getDict('ue_auth_code'),
|
||||
getDict('ue_event_type'),
|
||||
getDict('ue_event_cm_state'),
|
||||
]).then(resArr => {
|
||||
if (resArr[0].status === 'fulfilled') {
|
||||
dict.cdrSipCode = resArr[0].value;
|
||||
}
|
||||
if (resArr[1].status === 'fulfilled') {
|
||||
dict.cdrCallType = resArr[1].value;
|
||||
}
|
||||
if (resArr[2].status === 'fulfilled') {
|
||||
dict.ueAauthCode = resArr[2].value;
|
||||
}
|
||||
if (resArr[3].status === 'fulfilled') {
|
||||
dict.ueEventType = resArr[3].value;
|
||||
}
|
||||
if (resArr[4].status === 'fulfilled') {
|
||||
dict.ueEventCmState = resArr[4].value;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="activty">
|
||||
<template v-for="item in eventData" :key="item.eId">
|
||||
<!-- CDR事件IMS -->
|
||||
<div
|
||||
class="card-cdr"
|
||||
:class="{ active: item.eId === eventId }"
|
||||
v-if="item.eType === 'ims_cdr'"
|
||||
>
|
||||
<div class="card-cdr-item">
|
||||
<div>
|
||||
{{ t('views.dashboard.overview.userActivity.type') }}:
|
||||
<span>
|
||||
<DictTag
|
||||
:options="dict.cdrCallType"
|
||||
:value="item.data.callType"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div></div>
|
||||
<div>
|
||||
{{ t('views.dashboard.overview.userActivity.time') }}:
|
||||
<span :title="parseDateToStr(item.data.releaseTime * 1000)">
|
||||
{{ parseDateToStr(item.data.releaseTime * 1000) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-cdr-item">
|
||||
<div>
|
||||
{{ t('views.dashboard.overview.userActivity.caller') }}:
|
||||
<span :title="item.data.callerParty">
|
||||
{{ item.data.callerParty }}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ t('views.dashboard.overview.userActivity.called') }}:
|
||||
<span :title="item.data.calledParty">
|
||||
{{ item.data.calledParty }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="item.data.callType !== 'sms'">
|
||||
{{ t('views.dashboard.overview.userActivity.duration') }}:
|
||||
<span>{{ parseDuration(item.data.callDuration) }}</span>
|
||||
</div>
|
||||
<div v-else></div>
|
||||
</div>
|
||||
<div>
|
||||
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||
<span v-if="item.data.callType !== 'sms'">
|
||||
<DictTag
|
||||
:options="dict.cdrSipCode"
|
||||
:value="item.data.cause"
|
||||
value-default="0"
|
||||
/>
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ t('views.dashboard.overview.userActivity.resultOK') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- UE事件AMF -->
|
||||
<div
|
||||
class="card-ue"
|
||||
:class="{ active: item.eId === eventId }"
|
||||
v-if="item.eType === 'amf_ue'"
|
||||
>
|
||||
<div class="card-ue-item">
|
||||
<div>
|
||||
{{ t('views.dashboard.overview.userActivity.type') }}:
|
||||
<span>
|
||||
<DictTag :options="dict.ueEventType" :value="item.type" />
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
IMSI: <span :title="item.data.imsi">{{ item.data.imsi }}</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ t('views.dashboard.overview.userActivity.time') }}:
|
||||
<span
|
||||
v-if="item.type === 'auth-result'"
|
||||
:title="item.data.authTime"
|
||||
>
|
||||
{{ item.data.authTime }}
|
||||
</span>
|
||||
<span v-if="item.type === 'detach'" :title="item.data.detachTime">
|
||||
{{ item.data.detachTime }}
|
||||
</span>
|
||||
<span v-if="item.type === 'cm-state'" :title="item.data.changeTime">
|
||||
{{ item.data.changeTime }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-ue-w33" v-if="item.type === 'auth-result'">
|
||||
<div>
|
||||
GNB ID: <span>{{ item.data.gNBID }}</span>
|
||||
</div>
|
||||
<div>
|
||||
Cell ID: <span>{{ item.data.cellID }}</span>
|
||||
</div>
|
||||
<div>
|
||||
TAC ID: <span>{{ item.data.tacID }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="item.type === 'auth-result'">
|
||||
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||
<span>
|
||||
<DictTag :options="dict.ueAauthCode" :value="item.data.authCode" />
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="item.type === 'detach'">
|
||||
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||
<span>{{ t('views.dashboard.overview.userActivity.resultOK') }}</span>
|
||||
</div>
|
||||
<div class="card-ue-w33" v-if="item.type === 'cm-state'">
|
||||
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||
<span>
|
||||
<DictTag :options="dict.ueEventCmState" :value="item.data.status" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- UE事件MME -->
|
||||
<div
|
||||
class="card-ue"
|
||||
:class="{ active: item.eId === eventId }"
|
||||
v-if="item.eType === 'mme_ue'"
|
||||
>
|
||||
<div class="card-ue-item">
|
||||
<div>
|
||||
{{ t('views.dashboard.overview.userActivity.type') }}:
|
||||
<span v-if="item.type === 'cm-state'">
|
||||
{{
|
||||
dict.ueEventType
|
||||
.find(s => s.value === item.type)
|
||||
?.label.replace('CM', 'ECM')
|
||||
}}
|
||||
</span>
|
||||
<span v-else>
|
||||
<DictTag :options="dict.ueEventType" :value="item.type" />
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
IMSI: <span :title="item.data.imsi">{{ item.data.imsi }}</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ t('views.dashboard.overview.userActivity.time') }}:
|
||||
<span :title="item.data.timestamp">
|
||||
{{ parseDateToStr(+item.data.timestamp * 1000) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-ue-w33" v-if="item.type === 'auth-result'">
|
||||
<div>
|
||||
ENB ID: <span>{{ item.data.eNBID }}</span>
|
||||
</div>
|
||||
<div>
|
||||
Cell ID: <span>{{ item.data.cellID }}</span>
|
||||
</div>
|
||||
<div>
|
||||
TAC ID: <span>{{ item.data.tacID }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.type === 'auth-result'">
|
||||
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||
<span>
|
||||
<DictTag :options="dict.ueAauthCode" :value="item.data.result" />
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="item.type === 'detach'">
|
||||
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||
<span>{{ t('views.dashboard.overview.userActivity.resultOK') }}</span>
|
||||
</div>
|
||||
<div class="card-ue-w33" v-if="item.type === 'cm-state'">
|
||||
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||
<span>
|
||||
<DictTag :options="dict.ueEventCmState" :value="item.data.result" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.activty {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
height: 94%;
|
||||
color: #61a8ff;
|
||||
font-size: 0.75rem;
|
||||
& .card-ue {
|
||||
border: 1px #61a8ff solid;
|
||||
border-radius: 4px;
|
||||
padding: 0.2rem 0.5rem;
|
||||
margin-bottom: 0.3rem;
|
||||
line-height: 1rem;
|
||||
& span {
|
||||
color: #68d8fe;
|
||||
}
|
||||
&-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
& > div {
|
||||
width: 50%;
|
||||
white-space: nowrap;
|
||||
text-align: start;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
&-w33 {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
& > div {
|
||||
width: 33%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .card-cdr {
|
||||
border: 1px #61a8ff solid;
|
||||
border-radius: 4px;
|
||||
padding: 0.2rem 0.5rem;
|
||||
margin-bottom: 0.3rem;
|
||||
line-height: 1rem;
|
||||
& span {
|
||||
color: #68d8fe;
|
||||
}
|
||||
&-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
& > div {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
text-align: start;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .active {
|
||||
color: #faad14;
|
||||
border: 1px #faad14 solid;
|
||||
animation: backInRight 0.3s alternate;
|
||||
& span {
|
||||
color: #faad14;
|
||||
}
|
||||
}
|
||||
|
||||
/* 兼容当行显示字内容 */
|
||||
@media (max-width: 1720px) {
|
||||
& .card-cdr {
|
||||
&-item {
|
||||
display: block;
|
||||
& > div {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
& .card-ue {
|
||||
&-item {
|
||||
display: block;
|
||||
& > div {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 修改滚动条的样式 */
|
||||
&::-webkit-scrollbar {
|
||||
width: 8px; /* 设置滚动条宽度 */
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: #101129; /* 设置滚动条轨道背景颜色 */
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: #28293f; /* 设置滚动条滑块颜色 */
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #68d8fe; /* 设置鼠标悬停时滚动条滑块颜色 */
|
||||
}
|
||||
|
||||
@keyframes backInRight {
|
||||
0% {
|
||||
opacity: 0.7;
|
||||
-webkit-transform: translateX(2000px) scale(0.7);
|
||||
transform: translateX(2000px) scale(0.7);
|
||||
}
|
||||
|
||||
80% {
|
||||
opacity: 0.7;
|
||||
-webkit-transform: translateX(0) scale(0.7);
|
||||
transform: translateX(0) scale(0.7);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user