Merge remote-tracking branch 'origin/main' into lichang
This commit is contained in:
@@ -1783,6 +1783,10 @@ export default {
|
||||
reset: "System Reset",
|
||||
resetInstruction: "A system reset will erase all data in the current system, please proceed with caution!!!!",
|
||||
resetTipContent: 'Are you sure you want to clear all data from the current system and insist on continuing?',
|
||||
homeInstruction:'Set the home page',
|
||||
home: 'Home Page',
|
||||
homeTip:'Do you want to submit the current interface as the system interface?',
|
||||
homeSet:'Home Page Settings',
|
||||
},
|
||||
role:{
|
||||
allScopeOptions:'All data permissions',
|
||||
|
||||
@@ -1783,6 +1783,10 @@ export default {
|
||||
reset: "系统重置",
|
||||
resetInstruction: "系统重置将会清除当前系统内所有数据,请谨慎操作!!!",
|
||||
resetTipContent: '确认要清除当前系统内所有数据并坚持继续吗?',
|
||||
homeInstruction:'设置系统首页界面',
|
||||
home: '系统首页',
|
||||
homeTip:'确认要提交当前界面为系统界面吗?',
|
||||
homeSet:'系统首页设置',
|
||||
},
|
||||
role:{
|
||||
allScopeOptions:'全部数据权限',
|
||||
|
||||
446
src/views/configManage/neOverview/index.vue
Normal file
446
src/views/configManage/neOverview/index.vue
Normal file
@@ -0,0 +1,446 @@
|
||||
<script setup lang="ts">
|
||||
import { PageContainer } from 'antdv-pro-layout';
|
||||
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||||
import { message } from 'ant-design-vue/lib';
|
||||
import { reactive, toRaw, ref, onMounted, onBeforeUnmount, markRaw } from 'vue';
|
||||
import { listMain } from '@/api/index';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import { TooltipComponent } from 'echarts/components';
|
||||
import { GaugeChart } from 'echarts/charts';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import * as echarts from 'echarts/core';
|
||||
import { TitleComponent, LegendComponent } from 'echarts/components';
|
||||
import { PieChart } from 'echarts/charts';
|
||||
import { LabelLayout } from 'echarts/features';
|
||||
import { useRoute } from 'vue-router';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import useDictStore from '@/store/modules/dict';
|
||||
const { getDict } = useDictStore();
|
||||
const appStore = useAppStore();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
echarts.use([
|
||||
TooltipComponent,
|
||||
GaugeChart,
|
||||
TitleComponent,
|
||||
LegendComponent,
|
||||
PieChart,
|
||||
CanvasRenderer,
|
||||
LabelLayout,
|
||||
]);
|
||||
|
||||
/**图DOM节点实例对象 */
|
||||
const statusBar = ref<HTMLElement | undefined>(undefined);
|
||||
|
||||
/**图实例对象 */
|
||||
const statusBarChart = ref<any>(null);
|
||||
|
||||
/**网元状态字典数据 */
|
||||
let indexColor = ref<DictType[]>([
|
||||
{ label: 'Normal', value: 'normal', tagType: '', tagClass: '#91cc75' },
|
||||
{
|
||||
label: 'Abnormal',
|
||||
value: 'abnormal',
|
||||
tagType: '',
|
||||
tagClass: '#ee6666',
|
||||
},
|
||||
]);
|
||||
|
||||
/**表格字段列 */
|
||||
//customRender(){} ----单元格处理
|
||||
let tableColumns: ColumnsType = [
|
||||
{
|
||||
title: t('views.index.object'),
|
||||
dataIndex: 'name',
|
||||
align: 'center',
|
||||
key: 'status',
|
||||
},
|
||||
{
|
||||
title: t('views.index.realNeStatus'),
|
||||
dataIndex: 'status',
|
||||
align: 'center',
|
||||
customRender(opt) {
|
||||
if (opt.value == 'Normal') return t('views.index.normal');
|
||||
return t('views.index.abnormal');
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('views.index.reloadTime'),
|
||||
dataIndex: 'refresh',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('views.index.version'),
|
||||
dataIndex: 'version',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('views.index.serialNum'),
|
||||
dataIndex: 'serialNum',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('views.index.expiryDate'),
|
||||
dataIndex: 'expiryDate',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('views.index.ipAddress'),
|
||||
dataIndex: 'ipAddress',
|
||||
key: 'groupName',
|
||||
align: 'center',
|
||||
},
|
||||
];
|
||||
/**表格状态类型 */
|
||||
type TabeStateType = {
|
||||
/**加载等待 */
|
||||
loading: boolean;
|
||||
/**紧凑型 */
|
||||
size: string;
|
||||
/**搜索栏 */
|
||||
seached: boolean;
|
||||
/**记录数据 */
|
||||
data: object[];
|
||||
/**勾选记录 */
|
||||
selectedRowKeys: (string | number)[];
|
||||
};
|
||||
|
||||
/**表格状态 */
|
||||
let tableState: TabeStateType = reactive({
|
||||
loading: false,
|
||||
size: 'middle',
|
||||
seached: false,
|
||||
data: [],
|
||||
selectedRowKeys: [],
|
||||
});
|
||||
|
||||
/**表格状态 */
|
||||
let nfInfo: any = reactive({
|
||||
obj: 'OMC',
|
||||
version: appStore.version,
|
||||
status: t('views.index.normal'),
|
||||
outTimeDate: '',
|
||||
serialNum: appStore.serialNum,
|
||||
});
|
||||
|
||||
/**表格状态类型 */
|
||||
type nfStateType = {
|
||||
/**主机名 */
|
||||
hostName: string;
|
||||
/**操作系统信息 */
|
||||
osInfo: string;
|
||||
/**数据库信息 */
|
||||
dbInfo: string;
|
||||
/**IP地址 */
|
||||
ipAddress: string;
|
||||
/**端口 */
|
||||
port: number;
|
||||
/**版本 */
|
||||
version: string;
|
||||
/**CPU利用率 */
|
||||
cpuUse: string;
|
||||
/**内存使用 */
|
||||
memoryUse: string;
|
||||
/**用户容量 */
|
||||
capability: number;
|
||||
/**序列号 */
|
||||
serialNum: string;
|
||||
/**许可证到期日期 */
|
||||
/* selectedRowKeys: (string | number)[];*/
|
||||
expiryDate: string;
|
||||
};
|
||||
/**网元详细信息 */
|
||||
let pronInfo: nfStateType = reactive({
|
||||
hostName: '5gc',
|
||||
osInfo: 'Linux 5gc 4.15.0-112-generic 2020 x86_64 GNU/Linux',
|
||||
dbInfo: 'db v9.9.9',
|
||||
ipAddress: '-',
|
||||
port: 33030,
|
||||
version: '-',
|
||||
cpuUse: '-',
|
||||
memoryUse: '-',
|
||||
capability: 0,
|
||||
serialNum: '-',
|
||||
expiryDate: '-',
|
||||
});
|
||||
|
||||
/**查询网元状态列表 */
|
||||
function fnGetList(one: boolean) {
|
||||
if (tableState.loading) return;
|
||||
one && (tableState.loading = true);
|
||||
listMain().then(res => {
|
||||
tableState.data = res;
|
||||
tableState.loading = false;
|
||||
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: '',
|
||||
subtext: '',
|
||||
left: 'center',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
},
|
||||
color: indexColor.value.map(item => item.tagClass),
|
||||
series: [
|
||||
{
|
||||
name: t('views.index.realNeStatus'),
|
||||
type: 'pie',
|
||||
radius: '70%',
|
||||
center: ['50%', '50%'],
|
||||
data: [
|
||||
{ value: rightNum, name: t('views.index.normal') },
|
||||
{ value: errorNum, name: t('views.index.abnormal') },
|
||||
],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
|
||||
label: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**抽屉 网元详细信息 */
|
||||
const visible = ref(false);
|
||||
const closeDrawer = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
/**抽屉 网元详细信息 */
|
||||
|
||||
/**监听表格行事件*/
|
||||
function rowClick(record: any, index: any) {
|
||||
return {
|
||||
onClick: (event: any) => {
|
||||
if (
|
||||
toRaw(record).status == '异常' ||
|
||||
toRaw(record).status == 'Abnormal'
|
||||
) {
|
||||
message.error(t('views.index.neStatus'), 2);
|
||||
return false;
|
||||
} else {
|
||||
let pronData = toRaw(record);
|
||||
const totalMemInKB = pronData.memUsage?.totalMem;
|
||||
const nfUsedMemInKB = pronData.memUsage?.nfUsedMem;
|
||||
const sysMemUsageInKB = pronData.memUsage?.sysMemUsage;
|
||||
|
||||
// 将KB转换为MB
|
||||
const totalMemInMB = Math.round((totalMemInKB / 1024) * 100) / 100;
|
||||
const nfUsedMemInMB = Math.round((nfUsedMemInKB / 1024) * 100) / 100;
|
||||
const sysMemUsageInMB =
|
||||
Math.round((sysMemUsageInKB / 1024) * 100) / 100;
|
||||
|
||||
//渲染详细信息
|
||||
pronInfo = {
|
||||
hostName: pronData.hostName,
|
||||
osInfo: pronData.osInfo,
|
||||
dbInfo: pronData.dbInfo,
|
||||
ipAddress: pronData.ipAddress,
|
||||
port: pronData.port,
|
||||
version: pronData.version,
|
||||
cpuUse:
|
||||
pronData.name +
|
||||
':' +
|
||||
pronData.cpuUsage?.nfCpuUsage / 100 +
|
||||
'%; ' +
|
||||
'SYS:' +
|
||||
pronData.cpuUsage?.sysCpuUsage / 100 +
|
||||
'%',
|
||||
memoryUse:
|
||||
'Total:' +
|
||||
totalMemInMB +
|
||||
'MB; ' +
|
||||
pronData.name +
|
||||
':' +
|
||||
nfUsedMemInMB +
|
||||
'MB; SYS:' +
|
||||
sysMemUsageInMB +
|
||||
'MB',
|
||||
capability: pronData.capability,
|
||||
serialNum: pronData.serialNum,
|
||||
expiryDate: pronData.expiryDate,
|
||||
};
|
||||
}
|
||||
visible.value = true;
|
||||
},
|
||||
};
|
||||
}
|
||||
let timer: any;
|
||||
|
||||
/**
|
||||
* 国际化翻译转换
|
||||
*/
|
||||
function fnLocale() {
|
||||
let title = route.meta.title as string;
|
||||
if (title.indexOf('router.') !== -1) {
|
||||
title = t(title);
|
||||
}
|
||||
appStore.setTitle(title);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDict('index_status')
|
||||
.then(res => {
|
||||
if (res.length > 0) {
|
||||
indexColor.value = res;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
fnLocale();
|
||||
fnGetList(true);
|
||||
timer = setInterval(() => fnGetList(false), 10000); // 每隔10秒执行一次
|
||||
});
|
||||
});
|
||||
|
||||
// 在组件卸载之前清除定时器
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(timer);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PageContainer :breadcrumb="{}">
|
||||
<div>
|
||||
<a-drawer :visible="visible" @close="closeDrawer" :width="700">
|
||||
<a-descriptions bordered :column="1" :label-style="{ width: '160px' }">
|
||||
<a-descriptions-item :label="t('views.index.hostName')">{{
|
||||
pronInfo.hostName
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.osInfo')">{{
|
||||
pronInfo.osInfo
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.dbInfo')">{{
|
||||
pronInfo.dbInfo
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.ipAddress')">{{
|
||||
pronInfo.ipAddress
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.port')">{{
|
||||
pronInfo.port
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.version')">{{
|
||||
pronInfo.version
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.cpuUse')">{{
|
||||
pronInfo.cpuUse
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.memoryUse')">{{
|
||||
pronInfo.memoryUse
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.capability')">{{
|
||||
pronInfo.capability
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.serialNum')">{{
|
||||
pronInfo.serialNum
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.expiryDate')">{{
|
||||
pronInfo.expiryDate
|
||||
}}</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-drawer>
|
||||
</div>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :lg="14" :md="16" :xs="24">
|
||||
<!-- 表格列表 -->
|
||||
<a-table
|
||||
class="table"
|
||||
row-key="id"
|
||||
:columns="tableColumns"
|
||||
:loading="tableState.loading"
|
||||
:data-source="tableState.data"
|
||||
:pagination="false"
|
||||
:scroll="{ x: true }"
|
||||
:customRow="rowClick"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'status'">
|
||||
<div v-if="record.status == '正常' || record.status == 'Normal'">
|
||||
<a-tag color="blue">{{ record.name }}</a-tag>
|
||||
</div>
|
||||
<div v-else>
|
||||
<a-tag color="pink">{{ record.name }}</a-tag>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-col>
|
||||
<a-col :lg="10" :md="8" :xs="24">
|
||||
<a-card :title="t('views.index.runStatus')" style="margin-bottom: 16px">
|
||||
<div style="width: 100%; min-height: 200px" ref="statusBar"></div>
|
||||
</a-card>
|
||||
<a-card :title="t('views.index.mark')" style="margin-top: 16px">
|
||||
<a-descriptions
|
||||
bordered
|
||||
:column="1"
|
||||
:label-style="{ width: '160px' }"
|
||||
>
|
||||
<a-descriptions-item :label="t('views.index.object')">{{
|
||||
nfInfo.obj
|
||||
}}</a-descriptions-item>
|
||||
<template v-if="nfInfo.obj === 'OMC'">
|
||||
<a-descriptions-item :label="t('views.index.versionNum')">{{
|
||||
nfInfo.version
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.systemStatus')">{{
|
||||
nfInfo.status
|
||||
}}</a-descriptions-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-descriptions-item :label="t('views.index.serialNum')">{{
|
||||
nfInfo.serialNum
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.expiryDate')">{{
|
||||
nfInfo.outTimeDate
|
||||
}}</a-descriptions-item>
|
||||
</template>
|
||||
</a-descriptions>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</PageContainer>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@@ -612,13 +612,16 @@ function fnShowSet() {
|
||||
}
|
||||
|
||||
// key替换中文title
|
||||
function mapKeysWithReduce(data: any, titleMapping: any) {
|
||||
return data.map((item:any) => {
|
||||
return Object.keys(item).reduce((newItem:any, key:any) => {
|
||||
function mapKeysWithReduce(data: any[], titleMapping: Record<string, string>) {
|
||||
return data.map((item: any) => {
|
||||
if (typeof item !== 'object' || item === null) {
|
||||
return item; // 如果不是对象,直接返回原值
|
||||
}
|
||||
return Object.keys(item).reduce((newItem: Record<string, any>, key: string) => {
|
||||
const title = titleMapping[key] || key;
|
||||
newItem[title] = item[key];
|
||||
return newItem;
|
||||
});
|
||||
}, {}); // 确保初始值是一个空对象
|
||||
});
|
||||
}
|
||||
|
||||
@@ -673,6 +676,7 @@ function fnExportAll() {
|
||||
|
||||
return filteredObj;
|
||||
});
|
||||
console.log(mappArr);
|
||||
|
||||
res.data = mapKeysWithReduce(res.data, mappArr);
|
||||
writeSheet(res.data, 'alarm', sortData).then(fileBlob => {
|
||||
|
||||
@@ -427,13 +427,16 @@ function fnCancelConfirm() {
|
||||
}
|
||||
|
||||
// key替换中文title
|
||||
function mapKeysWithReduce(data: any, titleMapping: any) {
|
||||
return data.map((item:any) => {
|
||||
return Object.keys(item).reduce((newItem:any, key:any) => {
|
||||
function mapKeysWithReduce(data: any[], titleMapping: Record<string, string>) {
|
||||
return data.map((item: any) => {
|
||||
if (typeof item !== 'object' || item === null) {
|
||||
return item;
|
||||
}
|
||||
return Object.keys(item).reduce((newItem: Record<string, any>, key: string) => {
|
||||
const title = titleMapping[key] || key;
|
||||
newItem[title] = item[key];
|
||||
return newItem;
|
||||
});
|
||||
}, {});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,446 +1,59 @@
|
||||
<script setup lang="ts">
|
||||
import { PageContainer } from 'antdv-pro-layout';
|
||||
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||||
import { message } from 'ant-design-vue/lib';
|
||||
import { reactive, toRaw, ref, onMounted, onBeforeUnmount, markRaw } from 'vue';
|
||||
import { listMain } from '@/api/index';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import { TooltipComponent } from 'echarts/components';
|
||||
import { GaugeChart } from 'echarts/charts';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import * as echarts from 'echarts/core';
|
||||
import { TitleComponent, LegendComponent } from 'echarts/components';
|
||||
import { PieChart } from 'echarts/charts';
|
||||
import { LabelLayout } from 'echarts/features';
|
||||
import { useRoute } from 'vue-router';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import useDictStore from '@/store/modules/dict';
|
||||
const { getDict } = useDictStore();
|
||||
const appStore = useAppStore();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
import {
|
||||
defineAsyncComponent,
|
||||
onMounted,
|
||||
shallowRef,
|
||||
type Component,
|
||||
} from 'vue';
|
||||
import { getConfigKey, changeValue } from '@/api/system/config';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
|
||||
echarts.use([
|
||||
TooltipComponent,
|
||||
GaugeChart,
|
||||
TitleComponent,
|
||||
LegendComponent,
|
||||
PieChart,
|
||||
CanvasRenderer,
|
||||
LabelLayout,
|
||||
]);
|
||||
const currentComponent = shallowRef<Component | null>(
|
||||
defineAsyncComponent(
|
||||
() => import('@/views/configManage/neOverview/index.vue')
|
||||
)
|
||||
);
|
||||
|
||||
/**图DOM节点实例对象 */
|
||||
const statusBar = ref<HTMLElement | undefined>(undefined);
|
||||
|
||||
/**图实例对象 */
|
||||
const statusBarChart = ref<any>(null);
|
||||
|
||||
/**网元状态字典数据 */
|
||||
let indexColor = ref<DictType[]>([
|
||||
{ label: 'Normal', value: 'normal', tagType: '', tagClass: '#91cc75' },
|
||||
{
|
||||
label: 'Abnormal',
|
||||
value: 'abnormal',
|
||||
tagType: '',
|
||||
tagClass: '#ee6666',
|
||||
},
|
||||
]);
|
||||
|
||||
/**表格字段列 */
|
||||
//customRender(){} ----单元格处理
|
||||
let tableColumns: ColumnsType = [
|
||||
{
|
||||
title: t('views.index.object'),
|
||||
dataIndex: 'name',
|
||||
align: 'center',
|
||||
key: 'status',
|
||||
},
|
||||
{
|
||||
title: t('views.index.realNeStatus'),
|
||||
dataIndex: 'status',
|
||||
align: 'center',
|
||||
customRender(opt) {
|
||||
if (opt.value == 'Normal') return t('views.index.normal');
|
||||
return t('views.index.abnormal');
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('views.index.reloadTime'),
|
||||
dataIndex: 'refresh',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('views.index.version'),
|
||||
dataIndex: 'version',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('views.index.serialNum'),
|
||||
dataIndex: 'serialNum',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('views.index.expiryDate'),
|
||||
dataIndex: 'expiryDate',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('views.index.ipAddress'),
|
||||
dataIndex: 'ipAddress',
|
||||
key: 'groupName',
|
||||
align: 'center',
|
||||
},
|
||||
];
|
||||
/**表格状态类型 */
|
||||
type TabeStateType = {
|
||||
/**加载等待 */
|
||||
loading: boolean;
|
||||
/**紧凑型 */
|
||||
size: string;
|
||||
/**搜索栏 */
|
||||
seached: boolean;
|
||||
/**记录数据 */
|
||||
data: object[];
|
||||
/**勾选记录 */
|
||||
selectedRowKeys: (string | number)[];
|
||||
};
|
||||
|
||||
/**表格状态 */
|
||||
let tableState: TabeStateType = reactive({
|
||||
loading: false,
|
||||
size: 'middle',
|
||||
seached: false,
|
||||
data: [],
|
||||
selectedRowKeys: [],
|
||||
});
|
||||
|
||||
/**表格状态 */
|
||||
let nfInfo: any = reactive({
|
||||
obj: 'OMC',
|
||||
version: appStore.version,
|
||||
status: t('views.index.normal'),
|
||||
outTimeDate: '',
|
||||
serialNum: appStore.serialNum,
|
||||
});
|
||||
|
||||
/**表格状态类型 */
|
||||
type nfStateType = {
|
||||
/**主机名 */
|
||||
hostName: string;
|
||||
/**操作系统信息 */
|
||||
osInfo: string;
|
||||
/**数据库信息 */
|
||||
dbInfo: string;
|
||||
/**IP地址 */
|
||||
ipAddress: string;
|
||||
/**端口 */
|
||||
port: number;
|
||||
/**版本 */
|
||||
version: string;
|
||||
/**CPU利用率 */
|
||||
cpuUse: string;
|
||||
/**内存使用 */
|
||||
memoryUse: string;
|
||||
/**用户容量 */
|
||||
capability: number;
|
||||
/**序列号 */
|
||||
serialNum: string;
|
||||
/**许可证到期日期 */
|
||||
/* selectedRowKeys: (string | number)[];*/
|
||||
expiryDate: string;
|
||||
};
|
||||
/**网元详细信息 */
|
||||
let pronInfo: nfStateType = reactive({
|
||||
hostName: '5gc',
|
||||
osInfo: 'Linux 5gc 4.15.0-112-generic 2020 x86_64 GNU/Linux',
|
||||
dbInfo: 'db v9.9.9',
|
||||
ipAddress: '-',
|
||||
port: 33030,
|
||||
version: '-',
|
||||
cpuUse: '-',
|
||||
memoryUse: '-',
|
||||
capability: 0,
|
||||
serialNum: '-',
|
||||
expiryDate: '-',
|
||||
});
|
||||
|
||||
/**查询网元状态列表 */
|
||||
function fnGetList(one: boolean) {
|
||||
if (tableState.loading) return;
|
||||
one && (tableState.loading = true);
|
||||
listMain().then(res => {
|
||||
tableState.data = res;
|
||||
tableState.loading = false;
|
||||
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: '',
|
||||
subtext: '',
|
||||
left: 'center',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
},
|
||||
color: indexColor.value.map(item => item.tagClass),
|
||||
series: [
|
||||
{
|
||||
name: t('views.index.realNeStatus'),
|
||||
type: 'pie',
|
||||
radius: '70%',
|
||||
center: ['50%', '50%'],
|
||||
data: [
|
||||
{ value: rightNum, name: t('views.index.normal') },
|
||||
{ value: errorNum, name: t('views.index.abnormal') },
|
||||
],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
|
||||
label: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**抽屉 网元详细信息 */
|
||||
const visible = ref(false);
|
||||
const closeDrawer = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
/**抽屉 网元详细信息 */
|
||||
|
||||
/**监听表格行事件*/
|
||||
function rowClick(record: any, index: any) {
|
||||
return {
|
||||
onClick: (event: any) => {
|
||||
if (
|
||||
toRaw(record).status == '异常' ||
|
||||
toRaw(record).status == 'Abnormal'
|
||||
) {
|
||||
message.error(t('views.index.neStatus'), 2);
|
||||
return false;
|
||||
} else {
|
||||
let pronData = toRaw(record);
|
||||
const totalMemInKB = pronData.memUsage?.totalMem;
|
||||
const nfUsedMemInKB = pronData.memUsage?.nfUsedMem;
|
||||
const sysMemUsageInKB = pronData.memUsage?.sysMemUsage;
|
||||
|
||||
// 将KB转换为MB
|
||||
const totalMemInMB = Math.round((totalMemInKB / 1024) * 100) / 100;
|
||||
const nfUsedMemInMB = Math.round((nfUsedMemInKB / 1024) * 100) / 100;
|
||||
const sysMemUsageInMB =
|
||||
Math.round((sysMemUsageInKB / 1024) * 100) / 100;
|
||||
|
||||
//渲染详细信息
|
||||
pronInfo = {
|
||||
hostName: pronData.hostName,
|
||||
osInfo: pronData.osInfo,
|
||||
dbInfo: pronData.dbInfo,
|
||||
ipAddress: pronData.ipAddress,
|
||||
port: pronData.port,
|
||||
version: pronData.version,
|
||||
cpuUse:
|
||||
pronData.name +
|
||||
':' +
|
||||
pronData.cpuUsage?.nfCpuUsage / 100 +
|
||||
'%; ' +
|
||||
'SYS:' +
|
||||
pronData.cpuUsage?.sysCpuUsage / 100 +
|
||||
'%',
|
||||
memoryUse:
|
||||
'Total:' +
|
||||
totalMemInMB +
|
||||
'MB; ' +
|
||||
pronData.name +
|
||||
':' +
|
||||
nfUsedMemInMB +
|
||||
'MB; SYS:' +
|
||||
sysMemUsageInMB +
|
||||
'MB',
|
||||
capability: pronData.capability,
|
||||
serialNum: pronData.serialNum,
|
||||
expiryDate: pronData.expiryDate,
|
||||
};
|
||||
}
|
||||
visible.value = true;
|
||||
},
|
||||
};
|
||||
}
|
||||
let timer: any;
|
||||
/**匹配views里面所有的.vue或.tsx文件 */
|
||||
const views = import.meta.glob('../views/**/*.{vue,tsx}') as Record<
|
||||
string,
|
||||
() => Promise<Component>
|
||||
>;
|
||||
|
||||
/**
|
||||
* 国际化翻译转换
|
||||
* 查找页面模块
|
||||
* @param dirName 组件路径
|
||||
* @returns 路由懒加载函数
|
||||
*/
|
||||
function fnLocale() {
|
||||
let title = route.meta.title as string;
|
||||
if (title.indexOf('router.') !== -1) {
|
||||
title = t(title);
|
||||
function findView(dirName: string): () => Promise<Component> {
|
||||
for (const dir in views) {
|
||||
let viewDirName = '';
|
||||
const component = dir.match(/\/(.+)\.(vue|tsx)/);
|
||||
if (component && component.length === 3) {
|
||||
viewDirName = component[1];
|
||||
}
|
||||
if (viewDirName === dirName) {
|
||||
return views[dir];
|
||||
}
|
||||
}
|
||||
appStore.setTitle(title);
|
||||
return () => import('@/views/error/404.vue');
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDict('index_status')
|
||||
.then(res => {
|
||||
if (res.length > 0) {
|
||||
indexColor.value = res;
|
||||
//获取当前系统设置的首页路径
|
||||
getConfigKey('sys.homePage').then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
||||
if (res.data) {
|
||||
const asyncComponent = findView(`${res.data}`);
|
||||
currentComponent.value = defineAsyncComponent(asyncComponent);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
fnLocale();
|
||||
fnGetList(true);
|
||||
timer = setInterval(() => fnGetList(false), 10000); // 每隔10秒执行一次
|
||||
});
|
||||
});
|
||||
|
||||
// 在组件卸载之前清除定时器
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(timer);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PageContainer :breadcrumb="{}">
|
||||
<div>
|
||||
<a-drawer :visible="visible" @close="closeDrawer" :width="700">
|
||||
<a-descriptions bordered :column="1" :label-style="{ width: '160px' }">
|
||||
<a-descriptions-item :label="t('views.index.hostName')">
|
||||
{{ pronInfo.hostName }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.osInfo')">
|
||||
{{ pronInfo.osInfo }}
|
||||
</a-descriptions-item>
|
||||
<!-- <a-descriptions-item :label="t('views.index.dbInfo')">
|
||||
{{ pronInfo.dbInfo }}
|
||||
</a-descriptions-item> -->
|
||||
<a-descriptions-item :label="t('views.index.ipAddress')">
|
||||
{{ pronInfo.ipAddress }}
|
||||
</a-descriptions-item>
|
||||
<!-- <a-descriptions-item :label="t('views.index.port')">
|
||||
{{ pronInfo.port }}
|
||||
</a-descriptions-item> -->
|
||||
<a-descriptions-item :label="t('views.index.version')">
|
||||
{{ pronInfo.version }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.cpuUse')">
|
||||
{{ pronInfo.cpuUse }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.memoryUse')">
|
||||
{{ pronInfo.memoryUse }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.capability')">
|
||||
{{ pronInfo.capability }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.serialNum')">
|
||||
{{ pronInfo.serialNum }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.expiryDate')">
|
||||
{{ pronInfo.expiryDate }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-drawer>
|
||||
</div>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col :lg="14" :md="16" :xs="24">
|
||||
<!-- 表格列表 -->
|
||||
<a-table
|
||||
class="table"
|
||||
row-key="id"
|
||||
:columns="tableColumns"
|
||||
:loading="tableState.loading"
|
||||
:data-source="tableState.data"
|
||||
:pagination="false"
|
||||
:scroll="{ x: true }"
|
||||
:customRow="rowClick"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'status'">
|
||||
<div v-if="record.status == '正常' || record.status == 'Normal'">
|
||||
<a-tag color="blue">{{ record.name }}</a-tag>
|
||||
</div>
|
||||
<div v-else>
|
||||
<a-tag color="pink">{{ record.name }}</a-tag>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-col>
|
||||
<a-col :lg="10" :md="8" :xs="24">
|
||||
<a-card :title="t('views.index.runStatus')" style="margin-bottom: 16px">
|
||||
<div style="width: 100%; min-height: 200px" ref="statusBar"></div>
|
||||
</a-card>
|
||||
<a-card :title="t('views.index.mark')" style="margin-top: 16px">
|
||||
<a-descriptions
|
||||
bordered
|
||||
:column="1"
|
||||
:label-style="{ width: '160px' }"
|
||||
>
|
||||
<a-descriptions-item :label="t('views.index.object')">{{
|
||||
nfInfo.obj
|
||||
}}</a-descriptions-item>
|
||||
<template v-if="nfInfo.obj === 'OMC'">
|
||||
<a-descriptions-item :label="t('views.index.versionNum')">{{
|
||||
nfInfo.version
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.systemStatus')">{{
|
||||
nfInfo.status
|
||||
}}</a-descriptions-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-descriptions-item :label="t('views.index.serialNum')">{{
|
||||
nfInfo.serialNum
|
||||
}}</a-descriptions-item>
|
||||
<a-descriptions-item :label="t('views.index.expiryDate')">{{
|
||||
nfInfo.outTimeDate
|
||||
}}</a-descriptions-item>
|
||||
</template>
|
||||
</a-descriptions>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</PageContainer>
|
||||
<component :is="currentComponent" />
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
||||
129
src/views/system/setting/components/change-home-index.vue
Normal file
129
src/views/system/setting/components/change-home-index.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<script lang="ts" setup>
|
||||
import { Modal, message } from 'ant-design-vue/lib';
|
||||
import { onMounted, reactive, toRaw } from 'vue';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import { listMenu } from '@/api/system/menu';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
import { getConfigKey, changeValue } from '@/api/system/config';
|
||||
const { t } = useI18n();
|
||||
|
||||
type StateType = {
|
||||
edite: boolean;
|
||||
loading: boolean;
|
||||
open: boolean;
|
||||
default: any;
|
||||
options: any;
|
||||
};
|
||||
|
||||
let state: StateType = reactive({
|
||||
edite: false,
|
||||
loading: false,
|
||||
open: false,
|
||||
default: '',
|
||||
options: [],
|
||||
});
|
||||
|
||||
/**提交保存 */
|
||||
function fnSave() {
|
||||
Modal.confirm({
|
||||
title: t('common.tipTitle'),
|
||||
content: t('views.system.setting.homeTip'),
|
||||
onOk() {
|
||||
// 发送请求
|
||||
const hide = message.loading(t('common.loading'), 0);
|
||||
state.loading = true;
|
||||
changeValue({ key: 'sys.homePage', value: state.default })
|
||||
.then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
message.success(t('views.system.setting.saveSuccess'), 3);
|
||||
fnEdit(false);
|
||||
} else {
|
||||
message.error(res.msg, 3);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
state.loading = false;
|
||||
hide();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**进入可编辑 */
|
||||
function fnEdit(v: boolean) {
|
||||
state.edite = v;
|
||||
state.open = v;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
listMenu(toRaw({ status: 1 })).then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||
// 过滤旧前端菜单以及不是菜单类型以及路径为空
|
||||
res.data = res.data
|
||||
.filter(i => i.perms !== 'page' && i.menuType === 'M' && i.component)
|
||||
.map((item: any) => {
|
||||
state.options.push({
|
||||
label: item.menuName,
|
||||
value: item.component,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//获取当前系统设置的首页路径 111为configID
|
||||
getConfigKey('sys.homePage').then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
||||
state.default = res.data;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-row :gutter="16">
|
||||
<a-col :lg="12" :md="12" :xs="24" style="margin-bottom: 30px">
|
||||
<template v-if="state.edite">
|
||||
<a-form-item :label="t('views.system.setting.home')">
|
||||
<a-select
|
||||
ref="select"
|
||||
v-model:value="state.default"
|
||||
style="width: 240px"
|
||||
:disabled="false"
|
||||
:options="state.options"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-button type="primary" @click="fnSave">
|
||||
{{ t('views.system.setting.saveSubmit') }}
|
||||
</a-button>
|
||||
<a-button style="margin-left: 10px" @click="fnEdit(false)">
|
||||
{{ t('common.cancel') }}
|
||||
</a-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-form-item :label="t('views.system.setting.home')">
|
||||
<a-select
|
||||
ref="select"
|
||||
v-model:value="state.default"
|
||||
style="width: 240px"
|
||||
:disabled="true"
|
||||
:options="state.options"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-button type="dashed" @click="fnEdit(true)">
|
||||
{{ t('common.editText') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</a-col>
|
||||
<a-col :lg="12" :md="12" :xs="24">
|
||||
<a-typography>
|
||||
<a-typography-paragraph>
|
||||
{{ t('views.system.setting.homeInstruction') }}
|
||||
</a-typography-paragraph>
|
||||
</a-typography>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@@ -8,6 +8,7 @@ import ChangeHelpDoc from './components/change-help-doc.vue';
|
||||
import ChangeOfficialUrl from './components/change-official-url.vue';
|
||||
import ChangeI18n from './components/change-i18n.vue';
|
||||
import SystemReset from './components/system-reset.vue';
|
||||
import ChangeHome from './components/change-home-index.vue';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
@@ -49,6 +50,10 @@ const { t } = useI18n();
|
||||
</a-divider>
|
||||
<ChangeI18n></ChangeI18n>
|
||||
</div>
|
||||
<a-divider orientation="left">
|
||||
{{ t('views.system.setting.homeSet') }}
|
||||
</a-divider>
|
||||
<ChangeHome></ChangeHome>
|
||||
<a-divider orientation="left">
|
||||
{{ t('views.system.setting.reset') }}
|
||||
</a-divider>
|
||||
|
||||
Reference in New Issue
Block a user