diff --git a/.env.development b/.env.development index ccdd35ea..98a7b1ff 100644 --- a/.env.development +++ b/.env.development @@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network OMC" VITE_APP_CODE = "OMC" # 应用版本 -VITE_APP_VERSION = "2.250529" +VITE_APP_VERSION = "2.2506.4" # 接口基础URL地址-不带/后缀 VITE_API_BASE_URL = "/omc-api" diff --git a/.env.production b/.env.production index ccdd35ea..98a7b1ff 100644 --- a/.env.production +++ b/.env.production @@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network OMC" VITE_APP_CODE = "OMC" # 应用版本 -VITE_APP_VERSION = "2.250529" +VITE_APP_VERSION = "2.2506.4" # 接口基础URL地址-不带/后缀 VITE_API_BASE_URL = "/omc-api" diff --git a/CHANGELOG.md b/CHANGELOG.md index a4d2773e..98972264 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,110 @@ # 版本发布日志 +## 2.2506.4-20250627 + +- 修复 UDM-IMS数据批量新增/批量删除命令调整 +- 优化 AMF/MME对应的IMEI白名单模板去除index + +## 2.2506.3-20250620 + +- 新增 UDM签约cnType新增可选1仅5G/2仅4G +- 新增 给AMF/MME对应的IMEI白名单添加批量导入和批量删除功能 +- 新增 UDM voup/ims导入失败文件下载 + +## 2.2506.2-20250613 + +- 修复 数值控件属性maxlength拼写错误 +- 修复 空格内容无法编辑,提示信息显示方向 +- 优化 抓包文件目录列表显示文件大小 + +## 2.2506.1-20250607 + +- 优化 请求响应码常量,身份信息更换逻辑优化 +- 修复 更新tcpdump路径从/tmp到/usr/local以符合新目录结构 + + +## 2.2505.4-20250530 + +- 优化 网元参数配置修改tooltip位置为bottomLeft防止遮挡 +- 修复 服务器时间格式化指定时区 +- 优化 网元授权状态更新容量和更新时间局部变化 +- 修复 网元信令跟踪支持选择轻量版UPF +- 修复 看板数据加载间隔从5秒调整为10秒 +- 修复 看板UDM选择添加下拉菜单的弹出容器属性 + +## 2.2505.3-20250523 + +- 修复 修复同步操作加载提示未隐藏的问题 +- 修复 告警确认相关提示Tip多语言显示 +- 修复 网元授权许可证状态统一刷新功能 +- 修复 活动告警启用历史同步按钮 +- 修复 更新批量刷新信息,容量仅UDM/AMF/MME +- 修复 仪表盘数据会话属累加 +- 修复 网元主机信息编辑表单不验证免密类型, 主机列表的查询类型参数 + +## 2.2505.2-20250516 + +- 修复 编译类型检查错误 +- 新增 网元授权上传变更确认延迟2s刷新授权状态 +- 修复 英文修改基站名称为 RAN Node Name +- 新增 根据网元类型过滤导出备份数据可选的数据来源 +- 新增 跟踪任务添加标题,编号后面加任务详情 + +## 2.2505.1-20250509 + +- 新增 更新日志文件导出列表,添加系统登录和操作日志 +- 新增 网元授权添加用户容量列 +- 修复 OAM读取结构错误 +- 修复 仪表盘用户数据刷新时显示0闪烁 +- 新增 看板含有网元显示区域 +- 新增 根据网元显示特有菜单 +- 修复 移除网络错误时的退出登录状态处理逻辑 + +## 2.2504.4-20250430 + +- 新增 PCF用户策略页面多语言翻译 +- 新增 在上传切片文件到网元端时添加删除临时文件选项 +- 修复 优化PCF用户策略页面及接口调整 +- 修复 时间控件格式移除插件属性value-format="x" +- 新增 服务器时区UTC格式转换 +- 新增 添加网络错误处理,退出登录状态 +- 修复 替换获取服务器时间接口 +- 移除 移除部分旧/apt/rest/直连接口 +- 新增 网元服务操作局部状态刷新,补充提示信息 +- 修复 时间格式常量定义及默认格式 +- 新增 添加IMS SIP响应码原因显示 +- 修复 看板流量图确保时间以HH:mm:ss格式显示 +- 修复 看板用户活动乱序问题 +- 修复 修复引导获取token存储问题 + +## 2.2503.4-20250331 + +- 更新 依赖项版本 +- 修复 去除请求方法PATCH +- 重构 重构令牌管理逻辑,,统一状态码识别 +- 更新 编译文件引用缺失 +- 新增 UDMVolte用户特殊VoIP数据 +- 优化 补充多语言翻译 +- 修复 调度任务参数最大输入2000字符。日志查看id错误修复 +- 重构 统一ftp操作功能,备份文件查看下载删除功能 +- 优化 neFile目录移动到ne +- 新增 终端目录部分调整,添加udm-voip/volte功能页面 +- 新增 修改OAM配置中的同步功能为重启功能 +- 新增 添加告警信息导出功能,并优化历史告警页面 +- 修复 统一国际时区格式 +- 新增 更新锁屏密码Basd64编码处理,无密码直接进入 +- 新增 PCAP文件解析功能页面优化 +- 修复 移除信令接口查询 +- 新增 支持ws工具心跳ping消息 +- 重构 重构信令跟踪功能 +- 重构 重构网元跟踪任务功能页面 +- 修复 看板用户数初始neId传入失败,禁止选择当前项 +- 新增 添加网元配置文件备份的FTP配置管理功能 +- 修复 mml补充UDM特殊命令处理 +- 新增 UE添加导入模板下载功能 +- 修复 网元授权上传后点击ok检查状态 +- 新增 添加密码过期功能支持,密码有效期时间天 + ## 2.2404.1-20240402 - 新增 网元安装流程相关页面与操作相关接口联调 diff --git a/public/neDataImput/import_amf_imeiWhitelist_template.xlsx b/public/neDataImput/import_amf_imeiWhitelist_template.xlsx new file mode 100644 index 00000000..8716464a Binary files /dev/null and b/public/neDataImput/import_amf_imeiWhitelist_template.xlsx differ diff --git a/public/neDataImput/import_amf_whitelist_template.xlsx b/public/neDataImput/import_amf_whitelist_template.xlsx new file mode 100644 index 00000000..26b66e5a Binary files /dev/null and b/public/neDataImput/import_amf_whitelist_template.xlsx differ diff --git a/public/neDataImput/import_mme_imeiWhitelist_template.xlsx b/public/neDataImput/import_mme_imeiWhitelist_template.xlsx new file mode 100644 index 00000000..44957994 Binary files /dev/null and b/public/neDataImput/import_mme_imeiWhitelist_template.xlsx differ diff --git a/src/api/neData/udm_volte_ims.ts b/src/api/neData/udm_volte_ims.ts index 7a96bd30..7b65f0fb 100644 --- a/src/api/neData/udm_volte_ims.ts +++ b/src/api/neData/udm_volte_ims.ts @@ -71,13 +71,16 @@ export function batchAddUDMVolteIMS(data: Record, num: number) { /** * UDMVolteIMS用户删除 - * @param data 签约对象 + * @param neId 网元ID + * @param imsi_msisdn IMSI/MSISDN + * @param tag 标签 0-voip 1-volte * @returns object */ -export function delUDMVolteIMS(neId: string, imsi: string) { +export function delUDMVolteIMS(neId: string, imsi_msisdn: string, tag: string) { return request({ - url: `/neData/udm/volte-ims/${neId}/${imsi}`, + url: `/neData/udm/volte-ims/${neId}/${imsi_msisdn}`, method: 'DELETE', + params: { volte: tag }, timeout: 180_000, }); } @@ -87,12 +90,19 @@ export function delUDMVolteIMS(neId: string, imsi: string) { * @param neId 网元ID * @param imsi IMSI * @param num 数量 + * @param tag 标签 0-voip 1-volte * @returns object */ -export function batchDelUDMVolteIMS(neId: string, imsi: string, num: number) { +export function batchDelUDMVolteIMS( + neId: string, + imsi: string, + num: number, + tag: string +) { return request({ url: `/neData/udm/volte-ims/${neId}/${imsi}/${num}`, method: 'DELETE', + params: { volte: tag }, timeout: 180_000, }); } diff --git a/src/constants/result-constants.ts b/src/constants/result-constants.ts index 76332a40..ffc7af24 100644 --- a/src/constants/result-constants.ts +++ b/src/constants/result-constants.ts @@ -19,6 +19,18 @@ export const RESULT_MSG_SUCCESS: Record = { /**响应-code错误失败 */ export const RESULT_CODE_ERROR = 400001; +/**响应-code身份认证失败或者过期 */ +export const RESULT_CODE_AUTH = 401001; + +/**响应-code无效身份信息 */ +export const RESULT_CODE_AUTH_INVALID = 401002; + +/**响应-code令牌字符为空 */ +export const RESULT_CODE_AUTH_NOTOKEN = 401003; + +/**响应-code设备指纹信息不匹配 */ +export const RESULT_CODE_AUTH_DEVICE = 401004; + /**响应-code错误异常 */ export const RESULT_CODE_EXCEPTION = 500001; diff --git a/src/i18n/locales/en-US.ts b/src/i18n/locales/en-US.ts index 2571e585..485d9614 100644 --- a/src/i18n/locales/en-US.ts +++ b/src/i18n/locales/en-US.ts @@ -877,6 +877,10 @@ export default { rfspTip:'RFSP index, in NG-RAN, the index of a specific RRM configuration, parameter between 0 and 127', ueTypeTip: 'Operator-defined subscriber UE Usage Type, integer, parameter between 0 and 127', cnFlag: 'Whether to enable 5G Core Network service', + cnFlag0: 'No Access Allowed', + cnFlag1: 'Access Only 5G', + cnFlag2: 'Access Only 4G', + cnFlag3: 'Access 4G/5G', epsFlagTip: 'Whether to enable 4G EPS service', contextIdTip: 'To sign up for an APN Context ID, you must select it from the APN Context list.', apnContextTip: 'The list of APNs available to the phone, up to six, is defined in the HSS.', diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts index 740035f4..6afa5faa 100644 --- a/src/i18n/locales/zh-CN.ts +++ b/src/i18n/locales/zh-CN.ts @@ -877,6 +877,10 @@ export default { rfspTip:'RFSP 索引,在 NG-RAN 中,特定 RRM 配置的索引,参数介于0到127之间', ueTypeTip: '运营商定义的用户 UE Usage Type,整型,参数介于0到127之间', cnFlag: '是否开启 5G Core Network 服务', + cnFlag0: '不允许接入', + cnFlag1: '只能接入 5G', + cnFlag2: '只能接入 4G', + cnFlag3: '允许接入 4G/5G', epsFlagTip: '是否开启 4G EPS 服务', contextIdTip: '签约APN 上下文ID,必须从APN Context list 中选择。', apnContextTip: '手机可用的APN列表,最多六个,在HSS中定义。', diff --git a/src/plugins/http-fetch.ts b/src/plugins/http-fetch.ts index df8c2f28..d1d8ca5c 100644 --- a/src/plugins/http-fetch.ts +++ b/src/plugins/http-fetch.ts @@ -22,6 +22,10 @@ import { APP_DATA_API_KEY, } from '@/constants/app-constants'; import { + RESULT_CODE_AUTH, + RESULT_CODE_AUTH_DEVICE, + RESULT_CODE_AUTH_INVALID, + RESULT_CODE_AUTH_NOTOKEN, RESULT_CODE_ENCRYPT, RESULT_CODE_ERROR, RESULT_CODE_EXCEPTION, @@ -34,7 +38,7 @@ import { RESULT_MSG_TIMEOUT, RESULT_MSG_URL_RESUBMIT, } from '@/constants/result-constants'; -import { decryptAES, encryptAES } from '@/utils/encrypt-utils'; +import { decryptAES, encryptAES, hexMD5 } from '@/utils/encrypt-utils'; import { localGet } from '@/utils/cache-local-utils'; import { refreshToken } from '@/api/auth'; @@ -52,9 +56,7 @@ export type ResultType = { /**防止重复提交类型 */ type RepeatSubmitType = { - /**请求地址 */ - url: string; - /**请求数据 */ + /**请求数据MD5 */ data: string; /**请求时间 */ time: number; @@ -158,19 +160,19 @@ function beforeRequest(options: OptionsType): OptionsType | Promise { ['POST', 'PUT'].includes(options.method) ) { const requestObj: RepeatSubmitType = { - url: options.url, - data: JSON.stringify(options.data) || '', + data: hexMD5( + JSON.stringify({ + url: options.url, + data: JSON.stringify(options.data) || '', + }) + ), time: Date.now(), }; const sessionObj: RepeatSubmitType = sessionGetJSON(CACHE_SESSION_FATCH); if (sessionObj) { - const { url, data, time } = sessionObj; + const { data, time } = sessionObj; const interval = 3000; // 间隔时间(ms),小于此时间视为重复提交 - if ( - requestObj.url === url && - requestObj.data === data && - requestObj.time - time < interval - ) { + if (requestObj.data === data && requestObj.time - time < interval) { const message = RESULT_MSG_URL_RESUBMIT[language]; return Promise.resolve({ code: RESULT_CODE_ERROR, @@ -232,27 +234,33 @@ async function beforeResponse( ): Promise { // console.log('请求后的拦截', res); - // 登录失效时,移除授权令牌并重新刷新页面 - // 登录失效时,移除访问令牌并重新请求 - if (res.code === 401001) { - const result = await refreshToken(getRefreshToken()); + // 移除授权令牌并重新刷新页面 + function clearToken() { + delAccessToken(); + delRefreshToken(); + window.location.reload(); + } + // 令牌失效时 + if (res.code === RESULT_CODE_AUTH) { + const refreshTokenStr = getRefreshToken(); + if (!refreshTokenStr) { + clearToken(); + } + const result = await refreshToken(refreshTokenStr); // 更新访问令牌和刷新令牌 if (result.code === RESULT_CODE_SUCCESS) { setAccessToken(result.data.accessToken, result.data.refreshExpiresIn); setRefreshToken(result.data.refreshToken, result.data.refreshExpiresIn); return await request(options); + } else if (result.code === RESULT_CODE_AUTH_DEVICE) { + clearToken(); } else { - debugger - console.warn(result) - // delAccessToken(); - // delRefreshToken(); - window.location.reload(); + // clearToken(); } } - if ([401002, 401003].includes(res.code)) { - delAccessToken(); - delRefreshToken(); - window.location.reload(); + // 令牌解析错误 + if ([RESULT_CODE_AUTH_INVALID, RESULT_CODE_AUTH_NOTOKEN].includes(res.code)) { + clearToken(); } // 响应数据解密 diff --git a/src/utils/encrypt-utils.ts b/src/utils/encrypt-utils.ts index 351d125c..f1395f48 100644 --- a/src/utils/encrypt-utils.ts +++ b/src/utils/encrypt-utils.ts @@ -48,3 +48,12 @@ export function decryptAES(ciphertext: string, aeskey: string): string { } return ''; } + +/** + * MD5 编码 + * @param message 字符串信息 + * @returns hax码 + */ +export function hexMD5(message: string): string { + return CryptoJS.MD5(message).toString(CryptoJS.enc.Hex); +} diff --git a/src/views/ne/neConfig/hooks/useArrayBatchDel.ts b/src/views/ne/neConfig/hooks/useArrayBatchDel.ts new file mode 100644 index 00000000..78857c28 --- /dev/null +++ b/src/views/ne/neConfig/hooks/useArrayBatchDel.ts @@ -0,0 +1,85 @@ +import { delNeConfigData } from '@/api/ne/neConfig'; +import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; +import { message } from 'ant-design-vue'; +import { reactive, toRaw } from 'vue'; + +/** + * 批量删除array + * @param param 父级传入 { t, neTypeSelect, fnActiveConfigNode } + * @returns + */ +export default function useArrayBatch({ + t, + neTypeSelect, + fnActiveConfigNode, +}: any) { + /**状态属性 */ + const batchState = reactive({ + open: false, + loading: false, //批量删除 + paramName: '', + startIndex: 1, + num: 1, + }); + + /**对话框表格信息导入弹出窗口 */ + function modalBatchOpen(paramName: string) { + batchState.paramName = paramName; + batchState.open = true; + } + function modalBatchClose() { + if (batchState.loading) { + message.error({ + content: 'Delete is in progress, please wait for it to complete', + duration: 3, + }); + return; + } + batchState.open = false; + batchState.loading = false; + batchState.startIndex = 1; + batchState.num = 1; + fnActiveConfigNode('#'); + } + + async function modalBatchOk() { + let okNum = 0; + let failNum = 0; + const endIndex = batchState.startIndex + batchState.num - 1; + for (let i = endIndex; i >= batchState.startIndex; i--) { + const res = await delNeConfigData({ + neType: neTypeSelect.value[0], + neId: neTypeSelect.value[1], + paramName: batchState.paramName, + loc: `${i}`, + }); + if (res.code === RESULT_CODE_SUCCESS) { + okNum++; + } else { + failNum++; + break; + } + } + + if (okNum > 0) { + message.success({ + content: `Successfully deleted ${okNum} items`, + duration: 3, + }); + } + if (failNum > 0) { + message.error({ + content: `Delete failed, please check the index range`, + duration: 3, + }); + } + modalBatchClose(); + } + + return { + batchState, + modalBatchOpen, + modalBatchClose, + modalBatchOk, + }; +} diff --git a/src/views/ne/neConfig/hooks/useArrayImport.ts b/src/views/ne/neConfig/hooks/useArrayImport.ts new file mode 100644 index 00000000..2dd28b68 --- /dev/null +++ b/src/views/ne/neConfig/hooks/useArrayImport.ts @@ -0,0 +1,205 @@ +import { addNeConfigData, editNeConfigData } from '@/api/ne/neConfig'; +import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; +import { readSheet } from '@/utils/execl-utils'; +import { message } from 'ant-design-vue'; +import { reactive, toRaw } from 'vue'; +import saveAs from 'file-saver'; + +/** + * 导入文件加array + * @param param 父级传入 { t, neTypeSelect, arrayState, fnActiveConfigNode } + * @returns + */ +export default function useArrayImport({ + t, + neTypeSelect, + arrayState, + fnActiveConfigNode, +}: any) { + /**网元导入模板解析 */ + const m: Record = { + AMF: { + imeiWhitelist: { + filename: 'import_amf_imeiWhitelist_template', + fileetx: '.xlsx', + itemKey: 'imeiPrefixValue', + item: (row: Record) => { + return { + imeiPrefixValue: row['IMEI Prefix'], + index: 0, + }; + }, + }, + whitelist: { + filename: 'import_amf_whitelist_template', + fileetx: '.xlsx', + itemKey: 'imsiValue', + item: (row: Record) => { + return { + imsiValue: row['IMSI Value'], + imeiValue: row['IMEI Value/Prefix'], + index: 0, + }; + }, + }, + }, + MME: { + white_list: { + filename: 'import_mme_imeiWhitelist_template', + fileetx: '.xlsx', + itemKey: 'imei', + item: (row: Record) => { + return { + imei: row['IMEI'], + index: 0, + }; + }, + }, + }, + }; + + /**状态属性 */ + const importState = reactive({ + open: false, + msgArr: [] as string[], + loading: false, //开始导入 + itemKey: '', // 解析item的key + item: null as any, // 解析item方法 + paramName: '', + filename: '', + fileetx: '', + }); + + /**对话框表格信息导入弹出窗口 */ + function modalImportOpen(neType: string, paramName: string) { + const tmpM = m[neType][paramName]; + importState.itemKey = tmpM.itemKey; + importState.item = tmpM.item; + importState.paramName = paramName; + importState.filename = tmpM.filename; + importState.fileetx = tmpM.fileetx; + importState.open = true; + } + function modalImportClose() { + if (importState.loading) { + message.error({ + content: 'Import is in progress, please wait for it to complete', + duration: 3, + }); + return; + } + importState.open = false; + importState.msgArr = []; + importState.loading = false; + fnActiveConfigNode('#'); + } + + /**对话框表格信息导入上传 */ + async function modalImportUpload(file: File) { + const hide = message.loading(t('common.loading'), 0); + const [neType, neId] = neTypeSelect.value; + importState.msgArr = []; + + // 获取最大index + let index = 0; + if (arrayState.columnsData.length <= 0) { + index = 0; + } else { + const last = arrayState.columnsData[arrayState.columnsData.length - 1]; + index = last.index.value + 1; + } + + const reader = new FileReader(); + reader.onload = function (e: any) { + const arrayBuffer = e.target.result; + readSheet(arrayBuffer).then(async rows => { + if (rows.length <= 0) { + hide(); + message.error({ + content: t('views.neData.baseStation.importDataEmpty'), + duration: 3, + }); + return; + } + // 开始导入 + importState.loading = true; + for (const row of rows) { + const rowItem = importState.item(row); + const rowKey = rowItem[importState.itemKey]; + let result: any = null; + // 检查index是否定义 + const has = arrayState.columnsData.find( + (item: any) => item[importState.itemKey].value === rowKey + ); + if (has) { + // 已定义则更新 + rowItem.index = has.index.value; + result = await editNeConfigData({ + neType: neType, + neId: neId, + paramName: importState.paramName, + paramData: rowItem, + loc: `${rowItem.index}`, + }); + let msg = `index:${rowItem.index} update fail`; + if (result.code === RESULT_CODE_SUCCESS) { + msg = `index:${rowItem.index} update success`; + } + importState.msgArr.push(msg); + } else { + // 未定义则新增 + result = await addNeConfigData({ + neType: neType, + neId: neId, + paramName: importState.paramName, + paramData: Object.assign(rowItem, { index }), + loc: `${index}`, + }); + let msg = `index:${index} add fail`; + if (result.code === RESULT_CODE_SUCCESS) { + msg = `index:${index} add success`; + index += 1; + } + importState.msgArr.push(msg); + } + } + + hide(); + importState.loading = false; + }); + }; + reader.onerror = function (e) { + hide(); + console.error('reader file error:', e); + }; + reader.readAsArrayBuffer(file); + } + + /**对话框表格信息导入模板 */ + function modalImportTemplate() { + const hide = message.loading(t('common.loading'), 0); + + const baseUrl = import.meta.env.VITE_HISTORY_BASE_URL; + const templateUrl = `${ + baseUrl.length === 1 && baseUrl.indexOf('/') === 0 + ? '' + : baseUrl.indexOf('/') === -1 + ? '/' + baseUrl + : baseUrl + }/neDataImput`; + saveAs( + `${templateUrl}/${importState.filename}${importState.fileetx}`, + `${importState.filename}_${Date.now()}${importState.fileetx}` + ); + + hide(); + } + + return { + importState, + modalImportOpen, + modalImportClose, + modalImportUpload, + modalImportTemplate, + }; +} diff --git a/src/views/ne/neConfig/index.vue b/src/views/ne/neConfig/index.vue index ddd73d85..6633e02e 100644 --- a/src/views/ne/neConfig/index.vue +++ b/src/views/ne/neConfig/index.vue @@ -12,6 +12,8 @@ import useOptions from './hooks/useOptions'; import useConfigList from './hooks/useConfigList'; import useConfigArray from './hooks/useConfigArray'; import useConfigArrayChild from './hooks/useConfigArrayChild'; +import useArrayImport from './hooks/useArrayImport'; +import useArrayBatchDel from './hooks/useArrayBatchDel'; import { getAllNeConfig, getNeConfigData } from '@/api/ne/neConfig'; const neListStore = useNeListStore(); const { t } = useI18n(); @@ -219,16 +221,26 @@ function fnGetNeConfig() { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) { const arr = []; for (const item of res.data) { + // 是否可见 + if (!item['visible']) { + item['visible'] = 'public'; + } else if (item['visible'] == 'hide') { + continue; + } + // 权限控制 let paramPerms: string[] = []; if (item.paramPerms) { paramPerms = item.paramPerms.split(','); } else { paramPerms = ['post', 'put', 'delete']; } + const title = item.paramDisplay; + // 处理字符串开头特殊字符 + item.paramDisplay = title.replace(/[└─]+/, ''); arr.push({ ...item, children: undefined, - title: item.paramDisplay, + title: title, key: item.paramName, paramPerms, }); @@ -362,6 +374,21 @@ const { arrayEditClose, }); +const { + importState, + modalImportOpen, + modalImportClose, + modalImportUpload, + modalImportTemplate, +} = useArrayImport({ t, neTypeSelect, arrayState, fnActiveConfigNode }); + +const { batchState, modalBatchOpen, modalBatchClose, modalBatchOk } = + useArrayBatchDel({ + t, + neTypeSelect, + fnActiveConfigNode, + }); + onMounted(() => { // 获取网元网元列表 neCascaderOptions.value = neListStore.getNeCascaderOptions.filter( @@ -428,7 +455,7 @@ onMounted(() => { @@ -904,6 +965,84 @@ onMounted(() => { + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/views/neData/udm-sub/index.vue b/src/views/neData/udm-sub/index.vue index 2087f7d4..993f573c 100644 --- a/src/views/neData/udm-sub/index.vue +++ b/src/views/neData/udm-sub/index.vue @@ -1779,10 +1779,16 @@ onMounted(() => { > - {{ t('views.neUser.sub.enable') }} + {{ t('views.neUser.sub.cnFlag3') }} + + + {{ t('views.neUser.sub.cnFlag2') }} + + + {{ t('views.neUser.sub.cnFlag1') }} - {{ t('views.neUser.sub.disable') }} + {{ t('views.neUser.sub.cnFlag0') }} diff --git a/src/views/neData/udm-voip/index.vue b/src/views/neData/udm-voip/index.vue index 115cc618..98ddb348 100644 --- a/src/views/neData/udm-voip/index.vue +++ b/src/views/neData/udm-voip/index.vue @@ -26,7 +26,8 @@ import useNeListStore from '@/store/modules/ne_list'; import useI18n from '@/hooks/useI18n'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { saveAs } from 'file-saver'; -import { uploadFileToNE } from '@/api/tool/file'; +import { uploadFile } from '@/api/tool/file'; +import { getNeViewFile } from '@/api/tool/neFile'; const { t } = useI18n(); const neListStore = useNeListStore(); /**网元参数 */ @@ -489,15 +490,27 @@ function fnModalUploadImportUpload(file: File) { } const hide = message.loading(t('common.loading'), 0); uploadImportState.loading = true; - uploadFileToNE('UDM', neID, file, 5) + // 上传文件 + let formData = new FormData(); + formData.append('file', file); + formData.append('subPath', 'import'); + uploadFile(formData) .then(res => { if (res.code === RESULT_CODE_SUCCESS) { - return importUDMVOIP({ - neId: neID, - uploadPath: res.data, - }); + return res.data.filePath; + } else { + uploadImportState.msg = res.msg; + uploadImportState.loading = false; + return ''; } - return res; + }) + .then((filePath: string) => { + if (!filePath) return; + // 文件导入 + return importUDMVOIP({ + neId: neID, + uploadPath: filePath, + }); }) .then(res => { if (!res) return; @@ -517,6 +530,33 @@ function fnModalUploadImportUpload(file: File) { }); } +/**对话框表格信息导入失败原因 */ +function fnModalUploadImportFailReason() { + const neId = queryParams.neId; + if (!neId) return; + const hide = message.loading(t('common.loading'), 0); + getNeViewFile({ + neType: 'UDM', + neId: neId, + path: '/tmp', + fileName: 'import_imsuser_err_records.txt', + }) + .then(res => { + if (res.code === RESULT_CODE_SUCCESS) { + message.success(t('common.operateOk'), 3); + const blob = new Blob([res.data], { + type: 'text/plain', + }); + saveAs(blob, `import_udmvoip_err_records_${Date.now()}.txt`); + } else { + message.error(`${res.msg}`, 3); + } + }) + .finally(() => { + hide(); + }); +} + /**对话框表格信息导入模板 */ function fnModalDownloadImportTemplate() { const hide = message.loading(t('common.loading'), 0); @@ -554,7 +594,7 @@ onMounted(() => { if (neOtions.value.length > 0) { queryParams.neId = neOtions.value[0].value; } - + // 获取列表数据 fnGetList(); }); @@ -835,7 +875,7 @@ onMounted(() => { v-model:value="modalState.from.username" style="width: 100%" :min="4" - :maxlangth="16" + :maxlength="16" :placeholder="t('views.neData.udmVOIP.username')" > @@ -868,14 +908,13 @@ onMounted(() => { name="username" v-bind="modalStateFrom.validateInfos.username" > - - + { - + + + diff --git a/src/views/neData/udm-volte/index.vue b/src/views/neData/udm-volte/index.vue index 296591d2..0c96e32e 100644 --- a/src/views/neData/udm-volte/index.vue +++ b/src/views/neData/udm-volte/index.vue @@ -16,7 +16,8 @@ import useNeListStore from '@/store/modules/ne_list'; import useI18n from '@/hooks/useI18n'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { saveAs } from 'file-saver'; -import { uploadFileToNE } from '@/api/tool/file'; +import { uploadFile } from '@/api/tool/file'; +import { getNeViewFile } from '@/api/tool/neFile'; import { addUDMVolteIMS, batchAddUDMVolteIMS, @@ -93,7 +94,7 @@ type TabeStateType = { data: object[]; /**勾选记录 */ selectedRowKeys: (string | number)[]; - selectedRowIMSIs: (string | number)[]; + selectedRowIMSIs: Record[]; }; /**表格状态 */ @@ -202,7 +203,13 @@ function fnTableSize({ key }: MenuInfo) { /**表格多选 */ function fnTableSelectedRowKeys(keys: (string | number)[], rows: any[]) { tableState.selectedRowKeys = keys; - tableState.selectedRowIMSIs = rows.map(item => item.imsi); + tableState.selectedRowIMSIs = rows.map(item => { + return { + imsi: item.imsi, + msisdn: item.msisdn, + tag: item.tag, + }; + }); } /**对话框对象信息状态类型 */ @@ -308,7 +315,12 @@ function fnModalOk() { result = batchAddUDMVolteIMS(from, from.num); } if (modalState.type === 'delete') { - result = batchDelUDMVolteIMS(from.neId, from.imsi, from.num); + result = batchDelUDMVolteIMS( + from.neId, + `${from.imsi}_${from.msisdn}`, + from.num, + from.tag + ); } } else { if (modalState.type === 'add') { @@ -374,15 +386,14 @@ function fnModalVisibleByBatch(type: 'delete' | 'add') { /** * 记录删除 - * @param imsi 网元编号ID + * @param id 记录ID */ -function fnRecordDelete(imsi: string) { +function fnRecordDelete(id: string) { const neID = queryParams.neId; if (!neID) return; - let msg = imsi; - if (imsi === '0') { - msg = `${tableState.selectedRowIMSIs[0]}... ${tableState.selectedRowIMSIs.length}`; - imsi = tableState.selectedRowIMSIs.join(','); + let msg = id; + if (id === '0') { + msg = `${tableState.selectedRowIMSIs[0].imsi}... ${tableState.selectedRowIMSIs.length}`; } Modal.confirm({ @@ -390,17 +401,34 @@ function fnRecordDelete(imsi: string) { content: t('views.neData.udmVolteIMS.delTip', { num: msg }), onOk() { const hide = message.loading(t('common.loading'), 0); - delUDMVolteIMS(neID, imsi) - .then(res => { - if (res.code === RESULT_CODE_SUCCESS) { - message.success(t('common.operateOk'), 3); - fnGetList(); - } else { - message.error({ - content: `${res.msg}`, - duration: 3, - }); - } + let reqArr: any[] = []; + if (id === '0') { + const volteArr = tableState.selectedRowIMSIs + .filter(item => item.tag == '1') + .map(item => `${item.imsi}_${item.msisdn}`) + .join(','); + if (volteArr.length > 0) { + reqArr.push(delUDMVolteIMS(neID, volteArr, '1')); + } + const voipArr = tableState.selectedRowIMSIs + .filter(item => item.tag == '0') + .map(item => `${item.imsi}_${item.msisdn}`) + .join(','); + if (voipArr.length > 0) { + reqArr.push(delUDMVolteIMS(neID, voipArr, '0')); + } + } else { + const record: any = tableState.data.find((item: any) => item.id === id); + if (record) { + reqArr = [ + delUDMVolteIMS(neID, `${record.imsi}_${record.msisdn}`, record.tag), + ]; + } + } + Promise.all(reqArr) + .then(() => { + message.success(t('common.operateOk'), 3); + fnGetList(); }) .finally(() => { hide(); @@ -541,15 +569,27 @@ function fnModalUploadImportUpload(file: File) { } const hide = message.loading(t('common.loading'), 0); uploadImportState.loading = true; - uploadFileToNE('UDM', neID, file, 5) + // 上传文件 + let formData = new FormData(); + formData.append('file', file); + formData.append('subPath', 'import'); + uploadFile(formData) .then(res => { if (res.code === RESULT_CODE_SUCCESS) { - return importUDMVolteIMS({ - neId: neID, - uploadPath: res.data, - }); + return res.data.filePath; + } else { + uploadImportState.msg = res.msg; + uploadImportState.loading = false; + return ''; } - return res; + }) + .then((filePath: string) => { + if (!filePath) return; + // 文件导入 + return importUDMVolteIMS({ + neId: neID, + uploadPath: filePath, + }); }) .then(res => { if (!res) return; @@ -569,6 +609,33 @@ function fnModalUploadImportUpload(file: File) { }); } +/**对话框表格信息导入失败原因 */ +function fnModalUploadImportFailReason() { + const neId = queryParams.neId; + if (!neId) return; + const hide = message.loading(t('common.loading'), 0); + getNeViewFile({ + neType: 'UDM', + neId: neId, + path: '/tmp', + fileName: 'import_imsuser_err_records.txt', + }) + .then(res => { + if (res.code === RESULT_CODE_SUCCESS) { + message.success(t('common.operateOk'), 3); + const blob = new Blob([res.data], { + type: 'text/plain', + }); + saveAs(blob, `import_udmvolte_err_records_${Date.now()}.txt`); + } else { + message.error(`${res.msg}`, 3); + } + }) + .finally(() => { + hide(); + }); +} + /**对话框表格信息导入模板 */ function fnModalDownloadImportTemplate() { const hide = message.loading(t('common.loading'), 0); @@ -847,7 +914,7 @@ onMounted(() => { + { : '' " :label=" - modalState.isBatch && modalState.from.tag === '0' + modalState.isBatch ? t('views.neData.udmVolteIMS.startMSISDN') : 'MSISDN' " @@ -1089,13 +1193,23 @@ onMounted(() => { - + + + diff --git a/src/views/traceManage/pcap/file.vue b/src/views/traceManage/pcap/file.vue index c52bfbb3..3d7b8d7c 100644 --- a/src/views/traceManage/pcap/file.vue +++ b/src/views/traceManage/pcap/file.vue @@ -5,6 +5,7 @@ import { SizeType } from 'ant-design-vue/es/config-provider'; import { ColumnsType } from 'ant-design-vue/es/table'; import { Modal, message } from 'ant-design-vue/es'; import { parseDateToStr } from '@/utils/date-utils'; +import { parseSizeFromFile } from '@/utils/parse-utils'; import { getNeDirZip, getNeFile, listNeFiles } from '@/api/tool/neFile'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import ViewDrawer from '@/views/ne/neFile/components/ViewDrawer.vue'; @@ -69,6 +70,9 @@ let tableColumns: ColumnsType = reactive([ title: t('views.logManage.neFile.size'), dataIndex: 'size', align: 'left', + customRender(opt) { + return parseSizeFromFile(opt.value); + }, width: 100, }, { @@ -243,7 +247,7 @@ function fnDirCD(dir: string, index?: number) { queryParams.search = `${neType}_${queryParams.neId}`; } else { nePathArr.value = [ - `/tmp/omc/tcpdump/${neType.toLowerCase()}/${queryParams.neId}`, + `/usr/local/omc/tcpdump/${neType.toLowerCase()}/${queryParams.neId}`, ]; queryParams.search = ''; } @@ -269,7 +273,7 @@ function fnNeChange(keys: any, _: any) { nePathArr.value = ['/tmp']; queryParams.search = `${neType}_${neId}`; } else { - nePathArr.value = [`/tmp/omc/tcpdump/${neType.toLowerCase()}/${neId}`]; + nePathArr.value = [`/usr/local/omc/tcpdump/${neType.toLowerCase()}/${neId}`]; queryParams.search = ''; } fnGetList(1); diff --git a/src/views/traceManage/pcap/index.vue b/src/views/traceManage/pcap/index.vue index e65fb318..401cec43 100644 --- a/src/views/traceManage/pcap/index.vue +++ b/src/views/traceManage/pcap/index.vue @@ -438,7 +438,7 @@ function fnDownPCAP(row?: Record) { ); } else { const { neType, neId } = from.data; - const path = `/tmp/omc/tcpdump/${neType.toLowerCase()}/${neId}/${taskCode}`; + const path = `/usr/local/omc/tcpdump/${neType.toLowerCase()}/${neId}/${taskCode}`; reqArr.push( getNeDirZip({ neType, @@ -518,7 +518,7 @@ function fnModalVisibleByVive(id: string | number) { const from = modalState.from[id]; if (!from) return; const { neType, neId } = from.data; - const path = `/tmp/omc/tcpdump/${neType.toLowerCase()}/${neId}/${ + const path = `/usr/local/omc/tcpdump/${neType.toLowerCase()}/${neId}/${ from.taskCode }`; const files = from.taskFiles.filter(f => f.endsWith('log'));