372 lines
10 KiB
Vue
372 lines
10 KiB
Vue
<script setup lang="ts">
|
|
import { PageContainer } from 'antdv-pro-layout';
|
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
|
import { message } from 'ant-design-vue/es';
|
|
import {
|
|
reactive,
|
|
ref,
|
|
onMounted,
|
|
onBeforeUnmount,
|
|
markRaw,
|
|
useTemplateRef,
|
|
} from 'vue';
|
|
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';
|
|
import { listAllNeInfo } from '@/api/ne/neInfo';
|
|
import { parseDateToStr } from '@/utils/date-utils';
|
|
const { getDict } = useDictStore();
|
|
const appStore = useAppStore();
|
|
const route = useRoute();
|
|
const { t } = useI18n();
|
|
|
|
echarts.use([
|
|
TooltipComponent,
|
|
GaugeChart,
|
|
TitleComponent,
|
|
LegendComponent,
|
|
PieChart,
|
|
CanvasRenderer,
|
|
LabelLayout,
|
|
]);
|
|
|
|
/**图DOM节点实例对象 */
|
|
const statusBar = useTemplateRef<HTMLDivElement>('statusBar');
|
|
|
|
/**图实例对象 */
|
|
const statusBarChart = ref<any>(null);
|
|
|
|
/**表格字段列 */
|
|
let tableColumns: ColumnsType = [
|
|
{
|
|
title: t('views.index.object'),
|
|
dataIndex: 'neName',
|
|
align: 'left',
|
|
},
|
|
{
|
|
title: t('views.index.realNeStatus'),
|
|
dataIndex: 'serverState',
|
|
align: 'left',
|
|
key: 'status',
|
|
},
|
|
{
|
|
title: t('views.index.reloadTime'),
|
|
dataIndex: 'serverState',
|
|
align: 'left',
|
|
customRender(opt) {
|
|
if (opt.value?.refreshTime) {
|
|
return parseDateToStr(opt.value?.refreshTime);
|
|
}
|
|
return '-';
|
|
},
|
|
},
|
|
{
|
|
title: t('views.index.version'),
|
|
dataIndex: 'serverState',
|
|
align: 'left',
|
|
customRender(opt) {
|
|
return opt.value?.version || '-';
|
|
},
|
|
},
|
|
{
|
|
title: t('views.index.serialNum'),
|
|
dataIndex: 'serverState',
|
|
align: 'left',
|
|
customRender(opt) {
|
|
return opt.value?.sn || '-';
|
|
},
|
|
},
|
|
{
|
|
title: t('views.index.expiryDate'),
|
|
dataIndex: 'serverState',
|
|
align: 'left',
|
|
customRender(opt) {
|
|
return opt.value?.expire || '-';
|
|
},
|
|
},
|
|
{
|
|
title: t('views.index.ipAddress'),
|
|
dataIndex: 'ip',
|
|
align: 'left',
|
|
},
|
|
];
|
|
|
|
/**表格状态类型 */
|
|
type TabeStateType = {
|
|
/**加载等待 */
|
|
loading: boolean;
|
|
/**记录数据 */
|
|
data: Record<string, any>[];
|
|
/**勾选记录 */
|
|
selectedRowKeys: (string | number)[];
|
|
};
|
|
|
|
/**表格状态 */
|
|
let tableState: TabeStateType = reactive({
|
|
loading: false,
|
|
data: [],
|
|
selectedRowKeys: [],
|
|
});
|
|
|
|
/**状态 */
|
|
let serverState: any = ref({});
|
|
|
|
/**查询网元状态列表 */
|
|
async function fnGetList(reload: boolean = false) {
|
|
tableState.loading = !reload;
|
|
try {
|
|
const res = await listAllNeInfo({ bandStatus: true });
|
|
tableState.data = res.data;
|
|
} catch (error) {
|
|
console.error(error);
|
|
tableState.data = [];
|
|
}
|
|
tableState.loading = false;
|
|
if (tableState.data.length == 0) {
|
|
return;
|
|
}
|
|
|
|
var rightNum = 0;
|
|
var errorNum = 0;
|
|
for (const v of tableState.data) {
|
|
if (v?.serverState?.online) {
|
|
rightNum++;
|
|
} else {
|
|
errorNum++;
|
|
}
|
|
}
|
|
|
|
// 初始
|
|
if (!reload) {
|
|
// 选择第一个
|
|
if (tableState.data.length > 0) {
|
|
const item = tableState.data.find((item: any) => item.status === 1)
|
|
if (item) {
|
|
const id = item.id;
|
|
fnTableSelectedRowKeys([id]);
|
|
}
|
|
} else {
|
|
fnTableSelectedRowKeys(tableState.selectedRowKeys);
|
|
}
|
|
|
|
if (statusBar.value) {
|
|
fnDesign(statusBar.value, rightNum, errorNum);
|
|
}
|
|
} else {
|
|
statusBarChart.value.setOption({
|
|
series: [
|
|
{
|
|
data: [
|
|
{ value: rightNum, name: t('views.index.normal') },
|
|
{ value: errorNum, name: t('views.index.abnormal') },
|
|
],
|
|
},
|
|
],
|
|
});
|
|
}
|
|
}
|
|
|
|
function fnDesign(container: HTMLElement, rightNum: number, errorNum: number) {
|
|
/// 图表数据
|
|
const optionData: any = {
|
|
title: {
|
|
text: '',
|
|
subtext: '',
|
|
left: 'center',
|
|
},
|
|
tooltip: {
|
|
trigger: 'item',
|
|
},
|
|
legend: {
|
|
orient: 'vertical',
|
|
left: 'left',
|
|
},
|
|
color: dict.indexStatus.map(item => item.tagClass),
|
|
series: [
|
|
{
|
|
name: t('views.index.runStatus'),
|
|
type: 'pie',
|
|
radius: '70%',
|
|
center: ['50%', '50%'],
|
|
data: [
|
|
{ value: rightNum, name: t('views.index.normal') },
|
|
{ value: errorNum, name: t('views.index.abnormal') },
|
|
],
|
|
},
|
|
],
|
|
};
|
|
statusBarChart.value = markRaw(echarts.init(container, 'light'));
|
|
statusBarChart.value.setOption(optionData);
|
|
|
|
// 创建 ResizeObserver 实例
|
|
var observer = new ResizeObserver(entries => {
|
|
if (statusBarChart.value) {
|
|
statusBarChart.value.resize();
|
|
}
|
|
});
|
|
// 监听元素大小变化
|
|
observer.observe(container);
|
|
}
|
|
|
|
/**表格多选 */
|
|
function fnTableSelectedRowKeys(keys: (string | number)[]) {
|
|
if (keys.length <= 0) return;
|
|
const id = keys[0];
|
|
const row: any = tableState.data.find((item: any) => item.id === id);
|
|
if (!row) {
|
|
message.error(t('views.index.neStatus'), 2);
|
|
return;
|
|
}
|
|
const neState = row.serverState;
|
|
if (!neState?.online) {
|
|
message.error(t('views.index.neStatus'), 2);
|
|
return;
|
|
}
|
|
tableState.selectedRowKeys = keys;
|
|
// Mem 将KB转换为MB
|
|
// const totalMemInKB = neState.mem?.totalMem;
|
|
// const nfUsedMemInKB = neState.mem?.nfUsedMem;
|
|
// const sysMemUsageInKB = neState.mem?.sysMemUsage;
|
|
// const totalMemInMB = Math.round((totalMemInKB / 1024) * 100) / 100;
|
|
// const nfUsedMemInMB = Math.round((nfUsedMemInKB / 1024) * 100) / 100;
|
|
// const sysMemUsageInMB = Math.round((sysMemUsageInKB / 1024) * 100) / 100;
|
|
|
|
// CPU
|
|
// const nfCpu = neState.cpu?.nfCpuUsage;
|
|
// const sysCpu = neState.cpu?.sysCpuUsage;
|
|
// const nfCpuP = Math.round(nfCpu) / 100;
|
|
// const sysCpuP = Math.round(sysCpu) / 100;
|
|
|
|
serverState.value = Object.assign(
|
|
{
|
|
// cpuUse: `NE:${nfCpuP}%; SYS:${sysCpuP}%`,
|
|
// memoryUse: `Total: ${totalMemInMB}MB; NE: ${nfUsedMemInMB}MB; SYS: ${sysMemUsageInMB}MB`,
|
|
neIP: row.ip,
|
|
},
|
|
neState
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 国际化翻译转换
|
|
*/
|
|
function fnLocale() {
|
|
let title = route.meta.title as string;
|
|
if (title.indexOf('router.') !== -1) {
|
|
title = t(title);
|
|
}
|
|
appStore.setTitle(title);
|
|
}
|
|
|
|
/**字典数据 */
|
|
let dict: {
|
|
/**网元信息状态 */
|
|
neInfoStatus: DictType[];
|
|
/**主页状态 */
|
|
indexStatus: DictType[];
|
|
} = reactive({
|
|
neInfoStatus: [],
|
|
indexStatus: [],
|
|
});
|
|
|
|
let timer: any;
|
|
let timerFlag: boolean = false;
|
|
onMounted(() => {
|
|
// 初始字典数据
|
|
Promise.allSettled([getDict('ne_info_status'), getDict('index_status')])
|
|
.then(resArr => {
|
|
if (resArr[0].status === 'fulfilled') {
|
|
dict.neInfoStatus = resArr[0].value;
|
|
}
|
|
if (resArr[1].status === 'fulfilled') {
|
|
dict.indexStatus = resArr[1].value;
|
|
}
|
|
})
|
|
.finally(async () => {
|
|
fnLocale();
|
|
await fnGetList(false);
|
|
timer = setInterval(() => {
|
|
if (timerFlag) return;
|
|
fnGetList(true);
|
|
}, 10_000); // 每隔10秒执行一次
|
|
});
|
|
});
|
|
|
|
// 在组件卸载之前清除定时器
|
|
onBeforeUnmount(() => {
|
|
clearInterval(timer);
|
|
timer = null;
|
|
timerFlag = true;
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<PageContainer :breadcrumb="{}">
|
|
<a-row :gutter="16">
|
|
<a-col :lg="14" :md="16" :xs="24">
|
|
<!-- 表格列表 -->
|
|
<a-table class="table" row-key="id" size="small" :columns="tableColumns" :data-source="tableState.data"
|
|
:loading="tableState.loading" :pagination="false" :scroll="{ x: true }" :row-selection="{
|
|
type: 'radio',
|
|
columnWidth: '48px',
|
|
selectedRowKeys: tableState.selectedRowKeys,
|
|
onChange: fnTableSelectedRowKeys,
|
|
}">
|
|
<template #bodyCell="{ column, record }">
|
|
<template v-if="column.key === 'status'">
|
|
<DictTag :options="dict.neInfoStatus" :value="record.status" />
|
|
</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" size="small">
|
|
<div style="width: 100%; min-height: 200px" ref="statusBar"></div>
|
|
</a-card>
|
|
<a-card :loading="tableState.loading" :title="`${t('views.index.mark')} - ${serverState.neName || 'OMC'}`"
|
|
style="margin-top: 16px" size="small">
|
|
<a-descriptions bordered :column="1" :label-style="{ width: '160px' }">
|
|
<a-descriptions-item :label="t('views.index.hostName')">
|
|
{{ serverState.hostname }}
|
|
</a-descriptions-item>
|
|
<a-descriptions-item :label="t('views.index.osInfo')">
|
|
{{ serverState.os }}
|
|
</a-descriptions-item>
|
|
<a-descriptions-item :label="t('views.index.ipAddress')">
|
|
{{ serverState.neIP }}
|
|
</a-descriptions-item>
|
|
<a-descriptions-item :label="t('views.index.version')">
|
|
{{ serverState.version }}
|
|
</a-descriptions-item>
|
|
<!-- <a-descriptions-item :label="t('views.index.capability')">
|
|
{{ serverState.capability }}
|
|
</a-descriptions-item> -->
|
|
<!-- <a-descriptions-item :label="t('views.index.cpuUse')">
|
|
{{ serverState.cpuUse }}
|
|
</a-descriptions-item>
|
|
<a-descriptions-item :label="t('views.index.memoryUse')">
|
|
{{ serverState.memoryUse }}
|
|
</a-descriptions-item> -->
|
|
<a-descriptions-item :label="t('views.index.serialNum')">
|
|
{{ serverState.sn }}
|
|
</a-descriptions-item>
|
|
<a-descriptions-item :label="t('views.index.expiryDate')">
|
|
{{ serverState.expire }}
|
|
</a-descriptions-item>
|
|
</a-descriptions>
|
|
</a-card>
|
|
</a-col>
|
|
</a-row>
|
|
</PageContainer>
|
|
</template>
|
|
|
|
<style lang="less" scoped></style>
|