优化指标界面

This commit is contained in:
lai
2024-11-29 17:17:54 +08:00
parent 512bd6d8eb
commit 9383c17484
3 changed files with 400 additions and 253 deletions

View File

@@ -607,12 +607,12 @@ onMounted(() => {
<template #icon><FormOutlined /></template> <template #icon><FormOutlined /></template>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
<!-- <a-tooltip> <a-tooltip>
<template #title>{{ t('common.deleteText') }}</template> <template #title>{{ t('common.deleteText') }}</template>
<a-button type="link" @click.prevent="fnRecordDelete(record)"> <a-button type="link" @click.prevent="fnRecordDelete(record)">
<template #icon><DeleteOutlined /></template> <template #icon><DeleteOutlined /></template>
</a-button> </a-button>
</a-tooltip> --> </a-tooltip>
</a-space> </a-space>
</template> </template>

View File

@@ -23,6 +23,7 @@ import {
nextTick, nextTick,
onBeforeUnmount, onBeforeUnmount,
h, h,
watch
} from 'vue'; } from 'vue';
import { PageContainer } from 'antdv-pro-layout'; import { PageContainer } from 'antdv-pro-layout';
import { message, Modal, TableColumnType } from 'ant-design-vue/es'; import { message, Modal, TableColumnType } from 'ant-design-vue/es';
@@ -43,6 +44,8 @@ import { generateColorRGBA } from '@/utils/generate-utils';
import { OptionsType, WS } from '@/plugins/ws-websocket'; import { OptionsType, WS } from '@/plugins/ws-websocket';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { LineOutlined } from '@ant-design/icons-vue'; import { LineOutlined } from '@ant-design/icons-vue';
import useLayoutStore from '@/store/modules/layout';
const layoutStore = useLayoutStore();
const neInfoStore = useNeInfoStore(); const neInfoStore = useNeInfoStore();
const route = useRoute(); const route = useRoute();
const { t, currentLocale } = useI18n(); const { t, currentLocale } = useI18n();
@@ -216,6 +219,22 @@ const statsColumns: TableColumnType<any>[] = [
key: 'title', key: 'title',
width: '65%', width: '65%',
}, },
{
title: t('views.perfManage.kpiOverView.totalValue'),
dataIndex: 'total',
key: 'total',
width: '24%',
sorter: (a: any, b: any) => a.total - b.total,
sortDirections: ['ascend', 'descend'],
},
{
title: t('views.perfManage.kpiOverView.avgValue'),
dataIndex: 'avg',
key: 'avg',
width: '24%',
sorter: (a: any, b: any) => a.avg - b.avg,
sortDirections: ['ascend', 'descend'],
},
{ {
title: t('views.perfManage.kpiOverView.maxValue'), title: t('views.perfManage.kpiOverView.maxValue'),
dataIndex: 'max', dataIndex: 'max',
@@ -389,11 +408,22 @@ function fnGetList() {
const values = tableState.data.map((item: any) => { const values = tableState.data.map((item: any) => {
return item[columns.key] ? Number(item[columns.key]) : 0; return item[columns.key] ? Number(item[columns.key]) : 0;
}); });
// 计算总值
const total = Number(values.reduce((sum, val) => sum + val, 0).toFixed(2));
// 计算平均值
const avg = values.length > 0
? Number((total / values.length).toFixed(2))
: 0;
kpiStats.value.push({ kpiStats.value.push({
kpiId: columns.key, kpiId: columns.key,
title: columns.title, title: columns.title,
max: values.length > 0 ? Math.max(...values) : 0, max: values.length > 0 ? Math.max(...values) : 0,
min: values.length > 0 ? Math.min(...values) : 0, min: values.length > 0 ? Math.min(...values) : 0,
avg: avg,
total: total
}); });
} }
} }
@@ -418,6 +448,7 @@ function fnRanderChart() {
position: function (pt: any) { position: function (pt: any) {
return [pt[0], '10%']; return [pt[0], '10%'];
}, },
confine: true, // 限制 tooltip 显示范围
}, },
xAxis: { xAxis: {
//x类别轴 //x类别轴
@@ -500,6 +531,9 @@ function fnRanderChartData() {
key: `${columns.key}`, key: `${columns.key}`,
type: 'line', type: 'line',
symbol: 'none', symbol: 'none',
symbolSize: 6,
smooth: 0.6,
showSymbol: true,
sampling: 'lttb', sampling: 'lttb',
itemStyle: { itemStyle: {
color: color, color: color,
@@ -549,8 +583,32 @@ function fnRanderChartData() {
xAxis: { xAxis: {
type: 'category', type: 'category',
boundaryGap: false, boundaryGap: false,
axisLabel: {
color: document.documentElement.getAttribute('data-theme') === 'dark'
? '#CACADA'
: '#333'
},
splitLine: {
show: true,
lineStyle: {
color: getSplitLineColor()
}
},
data: chartDataXAxisData, data: chartDataXAxisData,
}, },
yAxis: {
axisLabel: {
color: document.documentElement.getAttribute('data-theme') === 'dark'
? '#CACADA'
: '#333'
},
splitLine: {
show: true,
lineStyle: {
color: getSplitLineColor()
}
},
},
series: chartDataYSeriesData, series: chartDataYSeriesData,
}, },
{ {
@@ -647,29 +705,38 @@ function wsMessage(res: Record<string, any>) {
} }
// 添加一个变量来跟踪当前选中的行 // 添加一个变量来跟踪当前选中的行
const selectedRow = ref<string | null>(null); const selectedRow = ref<string[]>([]);
// 添加处理行点击的方法 // 添加处理行点击的方法
function handleRowClick(record: any) { function handleRowClick(record: any) {
if (selectedRow.value === record.kpiId) { const index = selectedRow.value.indexOf(record.kpiId);
// 如果点击的是当前选中的行,则取消选中
selectedRow.value = null; // 如果已经选中,取消选中
// 更新图表,显示所有指标 if (index > -1) {
for (let key in chartLegendSelected) { selectedRow.value.splice(index, 1);
chartLegendSelected[key] = true; chartLegendSelected[record.title] = false;
}
} else { } else {
// 选中新行 // 添加新的选中项
selectedRow.value = record.kpiId; selectedRow.value.push(record.kpiId);
// 更新图表,只显示选中的指标
for (let key in chartLegendSelected) { // 如果只有一个选中项,重置为 false
if (key === record.title) { if (selectedRow.value.length === 1) {
chartLegendSelected[key] = true; Object.keys(chartLegendSelected).forEach(key => {
} else {
chartLegendSelected[key] = false; chartLegendSelected[key] = false;
} });
} }
chartLegendSelected[record.title] = true;
} }
// 如果没有选中项,设置所有图例为 true
if (selectedRow.value.length === 0) {
Object.keys(chartLegendSelected).forEach(key => {
chartLegendSelected[key] = true;
});
}
// 更新图表设置
kpiChart.value.setOption({ kpiChart.value.setOption({
legend: { legend: {
selected: chartLegendSelected, selected: chartLegendSelected,
@@ -677,6 +744,73 @@ function handleRowClick(record: any) {
}); });
} }
// 添加一个函数来获取当前主题下的网格线颜色
function getSplitLineColor() {
return document.documentElement.getAttribute('data-theme') === 'dark'
? '#333333'
: '#E8E8E8'; // 亮色模式返回 undefined使用默认颜色
}
// 监听主题变化
watch(
() => layoutStore.proConfig.theme, // 监听的值
(newValue) => {
if (kpiChart.value) {
const splitLineColor = getSplitLineColor();
// 绘制图数据
kpiChart.value.setOption(
{
tooltip: {
trigger: 'axis',
position: function (pt: any) {
return [pt[0], '10%'];
},
confine: true, // 限制 tooltip 显示范围
backgroundColor: newValue === 'dark'
? 'rgba(48, 48, 48, 0.8)'
: 'rgba(255, 255, 255, 0.9)',
borderColor: newValue === 'dark'
? '#555'
: '#ddd',
textStyle: {
color: newValue === 'dark'
? '#CACADA'
: '#333'
},
},
xAxis: {
axisLabel: {
color: newValue === 'dark'
? '#CACADA'
: '#333'
},
splitLine: {
show: true,
lineStyle: {
color: splitLineColor
}
}
},
yAxis: {
axisLabel: {
color: newValue === 'dark'
? '#CACADA'
: '#333'
},
splitLine: {
show: true,
lineStyle: {
color: splitLineColor
}
}
}
}
);
}
}
);
onMounted(() => { onMounted(() => {
// 目前支持的 AMF AUSF MME MOCNGW NSSF SMF UDM UPF PCF // 目前支持的 AMF AUSF MME MOCNGW NSSF SMF UDM UPF PCF
// 获取网元网元列表 // 获取网元网元列表
@@ -714,11 +848,16 @@ onMounted(() => {
} }
// 查询当前小时 // 查询当前小时
const nowDate: Date = new Date(); const now = new Date();
nowDate.setMinutes(0, 0, 0); now.setMinutes(0, 0, 0);
queryRangePicker.value[0] = `${nowDate.getTime()}`; // 设置起始时间为整点前一小时
nowDate.setMinutes(59, 59, 59); const startTime = new Date(now);
queryRangePicker.value[1] = `${nowDate.getTime()}`; startTime.setHours(now.getHours() - 1);
queryRangePicker.value[0] = `${startTime.getTime()}`;
// 设置结束时间为整点
const endTime = new Date(now);
endTime.setMinutes(59, 59, 59);
queryRangePicker.value[1] = `${endTime.getTime()}`;
fnGetListTitle(); fnGetListTitle();
// 绘图 // 绘图
fnRanderChart(); fnRanderChart();
@@ -739,68 +878,42 @@ onBeforeUnmount(() => {
<template> <template>
<PageContainer> <PageContainer>
<a-card <a-card v-show="tableState.seached" :bordered="false" :body-style="{ marginBottom: '24px', paddingBottom: 0 }">
v-show="tableState.seached"
:bordered="false"
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
>
<!-- 表格搜索栏 --> <!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParamsFrom" layout="horizontal"> <a-form :model="queryParams" name="queryParamsFrom" layout="horizontal">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24"> <a-col :lg="6" :md="12" :xs="24">
<a-form-item name="neType" :label="t('views.ne.common.neType')"> <a-form-item name="neType" :label="t('views.ne.common.neType')">
<a-cascader <a-cascader v-model:value="state.neType" :options="neCascaderOptions" :allow-clear="false"
v-model:value="state.neType" :placeholder="t('common.selectPlease')" />
:options="neCascaderOptions"
:allow-clear="false"
:placeholder="t('common.selectPlease')"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="10" :md="12" :xs="24"> <a-col :lg="10" :md="12" :xs="24">
<a-form-item <a-form-item :label="t('views.perfManage.goldTarget.timeFrame')" name="timeFrame">
:label="t('views.perfManage.goldTarget.timeFrame')" <a-range-picker v-model:value="queryRangePicker" bordered :allow-clear="false"
name="timeFrame" :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss" value-format="x"
> style="width: 100%"></a-range-picker>
<a-range-picker
v-model:value="queryRangePicker"
bordered
:allow-clear="false"
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
value-format="x"
style="width: 100%"
></a-range-picker>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="4" :md="12" :xs="24"> <a-col :lg="4" :md="12" :xs="24">
<a-form-item <a-form-item :label="t('views.perfManage.goldTarget.interval')" name="interval">
:label="t('views.perfManage.goldTarget.interval')" <a-select v-model:value="queryParams.interval" :placeholder="t('common.selectPlease')" :options="[
name="interval" { label: '5S', value: 5 },
> { label: '1M', value: 60 },
<a-select { label: '5M', value: 300 },
v-model:value="queryParams.interval" { label: '15M', value: 900 },
:placeholder="t('common.selectPlease')" { label: '30M', value: 1800 },
:options="[ { label: '60M', value: 3600 },
{ label: '5S', value: 5 }, ]" />
{ label: '1M', value: 60 },
{ label: '5M', value: 300 },
{ label: '15M', value: 900 },
{ label: '30M', value: 1800 },
{ label: '60M', value: 3600 },
]"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="2" :md="12" :xs="24"> <a-col :lg="2" :md="12" :xs="24">
<a-form-item> <a-form-item>
<a-space :size="8"> <a-space :size="8">
<a-button <a-button type="primary" :loading="tableState.loading" @click.prevent="fnGetListTitle()">
type="primary" <template #icon>
:loading="tableState.loading" <SearchOutlined />
@click.prevent="fnGetListTitle()" </template>
>
<template #icon><SearchOutlined /></template>
{{ t('common.search') }} {{ t('common.search') }}
</a-button> </a-button>
</a-space> </a-space>
@@ -814,24 +927,18 @@ onBeforeUnmount(() => {
<!-- 插槽-卡片左侧侧 --> <!-- 插槽-卡片左侧侧 -->
<template #title> <template #title>
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-button <a-button type="primary" :loading="tableState.loading" @click.prevent="fnChangShowType()">
type="primary" <template #icon>
:loading="tableState.loading" <AreaChartOutlined />
@click.prevent="fnChangShowType()" </template>
>
<template #icon> <AreaChartOutlined /> </template>
{{ {{
tableState.showTable tableState.showTable
? t('views.perfManage.goldTarget.kpiChartTitle') ? t('views.perfManage.goldTarget.kpiChartTitle')
: t('views.perfManage.goldTarget.kpiTableTitle') : t('views.perfManage.goldTarget.kpiTableTitle')
}} }}
</a-button> </a-button>
<a-button <a-button type="dashed" :loading="tableState.loading" @click.prevent="fnRecordExport()"
type="dashed" v-show="tableState.showTable">
:loading="tableState.loading"
@click.prevent="fnRecordExport()"
v-show="tableState.showTable"
>
<template #icon> <template #icon>
<ExportOutlined /> <ExportOutlined />
</template> </template>
@@ -846,26 +953,23 @@ onBeforeUnmount(() => {
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.reloadText') }}</template> <template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()"> <a-button type="text" @click.prevent="fnGetList()">
<template #icon><ReloadOutlined /></template> <template #icon>
<ReloadOutlined />
</template>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
<TableColumnsDnd <TableColumnsDnd v-if="tableColumns.length > 0" :cache-id="`kpiTarget_${state.neType[0]}`"
v-if="tableColumns.length > 0" :columns="tableColumns" v-model:columns-dnd="tableColumnsDnd"></TableColumnsDnd>
:cache-id="`kpiTarget_${state.neType[0]}`"
:columns="tableColumns"
v-model:columns-dnd="tableColumnsDnd"
></TableColumnsDnd>
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.sizeText') }}</template> <template #title>{{ t('common.sizeText') }}</template>
<a-dropdown trigger="click" placement="bottomRight"> <a-dropdown trigger="click" placement="bottomRight">
<a-button type="text"> <a-button type="text">
<template #icon><ColumnHeightOutlined /></template> <template #icon>
<ColumnHeightOutlined />
</template>
</a-button> </a-button>
<template #overlay> <template #overlay>
<a-menu <a-menu :selected-keys="[tableState.size as string]" @click="fnTableSize">
:selected-keys="[tableState.size as string]"
@click="fnTableSize"
>
<a-menu-item key="default"> <a-menu-item key="default">
{{ t('common.size.default') }} {{ t('common.size.default') }}
</a-menu-item> </a-menu-item>
@@ -894,37 +998,19 @@ onBeforeUnmount(() => {
size="small" size="small"
/> />
</a-form-item> --> </a-form-item> -->
<a-form-item <a-form-item :label="t('views.perfManage.goldTarget.realTimeData')" name="chartRealTime">
:label="t('views.perfManage.goldTarget.realTimeData')" <a-switch :disabled="tableState.loading" v-model:checked="state.chartRealTime"
name="chartRealTime" :checked-children="t('common.switch.open')" :un-checked-children="t('common.switch.shut')"
> @change="fnRealTimeSwitch" size="small" />
<a-switch
:disabled="tableState.loading"
v-model:checked="state.chartRealTime"
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
@change="fnRealTimeSwitch"
size="small"
/>
</a-form-item> </a-form-item>
</a-form> </a-form>
</template> </template>
<!-- 表格列表 --> <!-- 表格列表 -->
<a-table <a-table v-show="tableState.showTable" class="table" row-key="id" :columns="tableColumnsDnd"
v-show="tableState.showTable" :loading="tableState.loading" :data-source="tableState.data" :size="tableState.size"
class="table" :pagination="tablePagination" :scroll="{ x: tableColumnsDnd.length * 200, y: 'calc(100vh - 480px)' }"
row-key="id" @resizeColumn="(w: number, col: any) => (col.width = w)" :show-expand-column="false" @change="fnTableChange">
:columns="tableColumnsDnd"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: tableColumnsDnd.length * 200, y: 'calc(100vh - 480px)' }"
@resizeColumn="(w:number, col:any) => (col.width = w)"
:show-expand-column="false"
@change="fnTableChange"
>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'timeGroup'"> <template v-if="column.key === 'timeGroup'">
{{ parseDateToStr(+record.timeGroup) }} {{ parseDateToStr(+record.timeGroup) }}
@@ -934,26 +1020,15 @@ onBeforeUnmount(() => {
<!-- 图表 --> <!-- 图表 -->
<div style="padding: 24px" v-show="!tableState.showTable"> <div style="padding: 24px" v-show="!tableState.showTable">
<div <div ref="kpiChartDom" class="chart-container" style="height: 450px; width: 100%"></div>
ref="kpiChartDom"
class="chart-container"
style="height: 450px; width: 100%"
></div>
<div class="table-container"> <div class="table-container">
<a-table <a-table :columns="statsColumns" :data-source="kpiStats" :pagination="false" :scroll="{ y: 250 }" size="small"
:columns="statsColumns" :custom-row="record => ({
:data-source="kpiStats" onClick: () => handleRowClick(record),
:pagination="false" class: selectedRow.includes(record.kpiId) ? 'selected-row' : ''
:scroll="{ y: 250 }" })
size="small" " />
:custom-row="
record => ({
onClick: () => handleRowClick(record),
class: record.kpiId === selectedRow ? 'selected-row' : '',
})
"
/>
</div> </div>
</div> </div>
</a-card> </a-card>

View File

@@ -23,6 +23,7 @@ import {
nextTick, nextTick,
onBeforeUnmount, onBeforeUnmount,
h, h,
watch,
} from 'vue'; } from 'vue';
import { PageContainer } from 'antdv-pro-layout'; import { PageContainer } from 'antdv-pro-layout';
import { message, Modal, TableColumnType } from 'ant-design-vue/es'; import { message, Modal, TableColumnType } from 'ant-design-vue/es';
@@ -45,11 +46,15 @@ import { OptionsType, WS } from '@/plugins/ws-websocket';
import { LineOutlined } from '@ant-design/icons-vue'; import { LineOutlined } from '@ant-design/icons-vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import dayjs, { Dayjs } from 'dayjs'; import dayjs, { Dayjs } from 'dayjs';
import useLayoutStore from '@/store/modules/layout';
const layoutStore = useLayoutStore();
const neInfoStore = useNeInfoStore(); const neInfoStore = useNeInfoStore();
const route = useRoute(); const route = useRoute();
const { t, currentLocale } = useI18n(); const { t, currentLocale } = useI18n();
const ws = new WS(); const ws = new WS();
echarts.use([ echarts.use([
TooltipComponent, TooltipComponent,
GridComponent, GridComponent,
@@ -239,6 +244,14 @@ const statsColumns: TableColumnType<any>[] = [
key: 'title', key: 'title',
width: '65%', width: '65%',
}, },
{
title: t('views.perfManage.kpiOverView.avgValue'),
dataIndex: 'avg',
key: 'avg',
width: '24%',
sorter: (a: any, b: any) => a.avg - b.avg,
sortDirections: ['ascend', 'descend'],
},
{ {
title: t('views.perfManage.kpiOverView.maxValue'), title: t('views.perfManage.kpiOverView.maxValue'),
dataIndex: 'max', dataIndex: 'max',
@@ -435,11 +448,21 @@ function fnGetList() {
const values = tableState.data.map((item: any) => { const values = tableState.data.map((item: any) => {
return item[columns.key] ? Number(item[columns.key]) : 0; return item[columns.key] ? Number(item[columns.key]) : 0;
}); });
// 计算总值
const total = Number(values.reduce((sum, val) => sum + val, 0).toFixed(2));
// 计算平均值
const avg = values.length > 0
? Number((total / values.length).toFixed(2))
: 0;
kpiStats.value.push({ kpiStats.value.push({
kpiId: columns.key, kpiId: columns.key,
title: columns.title, title: columns.title,
max: values.length > 0 ? Math.max(...values) : 0, max: values.length > 0 ? Math.max(...values) : 0,
min: values.length > 0 ? Math.min(...values) : 0, min: values.length > 0 ? Math.min(...values) : 0,
avg
}); });
} }
} }
@@ -463,6 +486,18 @@ function fnRanderChart() {
position: function (pt: any) { position: function (pt: any) {
return [pt[0], '10%']; return [pt[0], '10%'];
}, },
confine: true, // 限制 tooltip 显示范围
backgroundColor: document.documentElement.getAttribute('data-theme') === 'dark'
? 'rgba(48, 48, 48, 0.8)'
: 'rgba(255, 255, 255, 0.9)',
borderColor: document.documentElement.getAttribute('data-theme') === 'dark'
? '#555'
: '#ddd',
textStyle: {
color: document.documentElement.getAttribute('data-theme') === 'dark'
? '#CACADA'
: '#333'
},
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
@@ -541,6 +576,9 @@ function fnRanderChartData() {
key: `${columns.key}`, key: `${columns.key}`,
type: 'line', type: 'line',
symbol: 'none', symbol: 'none',
symbolSize: 6,
smooth: 0.6,
showSymbol: true,
sampling: 'lttb', sampling: 'lttb',
itemStyle: { itemStyle: {
color: color, color: color,
@@ -594,8 +632,32 @@ function fnRanderChartData() {
xAxis: { xAxis: {
type: 'category', type: 'category',
boundaryGap: false, boundaryGap: false,
axisLabel: {
color: document.documentElement.getAttribute('data-theme') === 'dark'
? '#CACADA'
: '#333'
},
splitLine: {
show: true,
lineStyle: {
color: getSplitLineColor()
}
},
data: chartDataXAxisData, data: chartDataXAxisData,
}, },
yAxis: {
axisLabel: {
color: document.documentElement.getAttribute('data-theme') === 'dark'
? '#CACADA'
: '#333'
},
splitLine: {
show: true,
lineStyle: {
color: getSplitLineColor()
}
},
},
series: chartDataYSeriesData, series: chartDataYSeriesData,
}, },
{ {
@@ -680,29 +742,38 @@ function wsMessage(res: Record<string, any>) {
} }
// 添加一个变量来跟踪当前选中的行 // 添加一个变量来跟踪当前选中的行
const selectedRow = ref<string | null>(null); const selectedRow = ref<string[]>([]);
// 添加处理行点击的方法 // 添加处理行点击的方法
function handleRowClick(record: any) { function handleRowClick(record: any) {
if (selectedRow.value === record.kpiId) { const index = selectedRow.value.indexOf(record.kpiId);
// 如果点击的是当前选中的行,则取消选中
selectedRow.value = null; // 如果已经选中,取消选中
// 更新图表,显示所有指标 if (index > -1) {
for (let key in chartLegendSelected) { selectedRow.value.splice(index, 1);
chartLegendSelected[key] = true; chartLegendSelected[record.title] = false;
}
} else { } else {
// 选中新行 // 添加新的选中项
selectedRow.value = record.kpiId; selectedRow.value.push(record.kpiId);
// 更新图表,只显示选中的指标
for (let key in chartLegendSelected) { // 如果只有一个选中项,重置为 false
if (key === record.title) { if (selectedRow.value.length === 1) {
chartLegendSelected[key] = true; Object.keys(chartLegendSelected).forEach(key => {
} else {
chartLegendSelected[key] = false; chartLegendSelected[key] = false;
} });
} }
chartLegendSelected[record.title] = true;
} }
// 如果没有选中项,设置所有图例为 true
if (selectedRow.value.length === 0) {
Object.keys(chartLegendSelected).forEach(key => {
chartLegendSelected[key] = true;
});
}
// 更新图表设置
kpiChart.value.setOption({ kpiChart.value.setOption({
legend: { legend: {
selected: chartLegendSelected, selected: chartLegendSelected,
@@ -710,6 +781,66 @@ function handleRowClick(record: any) {
}); });
} }
// 监听主题变化
watch(
() => layoutStore.proConfig.theme, // 监听的值
(newValue) => {
if (kpiChart.value) {
const splitLineColor = getSplitLineColor();
// 绘制图数据
kpiChart.value.setOption(
{
tooltip: {
trigger: 'axis',
position: function (pt: any) {
return [pt[0], '10%'];
},
confine: true, // 限制 tooltip 显示范围
backgroundColor: newValue === 'dark'
? 'rgba(48, 48, 48, 0.8)'
: 'rgba(255, 255, 255, 0.9)',
borderColor: newValue === 'dark'
? '#555'
: '#ddd',
textStyle: {
color: newValue === 'dark'
? '#CACADA'
: '#333'
},
},
xAxis: {
axisLabel: {
color: newValue === 'dark'
? '#CACADA'
: '#333'
},
splitLine: {
show: true,
lineStyle: {
color: splitLineColor
}
}
},
yAxis: {
axisLabel: {
color: newValue === 'dark'
? '#CACADA'
: '#333'
},
splitLine: {
show: true,
lineStyle: {
color: splitLineColor
}
}
}
}
);
}
}
);
onMounted(() => { onMounted(() => {
// 目前支持的 AMF AUSF MME MOCNGW NSSF SMF UDM UPF PCF // 目前支持的 AMF AUSF MME MOCNGW NSSF SMF UDM UPF PCF
// 获取网元网元列表 // 获取网元网元列表
@@ -761,7 +892,6 @@ onMounted(() => {
} }
// 查询当前小时 // 查询当前小时
const now = new Date(); const now = new Date();
now.setMinutes(0, 0, 0); now.setMinutes(0, 0, 0);
// 设置起始时间为整点前一小时 // 设置起始时间为整点前一小时
@@ -794,51 +924,31 @@ onBeforeUnmount(() => {
<template> <template>
<PageContainer> <PageContainer>
<a-card <a-card v-show="tableState.seached" :bordered="false" :body-style="{ marginBottom: '24px', paddingBottom: 0 }">
v-show="tableState.seached"
:bordered="false"
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
>
<!-- 表格搜索栏 --> <!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParamsFrom" layout="horizontal"> <a-form :model="queryParams" name="queryParamsFrom" layout="horizontal">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24"> <a-col :lg="6" :md="12" :xs="24">
<a-form-item name="neType" :label="t('views.ne.common.neType')"> <a-form-item name="neType" :label="t('views.ne.common.neType')">
<a-cascader <a-cascader v-model:value="state.neType" :options="neCascaderOptions" :allow-clear="false"
v-model:value="state.neType" :placeholder="t('common.selectPlease')" />
:options="neCascaderOptions"
:allow-clear="false"
:placeholder="t('common.selectPlease')"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="10" :md="12" :xs="24"> <a-col :lg="10" :md="12" :xs="24">
<a-form-item <a-form-item :label="t('views.perfManage.goldTarget.timeFrame')" name="timeFrame">
:label="t('views.perfManage.goldTarget.timeFrame')" <a-range-picker v-model:value="queryRangePicker" bordered :allow-clear="false"
name="timeFrame" :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss" value-format="x" :presets="ranges"
> style="width: 100%"></a-range-picker>
<a-range-picker
v-model:value="queryRangePicker"
bordered
:allow-clear="false"
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
value-format="x"
:presets="ranges"
style="width: 100%"
></a-range-picker>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="2" :md="12" :xs="24"> <a-col :lg="2" :md="12" :xs="24">
<a-form-item> <a-form-item>
<a-space :size="8"> <a-space :size="8">
<a-button <a-button type="primary" :loading="tableState.loading" @click.prevent="fnGetListTitle()">
type="primary" <template #icon>
:loading="tableState.loading" <SearchOutlined />
@click.prevent="fnGetListTitle()" </template>
>
<template #icon><SearchOutlined /></template>
{{ t('common.search') }} {{ t('common.search') }}
</a-button> </a-button>
</a-space> </a-space>
@@ -852,24 +962,18 @@ onBeforeUnmount(() => {
<!-- 插槽-卡片左侧侧 --> <!-- 插槽-卡片左侧侧 -->
<template #title> <template #title>
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-button <a-button type="primary" :loading="tableState.loading" @click.prevent="fnChangShowType()">
type="primary" <template #icon>
:loading="tableState.loading" <AreaChartOutlined />
@click.prevent="fnChangShowType()" </template>
>
<template #icon> <AreaChartOutlined /> </template>
{{ {{
tableState.showTable tableState.showTable
? t('views.perfManage.goldTarget.kpiChartTitle') ? t('views.perfManage.goldTarget.kpiChartTitle')
: t('views.perfManage.goldTarget.kpiTableTitle') : t('views.perfManage.goldTarget.kpiTableTitle')
}} }}
</a-button> </a-button>
<a-button <a-button type="dashed" :loading="tableState.loading" @click.prevent="fnRecordExport()"
type="dashed" v-show="tableState.showTable">
:loading="tableState.loading"
@click.prevent="fnRecordExport()"
v-show="tableState.showTable"
>
<template #icon> <template #icon>
<ExportOutlined /> <ExportOutlined />
</template> </template>
@@ -884,26 +988,23 @@ onBeforeUnmount(() => {
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.reloadText') }}</template> <template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()"> <a-button type="text" @click.prevent="fnGetList()">
<template #icon><ReloadOutlined /></template> <template #icon>
<ReloadOutlined />
</template>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
<TableColumnsDnd <TableColumnsDnd v-if="tableColumns.length > 0" :cache-id="`kpiTarget_${state.neType[0]}`"
v-if="tableColumns.length > 0" :columns="tableColumns" v-model:columns-dnd="tableColumnsDnd"></TableColumnsDnd>
:cache-id="`kpiTarget_${state.neType[0]}`"
:columns="tableColumns"
v-model:columns-dnd="tableColumnsDnd"
></TableColumnsDnd>
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.sizeText') }}</template> <template #title>{{ t('common.sizeText') }}</template>
<a-dropdown trigger="click" placement="bottomRight"> <a-dropdown trigger="click" placement="bottomRight">
<a-button type="text"> <a-button type="text">
<template #icon><ColumnHeightOutlined /></template> <template #icon>
<ColumnHeightOutlined />
</template>
</a-button> </a-button>
<template #overlay> <template #overlay>
<a-menu <a-menu :selected-keys="[tableState.size as string]" @click="fnTableSize">
:selected-keys="[tableState.size as string]"
@click="fnTableSize"
>
<a-menu-item key="default"> <a-menu-item key="default">
{{ t('common.size.default') }} {{ t('common.size.default') }}
</a-menu-item> </a-menu-item>
@@ -932,61 +1033,32 @@ onBeforeUnmount(() => {
size="small" size="small"
/> />
</a-form-item> --> </a-form-item> -->
<a-form-item <a-form-item :label="t('views.perfManage.goldTarget.realTimeData')" name="chartRealTime">
:label="t('views.perfManage.goldTarget.realTimeData')" <a-switch :disabled="tableState.loading" v-model:checked="state.chartRealTime"
name="chartRealTime" :checked-children="t('common.switch.open')" :un-checked-children="t('common.switch.shut')"
> @change="fnRealTimeSwitch" size="small" />
<a-switch
:disabled="tableState.loading"
v-model:checked="state.chartRealTime"
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
@change="fnRealTimeSwitch"
size="small"
/>
</a-form-item> </a-form-item>
</a-form> </a-form>
</template> </template>
<!-- 表格列表 --> <!-- 表格列表 -->
<a-table <a-table v-show="tableState.showTable" class="table" row-key="id" :columns="tableColumnsDnd"
v-show="tableState.showTable" :loading="tableState.loading" :data-source="tableState.data" :size="tableState.size"
class="table" :pagination="tablePagination" :scroll="{ x: tableColumnsDnd.length * 200, y: 'calc(100vh - 480px)' }"
row-key="id" @resizeColumn="(w: number, col: any) => (col.width = w)" :show-expand-column="false" @change="fnTableChange">
:columns="tableColumnsDnd"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: tableColumnsDnd.length * 200, y: 'calc(100vh - 480px)' }"
@resizeColumn="(w:number, col:any) => (col.width = w)"
:show-expand-column="false"
@change="fnTableChange"
>
</a-table> </a-table>
<!-- 图表 --> <!-- 图表 -->
<div style="padding: 24px" v-show="!tableState.showTable"> <div style="padding: 24px" v-show="!tableState.showTable">
<div <div ref="kpiChartDom" class="chart-container" style="height: 450px; width: 100%"></div>
ref="kpiChartDom"
class="chart-container"
style="height: 450px; width: 100%"
></div>
<div class="table-container"> <div class="table-container">
<a-table <a-table :columns="statsColumns" :data-source="kpiStats" :pagination="false" :scroll="{ y: 250 }" size="small"
:columns="statsColumns" :custom-row="record => ({
:data-source="kpiStats" onClick: () => handleRowClick(record),
:pagination="false" class: selectedRow.includes(record.kpiId) ? 'selected-row' : ''
:scroll="{ y: 250 }" })
size="small" " />
:custom-row="
record => ({
onClick: () => handleRowClick(record),
class: record.kpiId === selectedRow ? 'selected-row' : '',
})
"
/>
</div> </div>
</div> </div>
</a-card> </a-card>