租户的仪表盘

This commit is contained in:
lai
2024-06-14 17:20:24 +08:00
parent 0d0603058c
commit 322dccfbc1
3 changed files with 398 additions and 367 deletions

View File

@@ -0,0 +1,134 @@
.cardClass {
width: 100%;
height: 100%;
border-radius: 10px;
}
.spaceButton{
margin-right: 3px
}
.visual {
width: 80px;
height: 80px;
display: block;
float: left;
padding-top: 2px;
padding-left: -30px;
margin-bottom: 15px;
font-size: 100px;
line-height: 55px;
background-size: contain;
opacity: 0.2; /* 调整透明度以使其成为背景 */
}
.visual45G {
width: 80px;
height: 80px;
display: block;
float: left;
padding-top: 2px;
padding-left: -30px;
margin-bottom: 15px;
font-size: 100px;
line-height: 55px;
background-size: contain;
opacity: 0.01; /* 调整透明度以使其成为背景 */
}
/* 45G基站数,在线用户数 */
.details {
position: absolute;
right: 15px;
padding-right: 15px;
}
/*
.details {
margin-left: 12px;
right: 15px;
padding-right: 15px;
}
*/
.number {
padding-top: 25px;
text-align: left;
font-size: 34px;
line-height: 36px;
letter-spacing: -1px;
margin-bottom: 0px;
font-weight: 300;
}
.desc {
text-align: right;
font-size: 16px;
letter-spacing: 0px;
font-weight: 400;
}
/* .more {
clear: both;
display: block;
padding: 6px 10px 6px 10px;
position: relative;
text-transform: uppercase;
font-weight: 300;
font-size: 11px;
opacity: 0.7;
filter: alpha(opacity=70);
color: #e00909;
background-color: #e00909;
}
.more:hover {
text-decoration: none;
opacity: 0.9;
filter: alpha(opacity=90);
} */
.flexibleSpan {
width: 100%;
position: relative;
line-height: 2rem;
white-space: nowrap;
text-align: start;
text-overflow: ellipsis;
overflow: hidden;
}
.flexibleSpan::before {
content: ' ';
position: absolute;
top: 1px;
left: 0;
right: 0;
width: 100%;
bottom: 0;
z-index: 0;
height: 2px;
border-radius: 4px;
}
.dataSpan::before{
background-image:linear-gradient(135deg,#93f9b9,#1d976c);
}
.gNodeBSpan::before {
background-image: linear-gradient(135deg, #3c8ce7, #00eaff);
}
.eNodeBSpan::before {
background-image: linear-gradient(135deg, #3bb2b8, #42e695);
}
.gUserSpan::before {
background-image: linear-gradient(135deg, #00eaff, #3c8ce7);
}
.eUserSpan::before {
background-image: linear-gradient(135deg, #42e695, #3bb2b8);
}

View File

@@ -0,0 +1,260 @@
<script setup lang="ts">
import { ref, onMounted, markRaw, reactive } from 'vue';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { listSub } from '@/api/neUser/sub';
import { listUENumBySMF } from '@/api/neUser/smf';
import { listBase5G } from '@/api/neUser/base5G';
const { t } = useI18n();
/**概览状态类型 */
type SkimStateType = {
/**UDM签约用户数量 */
udmSubNum: number;
/**SMF在线用户数 */
smfUeNum: number;
/**5G基站数量 */
gnbNum: number;
/**5G在线用户数量 */
gnbUeNum: number;
/**4G基站数量 */
enbNum: number;
/**4G在线用户数量 */
enbUeNum: number;
};
/**概览状态信息 */
let skimState: SkimStateType = reactive({
udmSubNum: 0,
smfUeNum: 0,
gnbNum: 0,
gnbUeNum: 0,
enbNum: 0,
enbUeNum: 0,
});
/**获取概览信息 */
async function fnGetSkim() {
const resArr = await Promise.allSettled([
listSub({
neid: '001',
pageNum: 1,
pageSize: 1,
}),
listUENumBySMF('001'),
listBase5G({
neType: 'AMF',
neId: '001',
}),
listBase5G({
neType: 'MME',
neId: '001',
}),
]);
if (resArr[0].status === 'fulfilled') {
const res0 = resArr[0].value;
if (res0.code === RESULT_CODE_SUCCESS) {
skimState.udmSubNum = res0.total;
}
}
if (resArr[1].status === 'fulfilled') {
const res1 = resArr[1].value;
if (res1.code === RESULT_CODE_SUCCESS) {
skimState.smfUeNum = res1.data;
}
}
if (resArr[2].status === 'fulfilled') {
const res2 = resArr[2].value;
if (res2.code === RESULT_CODE_SUCCESS) {
skimState.gnbNum = res2.total;
skimState.gnbUeNum = 0;
res2.rows.map((item: any) => {
skimState.gnbUeNum += item.ueNum;
});
}
}
if (resArr[3].status === 'fulfilled') {
const res3 = resArr[3].value;
if (res3.code === RESULT_CODE_SUCCESS) {
skimState.enbNum = res3.total;
skimState.enbUeNum = 0;
res3.rows.map((item: any) => {
skimState.enbUeNum += item.ueNum;
});
}
}
}
onMounted(() => {
fnGetSkim();
});
</script>
<template>
<a-row :gutter="[8, 8]" justify="space-between">
<a-col :lg="6" :md="24" :xs="24">
<a-card
:bordered="false"
style="background: linear-gradient(135deg, #abdcff, #0396ff)"
:hoverable="true"
class="cardClass"
>
<div class="visual">
<rise-outlined />
</div>
<div class="details">
<div class="number">
<UserOutlined class="spaceButton" />
{{ skimState.udmSubNum }}
</div>
<div class="desc">
<span class="flexibleSpan gNodeBSpan">
{{ t('views.dashboard.overview.skim.users') }}
</span>
</div>
</div>
</a-card>
</a-col>
<a-col :lg="6" :md="24" :xs="24">
<a-card
:bordered="false"
style="background: linear-gradient(135deg, #1d976c, #93f9b9)"
:hoverable="true"
class="cardClass"
>
<div class="visual">
<bar-chart-outlined />
</div>
<div class="details">
<div class="number">
<database-outlined class="spaceButton" />
{{ skimState.smfUeNum }}
</div>
<div class="desc">
<span class="flexibleSpan dataSpan">
{{ t('views.dashboard.overview.skim.smfUeNum') }}
</span>
</div>
</div>
</a-card>
</a-col>
<a-col :lg="6" :md="24" :xs="24">
<a-card
:bordered="false"
style="background: linear-gradient(135deg, #3c8ce7, #00eaff)"
:hoverable="true"
class="cardClass"
>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<div class="visual45G">
<bar-chart-outlined />
</div>
<div class="details">
<div class="number">
<wifi-outlined class="spaceButton" />
{{ skimState.gnbNum }}
</div>
<div class="desc">
<span class="flexibleSpan gNodeBSpan">{{
t('views.dashboard.overview.skim.gnbBase')
}}</span>
</div>
</div>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<div class="visual45G">
<bar-chart-outlined />
</div>
<div class="details">
<div class="number">
<UserOutlined class="spaceButton" />
{{ skimState.gnbUeNum }}
</div>
<div class="desc">
<span class="flexibleSpan gUserSpan">{{
t('views.dashboard.overview.skim.gnbUeNum')
}}</span>
</div>
</div>
</a-col>
</a-row>
</a-card>
</a-col>
<a-col :lg="6" :md="24" :xs="24">
<a-card
:bordered="false"
style="background: linear-gradient(135deg, #42e695, #3bb2b8)"
:hoverable="true"
class="cardClass"
>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<div class="visual45G">
<bar-chart-outlined />
</div>
<div class="details">
<div class="number">
<wifi-outlined class="spaceButton" />
{{ skimState.enbNum }}
</div>
<div class="desc">
<span class="flexibleSpan eNodeBSpan">{{
t('views.dashboard.overview.skim.enbBase')
}}</span>
</div>
</div>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<div class="visual45G">
<bar-chart-outlined />
</div>
<div class="details">
<div class="number">
<UserOutlined class="spaceButton" /> {{ skimState.enbUeNum }}
</div>
<div class="desc">
<span class="flexibleSpan eUserSpan">{{
t('views.dashboard.overview.skim.enbUeNum')
}}</span>
</div>
</div>
</a-col>
</a-row>
</a-card>
</a-col>
</a-row>
<!-- <a-card style="width: 300px">
<a-row>
<a-col :span="8"
><a-avatar :size="48">
<template #icon><UserOutlined /></template>
</a-avatar>
</a-col>
<a-col :span="8">
<a-statistic
title="Active Users"
:value="112893"
style="margin-right: 50px"
/>
</a-col>
<a-col :span="8">
<a-statistic
title="Active Users"
:value="112893"
style="margin-right: 50px"
/>
</a-col>
</a-row>
</a-card> -->
</template>
<style lang="less" scoped>
@import url('./css/module.css');
</style>

View File

@@ -1,373 +1,22 @@
<script setup lang="ts">
import * as echarts from 'echarts/core';
import {
TooltipComponent,
GridComponent,
TitleComponent,
LegendComponent,
DataZoomComponent,
} from 'echarts/components';
import { LineChart, LineSeriesOption } from 'echarts/charts';
import { UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
import { listMain } from '@/api/index';
import { PieChart } from 'echarts/charts';
import { LabelLayout } from 'echarts/features';
import { ref, onMounted, markRaw } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import useI18n from '@/hooks/useI18n';
import UPFTEST from './tenantUPF.vue';
import { BarChart } from 'echarts/charts';
import MODULE from './moduleInfo.vue';
const { t } = useI18n();
echarts.use([
TooltipComponent,
LegendComponent,
PieChart,
CanvasRenderer,
BarChart,
LabelLayout,
TooltipComponent,
GridComponent,
LegendComponent,
DataZoomComponent,
LineChart,
UniversalTransition,
TitleComponent,
]);
/**图DOM节点实例对象 */
const statusBar = ref<HTMLElement | undefined>(undefined);
/**图实例对象 */
const statusBarChart = ref<any>(null);
/**查询网元状态列表 */
function fnGetList(one: boolean) {
listMain().then(res => {
var rightNum = 0;
var errorNum = 0;
// if (res.length) nfInfo.serialNum = res[0].serialNum;
for (let i = 0; i < res.length; i++) {
if (res[i].status == '正常' || res[i].status == 'Normal') {
rightNum++;
} else {
errorNum++;
}
}
const optionData: any = {
title: {
text: t('views.dashboard.overview.skim.baseTitle'),
textStyle: {
//标题内容的样式
// color: '#000', //
fontStyle: 'normal', //lic主标题文字字体风格默认normal有italic(斜体),oblique(斜体)
//fontWeight: "700", //可选normal(正常)bold(加粗)bolder(加粗)lighter(变细)100|200|300|400|500...
// fontFamily: "PingFangSC-Regular, PingFang SC", //主题文字字体,默认微软雅黑
// fontSize: 20 //主题文字字体大小默认为18px
},
},
// legend: {
// top: '10%',
// textStyle: {
// fontSize: 10
// },
// data: ['低危', '高位']
// },
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
grid: {
top: '10%',
left: '3%',
right: '3%',
bottom: '1%',
containLabel: true,
},
xAxis: {
type: 'category',
data: [
t('views.dashboard.overview.skim.gnbBase'),
t('views.dashboard.overview.skim.gnbUeNum'),
t('views.dashboard.overview.skim.enbBase'),
t('views.dashboard.overview.skim.enbUeNum'),
],
axisTick: {
show: false,
}, // 取消坐标轴刻度线
axisLine: {
show: false, // 取消坐标轴线
}, // 取消坐标轴线
},
yAxis: {
type: 'value',
axisTick: {
show: false,
}, // 取消坐标轴刻度线
axisLine: {
show: false, // 取消坐标轴线
}, // 取消坐标轴线
axisLabel: {
show: false,
}, // 取消坐标轴刻度线
splitLine: {
show: false, // 取消网格线
}, // 取消网格线
},
series: [
{
data: [
3,
{
value: 5,
// itemStyle: {
// color: '#f9d3e3',
// },
},
2,
4,
],
itemStyle: {
borderRadius: [10, 10, 0, 0], // 设置四个圆角的半径,顺序为左上、右上、右下、左下
},
label: {
show: true, // 显示数值
position: 'inside', // 设置显示位置为柱状图内部
fontSize: 15, // 设置字体大小
formatter: (params: any) => {
if (!params.value) return '';
return `${params.value}`;
},
},
type: 'bar',
},
],
};
fnDesign(statusBar.value, optionData);
});
}
function fnDesign(container: HTMLElement | undefined, option: any) {
if (!container) return;
if (!statusBarChart.value) {
statusBarChart.value = markRaw(echarts.init(container, 'light'));
}
option && statusBarChart.value.setOption(option);
// 创建 ResizeObserver 实例
var observer = new ResizeObserver(entries => {
if (statusBarChart.value) {
statusBarChart.value.resize();
}
});
// 监听元素大小变化
observer.observe(container);
}
onMounted(() => {
fnGetList(true);
});
onMounted(() => {});
</script>
<template>
<PageContainer>
<div style="background-color: #f7f8fc; padding: 20px">
<a-row :gutter="16">
<a-col :span="8">
<a-card
:bordered="false"
style="
background: linear-gradient(135deg, #17ead9, #6078ea);
margin: 'left';
"
class="cardClass"
>
<a-row :gutter="16">
<a-col :span="10">
<a-avatar
:size="76"
style="
background: linear-gradient(135deg, #17ead9, #6078ea);
margin-top: 15px;
"
>
<template #icon><UserOutlined /></template>
</a-avatar>
</a-col>
<a-col :span="14">
<a-row :gutter="16">
<a-col :span="24">
<h1
style="
margin-top: 10px;
color: #e1f6e1;
font-size: 30px;
margin-right: 20px;
"
>
{{ t('views.dashboard.overview.skim.users') }}
</h1>
</a-col>
<a-col :span="24">
<a-statistic
:value="3000"
:value-style="{
color: '#e1f6e1',
fontWeight: 'bold',
fontSize: '32px',
marginLeft: '50px',
}"
style="margin-left: -50px"
>
</a-statistic>
</a-col>
</a-row>
</a-col>
</a-row>
<!-- <a-card-meta
:title="t('views.dashboard.overview.skim.users')"
description="This is the description"
style="margin-top: 20px; color: #f7f8fc"
class="my-card-meta1"
>
<template #avatar>
<a-avatar
:size="64"
style="background: linear-gradient(135deg, #17ead9, #6078ea)"
>
<template #icon><UserOutlined /></template>
</a-avatar>
</template>
</a-card-meta> -->
</a-card>
</a-col>
<a-col :span="8">
<a-card
:bordered="false"
style="
background: linear-gradient(135deg, #ce9ffc, #7367f0);
margin: 'center';
"
class="cardClass"
>
<a-row :gutter="16">
<a-col :span="10">
<a-avatar
:size="76"
style="
background: linear-gradient(135deg, #ce9ffc, #7367f0);
margin-top: 15px;
"
>
<template #icon><comment-outlined /></template>
</a-avatar>
</a-col>
<a-col :span="14">
<a-row :gutter="16">
<a-col :span="24">
<h1
style="margin-top: 10px; color: #e1f6e1; font-size: 30px"
>
{{ t('views.dashboard.overview.skim.imsUeNum') }}
</h1>
</a-col>
<a-col :span="24">
<a-statistic
:value="2"
:value-style="{
color: '#e1f6e1',
fontWeight: 'bold',
fontSize: '32px',
marginLeft: '50px',
}"
style="margin-right: 20px"
>
</a-statistic>
</a-col>
</a-row>
</a-col>
</a-row>
</a-card>
</a-col>
<a-col :span="8">
<a-card
:bordered="false"
style="
background: linear-gradient(135deg, #ffe985, #fa742b);
margin: 'right';
"
class="cardClass"
>
<a-row :gutter="16">
<a-col :span="10">
<a-avatar
:size="76"
style="
background: linear-gradient(135deg, #ffe985, #fa742b);
margin-top: 15px;
"
>
<template #icon><database-outlined /></template>
</a-avatar>
</a-col>
<a-col :span="14">
<a-row :gutter="16">
<a-col :span="24">
<h1
style="margin-top: 10px; color: #e1f6e1; font-size: 30px"
>
{{ t('views.dashboard.overview.skim.smfUeNum') }}
</h1>
</a-col>
<a-col :span="24">
<a-statistic
:value="5"
:value-style="{
color: '#e1f6e1',
fontWeight: 'bold',
fontSize: '32px',
marginLeft: '50px',
}"
style="margin-right: 20px"
>
</a-statistic>
</a-col>
</a-row>
</a-col>
</a-row>
</a-card>
</a-col>
<!-- <a-col :span="6">
<a-card :bordered="false" class="cardClass">
<div style="width: 100%; min-height: 200px" ref="statusBar"></div>
到时候画饼图 显示四个options 分别45G基站数/用户数
</a-card>
</a-col> -->
</a-row>
<MODULE />
</div>
<div style="background-color: #f7f8fc; padding: 20px">
<a-row :gutter="16">
<a-col :span="14">
<a-card :bordered="false" class="cardClass"> <UPFTEST /></a-card>
</a-col>
<a-col :span="10">
<a-card :bordered="false" class="cardClass">
<div style="width: 100%; min-height: 400px" ref="statusBar"></div>
</a-card>
</a-col>
</a-row>
<a-card :bordered="false" class="cardClass"> <UPFTEST /></a-card>
</div>
</PageContainer>
</template>
@@ -378,16 +27,4 @@ onMounted(() => {
width: 100%;
border-radius: 10px;
}
.chart {
width: 100%;
height: 100%;
margin-top: 0.1rem;
}
.chart-container {
/* 设置图表容器大小和位置 */
width: 100%;
height: 100%;
}
</style>