同步主线antd3
This commit is contained in:
@@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network OMC"
|
|||||||
VITE_APP_CODE = "OMC"
|
VITE_APP_CODE = "OMC"
|
||||||
|
|
||||||
# 应用版本
|
# 应用版本
|
||||||
VITE_APP_VERSION = "2.241028"
|
VITE_APP_VERSION = "2.241102"
|
||||||
|
|
||||||
# 接口基础URL地址-不带/后缀
|
# 接口基础URL地址-不带/后缀
|
||||||
VITE_API_BASE_URL = "/omc-api"
|
VITE_API_BASE_URL = "/omc-api"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network OMC"
|
|||||||
VITE_APP_CODE = "OMC"
|
VITE_APP_CODE = "OMC"
|
||||||
|
|
||||||
# 应用版本
|
# 应用版本
|
||||||
VITE_APP_VERSION = "2.241028"
|
VITE_APP_VERSION = "2.241102"
|
||||||
|
|
||||||
# 接口基础URL地址-不带/后缀
|
# 接口基础URL地址-不带/后缀
|
||||||
VITE_API_BASE_URL = "/omc-api"
|
VITE_API_BASE_URL = "/omc-api"
|
||||||
|
|||||||
@@ -567,6 +567,8 @@ export default {
|
|||||||
rowInfo: "Info",
|
rowInfo: "Info",
|
||||||
type: "Type",
|
type: "Type",
|
||||||
duration: "Duration",
|
duration: "Duration",
|
||||||
|
seizureTime: "Call Start Time",
|
||||||
|
releaseTime: "Hangup Time",
|
||||||
caller: "Caller",
|
caller: "Caller",
|
||||||
called: "Called",
|
called: "Called",
|
||||||
result: "Result",
|
result: "Result",
|
||||||
@@ -1069,6 +1071,8 @@ export default {
|
|||||||
expression:'Expression',
|
expression:'Expression',
|
||||||
description:' Description',
|
description:' Description',
|
||||||
kpiSet:' Statistical Settings',
|
kpiSet:' Statistical Settings',
|
||||||
|
sixHoursAgo:'Six hours ago',
|
||||||
|
threeHoursAgo:'Three hours ago.',
|
||||||
delCustomTip:'Confirm deletion of data item with record number {num}?',
|
delCustomTip:'Confirm deletion of data item with record number {num}?',
|
||||||
delCustom:' Successfully delete record number {num} custom indicator',
|
delCustom:' Successfully delete record number {num} custom indicator',
|
||||||
addCustom:' Add custom indicator',
|
addCustom:' Add custom indicator',
|
||||||
@@ -1095,6 +1099,7 @@ export default {
|
|||||||
"layout3": "Layout 3"
|
"layout3": "Layout 3"
|
||||||
},
|
},
|
||||||
kpiOverView:{
|
kpiOverView:{
|
||||||
|
"kpiChartTitle":"Overview of NE metrics",
|
||||||
"changeLine":"Change to Line Charts",
|
"changeLine":"Change to Line Charts",
|
||||||
"changeBar":"Change to Bar Charts",
|
"changeBar":"Change to Bar Charts",
|
||||||
"chooseShowMetrics":"Select the metric you want to display",
|
"chooseShowMetrics":"Select the metric you want to display",
|
||||||
|
|||||||
@@ -567,6 +567,8 @@ export default {
|
|||||||
rowInfo: "记录信息",
|
rowInfo: "记录信息",
|
||||||
type: "记录类型",
|
type: "记录类型",
|
||||||
duration: "通话时长",
|
duration: "通话时长",
|
||||||
|
seizureTime: "呼叫开始时间",
|
||||||
|
releaseTime: "挂断结束时间",
|
||||||
caller: "主叫",
|
caller: "主叫",
|
||||||
called: "被叫",
|
called: "被叫",
|
||||||
result: "结果",
|
result: "结果",
|
||||||
@@ -1069,6 +1071,8 @@ export default {
|
|||||||
expression:'计算公式',
|
expression:'计算公式',
|
||||||
description:'描述',
|
description:'描述',
|
||||||
kpiSet:'统计设置',
|
kpiSet:'统计设置',
|
||||||
|
sixHoursAgo:'6小时前',
|
||||||
|
threeHoursAgo:'3小时前',
|
||||||
delCustomTip:'确认删除记录编号为 {num} 的数据项?',
|
delCustomTip:'确认删除记录编号为 {num} 的数据项?',
|
||||||
delCustom:'成功删除记录编号为 {num} 自定义指标',
|
delCustom:'成功删除记录编号为 {num} 自定义指标',
|
||||||
addCustom:'添加自定义指标',
|
addCustom:'添加自定义指标',
|
||||||
@@ -1095,6 +1099,7 @@ export default {
|
|||||||
"layout3": "布局3"
|
"layout3": "布局3"
|
||||||
},
|
},
|
||||||
kpiOverView:{
|
kpiOverView:{
|
||||||
|
"kpiChartTitle":"网元指标概览",
|
||||||
"changeLine":"切换为折线图",
|
"changeLine":"切换为折线图",
|
||||||
"changeBar":"切换为柱状图",
|
"changeBar":"切换为柱状图",
|
||||||
"chooseShowMetrics":"选择需要显示的指标",
|
"chooseShowMetrics":"选择需要显示的指标",
|
||||||
@@ -1341,8 +1346,8 @@ export default {
|
|||||||
filter: "全局过滤",
|
filter: "全局过滤",
|
||||||
startTime: '开始时间',
|
startTime: '开始时间',
|
||||||
endTime: '结束时间',
|
endTime: '结束时间',
|
||||||
today: '昨天',
|
today: '今天',
|
||||||
yesterday: '今天',
|
yesterday: '昨天',
|
||||||
week: '本周',
|
week: '本周',
|
||||||
month: '本月',
|
month: '本月',
|
||||||
avgLoad: '平均负载',
|
avgLoad: '平均负载',
|
||||||
|
|||||||
@@ -167,6 +167,13 @@ let tableColumns: ColumnsType = [
|
|||||||
return cdrJSON.calledParty;
|
return cdrJSON.calledParty;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.result'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
key: 'cause',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('views.dashboard.cdr.duration'),
|
title: t('views.dashboard.cdr.duration'),
|
||||||
dataIndex: 'cdrJSON',
|
dataIndex: 'cdrJSON',
|
||||||
@@ -181,20 +188,29 @@ let tableColumns: ColumnsType = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.dashboard.cdr.result'),
|
title: t('views.dashboard.cdr.seizureTime'),
|
||||||
dataIndex: 'cdrJSON',
|
dataIndex: 'cdrJSON',
|
||||||
key: 'cause',
|
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 150,
|
width: 200,
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.dashboard.cdr.time'),
|
|
||||||
dataIndex: 'cdrJSON',
|
|
||||||
align: 'center',
|
|
||||||
width: 150,
|
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
const cdrJSON = opt.value;
|
const cdrJSON = opt.value;
|
||||||
return parseDateToStr(+cdrJSON.releaseTime * 1000);
|
if (typeof cdrJSON.seizureTime === 'number') {
|
||||||
|
return parseDateToStr(+cdrJSON.seizureTime * 1000);
|
||||||
|
}
|
||||||
|
return cdrJSON.seizureTime;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.releaseTime'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 200,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
if (typeof cdrJSON.releaseTime === 'number') {
|
||||||
|
return parseDateToStr(+cdrJSON.releaseTime * 1000);
|
||||||
|
}
|
||||||
|
return cdrJSON.releaseTime;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -774,61 +790,73 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template #expandedRowRender="{ record }">
|
<template #expandedRowRender="{ record }">
|
||||||
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
|
<a-row :gutter="16">
|
||||||
<a-divider orientation="left">
|
<a-col :lg="5" :md="12" :xs="24">
|
||||||
{{ t('views.dashboard.cdr.cdrInfo') }}
|
<a-divider orientation="left">
|
||||||
</a-divider>
|
{{ t('views.dashboard.cdr.cdrInfo') }}
|
||||||
<div>
|
</a-divider>
|
||||||
<span>{{ t('views.ne.common.neName') }}: </span>
|
<div>
|
||||||
<span>{{ record.neName }}</span>
|
<span>{{ t('views.ne.common.neName') }}: </span>
|
||||||
</div>
|
<span>{{ record.neName }}</span>
|
||||||
<div>
|
</div>
|
||||||
<span>{{ t('views.ne.common.rmUid') }}: </span>
|
<div>
|
||||||
<span>{{ record.rmUID }}</span>
|
<span>{{ t('views.ne.common.rmUid') }}: </span>
|
||||||
</div>
|
<span>{{ record.rmUID }}</span>
|
||||||
<div>
|
</div>
|
||||||
<span>{{ t('views.dashboard.cdr.time') }}: </span>
|
<div>
|
||||||
<span>{{ parseDateToStr(+record.timestamp * 1000) }}</span>
|
<span>{{ t('views.dashboard.cdr.time') }}: </span>
|
||||||
</div>
|
<span>{{ parseDateToStr(+record.timestamp * 1000) }}</span>
|
||||||
<a-divider orientation="left">
|
</div>
|
||||||
{{ t('views.dashboard.cdr.rowInfo') }}
|
</a-col>
|
||||||
</a-divider>
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<div>
|
<a-divider orientation="left">
|
||||||
<span>{{ t('views.dashboard.cdr.type') }}: </span>
|
{{ t('views.dashboard.cdr.rowInfo') }}
|
||||||
<DictTag
|
</a-divider>
|
||||||
:options="dict.cdrCallType"
|
<div>
|
||||||
:value="record.cdrJSON.callType"
|
<span>{{ t('views.dashboard.cdr.type') }}: </span>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>{{ t('views.dashboard.cdr.duration') }}: </span>
|
|
||||||
<span v-if="record.cdrJSON.callType !== 'sms'">
|
|
||||||
{{ parseDuration(record.cdrJSON.callDuration) }}
|
|
||||||
</span>
|
|
||||||
<span v-else> - </span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>{{ t('views.dashboard.cdr.caller') }}: </span>
|
|
||||||
<span>{{ record.cdrJSON.callerParty }}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>{{ t('views.dashboard.cdr.called') }}: </span>
|
|
||||||
<span>{{ record.cdrJSON.calledParty }}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>{{ t('views.dashboard.cdr.result') }}: </span>
|
|
||||||
<span v-if="record.cdrJSON.callType !== 'sms'">
|
|
||||||
<DictTag
|
<DictTag
|
||||||
:options="dict.cdrSipCode"
|
:options="dict.cdrCallType"
|
||||||
:value="record.cdrJSON.cause"
|
:value="record.cdrJSON.callType"
|
||||||
value-default="0"
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</div>
|
||||||
<span v-else>
|
<div>
|
||||||
{{ t('views.dashboard.cdr.resultOk') }}
|
<span>{{ t('views.dashboard.cdr.duration') }}: </span>
|
||||||
</span>
|
<span v-if="record.cdrJSON.callType !== 'sms'">
|
||||||
</div>
|
{{ parseDuration(record.cdrJSON.callDuration) }}
|
||||||
</div>
|
</span>
|
||||||
|
<span v-else> - </span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.caller') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.callerParty }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.called') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.calledParty }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.result') }}: </span>
|
||||||
|
<span v-if="record.cdrJSON.callType !== 'sms'">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.cdrSipCode"
|
||||||
|
:value="record.cdrJSON.cause"
|
||||||
|
value-default="0"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ t('views.dashboard.cdr.resultOk') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.seizureTime') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.seizureTime }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.releaseTime') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.releaseTime }}</span>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|||||||
@@ -780,9 +780,6 @@ onMounted(() => {
|
|||||||
ok-text="TXT"
|
ok-text="TXT"
|
||||||
ok-type="default"
|
ok-type="default"
|
||||||
@confirm="fnExportList('txt')"
|
@confirm="fnExportList('txt')"
|
||||||
:show-cancel="false"
|
|
||||||
cancel-text="CSV"
|
|
||||||
@cancel="fnExportList('csv')"
|
|
||||||
>
|
>
|
||||||
<a-button type="dashed">
|
<a-button type="dashed">
|
||||||
<template #icon><ExportOutlined /></template>
|
<template #icon><ExportOutlined /></template>
|
||||||
|
|||||||
@@ -24,10 +24,9 @@ import {
|
|||||||
onBeforeUnmount,
|
onBeforeUnmount,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { message, Modal } from 'ant-design-vue/lib';
|
import { message, Modal } from 'ant-design-vue/es';
|
||||||
import { ColumnsType } from 'ant-design-vue/lib/table';
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
|
|
||||||
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
|
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
|
||||||
import {
|
import {
|
||||||
RESULT_CODE_ERROR,
|
RESULT_CODE_ERROR,
|
||||||
@@ -43,6 +42,7 @@ import saveAs from 'file-saver';
|
|||||||
import { generateColorRGBA } from '@/utils/generate-utils';
|
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 dayjs, { Dayjs } from 'dayjs';
|
||||||
const neInfoStore = useNeInfoStore();
|
const neInfoStore = useNeInfoStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t, currentLocale } = useI18n();
|
const { t, currentLocale } = useI18n();
|
||||||
@@ -78,11 +78,24 @@ let neCascaderOptions = ref<Record<string, any>[]>([]);
|
|||||||
/**记录开始结束时间 */
|
/**记录开始结束时间 */
|
||||||
let queryRangePicker = ref<[string, string]>(['', '']);
|
let queryRangePicker = ref<[string, string]>(['', '']);
|
||||||
|
|
||||||
|
/**时间选择 */
|
||||||
|
let ranges = ref<Record<string, [Dayjs, Dayjs]>>({
|
||||||
|
[t('views.perfManage.customTarget.sixHoursAgo')]: [
|
||||||
|
dayjs().subtract(6, 'hours'),
|
||||||
|
dayjs(),
|
||||||
|
],
|
||||||
|
[t('views.perfManage.customTarget.threeHoursAgo')]: [
|
||||||
|
dayjs().subtract(3, 'hours'),
|
||||||
|
dayjs(),
|
||||||
|
],
|
||||||
|
[t('views.monitor.monitor.today')]: [dayjs().startOf('day'), dayjs()],
|
||||||
|
});
|
||||||
|
|
||||||
/**表格字段列 */
|
/**表格字段列 */
|
||||||
let tableColumns = ref<ColumnsType>([]);
|
let tableColumns = ref<any[]>([]);
|
||||||
|
|
||||||
/**表格字段列排序 */
|
/**表格字段列排序 */
|
||||||
let tableColumnsDnd = ref<ColumnsType>([]);
|
let tableColumnsDnd = ref<any[]>([]);
|
||||||
|
|
||||||
/**表格分页器参数 */
|
/**表格分页器参数 */
|
||||||
let tablePagination = reactive({
|
let tablePagination = reactive({
|
||||||
@@ -261,12 +274,14 @@ function fnGetListTitle() {
|
|||||||
tableColumns.value = [];
|
tableColumns.value = [];
|
||||||
tableColumnsDnd.value = [];
|
tableColumnsDnd.value = [];
|
||||||
fnRanderChartData();
|
fnRanderChartData();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
tableColumns.value = [];
|
tableColumns.value = [];
|
||||||
const columns: ColumnsType = [];
|
const columns: any[] = [];
|
||||||
for (const item of res.data) {
|
for (const item of res.data) {
|
||||||
const kpiDisplay = item[`unit`]? item[`title`]+ `(${item['unit']})`:item[`title`];
|
const kpiDisplay = item[`unit`]
|
||||||
|
? item[`title`] + `(${item['unit']})`
|
||||||
|
: item[`title`];
|
||||||
const kpiValue = item[`kpiId`];
|
const kpiValue = item[`kpiId`];
|
||||||
columns.push({
|
columns.push({
|
||||||
title: kpiDisplay,
|
title: kpiDisplay,
|
||||||
@@ -627,9 +642,9 @@ onMounted(() => {
|
|||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
now.setMinutes(0, 0, 0);
|
now.setMinutes(0, 0, 0);
|
||||||
queryRangePicker.value[0] = parseDateToStr(now.getTime());
|
queryRangePicker.value[0] = `${now.getTime()}`;
|
||||||
now.setMinutes(59, 59, 59);
|
now.setMinutes(59, 59, 59);
|
||||||
queryRangePicker.value[1] = parseDateToStr(now.getTime());
|
queryRangePicker.value[1] = `${now.getTime()}`;
|
||||||
fnGetListTitle();
|
fnGetListTitle();
|
||||||
// 绘图
|
// 绘图
|
||||||
fnRanderChart();
|
fnRanderChart();
|
||||||
@@ -659,10 +674,7 @@ onBeforeUnmount(() => {
|
|||||||
<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
|
<a-form-item name="neType" :label="t('views.ne.common.neType')">
|
||||||
name="neType"
|
|
||||||
:label="t('views.ne.common.neType')"
|
|
||||||
>
|
|
||||||
<a-cascader
|
<a-cascader
|
||||||
v-model:value="state.neType"
|
v-model:value="state.neType"
|
||||||
:options="neCascaderOptions"
|
:options="neCascaderOptions"
|
||||||
@@ -682,7 +694,8 @@ onBeforeUnmount(() => {
|
|||||||
:allow-clear="false"
|
:allow-clear="false"
|
||||||
:show-time="{ format: 'HH:mm:ss' }"
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
value-format="YYYY-MM-DD HH:mm:ss"
|
value-format="x"
|
||||||
|
:ranges="ranges"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
></a-range-picker>
|
></a-range-picker>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|||||||
@@ -3,22 +3,35 @@ import { ref, onMounted, onUnmounted, nextTick, computed } from 'vue';
|
|||||||
import * as echarts from 'echarts/core';
|
import * as echarts from 'echarts/core';
|
||||||
import { LegendComponent } from 'echarts/components';
|
import { LegendComponent } from 'echarts/components';
|
||||||
import { LineChart, BarChart } from 'echarts/charts';
|
import { LineChart, BarChart } from 'echarts/charts';
|
||||||
import { GridComponent, TooltipComponent, TitleComponent } from 'echarts/components';
|
import {
|
||||||
|
GridComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
TitleComponent,
|
||||||
|
} from 'echarts/components';
|
||||||
import { CanvasRenderer } from 'echarts/renderers';
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
import { getKPITitle, listKPIData } from '@/api/perfManage/goldTarget';
|
import { getKPITitle, listKPIData } from '@/api/perfManage/goldTarget';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import { message, Modal } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import { RESULT_CODE_ERROR, RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import {
|
||||||
|
RESULT_CODE_ERROR,
|
||||||
|
RESULT_CODE_SUCCESS,
|
||||||
|
} from '@/constants/result-constants';
|
||||||
import type { Dayjs } from 'dayjs';
|
import type { Dayjs } from 'dayjs';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
import { generateColorRGBA } from '@/utils/generate-utils';
|
import { generateColorRGBA } from '@/utils/generate-utils';
|
||||||
import { BarChartOutlined, LineChartOutlined, UnorderedListOutlined } from '@ant-design/icons-vue';
|
import {
|
||||||
|
BarChartOutlined,
|
||||||
|
LineChartOutlined,
|
||||||
|
UnorderedListOutlined,
|
||||||
|
DownOutlined,
|
||||||
|
MoreOutlined,
|
||||||
|
} from '@ant-design/icons-vue';
|
||||||
|
|
||||||
// 在这里定义 ChartDataItem 接口
|
// 在这里定义 ChartDataItem 接口
|
||||||
interface ChartDataItem {
|
interface ChartDataItem {
|
||||||
date: string; // 将存储完整的时间字符串,包含时分秒
|
date: string; // 将存储完整的时间字符串,包含时分秒
|
||||||
[kpiId: string]: string | number; // 动态指标值
|
[kpiId: string]: string | number; // 动态指标
|
||||||
}
|
}
|
||||||
|
|
||||||
echarts.use([
|
echarts.use([
|
||||||
@@ -28,7 +41,7 @@ echarts.use([
|
|||||||
TooltipComponent,
|
TooltipComponent,
|
||||||
TitleComponent,
|
TitleComponent,
|
||||||
CanvasRenderer,
|
CanvasRenderer,
|
||||||
LegendComponent
|
LegendComponent,
|
||||||
]);
|
]);
|
||||||
// WebSocket连接
|
// WebSocket连接
|
||||||
const ws = ref<WS | null>(null);
|
const ws = ref<WS | null>(null);
|
||||||
@@ -36,7 +49,7 @@ const ws = ref<WS | null>(null);
|
|||||||
//日期范围响应式变量
|
//日期范围响应式变量
|
||||||
const dateRange = ref<[string, string]>([
|
const dateRange = ref<[string, string]>([
|
||||||
dayjs().startOf('day').valueOf().toString(),
|
dayjs().startOf('day').valueOf().toString(),
|
||||||
dayjs().valueOf().toString()
|
dayjs().valueOf().toString(),
|
||||||
]);
|
]);
|
||||||
//实时数据状态
|
//实时数据状态
|
||||||
const isRealtime = ref(false);
|
const isRealtime = ref(false);
|
||||||
@@ -49,7 +62,7 @@ let chart: echarts.ECharts | null = null;
|
|||||||
//observer 变量 监听图表容器大小
|
//observer 变量 监听图表容器大小
|
||||||
let observer: ResizeObserver | null = null;
|
let observer: ResizeObserver | null = null;
|
||||||
|
|
||||||
//日期变化时触发数据变化时触发
|
//日期变化时更新图表数据
|
||||||
const handleDateChange = (
|
const handleDateChange = (
|
||||||
value: [string, string] | [Dayjs, Dayjs],
|
value: [string, string] | [Dayjs, Dayjs],
|
||||||
dateStrings: [string, string]
|
dateStrings: [string, string]
|
||||||
@@ -61,21 +74,19 @@ const handleDateChange = (
|
|||||||
|
|
||||||
dateRange.value = [
|
dateRange.value = [
|
||||||
dayjs(dateStrings[0]).valueOf().toString(),
|
dayjs(dateStrings[0]).valueOf().toString(),
|
||||||
dayjs(dateStrings[1]).valueOf().toString()
|
dayjs(dateStrings[1]).valueOf().toString(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
fetchChartData();
|
fetchChartData();
|
||||||
};
|
};
|
||||||
|
|
||||||
//切换实时数据方法
|
//切换实时数据
|
||||||
const toggleRealtime = () => {
|
const toggleRealtime = () => {
|
||||||
fnRealTimeSwitch(isRealtime.value);
|
fnRealTimeSwitch(isRealtime.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 定义所有网元类型
|
// 定义所有网元类型
|
||||||
const ALL_NE_TYPES = ['AMF', 'SMF', 'UPF', 'MME', 'IMS', 'SMSC'] as const;
|
const ALL_NE_TYPES = ['AMF', 'SMF', 'UPF', 'MME', 'IMS', 'SMSC'] as const;
|
||||||
type NeType = typeof ALL_NE_TYPES[number];
|
type NeType = (typeof ALL_NE_TYPES)[number];
|
||||||
|
|
||||||
// 定义要筛选的指标 ID,按网元类型组织
|
// 定义要筛选的指标 ID,按网元类型组织
|
||||||
const TARGET_KPI_IDS: Record<NeType, string[]> = {
|
const TARGET_KPI_IDS: Record<NeType, string[]> = {
|
||||||
@@ -84,13 +95,13 @@ const TARGET_KPI_IDS: Record<NeType, string[]> = {
|
|||||||
UPF: ['UPF.03', 'UPF.04', 'UPF.05', 'UPF.06'],
|
UPF: ['UPF.03', 'UPF.04', 'UPF.05', 'UPF.06'],
|
||||||
MME: ['MME.A.01', 'MME.A.02', 'MME.A.03'],
|
MME: ['MME.A.01', 'MME.A.02', 'MME.A.03'],
|
||||||
IMS: ['SCSCF.01', 'SCSCF.02', 'SCSCF.05', 'SCSCF.06'],
|
IMS: ['SCSCF.01', 'SCSCF.02', 'SCSCF.05', 'SCSCF.06'],
|
||||||
SMSC: ['SMSC.A.01', 'SMSC.A.02', 'SMSC.A.03']
|
SMSC: ['SMSC.A.01', 'SMSC.A.02', 'SMSC.A.03'],
|
||||||
};
|
};
|
||||||
|
|
||||||
// 实时数据开关函数
|
// 实时数据开关函数
|
||||||
const fnRealTimeSwitch = (bool: boolean) => {
|
const fnRealTimeSwitch = (bool: boolean) => {
|
||||||
if (bool) {
|
if (bool) {
|
||||||
if(!ws.value) {
|
if (!ws.value) {
|
||||||
ws.value = new WS();
|
ws.value = new WS();
|
||||||
}
|
}
|
||||||
chartData.value = [];
|
chartData.value = [];
|
||||||
@@ -104,18 +115,18 @@ const fnRealTimeSwitch = (bool: boolean) => {
|
|||||||
onerror: wsError,
|
onerror: wsError,
|
||||||
};
|
};
|
||||||
ws.value.connect(options);
|
ws.value.connect(options);
|
||||||
} else if(ws.value) {
|
} else if (ws.value) {
|
||||||
ws.value.close();//断开链接
|
ws.value.close(); //断开链接
|
||||||
ws.value = null;//清空链接
|
ws.value = null; //清空链接
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// 接收数据后错误回调
|
// 接收数据后错误回调
|
||||||
const wsError = () => {
|
const wsError = () => {
|
||||||
message.error(t('common.websocketError'));
|
message.error(t('common.websocketError'));
|
||||||
}
|
};
|
||||||
|
|
||||||
// 收数据回调
|
// 接收数据后回调
|
||||||
const wsMessage = (res: Record<string, any>) => {
|
const wsMessage = (res: Record<string, any>) => {
|
||||||
const { code, data } = res;
|
const { code, data } = res;
|
||||||
if (code === RESULT_CODE_ERROR) {
|
if (code === RESULT_CODE_ERROR) {
|
||||||
@@ -136,10 +147,10 @@ const wsMessage = (res: Record<string, any>) => {
|
|||||||
const newData: ChartDataItem = {
|
const newData: ChartDataItem = {
|
||||||
date: kpiEvent.timeGroup
|
date: kpiEvent.timeGroup
|
||||||
? kpiEvent.timeGroup.toString()
|
? kpiEvent.timeGroup.toString()
|
||||||
: Date.now().toString()
|
: Date.now().toString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// 只添加已选中的指标数据
|
// 只添加已选中的指标的数据
|
||||||
selectedKPIs.value.forEach(kpiId => {
|
selectedKPIs.value.forEach(kpiId => {
|
||||||
if (kpiEvent[kpiId] !== undefined) {
|
if (kpiEvent[kpiId] !== undefined) {
|
||||||
newData[kpiId] = Number(kpiEvent[kpiId]);
|
newData[kpiId] = Number(kpiEvent[kpiId]);
|
||||||
@@ -152,7 +163,7 @@ const wsMessage = (res: Record<string, any>) => {
|
|||||||
updateChartData(newData);
|
updateChartData(newData);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 取图表数据
|
// 获取图表数据方法
|
||||||
const fetchChartData = async () => {
|
const fetchChartData = async () => {
|
||||||
if (kpiColumns.value.length === 0) {
|
if (kpiColumns.value.length === 0) {
|
||||||
console.warn('No KPI columns available');
|
console.warn('No KPI columns available');
|
||||||
@@ -180,18 +191,16 @@ const fetchChartData = async () => {
|
|||||||
sortField: 'timeGroup',
|
sortField: 'timeGroup',
|
||||||
sortOrder: 'asc',
|
sortOrder: 'asc',
|
||||||
interval: 5,
|
interval: 5,
|
||||||
kpiIds: TARGET_KPI_IDS[neType].join(',')
|
kpiIds: TARGET_KPI_IDS[neType].join(','),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const res = await listKPIData(params);
|
const res = await listKPIData(params);
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
allData.push(...res.data);
|
allData.push(...res.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按时间分组合并数据
|
// 按时间分组合数据
|
||||||
const groupedData = new Map<string, any>();
|
const groupedData = new Map<string, any>();
|
||||||
allData.forEach(item => {
|
allData.forEach(item => {
|
||||||
const timeKey = item.timeGroup;
|
const timeKey = item.timeGroup;
|
||||||
@@ -215,7 +224,6 @@ const fetchChartData = async () => {
|
|||||||
return dataItem;
|
return dataItem;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
updateChart();
|
updateChart();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch chart data:', error);
|
console.error('Failed to fetch chart data:', error);
|
||||||
@@ -223,10 +231,10 @@ const fetchChartData = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 添加一个 Map 来存储每个指标的固定颜色
|
// 添加一个 Map 来存储每个指标的临时固定颜色
|
||||||
const kpiColors = new Map<string, string>();
|
const kpiColors = new Map<string, string>();
|
||||||
|
|
||||||
// 加图表类型的响应式变量
|
// 定义图表类型的响应式变量
|
||||||
const chartType = ref<'line' | 'bar'>('line');
|
const chartType = ref<'line' | 'bar'>('line');
|
||||||
|
|
||||||
// 添加切换图表类型的方法
|
// 添加切换图表类型的方法
|
||||||
@@ -235,51 +243,55 @@ const toggleChartType = () => {
|
|||||||
updateChart();
|
updateChart();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 修改 updateChart 函数中的系列配置
|
// 更新图表
|
||||||
const updateChart = () => {
|
const updateChart = () => {
|
||||||
if (!chart || !kpiColumns.value.length) return;
|
if (!chart || !kpiColumns.value.length) return; //首先检查图表实例和指标是否存在
|
||||||
|
//过滤出已选择的指标列
|
||||||
const filteredColumns = kpiColumns.value.filter(col => selectedKPIs.value.includes(col.kpiId));
|
const filteredColumns = kpiColumns.value.filter(col =>
|
||||||
const legendData = filteredColumns.map(item => item.title);
|
selectedKPIs.value.includes(col.kpiId)
|
||||||
|
);
|
||||||
|
const legendData = filteredColumns.map(item => item.title); //创建图例数据数组,包含所有选中的指标的标题
|
||||||
|
//为每个选中的指标创建一个系列配置
|
||||||
const series = filteredColumns.map(item => {
|
const series = filteredColumns.map(item => {
|
||||||
const color = kpiColors.get(item.kpiId) || generateColorRGBA();
|
const color = kpiColors.get(item.kpiId) || generateColorRGBA();
|
||||||
if (!kpiColors.has(item.kpiId)) {
|
if (!kpiColors.has(item.kpiId)) {
|
||||||
kpiColors.set(item.kpiId, color);
|
kpiColors.set(item.kpiId, color); //保持指标颜色的临时一致性
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: item.title,
|
name: item.title,
|
||||||
type: chartType.value, // 使用当前选择的图表类型
|
type: chartType.value, // 使用当前选择的图表类型
|
||||||
data: chartData.value.length > 0
|
data:
|
||||||
? chartData.value.map(dataItem => dataItem[item.kpiId] || 0)
|
chartData.value.length > 0
|
||||||
: [0],
|
? chartData.value.map(dataItem => dataItem[item.kpiId] || 0)
|
||||||
|
: [0],
|
||||||
smooth: chartType.value === 'line', // 只在折线图时使用平滑
|
smooth: chartType.value === 'line', // 只在折线图时使用平滑
|
||||||
symbol: chartType.value === 'line' ? 'circle' : undefined, // 只在折线图时显示标记
|
symbol: chartType.value === 'line' ? 'circle' : undefined, // 只在折线图时显示标记
|
||||||
symbolSize: chartType.value === 'line' ? 6 : undefined,
|
symbolSize: chartType.value === 'line' ? 6 : undefined,
|
||||||
showSymbol: chartType.value === 'line',
|
showSymbol: chartType.value === 'line',
|
||||||
itemStyle: { color }
|
itemStyle: { color },
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
//图表配置对象
|
||||||
const option = {
|
const option = {
|
||||||
title: {
|
title: {
|
||||||
text: '网元指标概览',
|
text: t('views.perfManage.kpiOverView.kpiChartTitle'),
|
||||||
left: 'center'
|
left: 'center',
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
position: function(pt: any) {
|
position: function (pt: any) {
|
||||||
return [pt[0], '10%'];
|
return [pt[0], '10%'];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
|
//图例配置
|
||||||
data: legendData,
|
data: legendData,
|
||||||
type: 'scroll',
|
type: 'scroll',
|
||||||
orient: 'horizontal',
|
orient: 'horizontal',
|
||||||
top: 25,
|
top: 25,
|
||||||
textStyle: {
|
textStyle: {
|
||||||
fontSize: 12
|
fontSize: 12,
|
||||||
},
|
},
|
||||||
selected: Object.fromEntries(legendData.map(name => [name, true])),
|
selected: Object.fromEntries(legendData.map(name => [name, true])),
|
||||||
show: true,
|
show: true,
|
||||||
@@ -289,46 +301,50 @@ const updateChart = () => {
|
|||||||
padding: [5, 10],
|
padding: [5, 10],
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
|
//网格配置
|
||||||
left: '3%',
|
left: '3%',
|
||||||
right: '4%',
|
right: '4%',
|
||||||
bottom: '3%',
|
bottom: '3%',
|
||||||
top: 100,
|
top: 100,
|
||||||
containLabel: true
|
containLabel: true,
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
|
//x轴配置
|
||||||
type: 'category',
|
type: 'category',
|
||||||
boundaryGap: false,
|
boundaryGap: false,
|
||||||
data: chartData.value.length > 0
|
data:
|
||||||
? chartData.value.map(item => {
|
chartData.value.length > 0
|
||||||
// 将时间戳转换为包含时分秒的格式
|
? chartData.value.map(item => {
|
||||||
return dayjs(Number(item.date)).format('YYYY-MM-DD HH:mm:ss');
|
// 将时间戳转换为包含时分秒的格式
|
||||||
})
|
return dayjs(Number(item.date)).format('YYYY-MM-DD HH:mm:ss');
|
||||||
: [''],
|
})
|
||||||
|
: [''],
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
formatter: (value: string) => {
|
formatter: (value: string) => {
|
||||||
// 自定义 x 轴标签的显示格式
|
// 自定义 x 轴标签的显示格式
|
||||||
return dayjs(value).format('YYYY-MM-DD HH:mm:ss');
|
return dayjs(value).format('YYYY-MM-DD HH:mm:ss');
|
||||||
},
|
},
|
||||||
rotate: 0,
|
rotate: 0,
|
||||||
interval: 'auto', // 自动计算显示间隔
|
interval: 'auto', // 自动计算显示间隔
|
||||||
align: 'right'
|
align: 'right',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
|
// y轴配置
|
||||||
type: 'value',
|
type: 'value',
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
formatter: '{value}'
|
formatter: '{value}',
|
||||||
},
|
},
|
||||||
// 添加自动计算的分割段数
|
// 添加自动计算的分割段数
|
||||||
splitNumber: 5,
|
splitNumber: 5,
|
||||||
// 添加自动计算的最小/最大值围
|
// 添加自动计算的最小/最大值围
|
||||||
scale: true
|
scale: true,
|
||||||
},
|
},
|
||||||
series: series
|
series: series, //配置数据
|
||||||
};
|
};
|
||||||
|
|
||||||
chart.setOption(option);
|
chart.setOption(option); //使用新的配置更新图表
|
||||||
chart.resize();
|
chart.resize(); //调整图表大小以适应容器
|
||||||
|
|
||||||
// 如果已经有 observer,先断开连接
|
// 如果已经有 observer,先断开连接
|
||||||
if (observer) {
|
if (observer) {
|
||||||
@@ -361,7 +377,6 @@ onMounted(async () => {
|
|||||||
chart = echarts.init(container);
|
chart = echarts.init(container);
|
||||||
|
|
||||||
if (kpiColumns.value.length > 0) {
|
if (kpiColumns.value.length > 0) {
|
||||||
|
|
||||||
updateChart();
|
updateChart();
|
||||||
await fetchChartData();
|
await fetchChartData();
|
||||||
} else {
|
} else {
|
||||||
@@ -378,101 +393,136 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 定义指标列类型
|
// 定义指列类型
|
||||||
interface KPIColumn {
|
interface KPIColumn {
|
||||||
title: string;
|
title: string;
|
||||||
dataIndex: string;
|
dataIndex: string;
|
||||||
key: string;
|
key: string;
|
||||||
kpiId: string;
|
kpiId: string;
|
||||||
|
neType: string; // 添加网元类型字段
|
||||||
}
|
}
|
||||||
|
|
||||||
// 存储指标列信
|
// 存储指标列信
|
||||||
const kpiColumns = ref<KPIColumn[]>([]);
|
const kpiColumns = ref<KPIColumn[]>([]);
|
||||||
// 添加选中标的的状态
|
// 添加选中指标的的状态
|
||||||
const selectedKPIs = ref<string[]>([]);
|
const selectedKPIs = ref<string[]>([]);
|
||||||
// 添加对话框可见性状态
|
// 添加对话框可见性状态
|
||||||
const isModalVisible = ref(false);
|
const isModalVisible = ref(false);
|
||||||
|
// 添加临时存储下拉框选择的数组
|
||||||
|
const tempSelectedKPIs = ref<string[]>([]);
|
||||||
|
|
||||||
// 打开对话框
|
// 添加一个变量保存打开对话框时的选择状态
|
||||||
|
const originalSelectedKPIs = ref<string[]>([]);
|
||||||
|
|
||||||
|
// 打开对话框的方法
|
||||||
const showKPISelector = () => {
|
const showKPISelector = () => {
|
||||||
|
// 保存当前的选择状态
|
||||||
|
originalSelectedKPIs.value = [...selectedKPIs.value];
|
||||||
|
|
||||||
|
// 初始化临时选择为当前已选择的其他指标
|
||||||
|
const primaryKPIs = Object.values(TARGET_KPI_IDS).flat();
|
||||||
|
tempSelectedKPIs.value = selectedKPIs.value.filter(
|
||||||
|
kpiId => !primaryKPIs.includes(kpiId)
|
||||||
|
);
|
||||||
isModalVisible.value = true;
|
isModalVisible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 添加保存选中指标到 localStorage 的方法
|
// 保存选中指标到 localStorage 的方法
|
||||||
const saveSelectedKPIs = () => {
|
const saveSelectedKPIs = () => {
|
||||||
localStorage.setItem('selectedKPIs', JSON.stringify(selectedKPIs.value));
|
localStorage.setItem('selectedKPIs', JSON.stringify(selectedKPIs.value));
|
||||||
};
|
};
|
||||||
|
|
||||||
// 窗口确认按钮
|
// 取消按钮的处理方法
|
||||||
const handleModalOk = () => {
|
const handleModalCancel = () => {
|
||||||
saveSelectedKPIs(); // 保存选择
|
// 恢复到打开对话框时的选择状态
|
||||||
updateChart();
|
selectedKPIs.value = [...originalSelectedKPIs.value];
|
||||||
|
// 清空临时选择
|
||||||
|
tempSelectedKPIs.value = [];
|
||||||
isModalVisible.value = false;
|
isModalVisible.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 取消按钮
|
// 确认按钮的处理方法
|
||||||
const handleModalCancel = () => {
|
const handleModalOk = () => {
|
||||||
|
// 获取主要指标列表
|
||||||
|
const primaryKPIs = Object.values(TARGET_KPI_IDS).flat();
|
||||||
|
|
||||||
|
// 获取当前在主界面选中的主要指标
|
||||||
|
const selectedPrimaryKPIs = selectedKPIs.value.filter(kpiId =>
|
||||||
|
primaryKPIs.includes(kpiId)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 合并选中的主要指标和临时选中的其他指标
|
||||||
|
selectedKPIs.value = Array.from(
|
||||||
|
new Set([
|
||||||
|
...selectedPrimaryKPIs, // 只包含已选中的主要指标
|
||||||
|
...tempSelectedKPIs.value, // 临时选中的其他指标
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
// 清空临时选择和原始选择
|
||||||
|
tempSelectedKPIs.value = [];
|
||||||
|
originalSelectedKPIs.value = [];
|
||||||
|
|
||||||
|
// 保存选择并更新图表
|
||||||
|
saveSelectedKPIs();
|
||||||
|
updateChart();
|
||||||
isModalVisible.value = false;
|
isModalVisible.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取网元指标
|
// 获取网元指标
|
||||||
const fetchSpecificKPI = async () => {
|
const fetchSpecificKPI = async () => {
|
||||||
const language = currentLocale.value.split('_')[0] === 'zh' ? 'cn' : currentLocale.value.split('_')[0];
|
const language =
|
||||||
|
currentLocale.value.split('_')[0] === 'zh'
|
||||||
|
? 'cn'
|
||||||
|
: currentLocale.value.split('_')[0];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let allKPIs: KPIColumn[] = [];
|
let allKPIs: KPIColumn[] = [];
|
||||||
|
|
||||||
// 使用 ALL_NE_TYPES 遍历网元类型
|
// 1. 获取所有网元的全部指标
|
||||||
for (const neType of ALL_NE_TYPES) {
|
for (const neType of ALL_NE_TYPES) {
|
||||||
|
const res = await getKPITitle(neType.toUpperCase());
|
||||||
const res = await getKPITitle(neType);
|
|
||||||
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
// 过滤当前网元类型的指标
|
// 转换指标格式
|
||||||
const filteredKPIs = res.data.filter(item =>
|
const formattedKPIs = res.data.map(item => ({
|
||||||
TARGET_KPI_IDS[neType].some(targetId =>
|
|
||||||
item.kpiId === targetId || // 完全匹配
|
|
||||||
item.kpiId.startsWith(targetId) // 前缀匹配
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// 转换并添加到总指标列表
|
|
||||||
const formattedKPIs = filteredKPIs.map(item => ({
|
|
||||||
title: item[`${language}Title`],
|
title: item[`${language}Title`],
|
||||||
dataIndex: item.kpiId,
|
dataIndex: item.kpiId,
|
||||||
key: item.kpiId,
|
key: item.kpiId,
|
||||||
kpiId: item.kpiId
|
kpiId: item.kpiId,
|
||||||
|
neType: neType, // 添加网元类型信息
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// 添加到所有指标数组
|
||||||
allKPIs = [...allKPIs, ...formattedKPIs];
|
allKPIs = [...allKPIs, ...formattedKPIs];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新指标列信息
|
// 2. 更新所有指标到 kpiColumns
|
||||||
kpiColumns.value = allKPIs;
|
kpiColumns.value = allKPIs;
|
||||||
|
|
||||||
// 尝试加载保存的选择
|
// 3. 尝试加载保存的选择
|
||||||
const savedKPIs = localStorage.getItem('selectedKPIs');
|
const savedKPIs = localStorage.getItem('selectedKPIs');
|
||||||
if (savedKPIs) {
|
if (savedKPIs) {
|
||||||
// 确保保存的选择仍然存在于前指标中
|
// 确保保存的选择仍然存在于当前指标中
|
||||||
const validSavedKPIs = JSON.parse(savedKPIs).filter(
|
const validSavedKPIs = JSON.parse(savedKPIs).filter((kpiId: string) =>
|
||||||
(kpiId: string) => kpiColumns.value.some(col => col.kpiId === kpiId)
|
kpiColumns.value.some(col => col.kpiId === kpiId)
|
||||||
);
|
);
|
||||||
if (validSavedKPIs.length > 0) {
|
if (validSavedKPIs.length > 0) {
|
||||||
selectedKPIs.value = validSavedKPIs;
|
selectedKPIs.value = validSavedKPIs;
|
||||||
} else {
|
} else {
|
||||||
// 如果没有有效的保存选择,则默认全选
|
// 如果没有有效的保存选择,则默认选择<E98089><E68BA9>要指标
|
||||||
selectedKPIs.value = kpiColumns.value.map(col => col.kpiId);
|
selectedKPIs.value = Object.values(TARGET_KPI_IDS).flat();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 如果没有保存的选择,则默认全选
|
// 如果没有保存的选择,则默认选择重要指标
|
||||||
selectedKPIs.value = kpiColumns.value.map(col => col.kpiId);
|
selectedKPIs.value = Object.values(TARGET_KPI_IDS).flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kpiColumns.value.length === 0) {
|
if (kpiColumns.value.length === 0) {
|
||||||
console.warn('No matching KPIs found');
|
console.warn('No KPIs found');
|
||||||
} else {
|
} else {
|
||||||
console.log(`Found ${kpiColumns.value.length} total KPIs:`,
|
console.log(`Found ${kpiColumns.value.length} total KPIs`);
|
||||||
kpiColumns.value.map(kpi => kpi.kpiId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return kpiColumns.value;
|
return kpiColumns.value;
|
||||||
@@ -483,9 +533,9 @@ const fetchSpecificKPI = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 确保只保留一个 onUnmounted 钩子
|
// onUnmounted 钩子
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if(ws.value && ws.value.state() === WebSocket.OPEN) {
|
if (ws.value && ws.value.state() === WebSocket.OPEN) {
|
||||||
ws.value.close();
|
ws.value.close();
|
||||||
}
|
}
|
||||||
if (observer) {
|
if (observer) {
|
||||||
@@ -502,7 +552,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
const { t, currentLocale } = useI18n();
|
const { t, currentLocale } = useI18n();
|
||||||
|
|
||||||
// 修改 updateChartData 方法,只更新数据而不重新渲染整个图表
|
// 更新图表数据方法
|
||||||
const updateChartData = (newData: ChartDataItem) => {
|
const updateChartData = (newData: ChartDataItem) => {
|
||||||
chartData.value.push(newData);
|
chartData.value.push(newData);
|
||||||
if (chartData.value.length > 100) {
|
if (chartData.value.length > 100) {
|
||||||
@@ -514,17 +564,17 @@ const updateChartData = (newData: ChartDataItem) => {
|
|||||||
xAxis: {
|
xAxis: {
|
||||||
data: chartData.value.map(item =>
|
data: chartData.value.map(item =>
|
||||||
dayjs(Number(item.date)).format('YYYY-MM-DD HH:mm:ss')
|
dayjs(Number(item.date)).format('YYYY-MM-DD HH:mm:ss')
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
series: selectedKPIs.value.map(kpiId => ({
|
series: selectedKPIs.value.map(kpiId => ({
|
||||||
type: chartType.value, // 使用当前选择的图表类型
|
type: chartType.value, // 使用当前选择的图表类型
|
||||||
data: chartData.value.map(item => item[kpiId] || 0)
|
data: chartData.value.map(item => item[kpiId] || 0),
|
||||||
}))
|
})),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 修改 groupedKPIs 计算属性,使用 TARGET_KPI_IDS 来分组
|
// groupedKPIs 计算属性,使用 TARGET_KPI_IDS 来分组过滤
|
||||||
const groupedKPIs = computed(() => {
|
const groupedKPIs = computed(() => {
|
||||||
const groups: Record<string, KPIColumn[]> = {};
|
const groups: Record<string, KPIColumn[]> = {};
|
||||||
|
|
||||||
@@ -532,15 +582,52 @@ const groupedKPIs = computed(() => {
|
|||||||
// 使用 TARGET_KPI_IDS 中定义的指标 ID 来过滤
|
// 使用 TARGET_KPI_IDS 中定义的指标 ID 来过滤
|
||||||
const targetIds = TARGET_KPI_IDS[neType];
|
const targetIds = TARGET_KPI_IDS[neType];
|
||||||
groups[neType] = kpiColumns.value.filter(kpi =>
|
groups[neType] = kpiColumns.value.filter(kpi =>
|
||||||
targetIds.some(targetId =>
|
targetIds.includes(kpi.kpiId)
|
||||||
kpi.kpiId === targetId || // 完全匹配
|
|
||||||
kpi.kpiId.startsWith(targetId) // 前缀匹配
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return groups;
|
return groups;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 计算其他指标
|
||||||
|
const secondaryKPIs = computed(() => {
|
||||||
|
const groups: Record<string, KPIColumn[]> = {};
|
||||||
|
|
||||||
|
if (kpiColumns.value.length === 0) {
|
||||||
|
console.warn('No KPI columns available');
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
ALL_NE_TYPES.forEach(neType => {
|
||||||
|
// 获取当前网元类型的主要指标 ID
|
||||||
|
const primaryIds = TARGET_KPI_IDS[neType];
|
||||||
|
|
||||||
|
// 从所有指标中筛选出当前网元其他指标
|
||||||
|
groups[neType] = kpiColumns.value.filter(kpi => {
|
||||||
|
// 检查是否不在主要指标列表中
|
||||||
|
const isNotPrimary = !primaryIds.includes(kpi.kpiId);
|
||||||
|
|
||||||
|
// 检查是否属于当前网元类型
|
||||||
|
// 使用 getKPITitle API 返回的原始数据中的网元类型信息
|
||||||
|
const isCurrentNeType = kpi.neType === neType;
|
||||||
|
|
||||||
|
return isCurrentNeType && isNotPrimary;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return groups;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加处理其他指标选择变化的方法
|
||||||
|
const handleSecondaryKPIChange = (kpiId: string, checked: boolean) => {
|
||||||
|
if (checked) {
|
||||||
|
// 如果选中,将指标 ID 添加到临时列表
|
||||||
|
if (!tempSelectedKPIs.value.includes(kpiId)) {
|
||||||
|
tempSelectedKPIs.value = [...tempSelectedKPIs.value, kpiId];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果取消选中,从临时列表中移除指标 ID
|
||||||
|
tempSelectedKPIs.value = tempSelectedKPIs.value.filter(id => id !== kpiId);
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="kpi-overview">
|
<div class="kpi-overview">
|
||||||
@@ -557,20 +644,27 @@ const groupedKPIs = computed(() => {
|
|||||||
<template #icon>
|
<template #icon>
|
||||||
<unordered-list-outlined />
|
<unordered-list-outlined />
|
||||||
</template>
|
</template>
|
||||||
{{t('views.perfManage.kpiOverView.chooseMetrics')}}
|
{{ t('views.perfManage.kpiOverView.chooseMetrics') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button @click="toggleChartType">
|
<a-button @click="toggleChartType">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<bar-chart-outlined v-if="chartType === 'line'" />
|
<bar-chart-outlined v-if="chartType === 'line'" />
|
||||||
<line-chart-outlined v-else />
|
<line-chart-outlined v-else />
|
||||||
</template>
|
</template>
|
||||||
{{ chartType === 'line' ? t('views.perfManage.kpiOverView.changeLine') : t('views.perfManage.kpiOverView.changeBar') }}
|
{{
|
||||||
|
chartType === 'line'
|
||||||
|
? t('views.perfManage.kpiOverView.changeLine')
|
||||||
|
: t('views.perfManage.kpiOverView.changeBar')
|
||||||
|
}}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-form-item :label="isRealtime ? t('views.dashboard.cdr.realTimeDataStart') : t('views.dashboard.cdr.realTimeDataStop')">
|
<a-form-item
|
||||||
<a-switch
|
:label="
|
||||||
v-model:checked="isRealtime"
|
isRealtime
|
||||||
@change="toggleRealtime"
|
? t('views.dashboard.cdr.realTimeDataStart')
|
||||||
/>
|
: t('views.dashboard.cdr.realTimeDataStop')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a-switch v-model:checked="isRealtime" @change="toggleRealtime" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div id="chartContainer" class="chart-container"></div>
|
<div id="chartContainer" class="chart-container"></div>
|
||||||
@@ -578,7 +672,7 @@ const groupedKPIs = computed(() => {
|
|||||||
<!-- 修改指标选择对话框 -->
|
<!-- 修改指标选择对话框 -->
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:visible="isModalVisible"
|
v-model:visible="isModalVisible"
|
||||||
:title="t('views.perfManage.kpiOverView.chooseShowMetrics')"
|
title="选择要显示的指标"
|
||||||
@ok="handleModalOk"
|
@ok="handleModalOk"
|
||||||
@cancel="handleModalCancel"
|
@cancel="handleModalCancel"
|
||||||
width="800px"
|
width="800px"
|
||||||
@@ -586,8 +680,51 @@ const groupedKPIs = computed(() => {
|
|||||||
>
|
>
|
||||||
<a-checkbox-group v-model:value="selectedKPIs">
|
<a-checkbox-group v-model:value="selectedKPIs">
|
||||||
<div class="kpi-checkbox-list">
|
<div class="kpi-checkbox-list">
|
||||||
<div v-for="neType in ALL_NE_TYPES" :key="neType" class="ne-type-group">
|
<a-card
|
||||||
<div class="ne-type-title">{{ neType.toUpperCase() }}</div>
|
v-for="neType in ALL_NE_TYPES"
|
||||||
|
:key="neType"
|
||||||
|
class="ne-type-card"
|
||||||
|
:bordered="false"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<span class="card-title">{{ neType.toUpperCase() }}</span>
|
||||||
|
</template>
|
||||||
|
<template #extra>
|
||||||
|
<a-dropdown v-if="secondaryKPIs[neType]?.length" trigger="click">
|
||||||
|
<a-button type="link" size="small">
|
||||||
|
<more-outlined />
|
||||||
|
<down-outlined />
|
||||||
|
<span class="secondary-count"
|
||||||
|
>({{ secondaryKPIs[neType].length }})</span
|
||||||
|
>
|
||||||
|
</a-button>
|
||||||
|
<template>
|
||||||
|
<div class="secondary-kpi-menu" @click.stop>
|
||||||
|
<div
|
||||||
|
v-for="kpi in secondaryKPIs[neType]"
|
||||||
|
:key="kpi.kpiId"
|
||||||
|
class="secondary-kpi-item"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<a-checkbox
|
||||||
|
:value="kpi.kpiId"
|
||||||
|
:checked="tempSelectedKPIs.includes(kpi.kpiId)"
|
||||||
|
@change="
|
||||||
|
(e:any) =>
|
||||||
|
handleSecondaryKPIChange(
|
||||||
|
kpi.kpiId,
|
||||||
|
e.target.checked
|
||||||
|
)
|
||||||
|
"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
{{ kpi.title }}
|
||||||
|
</a-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</template>
|
||||||
<div class="ne-type-items">
|
<div class="ne-type-items">
|
||||||
<div
|
<div
|
||||||
v-for="kpi in groupedKPIs[neType]"
|
v-for="kpi in groupedKPIs[neType]"
|
||||||
@@ -599,7 +736,7 @@ const groupedKPIs = computed(() => {
|
|||||||
</a-checkbox>
|
</a-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</a-card>
|
||||||
</div>
|
</div>
|
||||||
</a-checkbox-group>
|
</a-checkbox-group>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
@@ -607,11 +744,11 @@ const groupedKPIs = computed(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
/* 基础布局样式 */
|
||||||
.kpi-overview {
|
.kpi-overview {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 控制区域样式 */
|
|
||||||
.controls {
|
.controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
@@ -619,7 +756,6 @@ const groupedKPIs = computed(() => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 图表容器样式 */
|
|
||||||
.chart-container {
|
.chart-container {
|
||||||
height: calc(100vh - 160px);
|
height: calc(100vh - 160px);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -631,24 +767,8 @@ const groupedKPIs = computed(() => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 网元分组样式 */
|
/* 网元指标列表样式 */
|
||||||
.ne-type-group {
|
|
||||||
margin-bottom: 24px;
|
|
||||||
border: 1px solid #f0f0f0;
|
|
||||||
border-radius: 4px;
|
|
||||||
width: 100%;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ne-type-title {
|
|
||||||
padding: 12px 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
background-color: #fafafa;
|
|
||||||
border-bottom: 1px solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ne-type-items {
|
.ne-type-items {
|
||||||
padding: 16px;
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
@@ -663,29 +783,25 @@ const groupedKPIs = computed(() => {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 深度选择器样式 */
|
/* 其他指标下拉菜单样式 */
|
||||||
:deep(.ant-modal-body) {
|
.secondary-kpi-menu {
|
||||||
&::-webkit-scrollbar {
|
background: #fff;
|
||||||
width: 6px;
|
padding: 4px 0;
|
||||||
height: 6px;
|
border-radius: 2px;
|
||||||
}
|
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
|
||||||
|
0 6px 16px 0 rgba(0, 0, 0, 0.08);
|
||||||
&::-webkit-scrollbar-thumb {
|
max-height: 300px;
|
||||||
background: #ccc;
|
overflow-y: auto;
|
||||||
border-radius: 3px;
|
min-width: 200px;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
background: #f1f1f1;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 组件高度统一样式 */
|
.secondary-kpi-item {
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 组件统一样式 */
|
||||||
:deep(.ant-form-item),
|
:deep(.ant-form-item),
|
||||||
:deep(.ant-picker),
|
:deep(.ant-picker),
|
||||||
:deep(.ant-btn) {
|
:deep(.ant-btn) {
|
||||||
@@ -698,15 +814,16 @@ const groupedKPIs = computed(() => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-form-item-label) {
|
|
||||||
padding-right: 8px;
|
|
||||||
line-height: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-checkbox-wrapper) {
|
:deep(.ant-checkbox-wrapper) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 添加次要指标数量的样式 */
|
||||||
|
.secondary-count {
|
||||||
|
margin-left: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import useI18n from '@/hooks/useI18n';
|
|||||||
import { listMenu } from '@/api/system/menu';
|
import { listMenu } from '@/api/system/menu';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { getConfigKey, changeValue } from '@/api/system/config';
|
import { getConfigKey, changeValue } from '@/api/system/config';
|
||||||
|
import { parseDataToTree } from '@/utils/parse-tree-utils';
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
type StateType = {
|
type StateType = {
|
||||||
@@ -59,14 +60,21 @@ onMounted(() => {
|
|||||||
listMenu(toRaw({ status: 1 })).then(res => {
|
listMenu(toRaw({ status: 1 })).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
// 过滤旧前端菜单以及不是菜单类型以及路径为空
|
// 过滤旧前端菜单以及不是菜单类型以及路径为空
|
||||||
res.data = res.data
|
res.data = res.data.filter(i => i.perms !== 'page' && i.menuType !== 'B');
|
||||||
.filter(i => i.perms !== 'page' && i.menuType === 'M' && i.component)
|
state.options = parseDataToTree(res.data, 'menuId');
|
||||||
.map((item: any) => {
|
const setDisabledAndComponent = (item: any) => {
|
||||||
state.options.push({
|
if (!item.component) {
|
||||||
label: item.menuName,
|
item.disabled = true;
|
||||||
value: item.component,
|
item.component = 'Null' + item.menuId;
|
||||||
});
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
state.options.forEach((item: any) => {
|
||||||
|
setDisabledAndComponent(item); // 处理父菜单
|
||||||
|
if (item.children && Array.isArray(item.children)) {
|
||||||
|
item.children.forEach(setDisabledAndComponent); // 处理子菜单
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -84,13 +92,22 @@ onMounted(() => {
|
|||||||
<a-col :lg="12" :md="12" :xs="24" style="margin-bottom: 30px">
|
<a-col :lg="12" :md="12" :xs="24" style="margin-bottom: 30px">
|
||||||
<template v-if="state.edite">
|
<template v-if="state.edite">
|
||||||
<a-form-item :label="t('views.system.setting.home')">
|
<a-form-item :label="t('views.system.setting.home')">
|
||||||
<a-select
|
<a-tree-select
|
||||||
ref="select"
|
|
||||||
v-model:value="state.default"
|
v-model:value="state.default"
|
||||||
|
show-search
|
||||||
style="width: 240px"
|
style="width: 240px"
|
||||||
:disabled="false"
|
:tree-data="state.options"
|
||||||
:options="state.options"
|
:field-names="{
|
||||||
></a-select>
|
children: 'children',
|
||||||
|
label: 'menuName',
|
||||||
|
value: 'component',
|
||||||
|
}"
|
||||||
|
tree-default-expand-all
|
||||||
|
tree-node-label-prop="menuName"
|
||||||
|
tree-node-filter-prop="menuName"
|
||||||
|
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
></a-tree-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-button type="primary" @click="fnSave">
|
<a-button type="primary" @click="fnSave">
|
||||||
@@ -102,13 +119,23 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a-form-item :label="t('views.system.setting.home')">
|
<a-form-item :label="t('views.system.setting.home')">
|
||||||
<a-select
|
<a-tree-select
|
||||||
ref="select"
|
|
||||||
v-model:value="state.default"
|
v-model:value="state.default"
|
||||||
style="width: 240px"
|
|
||||||
:disabled="true"
|
:disabled="true"
|
||||||
:options="state.options"
|
show-search
|
||||||
></a-select>
|
style="width: 240px"
|
||||||
|
:tree-data="state.options"
|
||||||
|
:field-names="{
|
||||||
|
children: 'children',
|
||||||
|
label: 'menuName',
|
||||||
|
value: 'component',
|
||||||
|
}"
|
||||||
|
tree-default-expand-all
|
||||||
|
tree-node-label-prop="menuName"
|
||||||
|
tree-node-filter-prop="menuName"
|
||||||
|
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
></a-tree-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-button type="dashed" @click="fnEdit(true)">
|
<a-button type="dashed" @click="fnEdit(true)">
|
||||||
|
|||||||
@@ -149,6 +149,13 @@ let tableColumns: ColumnsType = [
|
|||||||
return cdrJSON.calledParty;
|
return cdrJSON.calledParty;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.result'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
key: 'cause',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('views.dashboard.cdr.caller'),
|
title: t('views.dashboard.cdr.caller'),
|
||||||
dataIndex: 'cdrJSON',
|
dataIndex: 'cdrJSON',
|
||||||
@@ -174,20 +181,29 @@ let tableColumns: ColumnsType = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.dashboard.cdr.result'),
|
title: t('views.dashboard.cdr.seizureTime'),
|
||||||
dataIndex: 'cdrJSON',
|
dataIndex: 'cdrJSON',
|
||||||
key: 'cause',
|
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 150,
|
width: 200,
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.dashboard.cdr.time'),
|
|
||||||
dataIndex: 'cdrJSON',
|
|
||||||
align: 'center',
|
|
||||||
width: 150,
|
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
const cdrJSON = opt.value;
|
const cdrJSON = opt.value;
|
||||||
return parseDateToStr(+cdrJSON.releaseTime * 1000);
|
if (typeof cdrJSON.seizureTime === 'number') {
|
||||||
|
return parseDateToStr(+cdrJSON.seizureTime * 1000);
|
||||||
|
}
|
||||||
|
return cdrJSON.seizureTime;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.releaseTime'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 200,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
if (typeof cdrJSON.releaseTime === 'number') {
|
||||||
|
return parseDateToStr(+cdrJSON.releaseTime * 1000);
|
||||||
|
}
|
||||||
|
return cdrJSON.releaseTime;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -656,61 +672,73 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template #expandedRowRender="{ record }">
|
<template #expandedRowRender="{ record }">
|
||||||
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
|
<a-row :gutter="16">
|
||||||
<a-divider orientation="left">
|
<a-col :lg="5" :md="12" :xs="24">
|
||||||
{{ t('views.dashboard.cdr.cdrInfo') }}
|
<a-divider orientation="left">
|
||||||
</a-divider>
|
{{ t('views.dashboard.cdr.cdrInfo') }}
|
||||||
<div>
|
</a-divider>
|
||||||
<span>{{ t('views.ne.common.neName') }}: </span>
|
<div>
|
||||||
<span>{{ record.neName }}</span>
|
<span>{{ t('views.ne.common.neName') }}: </span>
|
||||||
</div>
|
<span>{{ record.neName }}</span>
|
||||||
<div>
|
</div>
|
||||||
<span>{{ t('views.ne.common.rmUid') }}: </span>
|
<div>
|
||||||
<span>{{ record.rmUID }}</span>
|
<span>{{ t('views.ne.common.rmUid') }}: </span>
|
||||||
</div>
|
<span>{{ record.rmUID }}</span>
|
||||||
<div>
|
</div>
|
||||||
<span>{{ t('views.dashboard.cdr.time') }}: </span>
|
<div>
|
||||||
<span>{{ parseDateToStr(+record.timestamp * 1000) }}</span>
|
<span>{{ t('views.dashboard.cdr.time') }}: </span>
|
||||||
</div>
|
<span>{{ parseDateToStr(+record.timestamp * 1000) }}</span>
|
||||||
<a-divider orientation="left">
|
</div>
|
||||||
{{ t('views.dashboard.cdr.rowInfo') }}
|
</a-col>
|
||||||
</a-divider>
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<div>
|
<a-divider orientation="left">
|
||||||
<span>{{ t('views.dashboard.cdr.type') }}: </span>
|
{{ t('views.dashboard.cdr.rowInfo') }}
|
||||||
<DictTag
|
</a-divider>
|
||||||
:options="dict.cdrCallType"
|
<div>
|
||||||
:value="record.cdrJSON.callType"
|
<span>{{ t('views.dashboard.cdr.type') }}: </span>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>{{ t('views.dashboard.cdr.duration') }}: </span>
|
|
||||||
<span v-if="record.cdrJSON.callType !== 'sms'">
|
|
||||||
{{ parseDuration(record.cdrJSON.callDuration) }}
|
|
||||||
</span>
|
|
||||||
<span v-else> - </span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>{{ t('views.dashboard.cdr.caller') }}: </span>
|
|
||||||
<span>{{ record.cdrJSON.callerParty }}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>{{ t('views.dashboard.cdr.called') }}: </span>
|
|
||||||
<span>{{ record.cdrJSON.calledParty }}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>{{ t('views.dashboard.cdr.result') }}: </span>
|
|
||||||
<span v-if="record.cdrJSON.callType !== 'sms'">
|
|
||||||
<DictTag
|
<DictTag
|
||||||
:options="dict.cdrSipCode"
|
:options="dict.cdrCallType"
|
||||||
:value="record.cdrJSON.cause"
|
:value="record.cdrJSON.callType"
|
||||||
value-default="0"
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</div>
|
||||||
<span v-else>
|
<div>
|
||||||
{{ t('views.dashboard.overview.userActivity.resultOK') }}
|
<span>{{ t('views.dashboard.cdr.duration') }}: </span>
|
||||||
</span>
|
<span v-if="record.cdrJSON.callType !== 'sms'">
|
||||||
</div>
|
{{ parseDuration(record.cdrJSON.callDuration) }}
|
||||||
</div>
|
</span>
|
||||||
|
<span v-else> - </span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.caller') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.callerParty }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.called') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.calledParty }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.result') }}: </span>
|
||||||
|
<span v-if="record.cdrJSON.callType !== 'sms'">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.cdrSipCode"
|
||||||
|
:value="record.cdrJSON.cause"
|
||||||
|
value-default="0"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ t('views.dashboard.cdr.resultOk') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.seizureTime') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.seizureTime }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.releaseTime') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.releaseTime }}</span>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|||||||
Reference in New Issue
Block a user