diff --git a/src/api/faultManage/actAlarm.ts b/src/api/faultManage/actAlarm.ts index 3b2743a4..9bb59835 100644 --- a/src/api/faultManage/actAlarm.ts +++ b/src/api/faultManage/actAlarm.ts @@ -194,33 +194,13 @@ export async function exportAll(query: Record) { * @returns bolb */ export async function origGet() { - let totalSQL = `select count(*) as value,orig_severity as name from alarm WHERE alarm_status='1' and orig_severity!='Event' group by orig_severity`; - - // 发起请求 - const result = await request({ - url: `/api/rest/databaseManagement/v1/select/omc_db/alarm`, + return await request({ + url: `/neData/alarm/count/severity`, method: 'GET', params: { - SQL: totalSQL, + alarmStatus: 'Active', }, - timeout: 30_000, }); - //// - - // 解析数据 - if (result.code === RESULT_CODE_SUCCESS) { - const itemData = result.data.data; - if (Array.isArray(itemData)) { - const v = itemData[0]['alarm']; - if (Array.isArray(v)) { - result.data = v; - } - if (v === null) { - result.data = []; - } - } - } - return result; } /** @@ -228,34 +208,13 @@ export async function origGet() { * @param filterFlag 查询参数 * @returns object */ -export async function top3Sel(filterFlag?: string) { - let filter = ` WHERE alarm_status='1'and orig_severity='${filterFlag}'`; - if (!filterFlag) filter = "WHERE alarm_status='1'"; - - let top3SQL = `select count(*) as value,ne_type as name from alarm ${filter} and orig_severity!='Event' group by ne_type ORDER BY value desc limit 0,3 `; - - // 发起请求 - const result = await request({ - url: `/api/rest/databaseManagement/v1/select/omc_db/alarm`, +export async function top3Sel() { + return await request({ + url: `/neData/alarm/count/ne`, method: 'GET', params: { - SQL: top3SQL, + alarmStatus: 'Active', + top: 3, }, - timeout: 30_000, }); - - // 解析数据 - if (result.code === RESULT_CODE_SUCCESS) { - const itemData = result.data.data; - if (Array.isArray(itemData)) { - const v = itemData[0]['alarm']; - if (Array.isArray(v)) { - result.data = v; - } - if (v === null) { - result.data = []; - } - } - } - return result; } diff --git a/src/views/dashboard/overview/components/AlarnTypeBar/index.vue b/src/views/dashboard/overview/components/AlarnTypeBar/index.vue index 583bfd03..a1d63140 100644 --- a/src/views/dashboard/overview/components/AlarnTypeBar/index.vue +++ b/src/views/dashboard/overview/components/AlarnTypeBar/index.vue @@ -78,9 +78,9 @@ const alarmTypeType = ref([ /**告警类型Top数据 */ const alarmTypeTypeTop = ref([ - { name: 'AMF', value: 0 }, - { name: 'UDM', value: 0 }, - { name: 'SMF', value: 0 }, + { neType: 'AMF', total: 0 }, + { neType: 'UDM', total: 0 }, + { neType: 'SMF', total: 0 }, ]); // @@ -92,7 +92,7 @@ function initPicture() { if (res0.code === RESULT_CODE_SUCCESS && Array.isArray(res0.data)) { for (const item of res0.data) { let index = 0; - switch (item.name) { + switch (item.severity) { case 'Critical': index = 0; break; @@ -109,7 +109,7 @@ function initPicture() { // index = 4; // break; } - alarmTypeType.value[index].value = Number(item.value); + alarmTypeType.value[index].value = Number(item.total); } } } @@ -119,7 +119,7 @@ function initPicture() { alarmTypeTypeTop.value = alarmTypeTypeTop.value .concat(res1.data) .sort((a: any, b: any) => { - return b.value - a.value; + return b.total - a.total; }) .slice(0, 3); } @@ -210,7 +210,7 @@ function initPicture() { { offset: 1, color: '#2f54eb' }, ]), // 渐变 }, - data: alarmTypeTypeTop.value, + data: alarmTypeTypeTop.value.map((item: any) => item.total), }, ], // 柱状图设置 @@ -237,7 +237,7 @@ function initPicture() { show: false, }, inverse: true, - data: alarmTypeTypeTop.value.map((item: any) => item.name), + data: alarmTypeTypeTop.value.map((item: any) => item.neType), axisLabel: { color: '#A7D6F4', fontSize: 14, diff --git a/src/views/dashboard/overview2/components/AlarnTypeBar/index.vue b/src/views/dashboard/overview2/components/AlarnTypeBar/index.vue new file mode 100644 index 00000000..a1d63140 --- /dev/null +++ b/src/views/dashboard/overview2/components/AlarnTypeBar/index.vue @@ -0,0 +1,284 @@ + + + + + diff --git a/src/views/dashboard/overview2/components/IMSActivity/index.vue b/src/views/dashboard/overview2/components/IMSActivity/index.vue new file mode 100644 index 00000000..ca2aa043 --- /dev/null +++ b/src/views/dashboard/overview2/components/IMSActivity/index.vue @@ -0,0 +1,268 @@ + + + + + diff --git a/src/views/dashboard/overview2/components/NeResources/index.vue b/src/views/dashboard/overview2/components/NeResources/index.vue new file mode 100644 index 00000000..669214c8 --- /dev/null +++ b/src/views/dashboard/overview2/components/NeResources/index.vue @@ -0,0 +1,352 @@ + + + + + diff --git a/src/views/dashboard/overview2/components/Topology/index.vue b/src/views/dashboard/overview2/components/Topology/index.vue new file mode 100644 index 00000000..1b9e04c8 --- /dev/null +++ b/src/views/dashboard/overview2/components/Topology/index.vue @@ -0,0 +1,337 @@ + + + + + diff --git a/src/views/dashboard/overview2/components/UPFFlow/index.vue b/src/views/dashboard/overview2/components/UPFFlow/index.vue new file mode 100644 index 00000000..ac864183 --- /dev/null +++ b/src/views/dashboard/overview2/components/UPFFlow/index.vue @@ -0,0 +1,291 @@ + + + + + diff --git a/src/views/dashboard/overview2/components/UserActivity/index.vue b/src/views/dashboard/overview2/components/UserActivity/index.vue new file mode 100644 index 00000000..614b19a2 --- /dev/null +++ b/src/views/dashboard/overview2/components/UserActivity/index.vue @@ -0,0 +1,324 @@ + + + + + diff --git a/src/views/dashboard/overview2/css/index.css b/src/views/dashboard/overview2/css/index.css new file mode 100644 index 00000000..35fff9fb --- /dev/null +++ b/src/views/dashboard/overview2/css/index.css @@ -0,0 +1,399 @@ +.viewport { + /* 限定大小 */ + min-width: 1024px; + max-width: 1920px; + min-height: 780px; + margin: 0 auto; + position: relative; + display: flex; + padding: 5rem 0.833rem 0; + line-height: 1.15; + background-image: url(../images/bj.png); + height: 100vh; + margin-bottom: -20px; + background-size:80% 80%; + background-attachment:fixed; + -webkit-background-size: cover; +} + + +.column { + flex: 3; + position: relative; + display: flex; + flex-direction: column; +} + +/* 边框 */ +.panel { + box-sizing: border-box; + border: 2px solid rgba(252, 252, 252, 0); + border-width: 2.125rem 1.583rem 0.875rem 5.5rem; + position: relative; + margin-bottom: 0.833rem; +} +.panel .inner { + /* 装内容 */ + /* height: 60px; */ + position: absolute; + top: -2.125rem; + right: -1.583rem; + bottom: -0.875rem; + left: -5.5rem; + padding: 1rem 1.5rem; +} +.panel h3 { + font-size: 0.833rem; + color: #fff; +} + +.leftright { + width: 100%; + min-height: 2.5rem; + background: url(../images/title.png) no-repeat center center; + background-size: 100%; + color: #4c9bfd; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + padding: 0.5rem 1.2rem; + border-radius: 0.7rem 0.7rem 0 0; + margin: 0; + box-sizing: border-box; + text-shadow: 0 1px 4px #000a; + flex-wrap: nowrap; + /* 保证内容不换行且居中 */ +} + +.leftright .title { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + font-size: 1rem; + padding-left: 0; +} + +.centerStyle { + width: 100%; + min-height: 2.5rem; + background: url(../images/title.png) no-repeat center center; + background-size: 90%; + color: #4c9bfd; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + padding: 0.5rem 1.2rem; + border-radius: 0.7rem 0.7rem 0 0; + margin: 0; + box-sizing: border-box; + text-shadow: 0 1px 4px #000a; + flex-wrap: nowrap; + /* 保证内容不换行且居中 */ +} + +.centerStyle .title { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + font-size: 1rem; + padding-left: 0; +} + +/* 总览标题 */ +.brand { + background-image: url(../images/newBrand.png); + background-repeat: no-repeat; + background-size: cover; + background-position: center center; + position: absolute; + top: 0.833rem; + left: 0; + right: 0; + width: 100%; + height: 5rem; + display: flex; + flex-direction: column; + align-items: center; + cursor: pointer; +} +.brand .brand-title { + color: #ffffff; + font-size: 1.4rem; + font-weight: 600; + padding-top: 1rem; + padding-bottom: 0.5rem; +} +.brand .brand-desc { + color: #d9d9d9; + font-size: 0.9rem; +} + +/* 实时流量 */ +.upfFlow { + /* min-height: 16rem; */ + height: 40%; +} +.upfFlow .inner .chart { + width: 100%; + height: 100%; + margin-top: 0rem; +} + +/* 网络拓扑 */ +.topology { + /* min-height: 27.8rem; */ + height: 46.4%; + flex: 1; +} +.topology .inner h3 { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: baseline; +} +.topology .inner h3 .normal { + color: #52c41a; + font-size: 1.1rem; + margin-right: 8px; +} +.topology .inner h3 .abnormal { + color: #f5222d; + font-size: 1.1rem; +} +.topology .inner .chart { + width: 100%; + height: 100%; + margin-top: 1rem; +} + +/* 概览区域 */ +.skim { + /* min-height: 7.78rem; */ + height: 14.4%; +} +.skim .inner .data { + display: flex; + flex-direction: row; + align-items: center; + height: 90%; +} +.skim .inner .data .item { + display: flex; + flex-direction: column; + align-items: baseline; + width: 33%; +} +.skim .inner .data .item div { + font-size: 1.467rem; + color: #fff; + margin-bottom: 0; + display: flex; + align-items: baseline; + line-height: 2rem; +} +.skim .inner .data .item span { + color: #4c9bfd; + font-size: 0.833rem; + width: 100%; + position: relative; + line-height: 2rem; + white-space: nowrap; + text-align: start; + text-overflow: ellipsis; + overflow: hidden; +} +.skim .inner .data .item span::before { + content: ' '; + position: absolute; + top: 2px; + left: 0; + right: 0; + bottom: 0; + z-index: 0; + background-image: linear-gradient(to right, #fff, #fff0); + height: 1px; + border-radius: 4px; +} + +/* 概览区域 衍生基站信息 */ +.skim.base { + height: 11.25%; +} + +.skim.base .inner .data { + display: flex; + flex-direction: row; + align-items: center; + height: 75%; +} +.skim.base .inner .data .item { + display: flex; + flex-direction: column; + align-items: baseline; + width: 50%; +} + +/* 用户行为 */ +.userActivity { + /* min-height: 35.8rem; */ + height: 54.6%; + flex: 1; +} +.userActivity .inner .chart { + width: 100%; + height: 100%; + margin-top: 1rem; +} + +/* 流量统计 */ +.upfFlowTotal1 { + /* min-height: 7.5rem; */ + height: 14.4%; +} +.upfFlowTotal1 .inner h3 { + display: flex; + justify-content: space-between; +} +.upfFlowTotal1 .inner h3 .filter { + display: flex; +} +.upfFlowTotal1 .inner h3 .filter span { + display: block; + height: 0.75rem; + line-height: 1; + padding: 0 0.75rem; + color: #1950c4; + font-size: 0.75rem; + border-right: 0.083rem solid #00f2f1; + cursor: pointer; +} +.upfFlowTotal1 .inner h3 .filter span:first-child { + padding-left: 0; +} +.upfFlowTotal1 .inner h3 .filter span:last-child { + border-right: none; +} +.upfFlowTotal1 .inner h3 .filter span.active { + color: #fff; + font-size: 0.833rem; +} +.upfFlowTotal1 .inner .chart { + width: 100%; + height: 100%; + margin-top: 0.1rem; +} +.upfFlowTotal1 .inner .chart .data { + display: flex; + flex-direction: column; + justify-content: space-around; + height: 60%; +} +.upfFlowTotal1 .inner .chart .data .item { + display: flex; + justify-content: space-between; + align-items: baseline; +} +.upfFlowTotal1 .inner .chart .data .item h4 { + font-size: 1.467rem; + color: #fff; + margin-bottom: 0; +} +.upfFlowTotal1 .inner .chart .data .item span { + color: #4c9bfd; + font-size: 0.867rem; +} + + +/* 流量统计 */ +.upfFlowTotal { + /* min-height: 7.5rem; */ + height: 14.4%; +} +.upfFlowTotal .inner h3 { + display: flex; + justify-content: space-between; +} +.upfFlowTotal .inner h3 .filter { + display: flex; +} +.upfFlowTotal .inner h3 .filter span { + display: block; + height: 0.75rem; + line-height: 1; + padding: 0 0.75rem; + color: #1950c4; + font-size: 0.75rem; + border-right: 0.083rem solid #00f2f1; + cursor: pointer; +} +.upfFlowTotal .inner h3 .filter span:first-child { + padding-left: 0; +} +.upfFlowTotal .inner h3 .filter span:last-child { + border-right: none; +} +.upfFlowTotal .inner h3 .filter span.active { + color: #fff; + font-size: 0.833rem; +} +.upfFlowTotal .inner .chart { + width: 100%; + height: 100%; + margin-top: 0.1rem; +} +.upfFlowTotal .inner .chart .data { + display: flex; + flex-direction: column; + justify-content: space-around; + height: 60%; +} +.upfFlowTotal .inner .chart .data .item { + display: flex; + justify-content: space-between; + align-items: baseline; +} +.upfFlowTotal .inner .chart .data .item h4 { + font-size: 1.467rem; + color: #fff; + margin-bottom: 0; +} +.upfFlowTotal .inner .chart .data .item span { + color: #4c9bfd; + font-size: 0.867rem; +} + +/* 资源情况 */ +.resources { + /* min-height: 18rem; */ + height: 24.4%; +} +.resources .inner .chart { + width: 100%; + height: 100%; + margin-top: 1rem; +} + + +/* 告警统计 */ +.alarmType { + /* min-height: 25rem; */ + height: 35%; +} +.alarmType .inner .chart { + width: 100%; + height: 100%; +} + +/* 跳转鼠标悬浮 */ +.toRouter:hover { + cursor: pointer; + color: #fff !important; +} +.toRouter:hover > *, +.toRouter:hover > * > * { + color: #fff !important; +} diff --git a/src/views/dashboard/overview2/hooks/useTopology.ts b/src/views/dashboard/overview2/hooks/useTopology.ts new file mode 100644 index 00000000..ff83a840 --- /dev/null +++ b/src/views/dashboard/overview2/hooks/useTopology.ts @@ -0,0 +1,197 @@ +import { parseDateToStr } from '@/utils/date-utils'; +import { computed, reactive, ref } from 'vue'; + +/**非网元元素 */ +export const notNeNodes = [ + '5GC', + 'DN', + 'UE', + 'Base', + 'lan', + 'lan1', + 'lan2', + 'lan3', + 'lan4', + 'lan5', + 'lan6', + 'lan7', + 'LAN', + 'NR', +]; + +/**图状态 */ +export const graphState = reactive>({ + /**当前图组名 */ + group: '5GC System Architecture', + /**图数据 */ + data: { + combos: [], + edges: [], + nodes: [], + }, +}); + +/**图实例对象 */ +export const graphG6 = ref(null); + +/**图点击选择 */ +export const graphNodeClickID = ref('UPF_001'); + +/**图节点网元信息状态 */ +export const graphNodeState = computed(() =>{ + return graphState.data.nodes.map((item: any) => ({ + id: item.id, + label: item.label, + neInfo: item.neInfo, + neState: item.neState, + neInfoList:item.neInfoList, + neStateMap: item.neStateMap, + })) +} + +); + +/**图节点网元状态数量 */ +export const graphNodeStateNum = computed(() => { + let normal = 0; + let abnormal = 0; + for (const item of graphState.data.nodes) { + const neId = item.neState.neId; + if (neId) { + if (item.neState.online) { + normal += 1; + } else { + abnormal += 1; + } + } + } + return [normal, abnormal]; +}); + +/**网元状态请求标记 */ +export const neStateRequestMap = ref>(new Map()); + +/**neStateParse 网元状态 数据解析 */ +export function neStateParse(neType: string, data: Record,neId: string) { + // console.log('neStateParse',neType, data, neId); + + const { combos, edges, nodes } = graphState.data; + + const node = nodes.find((item: Record) => item.id === neType); + //console.log('neStateParse',node); + + if (!node) return; + + // 初始化状态映射 + if (!node.neStateMap) node.neStateMap = {}; + + // 更新网元状态 + const newNeState :any = { + ...data, // 先展开data对象 + refreshTime: parseDateToStr(data.refreshTime, 'HH:mm:ss'), + online: !!data.cpu, + neId: neId + }; + // 如果是001,更新节点状态。neInfo为主要的网元信息 + if (node.neInfo && node.neInfo.neId === neId) { + Object.assign(node.neState, newNeState); + } + + //console.log(node.neState) + // 无论是否为主要网元,都更新状态映射 + node.neStateMap[neId] = {...newNeState}; + // 通过 ID 查询节点实例 + const item = graphG6.value.findById(node.id); + if (item) { + // 检查当前节点下所有网元的状态 + const allStates = Object.values(node.neStateMap); + // 判断状态颜色 + let stateColor = '#52c41a'; // 默认绿色(所有网元都正常) + if (allStates.some((state: any) => !state.online)) { + // 如果有任何一个网元不正常 + stateColor = allStates.every((state: any) => !state.online) ? '#f5222d' : '#faad14'; // 红色(全部不正常)或黄色(部分不正常) + } + + // 图片类型不能填充 + if (node.type && node.type.startsWith('image')) { + // 更新节点 + if (node.label !== newNeState.neType) { + graphG6.value.updateItem(item, { + label: newNeState.neType, + }); + } + // 设置状态 + graphG6.value.setItemState(item, 'top-right-dot', stateColor); + } else { + // 更新节点 + graphG6.value.updateItem(item, { + label: newNeState.neType, + style: { + fill: stateColor, // 填充色 + stroke: stateColor, // 填充色 + }, + }); + // 设置状态 + graphG6.value.setItemState(item, 'stroke', newNeState.online); + } + + } + + // 设置边状态 + for (const edge of edges) { + const edgeSource: string = edge.source; + const edgeTarget: string = edge.target; + const neS = nodes.find((n: any) => n.id === edgeSource); + const neT = nodes.find((n: any) => n.id === edgeTarget); + // console.log(neS, edgeSource, neT, edgeTarget); + + if (neS && neT) { + // 通过 ID 查询节点实例 + // const item = graphG6.value.findById(edge.id); + // console.log( + // `${edgeSource} - ${edgeTarget}`, + // neS.neState.online && neT.neState.online + // ); + // const stateColor = neS.neState.online && neT.neState.online ? '#000000' : '#ff4d4f'; // 状态颜色 + // 更新边 + // graphG6.value.updateItem(item, { + // label: `${edgeSource} - ${edgeTarget}`, + // style: { + // stroke: stateColor, // 填充色 + // }, + // labelCfg: { + // style: { + // fill: '#ffffff', // 标签文本色 + // }, + // }, + // }); + // 设置状态 + graphG6.value.setItemState( + edge.id, + 'circle-move', + neS.neState.online && neT.neState.online + ); + } + if (neS && notNeNodes.includes(edgeTarget)) { + graphG6.value.setItemState(edge.id, 'line-dash', neS.neState.online); + } + if (neT && notNeNodes.includes(edgeSource)) { + graphG6.value.setItemState(edge.id, 'line-dash', neT.neState.online); + } + } + + // 请求标记复位 + neStateRequestMap.value.set(neType, false); +} + +/**属性复位 */ +export function topologyReset() { + graphState.data = { + combos: [], + edges: [], + nodes: [], + }; + graphG6.value = null; + graphNodeClickID.value = 'UPF_001'; + neStateRequestMap.value = new Map(); +} diff --git a/src/views/dashboard/overview2/hooks/useUPFTotalFlow.ts b/src/views/dashboard/overview2/hooks/useUPFTotalFlow.ts new file mode 100644 index 00000000..4366405d --- /dev/null +++ b/src/views/dashboard/overview2/hooks/useUPFTotalFlow.ts @@ -0,0 +1,110 @@ +import { parseDateToStr } from '@/utils/date-utils'; +import { parseSizeFromBits, parseSizeFromKbs } from '@/utils/parse-utils'; +import { ref } from 'vue'; + +type FDType = { + /**时间 */ + lineXTime: string[]; + /**上行 N3 */ + lineYUp: number[]; + /**下行 N6 */ + lineYDown: number[]; + /**容量 */ + cap: number; +}; + +/**UPF-流量数据 */ +export const upfFlowData = ref({ + lineXTime: [], + lineYUp: [], + lineYDown: [], + cap: 0, +}); + +/**UPF-流量数据 数据解析 */ +export function upfFlowParse(data: Record) { + upfFlowData.value.lineXTime.push(parseDateToStr(+data['timeGroup'], 'HH:mm:ss')); + const upN3 = parseSizeFromKbs(+data['UPF.03'], 5); + upfFlowData.value.lineYUp.push(upN3[0]); + const downN6 = parseSizeFromKbs(+data['UPF.06'], 5); + upfFlowData.value.lineYDown.push(downN6[0]); + upfFlowData.value.cap += 1; + // 超过 25 弹出 + if (upfFlowData.value.cap > 25) { + upfFlowData.value.lineXTime.shift(); + upfFlowData.value.lineYUp.shift(); + upfFlowData.value.lineYDown.shift(); + upfFlowData.value.cap -= 1; + } +} + +type TFType = { + /**上行 N3 */ + up: number; + upFrom: string; + /**下行 N6 */ + down: number; + downFrom: string; + /**请求标记 */ + requestFlag: boolean; +}; + +/**UPF-总流量数 */ +export const upfTotalFlow = ref>({ + '0': { + up: 0, + upFrom: '0 B', + down: 0, + downFrom: '0 B', + requestFlag: false, + }, + '7': { + up: 0, + upFrom: '0 B', + down: 0, + downFrom: '0 B', + requestFlag: false, + }, + '30': { + up: 0, + upFrom: '0 B', + down: 0, + downFrom: '0 B', + requestFlag: false, + }, +}); + +/**UPF-总流量数 数据解析 */ +export function upfTFParse(day: string, data: Record) { + let { up, down } = data; + upfTotalFlow.value[day] = { + up: up, + upFrom: parseSizeFromBits(up), + down: down, + downFrom: parseSizeFromBits(down), + requestFlag: false, + }; +} + +/**UPF-总流量数 选中 */ +export const upfTFActive = ref('0'); + +/**属性复位 */ +export function upfTotalFlowReset() { + upfFlowData.value = { + lineXTime: [], + lineYUp: [], + lineYDown: [], + cap: 0, + }; + for (const key of Object.keys(upfTotalFlow.value)) { + upfTotalFlow.value[key] = { + up: 0, + upFrom: '0 B', + down: 0, + downFrom: '0 B', + requestFlag: false, + }; + } + upfTFActive.value = '0'; +} diff --git a/src/views/dashboard/overview2/hooks/useUserActivity.ts b/src/views/dashboard/overview2/hooks/useUserActivity.ts new file mode 100644 index 00000000..76ffd23e --- /dev/null +++ b/src/views/dashboard/overview2/hooks/useUserActivity.ts @@ -0,0 +1,144 @@ +import { ref } from 'vue'; + +/**ueEventAMFParse UE会话事件AMF 数据解析 */ +function ueEventAMFParse( + item: Record +): false | Record { + let evData: Record = item.eventJSON; + if (typeof evData === 'string') { + try { + evData = JSON.parse(evData); + } catch (error) { + console.error(error); + } + } + + return { + eType: 'amf_ue', + eId: `amf_ue_${item.id}_${Date.now()}`, + eTime: +item.timestamp, + id: item.id, + type: item.eventType, + data: evData, + }; +} + +/**ueEventMMEParse UE会话事件MME 数据解析 */ +function ueEventMMEParse( + item: Record +): false | Record { + let evData: Record = item.eventJSON; + if (typeof evData === 'string') { + try { + evData = JSON.parse(evData); + } catch (error) { + console.error(error); + } + } + + return { + eType: 'mme_ue', + eId: `mme_ue_${item.id}_${Date.now()}`, + eTime: +item.timestamp, + id: item.id, + type: item.eventType, + data: evData, + }; +} + +/**cdrEventIMSParse CDR会话事件IMS 数据解析 */ +function cdrEventIMSParse( + item: Record +): false | Record { + let evData: Record = item.cdrJSON || item.CDR; + if (typeof evData === 'string') { + try { + evData = JSON.parse(evData); + } catch (error) { + console.error(error); + return false; + } + } + + // 指定显示CDR类型MOC/MTSM + if (!['MOC', 'MTSM'].includes(evData.recordType)) { + return false; + } + + return { + eType: 'ims_cdr', + eId: `ims_cdr_${item.id}_${Date.now()}`, + eTime: +item.timestamp, + id: item.id, + data: evData, + }; +} + +/**eventListParse 事件列表解析 */ +export function eventListParse( + type: 'ims_cdr' | 'amf_ue' | 'mme_ue', + data: any +) { + eventTotal.value += data.total; + for (const item of data.rows) { + let v: false | Record = false; + if (type === 'ims_cdr') { + v = cdrEventIMSParse(item); + } + if (type === 'amf_ue') { + v = ueEventAMFParse(item); + } + if (type === 'mme_ue') { + v = ueEventMMEParse(item); + } + + if (v) { + eventData.value.push(v); + } + } + // 激活选中 + if (eventData.value.length > 0) { + eventId.value = eventData.value[0].eId; + } +} + +/**eventItemParseAndPush 事件项解析并添加 */ +export async function eventItemParseAndPush( + type: 'ims_cdr' | 'amf_ue' | 'mme_ue', + item: any +) { + let v: false | Record = false; + if (type === 'ims_cdr') { + v = cdrEventIMSParse(item); + } + if (type === 'amf_ue') { + v = ueEventAMFParse(item); + } + if (type === 'mme_ue') { + v = ueEventMMEParse(item); + } + + if (v) { + eventData.value.unshift(v); + eventTotal.value += 1; + eventId.value = v.eId; + await new Promise(resolve => setTimeout(resolve, 800)); + if (eventData.value.length > 20) { + eventData.value.pop(); + } + } +} + +/**CDR+UE事件数据 */ +export const eventData = ref[]>([]); +/**CDR+UE事件总量 */ +export const eventTotal = ref(0); +/**CDR/UE事件推送id */ +export const eventId = ref(''); + +/**属性复位 */ +export function userActivityReset() { + eventData.value = []; + eventTotal.value = 0; + eventId.value = ''; +} diff --git a/src/views/dashboard/overview2/hooks/useWS.ts b/src/views/dashboard/overview2/hooks/useWS.ts new file mode 100644 index 00000000..6b589dd1 --- /dev/null +++ b/src/views/dashboard/overview2/hooks/useWS.ts @@ -0,0 +1,224 @@ +import { RESULT_CODE_ERROR } from '@/constants/result-constants'; +import { OptionsType, WS } from '@/plugins/ws-websocket'; +import { onBeforeUnmount, ref } from 'vue'; +import { + eventData, + eventListParse, + eventItemParseAndPush, + userActivityReset, +} from './useUserActivity'; +import { + upfTotalFlow, + upfTFParse, + upfFlowParse, + upfTotalFlowReset, +} from './useUPFTotalFlow'; +import { topologyReset, neStateParse, neStateRequestMap } from './useTopology'; +import PQueue from 'p-queue'; + +/**UPF-的Id */ +export const upfWhoId = ref(''); + +/**UPF-的RmUid */ +export const upfWhoRmUid = ref(''); + +/**websocket连接 */ +export default function useWS() { + const ws = new WS(); + const queue = new PQueue({ concurrency: 1, autoStart: true }); + + /**发消息 */ + function wsSend(data: Record) { + ws.send(data); + } + + /**接收数据后回调 */ + function wsMessage(res: Record) { + const { code, requestId, data } = res; + if (code === RESULT_CODE_ERROR) { + console.warn(res.msg); + return; + } + // 网元状态 + if (requestId && requestId.startsWith('neState')) { + const neType = requestId.split('_')[1]; + const neId = requestId.split('_')[2]; + neStateParse(neType, data,neId); + return; + } + + // 普通信息 + switch (requestId) { + // AMF_UE会话事件 + case 'amf_1010_001': + if (Array.isArray(data.rows)) { + eventListParse('amf_ue', data); + eventData.value.sort((a, b) => b.eTime - a.eTime); + } + break; + // MME_UE会话事件 + case 'mme_1011_001': + if (Array.isArray(data.rows)) { + eventListParse('mme_ue', data); + eventData.value.sort((a, b) => b.eTime - a.eTime); + } + break; + // IMS_CDR会话事件 + case 'ims_1005_001': + if (Array.isArray(data.rows)) { + eventListParse('ims_cdr', data); + eventData.value.sort((a, b) => b.eTime - a.eTime); + } + break; + //UPF-总流量数 + case 'upf_001_0': + upfTFParse('0', data); + break; + case 'upf_001_7': + upfTFParse('7', data); + break; + case 'upf_001_30': + upfTFParse('30', data); + break; + } + // 订阅组信息 + if (!data?.groupId) { + return; + } + switch (data.groupId) { + // kpiEvent 指标UPF + case '10_UPF_' + upfWhoId.value: + if (data.data) { + upfFlowParse(data.data); + } + break; + // AMF_UE会话事件 + case '1010_001': + if (data.data) { + queue.add(() => eventItemParseAndPush('amf_ue', data.data)); + } + break; + // MME_UE会话事件 + case '1011_001': + if (data.data) { + queue.add(() => eventItemParseAndPush('mme_ue', data.data)); + } + break; + // IMS_CDR会话事件 + case '1005_001': + if (data.data) { + queue.add(() => eventItemParseAndPush('ims_cdr', data.data)); + } + break; + } + } + + /**UPF-总流量数 发消息*/ + function upfTFSend(day: '0' | '7' | '30') { + // 请求标记检查避免重复发送 + if (upfTotalFlow.value[day].requestFlag) { + return; + } + upfTotalFlow.value[day].requestFlag = true; + + ws.send({ + requestId: `upf_001_${day}`, + type: 'upf_tf', + data: { + neType: 'UPF', + neId: '001', + day: Number(day), + }, + }); + } + + /**userActivitySend 用户行为事件基础列表数据 发消息*/ + function userActivitySend() { + // AMF_UE会话事件 + ws.send({ + requestId: 'amf_1010_001', + type: 'amf_ue', + data: { + neType: 'AMF', + neId: '001', + sortField: 'timestamp', + sortOrder: 'desc', + pageNum: 1, + pageSize: 20, + }, + }); + // MME_UE会话事件 + ws.send({ + requestId: 'mme_1011_001', + type: 'mme_ue', + data: { + neType: 'MME', + neId: '001', + sortField: 'timestamp', + sortOrder: 'desc', + pageNum: 1, + pageSize: 20, + }, + }); + // IMS_CDR会话事件 + ws.send({ + requestId: 'ims_1005_001', + type: 'ims_cdr', + data: { + neType: 'IMS', + neId: '001', + recordType: 'MOC', + sortField: 'timestamp', + sortOrder: 'desc', + pageNum: 1, + pageSize: 20, + }, + }); + } + + /**重新发送至UPF 10_UPF_neId */ + function reSendUPF(neId: string) { + upfWhoId.value = neId; + //初始时时无需还原全部属性以及关闭 + if (ws.state() === WebSocket.OPEN) { + ws.close(); + // userActivityReset(); + upfTotalFlowReset(); + neStateRequestMap.value = new Map(); + //topologyReset(); + } + const options: OptionsType = { + url: '/ws', + params: { + /**订阅通道组 + * + * 指标UPF (GroupID:10_neType_neId) + * AMF_UE会话事件(GroupID:1010_neId) + * MME_UE会话事件(GroupID:1011_neId) + * IMS_CDR会话事件(GroupID:1005_neId) + */ + subGroupID: '10_UPF_' + neId + ',1010_001,1011_001,1005_001', + }, + onmessage: wsMessage, + onerror: (ev: any) => { + console.error(ev); + }, + }; + ws.connect(options); + } + + onBeforeUnmount(() => { + ws.close(); + userActivityReset(); + upfTotalFlowReset(); + topologyReset(); + upfWhoRmUid.value = ''; + }); + + return { + wsSend, + userActivitySend, + upfTFSend, + reSendUPF, + }; +} diff --git a/src/views/dashboard/overview2/images/bj.png b/src/views/dashboard/overview2/images/bj.png new file mode 100644 index 00000000..a1de24e7 Binary files /dev/null and b/src/views/dashboard/overview2/images/bj.png differ diff --git a/src/views/dashboard/overview2/images/border.png b/src/views/dashboard/overview2/images/border.png new file mode 100644 index 00000000..b854cea4 Binary files /dev/null and b/src/views/dashboard/overview2/images/border.png differ diff --git a/src/views/dashboard/overview2/images/brand.png b/src/views/dashboard/overview2/images/brand.png new file mode 100644 index 00000000..2f6fb6a0 Binary files /dev/null and b/src/views/dashboard/overview2/images/brand.png differ diff --git a/src/views/dashboard/overview2/images/line.png b/src/views/dashboard/overview2/images/line.png new file mode 100644 index 00000000..34e99aca Binary files /dev/null and b/src/views/dashboard/overview2/images/line.png differ diff --git a/src/views/dashboard/overview2/images/newBrand.png b/src/views/dashboard/overview2/images/newBrand.png new file mode 100644 index 00000000..c7311574 Binary files /dev/null and b/src/views/dashboard/overview2/images/newBrand.png differ diff --git a/src/views/dashboard/overview2/images/rect.png b/src/views/dashboard/overview2/images/rect.png new file mode 100644 index 00000000..6c0ebf00 Binary files /dev/null and b/src/views/dashboard/overview2/images/rect.png differ diff --git a/src/views/dashboard/overview2/images/title.png b/src/views/dashboard/overview2/images/title.png new file mode 100644 index 00000000..797b3351 Binary files /dev/null and b/src/views/dashboard/overview2/images/title.png differ diff --git a/src/views/dashboard/overview2/index.vue b/src/views/dashboard/overview2/index.vue new file mode 100644 index 00000000..f4645ce5 --- /dev/null +++ b/src/views/dashboard/overview2/index.vue @@ -0,0 +1,783 @@ + + + + + diff --git a/src/views/faultManage/alarm-overview/images/bg.png b/src/views/faultManage/alarm-overview/images/bg.png new file mode 100644 index 00000000..2c52f488 Binary files /dev/null and b/src/views/faultManage/alarm-overview/images/bg.png differ diff --git a/src/views/faultManage/alarm-overview/index.vue b/src/views/faultManage/alarm-overview/index.vue new file mode 100644 index 00000000..0489e6c0 --- /dev/null +++ b/src/views/faultManage/alarm-overview/index.vue @@ -0,0 +1,1338 @@ + + + + + diff --git a/src/views/perfManage/overview/index.vue b/src/views/perfManage/overview/index.vue new file mode 100644 index 00000000..27f9dfa3 --- /dev/null +++ b/src/views/perfManage/overview/index.vue @@ -0,0 +1,1328 @@ + + + + +