From c22663505ce3c42f9eff33340dbc9c1ba51619fa Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Thu, 26 Dec 2024 18:39:09 +0800 Subject: [PATCH 01/12] =?UTF-8?q?fix:=20SMF-CDR=E5=8E=BB=E9=99=A4RatingGro?= =?UTF-8?q?up=E5=8C=BA=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/neData/smf.ts | 1 + src/views/dashboard/smfCDR/index.vue | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/api/neData/smf.ts b/src/api/neData/smf.ts index a7d8c3c0..05050abc 100644 --- a/src/api/neData/smf.ts +++ b/src/api/neData/smf.ts @@ -10,6 +10,7 @@ export function listSMFDataCDR(query: Record) { url: '/neData/smf/cdr/list', method: 'get', params: query, + timeout: 60_000, }); } diff --git a/src/views/dashboard/smfCDR/index.vue b/src/views/dashboard/smfCDR/index.vue index 727f262f..b9e41b1c 100644 --- a/src/views/dashboard/smfCDR/index.vue +++ b/src/views/dashboard/smfCDR/index.vue @@ -88,7 +88,7 @@ let tableState: TabeStateType = reactive({ }); /**表格字段列 */ -let tableColumns: ColumnsType = [ +let tableColumns = ref([ { title: t('common.rowId'), dataIndex: 'id', @@ -225,7 +225,7 @@ let tableColumns: ColumnsType = [ key: 'id', align: 'left', }, -]; +]); /**表格分页器参数 */ let tablePagination = reactive({ @@ -780,14 +780,14 @@ onBeforeUnmount(() => {
-
RatingGroup: {{ u.ratingGroup }}
+
- +
Data Total Volume: @@ -801,10 +801,10 @@ onBeforeUnmount(() => { Data Volume Uplink: {{ udata.dataVolumeUplink }}
-
+
From 51a8d6d3a006e320c5fdc68181a24ac2321026c1 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Thu, 26 Dec 2024 18:40:36 +0800 Subject: [PATCH 02/12] =?UTF-8?q?feat:=20SMF-CDR=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=B5=81=E9=87=8F=E4=BD=BF=E7=94=A8=E6=83=85=E5=86=B5=E5=9B=BE?= =?UTF-8?q?=E8=A1=A8=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/parse-utils.ts | 27 +- src/views/dashboard/smfCDRByIMSI/index.vue | 664 +++++++++++++++++++++ 2 files changed, 688 insertions(+), 3 deletions(-) create mode 100644 src/views/dashboard/smfCDRByIMSI/index.vue diff --git a/src/utils/parse-utils.ts b/src/utils/parse-utils.ts index e091aa8e..49a89ddd 100644 --- a/src/utils/parse-utils.ts +++ b/src/utils/parse-utils.ts @@ -171,8 +171,8 @@ export function parseSizeFromKbs(sizeByte: number, timeInterval: number): any { } /** - * 字节数转换单位 - * @param bits 字节Bit大小 64009540 = 512.08 MB + * 位数据转换单位 + * @param bits 位Bit大小 64009540 = 512.08 MB * @returns xx B / KB / MB / GB / TB / PB / EB / ZB / YB */ export function parseSizeFromBits(bits: number | string): string { @@ -181,7 +181,28 @@ export function parseSizeFromBits(bits: number | string): string { bits = bits * 8; const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; const unitIndex = Math.floor(Math.log2(bits) / 10); - const value = (bits / Math.pow(1000, unitIndex)).toFixed(2); + const value = bits / Math.pow(1000, unitIndex); const unti = units[unitIndex]; + if (unitIndex > 0) { + return `${value.toFixed(2)} ${unti}`; + } + return `${value} ${unti}`; +} + +/** + * 字节数转换单位 + * @param byte 字节Byte大小 64009540 = 512.08 MB + * @returns xx B / KB / MB / GB / TB / PB / EB / ZB / YB + */ +export function parseSizeFromByte(byte: number | string): string { + byte = Number(byte) || 0; + if (byte <= 0) return '0 B'; + const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + const unitIndex = Math.floor(Math.log2(byte) / 10); + const unti = units[unitIndex]; + const value = byte / Math.pow(1000, unitIndex); + if (unitIndex > 0) { + return `${value.toFixed(2)} ${unti}`; + } return `${value} ${unti}`; } diff --git a/src/views/dashboard/smfCDRByIMSI/index.vue b/src/views/dashboard/smfCDRByIMSI/index.vue new file mode 100644 index 00000000..976940e3 --- /dev/null +++ b/src/views/dashboard/smfCDRByIMSI/index.vue @@ -0,0 +1,664 @@ + + + + + From d33183ca5ec832f315476aa71b942578e5fb37ca Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Thu, 26 Dec 2024 20:11:34 +0800 Subject: [PATCH 03/12] =?UTF-8?q?style:=20SMF-CDR=E6=B5=81=E9=87=8F?= =?UTF-8?q?=E6=8A=A5=E8=A1=A8=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/dashboard/smfCDRByIMSI/index.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/dashboard/smfCDRByIMSI/index.vue b/src/views/dashboard/smfCDRByIMSI/index.vue index 976940e3..841bd578 100644 --- a/src/views/dashboard/smfCDRByIMSI/index.vue +++ b/src/views/dashboard/smfCDRByIMSI/index.vue @@ -47,7 +47,7 @@ let cdrChart: echarts.ECharts | null = null; /**图表配置 */ const option = { title: { - text: 'Data Volume Uplink / Downlink', + text: 'Data Usage Report', left: 'left', }, tooltip: { @@ -434,7 +434,7 @@ function fnRanderChartDataUpdate() { // 绘制图数据 cdrChart.setOption({ title: { - text: `Data Volume By IMSI ${queryParams.subscriberID}`, + text: `Data Usage Report of IMSI ${queryParams.subscriberID}`, }, xAxis: [ { From c40ee9c8cc994a45982107d4f60f2e849811da26 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Fri, 27 Dec 2024 19:06:12 +0800 Subject: [PATCH 04/12] =?UTF-8?q?fix:=20=E7=9C=8B=E6=9D=BF=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E8=8E=B7=E5=8F=96UE=E4=BF=AE=E5=A4=8D=E5=92=8CAMF-UE?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84=E5=8F=98=E6=9B=B4=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/UserActivity/index.vue | 22 +++++++------------ src/views/dashboard/overview/hooks/useWS.ts | 12 +++++----- src/views/dashboard/overview/index.vue | 2 +- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/views/dashboard/overview/components/UserActivity/index.vue b/src/views/dashboard/overview/components/UserActivity/index.vue index 1cb9d6c9..a5aaa9c9 100644 --- a/src/views/dashboard/overview/components/UserActivity/index.vue +++ b/src/views/dashboard/overview/components/UserActivity/index.vue @@ -137,18 +137,12 @@ onMounted(() => {
{{ t('views.dashboard.overview.userActivity.time') }}: - - {{ item.data.authTime }} - - - {{ item.data.detachTime }} - - - {{ item.data.changeTime }} - + +
@@ -167,7 +161,7 @@ onMounted(() => {
{{ t('views.dashboard.overview.userActivity.result') }}:  - +
@@ -177,7 +171,7 @@ onMounted(() => {
{{ t('views.dashboard.overview.userActivity.result') }}:  - +
diff --git a/src/views/dashboard/overview/hooks/useWS.ts b/src/views/dashboard/overview/hooks/useWS.ts index bd3e5f4f..62268578 100644 --- a/src/views/dashboard/overview/hooks/useWS.ts +++ b/src/views/dashboard/overview/hooks/useWS.ts @@ -49,7 +49,7 @@ export default function useWS() { // 普通信息 switch (requestId) { // AMF_UE会话事件 - case 'amf_1010': + case 'amf_1010_001': if (Array.isArray(data.rows)) { eventListParse('amf_ue', data); } @@ -95,13 +95,13 @@ export default function useWS() { } break; // MME_UE会话事件 - case '1011_001': + case '1011': if (data.data) { queue.add(() => eventItemParseAndPush('mme_ue', data.data)); } break; // IMS_CDR会话事件 - case '1005_001': + case '1005': if (data.data) { queue.add(() => eventItemParseAndPush('ims_cdr', data.data)); } @@ -132,7 +132,7 @@ export default function useWS() { function userActivitySend() { // AMF_UE会话事件 ws.send({ - requestId: 'amf_1010', + requestId: 'amf_1010_001', type: 'amf_ue', data: { neType: 'AMF', @@ -189,11 +189,11 @@ export default function useWS() { /**订阅通道组 * * 指标UPF (GroupID:12_neId) - * AMF_UE会话事件(GroupID:1010) + * AMF_UE会话事件(GroupID:1010_neId) * MME_UE会话事件(GroupID:1011_neId) * IMS_CDR会话事件(GroupID:1005_neId) */ - subGroupID: '12_' + rmUid + ',1010,1011_001,1005_001', + subGroupID: '12_' + rmUid + ',1010,1011,1005', }, onmessage: wsMessage, onerror: (ev: any) => { diff --git a/src/views/dashboard/overview/index.vue b/src/views/dashboard/overview/index.vue index 46b5ce37..81545c12 100644 --- a/src/views/dashboard/overview/index.vue +++ b/src/views/dashboard/overview/index.vue @@ -252,7 +252,7 @@ function fnSelectNe(value: any, option: any) { for (var key in upfTotalFlow.value) { upfTotalFlow.value[key].requestFlag = false; } - loadData(); + // loadData(); } // 定义一个方法返回 views 容器 From 2138896d433feac348967e692774ae5a31a9ca67 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Fri, 27 Dec 2024 19:07:02 +0800 Subject: [PATCH 05/12] =?UTF-8?q?fix:=20=E7=BD=91=E5=85=83=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E9=80=89=E6=8B=A9=E6=A1=86=E8=AD=A6=E5=91=8A=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/ne/neConfig/index.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/ne/neConfig/index.vue b/src/views/ne/neConfig/index.vue index 30cb39fc..5ab607b0 100644 --- a/src/views/ne/neConfig/index.vue +++ b/src/views/ne/neConfig/index.vue @@ -114,6 +114,7 @@ function fnSelectNeId(_: any, info: any) { neIdSelect.value = okArr.map((item: any) => item.value); } else { neTypeSelect.value[1] = info.value; + neIdSelect.value = [info.value]; } fnActiveConfigNode(treeState.data[0].key); } From 1cbce9ad0376660bcaf85ef939e8f9c016a06926 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Fri, 27 Dec 2024 19:08:58 +0800 Subject: [PATCH 06/12] =?UTF-8?q?feat:=20UE=E6=95=B0=E6=8D=AE=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E7=BB=9F=E4=B8=80=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/dashboard/amfUE/index.vue | 234 ++++++++++++++++++---------- src/views/dashboard/mmeUE/index.vue | 110 +++++++++---- 2 files changed, 224 insertions(+), 120 deletions(-) diff --git a/src/views/dashboard/amfUE/index.vue b/src/views/dashboard/amfUE/index.vue index 83e061e6..aff168bf 100644 --- a/src/views/dashboard/amfUE/index.vue +++ b/src/views/dashboard/amfUE/index.vue @@ -11,14 +11,21 @@ import { RESULT_CODE_SUCCESS, } from '@/constants/result-constants'; import useDictStore from '@/store/modules/dict'; +import useNeInfoStore from '@/store/modules/neinfo'; import { listAMFDataUE, delAMFDataUE, exportAMFDataUE } from '@/api/neData/amf'; +import { parseDateToStr } from '@/utils/date-utils'; import { OptionsType, WS } from '@/plugins/ws-websocket'; +import dayjs, { Dayjs } from 'dayjs'; import saveAs from 'file-saver'; import PQueue from 'p-queue'; +import { useClipboard } from '@vueuse/core'; +const { copy } = useClipboard({ legacy: true }); const { t } = useI18n(); const { getDict } = useDictStore(); const ws = new WS(); const queue = new PQueue({ concurrency: 1, autoStart: true }); +/**网元可选 */ +let neOtions = ref[]>([]); /**字典数据 */ let dict: { @@ -35,7 +42,10 @@ let dict: { }); /**开始结束时间 */ -let queryRangePicker = ref<[string, string]>(['', '']); +let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([ + dayjs().startOf('hour'), + dayjs().endOf('hour'), +]); /**查询参数 */ let queryParams = reactive({ @@ -47,9 +57,9 @@ let queryParams = reactive({ sortField: 'timestamp', sortOrder: 'desc', /**开始时间 */ - startTime: '', + startTime: undefined as undefined | number, /**结束时间 */ - endTime: '', + endTime: undefined as undefined | number, /**当前页数 */ pageNum: 1, /**每页条数 */ @@ -67,7 +77,7 @@ function fnQueryReset() { pageNum: 1, pageSize: 20, }); - queryRangePicker.value = ['', '']; + queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')]; tablePagination.current = 1; tablePagination.pageSize = 20; fnGetList(); @@ -141,9 +151,15 @@ let tableColumns: ColumnsType = [ { title: t('views.dashboard.ue.time'), dataIndex: 'eventJSON', - key: 'time', align: 'left', width: 150, + customRender(opt) { + const record = opt.value; + if (record?.time) { + return record.time; + } + return parseDateToStr(+record.timestamp * 1000); + }, }, { title: t('common.operate'), @@ -252,11 +268,19 @@ function fnGetList(pageNum?: number) { if (pageNum) { queryParams.pageNum = pageNum; } - if (!queryRangePicker.value) { - queryRangePicker.value = ['', '']; + + // 时间范围 + if ( + Array.isArray(queryRangePicker.value) && + queryRangePicker.value.length > 0 + ) { + queryParams.startTime = queryRangePicker.value[0].valueOf(); + queryParams.endTime = queryRangePicker.value[1].valueOf(); + } else { + queryParams.startTime = undefined; + queryParams.endTime = undefined; } - queryParams.startTime = queryRangePicker.value[0]; - queryParams.endTime = queryRangePicker.value[1]; + listAMFDataUE(toRaw(queryParams)).then(res => { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) { // 取消勾选 @@ -324,6 +348,18 @@ function fnExportList() { }); } +/** + * 复制CDR + * @param jsonStr JSON字符串 + */ +function fnRecordCopy(jsonStr: string) { + if (!jsonStr) return; + const text = JSON.stringify(jsonStr, null, 2); + copy(text).then(() => { + message.success(t('common.copyOk'), 3); + }); +} + /**实时数据开关 */ const realTimeData = ref(false); @@ -333,31 +369,30 @@ const realTimeData = ref(false); function fnRealTime() { realTimeData.value = !realTimeData.value; if (realTimeData.value) { + tableState.seached = false; // 建立链接 const options: OptionsType = { url: '/ws', params: { /**订阅通道组 * - * AMF_UE会话事件(GroupID:1010) + * AMF_UE会话事件(GroupID:1010_neId) */ - subGroupID: '1010', + subGroupID: `1010_${queryParams.neId}`, }, onmessage: wsMessage, - onerror: wsError, + onerror: (ev: any) => { + console.error(ev); + }, }; ws.connect(options); } else { ws.close(); + tableState.seached = true; + fnGetList(1); } } -/**接收数据后回调 */ -function wsError(ev: any) { - // 接收数据后回调 - console.error(ev); -} - /**接收数据后回调 */ function wsMessage(res: Record) { const { code, requestId, data } = res; @@ -371,7 +406,7 @@ function wsMessage(res: Record) { return; } // ueEvent AMF_UE会话事件 - if (data.groupId === '1010') { + if (data.groupId === `1010_${queryParams.neId}`) { const ueEvent = data.data; queue.add(async () => { modalState.maxId += 1; @@ -399,16 +434,40 @@ onMounted(() => { getDict('ue_auth_code'), getDict('ue_event_type'), getDict('ue_event_cm_state'), - ]) - .then(resArr => { - if (resArr[0].status === 'fulfilled') { - dict.ueAauthCode = resArr[0].value; - } - if (resArr[1].status === 'fulfilled') { - dict.ueEventType = resArr[1].value; - } - if (resArr[2].status === 'fulfilled') { - dict.ueEventCmState = resArr[2].value; + ]).then(resArr => { + if (resArr[0].status === 'fulfilled') { + dict.ueAauthCode = resArr[0].value; + } + if (resArr[1].status === 'fulfilled') { + dict.ueEventType = resArr[1].value; + } + if (resArr[2].status === 'fulfilled') { + dict.ueEventCmState = resArr[2].value; + } + }); + + // 获取网元网元列表 + useNeInfoStore() + .fnNelist() + .then(res => { + if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) { + if (res.data.length > 0) { + let arr: Record[] = []; + res.data.forEach(i => { + if (i.neType === 'AMF') { + arr.push({ value: i.neId, label: i.neName }); + } + }); + neOtions.value = arr; + if (arr.length > 0) { + queryParams.neId = arr[0].value; + } + } + } else { + message.warning({ + content: t('common.noData'), + duration: 2, + }); } }) .finally(() => { @@ -434,6 +493,16 @@ onBeforeUnmount(() => { + + + + + { > - + { > + + + + + + {{ t('common.search') }} + + + + {{ t('common.reset') }} + + + + { > - - - - - - {{ t('common.search') }} - - - - {{ t('common.reset') }} - - - - @@ -545,6 +614,7 @@ onBeforeUnmount(() => { :checked-children="t('common.switch.show')" :un-checked-children="t('common.switch.hide')" size="small" + :disabled="realTimeData" /> @@ -605,7 +675,7 @@ onBeforeUnmount(() => { @@ -614,32 +684,23 @@ onBeforeUnmount(() => { -