From 67d77b1ae92dd4368bf984ec7e154c7f94399eb9 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 25 Sep 2023 19:50:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=B7=9F=E8=B8=AA=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/trace/task.ts | 140 +++++++ src/i18n/locales/en-US.ts | 38 ++ src/i18n/locales/zh-CN.ts | 38 ++ src/utils/regular-utils.ts | 12 + src/views/trace/task/index.vue | 708 +++++++++++++++++++++++++++------ 5 files changed, 821 insertions(+), 115 deletions(-) create mode 100644 src/api/trace/task.ts diff --git a/src/api/trace/task.ts b/src/api/trace/task.ts new file mode 100644 index 00000000..3a9c08ee --- /dev/null +++ b/src/api/trace/task.ts @@ -0,0 +1,140 @@ +import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; +import { request } from '@/plugins/http-fetch'; +import { parseObjLineToHump } from '@/utils/parse-utils'; + +/** + * 查询任务列表 + * @param query 查询参数 + * @returns object + */ +export async function listTraceTask(query: Record) { + let totalSQL = 'select count(*) as total from trace_task where 1=1 '; + let rowsSQL = 'select * from trace_task where 1=1 '; + + // 查询 + let querySQL = ''; + if (query.imsi) { + querySQL += ` and imsi = '${query.imsi}' `; + } + + // 分页 + const pageNum = query.pageNum - 1; + const limtSql = ` limit ${pageNum},${query.pageSize} `; + + // 发起请求 + const result = await request({ + url: `/databaseManagement/v1/select/omc_db/trace_task`, + method: 'get', + params: { + totalSQL: totalSQL + querySQL, + rowsSQL: rowsSQL + querySQL + limtSql, + }, + }); + + // 解析数据 + if (result.code === RESULT_CODE_SUCCESS) { + const data: DataList = { + total: 0, + rows: [], + code: result.code, + msg: result.msg, + }; + result.data.data.forEach((item: any) => { + const itemData = item['trace_task']; + if (Array.isArray(itemData)) { + if (itemData.length === 1 && itemData[0]['total']) { + data.total = itemData[0]['total']; + } else { + data.rows = itemData.map(v => parseObjLineToHump(v)); + } + } + }); + return data; + } + return result; +} + +/** + * 查询任务详细 + * @param id 网元ID + * @returns object + */ +export async function getTraceTask(id: string | number) { + // 发起请求 + const result = await request({ + url: `/databaseManagement/v1/select/omc_db/trace_task`, + method: 'get', + params: { + SQL: `select * from trace_task where id = ${id}`, + }, + }); + // 解析数据 + if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) { + let data = result.data.data[0]; + return Object.assign(result, { + data: parseObjLineToHump(data['trace_task'][0]), + }); + } + return result; +} + +/** + * 新增任务 + * @param data 网元对象 + * @returns object + */ +export function addTraceTask(data: Record) { + return request({ + url: `/traceManagement/v1/subscriptions`, + method: 'post', + data: data, + }); +} + +/** + * 修改任务 + * @param data 网元对象 + * @returns object + */ +export function updateTraceTask(data: Record) { + return request({ + url: `/traceManagement/v1/subscriptions`, + method: 'put', + data: data, + }); +} + +/** + * 删除任务 + * @param noticeId 网元ID + * @returns object + */ +export async function delTraceTask(id: string) { + return request({ + url: `/traceManagement/v1/subscriptions?id=${id}`, + method: 'delete', + }); +} + +/** + * 获取网元跟踪接口列表 + * @returns object + */ +export async function getNeTraceInterfaceAll() { + // 发起请求 + const result = await request({ + url: `/databaseManagement/v1/elementType/omc_db/objectType/ne_info`, + method: 'get', + params: { + SQL: `SELECT ne_type,interface FROM trace_info`, + }, + }); + // 解析数据 + if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) { + let data = result.data.data[0]; + return Object.assign(result, { + data: parseObjLineToHump(data['trace_info']), + }); + } + return result; +} diff --git a/src/i18n/locales/en-US.ts b/src/i18n/locales/en-US.ts index 09c29de9..3cb8faa5 100644 --- a/src/i18n/locales/en-US.ts +++ b/src/i18n/locales/en-US.ts @@ -17,6 +17,7 @@ export default { close: 'Close', search: 'Search', reset: 'Reset', + viewText: 'View', addText: 'Add', editText: 'Edit', deleteText: 'Delete', @@ -175,6 +176,43 @@ export default { capStart: 'Start capturing packets', capStop: 'Stop capturing packets', }, + task: { + neTypePlease: 'Query network element type', + neType: 'Type', + neID: 'Identifying', + trackType: 'Tracking type', + trackTypePlease: 'Please select a tracking type', + creater: 'Created by', + startTime: 'Start time', + endTime: 'End time', + msisdn: 'MSISDN', + msisdnPlease: 'Please enter MSISDN', + msisdnTip: 'Mobile communication MSISDN number', + imsi: 'IMSI', + imsiPlease: 'Please enter IMSI', + imsiTip: 'Mobile communication IMSI number', + srcIp: 'Source IP address', + srcIpPlease: 'Please enter the source IP address', + srcIpTip: 'Current sender IPv4 address', + dstIp: 'Destination IP address', + dstIpPlease: 'Please enter the destination IP address', + dstIpTip: 'IPv4 address of the receiving end of the other party', + interfaces: 'Signaling interface', + interfacesPlease: 'Please enter the signaling interface', + signalPort: 'SignalPort', + signalPortPlease: 'Please enter the signaling port', + signalPortTip: 'Port corresponding to the interface', + rangePicker: 'Start End Time', + rangePickerPlease: 'Please select the start and end time of the task', + comment: 'Task description', + commentPlease: 'Task description can be entered', + addTask: "Add Task", + editTask: "Modify Task", + viewTask: "View Task", + errorTaskInfo: "Failed to obtain task information", + delTask: "Successfully deleted task {num}", + delTaskTip: "Are you sure to delete the data item with record number {num}?", + }, }, }, }; diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts index 51371fa3..f6eef0ba 100644 --- a/src/i18n/locales/zh-CN.ts +++ b/src/i18n/locales/zh-CN.ts @@ -17,6 +17,7 @@ export default { close: '关闭', search: '搜索', reset: '重置', + viewText: '查看详情', addText: '新增', editText: '编辑', deleteText: '删除', @@ -175,6 +176,43 @@ export default { capStart: '开始抓包', capStop: '停止抓包', }, + task: { + neTypePlease: '请选择网元类型', + neType: '网元类型', + neID: '网元内部标识', + trackType: '跟踪类型', + trackTypePlease: '请选择跟踪类型', + creater: '创建人', + startTime: '开始时间', + endTime: '结束时间', + msisdn: 'MSISDN', + msisdnPlease: '请输入MSISDN', + msisdnTip: '移动通信MSISDN编号', + imsi: 'IMSI', + imsiPlease: '请输入IMSI', + imsiTip: '移动通信IMSI编号', + srcIp: '源IP地址', + srcIpPlease: '请输入源IP地址', + srcIpTip: '当前发送端IPv4地址', + dstIp: '目标IP地址', + dstIpPlease: '请输入目标IP地址', + dstIpTip: '对方接收端IPv4地址', + interfaces: '信令接口', + interfacesPlease: '请输入信令接口', + signalPort: '信令端口', + signalPortPlease: '请输入信令端口', + signalPortTip: '接口对应的端口', + rangePicker: '开始结束时间', + rangePickerPlease: '请选择任务时间开始结束时间', + comment: '任务说明', + commentPlease: '可输入任务说明', + addTask: "添加任务", + editTask: "修改任务", + viewTask: "查看任务", + errorTaskInfo: "获取任务信息失败", + delTask: "成功删除任务 {num}", + delTaskTip: "确认删除记录编号为 {num} 的数据项?", + }, }, }, }; diff --git a/src/utils/regular-utils.ts b/src/utils/regular-utils.ts index a7463473..306a76a5 100644 --- a/src/utils/regular-utils.ts +++ b/src/utils/regular-utils.ts @@ -1,3 +1,15 @@ +/** + * 有效IPv4格式地址 + */ +export const regExpIPv4 = + /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + +/** + * 有效端口格式 + */ +export const regExpPort = + /^([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/; + /** * 有效账号格式 * diff --git a/src/views/trace/task/index.vue b/src/views/trace/task/index.vue index 0cdd4c71..30d79ad2 100644 --- a/src/views/trace/task/index.vue +++ b/src/views/trace/task/index.vue @@ -8,21 +8,33 @@ import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface'; import { ColumnsType } from 'ant-design-vue/lib/table'; import { parseDateToStr } from '@/utils/date-utils'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; -import { - listNeBackup, - delNeBackup, - downloadNeBackup, -} from '@/api/configManage/backupManage'; -import { saveAs } from 'file-saver'; import useI18n from '@/hooks/useI18n'; -import { getConfigInfo, updateConfig } from '@/api/configManage/config'; +import useUserStore from '@/store/modules/user'; import useNeInfoStore from '@/store/modules/neinfo'; +import { + addTraceTask, + delTraceTask, + getTraceTask, + listTraceTask, + updateTraceTask, +} from '@/api/trace/task'; +import useDictStore from '@/store/modules/dict'; +import { regExpIPv4, regExpPort } from '@/utils/regular-utils'; +const { getDict } = useDictStore(); const { t } = useI18n(); const route = useRoute(); /**路由标题 */ let title = ref((route.meta.title as string) ?? '标题'); +/**字典数据 */ +let dict: { + /**跟踪类型 */ + traceType: DictType[]; +} = reactive({ + traceType: [], +}); + /**查询参数 */ let queryParams = reactive({ /**网元类型 */ @@ -76,23 +88,38 @@ let tableColumns: ColumnsType = [ align: 'center', }, { - title: t('views.configManage.backupManage.neType'), + title: t('views.trace.task.neType'), dataIndex: 'neType', align: 'center', }, { - title: t('views.configManage.backupManage.neID'), + title: t('views.trace.task.neID'), dataIndex: 'neId', align: 'center', }, { - title: t('views.configManage.backupManage.fileName'), - dataIndex: 'fileName', + title: t('views.trace.task.trackType'), + dataIndex: 'traceType', + key: 'traceType', align: 'center', }, { - title: t('views.configManage.backupManage.createAt'), - dataIndex: 'createTime', + title: t('views.trace.task.trackType'), + dataIndex: 'accountId', + align: 'center', + }, + { + title: t('views.trace.task.startTime'), + dataIndex: 'startTime', + align: 'center', + customRender(opt) { + if (!opt.value) return ''; + return parseDateToStr(opt.value); + }, + }, + { + title: t('views.trace.task.endTime'), + dataIndex: 'endTime', align: 'center', customRender(opt) { if (!opt.value) return ''; @@ -139,49 +166,21 @@ function fnTableSize({ key }: MenuInfo) { tableState.size = key as SizeType; } -/**信息文件下载 */ -function fnDownloadFile(row: Record) { - Modal.confirm({ - title: '提示', - content: `确认下载记录编号为 【${row.id}】 的数据项文件?`, - onOk() { - const key = 'downloadNeBackup'; - message.loading({ content: t('common.loading'), key }); - downloadNeBackup(toRaw(row)).then(res => { - if (res.code === RESULT_CODE_SUCCESS) { - message.success({ - content: `已完成下载`, - key, - duration: 2, - }); - saveAs(res.data, `user_${Date.now()}.xlsx`); - } else { - message.error({ - content: `${res.msg}`, - key, - duration: 2, - }); - } - }); - }, - }); -} - /** * 备份信息删除 * @param row 记录编号ID */ -function fnRecordDelete(row: Record) { +function fnRecordDelete(id: string) { Modal.confirm({ - title: '提示', - content: `确认删除记录编号为 【${row.id}】 的数据项?`, + title: t('views.trace.task.tipTitle'), + content: t('views.trace.task.delTaskTip', { num: id }), onOk() { - const key = 'delNeBackup'; - message.loading({ content: '请稍等...', key }); - delNeBackup(toRaw(row)).then(res => { - if (res.code === 200) { + const key = 'delTraceTask'; + message.loading({ content: t('common.loading'), key }); + delTraceTask(id).then(res => { + if (res.code === RESULT_CODE_SUCCESS) { message.success({ - content: `删除成功`, + content: t('views.trace.task.delTask', { num: id }), key, duration: 2, }); @@ -198,11 +197,11 @@ function fnRecordDelete(row: Record) { }); } -/**查询备份信息列表 */ +/**查询信息列表 */ function fnGetList() { if (tableState.loading) return; tableState.loading = true; - listNeBackup(toRaw(queryParams)).then(res => { + listTraceTask(toRaw(queryParams)).then(res => { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) { // 取消勾选 if (tableState.selectedRowKeys.length > 0) { @@ -217,10 +216,20 @@ function fnGetList() { /**对话框对象信息状态类型 */ type ModalStateType = { + /**详情框是否显示 */ + visibleByView: boolean; /**新增框或修改框是否显示 */ visibleByEdit: boolean; /**标题 */ title: string; + /**网元类型设备对象 */ + neType: string[]; + /**网元类型设备对象接口 */ + neTypeInterface: Record[]; + /**网元类型设备对象接口选择 */ + neTypeInterfaceSelect: string[]; + /**任务开始结束时间 */ + timeRangePicker: [string, string]; /**表单数据 */ from: Record; /**确定按钮 loading */ @@ -229,57 +238,230 @@ type ModalStateType = { /**对话框对象信息状态 */ let modalState: ModalStateType = reactive({ + visibleByView: false, visibleByEdit: false, - title: '任务设置', + title: '', + neType: [], + neTypeInterface: [], + neTypeInterfaceSelect: [], + timeRangePicker: ['', ''], from: { - configTag: "", - autoBackupTime: "", + id: '', + neType: '', + neId: '', + traceType: 'Device', + startTime: '', + endTime: '', + comment: '', + // 跟踪类型用户 + imsi: '', + msisdn: '', + // 跟踪类型接口 + srcIp: '', + dstIp: '', + interfaces: '', + signalPort: '', }, confirmLoading: false, }); -/** - * 对话框弹出显示为 新增或者修改 - * @param noticeId 网元id, 不传为新增 - */ -function fnModalVisibleByEdit() { - if (modalState.confirmLoading) return; - const hide = message.loading('正在打开...', 0); - modalState.confirmLoading = true; - getConfigInfo('NfConfigSet').then(res => { - modalState.confirmLoading = false; - hide(); - if (res.code === RESULT_CODE_SUCCESS) { - modalState.from.configTag = res.data.configTag - modalState.from.autoBackupTime = res.data.value - modalState.title = t('views.configManage.backupManage.setBackupTask'); - modalState.visibleByEdit = true; - } else { - message.error(`获取配置信息失败`, 2); - } - }); -} - /**对话框内表单属性和校验规则 */ const modalStateFrom = Form.useForm( modalState.from, reactive({ - autoBackupTime: [{ required: true, message: '备份时间不能为空' }], + traceType: [ + { + required: true, + message: t('views.trace.task.trackTypePlease'), + }, + ], + neId: [ + { + required: true, + message: t('views.trace.task.neTypePlease'), + }, + ], + endTime: [ + { + required: true, + message: t('views.trace.task.rangePickerPlease'), + }, + ], + // 跟踪用户 + imsi: [ + { + required: true, + message: t('views.trace.task.imsiPlease'), + }, + ], + msisdn: [ + { + required: true, + message: t('views.trace.task.msisdnPlease'), + }, + ], + // 跟踪接口 + srcIp: [ + { + required: true, + pattern: regExpIPv4, + message: t('views.trace.task.srcIpPlease'), + }, + ], + dstIp: [ + { + required: true, + pattern: regExpIPv4, + message: t('views.trace.task.dstIpPlease'), + }, + ], + interfaces: [ + { + required: true, + message: t('views.trace.task.interfacesPlease'), + }, + ], + signalPort: [ + { + required: true, + pattern: regExpPort, + message: t('views.trace.task.signalPortPlease'), + }, + ], }) ); + +/**网元类型选择对应修改 */ +function fnNeChange(_: any, item: any) { + modalState.from.neType = item[1].neType; + modalState.from.neId = item[1].neId; + + modalState.from.interfaces = ''; + modalState.neTypeInterfaceSelect = []; + if (modalState.from.traceType !== 'Interface') return; + // 网元信令接口 + fnSelectInterfaceInit(item[1].neType); +} + +/**开始结束时间选择对应修改 */ +function fnRangePickerChange(_: any, item: any) { + modalState.from.startTime = item[0]; + modalState.from.endTime = item[1]; +} + +/**信令接口选择对应修改 */ +function fnSelectInterface(s: any, _: any) { + modalState.from.interfaces = s.join(','); +} + +/**信令接口选择初始 */ +function fnSelectInterfaceInit(neType: string) { + const interfaces = useNeInfoStore().traceInterfaceList; + modalState.neTypeInterface = interfaces + .filter(i => i.neType === neType) + .map(i => { + return { + value: i.interface, + label: i.interface, + }; + }); +} + +/** + * 对话框弹出显示为 新增或者修改 + * @param noticeId 网元id, 不传为新增 + */ +function fnModalVisibleByVive(id: string) { + if (modalState.confirmLoading) return; + const hide = message.loading(t('common.loading'), 0); + modalState.confirmLoading = true; + getTraceTask(id).then(res => { + modalState.confirmLoading = false; + hide(); + if (res.code === RESULT_CODE_SUCCESS) { + modalState.neType = [res.data.neType, res.data.neId]; + modalState.timeRangePicker = [res.data.startTime, res.data.endTime]; + modalState.from = Object.assign(modalState.from, res.data); + // 接口 + if (res.data.traceType === 'Interface') { + if ( + res.data.interfaces.length > 4 && + res.data.interfaces.includes('[') + ) { + modalState.neTypeInterfaceSelect = JSON.parse(res.data.interfaces); + } + fnSelectInterfaceInit(res.data.neType); + } + modalState.title = t('views.trace.task.viewTask'); + modalState.visibleByView = true; + } else { + message.error(t('views.trace.task.errorTaskInfo'), 3); + } + }); +} + +/** + * 对话框弹出显示为 新增或者修改 + * @param noticeId 网元id, 不传为新增 + */ +function fnModalVisibleByEdit(id?: string) { + if (!id) { + modalStateFrom.resetFields(); + modalState.title = t('views.trace.task.addTask'); + modalState.visibleByEdit = true; + } else { + if (modalState.confirmLoading) return; + const hide = message.loading(t('common.loading'), 0); + modalState.confirmLoading = true; + getTraceTask(id).then(res => { + modalState.confirmLoading = false; + hide(); + if (res.code === RESULT_CODE_SUCCESS && res.data) { + modalState.neType = [res.data.neType, res.data.neId]; + modalState.timeRangePicker = [res.data.startTime, res.data.endTime]; + modalState.from = Object.assign(modalState.from, res.data); + // 接口 + if (res.data.traceType === 'Interface') { + if ( + res.data.interfaces.length > 4 && + res.data.interfaces.includes('[') + ) { + modalState.neTypeInterfaceSelect = JSON.parse(res.data.interfaces); + } + fnSelectInterfaceInit(res.data.neType); + } + modalState.title = t('views.trace.task.editTask'); + modalState.visibleByEdit = true; + } else { + message.error(t('views.trace.task.errorTaskInfo'), 3); + } + }); + } +} + /** * 对话框弹出确认执行函数 * 进行表达规则校验 */ - function fnModalOk() { +function fnModalOk() { + const from = toRaw(modalState.from); + let valids = ['traceType', 'neId', 'endTime']; + if (from.traceType === 'UE') { + valids = valids.concat(['imsi', 'msisdn']); + } + if (from.traceType === 'Interface') { + valids = valids.concat(['srcIp', 'dstIp', 'interfaces', 'signalPort']); + } + from.accountId = useUserStore().userName; modalStateFrom - .validate() + .validate(valids) .then(e => { modalState.confirmLoading = true; - const from = toRaw(modalState.from); - const hide = message.loading({ content: t('common.loading') }); - updateConfig(from.configTag, {value: from.autoBackupTime}) + const traceTask = from.id ? updateTraceTask(from) : addTraceTask(from); + const hide = message.loading(t('common.loading'), 0); + traceTask .then(res => { + console.log(res); if (res.code === RESULT_CODE_SUCCESS) { message.success({ content: t('common.msgSuccess', { msg: modalState.title }), @@ -297,6 +479,7 @@ const modalStateFrom = Form.useForm( .finally(() => { hide(); modalState.confirmLoading = false; + fnGetList(); }); }) .catch(e => { @@ -309,13 +492,32 @@ const modalStateFrom = Form.useForm( * 进行表达规则校验 */ function fnModalCancel() { + modalState.visibleByView = false; modalState.visibleByEdit = false; + modalState.confirmLoading = false; modalStateFrom.resetFields(); + modalState.timeRangePicker = ['', '']; + modalState.neTypeInterfaceSelect = []; + modalState.neType = []; + modalState.neTypeInterface = []; } onMounted(() => { - // 获取列表数据 - fnGetList(); + // 初始字典数据 + Promise.allSettled([getDict('trace_type')]).then(resArr => { + if (resArr[0].status === 'fulfilled') { + dict.traceType = resArr[0].value; + } + }); + Promise.allSettled([ + // 获取网元网元列表 + useNeInfoStore().fnNelist(), + // 获取跟踪接口列表 + useNeInfoStore().fnNeTraceInterface(), + ]).finally(() => { + // 获取列表数据 + fnGetList(); + }); }); @@ -330,16 +532,13 @@ onMounted(() => { - - + + :placeholder="t('views.trace.task.neTypePlease')" + /> @@ -363,18 +562,16 @@ onMounted(() => {