Compare commits

...
49 Commits
Author SHA1 Message Date
TsMask 90395e7464 ref: 重构获取网元状态信息 2025-10-27 15:16:30 +08:00
TsMask 0b15ff1476 fix: AMF配置WhiteList Content导入index0无效问题 2025-10-24 20:46:20 +08:00
TsMask 8ced920aec chore: 更新版本号 2.2510.4 2025-10-24 20:11:19 +08:00
zhongzm 4ae74103f1 Merge remote-tracking branch 'origin/main-v2' into main-v2 2025-10-23 18:06:45 +08:00
zhongzm 1c4c1a796d feat:同步语音仪表盘:修改useNeInfoStore导入地址、补充接口导入、修改ims请求参数、修改响应码 2025-10-23 18:06:34 +08:00
TsMask a658852772 feat: 基站状态补充randId列 2025-10-23 14:15:46 +08:00
TsMask 0d13400229 chore: 更新版本号 2.2510.3 2025-10-18 18:09:04 +08:00
TsMask 7491ccc00b feat: 网元配置备份修改表单校验规则,添加备注字段必填项 2025-10-18 14:20:46 +08:00
TsMask fc64460870 feat: 网元配置选择传入neId查询配置数据 2025-10-17 10:20:40 +08:00
TsMask 323da55c9d feat: 更新根网管节点处理逻辑,支持无OMC情况 2025-10-15 11:42:27 +08:00
TsMask 26cf7fa560 style: 移除ID列显示 2025-10-14 11:08:58 +08:00
TsMask 5ea56e779a feat: 备份网元日志文件数据查看 2025-10-14 11:08:39 +08:00
TsMask 174a7afc47 chore: 更新版本号 2.2510.2 2025-10-11 19:06:04 +08:00
TsMask f4e61a0ebc fix: MML回车undefined问题,关闭搜索匹配 2025-10-11 17:28:05 +08:00
TsMask 97e5533d6e style: 移除ID列显示 2025-10-09 19:54:49 +08:00
TsMask e1c4229e58 style: 移除网元版本和授权ID列显示 2025-10-09 11:22:19 +08:00
TsMask c6e21c6c38 fix: SMSC-CDR添加结果原因说明 2025-09-28 20:13:13 +08:00
TsMask b1d0771d56 feat: UDM-auth数据导出按钮权限定义 2025-09-28 19:31:42 +08:00
TsMask 22b0b7e204 chore: 更新版本号 2.2509.4 2025-09-26 20:22:30 +08:00
TsMask 93f31eded4 Merge branch 'main-v2' of http://192.168.2.166:3180/OMC/ems_frontend_vue3 into main-v2 2025-09-25 15:30:32 +08:00
TsMask 1eec47f410 style: UDM数据显示创建时间列 2025-09-25 15:30:28 +08:00
zhongzm d9bc5b9f02 feat:中英文补充 2025-09-22 18:13:20 +08:00
zhongzm 3116e51338 feat:取消成功网元排列,仅保留显示失败网元 2025-09-22 18:09:27 +08:00
TsMask 3026ac1dd8 rev:流量格式化MB、GB 2025-09-19 14:19:07 +08:00
TsMask c01380da6b feat: 添加系统备份功能,包括导入导出OMC的API和界面支持 2025-09-19 12:04:49 +08:00
zhongzm 98e4d39cdf feat:流量格式化MB、GB 2025-09-19 09:18:32 +08:00
TsMask 1c025c2cc1 feat: 日志备份文件查看kpi文件记录 2025-09-18 11:02:20 +08:00
TsMask 20a12eba56 fix: 根据搜索条件导出,去除最大记录限制 2025-09-18 11:01:36 +08:00
TsMask 904488f59a feat: 添加活动告警自动刷新功能并优化代码结构 2025-09-15 19:32:11 +08:00
TsMask a2f93f7862 feat: 修复奇安信浏览器时区格式错误 2025-09-15 16:40:40 +08:00
TsMask d249d14c49 feat: 添加网元配置快速PLMN修改框 2025-09-15 16:11:08 +08:00
TsMask 58d0e6e884 feat: 移除掉一些权限标识 2025-09-15 14:23:17 +08:00
zhongzm 510e3e1006 feat:ue和ne模块权限按钮 2025-09-12 18:52:50 +08:00
zhongzm 02b12d76a7 Merge remote-tracking branch 'origin/main-v2' into main-v2 2025-09-12 17:09:32 +08:00
zhongzm 5839aa3cb1 feat:监控模块按钮权限 2025-09-12 17:09:17 +08:00
TsMask 83a289492d fix: UDM数据导出提示信息,导出总数 2025-09-12 17:08:07 +08:00
zhongzm 05d59ec526 feat:进度条显示调整 2025-09-11 11:42:29 +08:00
zhongzm c22d7604cf feat:fnModalOk参数传递修复 2025-09-11 11:04:05 +08:00
zhongzm 6f9639d3d4 feat:控制台警告修复 2025-09-11 10:38:13 +08:00
zhongzm 5932662e7c feat:网元许可快速上传功能 2025-09-10 18:08:53 +08:00
zhongzm cdbe9a1e42 feat:网元信息快速OAM功能 2025-09-10 16:27:34 +08:00
TsMask a032236d7d chore: 更新版本号 2.2508.4 2025-08-29 19:19:51 +08:00
TsMask 1dcd8a6e25 feat: 网元授权显示用户数/基站数 2025-08-29 18:49:58 +08:00
TsMask e075779b91 style: 网元当前版本显示是否有包存在 2025-08-29 18:49:49 +08:00
TsMask e5ce2ebb02 fix: cdr/ue导出操作后锁定按钮事件防重发 2025-08-28 16:09:45 +08:00
TsMask 3da786f6b9 feat: 网元可选静态可选项添加PGWC 2025-08-28 10:55:31 +08:00
TsMask d4e603be63 fix: 参数配置添加校验过滤长度~和-定义范围 2025-08-28 10:42:07 +08:00
TsMask a7024df325 fix: UPF流量统计数据同时获取刷新 2025-08-28 09:52:25 +08:00
TsMask b525b92163 fix: UPF流量统计字节数据格式化显示不一致问题 2025-08-28 09:51:24 +08:00
78 changed files with 4435 additions and 1853 deletions

No files matched your search

+47
View File
@@ -1,5 +1,52 @@
# 版本发布日志
## 2.2510.4-20251024
- 新增 同步语音仪表盘:修改useNeInfoStore导入地址、补充接口导入、修改ims请求参数、修改响应码
- 新增 基站状态补充randId列
## 2.2510.3-20251018
- 新增 备份网元日志文件数据查看
- 修复 更新根网管节点处理逻辑,支持无OMC情况
- 新增 网元配置选择传入neId查询相应版本配置数据
- 修复 网元配置备份修改表单校验规则,添加备注字段必填项
## 2.2510.2-20251011
- 新增 UDM-auth数据导出按钮权限定义
- 新增 SMSC-CDR添加结果原因说明
- 优化 移除CDR/UE/网元版本/授权列表显示ID列
- 修复 MML回车undefined问题,关闭搜索匹配
## 2.2509.4-20250926
- 优化 UDM数据显示创建时间列
- 优化 中英文补充
- 新增 添加系统备份功能,包括导入导出OMC的API和界面支持
- 新增 日志备份文件查看kpi文件记录
- 优化 根据搜索条件导出,去除最大记录限制
- 新增 添加活动告警自动刷新功能并优化代码结构
- 修复 奇安信浏览器时区格式错误
- 新增 添加网元配置快速PLMN修改框
- 新增 ue和ne模块权限按钮
- 新增 监控模块按钮权限
- 修复 UDM数据导出提示信息,导出总数
- 新增 网元许可快速上传功能
- 新增 网元信息快速OAM功能
## 2.2508.4-20250829
- 新增 网元授权显示用户数/基站数
- 优化 网元当前版本显示是否有包存在
- 优化 cdr/ue导出操作后锁定按钮事件防重发
- 优化 网元可选静态可选项添加PGWC
- 优化 参数配置添加校验过滤长度~和-定义范围
- 修复 UPF流量统计数据同时获取刷新
- 修复 UPF流量统计字节数据格式化显示不一致问题
- 新增 指标Title管理页面功能
- 新增 增加多个导出操作的超时时间至180秒
## 2.2508.2-20250815
- 优化 给config.js加随机数避免缓存
+2 -1
View File
@@ -6,10 +6,11 @@ import { request } from '@/plugins/http-fetch';
* @param query 查询参数
* @returns object
*/
export function getAllNeConfig(neType: string) {
export function getAllNeConfig(neType: string, neId: string) {
return request({
url: `/ne/config/list/${neType}`,
method: 'GET',
params: { neId },
timeout: 60_000,
});
}
+30
View File
@@ -36,3 +36,33 @@ export function updateBackupFTP(data: Record<string, any>) {
data,
});
}
/**
* 备份文件-导出OMC
* @returns object
*/
export function exportBackupOMC() {
return request({
url: '/neData/backup/export-omc',
method: 'POST',
responseType: 'blob',
timeout: 180_000,
});
}
/**
* 备份文件-导入OMC
* @param filePath 备份文件上传返回的/upload 路径
* @returns object
*/
export function importBackupOMC(filePath: string) {
return request({
url: '/neData/backup/import-omc',
method: 'POST',
data: {
neType: 'OMC',
path: filePath,
},
timeout: 180_000,
});
}
+14
View File
@@ -140,3 +140,17 @@ export function exportUDMAuth(data: Record<string, any>) {
timeout: 180_000,
});
}
/**
* UDM鉴权用户导出DecAuth
* @param neId 网元ID
* @returns bolb
*/
export function exportUDMDecAuth(neId: string) {
return request({
url: `/neData/udm/auth/export-dec?neId=${neId}`,
method: 'GET',
responseType: 'blob',
timeout: 180_000,
});
}
+25
View File
@@ -33,3 +33,28 @@ export async function getKPITitle(neType: string) {
// 解析数据//
return result;
}
//忙时呼叫
export async function getbusyhour(query: Record<string, any>) {
return request({
url: '/neData/ims/kpi/busy-hour',
method: 'GET',
params: query,
});
}
//MOS指标
export async function getMosHour(query: Record<string, any>) {
return request({
url: '/neData/ims/cdr/mos-hour',
method: 'GET',
params: query,
});
}
//CCT指标
export async function getCctHour(query: Record<string, any>) {
return request({
url: '/neData/ims/cdr/cct-hour',
method: 'GET',
params: query,
});
}
+6
View File
@@ -22,6 +22,12 @@ export const NE_TYPE_LIST = [
'CHF',
'HLR',
'SGWC',
'PGWC',
'IP-SM-GW',
'MMTel-AS',
'I-CSCF',
'P-CSCF',
'S-CSCF',
];
/**
+46 -10
View File
@@ -485,7 +485,7 @@ export default {
resultOk: "Success",
resultFail: "Fail",
delTip: "Confirm deletion of the data item numbered [{msg}]?",
exportTip: "Do you confirm to export the current query conditions of the CDR data? (Maximum 10,000 items can be exported.)",
exportTip: "Do you confirm to export the current query conditions of the CDR data?",
chargingID: 'Charging ID',
smfSubscriptionIDData: 'Subscription ID Data',
smfSubscriptionIDType: 'Subscription ID Type',
@@ -517,7 +517,7 @@ export default {
result: "Result",
resultOk: "Successes",
delTip: "Confirm deletion of the data item numbered [{msg}]?",
exportTip: "Do you confirm to export the event data of the current query condition? (Maximum 10,000 items can be exported.)",
exportTip: "Do you confirm to export the event data of the current query condition?",
},
},
ne: {
@@ -539,6 +539,8 @@ export default {
port: 'Port',
portTip: "Network element port default:33030",
capability: 'Capability',
ueNumber: 'UE Number',
nbNumber: 'Radio Number',
serialNum: 'Serial Number',
expiryDate: 'Expiry Date',
normalcy: 'Normal',
@@ -588,6 +590,22 @@ export default {
kpiTimerPlease: 'Please enter the reporting period (in seconds)',
omcIP: 'OMC IP',
},
quickOam: {
title: 'Quick OAM Configuration',
selectNe: 'Select NE',
omcIP: 'OMC IP',
oamPort: 'OAM Port',
progress:'Progress',
processing:'Progressing',
result:'Result',
success:'Success',
default:'Default',
allsuccess:'All NE have been configured successfully!',
},
quickUpload: {
title: 'Quick License Upload',
selectNe: 'Select NE',
},
backConf: {
export: 'Config Export',
import: 'Config Import',
@@ -699,6 +717,10 @@ export default {
uploadFile: "Upload License",
uploadChangeOk: 'Network Element renewed license successfully and is being calibrated in the background!',
uploadChangeFail: "Some network elements failed to update the license, please check whether the service terminal environment is available!",
quickUpload: {
title: 'Quick License Upload',
selectNe: 'Select NE',
},
},
neConfig: {
treeTitle: "Navigation Configuration",
@@ -824,7 +846,7 @@ export default {
passwordPlease: "Please enter your password correctly",
addTitle: 'Add VOIP subscriber',
delTip: 'Confirm that you want to delete the information of VOIP user as [{num}]?',
exportTip: "Confirm exporting xlsx table files based on search criteria?",
exportTip: "Confirm exporting all VOIP user data based on the current search criteria?",
},
udmVolteIMS: {
startMSISDN: 'Starting MSISDN',
@@ -833,7 +855,7 @@ export default {
vniTip: 'Example: ims.mnc000.mcc000.3gppnetwork.org',
vniPlease: 'Please enter VNI correctly',
delTip: 'Are you sure you want to delete the information of IMS signing as [{num}]?',
exportTip: "Confirm exporting xlsx table files based on search criteria?",
exportTip: "Confirm exporting all VoLTE user data based on the current search criteria?",
},
pcfSub: {
exportTip: 'Confirm exporting all user policy control information data?',
@@ -853,6 +875,7 @@ export default {
list: "List",
topology: "Topology",
nbName: "RAN Node Name",
nbId: "RAN Node ID",
ueNum: "UE Number",
topologyTitle: "Radio State Graph",
name: "Name",
@@ -884,7 +907,7 @@ export default {
neTypePlease: 'Query network element Object',
neType: 'UDM Object',
export: 'Export',
exportConfirm: 'Are you sure to export all authentication user data?',
exportConfirm: 'Confirm exporting all Authentication user data based on the current search criteria?',
checkExport : 'Check Export',
checkExportConfirm: 'Confirm exporting the checked authenticated user data?',
import: 'Import',
@@ -913,7 +936,7 @@ export default {
subInfo:' Subscription Info',
neType: 'UDM Object',
export: 'Export',
exportConfirm: 'Are you sure to export all signed user data?',
exportConfirm: 'Confirm exporting all Subscribers user data based on the current search criteria?',
checkExport : 'Check Export',
checkExportConfirm: 'Are you sure to export the data of the checked subscribers?',
import: 'Import',
@@ -1339,10 +1362,19 @@ export default {
deleteTipErr: "Failed to delete file",
sysloginLog:'System Login Log',
sysOperateLog:'System Operation Log',
cdrIMS:'CDR Voice',
cdrSMF:'CDR Data',
cdrSMSC:'CDR SMS',
cdrSGWC:'CDR Roaming Data',
neLog:'NE Log',
cdrIMS:'CDR IMS Voice',
cdrSMF:'CDR SMF Data',
cdrSMSC:'CDR SMSC SMS',
cdrSGWC:'CDR SGWC Roaming Data',
kpiIMS:'KPI IMS',
kpiAMF:'KPI AMF',
kpiUDM:'KPI UDM',
kpiSMF:'KPI SMF',
kpiPCF:'KPI PCF',
kpiUPF:'KPI UPF',
kpiMME:'KPI MME',
kpiSMSC:'KPI SMSC',
}
},
monitor: {
@@ -1887,6 +1919,10 @@ export default {
home: 'Home Page',
homeTip:'Do you want to submit the current interface as the system interface?',
homeSet:'Home Page Settings',
backup: 'System Backup',
backupInstruction: 'System backup will back up the net element information records and configuration files running on the current system, and can restore the system to the previous state!',
backupExportTip: 'Confirm to export system backup?',
backupImportTip: 'Confirm to import system backup?',
},
role:{
allScopeOptions:'All data permissions',
+46 -10
View File
@@ -485,7 +485,7 @@ export default {
resultOk: "成功",
resultFail: "失败",
delTip: "确认删除编号为【{msg}】的数据项?",
exportTip: "确认导出当前查询条件的话单数据吗?(导出最大支持一万条)",
exportTip: "确认导出当前查询条件的话单数据吗?",
chargingID: '计费ID',
smfSubscriptionIDData: '订阅 ID 数据',
smfSubscriptionIDType: '订阅 ID 类型',
@@ -517,7 +517,7 @@ export default {
result: "结果",
resultOk: "成功",
delTip: "确认删除编号为【{msg}】的数据项?",
exportTip: "确认导出当前查询条件的事件数据吗?(导出最大支持一万条)",
exportTip: "确认导出当前查询条件的事件数据吗?",
},
},
ne: {
@@ -539,6 +539,8 @@ export default {
port: '服务端口',
portTip: "网元服务端口,默认:33030",
capability: '容量',
ueNumber: '用户数',
nbNumber: '基站数',
serialNum: '序列号',
expiryDate: '许可证到期日期',
normalcy: '正常',
@@ -588,6 +590,22 @@ export default {
kpiTimerPlease: '请输入上报周期(单位秒)',
omcIP: 'OMC IP',
},
quickOam: {
title: '快速OAM配置',
selectNe: '选择网元',
omcIP: 'OMC IP地址',
oamPort: 'OAM端口',
progress:'配置进度',
processing:'正在处理',
result:'操作结果',
success:'成功',
default:'失败',
allsuccess:'所有网元配置成功!',
},
quickUpload: {
title: '快速许可证上传',
selectNe: '选择网元',
},
backConf: {
export: '配置导出',
import: '配置导入',
@@ -699,6 +717,10 @@ export default {
uploadFile: "上传许可证",
uploadChangeOk: '网元更新许可证成功,正在后台校验!',
uploadChangeFail: "部分网元更新许可证失败,请检查服务终端环境是否可用!",
quickUpload: {
title: '快速许可证上传',
selectNe: '选择网元',
},
},
neConfig: {
treeTitle: "配置导航",
@@ -824,7 +846,7 @@ export default {
passwordPlease: "请正确输入密码",
addTitle: '新增VOIP用户',
delTip: '确认要删除VOIP用户为【{num}】的信息吗?',
exportTip: "确认根据搜索条件导出xlsx表格文件吗?",
exportTip: "确认根据当前搜索条件导出全部VoIP用户数据吗?",
},
udmVolteIMS: {
startMSISDN: '起始MSISDN',
@@ -833,7 +855,7 @@ export default {
vniTip: '示例:ims.mnc000.mcc000.3gppnetwork.org',
vniPlease: '请正确输入VNI',
delTip: '确认要删除IMS签约为【{num}】的信息吗?',
exportTip: "确认根据搜索条件导出xlsx表格文件吗?",
exportTip: "确认根据当前搜索条件导出全部VoLTE用户数据吗?",
},
pcfSub: {
exportTip: '确认导出全部用户策略控制信息数据吗?',
@@ -853,6 +875,7 @@ export default {
list: "列表",
topology: "拓扑图",
nbName: "设备名称",
nbId: "设备ID",
ueNum: "在线用户数",
topologyTitle: "基站状态关系图",
name: "基站名称",
@@ -884,7 +907,7 @@ export default {
neTypePlease: '查询网元类型',
neType: 'UDM网元对象',
export: '导出',
exportConfirm: '确认导出全部鉴权用户数据吗?',
exportConfirm: '确认根据当前搜索条件导出全部鉴权用户数据吗?',
checkExport : '勾选导出',
checkExportConfirm: '确认导出已勾选的鉴权用户数据吗?',
import: '导入',
@@ -915,7 +938,7 @@ export default {
export: '导出',
exportConfirm: '确认导出全部签约用户数据吗?',
checkExport : '勾选导出',
checkExportConfirm: '确认导出已勾选的签约用户数据吗?',
checkExportConfirm: '确认根据当前搜索条件导出全部签约用户数据吗?',
import: '导入',
importFail: '失败记录',
loadDataConfirm: '确认要重新加载数据吗?',
@@ -1339,10 +1362,19 @@ export default {
deleteTipErr: "文件删除失败",
sysloginLog:'系统登录日志',
sysOperateLog:'系统操作日志',
cdrIMS:'语音话单',
cdrSMF:'数据话单',
cdrSMSC:'短信话单',
cdrSGWC:'漫游数据话单',
neLog:'网元日志',
cdrIMS:'话单 IMS 语音',
cdrSMF:'话单 SMF 数据',
cdrSMSC:'话单 SMSC 短信',
cdrSGWC:'话单 SGWC 漫游数据',
kpiIMS:'KPI IMS',
kpiAMF:'KPI AMF',
kpiUDM:'KPI UDM',
kpiSMF:'KPI SMF',
kpiPCF:'KPI PCF',
kpiUPF:'KPI UPF',
kpiMME:'KPI MME',
kpiSMSC:'KPI SMSC',
}
},
monitor: {
@@ -1887,6 +1919,10 @@ export default {
home: '系统首页',
homeTip:'确认要提交当前界面为系统界面吗?',
homeSet:'系统首页设置',
backup: '系统备份',
backupInstruction: '系统备份将会对当前系统上运行的网元信息记录及配置文件进行备份,可进行系统的恢复操作!',
backupExportTip: '确认要导出系统备份吗?',
backupImportTip: '确认要导入系统备份吗?',
},
role:{
allScopeOptions:'全部数据权限',
+6
View File
@@ -217,6 +217,7 @@ onMounted(() => {
let serverTimeInterval: any = null;
let serverTime = 0;
let serverTimeZone = getTimezoneOffset();
let activeAlarmRefresh = 0;
// 获取服务器时间
function fnGetServerTime() {
@@ -230,6 +231,7 @@ function fnGetServerTime() {
const serverTimeDom = document.getElementById('serverTimeDom');
serverTimeInterval = setInterval(() => {
serverTime += 1000;
activeAlarmRefresh += 1;
if (serverTimeDom) {
serverTimeDom.innerText = parseDateUTCToStr(
serverTime,
@@ -239,6 +241,10 @@ function fnGetServerTime() {
clearInterval(serverTimeInterval);
serverTimeInterval = null;
}
if (activeAlarmRefresh === 5) {
useAlarmStore().fnGetActiveAlarmInfo();
activeAlarmRefresh = 0;
}
}, 1000);
}
});
+31 -1
View File
@@ -17,6 +17,35 @@ export const RFC3339 = 'YYYY-MM-DDTHH:mm:ssZ';
/**国际时间 列如:Thu, Nov 14 2024 10:19 GMT+08:00 */
export const RFC822Z = 'ddd, MMM DD YYYY HH:mm [GMT]Z';
// 映射时区偏移量与IANA时区名称关联
export const offsetToIanaMap: Record<string, string> = {
'+0000': 'UTC',
'+0100': 'Europe/London',
'+0200': 'Europe/Paris',
'+0300': 'Europe/Moscow',
'+0400': 'Asia/Dubai',
'+0500': 'Asia/Karachi',
'+0600': 'Asia/Almaty',
'+0700': 'Asia/Bangkok',
'+0800': 'Asia/Shanghai',
'+0900': 'Asia/Tokyo',
'+1000': 'Australia/Sydney',
'+1100': 'Pacific/Noumea',
'+1200': 'Pacific/Fiji',
'-0100': 'America/Noronha',
'-0200': 'Atlantic/Azores',
'-0300': 'America/Argentina/Buenos_Aires',
'-0400': 'America/New_York',
'-0500': 'America/New_York',
'-0600': 'America/Chicago',
'-0700': 'America/Denver',
'-0800': 'America/Los_Angeles',
'-0900': 'Pacific/Honolulu',
'-1000': 'Pacific/Honolulu',
'-1100': 'Pacific/Pago_Pago',
'-1200': 'Pacific/Kwajalein',
};
/**
* 格式时间字符串
* @param dateStr 时间字符串
@@ -53,9 +82,10 @@ export function parseDateUTCToStr(
date: string | number | Date,
offset: string = '+0000'
): string {
const ianaTimezone = offsetToIanaMap[offset] || 'UTC';
return dayjs
.utc(date) // 将时间戳按 UTC 时间
.tz(offset) // 使用时区偏移
.tz(ianaTimezone) // 使用时区偏移
.format(`YYYY-MM-DD HH:mm:ss [UTC${offset}]`); // 格式化时间
}
-19
View File
@@ -177,25 +177,6 @@ export function parseSizeFromKbs(sizeByte: number, timeInterval: number): any {
return [(realBit / 1000 / 1000).toFixed(2), ' Mbits/sec'];
}
/**
* 位数据转换单位
* @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 {
bits = Number(bits) || 0;
if (bits <= 0) return '0 B';
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);
const unti = units[unitIndex];
if (unitIndex > 0) {
return `${value.toFixed(2)} ${unti}`;
}
return `${value} ${unti}`;
}
/**
* 字节数转换单位
* @param byte 字节Byte大小 64009540 = 512.08 MB
+6 -6
View File
@@ -88,12 +88,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 100,
// },
{
title: t('views.cbc.cbe.eventName'),
dataIndex: 'messageJson',
+14 -37
View File
@@ -63,7 +63,7 @@ let tableColumns: ColumnsType = [
align: 'left',
customRender(opt) {
if (opt.value?.refreshTime) {
return parseDateToStr(opt.value?.refreshTime, 'HH:mm:ss');
return parseDateToStr(opt.value?.refreshTime);
}
return '-';
},
@@ -94,11 +94,8 @@ let tableColumns: ColumnsType = [
},
{
title: t('views.index.ipAddress'),
dataIndex: 'serverState',
dataIndex: 'ip',
align: 'left',
customRender(opt) {
return opt.value?.neIP || '-';
},
},
];
@@ -151,10 +148,10 @@ async function fnGetList(reload: boolean = false) {
if (!reload) {
// 选择第一个
if (tableState.data.length > 0) {
const item = tableState.data.find((item: any) => item.status === 1)
const item = tableState.data.find((item: any) => item.status === 1)
if (item) {
const id = item.id;
fnTableSelectedRowKeys([id]);
fnTableSelectedRowKeys([id]);
}
} else {
fnTableSelectedRowKeys(tableState.selectedRowKeys);
@@ -250,8 +247,9 @@ function fnTableSelectedRowKeys(keys: (string | number)[]) {
serverState.value = Object.assign(
{
// cpuUse: `NE:${nfCpuP}%; SYS:${sysCpuP}%`,
// memoryUse: `Total: ${totalMemInMB}MB; NE: ${nfUsedMemInMB}MB; SYS: ${sysMemUsageInMB}MB`,
// cpuUse: `NE:${nfCpuP}%; SYS:${sysCpuP}%`,
// memoryUse: `Total: ${totalMemInMB}MB; NE: ${nfUsedMemInMB}MB; SYS: ${sysMemUsageInMB}MB`,
neIP: row.ip,
},
neState
);
@@ -315,22 +313,13 @@ onBeforeUnmount(() => {
<a-row :gutter="16">
<a-col :lg="14" :md="16" :xs="24">
<!-- 表格列表 -->
<a-table
class="table"
row-key="id"
size="small"
:columns="tableColumns"
:data-source="tableState.data"
:loading="tableState.loading"
:pagination="false"
:scroll="{ x: true }"
:row-selection="{
<a-table class="table" row-key="id" size="small" :columns="tableColumns" :data-source="tableState.data"
:loading="tableState.loading" :pagination="false" :scroll="{ x: true }" :row-selection="{
type: 'radio',
columnWidth: '48px',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
>
}">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'status'">
<DictTag :options="dict.neInfoStatus" :value="record.status" />
@@ -339,24 +328,12 @@ onBeforeUnmount(() => {
</a-table>
</a-col>
<a-col :lg="10" :md="8" :xs="24">
<a-card
:title="t('views.index.runStatus')"
style="margin-bottom: 16px"
size="small"
>
<a-card :title="t('views.index.runStatus')" style="margin-bottom: 16px" size="small">
<div style="width: 100%; min-height: 200px" ref="statusBar"></div>
</a-card>
<a-card
:loading="tableState.loading"
:title="`${t('views.index.mark')} - ${serverState.neName || 'OMC'}`"
style="margin-top: 16px"
size="small"
>
<a-descriptions
bordered
:column="1"
:label-style="{ width: '160px' }"
>
<a-card :loading="tableState.loading" :title="`${t('views.index.mark')} - ${serverState.neName || 'OMC'}`"
style="margin-top: 16px" size="small">
<a-descriptions bordered :column="1" :label-style="{ width: '160px' }">
<a-descriptions-item :label="t('views.index.hostName')">
{{ serverState.hostname }}
</a-descriptions-item>
+9 -7
View File
@@ -134,12 +134,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 100,
// },
{
title: 'IMSI',
dataIndex: 'eventJSON',
@@ -339,9 +339,11 @@ function fnExportList() {
title: t('common.tipTitle'),
content: t('views.dashboard.ue.exportTip'),
onOk() {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
const querys = toRaw(queryParams);
querys.pageSize = 10000;
querys.pageNum = 1;
querys.pageSize = tablePagination.total;
exportAMFDataUE(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
+9 -7
View File
@@ -141,12 +141,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 100,
// },
{
title: t('views.dashboard.cdr.recordType'),
dataIndex: 'cdrJSON',
@@ -408,9 +408,11 @@ function fnExportList() {
title: t('common.tipTitle'),
content: t('views.dashboard.cdr.exportTip'),
onOk() {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
const querys = toRaw(queryParams);
querys.pageSize = 10000;
querys.pageNum = 1;
querys.pageSize = tablePagination.total;
exportIMSDataCDR(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
+9 -7
View File
@@ -135,12 +135,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 100,
// },
{
title: 'IMSI',
dataIndex: 'eventJSON',
@@ -341,9 +341,11 @@ function fnExportList() {
title: t('common.tipTitle'),
content: t('views.dashboard.ue.exportTip'),
onOk() {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
const querys = toRaw(queryParams);
querys.pageSize = 10000;
querys.pageNum = 1;
querys.pageSize = tablePagination.total;
exportMMEDataUE(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
@@ -247,42 +247,7 @@ function fnChangeData(data: any[], itemID: string) {
// console.log(info.neState.cpu.sysCpuUsage);
// console.log(info.neState.mem);
// console.log(info.neState.disk);
let sysCpuUsage = 0;
let nfCpuUsage = 0;
if (info.neState.cpu) {
nfCpuUsage = info.neState.cpu.nfCpuUsage;
const nfCpu = +(info.neState.cpu.nfCpuUsage / 100);
nfCpuUsage = +nfCpu.toFixed(2);
if (nfCpuUsage > 100) {
nfCpuUsage = 100;
}
sysCpuUsage = info.neState.cpu.sysCpuUsage;
let sysCpu = +(info.neState.cpu.sysCpuUsage / 100);
sysCpuUsage = +sysCpu.toFixed(2);
if (sysCpuUsage > 100) {
sysCpuUsage = 100;
}
}
let sysMemUsage = 0;
if (info.neState.mem) {
const men = info.neState.mem.sysMemUsage;
sysMemUsage = +(men / 100).toFixed(2);
if (sysMemUsage > 100) {
sysMemUsage = 100;
}
}
let sysDiskUsage = 0;
if (info.neState.disk && Array.isArray(info.neState.disk.partitionInfo)) {
let disks: any[] = info.neState.disk.partitionInfo;
disks = disks.sort((a, b) => +b.used - +a.used);
if (disks.length > 0) {
const { total, used } = disks[0];
sysDiskUsage = +((used / total) * 100).toFixed(2);
}
}
const { nfCpuUsage, sysCpuUsage, sysMemUsage, sysDiskUsage } = info.neState
category.value[0].value = sysDiskUsage;
category.value[1].value = sysMemUsage;
@@ -74,8 +74,7 @@ export function neStateParse(neType: string, data: Record<string, any>) {
// 更新网元状态
const newNeState = Object.assign(node.neState, data, {
refreshTime: parseDateToStr(data.refreshTime, 'HH:mm:ss'),
online: !!data.cpu,
refreshTime: parseDateToStr(data.refreshTime, 'MM-DD HH:mm:ss'),
});
// 通过 ID 查询节点实例
@@ -1,5 +1,5 @@
import { parseDateToStr } from '@/utils/date-utils';
import { parseSizeFromBits, parseSizeFromKbs } from '@/utils/parse-utils';
import { parseSizeFromByte, parseSizeFromKbs } from '@/utils/parse-utils';
import { ref } from 'vue';
type FDType = {
@@ -81,9 +81,9 @@ export function upfTFParse(day: string, data: Record<string, number>) {
let { up, down } = data;
upfTotalFlow.value[day] = {
up: up,
upFrom: parseSizeFromBits(up),
upFrom: parseSizeFromByte(up),
down: down,
downFrom: parseSizeFromBits(down),
downFrom: parseSizeFromByte(down),
requestFlag: false,
};
}
+3 -3
View File
@@ -229,15 +229,15 @@ function loadData() {
interval10s.value = setInterval(() => {
if (!interval10s.value || !initFlag) return;
if (upfTFActive.value === '0') {
upfTFSend('7');
upfTFActive.value = '7';
} else if (upfTFActive.value === '7') {
upfTFSend('30');
upfTFActive.value = '30';
} else if (upfTFActive.value === '30') {
upfTFSend('0');
upfTFActive.value = '0';
}
upfTFSend('0');
upfTFSend('7');
upfTFSend('30');
}, 10_000);
clearInterval(interval5s.value);
@@ -71,9 +71,8 @@ const optionData: any = {
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.neCpu')}\n${
resourceData.value.neCpu
}%`;
return `${t('views.dashboard.overview.resources.neCpu')}\n${resourceData.value.neCpu
}%`;
},
textStyle: {
fontSize: 12,
@@ -103,9 +102,8 @@ const optionData: any = {
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.sysCpu')}\n${
resourceData.value.sysCpu
}%`;
return `${t('views.dashboard.overview.resources.sysCpu')}\n${resourceData.value.sysCpu
}%`;
},
textStyle: {
fontSize: 12,
@@ -135,9 +133,8 @@ const optionData: any = {
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.sysMem')}\n${
resourceData.value.sysMem
}%`;
return `${t('views.dashboard.overview.resources.sysMem')}\n${resourceData.value.sysMem
}%`;
},
textStyle: {
fontSize: 12,
@@ -167,9 +164,8 @@ const optionData: any = {
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.sysDisk')}\n${
resourceData.value.sysDisk
}%`;
return `${t('views.dashboard.overview.resources.sysDisk')}\n${resourceData.value.sysDisk
}%`;
},
textStyle: {
fontSize: 12,
@@ -213,45 +209,7 @@ function fnChangeData(data: any[], itemID: string) {
let info = data.find((item: any) => item.id === neType);
if (!info || !info.neStateMap[neID]?.online) return;
let sysCpuUsage = 0;
let nfCpuUsage = 0;
if (info.neStateMap[neID].cpu) {
nfCpuUsage = info.neStateMap[neID].cpu.nfCpuUsage;
const nfCpu = +(info.neStateMap[neID].cpu.nfCpuUsage / 100);
nfCpuUsage = +nfCpu.toFixed(2);
if (nfCpuUsage > 100) {
nfCpuUsage = 100;
}
sysCpuUsage = info.neStateMap[neID].cpu.sysCpuUsage;
let sysCpu = +(info.neStateMap[neID].cpu.sysCpuUsage / 100);
sysCpuUsage = +sysCpu.toFixed(2);
if (sysCpuUsage > 100) {
sysCpuUsage = 100;
}
}
let sysMemUsage = 0;
if (info.neStateMap[neID].mem) {
const men = info.neStateMap[neID].mem.sysMemUsage;
sysMemUsage = +(men / 100).toFixed(2);
if (sysMemUsage > 100) {
sysMemUsage = 100;
}
}
let sysDiskUsage = 0;
if (
info.neStateMap[neID].disk &&
Array.isArray(info.neStateMap[neID].disk.partitionInfo)
) {
let disks: any[] = info.neStateMap[neID].disk.partitionInfo;
disks = disks.sort((a, b) => +b.used - +a.used);
if (disks.length > 0) {
const { total, used } = disks[0];
sysDiskUsage = +((used / total) * 100).toFixed(2);
}
}
const { nfCpuUsage, sysCpuUsage, sysMemUsage, sysDiskUsage } = info.neStateMap[neID]
resourceData.value = {
neCpu: nfCpuUsage,
@@ -269,9 +227,8 @@ function fnChangeData(data: any[], itemID: string) {
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.neCpu')}\n${
resourceData.value.neCpu
}%`;
return `${t('views.dashboard.overview.resources.neCpu')}\n${resourceData.value.neCpu
}%`;
},
},
},
@@ -282,9 +239,8 @@ function fnChangeData(data: any[], itemID: string) {
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.sysCpu')}\n${
resourceData.value.sysCpu
}%`;
return `${t('views.dashboard.overview.resources.sysCpu')}\n${resourceData.value.sysCpu
}%`;
},
},
},
@@ -295,9 +251,8 @@ function fnChangeData(data: any[], itemID: string) {
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.sysMem')}\n${
resourceData.value.sysMem
}%`;
return `${t('views.dashboard.overview.resources.sysMem')}\n${resourceData.value.sysMem
}%`;
},
},
},
@@ -308,9 +263,8 @@ function fnChangeData(data: any[], itemID: string) {
label: {
normal: {
formatter: () => {
return `${t('views.dashboard.overview.resources.sysDisk')}\n${
resourceData.value.sysDisk
}%`;
return `${t('views.dashboard.overview.resources.sysDisk')}\n${resourceData.value.sysDisk
}%`;
},
},
},
@@ -38,18 +38,16 @@ export const graphG6 = ref<any>(null);
export const graphNodeClickID = ref<string>('UPF_001');
/**图节点网元信息状态 */
export const graphNodeState = computed(() =>{
return graphState.data.nodes.map((item: any) => ({
export const graphNodeState = computed(() => {
return graphState.data.nodes.map((item: any) => ({
id: item.id,
label: item.label,
neInfo: item.neInfo,
neState: item.neState,
neInfoList:item.neInfoList,
neInfoList: item.neInfoList,
neStateMap: item.neStateMap,
}))
}
);
}));
});
/**图节点网元状态数量 */
export const graphNodeStateNum = computed(() => {
@@ -72,8 +70,12 @@ export const graphNodeStateNum = computed(() => {
export const neStateRequestMap = ref<Map<string, boolean>>(new Map());
/**neStateParse 网元状态 数据解析 */
export function neStateParse(neType: string, data: Record<string, any>,neId: string) {
// console.log('neStateParse',neType, data, neId);
export function neStateParse(
neType: string,
data: Record<string, any>,
neId: string
) {
// console.log('neStateParse',neType, data, neId);
const { combos, edges, nodes } = graphState.data;
@@ -82,25 +84,24 @@ export function neStateParse(neType: string, data: Record<string, any>,neId: str
if (!node) return;
// 初始化状态映射
if (!node.neStateMap) node.neStateMap = {};
// 初始化状态映射
if (!node.neStateMap) node.neStateMap = {};
// 更新网元状态
const newNeState :any = {
...data, // 先展开data对象
refreshTime: parseDateToStr(data.refreshTime, 'HH:mm:ss'),
online: !!data.cpu,
neId: neId
const newNeState: any = {
...data, // 先展开data对象
refreshTime: parseDateToStr(data.refreshTime, 'MM-DD HH:mm:ss'),
neId: neId,
};
// 如果是001,更新节点状态。neInfo为主要的网元信息
if (node.neInfo && node.neInfo.neId === neId) {
Object.assign(node.neState, newNeState);
}
// 如果是001,更新节点状态。neInfo为主要的网元信息
if (node.neInfo && node.neInfo.neId === neId) {
Object.assign(node.neState, newNeState);
}
//console.log(node.neState)
// 无论是否为主要网元,都更新状态映射
node.neStateMap[neId] = {...newNeState};
// 通过 ID 查询节点实例
//console.log(node.neState)
// 无论是否为主要网元,都更新状态映射
node.neStateMap[neId] = { ...newNeState };
// 通过 ID 查询节点实例
const item = graphG6.value.findById(node.id);
if (item) {
// 检查当前节点下所有网元的状态
@@ -109,32 +110,33 @@ export function neStateParse(neType: string, data: Record<string, any>,neId: str
let stateColor = '#52c41a'; // 默认绿色(所有网元都正常)
if (allStates.some((state: any) => !state.online)) {
// 如果有任何一个网元不正常
stateColor = allStates.every((state: any) => !state.online) ? '#f5222d' : '#faad14'; // 红色(全部不正常)或黄色(部分不正常)
stateColor = allStates.every((state: any) => !state.online)
? '#f5222d'
: '#faad14'; // 红色(全部不正常)或黄色(部分不正常)
}
// 图片类型不能填充
if (node.type && node.type.startsWith('image')) {
// 更新节点
if (node.label !== newNeState.neType) {
graphG6.value.updateItem(item, {
label: newNeState.neType,
});
}
// 设置状态
graphG6.value.setItemState(item, 'top-right-dot', stateColor);
} else {
// 更新节点
// 图片类型不能填充
if (node.type && node.type.startsWith('image')) {
// 更新节点
if (node.label !== newNeState.neType) {
graphG6.value.updateItem(item, {
label: newNeState.neType,
style: {
fill: stateColor, // 填充色
stroke: stateColor, // 填充色
},
});
// 设置状态
graphG6.value.setItemState(item, 'stroke', newNeState.online);
}
// 设置状态
graphG6.value.setItemState(item, 'top-right-dot', stateColor);
} else {
// 更新节点
graphG6.value.updateItem(item, {
label: newNeState.neType,
style: {
fill: stateColor, // 填充色
stroke: stateColor, // 填充色
},
});
// 设置状态
graphG6.value.setItemState(item, 'stroke', newNeState.online);
}
}
// 设置边状态
@@ -1,5 +1,5 @@
import { parseDateToStr } from '@/utils/date-utils';
import { parseSizeFromBits, parseSizeFromKbs } from '@/utils/parse-utils';
import { parseSizeFromByte, parseSizeFromKbs } from '@/utils/parse-utils';
import { ref } from 'vue';
type FDType = {
@@ -79,9 +79,9 @@ export function upfTFParse(day: string, data: Record<string, number>) {
let { up, down } = data;
upfTotalFlow.value[day] = {
up: up,
upFrom: parseSizeFromBits(up),
upFrom: parseSizeFromByte(up),
down: down,
downFrom: parseSizeFromBits(down),
downFrom: parseSizeFromByte(down),
requestFlag: false,
};
}
+20 -19
View File
@@ -176,15 +176,15 @@ async function fnGetSkim() {
{
request: (neId: string) => listAMFNblist({ neId }),
process: async (res: any, neId: any) => {
console.log(neId)
if (res.code === RESULT_CODE_SUCCESS&& Array.isArray(res.data)) {
console.log(neId);
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
skimState.gnbNum += res.data.length;
skimState.gnbUeNum += res.data.reduce(
(sum: number, item: any) => sum + item.ueNum,
0
);
const amfNbRes = await listAMFNbStatelist({ neId });
console.log(amfNbRes)
console.log(amfNbRes);
if (
amfNbRes.code === RESULT_CODE_SUCCESS &&
Array.isArray(amfNbRes.data)
@@ -224,13 +224,13 @@ async function fnGetSkim() {
);
const mmeNbRes = await listMMENbStatelist({ neId });
console.log(mmeNbRes)
console.log(mmeNbRes);
if (
mmeNbRes.code === RESULT_CODE_SUCCESS &&
Array.isArray(mmeNbRes.data)
) {
// skimState.eNbSumNum += mmeNbRes.data.length;
console.log(mmeNbRes)
console.log(mmeNbRes);
tempEnbSumNum += mmeNbRes.data.length;
}
}
@@ -238,21 +238,20 @@ async function fnGetSkim() {
},
],
]);
console.log(neCascaderOptions)
console.log(neCascaderOptions);
const requests = neCascaderOptions.value.flatMap(
(ne: any) =>
ne.children
?.map((child: any) => {
console.log(child.neId)
console.log(child.neId);
const handler = neHandlers.get(child.neType);
return handler
? {
promise: handler.request(child.neId),
process: handler.process,
neId: child.neId, // 这里加上neId
}
neId: child.neId, // 这里加上neId
}
: null;
})
.filter(Boolean) || []
@@ -311,15 +310,15 @@ function loadData() {
interval10s.value = setInterval(() => {
if (!interval10s.value || !initFlag) return;
if (upfTFActive.value === '0') {
upfTFSend('7');
upfTFActive.value = '7';
} else if (upfTFActive.value === '7') {
upfTFSend('30');
upfTFActive.value = '30';
} else if (upfTFActive.value === '30') {
upfTFSend('0');
upfTFActive.value = '0';
}
upfTFSend('0');
upfTFSend('7');
upfTFSend('30');
}, 10_000);
clearInterval(interval5s.value);
@@ -375,12 +374,14 @@ async function fnSelectUDM(e: any) {
pageNum: 1,
pageSize: 1,
});
console.log(res)
console.log(res);
// listUDMSub({ neId: udmNeId.value, pageNum: 1, pageSize: 1 }).then(res => {
if (res.code === RESULT_CODE_SUCCESS && typeof res.data.total === 'number') {
if (
res.code === RESULT_CODE_SUCCESS &&
typeof res.data.total === 'number'
) {
skimState.udmSubNum = res.data.total;
console.log(res)
console.log(res);
} else {
skimState.udmSubNum = 0;
}
@@ -393,7 +394,7 @@ async function fnSelectUDM(e: any) {
}
/**资源控制-选择NE */
function fnSelectNeRe(e: any) {
console.log(e)
console.log(e);
graphNodeClickID.value = e.key;
}
//
@@ -408,7 +409,7 @@ const getPopupContainer = () => {
onMounted(() => {
// 获取网元网元列表
neListStore.neCascaderOptions.forEach(item => {
console.log(item)
console.log(item);
if (item.value === 'UPF') {
neOtions.value = JSON.parse(JSON.stringify(item.children));
}
+9 -7
View File
@@ -111,12 +111,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 100,
// },
{
title: t('views.dashboard.cdr.chargingID'), // 计费ID
dataIndex: 'cdrJSON',
@@ -378,9 +378,11 @@ function fnExportList() {
title: t('common.tipTitle'),
content: t('views.dashboard.cdr.exportTip'),
onOk() {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
const querys = toRaw(queryParams);
querys.pageSize = 10000;
querys.pageNum = 1;
querys.pageSize = tablePagination.total;
exportSGWCDataCDR(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
+22 -13
View File
@@ -20,6 +20,7 @@ import { OptionsType, WS } from '@/plugins/ws-websocket';
import PQueue from 'p-queue';
import saveAs from 'file-saver';
import dayjs, { type Dayjs } from 'dayjs';
import { parseSizeFromByte } from '@/utils/parse-utils';
import { useClipboard } from '@vueuse/core';
const { copy } = useClipboard({ legacy: true });
const { t } = useI18n();
@@ -107,12 +108,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns = ref<ColumnsType>([
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'center',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'center',
// width: 100,
// },
{
title: t('views.dashboard.cdr.chargingID'), // 计费ID
dataIndex: 'cdrJSON',
@@ -165,7 +166,7 @@ let tableColumns = ref<ColumnsType>([
}
}
}
return dataVolumeUplink;
return parseSizeFromByte(dataVolumeUplink);
},
},
{
@@ -190,7 +191,7 @@ let tableColumns = ref<ColumnsType>([
}
}
}
return dataVolumeDownlink;
return parseSizeFromByte(dataVolumeDownlink);
},
},
{
@@ -215,7 +216,7 @@ let tableColumns = ref<ColumnsType>([
}
}
}
return dataTotalVolume;
return parseSizeFromByte(dataTotalVolume);
},
},
{
@@ -405,9 +406,11 @@ function fnExportList() {
title: t('common.tipTitle'),
content: t('views.dashboard.cdr.exportTip'),
onOk() {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
const querys = toRaw(queryParams);
querys.pageSize = 10000;
querys.pageNum = 1;
querys.pageSize = tablePagination.total;
exportSMFDataCDR(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
@@ -810,15 +813,21 @@ onBeforeUnmount(() => {
<div>
<div>
<span>Data Total Volume: </span>
<span>{{ udata.dataTotalVolume }}</span>
<span>
{{ parseSizeFromByte(udata.dataTotalVolume || 0) }}
</span>
</div>
<div>
<span>Data Volume Downlink: </span>
<span>{{ udata.dataVolumeDownlink }}</span>
<span>
{{ parseSizeFromByte(udata.dataVolumeDownlink || 0) }}
</span>
</div>
<div>
<span>Data Volume Uplink: </span>
<span>{{ udata.dataVolumeUplink }}</span>
<span>
{{ parseSizeFromByte(udata.dataVolumeUplink || 0) }}
</span>
</div>
<!-- <div>
<span>Time: </span>
+39 -31
View File
@@ -33,10 +33,10 @@ const queue = new PQueue({ concurrency: 1, autoStart: true });
/**字典数据 */
let dict: {
/**CDR 响应原因代码类别类型 */
cdrCauseCode: DictType[];
/**CDR SMSC 响应原因代码类别类型 */
cdrSMSCCauseCode: DictType[];
} = reactive({
cdrCauseCode: [],
cdrSMSCCauseCode: [],
});
/**网元可选 */
@@ -136,12 +136,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 100,
// },
{
title: t('views.dashboard.cdr.recordType'),
dataIndex: 'cdrJSON',
@@ -186,12 +186,19 @@ let tableColumns: ColumnsType = [
},
{
title: t('views.dashboard.cdr.result'),
title: t('views.dashboard.cdr.resultCode'),
dataIndex: 'cdrJSON',
key: 'cause',
align: 'left',
width: 200,
},
{
title: t('views.dashboard.cdr.resultCause'),
dataIndex: 'cdrJSON',
key: 'result',
align: 'left',
width: 200,
},
{
title: t('views.dashboard.cdr.time'),
dataIndex: 'cdrJSON',
@@ -372,9 +379,11 @@ function fnExportList() {
title: t('common.tipTitle'),
content: t('views.dashboard.cdr.exportTip'),
onOk() {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
const querys = toRaw(queryParams);
querys.pageSize = 10000;
querys.pageNum = 1;
querys.pageSize = tablePagination.total;
exportSMSCDataCDR(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
@@ -470,9 +479,9 @@ function wsMessage(res: Record<string, any>) {
onMounted(() => {
// 初始字典数据
Promise.allSettled([getDict('cdr_cause_code')]).then(resArr => {
Promise.allSettled([getDict('cdr_cause_smsc')]).then(resArr => {
if (resArr[0].status === 'fulfilled') {
dict.cdrCauseCode = resArr[0].value;
dict.cdrSMSCCauseCode = resArr[0].value;
}
});
@@ -710,17 +719,15 @@ onBeforeUnmount(() => {
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'cause'">
<span v-if="record.cdrJSON.result === 0">
{{ t('views.dashboard.cdr.resultFail') }},
<DictTag
:options="dict.cdrCauseCode"
:value="record.cdrJSON.cause"
value-default="0"
/>
</span>
<span v-else>
{{ t('views.dashboard.cdr.resultOk') }}
</span>
<span v-if="record.cdrJSON.result === 0"> FAILED </span>
<span v-else> SUCCESS </span>
</template>
<template v-if="column.key === 'result'">
<DictTag
:options="dict.cdrSMSCCauseCode"
:value="record.cdrJSON.cause"
value-default="0"
/>
</template>
<template v-if="column.key === 'id'">
<a-space :size="8" align="center">
@@ -792,18 +799,19 @@ onBeforeUnmount(() => {
<span>{{ record.cdrJSON.calledParty }}</span>
</div>
<div>
<span>{{ t('views.dashboard.cdr.result') }}: </span>
<span v-if="record.cdrJSON.result === 0">
{{ t('views.dashboard.cdr.resultFail') }},
<span>{{ t('views.dashboard.cdr.resultCode') }}: </span>
<span v-if="record.cdrJSON.result === 0"> FAILED </span>
<span v-else> SUCCESS </span>
</div>
<div>
<span>{{ t('views.dashboard.cdr.resultCause') }}: </span>
<span>
<DictTag
:options="dict.cdrCauseCode"
:options="dict.cdrSMSCCauseCode"
:value="record.cdrJSON.cause"
value-default="0"
/>
</span>
<span v-else>
{{ t('views.dashboard.cdr.resultOk') }}
</span>
</div>
</a-col>
<a-col
+53 -19
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { reactive, ref, onMounted, toRaw } from 'vue';
import { reactive, ref, onMounted, toRaw, onBeforeUnmount } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { ProModal } from 'antdv-pro-modal';
import { message, Modal } from 'ant-design-vue/es';
@@ -560,22 +560,24 @@ function fnCancelConfirm() {
*/
function fnSync() {
const hide = message.loading(t('common.loading'), 0);
listSync().then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.faultManage.activeAlarm.sysncSuss'),
duration: 2,
});
fnGetList();
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
}).finally(()=>{
hide();
});
listSync()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.faultManage.activeAlarm.sysncSuss'),
duration: 2,
});
fnGetList();
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
})
.finally(() => {
hide();
});
}
/**
@@ -821,6 +823,24 @@ function fnGetList(pageNum?: number) {
});
}
let interval: any = null;
/**自动刷新 */
function AutoRefresh() {
interval = setInterval(() => {
listAct(toRaw(queryParams)).then((res: any) => {
if (res.code === RESULT_CODE_SUCCESS) {
const { total, rows } = res.data;
tablePagination.total = total;
tableState.data = rows;
} else {
tablePagination.total = 0;
tableState.data = [];
}
});
}, 5_000);
}
onMounted(() => {
// 初始字典数据
Promise.allSettled([
@@ -844,7 +864,12 @@ onMounted(() => {
});
// 获取列表
fnGetList();
// 自动刷新
AutoRefresh();
});
onBeforeUnmount(() => {
clearInterval(interval);
})
</script>
<template>
@@ -957,7 +982,11 @@ onMounted(() => {
{{ t('views.faultManage.activeAlarm.updateConfirm') }}
</a-button>
<a-button type="primary" @click.prevent="fnSync()">
<a-button
type="primary"
@click.prevent="fnSync()"
v-perms:has="['faultManage:active-alarm:async']"
>
<template #icon>
<ReloadOutlined />
</template>
@@ -974,6 +1003,7 @@ onMounted(() => {
danger
@click.prevent="fnClear()"
:disabled="state.selectedRowKeys.length <= 0"
v-perms:has="['faultManage:active-alarm:delete']"
>
<template #icon>
<DeleteOutlined />
@@ -981,7 +1011,11 @@ onMounted(() => {
{{ t('views.faultManage.activeAlarm.clear') }}
</a-button>
<a-button type="dashed" @click.prevent="fnExportList()">
<a-button
type="dashed"
@click.prevent="fnExportList()"
v-perms:has="['faultManage:active-alarm:export']"
>
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
@@ -744,7 +744,7 @@ onMounted(() => {
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-button type="dashed" @click.prevent="fnExportList()">
<a-button type="dashed" @click.prevent="fnExportList()" v-perms:has="['faultManage:history-alarm:export']">
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
+6 -6
View File
@@ -104,12 +104,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 100,
// },
{
title: t('views.ne.common.neType'),
dataIndex: 'neType',
+93 -9
View File
@@ -30,29 +30,78 @@ let sourceState = reactive({
label: t('views.logManage.exportFile.sysOperateLog'),
path: '/usr/local/omc/backup',
},
{
value: '/cdr/ims_cdr_event',
value: '/log/ne_log',
label: t('views.logManage.exportFile.neLog'),
path: '/usr/local/omc/backup',
},
{
value: '/cdr/ims',
label: t('views.logManage.exportFile.cdrIMS'),
path: '/usr/local/omc/backup',
neType: 'IMS',
},
{
value: '/cdr/smf_cdr_event',
value: '/cdr/smf',
label: t('views.logManage.exportFile.cdrSMF'),
path: '/usr/local/omc/backup',
neType: 'SMF',
},
{
value: '/cdr/smsc_cdr_event',
value: '/cdr/smsc',
label: t('views.logManage.exportFile.cdrSMSC'),
path: '/usr/local/omc/backup',
neType: 'SMSC',
},
{
value: '/cdr/sgwc_cdr_event',
label: t('views.logManage.exportFile.cdrSGWC'),
value: '/kpi/ims',
label: t('views.logManage.exportFile.kpiIMS'),
path: '/usr/local/omc/backup',
neType: 'SGWC',
neType: 'IMS',
},
{
value: '/kpi/amf',
label: t('views.logManage.exportFile.kpiAMF'),
path: '/usr/local/omc/backup',
neType: 'AMF',
},
{
value: '/kpi/udm',
label: t('views.logManage.exportFile.kpiUDM'),
path: '/usr/local/omc/backup',
neType: 'UDM',
},
{
value: '/kpi/smf',
label: t('views.logManage.exportFile.kpiSMF'),
path: '/usr/local/omc/backup',
neType: 'SMF',
},
{
value: '/kpi/pcf',
label: t('views.logManage.exportFile.kpiPCF'),
path: '/usr/local/omc/backup',
neType: 'PCF',
},
{
value: '/kpi/upf',
label: t('views.logManage.exportFile.kpiUPF'),
path: '/usr/local/omc/backup',
neType: 'UPF',
},
{
value: '/kpi/mme',
label: t('views.logManage.exportFile.kpiMME'),
path: '/usr/local/omc/backup',
neType: 'MME',
},
{
value: '/kpi/smsc',
label: t('views.logManage.exportFile.kpiSMSC'),
path: '/usr/local/omc/backup',
neType: 'SMSC',
},
],
/**选择value */
@@ -242,9 +291,31 @@ function fnRecordDelete(row: Record<string, any>) {
});
}
/**访问路径 */
let nePathArr = ref<string[]>([]);
/**进入目录 */
function fnDirCD(dir: string, index?: number) {
if (index === undefined) {
nePathArr.value.push(dir);
queryParams.path = nePathArr.value.join('/');
fnGetList(1);
return;
}
if (index === 0) {
nePathArr.value = [nePathArr.value[0]];
queryParams.path = nePathArr.value.join('/');
fnGetList(1);
} else {
nePathArr.value = nePathArr.value.slice(0, index + 1);
queryParams.path = nePathArr.value.join('/');
fnGetList(1);
}
}
/**网元类型选择对应修改 */
function fnNeChange(_: any, opt: any) {
queryParams.path = `${opt.path}${opt.value}`;
nePathArr.value = [`${opt.path}${opt.value}`];
queryParams.path = nePathArr.value.join('/');
ftpInfo.path = queryParams.path;
ftpInfo.tag = opt.value;
fnGetList(1);
@@ -346,8 +417,12 @@ onMounted(() => {
<span>{{ t('views.logManage.neFile.nePath') }}:</span>&nbsp;
<a-col>
<a-breadcrumb>
<a-breadcrumb-item>
{{ queryParams.path }}
<a-breadcrumb-item
v-for="(path, index) in nePathArr"
:key="path"
@click="fnDirCD(path, index)"
>
{{ path }}
</a-breadcrumb-item>
</a-breadcrumb>
</a-col>
@@ -421,6 +496,15 @@ onMounted(() => {
<template #icon><DeleteOutlined /></template>
</a-button>
</a-tooltip>
<a-button
type="link"
:loading="downLoading"
@click.prevent="fnDirCD(record.fileName)"
v-if="record.fileType === 'dir'"
>
<template #icon><FolderOutlined /></template>
{{ t('views.logManage.neFile.dirCd') }}
</a-button>
</a-space>
</template>
</template>
+6 -6
View File
@@ -99,12 +99,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = reactive([
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 100,
// },
{
title: t('views.logManage.forwarding.alarmInter'),
dataIndex: 'type',
+6 -6
View File
@@ -63,12 +63,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = reactive([
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 80,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 80,
// },
{
title: t('views.logManage.mml.account'),
dataIndex: 'user',
+1
View File
@@ -442,6 +442,7 @@ function fnGetList() {
/**自动完成搜索匹配前缀 */
function fnAutoCompleteSearch(value: string) {
state.autoCompleteSearch = [];
return; // 回车undefined问题,关闭搜索匹配
for (const item of state.autoCompleteData) {
const filterOptions = item.options.filter((s: any) => {
return `${s.operation} ${s.object}`.indexOf(value.toLowerCase()) >= 0;
+1
View File
@@ -408,6 +408,7 @@ function fnGetList() {
/**自动完成搜索匹配前缀 */
function fnAutoCompleteSearch(value: string) {
state.autoCompleteSearch = [];
return; // 回车undefined问题,关闭搜索匹配
for (const item of state.autoCompleteData) {
const filterOptions = item.options.filter((s: any) => {
return `${s.operation} ${s.object}`.indexOf(value.toLowerCase()) >= 0;
+18 -18
View File
@@ -49,15 +49,15 @@ function fnCacheKeyInfo(cacheKey: string) {
/**键名列表表格字段列 */
let cacheKeyTableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'num',
width: '50px',
align: 'center',
customRender(opt) {
return opt.index + 1;
},
},
// {
// title: t('common.rowId'),
// dataIndex: 'num',
// width: '50px',
// align: 'center',
// customRender(opt) {
// return opt.index + 1;
// },
// },
{
title: t('views.monitor.cache.cacheKey'),
dataIndex: 'cacheKey',
@@ -152,15 +152,15 @@ let cacheNameTable = reactive({
/**缓存列表表格字段列 */
let cacheNameTableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'num',
width: '50px',
align: 'center',
customRender(opt) {
return opt.index + 1;
},
},
// {
// title: t('common.rowId'),
// dataIndex: 'num',
// width: '50px',
// align: 'center',
// customRender(opt) {
// return opt.index + 1;
// },
// },
{
title: t('views.monitor.cache.cacheName'),
dataIndex: 'cacheName',
+14 -12
View File
@@ -98,11 +98,11 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'jobId',
align: 'center',
},
// {
// title: t('common.rowId'),
// dataIndex: 'jobId',
// align: 'center',
// },
{
title: t('views.monitor.job.jobName'),
dataIndex: 'jobName',
@@ -680,14 +680,14 @@ onMounted(() => {
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
<a-button
<!-- <a-button
type="default"
@click.prevent="fnJobLogView()"
v-perms:has="['monitor:job:query']"
>
<template #icon><ContainerOutlined /></template>
{{ t('views.monitor.job.jobLog') }}
</a-button>
</a-button> -->
<a-button
type="dashed"
danger
@@ -755,11 +755,13 @@ onMounted(() => {
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: tableColumns.length * 120 }"
:row-selection="{
type: 'checkbox',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
:row-selection="hasPermissions(['monitor:job:remove'])
? {
type: 'checkbox',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}
: undefined"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'jobGroup'">
+13 -6
View File
@@ -117,12 +117,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'logId',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'logId',
// align: 'left',
// width: 100,
// },
{
title: t('views.monitor.jobLog.jobName'),
dataIndex: 'jobName',
@@ -247,6 +247,13 @@ let modalState: ModalStateType = reactive({
*/
function fnModalVisibleByVive(row: Record<string, string>) {
modalState.from = Object.assign(modalState.from, row);
try {
modalState.from.jobMsg = JSON.stringify(
JSON.parse(modalState.from.jobMsg),
null,
4
);
} catch (_) {}
modalState.title = t('views.monitor.jobLog.viewLog');
modalState.open = true;
}
+10 -10
View File
@@ -41,16 +41,16 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'num',
width: '50px',
align: 'left',
customRender(opt) {
const idxNum = (tablePagination.current - 1) * tablePagination.pageSize;
return idxNum + opt.index + 1;
},
},
// {
// title: t('common.rowId'),
// dataIndex: 'num',
// width: '50px',
// align: 'left',
// customRender(opt) {
// const idxNum = (tablePagination.current - 1) * tablePagination.pageSize;
// return idxNum + opt.index + 1;
// },
// },
{
title: t('views.monitor.online.mettingId'),
dataIndex: 'tokenId',
+42 -3
View File
@@ -64,10 +64,50 @@ function fnGetList(refresh: boolean = false) {
Array.isArray(res.data) &&
res.data.length > 0
) {
// 根网管
let rootNodeInfo = { neName: 'OMC_001' };
const nodes = [];
const edges = [];
// 根网管
let rootNodeInfo = {
neName: 'ROOT',
serverState: {
online: true,
},
};
// 处理没有OMC的情况
const omcFilter = res.data.filter(v => v.neType === 'OMC');
if (omcFilter.length === 1) {
rootNodeInfo.neName = omcFilter[0].neName;
} else {
nodes.unshift({
id: rootNodeInfo.neName,
label: rootNodeInfo.neName,
info: rootNodeInfo,
labelCfg: {
position: 'bottom',
offset: 8,
style: {
fill: '#fff',
fontSize: 14,
},
},
size: 60,
icon: {
x: -30,
y: -30,
// 可更换为其他图片地址
img: parseBasePath('/svg/service_db.svg'),
width: 60,
height: 60,
},
});
edges.push({
source: rootNodeInfo.neName,
target: rootNodeInfo.neName,
label: `${rootNodeInfo.neName}-${rootNodeInfo.neName}`,
});
}
for (const item of res.data) {
item.serverState = {};
// 节点
@@ -96,7 +136,6 @@ function fnGetList(refresh: boolean = false) {
height: 60,
},
});
!rootNodeInfo.neName && (rootNodeInfo = item);
} else {
nodes.push({
id: item.neName,
@@ -143,11 +143,10 @@ const graphNodeTooltip = new Tooltip({
"
>
<div><strong>${t('views.monitor.topology.state')}:</strong><span>
${
neState.online
? t('views.monitor.topology.normalcy')
: t('views.monitor.topology.exceptions')
}
${neState.online
? t('views.monitor.topology.normalcy')
: t('views.monitor.topology.exceptions')
}
</span></div>
<div><strong>${t('views.monitor.topology.refreshTime')}:</strong><span>
${neState.refreshTime ?? '--'}
@@ -399,11 +398,10 @@ function wsMessage(res: Record<string, any>) {
const [neType, neId] = requestId.split('_');
const { combos, edges, nodes } = graphState.data;
const node = nodes.find((item: Record<string, any>) => item.id === neType);
console.log(data)
// 更新网元状态
const newNeState = Object.assign(node.neState, data, {
refreshTime: parseDateToStr(data.refreshTime, 'HH:mm:ss'),
online: !!data.cpu,
refreshTime: parseDateToStr(data.refreshTime, 'MM-DD HH:mm:ss'),
});
// 通过 ID 查询节点实例
@@ -505,11 +503,7 @@ onBeforeUnmount(() => {
<template>
<PageContainer>
<a-card
:bordered="false"
:body-style="{ marginBottom: '24px' }"
size="small"
>
<a-card :bordered="false" :body-style="{ marginBottom: '24px' }" size="small">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
@@ -521,12 +515,10 @@ onBeforeUnmount(() => {
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-button
type="default"
size="small"
@click.prevent="fnGraphDataLoad(true)"
>
<template #icon><ReloadOutlined /></template>
<a-button type="default" size="small" @click.prevent="fnGraphDataLoad(true)">
<template #icon>
<ReloadOutlined />
</template>
{{ t('common.reloadText') }}
</a-button>
</template>
@@ -0,0 +1,592 @@
<script setup lang="ts">
import { reactive, toRaw, watch, ref } from 'vue';
import { ProModal } from 'antdv-pro-modal';
import { Form, message } from 'ant-design-vue/es';
import useI18n from '@/hooks/useI18n';
import useNeListStore from '@/store/modules/ne_list';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import {
getNeConfigData,
addNeConfigData,
editNeConfigData,
} from '@/api/ne/neConfig';
const { t } = useI18n();
const neListStore = useNeListStore();
const emit = defineEmits(['ok', 'cancel', 'update:open']);
const props = defineProps({
open: {
type: Boolean,
default: false,
required: true,
},
});
/**网元类型_多neId */
let neCascaderOptions = ref<Record<string, any>[]>([]);
/**对话框对象信息状态类型 */
type ModalStateType = {
/**新增框或修改框是否显示 */
openByEdit: boolean;
/**标题 */
title: string;
/**表单数据 */
from: Record<string, any>;
/**网元类型 */
neType: string[];
/**需要配置的网元列表 */
toNe: {
neType: string;
neId: string;
}[];
/**提示信息 */
msg: string;
/**确定按钮 loading */
confirmLoading: boolean;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
openByEdit: false,
title: 'PLMN Info',
from: {
plmnId: {
mcc: '001',
mnc: '01',
},
tac: '4388',
snssai: {
sst: '1',
sd: '000001',
},
dnn_data: 'internet',
dnn_ims: 'ims',
},
neType: [],
toNe: [],
msg: '',
confirmLoading: false,
});
/**对话框内表单属性和校验规则 */
const modalStateFrom = Form.useForm(
modalState.from.plmnId,
reactive({
mcc: [
{
required: true,
message: t('common.inputPlease'),
},
],
mnc: [
{
required: true,
message: t('common.inputPlease'),
},
],
})
);
/**网元类型选择对应修改 */
function fnNeChange(p: any, c: any) {
let neList: { neType: string; neId: string }[] = [];
for (let i = 0; i < p.length; i++) {
const v = p[i];
if (v.length === 1) {
c[i][0].children.forEach((item: any) => {
neList.push({
neType: item.neType,
neId: item.neId,
});
});
} else if (v.length === 2) {
neList.push({
neType: v[0],
neId: v[1],
});
}
}
if (neList.length > 0) {
modalState.toNe = neList;
} else {
modalState.toNe = [];
}
}
/**
* 对话框弹出确认执行函数
* 进行表达规则校验
*/
function fnModalOk() {
if (modalState.confirmLoading) return;
if (modalState.toNe.length === 0) {
message.warning({
content: 'Please select NE',
duration: 3,
});
return;
}
const form = toRaw(modalState.from);
// console.log(form);
modalStateFrom
.validate()
.then(async () => {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
let errArr = [];
for (const item of modalState.toNe) {
const err = await toConfig(item.neType, item.neId, form);
if (err.length > 0) {
errArr.push(...err);
}
}
modalState.msg = errArr.join('\n');
modalState.confirmLoading = false;
emit('ok', JSON.parse(JSON.stringify(form)));
hide();
})
.catch(e => {
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
});
}
/**
* 对话框弹出关闭执行函数
* 进行表达规则校验
*/
function fnModalCancel() {
modalState.openByEdit = false;
modalState.confirmLoading = false;
modalStateFrom.resetFields();
emit('cancel');
emit('update:open', false);
// 还原默认值
modalState.from = {
plmnId: {
mcc: '001',
mnc: '01',
},
tac: '4388',
snssai: {
sst: '1',
sd: '000001',
},
dnn_data: 'internet',
dnn_ims: 'ims',
};
modalState.neType = [];
modalState.toNe = [];
modalState.msg = '';
}
/**监听是否显示,初始数据 */
watch(
() => props.open,
val => {
if (val) {
// 获取网元网元列表
neCascaderOptions.value = neListStore.getNeCascaderOptions.filter(
(item: any) => {
return ['MME', 'AMF', 'UDM', 'IMS'].includes(item.value); // 过滤不可用的网元
}
);
if (neCascaderOptions.value.length === 0) {
message.warning({
content: t('common.noData'),
duration: 3,
});
return;
}
modalState.openByEdit = true;
}
}
);
// =============== 处理函数
const supportMapper: Record<string, any> = {
AMF: [
{
type: 'array',
name: 'guami',
display: 'GUAMI List',
key: ['plmnId'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return {
index: index,
plmnId: plmn,
pointer: lastItem.pointer,
regionId: lastItem.regionId,
setId: lastItem.setId,
};
},
},
{
type: 'array',
name: 'tai',
display: 'TAI List',
key: ['plmnId'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return {
index: index,
plmnId: plmn,
tac: param.tac,
};
},
},
{
type: 'array',
name: 'slice',
display: 'Slice List',
key: ['plmnId'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
const sdStr = param.snssai.sd.padStart(6, '0');
return {
index: index,
plmnId: plmn,
sst: parseInt(param.snssai.sst),
sd: sdStr,
};
},
},
],
IMS: [
{
type: 'array',
name: 'plmn',
display: 'PLMN List',
key: ['mcc', 'mnc'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
const mccDomain = param.plmnId.mcc.padStart(3, '0');
const domain = `ims.mnc${param.plmnId.mnc}.mcc${mccDomain}.3gppnetwork.org`;
return {
index: index,
domain: domain,
mcc: param.plmnId.mcc,
mnc: param.plmnId.mnc,
};
},
},
],
UDM: [
{
type: 'list',
name: 'system',
display: 'System',
key: [
'supportedPlmn1',
'supportedPlmn2',
'supportedPlmn3',
'supportedPlmn4',
],
},
],
MME: [
{
type: 'array',
name: 'gummei',
display: 'Gummei List',
key: ['plmnId'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return Object.assign(lastItem, {
index: index,
plmnId: plmn,
});
},
},
{
type: 'array',
name: 'tai',
display: 'TAI List',
key: ['plmnId'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return {
index: index,
plmnId: plmn,
tac: parseInt(param.tac),
};
},
},
{
type: 'array',
name: 'hss',
display: 'HSS List',
key: ['imsiPre'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return Object.assign(lastItem, {
index: index,
imsiPre: plmn,
});
},
},
{
type: 'array',
name: 'sgw',
display: 'SGW List',
key: ['plmnId'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return Object.assign(lastItem, {
index: index,
plmnId: plmn,
tac: parseInt(param.tac),
});
},
},
],
};
async function toConfig(
ntType: string,
neId: string,
param: Record<string, any>
) {
let errMsgArr = [];
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
for (const rule of supportMapper[ntType]) {
const res = await getNeConfigData({
neType: ntType,
neId: neId,
paramName: rule.name,
});
if (res.code != RESULT_CODE_SUCCESS) {
// console.log('获取数据失败', rule);
errMsgArr.push(`${ntType}_${neId} ${rule.display} get data failed`);
continue;
}
if (res.data.length === 0) {
// console.log('数据为空', rule);
errMsgArr.push(`${ntType}_${neId} ${rule.display} data empty`);
continue;
}
if (rule.type === 'list') {
// key
const data = res.data[0];
let vArr = [];
for (const k of rule.key) {
vArr.push(data[k]);
}
if (vArr.includes(plmn)) {
// console.log('存在跳过', rule.name, vArr, plmn);
errMsgArr.push(`${ntType}_${neId} ${rule.display} already exists`);
continue;
}
if (vArr.includes('')) {
const index = vArr.findIndex(s => s == '');
// console.log('空白新增', rule.name, rule.key[index], plmn);
const resList = await editNeConfigData({
neType: ntType,
neId: neId,
paramName: rule.name,
paramData: {
[rule.key[index]]: plmn,
},
});
const state =
resList.code != RESULT_CODE_SUCCESS ? 'failed' : 'success';
errMsgArr.push(
`${ntType}_${neId} ${rule.display} blank items added ${state}`
);
} else {
// console.log(
// '不存在替换最后的',
// rule.name,
// rule.key[rule.key.length - 1],
// plmn
// );
const resList = await editNeConfigData({
neType: ntType,
neId: neId,
paramName: rule.name,
paramData: {
[rule.key[rule.key.length - 1]]: plmn,
},
});
const state =
resList.code != RESULT_CODE_SUCCESS ? 'failed' : 'success';
errMsgArr.push(
`${ntType}_${neId} ${rule.display} replace the final ${state}`
);
}
}
if (rule.type === 'array') {
// item
let vArr = [];
for (const item of res.data) {
vArr.push(item[rule.key]);
}
if (vArr.includes(plmn)) {
const item = res.data.find((s: any) => s[rule.key] == plmn);
if (!item) {
// console.log('没有找到', rule.name, index);
errMsgArr.push(`${ntType}_${neId} ${rule.display} not found`);
continue;
}
const index = item.index;
const updateItem = rule.item(index, param, item);
// console.log('存在修改', rule.name, index, updateItem);
const resList = await editNeConfigData({
neType: ntType,
neId: neId,
paramName: rule.name,
paramData: updateItem,
loc: `${index}`,
});
const state =
resList.code != RESULT_CODE_SUCCESS ? 'failed' : 'success';
errMsgArr.push(`${ntType}_${neId} ${rule.display} modify ${state}`);
} else {
let lastIndex = 0;
const arr = res.data.sort((a: any, b: any) => b.index - a.index);
if (arr.length != 0) {
lastIndex = arr[0].index + 1;
}
const addItem = rule.item(lastIndex, param, arr[0]);
// console.log('不存在新增', rule.name, lastIndex, addItem);
const resList = await addNeConfigData({
neType: ntType,
neId: neId,
paramName: rule.name,
paramData: addItem,
loc: `${lastIndex}`,
});
const state =
resList.code != RESULT_CODE_SUCCESS ? 'failed' : 'success';
errMsgArr.push(`${ntType}_${neId} ${rule.display} added ${state}`);
}
}
}
return errMsgArr;
}
</script>
<template>
<ProModal
:drag="true"
:width="800"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:open="modalState.openByEdit"
:title="modalState.title"
:confirm-loading="modalState.confirmLoading"
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form
name="modalStateFromByEdit"
layout="horizontal"
:label-col="{ span: 6 }"
:label-wrap="true"
>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="DNN_DATA" name="basic.dnn_data" v-if="false">
<a-input
v-model:value="modalState.from.dnn_data"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title> DNN </template>
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
<a-form-item
label="MCC"
name="basic.plmnId.mcc"
v-bind="modalStateFrom.validateInfos.mcc"
>
<a-input
v-model:value="modalState.from.plmnId.mcc"
placeholder="1-65535"
></a-input>
</a-form-item>
<a-form-item label="SST" name="basic.snssai.sst">
<a-input-number
v-model:value="modalState.from.snssai.sst"
:min="1"
:max="3"
placeholder="1-3"
style="width: 100%"
>
</a-input-number>
</a-form-item>
<a-form-item label="TAC" name="basic.tac">
<a-input
v-model:value="modalState.from.tac"
placeholder="1-65535"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="DNN_IMS" name="basic.dnn_ims" v-if="false">
<a-input
v-model:value="modalState.from.dnn_ims"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
</a-input>
</a-form-item>
<a-form-item
label="MNC"
name="basic.plmnId.mnc"
v-bind="modalStateFrom.validateInfos.mnc"
>
<a-input
v-model:value="modalState.from.plmnId.mnc"
placeholder="1-65535"
></a-input>
</a-form-item>
<a-form-item label="SD" name="basic.snssai.sd">
<a-input
v-model:value="modalState.from.snssai.sd"
placeholder="1-65535"
></a-input>
</a-form-item>
</a-col>
</a-row>
<a-divider orientation="left">To NE</a-divider>
<a-form-item
:label="t('views.ne.common.neType')"
name="neType"
:label-col="{ span: 3 }"
:label-wrap="true"
>
<a-cascader
v-model:value="modalState.neType"
:options="neCascaderOptions"
@change="fnNeChange"
multiple
:allow-clear="false"
:placeholder="t('views.ne.common.neTypePlease')"
/>
<a-textarea
:disabled="true"
:hidden="!modalState.msg"
:value="modalState.msg"
:auto-size="{ minRows: 2, maxRows: 8 }"
style="margin-top: 8px"
/>
</a-form-item>
</a-form>
</ProModal>
</template>
<style lang="less" scoped></style>
@@ -24,7 +24,7 @@ export default function useArrayImport({
fileetx: '.xlsx',
itemKey: 'index',
item: (row: Record<string, any>) => {
const index = row['Index'] || 0;
const index = row['Index'] ?? -1;
return {
imeiPrefixValue: `${row['IMEI Prefix']}`,
index: parseInt(index),
@@ -36,7 +36,7 @@ export default function useArrayImport({
fileetx: '.xlsx',
itemKey: 'index',
item: (row: Record<string, any>) => {
const index = row['Index'] || 0;
const index = row['Index'] ?? -1;
return {
imsiValue: `${row['IMSI Value']}`,
imeiValue: `${row['IMEI Value/Prefix']}`,
@@ -51,7 +51,7 @@ export default function useArrayImport({
fileetx: '.xlsx',
itemKey: 'index',
item: (row: Record<string, any>) => {
const index = row['Index'] || 0;
const index = row['Index'] ?? -1;
return {
imei: `${row['IMEI']}`,
index: parseInt(index),
@@ -128,7 +128,7 @@ export default function useArrayImport({
importState.loading = true;
for (const row of rows) {
const rowItem = importState.item(row);
const rowKey = rowItem[importState.itemKey] || -1;
const rowKey = rowItem[importState.itemKey];
let result: any = null;
// 检查index是否定义
const has = arrayState.columnsData.find(
+25 -27
View File
@@ -29,8 +29,13 @@ export default function useOptions({ t }: any) {
case 'int':
// filter: "0~128"
if (filter && filter.indexOf('~') !== -1) {
const filterArr = filter.split('~');
if (filter) {
let filterArr = ['0', '1'];
if (filter.indexOf('-') !== -1) {
filterArr = filter.split('-');
} else if (filter.indexOf('~') !== -1) {
filterArr = filter.split('~');
}
const minInt = parseInt(filterArr[0]);
const maxInt = parseInt(filterArr[1]);
const valueInt = parseInt(value);
@@ -47,18 +52,12 @@ export default function useOptions({ t }: any) {
break;
case 'ipv4':
if (!regExpIPv4.test(value)) {
return [
false,
t('views.ne.neConfig.requireIpv4', { display }),
];
return [false, t('views.ne.neConfig.requireIpv4', { display })];
}
break;
case 'ipv6':
if (!regExpIPv6.test(value)) {
return [
false,
t('views.ne.neConfig.requireIpv6', { display }),
];
return [false, t('views.ne.neConfig.requireIpv6', { display })];
}
break;
case 'enum':
@@ -71,10 +70,7 @@ export default function useOptions({ t }: any) {
}
if (!Object.keys(filterJson).includes(`${value}`)) {
return [
false,
t('views.ne.neConfig.requireEnum', { display }),
];
return [false, t('views.ne.neConfig.requireEnum', { display })];
}
}
break;
@@ -90,10 +86,7 @@ export default function useOptions({ t }: any) {
}
if (!Object.values(filterJson).includes(`${value}`)) {
return [
false,
t('views.ne.neConfig.requireBool', { display }),
];
return [false, t('views.ne.neConfig.requireBool', { display })];
}
}
break;
@@ -101,12 +94,20 @@ export default function useOptions({ t }: any) {
// filter: "0~128"
// 字符串长度判断
if (filter && filter.indexOf('~') !== -1) {
if (filter) {
try {
const filterArr = filter.split('~');
let rule = new RegExp(
'^\\S{' + filterArr[0] + ',' + filterArr[1] + '}$'
);
let rule: RegExp = new RegExp('^.*$');
if (filter.indexOf('-') !== -1) {
const filterArr = filter.split('-');
rule = new RegExp(
'^.{' + filterArr[0] + ',' + filterArr[1] + '}$'
);
} else if (filter.indexOf('~') !== -1) {
const filterArr = filter.split('~');
rule = new RegExp(
'^\\S{' + filterArr[0] + ',' + filterArr[1] + '}$'
);
}
if (!rule.test(value)) {
return [
false,
@@ -157,10 +158,7 @@ export default function useOptions({ t }: any) {
break;
default:
return [
false,
t('views.ne.neConfig.requireUn', { display }),
];
return [false, t('views.ne.neConfig.requireUn', { display })];
}
return result;
}
+19 -2
View File
@@ -7,6 +7,7 @@ import { message } from 'ant-design-vue/es';
import { DataNode } from 'ant-design-vue/es/tree';
import useI18n from '@/hooks/useI18n';
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
import QuickSetup from './components/QuickSetup.vue';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useNeListStore from '@/store/modules/ne_list';
import useOptions from './hooks/useOptions';
@@ -208,7 +209,7 @@ function fnActiveConfigNode(key: string | number) {
/**查询配置可选属性值列表 */
function fnGetNeConfig() {
const neType = neTypeSelect.value[0];
const [neType, neId] = neTypeSelect.value ;
if (!neType) {
message.warning({
content: t('views.ne.neConfig.neTypePleace'),
@@ -219,7 +220,7 @@ function fnGetNeConfig() {
treeState.loading = true;
// 获取数据
getAllNeConfig(neType).then(res => {
getAllNeConfig(neType, neId).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
const arr = [];
for (const item of res.data) {
@@ -391,6 +392,12 @@ const { batchState, modalBatchOpen, modalBatchClose, modalBatchOk } =
fnActiveConfigNode,
});
/**快速修改编辑框 */
const quickOpen = ref(false);
function fnQuickOpen() {
quickOpen.value = !quickOpen.value;
}
onMounted(() => {
// 获取网元网元列表
neCascaderOptions.value = neListStore.getNeCascaderOptions.filter(
@@ -430,6 +437,13 @@ onMounted(() => {
<template>
<PageContainer>
<template #content> </template>
<template #contentExtra>
<a-button type="primary" @click="fnQuickOpen">
Quickly Modify PLMN
</a-button>
</template>
<a-row :gutter="16">
<a-col
:lg="6"
@@ -1053,6 +1067,9 @@ onMounted(() => {
</a-row>
</a-form>
</ProModal>
<!-- 快速修改 -->
<QuickSetup v-model:open="quickOpen"></QuickSetup>
</PageContainer>
</template>
+23 -15
View File
@@ -73,12 +73,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns = ref<TableColumnsType>([
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 100,
// },
{
title: t('views.ne.common.neType'),
dataIndex: 'neType',
@@ -303,12 +303,8 @@ function fnModalVisibleByEdit(row: Record<string, any>) {
const modalStateFrom = Form.useForm(
modalState.from,
reactive({
name: [
{
required: true,
message: '请输入名称',
},
],
name: [{ required: true, message: t('common.inputPlease') }],
remark: [{ required: true, message: t('common.inputPlease') }],
})
);
@@ -462,7 +458,11 @@ function fnSyncFileToFTP(row: Record<string, any>) {
<template #title>
{{ t('views.ne.neConfigBackup.backupModal.title') }}
</template>
<a-button type="text" @click.prevent="fnFTPModalOpen()">
<a-button
type="text"
@click.prevent="fnFTPModalOpen()"
v-perms:has="['ne:neConfigBackup:ftp']"
>
<template #icon><DeliveredProcedureOutlined /></template>
</a-button>
</a-tooltip>
@@ -524,13 +524,21 @@ function fnSyncFileToFTP(row: Record<string, any>) {
<template #title>
{{ t('views.ne.neConfigBackup.backupModal.pushFileOper') }}
</template>
<a-button type="link" @click.prevent="fnSyncFileToFTP(record)">
<a-button
type="link"
@click.prevent="fnSyncFileToFTP(record)"
v-perms:has="['ne:neConfigBackup:ftpSync']"
>
<template #icon><CloudServerOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.downloadText') }}</template>
<a-button type="link" @click.prevent="fnDownloadFile(record)">
<a-button
type="link"
@click.prevent="fnDownloadFile(record)"
v-perms:has="['ne:neConfigBackup:download']"
>
<template #icon><DownloadOutlined /></template>
</a-button>
</a-tooltip>
+3 -2
View File
@@ -30,7 +30,7 @@ let neIdSelect = ref<string[]>([]);
let neTypeSelectStatus = ref(true);
/**网元类型neType选择 */
async function fnSelectNeType(_: any, info: any) {
if (!info) return;
if (!info) return;
await fnGetNeConfig(info.value);
if (treeState.data.length === 0) {
message.warning({
@@ -226,7 +226,8 @@ async function fnGetNeConfig(neType: string) {
treeState.loading = true;
// 获取数据
const res = await getAllNeConfig(neType);
let neId = neTypeSelect.value[1];
const res = await getAllNeConfig(neType, neId);
if (res.code === RESULT_CODE_SUCCESS) {
const arr = [];
for (const v of res.data) {
+22
View File
@@ -10,6 +10,7 @@ import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useNeListStore from '@/store/modules/ne_list';
import useI18n from '@/hooks/useI18n';
import ViewDrawer from './components/ViewDrawer.vue';
import BackupModal from '@/views/ne/neConfigBackup/components/BackupModal.vue';
import saveAs from 'file-saver';
import { useRoute } from 'vue-router';
import { parseSizeFromFile } from '@/utils/parse-utils';
@@ -282,6 +283,12 @@ function fnDrawerOpen(row: Record<string, any>) {
viewDrawerState.open = !viewDrawerState.open;
}
/**打开FTP配置窗口 */
const openFTPModal = ref<boolean>(false);
function fnFTPModalOpen() {
openFTPModal.value = !openFTPModal.value;
}
onMounted(() => {
if (routeParams.neType) {
neTypeSelect.value = [routeParams.neType, routeParams.neId];
@@ -327,6 +334,18 @@ onMounted(() => {
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<a-tooltip placement="topRight">
<template #title>
{{ t('views.ne.neConfigBackup.backupModal.title') }}
</template>
<a-button
type="text"
@click.prevent="fnFTPModalOpen()"
v-perms:has="['ne-data:backup-data:ftp']"
>
<template #icon><DeliveredProcedureOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
@@ -387,6 +406,9 @@ onMounted(() => {
:ne-type="viewDrawerState.neType"
:ne-id="viewDrawerState.neId"
></ViewDrawer>
<!-- FTP配置窗口 -->
<BackupModal v-model:open="openFTPModal"></BackupModal>
</PageContainer>
</template>
+6 -6
View File
@@ -80,12 +80,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'center',
width: 50,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'center',
// width: 50,
// },
{
title: t('views.ne.neHost.hostType'),
dataIndex: 'hostType',
+6 -6
View File
@@ -78,12 +78,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'cmdId',
align: 'center',
width: 50,
},
// {
// title: t('common.rowId'),
// dataIndex: 'cmdId',
// align: 'center',
// width: 50,
// },
{
title: t('views.ne.neHostCmd.cmdType'),
dataIndex: 'cmdType',
@@ -0,0 +1,435 @@
<script setup lang="ts">
import { reactive, toRaw, watch, ref } from 'vue';
import { ProModal } from 'antdv-pro-modal';
import { message, Form, Progress } from 'ant-design-vue/es';
import { CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons-vue';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { getOAMFile, saveOAMFile, serviceNeAction } from '@/api/ne/neInfo';
import useNeListStore from '@/store/modules/ne_list';
const { t } = useI18n();
const neListStore = useNeListStore();
const emit = defineEmits(['ok', 'cancel', 'update:open']);
const props = defineProps({
open: {
type: Boolean,
default: false,
},
});
/**对话框对象信息状态类型 */
type ModalStateType = {
/**是否显示 */
openByEdit: boolean;
/**标题 */
title: string;
/**表单数据 */
from: {
selectedNeList: string[];
omcIP: string;
oamPort: number;
restart: boolean;
};
/**确定按钮 loading */
confirmLoading: boolean;
/**进度显示 */
progress: {
visible: boolean;
current: number;
total: number;
currentNe: string;
};
/**操作结果 */
results: Array<{
neType: string;
neId: string;
neName: string;
success: boolean;
message: string;
}>;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
openByEdit: false,
title: '快速OAM配置',
from: {
selectedNeList: [],
omcIP: '127.0.0.1',
oamPort: 33030,
restart: false,
},
confirmLoading: false,
progress: {
visible: false,
current: 0,
total: 0,
currentNe: '',
},
results: [],
});
/**对话框内表单属性和校验规则 */
const modalStateFrom = Form.useForm(
modalState.from,
reactive({
selectedNeList: [
{
required: true,
message: t('views.ne.neInfo.quickOam.selectNe'),
},
],
omcIP: [
{
required: true,
},
{
pattern: /^((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]?)$/,
},
],
oamPort: [
{
required: true,
},
],
})
);
/**获取网元列表选项 */
const neOptions = ref<Array<{ label: string; value: string; neType: string; neId: string; neName: string }>>([]);
/**
* 初始化网元选项
*/
async function initNeOptions() {
// 确保网元列表已加载
await neListStore.fnNelistRefresh();
// 从store获取网元列表,过滤掉OMC类型
const neList = neListStore.getNeList.filter((ne: any) => ne.neType !== 'OMC');
neOptions.value = neList.map((ne: any) => ({
label: `${ne.neType}-${ne.neId} (${ne.neName})`,
value: `${ne.neType}@${ne.neId}`,
neType: ne.neType,
neId: ne.neId,
neName: ne.neName,
}));
}
/**
* 单个网元OAM配置(复用OAMModal的逻辑)
* @param neType 网元类型
* @param neId 网元ID
* @param omcIP OMC IP地址
* @param oamPort OAM端口
* @param restart 是否重启网元
* @returns Promise<{success: boolean, message: string}>
*/
async function configureSingleNeOAM(neType: string, neId: string, omcIP: string, oamPort: number, restart: boolean = false) {
try {
// 1. 获取当前OAM配置(复用OAMModal的获取逻辑)
const getRes = await getOAMFile(neType, neId);
if (getRes.code !== RESULT_CODE_SUCCESS) {
return {
success: false,
message: `${getRes.msg}`,
};
}
// 2. 构建保存数据(复用OAMModal的数据结构转换逻辑)
const data = getRes.data;
const ipType = data?.oamConfig?.ipType || 'ipv4';
const saveContent = {
omcIP: omcIP,
oamEnable: data?.oamConfig?.enable || false,
oamPort: oamPort,
snmpEnable: data?.snmpConfig?.enable || false,
snmpPort: data?.snmpConfig?.port || 4957,
kpiEnable: data?.kpiConfig?.enable || false,
kpiTimer: data?.kpiConfig?.timer || 60,
};
// 3. 保存配置(复用OAMModal的保存逻辑)
const saveRes = await saveOAMFile({
neType,
neId,
content: saveContent,
sync: true,
});
if (saveRes.code === RESULT_CODE_SUCCESS) {
// 如果需要重启网元,调用重启服务(复用OAMModal的重启逻辑)
if (restart) {
try {
await serviceNeAction({
neType,
neId,
action: 'restart',
});
} catch (restartError: any) {
return {
success: true,
message: `${restartError.message || restartError}`,
};
}
}
return {
success: true,
message: restart ? `${t('views.ne.neInfo.quickOam.success')}` : `${t('views.ne.neInfo.quickOam.success')}`,
};
} else {
return {
success: false,
message: `${saveRes.msg}`,
};
}
} catch (error: any) {
return {
success: false,
message: `${error.message || error}`,
};
}
}
/**
* 对话框弹出确认执行函数
* 进行表达规则校验
*/
async function fnModalOk() {
try {
await modalStateFrom.validate();
modalState.confirmLoading = true;
modalState.progress.visible = true;
modalState.progress.current = 0;
modalState.progress.total = modalState.from.selectedNeList.length;
modalState.results = [];
const { omcIP, oamPort, restart } = modalState.from;
// 循环处理每个选中的网元
for (let i = 0; i < modalState.from.selectedNeList.length; i++) {
const neInfo = modalState.from.selectedNeList[i];
const [neType, neId] = neInfo.split('@');
const neName = neOptions.value.find(opt => opt.value === neInfo)?.neName || neId;
modalState.progress.current = i + 1;
modalState.progress.currentNe = `${neType}-${neId}`;
// 调用单个网元配置函数
const result = await configureSingleNeOAM(neType, neId, omcIP, oamPort, restart);
modalState.results.push({
neType,
neId,
neName,
success: result.success,
message: result.message,
});
}
// 显示操作结果
const successCount = modalState.results.filter(r => r.success).length;
const failCount = modalState.results.length - successCount;
if (failCount === 0) {
message.success(`${t('views.ne.neInfo.quickOam.success')} ${successCount} `, 5);
} else {
message.warning(`${t('views.ne.neInfo.quickOam.success')} ${successCount} ,${t('views.ne.neInfo.quickOam.default')} ${failCount} `, 5);
}
// 不自动关闭弹窗,让用户查看结果
// emit('ok');
// fnModalCancel();
} catch (error: any) {
message.error(t('common.errorFields', { num: error.errorFields?.length || 0 }), 3);
} finally {
modalState.confirmLoading = false;
modalState.progress.visible = false;
}
}
/**
* 对话框弹出关闭执行函数
*/
function fnModalCancel() {
modalState.openByEdit = false;
modalState.confirmLoading = false;
modalState.progress.visible = false;
modalState.progress.current = 0;
modalState.progress.total = 0;
modalState.progress.currentNe = '';
modalState.results = [];
modalStateFrom.resetFields();
emit('cancel');
emit('update:open', false);
}
/**
* 手动关闭弹窗(用于操作完成后)
*/
function fnModalClose() {
fnModalCancel();
}
/**监听是否显示,初始数据 */
watch(
() => props.open,
val => {
if (val) {
initNeOptions();
modalState.title = t('views.ne.neInfo.quickOam.title');
modalState.openByEdit = true;
}
}
);
</script>
<template>
<ProModal
:drag="true"
:destroyOnClose="true"
:body-style="{ maxHeight: '600px', 'overflow-y': 'auto' }"
:keyboard="false"
:mask-closable="false"
:open="modalState.openByEdit"
:title="modalState.title"
:confirm-loading="modalState.confirmLoading"
:width='500'
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form
name="modalStateFrom"
layout="horizontal"
:label-col="{ span: 8 }"
:labelWrap="true"
>
<a-form-item
:label="t('views.ne.neInfo.oam.restart')"
name="restart"
>
<a-switch
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
v-model:checked="modalState.from.restart"
/>
</a-form-item>
<a-form-item
:label="t('views.ne.neInfo.quickOam.omcIP')"
name="omcIP"
v-bind="modalStateFrom.validateInfos.omcIP"
>
<a-input
v-model:value="modalState.from.omcIP"
:maxlength="128"
/>
</a-form-item>
<a-form-item
:label="t('views.ne.neInfo.quickOam.oamPort')"
name="oamPort"
v-bind="modalStateFrom.validateInfos.oamPort"
>
<a-input-number
v-model:value="modalState.from.oamPort"
:min="3000"
:max="65535"
:step="1"
style="width: 100%"
/>
</a-form-item>
<a-form-item
:label="t('views.ne.neInfo.quickOam.selectNe')"
name="selectedNeList"
v-bind="modalStateFrom.validateInfos.selectedNeList"
>
<a-select
v-model:value="modalState.from.selectedNeList"
mode="multiple"
:options="neOptions"
:placeholder="t('common.selectPlease')"
:max-tag-count="3"
style="width: 100%"
/>
</a-form-item>
</a-form>
<!-- 进度显示 -->
<div v-if="modalState.progress.visible || modalState.results.length > 0" style="margin-top: 20px;">
<a-divider>{{ t('views.ne.neInfo.quickOam.progress') }}</a-divider>
<div style="margin-bottom: 10px;">
<span>{{ t('views.ne.neInfo.quickOam.processing') }}: {{ modalState.progress.currentNe }}</span>
<span style="float: right;">
{{ modalState.progress.current }} / {{ modalState.progress.total }}
</span>
</div>
<!-- <Progress-->
<!-- :percent="Math.round((modalState.progress.current / modalState.progress.total) * 100)"-->
<!-- :status="modalState.progress.current === modalState.progress.total ? 'success' : 'active'"-->
<!-- :show-info="false"-->
<!-- style="overflow: hidden;"-->
<!-- />-->
<div style="width: 100%; overflow: hidden;">
<Progress
:percent="Math.round((modalState.progress.current / modalState.progress.total) * 100)"
:status="modalState.progress.current === modalState.progress.total ? 'success' : 'active'"
:show-info="false"
style="width: 100%;"
/>
</div>
</div>
<!-- 操作结果 -->
<div v-if="modalState.results.length > 0" style="margin-top: 20px;">
<a-divider>{{ t('views.ne.neInfo.quickOam.result') }}</a-divider>
<div style="max-height: 200px; overflow-y: auto;">
<!-- 只显示失败的网元 -->
<div
v-for="(result, index) in modalState.results.filter(r => !r.success)"
:key="index"
style="margin-bottom: 8px; padding: 8px; border-radius: 4px;"
:style="{
backgroundColor: '#fff2f0',
border: '1px solid #ffccc7',
}"
>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span>
<CloseCircleOutlined
:style="{ color: '#ff4d4f', marginRight: '8px' }"
/>
{{ result.neType }}-{{ result.neId }} ({{ result.neName }})
</span>
<span :style="{ color: '#ff4d4f', fontSize: '12px' }">
{{ result.message }}
</span>
</div>
</div>
<!-- 如果没有失败的网元,显示提示信息 -->
<div
v-if="modalState.results.filter(r => !r.success).length === 0"
style="text-align: center; padding: 20px; color: #52c41a;"
>
<CheckCircleOutlined :style="{ marginRight: '8px' }" />
{{ t('views.ne.neInfo.quickOam.allsuccess') }}
</div>
</div>
</div>
</ProModal>
</template>
<style lang="less" scoped>
.ant-progress-text {
color: #1890ff;
}
</style>
+114 -151
View File
@@ -1,5 +1,11 @@
<script setup lang="ts">
import { reactive, onMounted, toRaw, defineAsyncComponent, ref } from 'vue';
import {
reactive,
onMounted,
toRaw,
defineAsyncComponent,
ref,
} from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { message, Modal } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider';
@@ -11,6 +17,7 @@ import useNeListStore from '@/store/modules/ne_list';
import { listNeInfo, delNeInfo, stateNeInfo } from '@/api/ne/neInfo';
import useDictStore from '@/store/modules/dict';
import useNeOptions from './hooks/useNeOptions';
import { hasPermissions } from '@/plugins/auth-user';
const { getDict } = useDictStore();
const neListStore = useNeListStore();
const { t } = useI18n();
@@ -33,6 +40,10 @@ const OAMModal = defineAsyncComponent(
const BackConfModal = defineAsyncComponent(
() => import('./components/BackConfModal.vue')
);
// 快速OAM配置
const QuickOAMModal = defineAsyncComponent(
() => import('./components/QuickOAMModal.vue')
);
const backConf = ref(); // 引用句柄,取导出函数
/**字典数据 */
@@ -186,6 +197,8 @@ type ModalStateType = {
openByBackConf: boolean;
/**OAM文件配置框是否显示 */
openByOAM: boolean;
/**快速OAM配置框是否显示 */
openByQuickOAM: boolean;
/**新增框或修改框是否显示 */
openByEdit: boolean;
/**新增框或修改框ID */
@@ -201,6 +214,7 @@ type ModalStateType = {
let modalState: ModalStateType = reactive({
openByBackConf: false,
openByOAM: false,
openByQuickOAM: false,
openByEdit: false,
editId: 0,
neId: '',
@@ -257,8 +271,6 @@ function reloadRowInfo(row: Record<string, any>) {
item.status = '0';
}
Object.assign(item.serverState, res.data);
const resouresUsage = parseResouresUsage(item.serverState);
Reflect.set(item, 'resoures', resouresUsage);
}
})
.finally(() => {
@@ -275,6 +287,7 @@ function fnModalEditCancel() {
modalState.openByEdit = false;
modalState.openByOAM = false;
modalState.openByBackConf = false;
modalState.openByQuickOAM = false;
}
/**
@@ -360,6 +373,9 @@ function fnRecordMore(type: string | number, row: Record<string, any>) {
modalState.neType = row.neType;
modalState.openByBackConf = !modalState.openByBackConf;
break;
case 'quickOAM':
modalState.openByQuickOAM = !modalState.openByQuickOAM;
break;
default:
console.warn(type);
break;
@@ -382,23 +398,7 @@ function fnGetList(pageNum?: number) {
}
const { total, rows } = res.data;
tablePagination.total = total;
// 遍历处理资源情况数值
tableState.data = rows.map((item: any) => {
let resouresUsage = {
sysDiskUsage: 0,
sysMemUsage: 0,
sysCpuUsage: 0,
nfCpuUsage: 0,
};
const neState = item.serverState;
if (neState) {
resouresUsage = parseResouresUsage(neState);
} else {
item.serverState = { online: false };
}
Reflect.set(item, 'resoures', resouresUsage);
return item;
});
tableState.data = rows
}
tableState.loading = false;
})
@@ -423,33 +423,29 @@ onMounted(() => {
<template>
<PageContainer>
<a-card
v-show="tableState.seached"
:bordered="false"
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
>
<a-card v-show="tableState.seached" :bordered="false" :body-style="{ marginBottom: '24px', paddingBottom: 0 }">
<!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item :label="t('views.ne.common.neType')" name="neType ">
<a-auto-complete
v-model:value="queryParams.neType"
:options="neListStore.getNeSelectOtions"
allow-clear
:placeholder="t('common.inputPlease')"
/>
<a-auto-complete v-model:value="queryParams.neType" :options="neListStore.getNeSelectOtions" allow-clear
:placeholder="t('common.inputPlease')" />
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList(1)">
<template #icon><SearchOutlined /></template>
<template #icon>
<SearchOutlined />
</template>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
<template #icon>
<ClearOutlined />
</template>
{{ t('common.reset') }}
</a-button>
</a-space>
@@ -463,18 +459,25 @@ onMounted(() => {
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
<template #icon><PlusOutlined /></template>
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()" v-perms:has="['ne:neInfo:add']">
<template #icon>
<PlusOutlined />
</template>
{{ t('common.addText') }}
</a-button>
<a-button
type="default"
danger
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')"
>
<template #icon><DeleteOutlined /></template>
<a-button type="default" :loading="modalState.confirmLoading" @click.prevent="fnRecordMore('quickOAM', {})"
v-perms:has="['ne:neInfo:oam']">
<template #icon>
<SettingOutlined />
</template>
{{ t('views.ne.neInfo.quickOam.title') }}
</a-button>
<a-button type="default" danger :disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading" @click.prevent="fnRecordDelete('0')"
v-perms:has="['ne:neInfo:delete']">
<template #icon>
<DeleteOutlined />
</template>
{{ t('common.deleteText') }}
</a-button>
</a-space>
@@ -485,30 +488,27 @@ onMounted(() => {
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
v-model:checked="tableState.seached"
:checked-children="t('common.switch.show')"
:un-checked-children="t('common.switch.hide')"
size="small"
/>
<a-switch v-model:checked="tableState.seached" :checked-children="t('common.switch.show')"
:un-checked-children="t('common.switch.hide')" size="small" />
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
<template #icon><ReloadOutlined /></template>
<template #icon>
<ReloadOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.sizeText') }}</template>
<a-dropdown trigger="click" placement="bottomRight">
<a-button type="text">
<template #icon><ColumnHeightOutlined /></template>
<template #icon>
<ColumnHeightOutlined />
</template>
</a-button>
<template #overlay>
<a-menu
:selected-keys="[tableState.size as string]"
@click="fnTableSize"
>
<a-menu :selected-keys="[tableState.size as string]" @click="fnTableSize">
<a-menu-item key="default">
{{ t('common.size.default') }}
</a-menu-item>
@@ -526,22 +526,14 @@ onMounted(() => {
</template>
<!-- 表格列表 -->
<a-table
class="table"
row-key="id"
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: tableColumns.length * 120 }"
:row-selection="{
<a-table class="table" row-key="id" :columns="tableColumns" :loading="tableState.loading"
:data-source="tableState.data" :size="tableState.size" :pagination="tablePagination"
:scroll="{ x: tableColumns.length * 120 }" :row-selection="{
type: 'checkbox',
columnWidth: '48px',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
>
}">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'status'">
<DictTag :options="dict.neInfoStatus" :value="record.status" />
@@ -550,41 +542,42 @@ onMounted(() => {
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.editText') }}</template>
<a-button
type="link"
@click.prevent="fnModalVisibleByEdit(record)"
>
<template #icon><FormOutlined /></template>
<a-button type="link" @click.prevent="fnModalVisibleByEdit(record)" v-perms:has="['ne:neInfo:edit']">
<template #icon>
<FormOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>
{{ t('views.ne.common.restart') }}
</template>
<a-button
type="link"
@click.prevent="fnRecordMore('restart', record)"
>
<template #icon><UndoOutlined /></template>
<a-button type="link" @click.prevent="fnRecordMore('restart', record)"
v-perms:has="['ne:neInfo:restart']">
<template #icon>
<UndoOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip placement="left">
<template #title>{{ t('common.moreText') }}</template>
<a-dropdown placement="bottomRight" trigger="click">
<a-button type="link">
<template #icon><EllipsisOutlined /> </template>
<template #icon>
<EllipsisOutlined />
</template>
</a-button>
<template #overlay>
<a-menu @click="({ key }:any) => fnRecordMore(key, record)">
<a-menu @click="({ key }: any) => fnRecordMore(key, record)">
<a-menu-item key="log">
<FileTextOutlined />
{{ t('views.ne.common.log') }}
</a-menu-item>
<a-menu-item key="start">
<a-menu-item key="start" v-if="hasPermissions(['ne:neInfo:start'])">
<ThunderboltOutlined />
{{ t('views.ne.common.start') }}
</a-menu-item>
<a-menu-item key="stop">
<a-menu-item key="stop" v-if="hasPermissions(['ne:neInfo:stop'])">
<CloseSquareOutlined />
{{ t('views.ne.common.stop') }}
</a-menu-item>
@@ -592,23 +585,23 @@ onMounted(() => {
<SyncOutlined />
{{ t('views.ne.common.reload') }}
</a-menu-item>
<a-menu-item key="delete">
<a-menu-item key="delete" v-if="hasPermissions(['ne:neInfo:delete'])">
<DeleteOutlined />
{{ t('common.deleteText') }}
</a-menu-item>
<a-menu-item
key="oam"
v-if="!['OMC'].includes(record.neType)"
>
<a-menu-item key="oam" v-if="
!['OMC'].includes(record.neType) ||
hasPermissions(['ne:neInfo:oam'])
">
<FileTextOutlined />
{{ t('views.ne.common.oam') }}
</a-menu-item>
<!-- 配置备份 -->
<a-menu-item key="backConfExport">
<a-menu-item key="backConfExport" v-if="hasPermissions(['ne:neInfo:export'])">
<ExportOutlined />
{{ t('views.ne.neInfo.backConf.export') }}
</a-menu-item>
<a-menu-item key="backConfImport">
<a-menu-item key="backConfImport" v-if="hasPermissions(['ne:neInfo:import'])">
<ImportOutlined />
{{ t('views.ne.neInfo.backConf.import') }}
</a-menu-item>
@@ -648,59 +641,39 @@ onMounted(() => {
</a-divider>
<div>
<span>{{ t('views.ne.neInfo.neCpu') }}:</span>
<a-progress
status="normal"
:stroke-color="
record.resoures.nfCpuUsage < 30
? '#52c41a'
: record.resoures.nfCpuUsage > 70
? '#ff4d4f'
: '#1890ff'
"
:percent="record.resoures.nfCpuUsage"
/>
<a-progress status="normal" :stroke-color="record.serverState.nfCpuUsage < 30
? '#52c41a'
: record.serverState.nfCpuUsage > 70
? '#ff4d4f'
: '#1890ff'
" :percent="record.serverState.nfCpuUsage" />
</div>
<div>
<span>{{ t('views.ne.neInfo.sysCpu') }}:</span>
<a-progress
status="normal"
:stroke-color="
record.resoures.sysCpuUsage < 30
? '#52c41a'
: record.resoures.sysCpuUsage > 70
? '#ff4d4f'
: '#1890ff'
"
:percent="record.resoures.sysCpuUsage"
/>
<a-progress status="normal" :stroke-color="record.serverState.sysCpuUsage < 30
? '#52c41a'
: record.serverState.sysCpuUsage > 70
? '#ff4d4f'
: '#1890ff'
" :percent="record.serverState.sysCpuUsage" />
</div>
<div>
<span>{{ t('views.ne.neInfo.sysMem') }}:</span>
<a-progress
status="normal"
:stroke-color="
record.resoures.sysMemUsage < 30
? '#52c41a'
: record.resoures.sysMemUsage > 70
? '#ff4d4f'
: '#1890ff'
"
:percent="record.resoures.sysMemUsage"
/>
<a-progress status="normal" :stroke-color="record.serverState.sysMemUsage < 30
? '#52c41a'
: record.serverState.sysMemUsage > 70
? '#ff4d4f'
: '#1890ff'
" :percent="record.serverState.sysMemUsage" />
</div>
<div>
<span>{{ t('views.ne.neInfo.sysDisk') }}:</span>
<a-progress
status="normal"
:stroke-color="
record.resoures.sysDiskUsage < 30
? '#52c41a'
: record.resoures.sysDiskUsage > 70
? '#ff4d4f'
: '#1890ff'
"
:percent="record.resoures.sysDiskUsage"
/>
<a-progress status="normal" :stroke-color="record.serverState.sysDiskUsage < 30
? '#52c41a'
: record.serverState.sysDiskUsage > 70
? '#ff4d4f'
: '#1890ff'
" :percent="record.serverState.sysDiskUsage" />
</div>
</a-col>
</a-row>
@@ -709,29 +682,19 @@ onMounted(() => {
</a-card>
<!-- 新增框或修改框 -->
<EditModal
v-model:open="modalState.openByEdit"
:edit-id="modalState.editId"
@ok="fnModalEditOk"
@cancel="fnModalEditCancel"
></EditModal>
<EditModal v-model:open="modalState.openByEdit" :edit-id="modalState.editId" @ok="fnModalEditOk"
@cancel="fnModalEditCancel"></EditModal>
<!-- OAM编辑框 -->
<OAMModal
v-model:open="modalState.openByOAM"
:ne-id="modalState.neId"
:ne-type="modalState.neType"
@cancel="fnModalEditCancel"
></OAMModal>
<OAMModal v-model:open="modalState.openByOAM" :ne-id="modalState.neId" :ne-type="modalState.neType"
@cancel="fnModalEditCancel"></OAMModal>
<!-- 配置文件备份框 -->
<BackConfModal
ref="backConf"
v-model:open="modalState.openByBackConf"
:ne-id="modalState.neId"
:ne-type="modalState.neType"
@cancel="fnModalEditCancel"
></BackConfModal>
<BackConfModal ref="backConf" v-model:open="modalState.openByBackConf" :ne-id="modalState.neId"
:ne-type="modalState.neType" @cancel="fnModalEditCancel"></BackConfModal>
<!-- 快速OAM配置框 -->
<QuickOAMModal v-model:open="modalState.openByQuickOAM" @cancel="fnModalEditCancel"></QuickOAMModal>
</PageContainer>
</template>
@@ -0,0 +1,449 @@
<script setup lang="ts">
import { reactive, toRaw, watch, ref } from 'vue';
import { ProModal } from 'antdv-pro-modal';
import { message, Form, Progress, Upload } from 'ant-design-vue/es';
import { UploadRequestOption } from 'ant-design-vue/es/vc-upload/interface';
import { FileType } from 'ant-design-vue/es/upload/interface';
import { UploadOutlined, CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons-vue';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { changeNeLicense, getNeLicenseByTypeAndID } from '@/api/ne/neLicense';
import { uploadFile } from '@/api/tool/file';
import useNeListStore from '@/store/modules/ne_list';
const { t } = useI18n();
const neListStore = useNeListStore();
const emit = defineEmits(['ok', 'cancel', 'update:open']);
const props = defineProps({
open: {
type: Boolean,
default: false,
},
});
/**对话框对象信息状态类型 */
type ModalStateType = {
/**是否显示 */
openByEdit: boolean;
/**标题 */
title: string;
/**表单数据 */
from: {
selectedNeList: string[];
licensePath: string;
remark: string;
};
/**确定按钮 loading */
confirmLoading: boolean;
/**上传文件 */
uploadFiles: any[];
/**进度显示 */
progress: {
visible: boolean;
current: number;
total: number;
currentNe: string;
};
/**操作结果 */
results: Array<{
neType: string;
neId: string;
neName: string;
success: boolean;
message: string;
}>;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
openByEdit: false,
title: t('views.ne.neLicense.quickUpload.title'),
from: {
selectedNeList: [],
licensePath: '',
remark: '',
},
confirmLoading: false,
uploadFiles: [],
progress: {
visible: false,
current: 0,
total: 0,
currentNe: '',
},
results: [],
});
/**对话框内表单属性和校验规则 */
const modalStateFrom = Form.useForm(
modalState.from,
reactive({
selectedNeList: [
{
required: true,
},
],
licensePath: [
{
required: true,
},
],
})
);
/**获取网元列表选项 */
const neOptions = ref<Array<{ label: string; value: string; neType: string; neId: string; neName: string }>>([]);
/**
* 初始化网元选项
*/
async function initNeOptions() {
// 确保网元列表已加载
await neListStore.fnNelistRefresh();
// 从store获取网元列表,过滤掉OMC类型
const neList = neListStore.getNeList.filter((ne: any) => ne.neType !== 'OMC');
neOptions.value = neList.map((ne: any) => ({
label: `${ne.neType}-${ne.neId} (${ne.neName})`,
value: `${ne.neType}@${ne.neId}`,
neType: ne.neType,
neId: ne.neId,
neName: ne.neName,
}));
}
/**
* 单个网元许可证上传(复用EditModal的逻辑)
* @param neType 网元类型
* @param neId 网元ID
* @param licensePath 许可证文件路径
* @param remark 备注
* @returns Promise<{success: boolean, message: string}>
*/
async function uploadSingleNeLicense(neType: string, neId: string, licensePath: string, remark: string = '') {
try {
// 1. 获取网元许可证信息(复用EditModal的获取逻辑)
const getRes = await getNeLicenseByTypeAndID(neType, neId);
if (getRes.code !== RESULT_CODE_SUCCESS) {
return {
success: false,
message: ` ${getRes.msg}`,
};
}
// 2. 构建上传数据(复用EditModal的数据结构)
const uploadData = {
id: getRes.data.id,
neType: neType,
neId: neId,
licensePath: licensePath,
remark: remark,
reload: false, // 网元授权不允许操作重启
};
// 3. 上传许可证(复用EditModal的上传逻辑)
const uploadRes = await changeNeLicense(uploadData);
if (uploadRes.code === RESULT_CODE_SUCCESS) {
return {
success: true,
message: t('views.ne.neInfo.quickOam.success'),
};
} else {
return {
success: false,
message: ` ${uploadRes.msg}`,
};
}
} catch (error: any) {
return {
success: false,
message: ` ${error.message || error}`,
};
}
}
/**
* 对话框弹出确认执行函数
* 进行表达规则校验
*/
async function fnModalOk() {
try {
await modalStateFrom.validate();
modalState.confirmLoading = true;
modalState.progress.visible = true;
modalState.progress.current = 0;
modalState.progress.total = modalState.from.selectedNeList.length;
modalState.results = [];
const { licensePath, remark } = modalState.from;
// 循环处理每个选中的网元
for (let i = 0; i < modalState.from.selectedNeList.length; i++) {
const neInfo = modalState.from.selectedNeList[i];
const [neType, neId] = neInfo.split('@');
const neName = neOptions.value.find(opt => opt.value === neInfo)?.neName || neId;
modalState.progress.current = i + 1;
modalState.progress.currentNe = `${neType}-${neId}`;
// 调用单个网元许可证上传函数
const result = await uploadSingleNeLicense(neType, neId, licensePath, remark);
modalState.results.push({
neType,
neId,
neName,
success: result.success,
message: result.message,
});
}
// 显示操作结果
const successCount = modalState.results.filter(r => r.success).length;
const failCount = modalState.results.length - successCount;
if (failCount === 0) {
message.success(`${t('views.ne.neInfo.quickOam.success')} ${successCount} `, 5);
} else {
message.warning(`${t('views.ne.neInfo.quickOam.success')} ${successCount} ,${t('views.ne.neInfo.quickOam.default')} ${failCount} `, 5);
}
const firstSuccessNe = modalState.results.find(r => r.success);
const paramForMain = firstSuccessNe ? {
neType: firstSuccessNe.neType,
neId: firstSuccessNe.neId
} : {
neType: 'BATCH', // 批量操作
neId: 'OPERATION'
};
emit('ok', paramForMain);
fnModalCancel();
} catch (error: any) {
message.error(t('common.errorFields', { num: error.errorFields?.length || 0 }), 3);
} finally {
modalState.confirmLoading = false;
modalState.progress.visible = false;
}
}
/**
* 对话框弹出关闭执行函数
*/
function fnModalCancel() {
modalState.openByEdit = false;
modalState.confirmLoading = false;
modalState.progress.visible = false;
modalState.progress.current = 0;
modalState.progress.total = 0;
modalState.progress.currentNe = '';
modalState.results = [];
modalStateFrom.resetFields();
modalState.uploadFiles = [];
modalState.from.licensePath = '';
emit('cancel');
emit('update:open', false);
}
/**表单上传前删除 */
function fnBeforeRemoveFile(file: any) {
modalState.from.licensePath = '';
return true;
}
/**表单上传前检查或转换压缩 */
function fnBeforeUploadFile(file: FileType) {
if (modalState.confirmLoading) return false;
if (!file.name.endsWith('.ini')) {
const msg = `${t('components.UploadModal.onlyAllow')} .ini`;
message.error(msg, 3);
return Upload.LIST_IGNORE;
}
const isLt3M = file.size / 1024 / 1024 < 3;
if (!isLt3M) {
const msg = `${t('components.UploadModal.allowFilter')} 3MB`;
message.error(msg, 3);
return Upload.LIST_IGNORE;
}
return true;
}
/**表单上传文件 */
function fnUploadFile(up: UploadRequestOption) {
// 发送请求
const hide = message.loading(t('common.loading'), 0);
modalState.confirmLoading = true;
let formData = new FormData();
formData.append('file', up.file);
formData.append('subPath', 'license');
uploadFile(formData)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
// 改为完成状态
const file = modalState.uploadFiles[0];
file.percent = 100;
file.status = 'done';
// 预置到表单
modalState.from.licensePath = res.data.filePath;
} else {
message.error(res.msg, 3);
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
}
/**监听是否显示,初始数据 */
watch(
() => props.open,
val => {
if (val) {
initNeOptions();
modalState.title = t('views.ne.neLicense.quickUpload.title');
modalState.openByEdit = true;
}
}
);
</script>
<template>
<ProModal
:drag="true"
:destroyOnClose="true"
:body-style="{ maxHeight: '600px', 'overflow-y': 'auto' }"
:keyboard="false"
:mask-closable="false"
:open="modalState.openByEdit"
:title="modalState.title"
:confirm-loading="modalState.confirmLoading"
:width="500"
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form
name="modalStateFrom"
layout="horizontal"
:label-col="{ span: 8 }"
:labelWrap="true"
>
<a-form-item
:label="t('views.ne.neLicense.licensePath')"
name="file"
v-bind="modalStateFrom.validateInfos.licensePath"
>
<a-upload
name="file"
v-model:file-list="modalState.uploadFiles"
accept=".ini"
list-type="text"
:max-count="1"
:show-upload-list="{
showPreviewIcon: false,
showRemoveIcon: true,
showDownloadIcon: false,
}"
@remove="fnBeforeRemoveFile"
:before-upload="fnBeforeUploadFile"
:custom-request="fnUploadFile"
:disabled="modalState.confirmLoading"
>
<a-button type="primary">
<template #icon>
<UploadOutlined />
</template>
{{ t('views.ne.neLicense.upload') }}
</a-button>
</a-upload>
</a-form-item>
<a-form-item :label="t('common.remark')" name="remark">
<a-textarea
v-model:value="modalState.from.remark"
:maxlength="200"
:show-count="true"
:placeholder="t('common.inputPlease')"
/>
</a-form-item>
<a-form-item
:label="t('views.ne.neLicense.quickUpload.selectNe')"
name="selectedNeList"
v-bind="modalStateFrom.validateInfos.selectedNeList"
>
<a-select
v-model:value="modalState.from.selectedNeList"
mode="multiple"
:options="neOptions"
:placeholder="t('common.selectPlease')"
:max-tag-count="3"
style="width: 100%"
/>
</a-form-item>
</a-form>
<!-- 进度显示 -->
<div v-if="modalState.progress.visible || modalState.results.length > 0" style="margin-top: 20px; overflow: hidden;">
<a-divider>{{ t('views.ne.neInfo.quickOam.progress') }}</a-divider>
<div style="margin-bottom: 10px;">
<span>{{ t('views.ne.neInfo.quickOam.processing') }}: {{ modalState.progress.currentNe }}</span>
<span style="float: right;">
{{ modalState.progress.current }} / {{ modalState.progress.total }}
</span>
</div>
<div style="width: 100%; overflow: hidden;">
<Progress
:percent="Math.round((modalState.progress.current / modalState.progress.total) * 100)"
:status="modalState.progress.current === modalState.progress.total ? 'success' : 'active'"
:show-info="false"
style="width: 100%;"
/>
</div>
</div>
<!-- 操作结果 -->
<div v-if="modalState.results.length > 0" style="margin-top: 20px;">
<a-divider>{{ t('views.ne.neInfo.quickOam.result') }}</a-divider>
<div style="max-height: 200px; overflow-y: auto;">
<div
v-for="(result, index) in modalState.results"
:key="index"
style="margin-bottom: 8px; padding: 8px; border-radius: 4px;"
:style="{
backgroundColor: result.success ? '#f6ffed' : '#fff2f0',
border: `1px solid ${result.success ? '#b7eb8f' : '#ffccc7'}`,
}"
>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span>
<CheckCircleOutlined
v-if="result.success"
:style="{ color: '#52c41a', marginRight: '8px' }"
/>
<CloseCircleOutlined
v-else
:style="{ color: '#ff4d4f', marginRight: '8px' }"
/>
{{ result.neType }}-{{ result.neId }} ({{ result.neName }})
</span>
<span :style="{ color: result.success ? '#52c41a' : '#ff4d4f', fontSize: '12px' }">
{{ result.message }}
</span>
</div>
</div>
</div>
</div>
</ProModal>
</template>
<style lang="less" scoped>
.ant-progress-text {
color: #1890ff;
}
</style>
+50 -11
View File
@@ -16,6 +16,10 @@ const neListStore = useNeListStore();
const EditModal = defineAsyncComponent(
() => import('./components/EditModal.vue')
);
// 快速许可证上传
const QuickLicenseModal = defineAsyncComponent(
() => import('./components/QuickLicenseModal.vue')
);
/**字典数据-状态 */
let dictStatus = ref<DictType[]>([]);
@@ -67,12 +71,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns = ref<TableColumnsType>([
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 100,
// },
{
title: t('views.ne.common.neType'),
dataIndex: 'neType',
@@ -105,8 +109,8 @@ let tableColumns = ref<TableColumnsType>([
width: 120,
},
{
title: t('views.ne.common.capability'),
dataIndex: 'capability',
title: t('views.ne.common.ueNumber'),
dataIndex: 'ueNumber',
align: 'left',
customRender(opt) {
if (['UDM', 'AMF', 'MME'].includes(opt.record.neType)) {
@@ -116,6 +120,18 @@ let tableColumns = ref<TableColumnsType>([
},
width: 100,
},
{
title: t('views.ne.common.nbNumber'),
dataIndex: 'nbNumber',
align: 'left',
customRender(opt) {
if (['AMF', 'MME'].includes(opt.record.neType)) {
return opt.value;
}
return '-';
},
width: 100,
},
{
title: t('common.remark'),
dataIndex: 'remark',
@@ -198,7 +214,7 @@ function fnGetList(pageNum?: number) {
tablePagination.total = totalV;
if (
tablePagination.total <=
(queryParams.pageNum - 1) * tablePagination.pageSize &&
(queryParams.pageNum - 1) * tablePagination.pageSize &&
queryParams.pageNum !== 1
) {
tableState.loading = false;
@@ -216,6 +232,8 @@ function fnGetList(pageNum?: number) {
type ModalStateType = {
/**新增框或修改框是否显示 */
openByEdit: boolean;
/**快速许可证上传框是否显示 */
openByQuickUpload: boolean;
/**授权记录ID */
licenseId: number;
/**确定按钮 loading */
@@ -225,6 +243,7 @@ type ModalStateType = {
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
openByEdit: false,
openByQuickUpload: false,
licenseId: 0,
confirmLoading: false,
});
@@ -258,6 +277,7 @@ function fnModalOk(e: any) {
*/
function fnModalCancel() {
modalState.openByEdit = false;
modalState.openByQuickUpload = false;
modalState.licenseId = 0;
}
@@ -276,7 +296,8 @@ function fnRecordState(row: Record<string, any>) {
row.status = '1';
row.serialNum = res.data.sn;
row.expiryDate = res.data.expire;
row.capability = res.data.capability;
row.ueNumber = res.data.ueNumber;
row.nbNumber = res.data.nbNumber;
row.updateTime = new Date().getTime();
message.success(
`${row.neType} ${row.neId} ${dictStatus.value[1].label}`,
@@ -397,10 +418,20 @@ onMounted(() => {
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-button
type="primary"
:loading="modalState.confirmLoading"
@click.prevent="modalState.openByQuickUpload = true"
v-perms:has="['ne:neLicense:upload']"
>
<template #icon><UploadOutlined /></template>
{{ t('views.ne.neLicense.quickUpload.title') }}
</a-button>
<a-button
type="default"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordStateReload()"
v-perms:has="['ne:neLicense:reload']"
>
<template #icon><SyncOutlined /></template>
{{ t('views.ne.neLicense.reloadBatch') }}
@@ -475,7 +506,7 @@ onMounted(() => {
<a-space :size="8" align="center">
<a-tooltip placement="topRight">
<template #title>{{ t('views.ne.neLicense.reload') }}</template>
<a-button type="link" @click.prevent="fnRecordState(record)">
<a-button type="link" @click.prevent="fnRecordState(record)" v-perms:has="['ne:neLicense:sync']">
<template #icon><SyncOutlined /> </template>
</a-button>
</a-tooltip>
@@ -484,6 +515,7 @@ onMounted(() => {
<a-button
type="link"
@click.prevent="fnModalVisibleByEdit(record.id)"
v-perms:has="['ne:neLicense:edit']"
>
<template #icon><UploadOutlined /> </template>
</a-button>
@@ -501,6 +533,13 @@ onMounted(() => {
@ok="fnModalOk"
@cancel="fnModalCancel"
></EditModal>
<!-- 快速许可证上传框 -->
<QuickLicenseModal
v-model:open="modalState.openByQuickUpload"
@ok="fnModalOk"
@cancel="fnModalCancel"
></QuickLicenseModal>
</PageContainer>
</template>
@@ -18,12 +18,12 @@ const EditModal = defineAsyncComponent(
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 50,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 50,
// },
{
title: t('views.ne.common.neType'),
dataIndex: 'neType',
+19 -12
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { reactive, ref, onMounted, toRaw, defineAsyncComponent } from 'vue';
import { reactive, ref, onMounted, toRaw, defineAsyncComponent,computed } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { Modal, TableColumnsType, message } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider';
@@ -11,6 +11,7 @@ import { listNeSoftware, delNeSoftware } from '@/api/ne/neSoftware';
import { parseDateToStr } from '@/utils/date-utils';
import { downloadFile } from '@/api/tool/file';
import { saveAs } from 'file-saver';
import { hasPermissions } from '@/plugins/auth-user';
const neListStore = useNeListStore();
const { t } = useI18n();
@@ -21,7 +22,10 @@ const EditModal = defineAsyncComponent(
const UploadMoreFile = defineAsyncComponent(
() => import('./components/UploadMoreFile.vue')
);
const hasAnyBatchPermission = computed(() => {
return hasPermissions(['ne:neSoftware:download']) ||
hasPermissions(['ne:neSoftware:delete']) ;
});
/**查询参数 */
let queryParams = reactive({
/**网元类型 */
@@ -79,12 +83,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns = ref<TableColumnsType>([
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 100,
// },
{
title: t('views.ne.common.neType'),
dataIndex: 'neType',
@@ -400,7 +404,7 @@ onMounted(() => {
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-button type="primary" @click.prevent="fnModalVisibleByEdit(0)">
<a-button type="primary" @click.prevent="fnModalVisibleByEdit(0)" v-perms:has="['ne:neSoftware:upload']">
<template #icon><UploadOutlined /></template>
{{ t('views.ne.neSoftware.upload') }}
</a-button>
@@ -410,6 +414,7 @@ onMounted(() => {
@click.prevent="
() => (modalState.openByMoreFile = !modalState.openByMoreFile)
"
v-perms:has="['ne:neSoftware:upload']"
>
<template #icon><UploadOutlined /></template>
<template v-if="tableState.selectedRowOne.neType">
@@ -426,6 +431,7 @@ onMounted(() => {
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')"
v-perms:has="['ne:neSoftware:delete']"
>
<template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }}
@@ -521,6 +527,7 @@ onMounted(() => {
<a-button
type="link"
@click.prevent="fnModalVisibleByEdit(record.id)"
v-perms:has="['ne:neSoftware:edit']"
>
<template #icon> <ProfileOutlined /></template>
</a-button>
@@ -528,17 +535,17 @@ onMounted(() => {
<a-tooltip placement="left">
<template #title>{{ t('common.moreText') }}</template>
<a-dropdown placement="bottomRight" trigger="click">
<a-dropdown placement="bottomRight" trigger="click" v-if="hasAnyBatchPermission">
<a-button type="link">
<template #icon><EllipsisOutlined /> </template>
</a-button>
<template #overlay>
<a-menu @click="({ key }:any) => fnRecordMore(key, record)">
<a-menu-item key="download">
<a-menu-item key="download" v-if="hasPermissions(['ne:neSoftware:download'])">
<DownloadOutlined />
{{ t('common.downloadText') }}
</a-menu-item>
<a-menu-item key="delete">
<a-menu-item key="delete" v-if="hasPermissions(['ne:neSoftware:delete'])">
<DeleteOutlined />
{{ t('common.deleteText') }}
</a-menu-item>
+27 -6
View File
@@ -87,12 +87,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns = ref<TableColumnsType>([
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 100,
// },
{
title: t('views.ne.common.neType'),
dataIndex: 'neType',
@@ -108,6 +108,7 @@ let tableColumns = ref<TableColumnsType>([
{
title: t('views.ne.neVersion.version'),
dataIndex: 'version',
key: 'version',
align: 'left',
width: 150,
resizable: true,
@@ -505,6 +506,7 @@ onMounted(() => {
@click.prevent="
() => (modalState.openByEdit = !modalState.openByEdit)
"
v-perms:has="['ne:neVersion:upload']"
>
<template #icon><UploadOutlined /></template>
{{ t('views.ne.neSoftware.upload') }}
@@ -515,6 +517,7 @@ onMounted(() => {
@click.prevent="
() => (modalState.openByMoreFile = !modalState.openByMoreFile)
"
v-perms:has="['ne:neVersion:upload']"
>
<template #icon><UploadOutlined /></template>
<template v-if="tableState.selectedRowOne.neType">
@@ -531,6 +534,7 @@ onMounted(() => {
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordUpgradeConfirm()"
v-perms:has="['ne:neVersion:upgrade']"
>
<template #icon><ThunderboltOutlined /></template>
{{ t('views.ne.neVersion.upgradeBatch') }}
@@ -605,6 +609,21 @@ onMounted(() => {
<template v-if="column.key === 'status'">
<DictTag :options="dictStatus" :value="record.status" />
</template>
<template v-if="column.key === 'version'">
{{ record.version }}
<a-tooltip
placement="topRight"
v-if="
record.version &&
(record.path === '' || record.path === '-')
"
>
<template #title>
{{ t('views.ne.neVersion.noPath') }}
</template>
<InfoCircleOutlined />
</a-tooltip>
</template>
<template v-if="column.key === 'preVersion'">
{{ record.preVersion }}
<a-tooltip
@@ -629,6 +648,7 @@ onMounted(() => {
<a-button
type="link"
@click.prevent="fnRecordVersion('upgrade', record)"
v-perms:has="['ne:neVersion:upgrade']"
>
<template #icon><ThunderboltOutlined /></template>
</a-button>
@@ -640,6 +660,7 @@ onMounted(() => {
<a-button
type="link"
@click.prevent="fnRecordVersion('rollback', record)"
v-perms:has="['ne:neVersion:rollback']"
>
<template #icon><RollbackOutlined /></template>
</a-button>
+16 -9
View File
@@ -21,27 +21,27 @@ let sourceState = reactive({
/**文件列表 */
list: [
{
value: '/auth',
value: '/udm/auth',
label: t('views.neData.backupData.auth'),
path: '/usr/local/omc/backup/udm_data',
path: '/usr/local/omc/backup',
neType: 'UDM',
},
{
value: '/sub',
value: '/udm/sub',
label: t('views.neData.backupData.sub'),
path: '/usr/local/omc/backup/udm_data',
path: '/usr/local/omc/backup',
neType: 'UDM',
},
{
value: '/voip',
value: '/udm/voip',
label: t('views.neData.backupData.voip'),
path: '/usr/local/omc/backup/udm_data',
path: '/usr/local/omc/backup',
neType: 'UDM',
},
{
value: '/volte',
value: '/udm/volte',
label: t('views.neData.backupData.volte'),
path: '/usr/local/omc/backup/udm_data',
path: '/usr/local/omc/backup',
neType: 'UDM+IMS',
},
],
@@ -353,7 +353,11 @@ onMounted(() => {
<template #title>
{{ t('views.ne.neConfigBackup.backupModal.title') }}
</template>
<a-button type="text" @click.prevent="fnFTPModalOpen()">
<a-button
type="text"
@click.prevent="fnFTPModalOpen()"
v-perms:has="['ne-data:backup-data:ftp']"
>
<template #icon><DeliveredProcedureOutlined /></template>
</a-button>
</a-tooltip>
@@ -387,6 +391,7 @@ onMounted(() => {
<a-button
type="link"
@click.prevent="fnSyncFileToFTP(record.fileName)"
v-perms:has="['ne-data:backup-data:ftpSync']"
>
<template #icon><CloudServerOutlined /></template>
</a-button>
@@ -397,6 +402,7 @@ onMounted(() => {
type="link"
:loading="downLoading"
@click.prevent="fnDownloadFile(record)"
v-perms:has="['ne-data:backup-data:download']"
>
<template #icon><DownloadOutlined /></template>
</a-button>
@@ -407,6 +413,7 @@ onMounted(() => {
type="link"
:loading="delLoading"
@click.prevent="fnRecordDelete(record)"
v-perms:has="['ne-data:backup-data:delete']"
>
<template #icon><DeleteOutlined /></template>
</a-button>
@@ -109,12 +109,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns = ref<ColumnsType>([
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 80,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 80,
// },
{
title: t('views.neData.baseStation.name'),
dataIndex: 'name',
@@ -150,6 +150,15 @@ let tableColumns = ref<ColumnsType>([
minWidth: 100,
maxWidth: 200,
},
{
title: t('views.neData.baseStation.nbId'),
dataIndex: 'nbId',
align: 'left',
resizable: true,
width: 100,
minWidth: 100,
maxWidth: 200,
},
{
title: t('views.neData.baseStation.state'),
dataIndex: 'state',
@@ -202,6 +211,8 @@ function fnExportList() {
onOk() {
const hide = message.loading(t('common.loading'), 0);
const querys = toRaw(queryParams);
querys.pageNum = 1;
querys.pageSize = tablePagination.total;
exportNBState(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
@@ -245,7 +256,7 @@ function fnGetList(pageNum?: number) {
}
listNBState(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
if (res.code === RESULT_CODE_SUCCESS) {
// 取消勾选
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
@@ -256,7 +267,7 @@ function fnGetList(pageNum?: number) {
tableState.data = rows;
if (
tablePagination.total <=
(queryParams.pageNum - 1) * tablePagination.pageSize &&
(queryParams.pageNum - 1) * tablePagination.pageSize &&
queryParams.pageNum !== 1
) {
tableState.loading = false;
@@ -288,62 +299,43 @@ watch(
</script>
<template>
<ProModal
:drag="true"
:destroyOnClose="true"
:width="1200"
:title="props.title"
:open="props.open"
:keyboard="false"
:mask-closable="false"
@cancel="fnModalCancel"
:footer="null"
>
<ProModal :drag="true" :destroyOnClose="true" :width="1200" :title="props.title" :open="props.open" :keyboard="false"
:mask-closable="false" @cancel="fnModalCancel" :footer="null">
<!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16" style="margin-left: 0; margin-right: 0">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item
:label="t('views.neData.baseStation.state')"
name="status"
>
<a-select
v-model:value="queryParams.status"
allow-clear
:placeholder="t('common.selectPlease')"
:options="nbStateOptions"
>
<a-form-item :label="t('views.neData.baseStation.state')" name="status">
<a-select v-model:value="queryParams.status" allow-clear :placeholder="t('common.selectPlease')"
:options="nbStateOptions">
</a-select>
</a-form-item>
</a-col>
<a-col :lg="16" :md="12" :xs="24">
<a-form-item
:label="t('views.neData.baseStation.time')"
name="queryRangePicker"
>
<a-range-picker
v-model:value="queryRangePicker"
:bordered="true"
:allow-clear="true"
style="width: 100%"
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
></a-range-picker>
<a-form-item :label="t('views.neData.baseStation.time')" name="queryRangePicker">
<a-range-picker v-model:value="queryRangePicker" :bordered="true" :allow-clear="true" style="width: 100%"
:show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"></a-range-picker>
</a-form-item>
</a-col>
<a-col :lg="24" :md="24" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList(1)">
<template #icon><SearchOutlined /></template>
<template #icon>
<SearchOutlined />
</template>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
<template #icon>
<ClearOutlined />
</template>
{{ t('common.reset') }}
</a-button>
<a-button type="dashed" @click.prevent="fnExportList()">
<template #icon><ExportOutlined /></template>
<template #icon>
<ExportOutlined />
</template>
{{ t('common.export') }}
</a-button>
</a-space>
@@ -353,24 +345,13 @@ watch(
</a-form>
<!-- 表格列表 -->
<a-table
class="table"
row-key="id"
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
<a-table class="table" row-key="id" :columns="tableColumns" :loading="tableState.loading"
:data-source="tableState.data" :size="tableState.size" :pagination="tablePagination"
:scroll="{ x: tableColumns.length * 120, y: 'calc(100vh - 480px)' }"
@resizeColumn="(w:number, col:any) => (col.width = w)"
>
@resizeColumn="(w: number, col: any) => (col.width = w)">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'state'">
<DictTag
:options="nbStateOptions"
:value="record.state"
value-default="OFF"
/>
<DictTag :options="nbStateOptions" :value="record.state" value-default="OFF" />
</template>
</template>
</a-table>
+77 -153
View File
@@ -140,6 +140,12 @@ let tableColumns = ref<ColumnsType>([
minWidth: 100,
maxWidth: 200,
},
{
title: t('views.neData.baseStation.nbId'),
dataIndex: 'ranId',
align: 'left',
width: 120,
},
{
title: t('views.neData.baseStation.ueNum'),
dataIndex: 'ueNum',
@@ -641,13 +647,12 @@ function fnModalImportUpload(file: File) {
/**对话框表格信息导入模板 */
async function fnModalImportTemplate() {
const baseUrl = import.meta.env.VITE_HISTORY_BASE_URL;
const xlsxUrl = `${
baseUrl.length === 1 && baseUrl.indexOf('/') === 0
const xlsxUrl = `${baseUrl.length === 1 && baseUrl.indexOf('/') === 0
? ''
: baseUrl.indexOf('/') === -1
? '/' + baseUrl
: baseUrl
}/nbStateImput`;
? '/' + baseUrl
: baseUrl
}/nbStateImput`;
const lang = currentLocale.value.split('_')[0];
saveAs(
`${xlsxUrl}/${lang}.xlsx`,
@@ -669,7 +674,7 @@ onMounted(() => {
});
return;
}
// 无查询参数neType时 默认选择AMF
const queryNeType = (route.query.neType as string) || 'AMF';
const item = neCascaderOptions.value.find(s => s.value === queryNeType);
@@ -688,46 +693,35 @@ onMounted(() => {
<template>
<div>
<a-card
:bordered="false"
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
>
<a-card :bordered="false" :body-style="{ marginBottom: '24px', paddingBottom: 0 }">
<!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item :label="t('views.ne.common.neType')" name="neType ">
<a-cascader
v-model:value="neTypeAndId"
:options="neCascaderOptions"
:allow-clear="false"
:placeholder="t('common.selectPlease')"
@change="fnGetList"
/>
<a-cascader v-model:value="neTypeAndId" :options="neCascaderOptions" :allow-clear="false"
:placeholder="t('common.selectPlease')" @change="fnGetList" />
</a-form-item>
</a-col>
<a-col :lg="4" :md="6" :xs="24">
<a-form-item
:label="t('views.neData.baseStation.state')"
name="state"
>
<a-select
v-model:value="queryParams.state"
:options="nbState"
:placeholder="t('common.selectPlease')"
@change="fnGetList"
/>
<a-form-item :label="t('views.neData.baseStation.state')" name="state">
<a-select v-model:value="queryParams.state" :options="nbState" :placeholder="t('common.selectPlease')"
@change="fnGetList" />
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList()">
<template #icon><SearchOutlined /></template>
<template #icon>
<SearchOutlined />
</template>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
<template #icon>
<ClearOutlined />
</template>
{{ t('common.reset') }}
</a-button>
</a-space>
@@ -747,35 +741,36 @@ onMounted(() => {
</template>
{{ t('common.addText') }}
</a-button>
<a-button
type="default"
:disabled="tableState.selectedRowKeys.length != 1"
:loading="modalState.confirmLoading"
@click.prevent="fnModalVisibleByEdit('0')"
>
<template #icon><FormOutlined /></template>
<a-button type="default" :disabled="tableState.selectedRowKeys.length != 1"
:loading="modalState.confirmLoading" @click.prevent="fnModalVisibleByEdit('0')">
<template #icon>
<FormOutlined />
</template>
{{ t('common.editText') }}
</a-button>
<a-button
type="default"
danger
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')"
>
<template #icon><DeleteOutlined /></template>
<a-button type="default" danger :disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading" @click.prevent="fnRecordDelete('0')">
<template #icon>
<DeleteOutlined />
</template>
{{ t('common.deleteText') }}
</a-button>
<a-button type="dashed" @click.prevent="fnModalImportOpen()">
<template #icon><ImportOutlined /></template>
<template #icon>
<ImportOutlined />
</template>
{{ t('common.import') }}
</a-button>
<a-button type="dashed" @click.prevent="fnExportList()">
<template #icon><ExportOutlined /></template>
<template #icon>
<ExportOutlined />
</template>
{{ t('common.export') }}
</a-button>
<a-button type="default" @click.prevent="fnHistoryView()">
<template #icon><ContainerOutlined /></template>
<template #icon>
<ContainerOutlined />
</template>
{{ t('views.neData.baseStation.history') }}
</a-button>
</a-space>
@@ -785,19 +780,15 @@ onMounted(() => {
<template #extra>
<a-space :size="8" align="center">
<div>
<template
v-if="
queryParams.state === undefined || queryParams.state === 'ON'
"
>
<template v-if="
queryParams.state === undefined || queryParams.state === 'ON'
">
{{ t('views.neData.baseStation.online') }}:
<strong style="color: green">{{ stateNum[0] }} </strong>
</template>
<template
v-if="
queryParams.state === undefined || queryParams.state === 'OFF'
"
>
<template v-if="
queryParams.state === undefined || queryParams.state === 'OFF'
">
&nbsp;
{{ t('views.neData.baseStation.offline') }}:
<strong style="color: red">
@@ -808,139 +799,72 @@ onMounted(() => {
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
<template #icon><ReloadOutlined /></template>
<template #icon>
<ReloadOutlined />
</template>
</a-button>
</a-tooltip>
</a-space>
</template>
<!-- 表格列表 -->
<a-table
class="table"
row-key="index"
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:row-selection="{
<a-table class="table" row-key="index" :columns="tableColumns" :loading="tableState.loading"
:data-source="tableState.data" :size="tableState.size" :pagination="tablePagination" :row-selection="{
type: 'checkbox',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
:scroll="{ x: tableColumns.length * 120, y: 'calc(100vh - 480px)' }"
@resizeColumn="(w:number, col:any) => (col.width = w)"
>
}" :scroll="{ x: tableColumns.length * 120, y: 'calc(100vh - 480px)' }"
@resizeColumn="(w: number, col: any) => (col.width = w)">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'state'">
<DictTag
:options="nbState"
:value="record.state"
value-default="OFF"
/>
<DictTag :options="nbState" :value="record.state" value-default="OFF" />
</template>
</template>
</a-table>
</a-card>
<!-- 新增框或修改框 -->
<ProModal
:drag="true"
:width="500"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:open="modalState.openByEdit"
:title="modalState.title"
:confirm-loading="modalState.confirmLoading"
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form
name="modalStateFrom"
layout="horizontal"
:label-col="{ span: 6 }"
:labelWrap="true"
>
<a-form-item
:label="t('views.neData.baseStation.name')"
name="name"
v-bind="modalStateFrom.validateInfos.name"
>
<a-input
v-model:value="modalState.from.name"
allow-clear
:maxlength="64"
>
<ProModal :drag="true" :width="500" :destroyOnClose="true" :keyboard="false" :mask-closable="false"
:open="modalState.openByEdit" :title="modalState.title" :confirm-loading="modalState.confirmLoading"
@ok="fnModalOk" @cancel="fnModalCancel">
<a-form name="modalStateFrom" layout="horizontal" :label-col="{ span: 6 }" :labelWrap="true">
<a-form-item :label="t('views.neData.baseStation.name')" name="name" v-bind="modalStateFrom.validateInfos.name">
<a-input v-model:value="modalState.from.name" allow-clear :maxlength="64">
</a-input>
</a-form-item>
<a-form-item
:label="t('views.neData.baseStation.position')"
name="position"
v-bind="modalStateFrom.validateInfos.position"
>
<a-input
v-model:value="modalState.from.position"
allow-clear
:maxlength="64"
>
<a-form-item :label="t('views.neData.baseStation.position')" name="position"
v-bind="modalStateFrom.validateInfos.position">
<a-input v-model:value="modalState.from.position" allow-clear :maxlength="64">
</a-input>
</a-form-item>
<a-form-item
v-if="!modalState.from.state"
:label="t('views.neData.baseStation.address')"
name="address"
v-bind="modalStateFrom.validateInfos.address"
>
<a-input
v-model:value="modalState.from.address"
allow-clear
:maxlength="64"
>
<a-form-item v-if="!modalState.from.state" :label="t('views.neData.baseStation.address')" name="address"
v-bind="modalStateFrom.validateInfos.address">
<a-input v-model:value="modalState.from.address" allow-clear :maxlength="64">
</a-input>
</a-form-item>
</a-form>
</ProModal>
<!-- 状态历史框 -->
<HistoryModal
v-if="neTypeAndId.length > 1"
v-model:open="modalState.openByHistory"
:title="t('views.neData.baseStation.history')"
:ne-type="neTypeAndId[0]"
:ne-id="neTypeAndId[1]"
@cancel="fnModalCancel"
></HistoryModal>
<HistoryModal v-if="neTypeAndId.length > 1" v-model:open="modalState.openByHistory"
:title="t('views.neData.baseStation.history')" :ne-type="neTypeAndId[0]" :ne-id="neTypeAndId[1]"
@cancel="fnModalCancel"></HistoryModal>
<!-- 上传导入表格数据文件框 -->
<UploadModal
:title="t('common.import')"
@upload="fnModalImportUpload"
@close="fnModalImportClose"
v-model:open="modalState.openByImport"
:ext="['.xls', '.xlsx']"
:size="10"
>
<UploadModal :title="t('common.import')" @upload="fnModalImportUpload" @close="fnModalImportClose"
v-model:open="modalState.openByImport" :ext="['.xls', '.xlsx']" :size="10">
<template #default>
<a-row justify="space-between" align="middle">
<a-col :span="12"> </a-col>
<a-col :span="6">
<a-button
type="link"
:title="t('views.system.user.downloadObj')"
@click.prevent="fnModalImportTemplate"
>
<a-button type="link" :title="t('views.system.user.downloadObj')" @click.prevent="fnModalImportTemplate">
{{ t('views.system.user.downloadObj') }}
</a-button>
</a-col>
</a-row>
<a-textarea
:disabled="true"
:hidden="modalState.importMsgArr.length <= 0"
:value="modalState.importMsgArr.join('\r\n')"
:auto-size="{ minRows: 2, maxRows: 8 }"
style="background-color: transparent; color: rgba(0, 0, 0, 0.85)"
/>
<a-textarea :disabled="true" :hidden="modalState.importMsgArr.length <= 0"
:value="modalState.importMsgArr.join('\r\n')" :auto-size="{ minRows: 2, maxRows: 8 }"
style="background-color: transparent; color: rgba(0, 0, 0, 0.85)" />
</template>
</UploadModal>
</div>
+11 -11
View File
@@ -132,10 +132,9 @@ const graphNodeTooltip = new Tooltip({
"
>
<div><strong>${t('views.neData.baseStation.state')}:</strong><span>
${
nInfo.state === 'ON'
? t('views.neData.baseStation.online')
: t('views.neData.baseStation.offline')
${nInfo.state === 'ON'
? t('views.neData.baseStation.online')
: t('views.neData.baseStation.offline')
}
</span></div>
<div><strong>${t('views.neData.baseStation.time')}:</strong><span>
@@ -147,6 +146,8 @@ const graphNodeTooltip = new Tooltip({
${nInfo.address ?? '--'}</span></div>
<div><strong>${t('views.neData.baseStation.nbName')}:</strong><span>
${nInfo.nbName ?? '--'}</span></div>
<div><strong>${t('views.neData.baseStation.nbId')}:</strong><span>
${nInfo.ranId ?? '--'}</span></div>
<div><strong>${t('views.neData.baseStation.ueNum')}:</strong><span>
${nInfo.ueNum ?? '--'}</span></div>
<div><strong>${t('views.neData.baseStation.name')}:</strong><span>
@@ -169,11 +170,10 @@ const graphNodeTooltip = new Tooltip({
"
>
<div><strong>${t('views.monitor.topology.state')}:</strong><span>
${
nInfo.online
? t('views.monitor.topology.normalcy')
: t('views.monitor.topology.exceptions')
}
${nInfo.online
? t('views.monitor.topology.normalcy')
: t('views.monitor.topology.exceptions')
}
</span></div>
<div><strong>${t('views.monitor.topology.refreshTime')}:</strong><span>
${nInfo.refreshTime ?? '--'}
@@ -359,7 +359,7 @@ function handleRanderGraph(container: HTMLElement | null, data: GraphData) {
/**
* 获取图组数据渲染到画布
*/
async function fnGraphDataLoad() {
async function fnGraphDataLoad() {
const dataNe = await fnGraphDataBase();
Object.assign(graphData, dataNe);
graphG6.value = handleRanderGraph(graphG6Dom.value, dataNe);
@@ -417,7 +417,7 @@ async function fnGraphDataBase() {
target: id,
});
}
item.children.forEach((v: any) => {});
item.children.forEach((v: any) => { });
continue;
}
}
+58 -5
View File
@@ -23,9 +23,11 @@ import {
importUDMAuth,
resetUDMAuth,
listUDMAuth,
exportUDMDecAuth,
} from '@/api/neData/udm_auth';
import { uploadFile } from '@/api/tool/file';
import { getNeViewFile } from '@/api/tool/neFile';
import { parseDateToStr } from '@/utils/date-utils';
const { t } = useI18n();
const neListStore = useNeListStore();
/**网元参数 */
@@ -117,6 +119,16 @@ let tableColumns = ref<ColumnsType>([
align: 'center',
width: 100,
},
{
title: 'Create Time',
dataIndex: 'createTime',
align: 'left',
width: 250,
customRender(opt) {
if (!opt.value) return '';
return parseDateToStr(+opt.value);
},
},
{
title: t('common.operate'),
key: 'imsi',
@@ -505,6 +517,8 @@ function fnExportList(type: string) {
const neId = queryParams.neId;
if (!neId) return;
const hide = message.loading(t('common.loading'), 0);
queryParams.pageNum = 1;
queryParams.pageSize = tablePagination.total;
exportUDMAuth(Object.assign({ type: type }, queryParams))
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
@@ -519,6 +533,25 @@ function fnExportList(type: string) {
});
}
/** UDM鉴权用户导出DecAuth */
function fnExportDec() {
const neId = queryParams.neId;
if (!neId) return;
const hide = message.loading(t('common.loading'), 0);
exportUDMDecAuth(neId)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.msgSuccess', { msg: t('common.export') }), 3);
saveAs(res.data, `UDMAuth_dec_${Date.now()}.txt`);
} else {
message.error(`${res.msg}`, 3);
}
})
.finally(() => {
hide();
});
}
/**重新加载数据 */
function fnLoadData() {
const neId = queryParams.neId;
@@ -817,7 +850,11 @@ onMounted(() => {
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-flex wrap="wrap" gap="small">
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
<a-button
type="primary"
@click.prevent="fnModalVisibleByEdit()"
v-perms:has="['neData:udm-auth:add']"
>
<template #icon>
<PlusOutlined />
</template>
@@ -829,6 +866,7 @@ onMounted(() => {
danger
ghost
@click.prevent="fnModalVisibleByBatch()"
v-perms:has="['neData:udm-auth:delete']"
>
<template #icon>
<DeleteOutlined />
@@ -848,13 +886,18 @@ onMounted(() => {
danger
:disabled="modalState.loadDataLoading"
:loading="modalState.loadDataLoading"
v-perms:has="['neData:udm-auth:reload']"
>
<template #icon><SyncOutlined /></template>
{{ t('views.neUser.auth.loadData') }}
</a-button>
</a-popconfirm>
<a-button type="dashed" @click.prevent="fnModalUploadImportOpen">
<a-button
type="dashed"
@click.prevent="fnModalUploadImportOpen"
v-perms:has="['neData:udm-auth:import']"
>
<template #icon><ImportOutlined /></template>
{{ t('views.neUser.auth.import') }}
</a-button>
@@ -865,13 +908,20 @@ onMounted(() => {
ok-text="TXT"
ok-type="default"
@confirm="fnExportList('txt')"
v-if="false"
>
<a-button type="dashed">
<a-button type="dashed" v-perms:has="['neData:udm-auth:export']">
<template #icon><ExportOutlined /></template>
{{ t('views.neUser.auth.export') }}
</a-button>
</a-popconfirm>
<a-button
type="dashed"
@click="fnExportDec()"
v-perms:has="['neData:udm-auth:export-dec']"
>
<template #icon><ExportOutlined /></template>
Export Dec
</a-button>
<a-button
type="default"
@@ -879,6 +929,7 @@ onMounted(() => {
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.loadDataLoading"
@click.prevent="fnRecordDelete('0')"
v-perms:has="['neData:udm-auth:delete']"
>
<template #icon><DeleteOutlined /></template>
{{ t('views.neUser.auth.checkDel') }}
@@ -891,11 +942,11 @@ onMounted(() => {
ok-type="default"
@confirm="fnRecordExport('txt')"
:disabled="tableState.selectedRowKeys.length <= 0"
v-if="false"
>
<a-button
type="default"
:disabled="tableState.selectedRowKeys.length <= 0"
v-perms:has="['neData:udm-auth:export']"
>
<template #icon><ExportOutlined /></template>
{{ t('views.neUser.auth.checkExport') }}
@@ -980,6 +1031,7 @@ onMounted(() => {
<a-button
type="link"
@click.prevent="fnModalVisibleByEdit(record)"
v-perms:has="['neData:udm-auth:edit']"
>
<template #icon>
<FormOutlined />
@@ -991,6 +1043,7 @@ onMounted(() => {
<a-button
type="link"
@click.prevent="fnRecordDelete(record.imsi)"
v-perms:has="['neData:udm-auth:delete']"
>
<template #icon>
<DeleteOutlined />
+30 -3
View File
@@ -26,6 +26,7 @@ import {
} from '@/api/neData/udm_sub';
import { uploadFile } from '@/api/tool/file';
import { getNeViewFile } from '@/api/tool/neFile';
import { parseDateToStr } from '@/utils/date-utils';
const { t } = useI18n();
const neListStore = useNeListStore();
/**网元参数 */
@@ -155,6 +156,16 @@ let tableColumns = ref<ColumnsType>([
minWidth: 150,
maxWidth: 500,
},
{
title: 'Create Time',
dataIndex: 'createTime',
align: 'left',
width: 250,
customRender(opt) {
if (!opt.value) return '';
return parseDateToStr(+opt.value);
},
},
{
title: t('common.operate'),
key: 'imsi',
@@ -875,6 +886,8 @@ function fnExportList(type: string) {
const neId = queryParams.neId;
if (!neId) return;
const hide = message.loading(t('common.loading'), 0);
queryParams.pageNum = 1;
queryParams.pageSize = tablePagination.total;
exportUDMSub(Object.assign({ type: type }, queryParams))
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
@@ -1218,7 +1231,11 @@ onMounted(() => {
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-flex wrap="wrap" gap="small">
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
<a-button
type="primary"
@click.prevent="fnModalVisibleByEdit()"
v-perms:has="['neData:udm-sub:add']"
>
<template #icon>
<PlusOutlined />
</template>
@@ -1230,6 +1247,7 @@ onMounted(() => {
danger
ghost
@click.prevent="fnModalVisibleByBatch()"
v-perms:has="['neData:udm-sub:delete']"
>
<template #icon>
<DeleteOutlined />
@@ -1249,6 +1267,7 @@ onMounted(() => {
danger
:disabled="modalState.loadDataLoading"
:loading="modalState.loadDataLoading"
v-perms:has="['neData:udm-sub:reload']"
>
<template #icon>
<SyncOutlined />
@@ -1257,7 +1276,11 @@ onMounted(() => {
</a-button>
</a-popconfirm>
<a-button type="dashed" @click.prevent="fnModalUploadImportOpen">
<a-button
type="dashed"
@click.prevent="fnModalUploadImportOpen"
v-perms:has="['neData:udm-sub:import']"
>
<template #icon>
<ImportOutlined />
</template>
@@ -1271,7 +1294,7 @@ onMounted(() => {
ok-type="default"
@confirm="fnExportList('txt')"
>
<a-button type="dashed">
<a-button type="dashed" v-perms:has="['neData:udm-sub:export']">
<template #icon>
<ExportOutlined />
</template>
@@ -1285,6 +1308,7 @@ onMounted(() => {
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.loadDataLoading"
@click.prevent="fnRecordDelete('0')"
v-perms:has="['neData:udm-sub:delete']"
>
<template #icon>
<DeleteOutlined />
@@ -1303,6 +1327,7 @@ onMounted(() => {
<a-button
type="default"
:disabled="tableState.selectedRowKeys.length <= 0"
v-perms:has="['neData:udm-sub:export']"
>
<template #icon>
<ExportOutlined />
@@ -1408,6 +1433,7 @@ onMounted(() => {
<a-button
type="link"
@click.prevent="fnModalVisibleByEdit(record.imsi)"
v-perms:has="['neData:udm-sub:edit']"
>
<template #icon>
<FormOutlined />
@@ -1419,6 +1445,7 @@ onMounted(() => {
<a-button
type="link"
@click.prevent="fnRecordDelete(record.imsi)"
v-perms:has="['neData:udm-sub:delete']"
>
<template #icon>
<DeleteOutlined />
+45 -7
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { reactive, ref, onMounted, toRaw } from 'vue';
import { reactive, ref, onMounted, toRaw, computed } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { ProModal } from 'antdv-pro-modal';
import {
@@ -28,6 +28,8 @@ import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { saveAs } from 'file-saver';
import { uploadFile } from '@/api/tool/file';
import { getNeViewFile } from '@/api/tool/neFile';
import { hasPermissions } from '@/plugins/auth-user';
import { parseDateToStr } from '@/utils/date-utils';
const { t } = useI18n();
const neListStore = useNeListStore();
/**网元参数 */
@@ -49,6 +51,13 @@ let queryParams = reactive({
pageSize: 20,
});
const hasAnyBatchPermission = computed(() => {
return (
hasPermissions(['neData:udm-voip:add']) ||
hasPermissions(['neData:udm-voip:delete'])
);
});
/**查询参数重置 */
function fnQueryReset() {
queryParams = Object.assign(queryParams, {
@@ -94,6 +103,16 @@ let tableColumns = ref<TableColumnsType>([
minWidth: 100,
maxWidth: 300,
},
{
title: 'Create Time',
dataIndex: 'createTime',
align: 'left',
width: 250,
customRender(opt) {
if (!opt.value) return '';
return parseDateToStr(+opt.value);
},
},
{
title: t('common.operate'),
key: 'id',
@@ -363,6 +382,8 @@ function fnExportList(type: string) {
const neId = queryParams.neId;
if (!neId) return;
const hide = message.loading(t('common.loading'), 0);
queryParams.pageNum = 1;
queryParams.pageSize = tablePagination.total;
exportUDMVOIP(Object.assign({ type: type }, queryParams))
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
@@ -659,7 +680,11 @@ onMounted(() => {
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
<a-button
type="primary"
@click.prevent="fnModalVisibleByEdit()"
v-perms:has="['neData:udm-voip:add']"
>
<template #icon>
<PlusOutlined />
</template>
@@ -672,23 +697,30 @@ onMounted(() => {
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')"
v-perms:has="['neData:udm-voip:delete']"
>
<template #icon><DeleteOutlined /></template>
{{ t('views.neData.common.checkDel') }}
</a-button>
<a-dropdown trigger="click">
<a-dropdown trigger="click" v-if="hasAnyBatchPermission">
<a-button>
{{ t('views.neData.common.batchOper') }}
<DownOutlined />
</a-button>
<template #overlay>
<a-menu @click="({ key }:any) => fnModalVisibleByBatch(key)">
<a-menu-item key="add">
<a-menu-item
key="add"
v-if="hasPermissions(['neData:udm-voip:add'])"
>
<PlusOutlined />
{{ t('views.neData.common.batchAddText') }}
</a-menu-item>
<a-menu-item key="delete">
<a-menu-item
key="delete"
v-if="hasPermissions(['neData:udm-voip:delete'])"
>
<DeleteOutlined />
{{ t('views.neData.common.batchDelText') }}
</a-menu-item>
@@ -709,13 +741,18 @@ onMounted(() => {
danger
:disabled="modalState.loadDataLoading"
:loading="modalState.loadDataLoading"
v-perms:has="['neData:udm-voip:reload']"
>
<template #icon><SyncOutlined /></template>
{{ t('views.neData.common.loadData') }}
</a-button>
</a-popconfirm>
<a-button type="dashed" @click.prevent="fnModalUploadImportOpen">
<a-button
type="dashed"
@click.prevent="fnModalUploadImportOpen"
v-perms:has="['neData:udm-voip:import']"
>
<template #icon><ImportOutlined /></template>
{{ t('common.import') }}
</a-button>
@@ -726,7 +763,7 @@ onMounted(() => {
ok-type="default"
@confirm="fnExportList('txt')"
>
<a-button type="dashed">
<a-button type="dashed" v-perms:has="['neData:udm-voip:export']">
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
@@ -816,6 +853,7 @@ onMounted(() => {
<a-button
type="link"
@click.prevent="fnRecordDelete(record.username)"
v-perms:has="['neData:udm-voip:delete']"
>
<template #icon>
<DeleteOutlined />
+45 -8
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { reactive, ref, onMounted, toRaw } from 'vue';
import { reactive, ref, onMounted, toRaw, computed } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { ProModal } from 'antdv-pro-modal';
import {
@@ -28,8 +28,16 @@ import {
listUDMVolteIMS,
resetUDMVolteIMS,
} from '@/api/neData/udm_volte_ims';
import { hasPermissions } from '@/plugins/auth-user';
import { parseDateToStr } from '@/utils/date-utils';
const { t } = useI18n();
const neListStore = useNeListStore();
const hasAnyBatchPermission = computed(() => {
return (
hasPermissions(['neData:udm-volte:add']) ||
hasPermissions(['neData:udm-volte:delete'])
);
});
/**字典数据 */
let dict: {
/**Tag标签类型 0=VoIP, 1=VoLTE */
@@ -145,6 +153,16 @@ let tableColumns = ref<TableColumnsType>([
minWidth: 250,
maxWidth: 400,
},
{
title: 'Create Time',
dataIndex: 'createTime',
align: 'left',
width: 250,
customRender(opt) {
if (!opt.value) return '';
return parseDateToStr(+opt.value);
},
},
{
title: t('common.operate'),
key: 'id',
@@ -442,6 +460,8 @@ function fnExportList(type: string) {
const neId = queryParams.neId;
if (!neId) return;
const hide = message.loading(t('common.loading'), 0);
queryParams.pageNum = 1;
queryParams.pageSize = tablePagination.total;
exportUDMVolteIMS(Object.assign({ type: type }, queryParams))
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
@@ -449,7 +469,7 @@ function fnExportList(type: string) {
content: t('common.operateOk'),
duration: 2,
});
saveAs(res.data, `UDM_VOLTE_${neId}_${Date.now()}.${type}`);
saveAs(res.data, `UDM_VoLTE_${neId}_${Date.now()}.${type}`);
} else {
message.error({
content: `${res.msg}`,
@@ -755,7 +775,11 @@ onMounted(() => {
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
<a-button
type="primary"
@click.prevent="fnModalVisibleByEdit()"
v-perms:has="['neData:udm-volte:add']"
>
<template #icon>
<PlusOutlined />
</template>
@@ -768,23 +792,30 @@ onMounted(() => {
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')"
v-perms:has="['neData:udm-volte:delete']"
>
<template #icon><DeleteOutlined /></template>
{{ t('views.neData.common.checkDel') }}
</a-button>
<a-dropdown trigger="click">
<a-dropdown trigger="click" v-if="hasAnyBatchPermission">
<a-button>
{{ t('views.neData.common.batchOper') }}
<DownOutlined />
</a-button>
<template #overlay>
<a-menu @click="({ key }:any) => fnModalVisibleByBatch(key)">
<a-menu-item key="add">
<a-menu-item
key="add"
v-if="hasPermissions(['neData:udm-volte:add'])"
>
<PlusOutlined />
{{ t('views.neData.common.batchAddText') }}
</a-menu-item>
<a-menu-item key="delete">
<a-menu-item
key="delete"
v-if="hasPermissions(['neData:udm-volte:delete'])"
>
<DeleteOutlined />
{{ t('views.neData.common.batchDelText') }}
</a-menu-item>
@@ -805,13 +836,18 @@ onMounted(() => {
danger
:disabled="modalState.loadDataLoading"
:loading="modalState.loadDataLoading"
v-perms:has="['neData:udm-volte:reload']"
>
<template #icon><SyncOutlined /></template>
{{ t('views.neData.common.loadData') }}
</a-button>
</a-popconfirm>
<a-button type="dashed" @click.prevent="fnModalUploadImportOpen">
<a-button
type="dashed"
@click.prevent="fnModalUploadImportOpen"
v-perms:has="['neData:udm-volte:import']"
>
<template #icon><ImportOutlined /></template>
{{ t('common.import') }}
</a-button>
@@ -822,7 +858,7 @@ onMounted(() => {
ok-type="default"
@confirm="fnExportList('txt')"
>
<a-button type="dashed">
<a-button type="dashed" v-perms:has="['neData:udm-volte:export']">
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
@@ -915,6 +951,7 @@ onMounted(() => {
<a-button
type="link"
@click.prevent="fnRecordDelete(record.id)"
v-perms:has="['neData:udm-volte:delete']"
>
<template #icon>
<DeleteOutlined />
+6 -6
View File
@@ -80,12 +80,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns = ref<ColumnsType>([
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 100,
// },
{
title: 'NE Type',
dataIndex: 'neType',
File diff suppressed because it is too large. Load diff
+5 -5
View File
@@ -77,11 +77,11 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'center',
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'center',
// },
{
title: t('views.perfManage.taskManage.neType'),
dataIndex: 'neType',
+9 -8
View File
@@ -21,6 +21,7 @@ import useDictStore from '@/store/modules/dict';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useI18n from '@/hooks/useI18n';
import type { Dayjs } from 'dayjs';
import { hasPermissions } from '@/plugins/auth-user';
const { t } = useI18n();
const { getDict } = useDictStore();
@@ -95,12 +96,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns = ref<ColumnsType>([
{
title: t('common.rowId'),
dataIndex: 'configId',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'configId',
// align: 'left',
// width: 100,
// },
{
title: t('views.system.config.configName'),
dataIndex: 'configName',
@@ -665,11 +666,11 @@ onMounted(() => {
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: tableColumns.length * 120 }"
:row-selection="{
:row-selection="hasPermissions(['system:config:remove']) ? {
type: 'checkbox',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
}: undefined"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'configType'">
+6 -6
View File
@@ -83,12 +83,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns = ref<ColumnsType>([
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 100,
// },
{
title: t('views.system.loginSource.uid'),
dataIndex: 'uid',
@@ -0,0 +1,140 @@
<script lang="ts" setup>
import { message, Modal } from 'ant-design-vue/es';
import { reactive } from 'vue';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { FileType } from 'ant-design-vue/es/upload/interface';
import { UploadRequestOption } from 'ant-design-vue/es/vc-upload/interface';
import { uploadFileChunk } from '@/api/tool/file';
import { exportBackupOMC, importBackupOMC } from '@/api/neData/backup';
import saveAs from 'file-saver';
const { t } = useI18n();
type StateType = {
loading: boolean;
};
let state: StateType = reactive({
loading: false,
});
/**导出 */
function fnExport() {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.system.setting.backupExportTip'),
onOk() {
const hide = message.loading(t('common.loading'), 0);
exportBackupOMC()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
saveAs(res.data, `BackupOMC-${Date.now()}.zip`);
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
});
},
});
}
/**上传前检查或转换压缩 */
function fnBeforeUpload(file: FileType) {
if (state.loading) return false;
console.log(file);
if (file.type !== 'application/x-zip-compressed') {
message.error(
t('views.system.setting.uploadFormat', { format: 'zip' }),
3
);
}
// const isLt10M = file.size / 1024 / 1024 < 10;
// if (!isLt10M) {
// message.error(t('views.system.setting.uploadSize', { size: 10 }), 3);
// }
return true;
}
/**上传变更 */
function fnUpload(up: UploadRequestOption) {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.system.setting.backupImportTip'),
onOk() {
// 发送请求
const hide = message.loading(t('common.loading'), 0);
state.loading = true;
uploadFileChunk(up.file as File, 5, 'import')
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
return importBackupOMC(res.data.filePath);
}
return undefined;
})
.then(res => {
if (!res) {
return;
}
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
state.loading = false;
hide();
});
},
});
}
</script>
<template>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-button type="default" @click="fnExport()">
{{ t('common.export') }}
</a-button>
<a-upload
name="file"
list-type="picture"
accept=".zip"
:max-count="1"
:show-upload-list="false"
:before-upload="fnBeforeUpload"
:custom-request="fnUpload"
style="margin-left: 10px"
>
<a-button type="link" :disabled="state.loading">
{{ t('common.import') }}
</a-button>
</a-upload>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-typography>
<a-typography-paragraph>
{{ t('views.system.setting.backupInstruction') }}
</a-typography-paragraph>
</a-typography>
</a-col>
</a-row>
</template>
<style lang="less" scoped></style>
+19 -8
View File
@@ -8,6 +8,7 @@ import ChangeHelpDoc from './components/change-help-doc.vue';
import ChangeOfficialUrl from './components/change-official-url.vue';
import ChangeI18n from './components/change-i18n.vue';
import SystemReset from './components/system-reset.vue';
import SystemBackup from './components/system-backup.vue';
import ChangeHome from './components/change-home-index.vue';
import useI18n from '@/hooks/useI18n';
const { t } = useI18n();
@@ -50,14 +51,24 @@ const { t } = useI18n();
</a-divider>
<ChangeI18n></ChangeI18n>
</div>
<a-divider orientation="left">
{{ t('views.system.setting.homeSet') }}
</a-divider>
<ChangeHome></ChangeHome>
<a-divider orientation="left">
{{ t('views.system.setting.reset') }}
</a-divider>
<SystemReset></SystemReset>
<div v-perms:has="['system:setting:homeSet']">
<a-divider orientation="left">
{{ t('views.system.setting.homeSet') }}
</a-divider>
<ChangeHome></ChangeHome>
</div>
<div v-perms:has="['system:setting:backup']">
<a-divider orientation="left">
{{ t('views.system.setting.backup') }}
</a-divider>
<SystemBackup></SystemBackup>
</div>
<div v-perms:has="['system:setting:reset']">
<a-divider orientation="left">
{{ t('views.system.setting.reset') }}
</a-divider>
<SystemReset></SystemReset>
</div>
</a-card>
</PageContainer>
</template>
@@ -65,12 +65,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'center',
width: 50,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'center',
// width: 50,
// },
{
title: t('views.ne.neHost.hostType'),
dataIndex: 'hostType',
+5 -2
View File
@@ -593,13 +593,13 @@ onMounted(() => {
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-button @click="fnFileView()">
<a-button @click="fnFileView()" v-perms:has="['traceManage:pcap:fileview']">
<FileSearchOutlined />
{{ t('views.traceManage.pcap.fileView') }}
</a-button>
<a-dropdown trigger="click">
<a-button :disabled="tableState.selectedRowKeys.length <= 0">
<a-button :disabled="tableState.selectedRowKeys.length <= 0" >
{{ t('views.traceManage.pcap.batchOper') }}
<DownOutlined />
</a-button>
@@ -678,6 +678,7 @@ onMounted(() => {
size="small"
:disabled="modalState.from[record.id].loading"
@click.prevent="fnRecordStart(record)"
v-perms:has="['traceManage:pcap:start']"
>
<template #icon><PlayCircleOutlined /> </template>
</a-button>
@@ -697,6 +698,7 @@ onMounted(() => {
danger
size="small"
@click.prevent="fnRecordStop(record)"
v-perms:has="['traceManage:pcap:stop']"
>
<template #icon><StopOutlined /> </template>
</a-button>
@@ -736,6 +738,7 @@ onMounted(() => {
ghost
size="small"
@click.prevent="fnDownPCAP(record)"
v-perms:has="['traceManage:pcap:download']"
>
<template #icon><DownloadOutlined /> </template>
</a-button>
+6 -6
View File
@@ -83,12 +83,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = reactive([
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'right',
width: 50,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'right',
// width: 50,
// },
{
title: t('views.traceManage.task.imsi'),
dataIndex: 'imsi',
+6 -6
View File
@@ -95,12 +95,12 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
// {
// title: t('common.rowId'),
// dataIndex: 'id',
// align: 'left',
// width: 100,
// },
{
title: t('views.traceManage.task.msgNe'),
dataIndex: 'msgNe',
+6 -1
View File
@@ -701,7 +701,7 @@ onMounted(() => {
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-button type="primary" @click.prevent="fnModalOpenByEdit()">
<a-button type="primary" @click.prevent="fnModalOpenByEdit()" v-perms:has="['traceManage:task:add']">
<template #icon><PlusOutlined /></template>
{{ t('common.addText') }}
</a-button>
@@ -712,6 +712,7 @@ onMounted(() => {
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')"
v-perms:has="['traceManage:task:delete']"
>
<template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }}
@@ -796,6 +797,7 @@ onMounted(() => {
<a-button
type="link"
@click.prevent="fnRecordView(record, 'data')"
v-perms:has="['traceManage:task:dataview']"
>
<template #icon><ContainerOutlined /></template>
</a-button>
@@ -809,6 +811,7 @@ onMounted(() => {
<a-button
type="link"
@click.prevent="fnRecordView(record, 'analyze')"
v-perms:has="['traceManage:task:pcapview']"
>
<template #icon><BarsOutlined /></template>
</a-button>
@@ -820,6 +823,7 @@ onMounted(() => {
<a-button
type="link"
@click.prevent="fnModalOpenByEdit(record.id)"
v-perms:has="['traceManage:task:edit']"
>
<template #icon><ProfileOutlined /></template>
</a-button>
@@ -830,6 +834,7 @@ onMounted(() => {
type="link"
danger
@click.prevent="fnRecordDelete(record.id)"
v-perms:has="['traceManage:task:delete']"
>
<template #icon><DeleteOutlined /></template>
</a-button>
+3 -1
View File
@@ -582,6 +582,7 @@ onBeforeUnmount(() => {
type="primary"
@click="fnStart"
:disabled="!taskState.stop && taskState.task.taskNo !== ''"
v-perms:has="['traceManage:wireshark:start']"
>
<PlayCircleOutlined />
Start Trace
@@ -610,6 +611,7 @@ onBeforeUnmount(() => {
danger
@click.prevent="fnStop()"
:disabled="taskState.stop || taskState.task.taskNo === ''"
v-perms:has="['traceManage:wireshark:stop']"
>
<template #icon><CloseCircleOutlined /></template>
Stop Trace
@@ -701,7 +703,7 @@ onBeforeUnmount(() => {
:data-source="tableState.data"
size="small"
:pagination="false"
:row-class-name="(record:any) => {
:row-class-name="(record:any) => {
return `table-striped-${record.protocol}`
}"
:customRow="