diff --git a/src/views/dashboard/mmeUE/index.vue b/src/views/dashboard/mmeUE/index.vue index 74a6523d..ddd3acfc 100644 --- a/src/views/dashboard/mmeUE/index.vue +++ b/src/views/dashboard/mmeUE/index.vue @@ -16,6 +16,7 @@ import { parseDateToStr } from '@/utils/date-utils'; import { OptionsType, WS } from '@/plugins/ws-websocket'; import saveAs from 'file-saver'; import PQueue from 'p-queue'; + const { t } = useI18n(); const { getDict } = useDictStore(); const ws = new WS(); @@ -45,6 +46,7 @@ let queryParams = reactive({ neId: '001', eventType: 'auth-result', imsi: '', + tenantName: '', sortField: 'timestamp', sortOrder: 'desc', /**开始时间 */ @@ -63,6 +65,7 @@ function fnQueryReset() { queryParams = Object.assign(queryParams, { eventType: 'auth-result', imsi: '', + tenantName: '', startTime: '', endTime: '', pageNum: 1, @@ -149,6 +152,13 @@ let tableColumns: ColumnsType = [ return parseDateToStr(+cdrJSON.timestamp * 1000); }, }, + { + title: t('views.dashboard.cdr.tenantName'), + dataIndex: 'tenantName', + align: 'center', + key: 'tenantName', + width: 150, + }, { title: t('common.operate'), key: 'id', @@ -461,6 +471,17 @@ onBeforeUnmount(() => { > + + + + + +import { reactive, onMounted, toRaw, ref, onBeforeUnmount } from 'vue'; +import { PageContainer } from 'antdv-pro-layout'; +import { message, Modal } from 'ant-design-vue/lib'; +import { SizeType } from 'ant-design-vue/lib/config-provider'; +import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface'; +import { ColumnsType } from 'ant-design-vue/lib/table'; +import useI18n from '@/hooks/useI18n'; +import { + RESULT_CODE_ERROR, + RESULT_CODE_SUCCESS, +} from '@/constants/result-constants'; +import useDictStore from '@/store/modules/dict'; +import { listMMEDataUE, delMMEDataUE, exportMMEDataUE } from '@/api/neData/mme'; +import { parseDateToStr } from '@/utils/date-utils'; +import { OptionsType, WS } from '@/plugins/ws-websocket'; +import saveAs from 'file-saver'; +import PQueue from 'p-queue'; +const { t } = useI18n(); +const { getDict } = useDictStore(); +const ws = new WS(); +const queue = new PQueue({ concurrency: 1, autoStart: true }); + +/**字典数据 */ +let dict: { + /**UE 事件认证代码类型 */ + ueAauthCode: DictType[]; + /**UE 事件类型 */ + ueEventType: DictType[]; + /**UE 事件CM状态 */ + ueEventCmState: DictType[]; +} = reactive({ + ueAauthCode: [], + ueEventType: [], + ueEventCmState: [], +}); + +/**开始结束时间 */ +let queryRangePicker = ref<[string, string]>(['', '']); + +/**查询参数 */ +let queryParams = reactive({ + /**网元类型 */ + neType: 'MME', + neId: '001', + eventType: 'auth-result', + imsi: '', + sortField: 'timestamp', + sortOrder: 'desc', + /**开始时间 */ + startTime: '', + /**结束时间 */ + endTime: '', + /**当前页数 */ + pageNum: 1, + /**每页条数 */ + pageSize: 20, +}); + +/**查询参数重置 */ +function fnQueryReset() { + eventTypes.value = ['auth-result']; + queryParams = Object.assign(queryParams, { + eventType: 'auth-result', + imsi: '', + startTime: '', + endTime: '', + pageNum: 1, + pageSize: 20, + }); + queryRangePicker.value = ['', '']; + tablePagination.current = 1; + tablePagination.pageSize = 20; + fnGetList(); +} + +/**记录类型 */ +const eventTypes = ref(['auth-result']); + +/**查询记录类型变更 */ +function fnQueryEventTypeChange(value: any) { + if (Array.isArray(value)) { + queryParams.eventType = value.join(','); + } +} + +/**表格状态类型 */ +type TabeStateType = { + /**加载等待 */ + loading: boolean; + /**紧凑型 */ + size: SizeType; + /**搜索栏 */ + seached: boolean; + /**记录数据 */ + data: object[]; + /**勾选记录 */ + selectedRowKeys: (string | number)[]; +}; + +/**表格状态 */ +let tableState: TabeStateType = reactive({ + loading: false, + size: 'middle', + seached: true, + data: [], + selectedRowKeys: [], +}); + +/**表格字段列 */ +let tableColumns: ColumnsType = [ + { + title: t('common.rowId'), + dataIndex: 'id', + align: 'left', + width: 100, + }, + { + title: 'IMSI', + dataIndex: 'eventJSON', + align: 'left', + width: 150, + customRender(opt) { + const eventJSON = opt.value; + return eventJSON.imsi; + }, + }, + { + title: t('views.dashboard.ue.eventType'), + dataIndex: 'eventType', + key: 'eventType', + align: 'left', + width: 150, + }, + { + title: t('views.dashboard.ue.result'), + dataIndex: 'eventJSON', + key: 'result', + align: 'left', + width: 150, + }, + { + title: t('views.dashboard.ue.time'), + dataIndex: 'eventJSON', + align: 'left', + width: 150, + customRender(opt) { + const cdrJSON = opt.value; + return parseDateToStr(+cdrJSON.timestamp * 1000); + }, + }, + + { + title: t('common.operate'), + key: 'id', + align: 'left', + }, +]; + +/**表格分页器参数 */ +let tablePagination = reactive({ + /**当前页数 */ + current: 1, + /**每页条数 */ + pageSize: 20, + /**默认的每页条数 */ + defaultPageSize: 20, + /**指定每页可以显示多少条 */ + pageSizeOptions: ['10', '20', '50', '100'], + /**只有一页时是否隐藏分页器 */ + hideOnSinglePage: false, + /**是否可以快速跳转至某页 */ + showQuickJumper: true, + /**是否可以改变 pageSize */ + showSizeChanger: true, + /**数据总数 */ + total: 0, + showTotal: (total: number) => t('common.tablePaginationTotal', { total }), + onChange: (page: number, pageSize: number) => { + tablePagination.current = page; + tablePagination.pageSize = pageSize; + queryParams.pageNum = page; + queryParams.pageSize = pageSize; + fnGetList(); + }, +}); + +/**表格紧凑型变更操作 */ +function fnTableSize({ key }: MenuInfo) { + tableState.size = key as SizeType; +} + +/**表格多选 */ +function fnTableSelectedRowKeys(keys: (string | number)[]) { + tableState.selectedRowKeys = keys; +} + +/**对话框对象信息状态类型 */ +type ModalStateType = { + /**确定按钮 loading */ + confirmLoading: boolean; + /**最大ID值 */ + maxId: number; +}; + +/**对话框对象信息状态 */ +let modalState: ModalStateType = reactive({ + confirmLoading: false, + maxId: 0, +}); + +/** + * 记录删除 + * @param id 编号 + */ +function fnRecordDelete(id: string) { + if (!id || modalState.confirmLoading) return; + let msg = id; + if (id === '0') { + msg = `${id}... ${tableState.selectedRowKeys.length}`; + id = tableState.selectedRowKeys.join(','); + } + + Modal.confirm({ + title: t('common.tipTitle'), + content: t('views.dashboard.ue.delTip', { msg }), + onOk() { + modalState.confirmLoading = true; + const hide = message.loading(t('common.loading'), 0); + delMMEDataUE(id) + .then(res => { + if (res.code === RESULT_CODE_SUCCESS) { + message.success({ + content: t('common.operateOk'), + duration: 3, + }); + fnGetList(1); + } else { + message.error({ + content: `${res.msg}`, + duration: 3, + }); + } + }) + .finally(() => { + hide(); + modalState.confirmLoading = false; + }); + }, + }); +} + +/**查询列表, pageNum初始页数 */ +function fnGetList(pageNum?: number) { + if (tableState.loading) return; + tableState.loading = true; + if (pageNum) { + queryParams.pageNum = pageNum; + } + if (!queryRangePicker.value) { + queryRangePicker.value = ['', '']; + } + queryParams.startTime = queryRangePicker.value[0]; + queryParams.endTime = queryRangePicker.value[1]; + listMMEDataUE(toRaw(queryParams)).then(res => { + if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) { + // 取消勾选 + if (tableState.selectedRowKeys.length > 0) { + tableState.selectedRowKeys = []; + } + tablePagination.total = res.total; + // 遍历处理cdr字符串数据 + tableState.data = res.rows.map(item => { + let eventJSON = item.eventJSON; + if (!eventJSON) { + Reflect.set(item, 'eventJSON', {}); + } + + try { + eventJSON = JSON.parse(eventJSON); + Reflect.set(item, 'eventJSON', eventJSON); + } catch (error) { + console.error(error); + Reflect.set(item, 'eventJSON', {}); + } + + return item; + }); + + // 取最大值ID用作实时累加 + if (res.total > 0) { + modalState.maxId = Number(res.rows[0].id); + } + } + tableState.loading = false; + }); +} + +/**列表导出 */ +function fnExportList() { + if (modalState.confirmLoading) return; + Modal.confirm({ + title: t('common.tipTitle'), + content: t('views.dashboard.ue.exportTip'), + onOk() { + const hide = message.loading(t('common.loading'), 0); + const querys = toRaw(queryParams); + querys.pageSize = 10000; + exportMMEDataUE(querys) + .then(res => { + if (res.code === RESULT_CODE_SUCCESS) { + message.success({ + content: t('common.operateOk'), + duration: 3, + }); + saveAs(res.data, `mme_ue_event_export_${Date.now()}.xlsx`); + } else { + message.error({ + content: `${res.msg}`, + duration: 3, + }); + } + }) + .finally(() => { + hide(); + modalState.confirmLoading = false; + }); + }, + }); +} + +/**实时数据开关 */ +const realTimeData = ref(false); + +/** + * 实时数据 + */ +function fnRealTime() { + realTimeData.value = !realTimeData.value; + if (realTimeData.value) { + // 建立链接 + const options: OptionsType = { + url: '/ws', + params: { + /**订阅通道组 + * + * MME_UE会话事件(GroupID:1011) + */ + subGroupID: '1011', + }, + onmessage: wsMessage, + onerror: wsError, + }; + ws.connect(options); + } else { + ws.close(); + } +} + +/**接收数据后回调 */ +function wsError(ev: any) { + // 接收数据后回调 + console.error(ev); +} + +/**接收数据后回调 */ +function wsMessage(res: Record) { + const { code, requestId, data } = res; + if (code === RESULT_CODE_ERROR) { + console.warn(res.msg); + return; + } + + // 订阅组信息 + if (!data?.groupId) { + return; + } + // ueEvent MME_UE会话事件 + if (data.groupId === '1011') { + const ueEvent = data.data; + queue.add(async () => { + modalState.maxId += 1; + tableState.data.unshift({ + id: modalState.maxId, + neType: ueEvent.neType, + neName: ueEvent.neName, // 空 + rmUID: ueEvent.rmUID, // 空 + timestamp: ueEvent.timestamp, + eventType: ueEvent.eventType, + eventJSON: ueEvent.eventJSON, + }); + tablePagination.total += 1; + if (tableState.data.length > 100) { + tableState.data.pop(); + } + await new Promise(resolve => setTimeout(resolve, 800)); + }); + } +} + +onMounted(() => { + // 初始字典数据 + Promise.allSettled([ + 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; + } + }) + .finally(() => { + // 获取列表数据 + fnGetList(); + }); +}); + +onBeforeUnmount(() => { + if (ws.state() !== -1) { + ws.close(); + } +}); + + + + +