954 lines
29 KiB
Vue
954 lines
29 KiB
Vue
<script setup lang="ts">
|
||
import { onBeforeUnmount, onMounted, reactive, ref, nextTick } from 'vue';
|
||
import svgBase from '@/assets/svg/base.svg';
|
||
import svgUserIMS from '@/assets/svg/userIMS.svg';
|
||
import svgUserSMF from '@/assets/svg/userSMF.svg';
|
||
import useI18n from '@/hooks/useI18n';
|
||
import Topology from './components/Topology/index.vue';
|
||
import NeResources from './components/NeResources/index.vue';
|
||
import UserActivity from './components/UserActivity/index.vue';
|
||
import AlarnTypeBar from './components/AlarnTypeBar/index.vue';
|
||
import UPFFlow from './components/UPFFlow/index.vue';
|
||
import { listUDMSub } from '@/api/neData/udm_sub';
|
||
import { listUENumBySMF } from '@/api/neUser/smf';
|
||
import { listUENumByIMS } from '@/api/neUser/ims';
|
||
import { listBase5G } from '@/api/neUser/base5G';
|
||
import { listIMSSub } from '@/api/neData/ims_sub';
|
||
import { listUDMAuth } from '@/api/neData/voip_auth';
|
||
import {
|
||
graphNodeClickID,
|
||
graphState,
|
||
notNeNodes,
|
||
graphNodeStateNum,
|
||
neStateRequestMap,
|
||
} from './hooks/useTopology';
|
||
import { upfTotalFlow, upfTFActive } from './hooks/useUPFTotalFlow';
|
||
import { useFullscreen } from '@vueuse/core';
|
||
import useWS from './hooks/useWS';
|
||
import useAppStore from '@/store/modules/app';
|
||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||
import { useRouter } from 'vue-router';
|
||
import useNeInfoStore from '@/store/modules/neinfo';
|
||
import { message } from 'ant-design-vue';
|
||
import { upfWhoId } from './hooks/useWS';
|
||
|
||
const neInfoStore = useNeInfoStore();
|
||
const router = useRouter();
|
||
const appStore = useAppStore();
|
||
const { t } = useI18n();
|
||
const { wsSend, userActivitySend, upfTFSend, reSendUPF } = useWS();
|
||
|
||
// const viewportDom = ref<HTMLElement | null>(null);
|
||
//
|
||
// // 定义 resize 处理函数,确保能正确移除事件监听器
|
||
// const handleResize = () => {
|
||
// setTimeout(calculateScale, 100);
|
||
// };
|
||
//
|
||
//
|
||
//
|
||
//
|
||
//
|
||
// // 计算缩放比例的函数
|
||
// const calculateScale = () => {
|
||
// const container = document.querySelector('.dashboard-container') as HTMLElement;
|
||
// const wrapper = document.querySelector('.dashboard-wrapper') as HTMLElement;
|
||
//
|
||
// if (!container || !wrapper) return;
|
||
//
|
||
// const containerWidth = 1400; // 设计宽度
|
||
// const containerHeight = 900; // 设计高度
|
||
//
|
||
// // 获取可用空间(减去padding)
|
||
// const availableWidth = wrapper.clientWidth - 40;
|
||
// const availableHeight = wrapper.clientHeight - 40;
|
||
//
|
||
// // 计算缩放比例
|
||
// const scaleX = availableWidth / containerWidth;
|
||
// const scaleY = availableHeight / containerHeight;
|
||
//
|
||
// // 选择较小的比例,确保内容完全可见
|
||
// const scale = Math.min(scaleX, scaleY);
|
||
//
|
||
//
|
||
// container.style.transform = `scale(${scale})`;
|
||
// container.style.transformOrigin = 'center center';
|
||
// };
|
||
|
||
/**概览状态类型 */
|
||
type SkimStateType = {
|
||
/**UDM签约用户数量 */
|
||
udmSubNum: number;
|
||
/**SMF在线用户数 */
|
||
smfUeNum: number;
|
||
/**IMS在线用户数 */
|
||
imsUeNum: number;
|
||
/**5G基站数量 */
|
||
gnbNum: number;
|
||
/**5G在线用户数量 */
|
||
gnbUeNum: number;
|
||
/**4G基站数量 */
|
||
enbNum: number;
|
||
/**4G在线用户数量 */
|
||
enbUeNum: number;
|
||
/**IMS用户数量 */
|
||
imsUserNum: number;
|
||
/**VOIP用户数量 */
|
||
voipUserNum: number;
|
||
};
|
||
|
||
/**概览状态信息 */
|
||
let skimState: SkimStateType = reactive({
|
||
udmSubNum: 0,
|
||
smfUeNum: 0,
|
||
imsUeNum: 0,
|
||
gnbNum: 0,
|
||
gnbUeNum: 0,
|
||
enbNum: 0,
|
||
enbUeNum: 0,
|
||
imsUserNum: 0,
|
||
voipUserNum: 0,
|
||
});
|
||
|
||
/**网元参数 */
|
||
let neCascaderOptions = ref<Record<string, any>[]>([]);
|
||
|
||
/**总览节点 */
|
||
const viewportDom = ref<HTMLElement | null>(null);
|
||
const { isFullscreen, toggle } = useFullscreen(viewportDom);
|
||
|
||
let initFlag = false;
|
||
/**10s调度器 */
|
||
const interval10s = ref<any>(null);
|
||
|
||
/**5s调度器 */
|
||
const interval5s = ref<any>(null);
|
||
|
||
/**查询网元状态 */
|
||
function fnGetNeState() {
|
||
// 获取节点状态
|
||
for (const node of graphState.data.nodes) {
|
||
if (notNeNodes.includes(node.id)) continue;
|
||
|
||
const neInfoList = node.neInfoList || [];
|
||
if (neInfoList.length === 0) continue;
|
||
|
||
for (const neInfo of neInfoList) {
|
||
if (!neInfo.neType || !neInfo.neId) continue;
|
||
|
||
wsSend({
|
||
requestId: `neState_${neInfo.neType}_${neInfo.neId}`,
|
||
type: 'ne_state',
|
||
data: {
|
||
neType: neInfo.neType,
|
||
neId: neInfo.neId,
|
||
},
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
/**获取概览信息 */
|
||
async function fnGetSkim() {
|
||
const neHandlers = new Map([
|
||
// [
|
||
// 'UDM',
|
||
// {
|
||
// request: (neId: string) =>
|
||
// listUDMSub({ neId: neId, pageNum: 1, pageSize: 1 }),
|
||
// process: (res: any) =>
|
||
// res.code === RESULT_CODE_SUCCESS &&
|
||
// (skimState.udmSubNum += res.total),
|
||
// },
|
||
// ],
|
||
[
|
||
'SMF',
|
||
{
|
||
request: (neId: string) => listUENumBySMF(neId),
|
||
process: (res: any) =>
|
||
res.code === RESULT_CODE_SUCCESS && (skimState.smfUeNum += res.data),
|
||
},
|
||
],
|
||
[
|
||
'IMS',
|
||
{
|
||
request: (neId: string) => listUENumByIMS(neId),
|
||
process: (res: any) =>
|
||
res.code === RESULT_CODE_SUCCESS && (skimState.imsUeNum += res.data),
|
||
},
|
||
],
|
||
[
|
||
'AMF',
|
||
{
|
||
request: (neId: string) => listBase5G({ neType: 'AMF', neId }),
|
||
process: (res: any) => {
|
||
if (res.code === RESULT_CODE_SUCCESS) {
|
||
skimState.gnbNum += res.total;
|
||
skimState.gnbUeNum += res.rows.reduce(
|
||
(sum: number, item: any) => sum + item.ueNum,
|
||
0
|
||
);
|
||
}
|
||
},
|
||
},
|
||
],
|
||
[
|
||
'MME',
|
||
{
|
||
request: (neId: string) => listBase5G({ neType: 'MME', neId }),
|
||
process: (res: any) => {
|
||
if (res.code === RESULT_CODE_SUCCESS) {
|
||
skimState.enbNum += res.total;
|
||
skimState.enbUeNum += res.rows.reduce(
|
||
(sum: number, item: any) => sum + item.ueNum,
|
||
0
|
||
);
|
||
}
|
||
},
|
||
},
|
||
],
|
||
]);
|
||
|
||
const requests = neCascaderOptions.value.flatMap(
|
||
(ne: any) =>
|
||
ne.children
|
||
?.map((child: any) => {
|
||
const handler = neHandlers.get(child.neType);
|
||
return handler
|
||
? {
|
||
promise: handler.request(child.neId),
|
||
process: handler.process,
|
||
}
|
||
: null;
|
||
})
|
||
.filter(Boolean) || []
|
||
);
|
||
|
||
const results = await Promise.allSettled(requests.map(r => r.promise));
|
||
|
||
// 重置
|
||
Object.assign(skimState, {
|
||
// udmSubNum: 0,
|
||
smfUeNum: 0,
|
||
imsUeNum: 0,
|
||
gnbNum: 0,
|
||
gnbUeNum: 0,
|
||
enbNum: 0,
|
||
enbUeNum: 0,
|
||
// imsUserNum: 0,
|
||
// voipUserNum: 0,
|
||
});
|
||
results.forEach((result: any, index: any) => {
|
||
if (result.status === 'fulfilled') {
|
||
requests[index].process(result.value);
|
||
} else {
|
||
requests[index].process(0);
|
||
}
|
||
});
|
||
|
||
// UDM
|
||
listUDMSub({ neId: udmNeId.value, pageNum: 1, pageSize: 1 }).then(res => {
|
||
if (res.code === RESULT_CODE_SUCCESS) {
|
||
skimState.udmSubNum = res.total;
|
||
}
|
||
});
|
||
|
||
// IMS用户数
|
||
listIMSSub({ neId: udmNeId.value, pageNum: 1, pageSize: 1 }).then(res => {
|
||
if (res.code === RESULT_CODE_SUCCESS) {
|
||
skimState.imsUserNum = res.total;
|
||
}
|
||
});
|
||
|
||
// VOIP用户数
|
||
listUDMAuth({ neId: udmNeId.value, pageNum: 1, pageSize: 1 }).then(res => {
|
||
if (res.code === RESULT_CODE_SUCCESS) {
|
||
skimState.voipUserNum = res.total;
|
||
}
|
||
});
|
||
}
|
||
|
||
/**初始数据函数 */
|
||
function loadData() {
|
||
fnGetNeState(); // 获取网元状态
|
||
userActivitySend();
|
||
upfTFSend('0');
|
||
upfTFSend('7');
|
||
upfTFSend('30');
|
||
|
||
clearInterval(interval10s.value);
|
||
interval10s.value = setInterval(() => {
|
||
if (!interval10s.value) return;
|
||
if (upfTFActive.value === '0') {
|
||
upfTFActive.value = '7';
|
||
} else if (upfTFActive.value === '7') {
|
||
upfTFActive.value = '30';
|
||
} else if (upfTFActive.value === '30') {
|
||
upfTFActive.value = '0';
|
||
}
|
||
upfTFSend('0');
|
||
upfTFSend('7');
|
||
upfTFSend('30');
|
||
}, 10_000);
|
||
|
||
clearInterval(interval5s.value);
|
||
interval5s.value = setInterval(() => {
|
||
if (!interval5s.value || !initFlag) return;
|
||
fnGetSkim(); // 获取概览信息
|
||
fnGetNeState(); // 获取网元状态
|
||
}, 10_000);
|
||
}
|
||
|
||
/**栏目信息跳转 */
|
||
function fnToRouter(name: string, query?: any) {
|
||
router.push({ name, query });
|
||
}
|
||
|
||
/**网元参数 */
|
||
let neOtions = ref<Record<string, any>[]>([]);
|
||
|
||
// UPF实时流量下拉框选择
|
||
function fnSelectNe(value: any, option: any) {
|
||
upfWhoId.value = value;
|
||
reSendUPF(value);
|
||
// upfTotalFlow.value.map((item: any) => {
|
||
// item.requestFlag = false;
|
||
// });
|
||
|
||
for (var key in upfTotalFlow.value) {
|
||
upfTotalFlow.value[key].requestFlag = false;
|
||
}
|
||
// loadData();
|
||
}
|
||
|
||
let udmNeId = ref<string>('001');
|
||
let udmOtions = ref<Record<string, any>[]>([]);
|
||
let onlineOtions = ref<Record<string, any>[]>([]);
|
||
|
||
/**用户数量-选择UDM */
|
||
function fnSelectUDM(e: any) {
|
||
udmNeId.value = e.key;
|
||
listUDMSub({ neId: udmNeId.value, pageNum: 1, pageSize: 1 }).then(res => {
|
||
if (res.code === RESULT_CODE_SUCCESS) {
|
||
skimState.udmSubNum = res.total;
|
||
}
|
||
});
|
||
|
||
// 更新IMS用户数
|
||
listIMSSub({ neId: udmNeId.value, pageNum: 1, pageSize: 1 }).then(res => {
|
||
if (res.code === RESULT_CODE_SUCCESS) {
|
||
skimState.imsUserNum = res.total;
|
||
}
|
||
});
|
||
|
||
// 更新VOIP用户数
|
||
listUDMAuth({ neId: udmNeId.value, pageNum: 1, pageSize: 1 }).then(res => {
|
||
if (res.code === RESULT_CODE_SUCCESS) {
|
||
skimState.voipUserNum = res.total;
|
||
}
|
||
});
|
||
}
|
||
/**资源控制-选择NE */
|
||
function fnSelectNeRe(e: any) {
|
||
graphNodeClickID.value = e.key;
|
||
}
|
||
//
|
||
// 定义一个方法返回 views 容器
|
||
const getPopupContainer = () => {
|
||
// 使用 ref 或其他方式来引用你的 views 容器
|
||
// 如果 views 容器直接在这个组件内部,你可以使用 ref
|
||
// 但在这个例子中,我们假设它是通过类名来获取的
|
||
// return document.querySelector('.dashboard-wrapper');
|
||
return document.querySelector('.viewport');
|
||
};
|
||
|
||
onMounted(() => {
|
||
|
||
|
||
// // 使用自定义缩放方案
|
||
// nextTick(() => {
|
||
// // 确保DOM完全渲染
|
||
// setTimeout(() => {
|
||
// calculateScale();
|
||
// }, 100);
|
||
//
|
||
// // 监听窗口大小变化
|
||
// window.addEventListener('resize', handleResize);
|
||
// });
|
||
|
||
neInfoStore
|
||
.fnNelist()
|
||
.then(res => {
|
||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||
if (res.data.length > 0) {
|
||
// UPF
|
||
let arr: Record<string, any>[] = [];
|
||
res.data.forEach(i => {
|
||
if (i.neType === 'UPF') {
|
||
arr.push({ value: i.neId, label: i.neName, rmUid: i.rmUid });
|
||
}
|
||
});
|
||
neOtions.value = arr;
|
||
if (arr.length > 0) {
|
||
//queryParams.neRealId = arr[0].value;
|
||
fnSelectNe(arr[0].value, arr[0]);
|
||
}
|
||
//online Ne
|
||
let onlineArr: Record<string, any>[] = [];
|
||
|
||
// UDM
|
||
let arr1: Record<string, any>[] = [];
|
||
res.data.forEach((v: any) => {
|
||
if (
|
||
v.status &&
|
||
[
|
||
'UDM',
|
||
'UPF',
|
||
'AUSF',
|
||
'PCF',
|
||
'SMF',
|
||
'AMF',
|
||
'OMC',
|
||
'SMSC',
|
||
'IMS',
|
||
'MME',
|
||
].includes(v.neType)
|
||
) {
|
||
onlineArr.push({
|
||
value: v.neType + '_' + v.neId,
|
||
label: v.neName,
|
||
rmUid: v.rmUid,
|
||
});
|
||
}
|
||
if (v.neType === 'UDM') {
|
||
arr1.push({ value: v.neId, label: v.neName, rmUid: v.rmUid });
|
||
}
|
||
});
|
||
udmOtions.value = arr1;
|
||
onlineOtions.value = onlineArr;
|
||
if (arr1.length > 0) {
|
||
fnSelectUDM({ key: arr1[0].value });
|
||
}
|
||
|
||
if (onlineArr.length > 0) {
|
||
fnSelectNeRe({ key: onlineArr[0].value });
|
||
}
|
||
|
||
// 过滤不可用的网元
|
||
neCascaderOptions.value = neInfoStore.getNeCascaderOptions.filter(
|
||
(item: any) => {
|
||
return ['UDM', 'SMF', 'IMS', 'AMF', 'MME'].includes(item.value);
|
||
}
|
||
);
|
||
if (neCascaderOptions.value.length === 0) {
|
||
message.warning({
|
||
content: t('common.noData'),
|
||
duration: 2,
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
} else {
|
||
message.warning({
|
||
content: t('common.noData'),
|
||
duration: 2,
|
||
});
|
||
}
|
||
})
|
||
.finally(() => {
|
||
initFlag = true;
|
||
fnGetSkim().then(() => {
|
||
loadData();
|
||
});
|
||
});
|
||
});
|
||
|
||
onBeforeUnmount(() => {
|
||
clearInterval(interval10s.value);
|
||
interval10s.value = null;
|
||
clearInterval(interval5s.value);
|
||
interval5s.value = null;
|
||
initFlag = false;
|
||
// // 清理事件监听和样式
|
||
// window.removeEventListener('resize', handleResize);
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<!-- <div class="dashboard-wrapper">-->
|
||
<!-- <div class="dashboard-container">-->
|
||
<div class="viewport" ref="viewportDom">
|
||
<div class="brand">
|
||
<div
|
||
class="brand-title"
|
||
@click="toggle"
|
||
:title="t('views.dashboard.overview.fullscreen')"
|
||
>
|
||
{{ t('views.dashboard.overview.title') }}
|
||
<FullscreenExitOutlined v-if="isFullscreen" />
|
||
<FullscreenOutlined v-else />
|
||
</div>
|
||
<div class="brand-desc">{{ appStore.appName }}</div>
|
||
</div>
|
||
|
||
<div class="column">
|
||
<!--概览-->
|
||
<div class="skim panel">
|
||
<div class="inner" style="padding: 0.8rem 1.5rem">
|
||
<h3 style="display: flex; align-items: center; justify-content: space-between; margin: 0 0 0.3rem 0; padding: 0; line-height: 1; height: 20px; font-size: 0.833rem">
|
||
<span style="display: flex; align-items: center">
|
||
<IdcardOutlined style="color: #68d8fe" />
|
||
{{ t('views.dashboard.overview.skim.userTitle') }}
|
||
</span>
|
||
<a-dropdown
|
||
:trigger="['click']"
|
||
:get-Popup-Container="getPopupContainer"
|
||
style="margin-left: 8px"
|
||
>
|
||
<div class="toDeep-text" style="padding: 2px 6px; font-size: 0.7rem; line-height: 1; height: 16px; display: inline-flex; align-items: center">
|
||
{{ udmOtions.find(item => item.value === udmNeId)?.label || 'UDM' }}
|
||
<DownOutlined style="margin-left: 4px; font-size: 9px" />
|
||
</div>
|
||
<template #overlay>
|
||
<a-menu @click="fnSelectUDM">
|
||
<a-menu-item
|
||
v-for="v in udmOtions"
|
||
:key="v.value"
|
||
:disabled="udmNeId === v.value"
|
||
>
|
||
{{ v.label }}
|
||
</a-menu-item>
|
||
</a-menu>
|
||
</template>
|
||
</a-dropdown>
|
||
</h3>
|
||
<div class="data">
|
||
<div
|
||
class="item toRouter"
|
||
:title="t('views.dashboard.overview.Users')"
|
||
>
|
||
<div @click="fnToRouter('Sub_2010')">
|
||
<UserOutlined
|
||
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
|
||
/>
|
||
{{ skimState.udmSubNum }}
|
||
</div>
|
||
<span>{{ t('views.dashboard.overview.skim.users') }}
|
||
<!-- <a-dropdown-->
|
||
<!-- :trigger="['click']"-->
|
||
<!-- :get-Popup-Container="getPopupContainer"-->
|
||
<!-- >-->
|
||
<!-- <div class="toDeep-text">-->
|
||
<!-- {{ t('views.dashboard.overview.skim.users') }}-->
|
||
<!-- <DownOutlined style="margin-left: 12px; font-size: 12px" />-->
|
||
<!-- </div>-->
|
||
<!-- <template #overlay>-->
|
||
<!-- <a-menu @click="fnSelectUDM">-->
|
||
<!-- <a-menu-item-->
|
||
<!-- v-for="v in udmOtions"-->
|
||
<!-- :key="v.value"-->
|
||
<!-- :disabled="udmNeId === v.value"-->
|
||
<!-- >-->
|
||
<!-- {{ v.label }}-->
|
||
<!-- </a-menu-item>-->
|
||
<!-- </a-menu>-->
|
||
<!-- </template>-->
|
||
<!-- </a-dropdown>-->
|
||
</span>
|
||
</div>
|
||
<div
|
||
class="item toRouter"
|
||
@click="fnToRouter('ImsUDM_2012')"
|
||
:title="t('views.dashboard.overview.IMSUsers')"
|
||
style="margin: 0 12px"
|
||
>
|
||
<div>
|
||
<UserOutlined
|
||
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
|
||
/>
|
||
{{ skimState.imsUserNum }}
|
||
</div>
|
||
<span>
|
||
{{t('views.dashboard.overview.skim.ims')}}
|
||
</span>
|
||
</div>
|
||
<div
|
||
class="item toRouter"
|
||
@click="fnToRouter('Voip_2011')"
|
||
:title="t('views.dashboard.overview.VoIPUsers')"
|
||
>
|
||
<div>
|
||
<UserOutlined
|
||
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
|
||
/>
|
||
{{ skimState.voipUserNum }}
|
||
</div>
|
||
<span>
|
||
{{t('views.dashboard.overview.skim.voip')}}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="skim panel base" v-perms:has="['dashboard:overview:gnbBase']">
|
||
<div class="inner">
|
||
<h3>
|
||
<GlobalOutlined style="color: #68d8fe" /> 5G
|
||
{{ t('views.dashboard.overview.skim.baseTitle') }}
|
||
</h3>
|
||
<div class="data">
|
||
<div
|
||
class="item toRouter"
|
||
@click="fnToRouter('BaseStation_2096', { neType: 'AMF' })"
|
||
:title="t('views.dashboard.overview.FivegNodeN')"
|
||
>
|
||
<div style="align-items: flex-start">
|
||
<img
|
||
:src="svgBase"
|
||
style="width: 18px; margin-right: 8px; height: 2rem"
|
||
/>
|
||
{{ skimState.gnbNum }}
|
||
</div>
|
||
<span>{{ t('views.dashboard.overview.skim.gnbBase') }}</span>
|
||
</div>
|
||
<div
|
||
class="item toRouter"
|
||
@click="fnToRouter('BaseStation_2096', { neType: 'AMF' })"
|
||
:title="t('views.dashboard.overview.Fiveusers')"
|
||
>
|
||
<div style="align-items: flex-start">
|
||
<UserOutlined
|
||
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
|
||
/>
|
||
{{ skimState.gnbUeNum }}
|
||
</div>
|
||
<span>{{ t('views.dashboard.overview.skim.gnbUeNum') }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="skim panel base" v-perms:has="['dashboard:overview:enbBase']">
|
||
<div class="inner">
|
||
<h3>
|
||
<GlobalOutlined style="color: #68d8fe" /> 4G
|
||
{{ t('views.dashboard.overview.skim.baseTitle') }}
|
||
</h3>
|
||
<div class="data">
|
||
<div
|
||
class="item toRouter"
|
||
@click="fnToRouter('BaseStation_2096', { neType: 'MME' })"
|
||
:title="t('views.dashboard.overview.FourgNodeN')"
|
||
>
|
||
<div style="align-items: flex-start">
|
||
<img
|
||
:src="svgBase"
|
||
style="width: 18px; margin-right: 8px; height: 2rem"
|
||
/>
|
||
{{ skimState.enbNum }}
|
||
</div>
|
||
<span>{{ t('views.dashboard.overview.skim.enbBase') }}</span>
|
||
</div>
|
||
<div
|
||
class="item toRouter"
|
||
@click="fnToRouter('BaseStation_2096', { neType: 'MME' })"
|
||
:title="t('views.dashboard.overview.Fourusers')"
|
||
>
|
||
<div style="align-items: flex-start">
|
||
<UserOutlined
|
||
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
|
||
/>
|
||
{{ skimState.enbUeNum }}
|
||
</div>
|
||
<span>{{ t('views.dashboard.overview.skim.enbUeNum') }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Online Information -->
|
||
<div class="skim panel base" v-perms:has="['dashboard:overview:onlineInfo']">
|
||
<div class="inner">
|
||
<h3>
|
||
<IdcardOutlined style="color: #68d8fe" /> {{ t('views.dashboard.overview.skim.onlineinfo') }}
|
||
</h3>
|
||
<div class="data">
|
||
<div
|
||
class="item toRouter"
|
||
@click="fnToRouter('Ims_2080')"
|
||
:title="t('views.dashboard.overview.VoNR')"
|
||
v-perms:has="['dashboard:overview:imsUeNum']"
|
||
>
|
||
<div style="align-items: flex-start">
|
||
<img :src="svgUserIMS" style="width: 18px; margin-right: 8px; height: 2rem" />
|
||
{{ skimState.imsUeNum }}
|
||
</div>
|
||
<span>{{ t('views.dashboard.overview.skim.imsUeNum') }}</span>
|
||
</div>
|
||
<div
|
||
class="item toRouter"
|
||
@click="fnToRouter('Ue_2081')"
|
||
:title="t('views.dashboard.overview.sessions')"
|
||
v-perms:has="['dashboard:overview:smfUeNum']"
|
||
>
|
||
<div style="align-items: flex-start">
|
||
<img :src="svgUserSMF" style="width: 18px; margin-right: 8px; height: 2rem" />
|
||
{{ skimState.smfUeNum }}
|
||
</div>
|
||
<span>{{ t('views.dashboard.overview.skim.smfUeNum') }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 用户行为 -->
|
||
<!-- <div class="userActivity panel">-->
|
||
<!-- <div class="inner">-->
|
||
<!-- <h3>-->
|
||
<!-- <WhatsAppOutlined style="color: #68d8fe" /> -->
|
||
<!-- {{ t('views.dashboard.overview.userActivity.title') }}-->
|
||
<!-- </h3>-->
|
||
<!-- <div class="chart">-->
|
||
<!-- <UserActivity />-->
|
||
<!-- </div>-->
|
||
<!-- </div>-->
|
||
<!-- </div>-->
|
||
<div class="userActivity panel">
|
||
<div class="inner">
|
||
<h3 style="display: flex; align-items: center">
|
||
<DashboardOutlined style="color: #68d8fe" />
|
||
{{ t('views.dashboard.overview.resources.title') }}:
|
||
<a-dropdown
|
||
:trigger="['click']"
|
||
:get-Popup-Container="getPopupContainer"
|
||
>
|
||
<div class="toDeep-text">
|
||
{{ graphNodeClickID }}
|
||
<DownOutlined style="margin-left: 12px; font-size: 12px" />
|
||
</div>
|
||
<template #overlay>
|
||
<a-menu @click="fnSelectNeRe">
|
||
<a-menu-item v-for="v in onlineOtions" :key="v.value">
|
||
{{ v.label }}
|
||
</a-menu-item>
|
||
</a-menu>
|
||
</template>
|
||
</a-dropdown>
|
||
</h3>
|
||
<div class="chart">
|
||
<NeResources />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="column" style="flex: 4; margin: 1.333rem 0.833rem 0">
|
||
<!-- 实时流量 -->
|
||
<div class="upfFlow panel">
|
||
<div class="inner">
|
||
<h3
|
||
class="toRouter"
|
||
:title="t('views.dashboard.overview.UPFjump')"
|
||
style="display: flex; align-items: center"
|
||
>
|
||
<AreaChartOutlined style="color: #68d8fe" />
|
||
<span @click="fnToRouter('GoldTarget_2104')">{{
|
||
t('views.dashboard.overview.upfFlow.title')
|
||
}}</span>
|
||
<a-select
|
||
v-model:value="upfWhoId"
|
||
:options="neOtions"
|
||
:get-Popup-Container="getPopupContainer"
|
||
class="toDeep"
|
||
style="width: 100px; color: #fff; margin-left: auto"
|
||
@change="fnSelectNe"
|
||
/>
|
||
</h3>
|
||
|
||
<div class="chart">
|
||
<UPFFlow />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 网络拓扑 -->
|
||
<div class="topology panel">
|
||
<div class="inner">
|
||
<h3
|
||
class="toRouter"
|
||
@click="fnToRouter('TopologyArchitecture_2128')"
|
||
:title="t('views.dashboard.overview.Networkjump')"
|
||
>
|
||
<span>
|
||
<ApartmentOutlined style="color: #68d8fe" />
|
||
{{ t('views.dashboard.overview.topology.title') }}
|
||
</span>
|
||
<span>
|
||
{{ t('views.dashboard.overview.topology.normal') }}:
|
||
<span class="normal"> {{ graphNodeStateNum[0] }} </span>
|
||
{{ t('views.dashboard.overview.topology.abnormal') }}:
|
||
<span class="abnormal"> {{ graphNodeStateNum[1] }} </span>
|
||
</span>
|
||
</h3>
|
||
<div class="chart">
|
||
<Topology />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="column">
|
||
<!-- 流量统计 -->
|
||
<div class="upfFlowTotal panel">
|
||
<div class="inner">
|
||
<h3>
|
||
<span>
|
||
<SwapOutlined style="color: #68d8fe" />
|
||
{{ t('views.dashboard.overview.upfFlowTotal.title') }}
|
||
</span>
|
||
|
||
<!-- 筛选 -->
|
||
<div class="filter">
|
||
<span
|
||
:data-key="v"
|
||
:class="{ active: upfTFActive === v }"
|
||
v-for="v in ['0', '7', '30']"
|
||
:key="v"
|
||
@click="
|
||
() => {
|
||
upfTFActive = v;
|
||
}
|
||
"
|
||
>
|
||
{{
|
||
v === '0'
|
||
? '24' + t('common.units.hour')
|
||
: v + t('common.units.day')
|
||
}}
|
||
</span>
|
||
</div>
|
||
</h3>
|
||
<div class="chart">
|
||
<!-- 数据 -->
|
||
<div class="data">
|
||
<div class="item">
|
||
<span>
|
||
<ArrowUpOutlined style="color: #597ef7" />
|
||
{{ t('views.dashboard.overview.upfFlowTotal.up') }}
|
||
</span>
|
||
<h4>{{ upfTotalFlow[upfTFActive].upFrom }}</h4>
|
||
</div>
|
||
<div class="item">
|
||
<span>
|
||
<ArrowDownOutlined style="color: #52c41a" />
|
||
{{ t('views.dashboard.overview.upfFlowTotal.down') }}
|
||
</span>
|
||
<h4>{{ upfTotalFlow[upfTFActive].downFrom }}</h4>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 告警统计 -->
|
||
<div class="alarmType panel">
|
||
<div class="inner">
|
||
<h3
|
||
class="toRouter"
|
||
@click="fnToRouter('ActiveAlarm_2088')"
|
||
:title="t('views.dashboard.overview.Alarmjump')"
|
||
>
|
||
<PieChartOutlined style="color: #68d8fe" />
|
||
{{ t('views.dashboard.overview.alarmTypeBar.alarmSum') }}
|
||
</h3>
|
||
<div class="chart">
|
||
<AlarnTypeBar />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- <!– 资源情况 –>-->
|
||
<!-- <div class="resources panel">-->
|
||
<!-- <div class="inner">-->
|
||
<!-- <h3 style="display: flex; align-items: center">-->
|
||
<!-- <DashboardOutlined style="color: #68d8fe" /> -->
|
||
<!-- {{ t('views.dashboard.overview.resources.title') }}:-->
|
||
<!-- <a-dropdown-->
|
||
<!-- :trigger="['click']"-->
|
||
<!-- :get-Popup-Container="getPopupContainer"-->
|
||
<!-- >-->
|
||
<!-- <div class="toDeep-text">-->
|
||
<!-- {{ graphNodeClickID }}-->
|
||
<!-- <DownOutlined style="margin-left: 12px; font-size: 12px" />-->
|
||
<!-- </div>-->
|
||
<!-- <template #overlay>-->
|
||
<!-- <a-menu @click="fnSelectNeRe">-->
|
||
<!-- <a-menu-item v-for="v in onlineOtions" :key="v.value">-->
|
||
<!-- {{ v.label }}-->
|
||
<!-- </a-menu-item>-->
|
||
<!-- </a-menu>-->
|
||
<!-- </template>-->
|
||
<!-- </a-dropdown>-->
|
||
<!-- </h3>-->
|
||
<!-- <div class="chart">-->
|
||
<!-- <NeResources />-->
|
||
<!-- </div>-->
|
||
<!-- </div>-->
|
||
<!-- </div>-->
|
||
</div>
|
||
</div>
|
||
<!-- </div>-->
|
||
<!-- </div>-->
|
||
</template>
|
||
<style lang="less" scoped>
|
||
@import url('./css/index.css');
|
||
|
||
/* Dashboard 页面专用包装器,不影响其他页面 */
|
||
//.dashboard-wrapper {
|
||
// position: absolute;
|
||
// top: 0;
|
||
// left: 0;
|
||
// width: 100%;
|
||
// height: 100%;
|
||
// background: #0b1023;
|
||
// display: flex;
|
||
// align-items: center;
|
||
// justify-content: center;
|
||
// padding: 20px;
|
||
// overflow: hidden;
|
||
// box-sizing: border-box;
|
||
//}
|
||
//
|
||
//.dashboard-container {
|
||
// width: 1400px;
|
||
// height: 900px;
|
||
// background: #0b1023;
|
||
// position: relative;
|
||
// overflow: visible;
|
||
// flex-shrink: 0;
|
||
//}
|
||
|
||
.toDeep {
|
||
--editor-background-color: blue;
|
||
}
|
||
|
||
.toDeep :deep(.ant-select-selector) {
|
||
background-color: #101129;
|
||
border: none;
|
||
}
|
||
|
||
.toDeep :deep(.ant-select-arrow) {
|
||
color: #fff;
|
||
}
|
||
|
||
.toDeep :deep(.ant-select-selection-item) {
|
||
color: #fff;
|
||
}
|
||
|
||
.toDeep-text {
|
||
color: #4c9bfd !important;
|
||
font-size: 0.844rem !important;
|
||
position: relative !important;
|
||
line-height: 2rem !important;
|
||
white-space: nowrap !important;
|
||
text-align: start !important;
|
||
text-overflow: ellipsis !important;
|
||
overflow: hidden !important;
|
||
}
|
||
</style>
|