60 Commits

Author SHA1 Message Date
TsMask
3dac294b48 chore: 更新版本号 2.241123-fix 2025-03-08 10:33:09 +08:00
TsMask
20007f4732 feat: 添加导出所有学生配置功能,优化提示信息 2025-03-08 10:31:54 +08:00
TsMask
0cb892e7f3 chore: 更新版本号 2.241123 2024-11-23 16:43:37 +08:00
TsMask
88d4b0cbb6 style: 修复样式 2024-11-23 16:17:27 +08:00
TsMask
729d5518e8 style: 修复样式 2024-11-23 15:51:31 +08:00
TsMask
cb510d9fdb style: 修复样式 2024-11-23 15:16:52 +08:00
TsMask
f310ae4f38 style: 修复样式 2024-11-23 14:44:49 +08:00
TsMask
f070764b69 feat: 添加实训教学 2024-11-23 12:25:06 +08:00
TsMask
0c96b4d130 feat: 添加实训教学 2024-11-23 11:20:45 +08:00
TsMask
285c9b660b feat: 实训教学模块 2024-11-23 11:18:40 +08:00
TsMask
2fb3467fb5 mager: 合并11.2版本 2024-11-23 10:57:55 +08:00
TsMask
63d7d11350 Merge remote-tracking branch 'origin/main' into practical-training 2024-08-03 14:11:19 +08:00
TsMask
2bd98540be Merge remote-tracking branch 'origin/main' into practical-training 2024-08-01 17:53:53 +08:00
TsMask
630c63e23f feat: 配置数据导出成表格,多语言翻译 2024-08-01 16:07:17 +08:00
TsMask
ada1f388fc fix: 参数配置array展开显示异常 2024-07-30 09:32:52 +08:00
TsMask
b9cf34714f feat: 网元信息允许教师备份操作权限控制 2024-07-29 15:28:22 +08:00
TsMask
53200d5f41 feat: 网元配置备份操作权限控制删除/编辑按钮 2024-07-29 15:27:50 +08:00
TsMask
52d5b9b732 style: 学生关闭右击样式 2024-07-27 16:14:59 +08:00
TsMask
292488f20c fix: 配置载入时禁用切换网元 2024-07-27 16:13:08 +08:00
TsMask
b0b844f0f6 fix: 学生禁止操作删除日志CDR/Event 2024-07-27 16:11:59 +08:00
TsMask
0819fbdb44 Merge remote-tracking branch 'origin/main' into practical-training 2024-07-27 15:03:16 +08:00
TsMask
a88ae826cc Merge remote-tracking branch 'origin/main' into practical-training 2024-07-27 14:16:15 +08:00
TsMask
76499d98e0 fix: 参数数据与示例对比差异显示功能 2024-07-22 18:16:48 +08:00
TsMask
94ccb45f8c style: 配置历史记录抽屉展开的title初始为空避免显示错误 2024-07-22 18:14:29 +08:00
TsMask
79e2b4ac26 feat: 添加是否系统管理员的权限判断函数 2024-07-22 11:46:23 +08:00
TsMask
b2510c0eb3 fix: 班级学生列表搜索时刷新合并申请状态,可直接应用和退回操作 2024-07-22 11:45:15 +08:00
TsMask
f977b6ea53 style: 添加用户水印,学生菜单布局侧边栏显示 2024-07-19 17:32:58 +08:00
TsMask
1e9c6a51be fix: 教师只能重启网元,禁止操作网元信息 2024-07-19 17:31:45 +08:00
TsMask
d68a773214 feat: 历史记录抽屉查看数据对比差异 2024-07-19 16:23:01 +08:00
TsMask
f60f26ae89 fix: 参数配置列表编辑切换属性回显错误 2024-07-19 16:22:15 +08:00
TsMask
c35a5a9c33 fix: 参数配置教师操作学生应用和退回 2024-07-18 15:04:51 +08:00
TsMask
4d755cea5d feat: 参数应用申请操作和查看功能 2024-07-17 17:04:13 +08:00
TsMask
c87458bc23 feat: 参数配置教师应用和切换学生功能 2024-07-17 17:03:32 +08:00
TsMask
cfd04ba1b6 Merge remote-tracking branch 'origin/main' into practical-training 2024-07-16 10:01:32 +08:00
TsMask
4af02693a4 feat: 教师选择学生进行切换查看学生配置数据信息 2024-07-15 17:59:30 +08:00
TsMask
66eb27813f feat: 新增网元应用申请提交列表操作 2024-07-15 17:56:28 +08:00
TsMask
65c8c6f809 perf: 参数配置页面函数重构 2024-07-11 14:49:26 +08:00
TsMask
ecaa0ce077 fix: 参数配置接入实训调整网元类型选择,权限按钮 2024-07-11 10:24:27 +08:00
TsMask
99140f0641 feat: 接入实训接口调试参数配置 2024-07-11 10:23:18 +08:00
TsMask
f3c46976ae Merge remote-tracking branch 'origin/main' into practical-training 2024-07-09 19:09:04 +08:00
TsMask
efa8764bd1 feat: 配置参数直连数据获取优化 2024-07-09 10:05:43 +08:00
TsMask
bc0e463191 fix: 用户岗位和角色指定只能选一个 2024-07-08 11:25:10 +08:00
TsMask
45bf9fe115 Merge remote-tracking branch 'origin/main' into practical-training 2024-07-08 10:57:22 +08:00
TsMask
4c85c61f05 fix: 右上角告警和帮助图标仅教师和管理员看见 2024-07-06 14:22:04 +08:00
TsMask
318f84504f fix: 看板数字活动连接禁止学生角色跳转页面 2024-07-06 14:20:06 +08:00
TsMask
53fa36eace Merge remote-tracking branch 'origin/main' into practical-training 2024-07-03 15:33:26 +08:00
TsMask
9123484bf5 Merge remote-tracking branch 'origin/main' into practical-training 2024-06-28 12:04:57 +08:00
TsMask
e7e4557e96 style: 个人信息隐藏手机号和邮箱的输入 2024-06-27 16:07:55 +08:00
TsMask
6b1fe4a582 Merge remote-tracking branch 'origin/main' into practical-training 2024-06-27 15:36:40 +08:00
TsMask
2610ab17ad Merge remote-tracking branch 'origin/main' into practical-training 2024-06-26 17:25:33 +08:00
TsMask
b84a08d825 fix: 教师不能管理分配部门,屏蔽电话邮箱 2024-06-24 17:13:09 +08:00
TsMask
824c38593b Merge remote-tracking branch 'origin/main' into practical-training 2024-06-24 11:18:30 +08:00
TsMask
69fb3df7ec Merge remote-tracking branch 'origin/main' into practical-training 2024-06-20 14:56:37 +08:00
TsMask
42827796c7 fix: 用户管理内教师角色只能新增学生 2024-06-20 10:28:01 +08:00
TsMask
eb38211e3b style: 部门改为班级 2024-06-20 10:27:28 +08:00
TsMask
fd1d2a5bad Merge remote-tracking branch 'origin/main' into practical-training 2024-06-20 10:19:01 +08:00
TsMask
2d5bfb9842 Merge remote-tracking branch 'origin/main' into practical-training 2024-06-20 09:34:54 +08:00
TsMask
c0a7f28958 Merge remote-tracking branch 'origin/main' into practical-training 2024-06-19 14:26:24 +08:00
TsMask
5f4e47e998 Merge remote-tracking branch 'origin/main' into practical-training 2024-06-19 12:04:40 +08:00
TsMask
19a91936de style: 默认菜单主题色dark 2024-06-19 12:03:59 +08:00
478 changed files with 33301 additions and 60763 deletions

View File

@@ -1,5 +1,5 @@
# 历史路径-哈希带井号标识
VITE_HISTORY_HASH = true
VITE_HISTORY_HASH = false
# 历史路径-前缀URL如/h5
VITE_HISTORY_BASE_URL = "/"
@@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network OMC"
VITE_APP_CODE = "OMC"
# 应用版本
VITE_APP_VERSION = "local-dev"
VITE_APP_VERSION = "2.241123-fix"
# 接口基础URL地址-不带/后缀
VITE_API_BASE_URL = "/omc-api"

View File

@@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network OMC"
VITE_APP_CODE = "OMC"
# 应用版本
VITE_APP_VERSION = "local-prod"
VITE_APP_VERSION = "2.241123-fix"
# 接口基础URL地址-不带/后缀
VITE_API_BASE_URL = "/omc-api"

View File

@@ -1,164 +1,5 @@
# 版本发布日志
## 2.2508.2-20250815
- 优化 给config.js加随机数避免缓存
- 新增 第三方登录认证功能和管理页,第三方用户不可删除和修改密码
- 优化 系统操作日志详情json格式化显示
- 优化 网元信息更新禁止修改neType neId
- 修复 用户数累加问题修复,液体图中浮标显示错误修复
- 修复 基站数修复以及资源模块下拉框修复
- 修复 告警事件时间显示不是时间格式字符串
- 修复 透明柱状图自适应,数值标签定位
- 修复 告警数据参数调整
## 2.2508.1-20250808
- 修复 UDM鉴权更新ki长度提示错误无法发送请求
- 优化 MML命令为空判断不发送MML日志列表显示
- 优化 关闭网元reload操作只有amf smf upf udm四个网元支持改用mml发送
- 优化 网元概览隐藏容量字段显示CBC创建时间格式显示错误
- 优化 系统用户账号简单4位长度
- 修复 看板2用户数量显示不出修改广播帮助的数量提示
- 优化 性能栏目相关页面主动呼叫改正在通话,网元切换清除缓存数据
## 2.2507.4-20250801
- 新增 cbc界面
- 修复 数据处理修复,显示优化
- 修复 告警模块优化,底部边距,数据采集说明
## 2.2507.3-20250725
- 优化 将UDM鉴权导出按钮隐藏
- 修复 自定义指标数值格式处理,导出表格修复
- 修复 仪表盘2用户事件显示不正常
- 修复 自定义指标数值格式化保留3位小数
- 优化 参数配置AMF导入Index字段存在更新不存在默认新增
## 2.2507.2-20250718
- 优化 变更nssf/n3iwf接口调用
- 优化 调整告警类型参数值,参数配置列表项不记录勾选状态
- 优化 调整UE数据返回参数
- 修复 基站状态拓扑图显示失败
- 修复 基站状态记录和smscCDR导出改为get行为
- 新增 mt版本移动过来的的告警/仪表/性能大屏页面
- 移除 删除无用文件
- 修复 指标页面数据获取异常接口调整
## 2.2507.1-20250705
- 修复 缓存管理列表key查询URL路径错误
- 优化 SMF-Date显示图表日期显示不完整改日期格式
- 修复 跟踪任务编号的传入获取信息不一致
- 修复 参数配置可见visible属性判断
## 2.2506.4-20250627
- 修复 UDM-IMS数据批量新增/批量删除命令调整
- 优化 AMF/MME对应的IMEI白名单模板去除index
## 2.2506.3-20250620
- 新增 UDM签约cnType新增可选1仅5G/2仅4G
- 新增 给AMF/MME对应的IMEI白名单添加批量导入和批量删除功能
- 新增 UDM voup/ims导入失败文件下载
## 2.2506.2-20250613
- 修复 数值控件属性maxlength拼写错误
- 修复 空格内容无法编辑,提示信息显示方向
- 优化 抓包文件目录列表显示文件大小
## 2.2506.1-20250607
- 优化 请求响应码常量,身份信息更换逻辑优化
- 修复 更新tcpdump路径从/tmp到/usr/local以符合新目录结构
## 2.2505.4-20250530
- 优化 网元参数配置修改tooltip位置为bottomLeft防止遮挡
- 修复 服务器时间格式化指定时区
- 优化 网元授权状态更新容量和更新时间局部变化
- 修复 网元信令跟踪支持选择轻量版UPF
- 修复 看板数据加载间隔从5秒调整为10秒
- 修复 看板UDM选择添加下拉菜单的弹出容器属性
## 2.2505.3-20250523
- 修复 修复同步操作加载提示未隐藏的问题
- 修复 告警确认相关提示Tip多语言显示
- 修复 网元授权许可证状态统一刷新功能
- 修复 活动告警启用历史同步按钮
- 修复 更新批量刷新信息容量仅UDM/AMF/MME
- 修复 仪表盘数据会话属累加
- 修复 网元主机信息编辑表单不验证免密类型, 主机列表的查询类型参数
## 2.2505.2-20250516
- 修复 编译类型检查错误
- 新增 网元授权上传变更确认延迟2s刷新授权状态
- 修复 英文修改基站名称为 RAN Node Name
- 新增 根据网元类型过滤导出备份数据可选的数据来源
- 新增 跟踪任务添加标题,编号后面加任务详情
## 2.2505.1-20250509
- 新增 更新日志文件导出列表,添加系统登录和操作日志
- 新增 网元授权添加用户容量列
- 修复 OAM读取结构错误
- 修复 仪表盘用户数据刷新时显示0闪烁
- 新增 看板含有网元显示区域
- 新增 根据网元显示特有菜单
- 修复 移除网络错误时的退出登录状态处理逻辑
## 2.2504.4-20250430
- 新增 PCF用户策略页面多语言翻译
- 新增 在上传切片文件到网元端时添加删除临时文件选项
- 修复 优化PCF用户策略页面及接口调整
- 修复 时间控件格式移除插件属性value-format="x"
- 新增 服务器时区UTC格式转换
- 新增 添加网络错误处理,退出登录状态
- 修复 替换获取服务器时间接口
- 移除 移除部分旧/apt/rest/直连接口
- 新增 网元服务操作局部状态刷新,补充提示信息
- 修复 时间格式常量定义及默认格式
- 新增 添加IMS SIP响应码原因显示
- 修复 看板流量图确保时间以HH:mm:ss格式显示
- 修复 看板用户活动乱序问题
- 修复 修复引导获取token存储问题
## 2.2503.4-20250331
- 更新 依赖项版本
- 修复 去除请求方法PATCH
- 重构 重构令牌管理逻辑,,统一状态码识别
- 更新 编译文件引用缺失
- 新增 UDMVolte用户特殊VoIP数据
- 优化 补充多语言翻译
- 修复 调度任务参数最大输入2000字符。日志查看id错误修复
- 重构 统一ftp操作功能备份文件查看下载删除功能
- 优化 neFile目录移动到ne
- 新增 终端目录部分调整添加udm-voip/volte功能页面
- 新增 修改OAM配置中的同步功能为重启功能
- 新增 添加告警信息导出功能,并优化历史告警页面
- 修复 统一国际时区格式
- 新增 更新锁屏密码Basd64编码处理无密码直接进入
- 新增 PCAP文件解析功能页面优化
- 修复 移除信令接口查询
- 新增 支持ws工具心跳ping消息
- 重构 重构信令跟踪功能
- 重构 重构网元跟踪任务功能页面
- 修复 看板用户数初始neId传入失败禁止选择当前项
- 新增 添加网元配置文件备份的FTP配置管理功能
- 修复 mml补充UDM特殊命令处理
- 新增 UE添加导入模板下载功能
- 修复 网元授权上传后点击ok检查状态
- 新增 添加密码过期功能支持,密码有效期时间天
## 2.2404.1-20240402
- 新增 网元安装流程相关页面与操作相关接口联调

View File

@@ -2,18 +2,14 @@
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="google" content="notranslate" />
<meta name="google" content="notranslate">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>loading...</title>
<link rel="icon" href="/favicon.ico" />
<link rel="preload" href="/loading.js" as="script" />
<link rel="preload" href="/loading.js" as="script">
<script async src="/loading.js"></script>
<script>
var script = document.createElement('script');
script.src = '/config.js?nocache=' + new Date().getTime();
script.async = true;
document.head.appendChild(script);
</script>
<link rel="preload" href="/config.js" as="script">
<script async src="/config.js"></script>
</head>
<body>
<div id="app"></div>

View File

@@ -12,51 +12,50 @@
"preview": "vite preview"
},
"dependencies": {
"@ant-design/icons-vue": "7.0.1",
"@antv/g6": "4.8.25",
"@codemirror/lang-javascript": "6.2.3",
"@codemirror/lang-yaml": "6.1.2",
"@codemirror/merge": "6.10.0",
"@codemirror/theme-one-dark": "6.1.2",
"@tato30/vue-pdf": "1.11.3",
"@vueuse/core": "13.0.0",
"@xterm/addon-fit": "0.10.0",
"@xterm/xterm": "5.5.0",
"ant-design-vue": "4.2.6",
"antdv-pro-layout": "4.2.0",
"antdv-pro-modal": "4.0.8",
"codemirror": "6.0.1",
"crypto-js": "4.2.0",
"dayjs": "1.11.13",
"echarts": "5.6.0",
"echarts-liquidfill": "^3.1.0",
"file-saver": "2.0.5",
"grid-layout-plus": "1.0.6",
"intl-tel-input": "25.2.0",
"@ant-design/icons-vue": "^7.0.1",
"@antv/g6": "~4.8.24",
"@codemirror/lang-javascript": "^6.2.2",
"@codemirror/lang-yaml": "^6.1.1",
"@codemirror/merge": "^6.7.2",
"@codemirror/theme-one-dark": "^6.1.2",
"@tato30/vue-pdf": "^1.11.2",
"@vueuse/core": "11.2.0",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"ant-design-vue": "^4.2.5",
"antdv-pro-layout": "^4.1.9",
"antdv-pro-modal": "^4.0.5",
"codemirror": "^6.0.1",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.11",
"echarts": "~5.5.0",
"file-saver": "^2.0.5",
"grid-layout-plus": "^1.0.5",
"intl-tel-input": "^24.6.0",
"js-base64": "^3.7.7",
"js-cookie": "^3.0.5",
"localforage": "^1.10.0",
"nprogress": "^0.2.0",
"p-queue": "8.0.1",
"pinia": "2.3.0",
"vue": "3.5.13",
"vue-i18n": "11.1.2",
"vue-router": "4.5.0",
"vue3-smooth-dnd": "0.0.6",
"xlsx": "0.18.5"
"p-queue": "~8.0.1",
"pinia": "2.2.6",
"vue": "^3.5.12",
"vue-i18n": "^10.0.4",
"vue-router": "^4.4.5",
"vue3-smooth-dnd": "^0.0.6",
"xlsx": "~0.18.5"
},
"devDependencies": {
"@types/crypto-js": "4.2.2",
"@types/file-saver": "2.0.7",
"@types/js-cookie": "3.0.6",
"@types/node": "^18.0.0",
"@types/nprogress": "0.2.3",
"@vitejs/plugin-vue": "5.2.3",
"less": "4.2.2",
"typescript": "5.8.2",
"unplugin-vue-components": "0.28.0",
"vite": "6.3.3",
"vite-plugin-compression": "0.5.1",
"vue-tsc": "2.2.8"
"@types/crypto-js": "^4.2.2",
"@types/file-saver": "^2.0.7",
"@types/js-cookie": "^3.0.6",
"@types/node": "^22.7.7",
"@types/nprogress": "^0.2.3",
"@vitejs/plugin-vue": "^5.1.4",
"less": "^4.2.0",
"typescript": "^5.6.3",
"unplugin-vue-components": "^0.27.4",
"vite": "5.4.10",
"vite-plugin-compression": "~0.5.1",
"vue-tsc": "^2.1.8"
}
}

View File

@@ -0,0 +1,27 @@
# 实训教学模块
网元固定一套ne_id 默认使用`001`
## 静态资源
将目录下文件放置到对应目录 替换约18个文件
- i18n 对应覆盖 src\i18n
- views 对应覆盖 src\views
## 涉及文件
- src\i18n\locales\en-US.ts
- src\i18n\locales\zh-CN.ts
- src\views\monitor\topologyArchitecture\index.vue
- src\views\configManage\configParamTreeTable
- src\views\configManage\configParamApply
- src\views\ne\neInfo\index.vue
- src\plugins\auth-user.ts
- src\views\dashboard\amfUE\index.vue
- src\views\dashboard\mmeUE\index.vue
- src\views\dashboard\imsCDR\index.vue
- src\views\dashboard\smfCDR\index.vue
- src\views\dashboard\smscCDR\index.vue
- src\store\modules\user.ts

View File

@@ -0,0 +1,120 @@
import { request } from '@/plugins/http-fetch';
/**
* 保存为示例配置 (仅管理员操作)
* @param query 查询参数
* @returns object
*/
export function ptSaveAsDefault(neType: string, neid: string) {
return request({
url: `/pt/neConfigData/saveAsDefault`,
method: 'post',
data: { neType, neid },
});
}
/**
* 重置为示例配置 (仅学生/教师操作)
* @param query 查询参数
* @returns object
*/
export function ptResetAsDefault(neType: string) {
return request({
url: `/pt/neConfigData/resetAsDefault`,
method: 'post',
data: { neType },
});
}
/**
* 数据比较示例
* @param params 查询参数
* @returns object
*/
export function ptContrastAsDefault(params: Record<string, any>) {
return request({
url: `/pt/neConfigData/contrast`,
params,
method: 'get',
});
}
/**
* 配置数据导出Excel
* @param student 仅教师 student
* @returns object
*/
export function ptExport(student: string | undefined) {
return request({
url: `/pt/neConfigData/export`,
method: 'get',
params: { student },
responseType: 'blob',
timeout: 180_000,
});
}
/**
* 配置数据导出Excel (仅教师全量)
* @returns object
*/
export function ptExportAll() {
return request({
url: `/pt/neConfigData/export-all`,
method: 'get',
responseType: 'blob',
timeout: 180_000,
});
}
/**
* 网元参数配置信息
* @param params 数据 {neType,paramName}
* @returns object
*/
export function getPtNeConfigData(params: Record<string, any>) {
return request({
url: `/pt/neConfigData`,
params,
method: 'get',
});
}
/**
* 网元参数配置数据更新
* @param data 数据 {neType,paramName:"参数名",paramData:{参数},loc:"层级index仅array"}
* @returns object
*/
export function editPtNeConfigData(data: Record<string, any>) {
return request({
url: `/pt/neConfigData`,
method: 'put',
data: data,
});
}
/**
* 网元参数配置新增array
* @param data 数据 {neType,paramName:"参数名",paramData:{参数},loc:"层级index"}
* @returns object
*/
export function addPtNeConfigData(data: Record<string, any>) {
return request({
url: `/pt/neConfigData`,
method: 'post',
data: data,
});
}
/**
* 网元参数配置删除array
* @param params 数据 {neType,paramName:"参数名",loc:"层级index"}
* @returns object
*/
export function delPtNeConfigData(params: Record<string, any>) {
return request({
url: `/pt/neConfigData`,
method: 'delete',
params,
});
}

View File

@@ -0,0 +1,53 @@
import { request } from '@/plugins/http-fetch';
/**
* 班级学生列表 (仅教师操作)
* @param params 数据 {userName}
* @returns object
*/
export function getPtClassStudents(params?: Record<string, any>) {
return request({
url: `/pt/neConfigApply/students`,
params,
method: 'get',
});
}
/**
* 网元参数配置应用申请列表
* @param params 数据 {neType,paramName}
* @returns object
*/
export function getPtNeConfigApplyList(params: Record<string, any>) {
return request({
url: `/pt/neConfigApply/list`,
params,
method: 'get',
});
}
/**
* 网元参数配置应用申请提交(仅学生操作)
* @param data 数据 { "neType": "MME", "status": "1" }
* @returns object
*/
export function stuPtNeConfigApply(data: Record<string, any>) {
return request({
url: `/pt/neConfigApply`,
method: 'post',
data: data,
});
}
/**
* 网元参数配置应用申请状态变更(仅管理员/教师操作)
* @param data 数据 { "applyId": "1", "neType": "MME", "status": "3", "backInfo": "sgw参数错误" }
* @returns object
*/
export function updatePtNeConfigApply(data: Record<string, any>) {
return request({
url: `/pt/neConfigApply`,
method: 'put',
data: data,
});
}

View File

@@ -0,0 +1,27 @@
import { request } from '@/plugins/http-fetch';
/**
* 网元参数配置数据变更日志信息
* @param params 数据 {neType,paramName}
* @returns object
*/
export function getPtNeConfigDataLogList(params: Record<string, any>) {
return request({
url: `/pt/neConfigDataLog`,
params,
method: 'get',
});
}
/**
* 网元参数配置数据变更日志还原到数据
* @param data 数据 { "id": "1", "value": "old" }
* @returns object
*/
export function restorePtNeConfigDataLog(data: Record<string, any>) {
return request({
url: `/pt/neConfigDataLog/restore`,
method: 'put',
data: data,
});
}

View File

@@ -0,0 +1,42 @@
import { request } from '@/plugins/http-fetch';
/**
* 导入用户模板数据
* @param data 表单数据对象
* @returns object
*/
export function importData(data: FormData) {
return request({
url: '/pt/system/user/importData',
method: 'post',
data,
dataType: 'form-data',
timeout: 180_000,
});
}
/**
* 导入用户模板下载
* @returns bolb
*/
export function importTemplate() {
return request({
url: '/pt/system/user/importTemplate',
method: 'get',
responseType: 'blob',
});
}
/**
* 用户列表导出
* @param query 查询参数
* @returns bolb
*/
export function exportUser(query: Record<string, any>) {
return request({
url: '/pt/system/user/export',
method: 'post',
data: query,
responseType: 'blob',
});
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
import { ADMIN_PERMISSION, ADMIN_ROLE_KEY } from '@/constants/admin-constants';
import useUserStore from '@/store/modules/user';
/**
* 是否系统管理员
* @returns true | false
*/
export function isSystemAdmin(): boolean {
const userPermissions = useUserStore().permissions;
if (userPermissions.includes(ADMIN_PERMISSION)) return true;
const userRoles = useUserStore().roles;
if (userRoles.includes(ADMIN_ROLE_KEY)) return true;
return false;
}
/**
* 只需含有其中权限
* @param role 权限字符数组
* @returns true | false
*/
export function hasPermissions(permissions: string[]): boolean {
if (!permissions || permissions.length === 0) return false;
const userPermissions = useUserStore().permissions;
if (!userPermissions || userPermissions.length === 0) return false;
if (userPermissions.includes(ADMIN_PERMISSION)) return true;
return permissions.some(p => userPermissions.some(up => up === p));
}
/**
* 同时匹配其中权限
* @param role 权限字符数组
* @returns true | false
*/
export function matchPermissions(permissions: string[]): boolean {
if (!permissions || permissions.length === 0) return false;
const userPermissions = useUserStore().permissions;
if (!userPermissions || userPermissions.length === 0) return false;
if (userPermissions.includes(ADMIN_PERMISSION)) return true;
return permissions.every(p => userPermissions.some(up => up === p));
}
/**
* 只需含有其中角色
* @param role 角色字符数组
* @returns true | false
*/
export function hasRoles(roles: string[]): boolean {
if (!roles || roles.length === 0) return false;
const userRoles = useUserStore().roles;
if (!userRoles || userRoles.length === 0) return false;
if (userRoles.includes(ADMIN_ROLE_KEY)) return true;
return roles.some(r => userRoles.some(ur => ur === r));
}
/**
* 同时匹配其中角色
* @param role 角色字符数组
* @returns true | false
*/
export function matchRoles(roles: string[]): boolean {
if (!roles || roles.length === 0) return false;
const userRoles = useUserStore().roles;
if (!userRoles || userRoles.length === 0) return false;
if (userRoles.includes(ADMIN_ROLE_KEY)) return true;
return roles.every(r => userRoles.some(ur => ur === r));
}

View File

@@ -0,0 +1,171 @@
import defaultAvatar from '@/assets/images/default_avatar.png';
import useLayoutStore from './layout';
import { login, logout, getInfo } from '@/api/login';
import { setToken, removeToken } from '@/plugins/auth-token';
import { defineStore } from 'pinia';
import { TOKEN_RESPONSE_FIELD } from '@/constants/token-constants';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { parseUrlPath } from '@/plugins/file-static-url';
/**用户信息类型 */
type UserInfo = {
/**用户ID */
userId: string;
/**登录账号 */
userName: string;
/**用户角色 字符串数组 */
roles: string[];
/**用户权限 字符串数组 */
permissions: string[];
/**用户头像 */
avatar: string;
/**用户昵称 */
nickName: string;
/**用户手机号 */
phonenumber: string;
/**用户邮箱 */
email: string;
/**用户性别 */
sex: string | undefined;
/**其他信息 */
profile: Record<string, any>;
};
const useUserStore = defineStore('user', {
state: (): UserInfo => ({
userId: '',
userName: '',
roles: [],
permissions: [],
avatar: '',
nickName: '',
phonenumber: '',
email: '',
sex: undefined,
profile: {},
}),
getters: {
/**
* 获取正确头像地址
* @param state 内部属性不用传入
* @returns 头像地址url
*/
getAvatar(state) {
if (!state.avatar) {
return defaultAvatar;
}
return parseUrlPath(state.avatar);
},
/**
* 获取基础信息属性
* @param state 内部属性不用传入
* @returns 基础信息
*/
getBaseInfo(state) {
return {
nickName: state.nickName,
phonenumber: state.phonenumber,
email: state.email,
sex: state.sex,
};
},
},
actions: {
/**
* 更新基础信息属性
* @param data 变更信息
*/
setBaseInfo(data: Record<string, any>) {
this.nickName = data.nickName;
this.phonenumber = data.phonenumber;
this.email = data.email;
this.sex = data.sex;
},
/**
* 更新头像
* @param avatar 上传后的地址
*/
setAvatar(avatar: string) {
this.avatar = avatar;
},
/**
* 获取正确头像地址
* @param avatar
*/
fnAvatar(avatar: string) {
if (!avatar) {
return defaultAvatar;
}
return parseUrlPath(avatar);
},
// 登录
async fnLogin(loginBody: Record<string, string>) {
const res = await login(loginBody);
if (res.code === RESULT_CODE_SUCCESS && res.data) {
const token = res.data[TOKEN_RESPONSE_FIELD];
setToken(token);
}
return res;
},
// 获取用户信息
async fnGetInfo() {
const res = await getInfo();
if (res.code === RESULT_CODE_SUCCESS && res.data) {
const { user, roles, permissions } = res.data;
this.userId = user.userId;
// 登录账号
this.userName = user.userName;
// 用户头像
this.avatar = user.avatar;
// 基础信息
this.nickName = user.nickName;
this.phonenumber = user.phonenumber;
this.email = user.email;
this.sex = user.sex;
// 验证返回的roles是否是一个非空数组
if (Array.isArray(roles) && roles.length > 0) {
this.roles = roles;
this.permissions = permissions;
} else {
this.roles = ['ROLE_DEFAULT'];
this.permissions = [];
}
// 水印文字信息=用户昵称 手机号
let waterMarkContent = this.userName;
if (this.phonenumber) {
waterMarkContent = `${this.userName} ${this.phonenumber}`;
}
// useLayoutStore().changeWaterMark(waterMarkContent);
useLayoutStore().changeWaterMark('');
// 学生布局用不一样的
if (this.roles.includes('student')) {
useLayoutStore().changeConf('layout', 'side');
useLayoutStore().changeConf('menuTheme', 'dark');
useLayoutStore().changeConf('tabRender', false);
}
}
// 网络错误时退出登录状态
if (res.code === 0) {
removeToken();
window.location.reload();
}
return res;
},
// 退出系统
async fnLogOut() {
try {
await logout();
} catch (error) {
throw error;
} finally {
this.roles = [];
this.permissions = [];
removeToken();
}
},
},
});
export default useUserStore;

View File

@@ -1,40 +1,42 @@
<script setup lang="ts">
import { reactive, ref, onMounted, toRaw } from 'vue';
import { reactive, onMounted, toRaw, computed } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { ProModal } from 'antdv-pro-modal';
import { message, Form, Modal } from 'ant-design-vue/es';
import { Form, message, Modal } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider';
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import { ColumnsType } from 'ant-design-vue/es/table';
import useNeStore from '@/store/modules/ne';
import { parseDateToStr } from '@/utils/date-utils';
import useDictStore from '@/store/modules/dict';
import useNeInfoStore from '@/store/modules/neinfo';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useI18n from '@/hooks/useI18n';
import {
addKPITitle,
delKPITitle,
listKPITitle,
updateKPITitle,
} from '@/api/neData/kpi-title';
getPtNeConfigApplyList,
stuPtNeConfigApply,
updatePtNeConfigApply,
} from '@/api/pt/neConfigApply';
import { hasRoles } from '@/plugins/auth-user';
const { t } = useI18n();
const { getDict } = useDictStore();
const neStore = useNeStore();
const neInfoStore = useNeInfoStore();
/**字典数据 */
let dict: {
sysNormalDisable: DictType[];
/**配置申请应用状态 */
ptConfigApplyStatus: DictType[];
} = reactive({
sysNormalDisable: [],
ptConfigApplyStatus: [],
});
/**查询参数 */
let queryParams = reactive({
/**参数名称 */
/**网元类型 */
neType: '',
/**参数键名 */
title: '',
/**系统内置 */
statusFlag: undefined,
/**申请人 */
createBy: '',
/**状态 */
status: undefined,
/**当前页数 */
pageNum: 1,
/**每页条数 */
@@ -45,8 +47,8 @@ let queryParams = reactive({
function fnQueryReset() {
queryParams = Object.assign(queryParams, {
neType: '',
title: '',
statusFlag: undefined,
createBy: '',
status: undefined,
pageNum: 1,
pageSize: 20,
});
@@ -79,52 +81,64 @@ let tableState: TabeStateType = reactive({
});
/**表格字段列 */
let tableColumns = ref<ColumnsType>([
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
align: 'center',
width: 100,
},
{
title: 'NE Type',
title: t('views.ne.common.neType'),
dataIndex: 'neType',
align: 'left',
width: 100,
},
{
title: 'KPI ID',
dataIndex: 'kpiId',
title: '申请人',
dataIndex: 'createBy',
align: 'left',
width: 120,
},
{
title: '申请时间',
dataIndex: 'createTime',
align: 'left',
width: 150,
customRender(opt) {
if (+opt.value <= 0) return '';
return parseDateToStr(+opt.value);
},
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
align: 'left',
width: 100,
},
{
title: 'CN Title',
dataIndex: 'cnTitle',
title: '处理人',
dataIndex: 'updateBy',
align: 'left',
width: 200,
ellipsis: true,
width: 120,
},
{
title: 'EN Title',
dataIndex: 'enTitle',
title: '处理时间',
dataIndex: 'updateTime',
align: 'left',
width: 200,
ellipsis: true,
},
{
title: 'Status',
dataIndex: 'statusFlag',
key: 'statusFlag',
align: 'left',
width: 100,
width: 150,
customRender(opt) {
if (+opt.value <= 0) return '';
return parseDateToStr(+opt.value);
},
},
{
title: t('common.operate'),
key: 'id',
align: 'left',
},
]);
];
/**表格分页器参数 */
let tablePagination = reactive({
@@ -161,12 +175,48 @@ function fnTableSize({ key }: MenuInfo) {
}
/**表格多选 */
function fnTableSelectedRowKeys(keys: (string | number)[]) {
tableState.selectedRowKeys = keys;
function fnTableSelectedRowKeys(keys: (string | number)[], infos: any) {
const arr = [];
for (const item of infos) {
if (item.status === '0') {
arr.push(item.id);
}
}
tableState.selectedRowKeys = arr;
}
/**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (tableState.loading) return;
tableState.loading = true;
if (pageNum) {
queryParams.pageNum = pageNum;
}
getPtNeConfigApplyList(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
//
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
tablePagination.total = res.total;
tableState.data = res.rows;
if (
tablePagination.total <=
(queryParams.pageNum - 1) * tablePagination.pageSize &&
queryParams.pageNum !== 1
) {
tableState.loading = false;
fnGetList(queryParams.pageNum - 1);
}
}
tableState.loading = false;
});
}
/**对话框对象信息状态类型 */
type ModalStateType = {
/**详情框是否显示 */
openByView: boolean;
/**新增框或修改框是否显示 */
openByEdit: boolean;
/**标题 */
@@ -175,52 +225,34 @@ type ModalStateType = {
from: Record<string, any>;
/**确定按钮 loading */
confirmLoading: boolean;
/**cron生成框是否显示 */
openByCron: boolean;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
openByView: false,
openByEdit: false,
title: 'KPI Title',
title: '任务',
from: {
id: undefined,
cnTitle: '',
enTitle: '',
kpiId: '',
neType: '',
statusFlag: '1',
createBy: '',
createTime: 0,
updateBy: '',
updateTime: 0,
neType: 'MME',
status: '0',
backInfo: '',
},
confirmLoading: false,
openByCron: false,
});
/**对话框内表单属性和校验规则 */
const modalStateFrom = Form.useForm(
modalState.from,
reactive({
neType: [
{
required: true,
message: t('common.inputPlease'),
},
],
kpiId: [
{
required: true,
message: t('common.inputPlease'),
},
],
cnTitle: [
{
required: true,
message: t('common.inputPlease'),
},
],
enTitle: [
{
required: true,
message: t('common.inputPlease'),
},
],
statusFlag: [
status: [
{
required: true,
message: t('common.selectPlease'),
@@ -230,25 +262,23 @@ const modalStateFrom = Form.useForm(
);
/**
* 对话框弹出显示为 新增或者修改
* @param configId 参数编号id, 不传为新增
* 对话框弹出显示为 查看
* @param jobId 任务id
*/
function fnModalVisibleByEdit(row?: Record<string, any>) {
if (!row) {
modalStateFrom.resetFields();
modalState.title = 'Add KPI Title Info';
modalState.openByEdit = true;
return;
}
//
if (modalState.confirmLoading) return;
modalState.from.id = row.id;
modalState.from.enTitle = row.enTitle;
modalState.from.cnTitle = row.cnTitle;
modalState.from.kpiId = row.kpiId;
modalState.from.neType = row.neType;
modalState.from.statusFlag = row.statusFlag;
modalState.title = 'Update KPI Title Info';
function fnModalVisibleByVive(row: Record<string, any>) {
modalState.from = Object.assign(modalState.from, row);
modalState.title = '查看';
modalState.openByView = true;
}
/**
* 对话框弹出显示为
* @param jobId 任务id
*/
function fnModalVisibleByEdit(row: Record<string, any>) {
Object.assign(modalState.from, row);
modalState.from.status = '3';
modalState.title = '编辑状态';
modalState.openByEdit = true;
}
@@ -261,28 +291,31 @@ function fnModalOk() {
.validate()
.then(() => {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
const from = toRaw(modalState.from);
const req = from.id ? updateKPITitle(from) : addKPITitle(from);
req
updatePtNeConfigApply({
applyId: from.id,
neType: from.neType,
status: from.status,
backInfo: from.backInfo,
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', { msg: modalState.title }),
duration: 2,
content: t('common.operateOk'),
duration: 3,
});
modalState.openByEdit = false;
modalStateFrom.resetFields();
fnGetList(1);
fnGetList();
} else {
message.error({
content: `${res.msg}`,
duration: 2,
duration: 3,
});
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
})
@@ -297,82 +330,70 @@ function fnModalOk() {
*/
function fnModalCancel() {
modalState.openByEdit = false;
modalStateFrom.resetFields();
modalState.openByView = false;
modalState.from = {};
}
/**
* 配置删除
* @param id 编号ID
*/
function fnRecordDelete(id: string = '0') {
if (id === '0') {
id = tableState.selectedRowKeys.join(',');
}
/**批量退回 */
function fnRecordBack(row?: Record<string, any>) {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.system.config.delTip', { num: id }),
content: row
? '确认要撤回配置应用申请吗?'
: '确认要批量退回学生的配置应用申请吗?',
onOk() {
const hide = message.loading(t('common.loading'), 0);
delKPITitle(id)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.system.config.delOk'),
duration: 2,
});
fnGetList();
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
})
.finally(() => {
hide();
let result: any;
if (row) {
result = stuPtNeConfigApply({ neType: row.neType, status: '1' });
} else {
result = updatePtNeConfigApply({
status: '3',
backId: tableState.selectedRowKeys.join(','),
backInfo: '请重新检查配置',
});
}
result.then((res: any) => {
fnGetList();
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
});
},
});
}
/**查询参数配置列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (tableState.loading) return;
tableState.loading = true;
if (pageNum) {
queryParams.pageNum = pageNum;
/**应用状态 */
const applyStatus = computed(() => {
if (hasRoles(['student'])) {
return dict.ptConfigApplyStatus.filter(s => ['0', '1'].includes(s.value));
}
listKPITitle(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
//
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
tablePagination.total = res.data.total;
tableState.data = res.data.rows;
if (
tablePagination.total <=
(queryParams.pageNum - 1) * tablePagination.pageSize &&
queryParams.pageNum !== 1
) {
tableState.loading = false;
fnGetList(queryParams.pageNum - 1);
}
}
tableState.loading = false;
});
}
let data = dict.ptConfigApplyStatus;
if (modalState.openByEdit && modalState.from.id) {
data = dict.ptConfigApplyStatus.filter(s => ['2', '3'].includes(s.value));
}
return data;
});
onMounted(() => {
//
Promise.allSettled([getDict('sys_normal_disable')]).then(resArr => {
Promise.allSettled([getDict('pt_config_apply_status')]).then(resArr => {
if (resArr[0].status === 'fulfilled') {
dict.sysNormalDisable = resArr[0].value;
dict.ptConfigApplyStatus = resArr[0].value;
}
});
//
fnGetList();
//
neInfoStore.fnNelist().finally(() => {
//
fnGetList();
});
});
</script>
@@ -387,36 +408,29 @@ onMounted(() => {
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item label="NE Type" name="neType">
<a-form-item
:label="t('views.configManage.license.neType')"
name="neType "
>
<a-auto-complete
v-model:value="queryParams.neType"
:options="neStore.getNeSelectOtions"
:options="neInfoStore.getNeSelectOtions"
allow-clear
:placeholder="t('common.inputPlease')"
:placeholder="t('views.configManage.license.neTypePlease')"
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item label="Title" name="title">
<a-input
v-model:value="queryParams.title"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item label="Status" name="statusFlag">
<a-form-item label="状态" name="status">
<a-select
v-model:value="queryParams.statusFlag"
v-model:value="queryParams.status"
allow-clear
:placeholder="t('common.selectPlease')"
:options="dict.sysNormalDisable"
:options="applyStatus"
>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
@@ -438,26 +452,23 @@ onMounted(() => {
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
<template #icon><PlusOutlined /></template>
{{ t('common.addText') }}
</a-button>
<div class="button-container">
<a-button
type="default"
danger
:disabled="tableState.selectedRowKeys.length <= 0"
@click.prevent="fnRecordDelete()"
@click.prevent="fnRecordBack()"
v-roles:has="['admin', 'teacher']"
>
<template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }}
批量退回
</a-button>
</a-space>
</div>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<div class="button-container">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
@@ -497,7 +508,7 @@ onMounted(() => {
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</div>
</template>
<!-- 表格列表 -->
@@ -508,8 +519,8 @@ onMounted(() => {
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: tableColumns.length * 120 }"
:pagination="tablePagination"
:row-selection="{
type: 'checkbox',
selectedRowKeys: tableState.selectedRowKeys,
@@ -517,15 +528,32 @@ onMounted(() => {
}"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'statusFlag'">
<template v-if="column.key === 'status'">
<DictTag
:options="dict.sysNormalDisable"
:value="record.statusFlag"
:options="dict.ptConfigApplyStatus"
:value="record.status"
/>
</template>
<template v-if="column.key === 'id'">
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.viewText') }}</template>
<a-button
type="link"
@click.prevent="fnModalVisibleByVive(record)"
>
<template #icon><ProfileOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip v-if="record.status === '0' && hasRoles(['student'])">
<template #title>撤回</template>
<a-button type="link" @click.prevent="fnRecordBack(record)">
<template #icon><RollbackOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip
v-if="record.status === '0' && hasRoles(['admin', 'teacher'])"
>
<template #title>{{ t('common.editText') }}</template>
<a-button
type="link"
@@ -534,31 +562,87 @@ onMounted(() => {
<template #icon><FormOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.deleteText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordDelete(record.id)"
>
<template #icon><DeleteOutlined /></template>
</a-button>
</a-tooltip>
</a-space>
</template>
</template>
</a-table>
</a-card>
<!-- 详情框 -->
<ProModal
:drag="true"
:width="800"
:open="modalState.openByView"
:title="modalState.title"
@cancel="fnModalCancel"
>
<a-form layout="horizontal" :label-col="{ span: 6 }" :label-wrap="true">
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.ne.common.neType')" name="neType">
{{ modalState.from.neType }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="状态" name="status">
<DictTag
:options="dict.ptConfigApplyStatus"
:value="modalState.from.status"
/>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="申请人" name="createBy">
{{ modalState.from.createBy }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="申请时间" name="createTime">
{{ parseDateToStr(+modalState.from.createTime) }}
</a-form-item>
</a-col>
</a-row>
<a-row v-if="modalState.from.status !== '0'">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="处理人" name="updateBy">
{{ modalState.from.updateBy }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="处理时间" name="updateTime">
{{ parseDateToStr(+modalState.from.updateTime) }}
</a-form-item>
</a-col>
</a-row>
<a-form-item
v-if="modalState.from.status === '3'"
label="退回说明"
name="backInfo"
:label-col="{ span: 3 }"
:label-wrap="true"
>
{{ modalState.from.backInfo }}
</a-form-item>
</a-form>
<template #footer>
<a-button key="cancel" @click="fnModalCancel">
{{ t('common.close') }}
</a-button>
</template>
</ProModal>
<!-- 新增框或修改框 -->
<ProModal
:drag="true"
:width="800"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:open="modalState.openByEdit"
:title="modalState.title"
:confirm-loading="modalState.confirmLoading"
:destroyOnClose="true"
@ok="fnModalOk"
@cancel="fnModalCancel"
>
@@ -568,84 +652,56 @@ onMounted(() => {
:label-col="{ span: 6 }"
:label-wrap="true"
>
<a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
label="NE Type"
name="neType"
v-bind="modalStateFrom.validateInfos.cnTitle"
>
<a-auto-complete
v-model:value="modalState.from.neType"
:options="neStore.getNeSelectOtions"
allow-clear
:placeholder="t('common.inputPlease')"
:disabled="!!modalState.from.id"
/>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
label="KPI ID"
name="kpiId"
v-bind="modalStateFrom.validateInfos.cnTitle"
>
<a-input
v-model:value="modalState.from.kpiId"
allow-clear
:placeholder="t('common.inputPlease')"
:disabled="!!modalState.from.id"
></a-input>
<a-form-item :label="t('views.ne.common.neType')" name="neType">
{{ modalState.from.neType }}
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
label="CN Title"
name="cnTitle"
v-bind="modalStateFrom.validateInfos.cnTitle"
>
<a-input
v-model:value="modalState.from.cnTitle"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
<a-form-item label="申请人" name="createBy">
{{ modalState.from.createBy }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
label="EN Title"
name="enTitle"
v-bind="modalStateFrom.validateInfos.enTitle"
>
<a-input
v-model:value="modalState.from.enTitle"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
<a-form-item label="申请时间" name="createTime">
{{ parseDateToStr(+modalState.from.createTime) }}
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
label="Status"
name="statusFlag"
v-bind="modalStateFrom.validateInfos.statusFlag"
>
<a-select
v-model:value="modalState.from.statusFlag"
:placeholder="t('common.selectPlease')"
:options="dict.sysNormalDisable"
>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24"> </a-col>
</a-row>
<a-form-item
label="状态"
name="status"
:label-col="{ span: 3 }"
:label-wrap="true"
v-bind="modalStateFrom.validateInfos.status"
>
<a-select
v-model:value="modalState.from.status"
:placeholder="t('common.selectPlease')"
:options="applyStatus"
>
</a-select>
</a-form-item>
<a-form-item
v-if="modalState.from.status === '3'"
label="退回说明"
name="backInfo"
:label-col="{ span: 3 }"
:label-wrap="true"
>
<a-textarea
v-model:value="modalState.from.backInfo"
:auto-size="{ minRows: 2, maxRows: 6 }"
:maxlength="400"
:placeholder="t('common.inputPlease')"
/>
</a-form-item>
</a-form>
</ProModal>
</PageContainer>

View File

@@ -0,0 +1,327 @@
<script setup lang="ts">
import { reactive, onMounted, toRaw, watch } from 'vue';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import CodemirrorEditeDiff from '@/components/CodemirrorEditeDiff/index.vue';
import { parseDateToStr } from '@/utils/date-utils';
import {
getPtNeConfigDataLogList,
restorePtNeConfigDataLog,
} from '@/api/pt/neConfigDataLog';
import useDictStore from '@/store/modules/dict';
import { message } from 'ant-design-vue';
const { t } = useI18n();
const emit = defineEmits(['ok', 'cancel', 'update:open']);
const props = defineProps({
open: {
type: Boolean,
default: false,
},
/**网元类型 */
neType: {
type: String,
default: '',
},
/**参数名 */
paramName: {
type: String,
default: '',
},
/**学生用户账号 */
student: {
type: String,
default: '',
},
});
const { getDict } = useDictStore();
/**字典数据 */
let dict: {
/**业务类型 */
sysBusinessType: DictType[];
} = reactive({
sysBusinessType: [],
});
/**对话框对象信息状态类型 */
type StateType = {
/**新增框或修改框是否显示 */
openByList: boolean;
/**差异比较框是否显示 */
openByDiff: boolean;
/**标题 */
title: string;
/**加载状态 */
loading: boolean;
/**数据 */
data: Record<string, any>[];
/**差异数据 */
dataDiff: Record<string, any>;
/**确定按钮 loading */
confirmLoading: boolean;
};
/**对话框对象信息状态 */
let state: StateType = reactive({
openByList: false,
openByDiff: false,
title: '操作参数名称-学生账号',
loading: false,
data: [],
dataDiff: {},
confirmLoading: false,
});
function onClose() {
state.loading = false;
state.openByList = false;
state.openByDiff = false;
state.data = [];
state.dataDiff = {};
emit('cancel');
emit('update:open', false);
queryParams = {
neType: '',
paramName: '',
student: '',
pageNum: 1,
pageSize: 10,
};
}
/**查询参数 */
let queryParams = reactive({
/**网元类型 */
neType: '',
/**可用属性值 */
paramName: '',
/**学生账号 */
student: '',
/**当前页数 */
pageNum: 1,
/**每页条数 */
pageSize: 10,
});
/**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (state.loading) return;
state.loading = true;
if (pageNum) {
queryParams.pageNum = pageNum;
if (pageNum === 1) state.data = [];
}
getPtNeConfigDataLogList(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
// tablePagination.total = res.total;
state.data = state.data.concat(res.rows);
// 去首个做标题
if (queryParams.pageNum === 1 && state.data.length > 0) {
const item = state.data[0];
state.title = `${item.paramDisplay} - ${item.createBy}`;
}
if (state.data.length <= res.total && res.rows.length > 0) {
queryParams.pageNum++;
}
}
state.loading = false;
});
}
/**差异比较框打开 */
function fnMergeCellOpen(row: Record<string, any>) {
state.dataDiff = row;
state.dataDiff.paramJsonOld = JSON.stringify(
JSON.parse(state.dataDiff.paramJsonOld),
null,
2
);
state.dataDiff.paramJsonNew = JSON.stringify(
JSON.parse(state.dataDiff.paramJsonNew),
null,
2
);
state.openByDiff = true;
}
/**差异比较框关闭 */
function fnMergeCellClose() {
state.openByDiff = false;
state.dataDiff = {};
}
/**差异比较还原 */
function fnMergeCellRestore(value: 'old' | 'new') {
if (state.confirmLoading) return;
const id = state.dataDiff.id;
restorePtNeConfigDataLog({ id, value })
.then((res: any) => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
fnMergeCellClose();
fnGetList(1);
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
state.confirmLoading = false;
});
}
/**监听是否显示,初始数据 */
watch(
() => props.open,
val => {
if (val) {
if (props.neType && props.paramName) {
state.title = '';
state.openByList = true;
// 根据条件查询数据
queryParams.neType = props.neType;
queryParams.paramName = props.paramName;
if (props.student) {
queryParams.student = props.student;
}
fnGetList();
}
}
}
);
onMounted(() => {
// 初始字典数据
Promise.allSettled([getDict('sys_oper_type')]).then(resArr => {
if (resArr[0].status === 'fulfilled') {
dict.sysBusinessType = resArr[0].value;
}
});
});
</script>
<template>
<div>
<a-drawer
:width="500"
:title="state.title"
placement="right"
:open="state.openByList"
@close="onClose"
>
<a-list
class="demo-loadmore-list"
item-layout="horizontal"
:data-source="state.data"
>
<template #loadMore>
<div
:style="{
textAlign: 'center',
marginTop: '12px',
height: '32px',
lineHeight: '32px',
}"
>
<a-button @click="fnGetList()" :loading="state.loading">
{{ t('views.configManage.configParamForm.ptDiffLoad') }}
</a-button>
</div>
</template>
<template #renderItem="{ item }">
<a-list-item>
<template #actions>
<a-tooltip>
<template #title>
{{ t('views.configManage.configParamForm.ptDiffMerge') }}
</template>
<a-button type="primary" @click.prevent="fnMergeCellOpen(item)">
<template #icon><MergeCellsOutlined /></template>
</a-button>
</a-tooltip>
</template>
<a-list-item-meta>
<template #title>
<DictTag
:options="dict.sysBusinessType"
:value="item.operaType"
/>
</template>
<template #description>
{{ parseDateToStr(item.createTime) }}
</template>
</a-list-item-meta>
</a-list-item>
</template>
</a-list>
</a-drawer>
<a-modal
:width="800"
:destroyOnClose="true"
:mask-closable="false"
v-model:open="state.openByDiff"
:footer="null"
:body-style="{ padding: 0, maxHeight: '650px', 'overflow-y': 'auto' }"
@ok="fnMergeCellClose()"
@cancel="fnMergeCellClose()"
>
<template #title>
<DictTag
:options="dict.sysBusinessType"
:value="state.dataDiff.operaType"
/>
{{ parseDateToStr(state.dataDiff.createTime) }}
</template>
<div class="diffBack">
<div>
<a-button
type="text"
:loading="state.confirmLoading"
@click.prevent="fnMergeCellRestore('old')"
>
<template #icon><MergeCellsOutlined /></template>
{{ t('views.configManage.configParamForm.ptDiffRest') }}
</a-button>
</div>
<div>
<a-button
type="text"
:loading="state.confirmLoading"
@click.prevent="fnMergeCellRestore('new')"
>
<template #icon><MergeCellsOutlined /></template>
{{ t('views.configManage.configParamForm.ptDiffRest') }}
</a-button>
</div>
</div>
<CodemirrorEditeDiff
:old-area="state.dataDiff.paramJsonOld"
:new-area="state.dataDiff.paramJsonNew"
></CodemirrorEditeDiff>
</a-modal>
</div>
</template>
<style lang="less" scoped>
.diffBack {
display: flex;
flex-direction: row;
justify-content: space-between;
& > div:first-child {
flex: 1;
background: #fa9;
}
& > div:last-child {
flex: 1;
background: #8f8;
}
}
</style>

View File

@@ -1,8 +1,8 @@
import {
addNeConfigData,
delNeConfigData,
editNeConfigData,
} from '@/api/ne/neConfig';
addPtNeConfigData,
delPtNeConfigData,
editPtNeConfigData,
} from '@/api/pt/neConfig';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { Modal, message } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider';
@@ -10,13 +10,12 @@ import { reactive, watch } from 'vue';
/**
* array类型
* @param param { t, treeState, neTypeSelect, fnActiveConfigNode, ruleVerification, modalState, fnModalCancel}
* @param param { t, treeState, fnActiveConfigNode, ruleVerification, modalState, fnModalCancel}
* @returns
*/
export default function useConfigArray({
t,
treeState,
neTypeSelect,
fnActiveConfigNode,
ruleVerification,
modalState,
@@ -67,7 +66,7 @@ export default function useConfigArray({
}
// 特殊SMF-upfid选择
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(row, 'upfId')) {
if (treeState.neType === 'SMF' && Reflect.has(row, 'upfId')) {
const v = row.upfId.value;
if (typeof v === 'string') {
if (v === '') {
@@ -104,7 +103,7 @@ export default function useConfigArray({
const loc = `${from['index']['value']}`;
// 特殊SMF-upfid选择
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(from, 'upfId')) {
if (treeState.neType === 'SMF' && Reflect.has(from, 'upfId')) {
const v = from.upfId.value;
if (Array.isArray(v)) {
from.upfId.value = v.join(';');
@@ -132,9 +131,8 @@ export default function useConfigArray({
// 发送
const hide = message.loading(t('common.loading'), 0);
editNeConfigData({
neType: neTypeSelect.value[0],
neUid: neTypeSelect.value[1],
editPtNeConfigData({
neType: treeState.neType,
paramName: treeState.selectNode.paramName,
paramData: data,
loc: loc,
@@ -172,9 +170,8 @@ export default function useConfigArray({
num: title,
}),
onOk() {
delNeConfigData({
neType: neTypeSelect.value[0],
neUid: neTypeSelect.value[1],
delPtNeConfigData({
neType: treeState.neType,
paramName: treeState.selectNode.paramName,
loc: loc,
}).then(res => {
@@ -212,7 +209,7 @@ export default function useConfigArray({
}
// 特殊SMF-upfid选择
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(row, 'upfId')) {
if (treeState.neType === 'SMF' && Reflect.has(row, 'upfId')) {
const v = row.upfId.value;
if (typeof v === 'string') {
if (v === '') {
@@ -238,7 +235,7 @@ export default function useConfigArray({
/**多列表新增单行确认 */
function arrayAddOk(from: Record<string, any>) {
// 特殊SMF-upfid选择
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(from, 'upfId')) {
if (treeState.neType === 'SMF' && Reflect.has(from, 'upfId')) {
const v = from.upfId.value;
if (Array.isArray(v)) {
from.upfId.value = v.join(';');
@@ -266,9 +263,8 @@ export default function useConfigArray({
// 发送
const hide = message.loading(t('common.loading'), 0);
addNeConfigData({
neType: neTypeSelect.value[0],
neUid: neTypeSelect.value[1],
addPtNeConfigData({
neType: treeState.neType,
paramName: treeState.selectNode.paramName,
paramData: data,
loc: `${from['index']['value']}`,
@@ -371,7 +367,7 @@ export default function useConfigArray({
}
// 特殊SMF-upfid选择
if (neTypeSelect.value[0] === 'SMF' && row.name === 'upfId') {
if (treeState.neType === 'SMF' && row.name === 'upfId') {
const v = row.value;
if (typeof v === 'string') {
if (v === '') {

View File

@@ -1,8 +1,8 @@
import {
addNeConfigData,
editNeConfigData,
delNeConfigData,
} from '@/api/ne/neConfig';
addPtNeConfigData,
delPtNeConfigData,
editPtNeConfigData,
} from '@/api/pt/neConfig';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { Modal, message } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider';
@@ -10,13 +10,12 @@ import { nextTick, reactive } from 'vue';
/**
* array类型的嵌套array
* @param param { t, treeState, neTypeSelect, fnActiveConfigNode, ruleVerification, modalState, arrayState, arrayInitEdit, arrayInitAdd, arrayEditClose}
* @param param { t, treeState, fnActiveConfigNode, ruleVerification, modalState, arrayState, arrayInitEdit, arrayInitAdd, arrayEditClose}
* @returns
*/
export default function useConfigArrayChild({
t,
treeState,
neTypeSelect,
treeState,
fnActiveConfigNode,
ruleVerification,
modalState,
@@ -200,9 +199,8 @@ export default function useConfigArrayChild({
// 发送
const hide = message.loading(t('common.loading'), 0);
editNeConfigData({
neType: neTypeSelect.value[0],
neUid: neTypeSelect.value[1],
editPtNeConfigData({
neType: treeState.neType,
paramName: treeState.selectNode.paramName,
paramData: data,
loc,
@@ -241,9 +239,8 @@ export default function useConfigArrayChild({
num: title,
}),
onOk() {
delNeConfigData({
neType: neTypeSelect.value[0],
neUid: neTypeSelect.value[1],
delPtNeConfigData({
neType: treeState.neType,
paramName: treeState.selectNode.paramName,
loc,
}).then(res => {
@@ -311,9 +308,8 @@ export default function useConfigArrayChild({
// 发送
const hide = message.loading(t('common.loading'), 0);
addNeConfigData({
neType: neTypeSelect.value[0],
neUid: neTypeSelect.value[1],
addPtNeConfigData({
neType: treeState.neType,
paramName: treeState.selectNode.paramName,
paramData: data,
loc,

View File

@@ -1,4 +1,4 @@
import { editNeConfigData } from '@/api/ne/neConfig';
import { editPtNeConfigData } from '@/api/pt/neConfig';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { SizeType } from 'ant-design-vue/es/config-provider';
import { message } from 'ant-design-vue/es';
@@ -6,15 +6,10 @@ import { reactive, toRaw } from 'vue';
/**
* list类型参数处理
* @param param {t, treeState, neTypeSelect, ruleVerification}
* @param param {t, treeState, ruleVerification}
* @returns
*/
export default function useConfigList({
t,
treeState,
neTypeSelect,
ruleVerification,
}: any) {
export default function useConfigList({ t, treeState, ruleVerification }: any) {
/**单列表状态类型 */
type ListStateType = {
/**紧凑型 */
@@ -86,9 +81,8 @@ export default function useConfigList({
// 发送
listState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
editNeConfigData({
neType: neTypeSelect.value[0],
neUid: neTypeSelect.value[1],
editPtNeConfigData({
neType: treeState.neType,
paramName: treeState.selectNode.paramName,
paramData: {
[from['name']]: from['value'],

View File

@@ -0,0 +1,243 @@
import { ptSaveAsDefault, ptResetAsDefault } from '@/api/pt/neConfig';
import {
getPtClassStudents,
stuPtNeConfigApply,
updatePtNeConfigApply,
} from '@/api/pt/neConfigApply';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { Modal } from 'ant-design-vue/es';
import { message } from 'ant-design-vue/lib';
import { computed, reactive } from 'vue';
/**
* 实训教学函数
* @param param 父级传入 {t,fnActiveConfigNode}
* @returns
*/
export default function usePtOptions({ t, fnActiveConfigNode }: any) {
const ptConfigState = reactive({
saveLoading: false,
restLoading: false,
applyLoading: false,
});
/**(管理员)保存网元下所有配置为示例配置 */
function ptConfigSave(neType: string) {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.configManage.configParamForm.ptLoadTip'),
onOk() {
ptConfigState.saveLoading = true;
ptSaveAsDefault(neType, '001')
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
ptConfigState.saveLoading = false;
fnActiveConfigNode('#');
});
},
});
}
/**重置网元下所有配置 */
function ptConfigReset(neType: string) {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.configManage.configParamForm.ptResetTip'),
onOk() {
ptConfigState.restLoading = true;
ptResetAsDefault(neType)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
ptConfigState.restLoading = false;
fnActiveConfigNode('#');
});
},
});
}
/**配置下方应用(学生)申请撤回和(管理/教师)应用退回 */
function ptConfigApply(
neType: string,
status: '0' | '1' | '2' | '3',
student?: string
) {
let result: any;
if (status === '2' || status === '3') {
let from: {
neType: string;
status: string;
student?: string;
backInfo?: string;
} = {
neType,
status: '2',
};
if (student) {
if (status === '2') {
from = { neType, status, student };
}
if (status === '3') {
from = { neType, status, student, backInfo: '请重新检查配置' };
}
}
result = updatePtNeConfigApply(from);
}
if (status === '0' || status === '1') {
result = stuPtNeConfigApply({ neType, status });
}
if (!result) return;
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.configManage.configParamForm.ptApplyStuTip', {
ne: neType,
}),
onOk() {
ptConfigState.applyLoading = true;
result
.then((res: any) => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
// 教师修改学生时改变状态
if (student) {
const item = classState.studentOptionsDef.find(
s => s.value === classState.student
);
if (item) {
item.applyStatus = status;
}
}
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
ptConfigState.applyLoading = false;
});
},
});
}
const classState = reactive<{
/**学生账号 */
student: string | undefined;
/**学生可选择列表 */
studentOptions: {
value: string;
label: string;
applyId: string;
applyStatus: string;
}[];
studentOptionsDef: {
value: string;
label: string;
applyId: string;
applyStatus: string;
}[];
}>({
student: undefined,
studentOptions: [],
studentOptionsDef: [],
});
/**学生选择搜索 */
function studentChange(v: any) {
if (!v) {
Object.assign(classState.studentOptions, classState.studentOptionsDef);
}
fnActiveConfigNode('#');
}
let timeout: any;
/**学生选择搜索 */
function studentSearch(neType: string, val: string) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
if (!val) {
Object.assign(classState.studentOptions, classState.studentOptionsDef);
return;
}
timeout = setTimeout(() => classStudents(neType, val), 500);
}
/**班级学生列表 */
function classStudents(neType: string, val?: string) {
getPtClassStudents({ neType, userName: val }).then(res => {
classState.studentOptions = [];
if (!Array.isArray(res.data) || res.data.length <= 0) {
return;
}
for (const v of res.data) {
classState.studentOptions.push({
value: v.userName,
label: v.userName,
applyId: v.applyId,
applyStatus: v.applyStatus,
});
// 设为最新状态
const item = classState.studentOptionsDef.find(
s => s.value === v.userName
);
if (item) {
item.applyStatus = v.applyStatus;
}
}
if (!val) {
Object.assign(classState.studentOptionsDef, classState.studentOptions);
}
});
}
// 学生状态
const studentStatus = computed(() => {
const item = classState.studentOptionsDef.find(
s => s.value === classState.student
);
if (item) return item.applyStatus;
return '';
});
return {
ptConfigState,
ptConfigSave,
ptConfigReset,
ptConfigApply,
classState,
classStudents,
studentStatus,
studentSearch,
studentChange,
};
}

View File

@@ -1,33 +1,42 @@
<script setup lang="ts">
import { reactive, ref, onMounted, toRaw, watch } from 'vue';
import { useRoute } from 'vue-router';
import {
reactive,
ref,
onMounted,
toRaw,
watch,
defineAsyncComponent,
} from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { ProModal } from 'antdv-pro-modal';
import { message } from 'ant-design-vue/es';
import { message, Modal } 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 { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useNeStore from '@/store/modules/ne';
import useNeInfoStore from '@/store/modules/neinfo';
import useOptions from './hooks/useOptions';
import usePtOptions from './hooks/usePtOptions';
import useConfigList from './hooks/useConfigList';
import useConfigArray from './hooks/useConfigArray';
import useConfigArrayChild from './hooks/useConfigArrayChild';
import useArrayImport from './hooks/useArrayImport';
import useArrayBatchDel from './hooks/useArrayBatchDel';
import { getAllNeConfig, getNeConfigData } from '@/api/ne/neConfig';
const neStore = useNeStore();
import { getAllNeConfig } from '@/api/ne/neConfig';
import {
getPtNeConfigData,
ptContrastAsDefault,
ptExport,
ptExportAll,
} from '@/api/pt/neConfig';
import { isSystemAdmin, hasRoles } from '@/plugins/auth-user';
const neInfoStore = useNeInfoStore();
import saveAs from 'file-saver';
const { t } = useI18n();
const route = useRoute();
const { ruleVerification, smfByUPFIdLoadData, smfByUPFIdOptions } = useOptions({
t,
});
/**网元类型_多neId */
let neCascaderOptions = ref<Record<string, any>[]>([]);
/**网元类型选择 type,neUid */
let neTypeSelect = ref<string[]>(['', '']);
/**网元参数 */
let neSelectOptions = ref<Record<string, any>[]>([]);
/**左侧导航是否可收起 */
let collapsible = ref<boolean>(true);
@@ -41,6 +50,8 @@ function changeCollapsible() {
type TreeStateType = {
/**网元 loading */
loading: boolean;
/**网元类型,教学固定neId:001 */
neType: string;
/**网元配置 tree */
data: DataNode[];
/**选择对应Node一级 tree */
@@ -60,6 +71,7 @@ type TreeStateType = {
let treeState: TreeStateType = reactive({
loading: true,
neType: '',
data: [],
selectNode: {
paramName: '',
@@ -105,11 +117,19 @@ function fnActiveConfigNode(key: string | number) {
treeState.selectNode = JSON.parse(JSON.stringify(param));
//
getNeConfigData({
neType: neTypeSelect.value[0],
neUid: neTypeSelect.value[1],
const query: {
neType: string;
paramName: string | number;
student: string | undefined;
} = {
neType: treeState.neType,
paramName: key,
}).then(res => {
student: undefined,
};
if (hasRoles(['teacher'])) {
query.student = classState.student;
}
getPtNeConfigData(query).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
const ruleArr = param.paramData;
const dataArr = res.data;
@@ -199,7 +219,9 @@ function fnActiveConfigNode(key: string | number) {
}, 300);
} else {
message.warning({
content: `${param.paramDisplay} ${t('views.ne.neConfig.noConfigData')}`,
content: `${param.paramDisplay} ${t(
'views.configManage.configParamForm.noConfigData'
)}`,
duration: 3,
});
}
@@ -208,10 +230,10 @@ function fnActiveConfigNode(key: string | number) {
/**查询配置可选属性值列表 */
function fnGetNeConfig() {
const neType = neTypeSelect.value[0];
const neType = treeState.neType;
if (!neType) {
message.warning({
content: t('views.ne.neConfig.neTypePleace'),
content: t('views.configManage.configParamForm.neTypePleace'),
duration: 3,
});
return;
@@ -219,45 +241,42 @@ function fnGetNeConfig() {
treeState.loading = true;
//
getAllNeConfig(neType).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
const arr = [];
for (const item of res.data) {
//
if (!item['visible']) {
item['visible'] = 'public';
} else if (item['visible'] == 'hide') {
continue;
getAllNeConfig(neType)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
const arr = [];
for (const item of res.data) {
let paramPerms: string[] = [];
if (item.paramPerms) {
paramPerms = item.paramPerms.split(',');
} else {
paramPerms = ['post', 'put', 'delete'];
}
arr.push({
...item,
children: undefined,
title: item.paramDisplay,
key: item.paramName,
paramPerms,
});
}
//
let paramPerms: string[] = [];
if (item.paramPerms) {
paramPerms = item.paramPerms.split(',');
} else {
paramPerms = ['post', 'put', 'delete'];
treeState.data = arr;
treeState.loading = false;
// tag
if (res.data.length > 0) {
const item = JSON.parse(JSON.stringify(treeState.data[0]));
treeState.selectNode = item;
treeState.selectLoading = false;
fnActiveConfigNode(item.key);
}
const title = item.paramDisplay;
//
item.paramDisplay = title.replace(/[└─]+/, '');
arr.push({
...item,
children: undefined,
title: title,
key: item.paramName,
paramPerms,
});
}
treeState.data = arr;
treeState.loading = false;
// tag
if (res.data.length > 0) {
const item = JSON.parse(JSON.stringify(treeState.data[0]));
treeState.selectNode = item;
treeState.selectLoading = false;
fnActiveConfigNode(item.key);
})
.finally(() => {
//
if (hasRoles(['teacher'])) {
classStudents(neType);
}
}
});
});
}
/**对话框对象信息状态类型 */
@@ -326,14 +345,26 @@ watch(
() => modalState.open,
val => {
// SMFUPF id
if (val && neTypeSelect.value[0] === 'SMF') {
smfByUPFIdLoadData(neTypeSelect.value[1]);
if (val && treeState.neType === 'SMF') {
smfByUPFIdLoadData('001');
}
}
);
const {
ptConfigState,
ptConfigSave,
ptConfigReset,
ptConfigApply,
classState,
studentStatus,
studentSearch,
studentChange,
classStudents,
} = usePtOptions({ t, fnActiveConfigNode });
const { tablePagination, listState, listEdit, listEditClose, listEditOk } =
useConfigList({ t, treeState, neTypeSelect, ruleVerification });
useConfigList({ t, treeState, ruleVerification });
const {
arrayState,
@@ -348,7 +379,6 @@ const {
} = useConfigArray({
t,
treeState,
neTypeSelect,
fnActiveConfigNode,
ruleVerification,
modalState,
@@ -366,7 +396,6 @@ const {
} = useConfigArrayChild({
t,
treeState,
neTypeSelect,
fnActiveConfigNode,
ruleVerification,
modalState,
@@ -376,58 +405,265 @@ const {
arrayEditClose,
});
const {
importState,
modalImportOpen,
modalImportClose,
modalImportUpload,
modalImportTemplate,
} = useArrayImport({ t, neTypeSelect, arrayState, fnActiveConfigNode });
//
const OpeateDrawer = defineAsyncComponent(
() => import('./components/OpeateDrawer.vue')
);
const operateDrawer = ref<boolean>(false);
/**打开历史 */
function openOpeateDrawer() {
operateDrawer.value = !operateDrawer.value;
}
const { batchState, modalBatchOpen, modalBatchClose, modalBatchOk } =
useArrayBatchDel({
t,
neTypeSelect,
fnActiveConfigNode,
//
const dataDiffState = reactive({
open: false,
title: t('views.configManage.configParamForm.ptDiff'),
exampleData: '',
data: '',
});
//
function fnDataDiff() {
ptContrastAsDefault({
neType: treeState.neType,
paramName: treeState.selectNode.paramName,
student: classState.student,
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
dataDiffState.data = JSON.stringify(res.data, null, 2);
dataDiffState.exampleData = JSON.stringify(res.exampleData, null, 2);
dataDiffState.title = treeState.selectNode.paramDisplay;
dataDiffState.open = true;
}
});
}
// Excel
function fnDataExport() {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.configManage.configParamForm.ptExportTip'),
onOk() {
const hide = message.loading(t('common.loading'), 0);
ptExport(classState.student)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 2,
});
saveAs(res.data, `config_data_${Date.now()}.xlsx`);
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
})
.finally(() => {
hide();
});
},
});
}
// Excel
function fnDataExportAll() {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.configManage.configParamForm.ptExportTip'),
onOk() {
const hide = message.loading(t('common.loading'), 0);
ptExportAll()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 2,
});
saveAs(res.data, `students_config_data_${Date.now()}.zip`);
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
})
.finally(() => {
hide();
});
},
});
}
onMounted(() => {
//
neCascaderOptions.value = neStore.getNeCascaderOptions.filter((item: any) => {
return !['LMF', 'NEF'].includes(item.value); //
});
if (neCascaderOptions.value.length === 0) {
message.warning({
content: t('common.noData'),
duration: 2,
});
return;
}
// AMF
const queryNeType = route.query.neType as string;
const queryNeUid = route.query.neUid as string;
const item = neCascaderOptions.value.find(s => s.value === queryNeType);
if (item && item.children) {
const info = item.children.find((s: any) => s.neUid === queryNeUid);
if (info) {
neTypeSelect.value = [info.neType, info.neUid];
} else {
// ID
const info = item.children[0];
if (info) {
neTypeSelect.value = [info.neType, info.neUid];
neInfoStore.fnNelist().then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
//
neSelectOptions.value = neInfoStore.getNeSelectOtions.filter(
(item: any) => {
return !['OMC', 'LMF', 'NEF'].includes(item.value);
}
);
if (neSelectOptions.value.length === 0) {
message.warning({
content: t('common.noData'),
duration: 2,
});
return;
}
// AMF
const item = neSelectOptions.value.find(s => s.value === 'AMF');
if (item && item.children) {
const info = item.children[0];
treeState.neType = info.neType;
} else {
const info = neSelectOptions.value[0].children[0];
treeState.neType = info.neType;
}
fnGetNeConfig();
}
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
} else {
const info = neCascaderOptions.value[0].children[0];
neTypeSelect.value = [info.neType, info.neUid];
}
fnGetNeConfig();
});
});
</script>
<template>
<PageContainer>
<a-card
v-if="!isSystemAdmin()"
:bordered="false"
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
>
<a-row :gutter="16">
<a-col :lg="8" :md="12" :xs="24" v-roles:has="['teacher']">
<a-form-item
:label="t('views.configManage.configParamForm.ptApplyShow')"
name="student "
>
<a-select
v-model:value="classState.student"
show-search
:placeholder="t('common.selectPlease')"
:default-active-first-option="false"
:show-arrow="false"
:allow-clear="true"
:filter-option="false"
:not-found-content="null"
:options="classState.studentOptions"
@search="(v:any)=> studentSearch(treeState.neType, v)"
@change="studentChange"
>
<template #option="{ value, label, applyStatus }">
<span>{{ label }} </span>
<a-tag
v-if="applyStatus === '0'"
color="processing"
style="position: absolute; right: 0"
>
{{
applyStatus &&
t('views.configManage.configParamForm.ptApply')
}}
</a-tag>
</template>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<!-- 教师查看学生 -->
<template
v-if="
hasRoles(['teacher']) &&
classState.student &&
studentStatus === '0'
"
>
<a-button
@click="
ptConfigApply(treeState.neType, '2', classState.student)
"
:loading="ptConfigState.applyLoading"
>
{{ t('views.configManage.configParamForm.ptApplyStuNE') }}
</a-button>
<a-button
@click="
ptConfigApply(treeState.neType, '3', classState.student)
"
:loading="ptConfigState.applyLoading"
>
{{ t('views.configManage.configParamForm.ptApplyStuRack') }}
</a-button>
</template>
<a-button
v-roles:has="['admin', 'teacher']"
v-show="!classState.student"
@click="ptConfigApply(treeState.neType, '2')"
:loading="ptConfigState.applyLoading"
>
{{ t('views.configManage.configParamForm.ptApplyNE') }}
</a-button>
<a-button
@click="ptConfigSave(treeState.neType)"
:loading="ptConfigState.saveLoading"
v-roles:has="['admin']"
>
{{ t('views.configManage.configParamForm.ptLoad') }}
</a-button>
<a-button
@click="ptConfigReset(treeState.neType)"
v-show="!classState.student"
:loading="ptConfigState.restLoading"
v-roles:has="['teacher']"
>
{{ t('views.configManage.configParamForm.ptReset') }}
</a-button>
<a-button @click="fnDataExport()" v-roles:has="['teacher']">
{{ t('views.configManage.configParamForm.ptExport') }}
</a-button>
<a-button @click="fnDataExportAll()" v-roles:has="['teacher']">
{{ t('views.configManage.configParamForm.ptExportAll') }}
</a-button>
<!-- 学生 -->
<a-button
@click="ptConfigReset(treeState.neType)"
:loading="ptConfigState.restLoading"
v-roles:has="['student']"
>
{{ t('views.configManage.configParamForm.ptReset') }}
</a-button>
<a-button
@click="ptConfigApply(treeState.neType, '0')"
:loading="ptConfigState.applyLoading"
v-roles:has="['student']"
>
{{
t('views.configManage.configParamForm.ptApplyStu', {
ne: treeState.neType,
})
}}
</a-button>
<a-button @click="fnDataExport()" v-roles:has="['student']">
{{ t('views.configManage.configParamForm.ptExport') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
</a-row>
</a-card>
<a-row :gutter="16">
<a-col
:lg="6"
@@ -439,18 +675,22 @@ onMounted(() => {
<!-- 网元类型 -->
<a-card size="small" :bordered="false" :loading="treeState.loading">
<template #title>
{{ t('views.ne.neConfig.treeTitle') }}
{{ t('views.configManage.configParamForm.treeTitle') }}
<span v-roles:has="['teacher']" class="student-name">
{{ classState.student }}
</span>
</template>
<a-form layout="vertical" autocomplete="off">
<a-form-item name="neId ">
<a-cascader
v-model:value="neTypeSelect"
:options="neCascaderOptions"
<a-select
v-model:value="treeState.neType"
:options="neSelectOptions"
:allow-clear="false"
:disabled="ptConfigState.saveLoading"
@change="fnGetNeConfig"
/>
</a-form-item>
<a-form-item name="treeStateData">
<a-form-item name="listeningPort">
<a-tree
:tree-data="treeState.data"
:selected-keys="[treeState.selectNode.paramName]"
@@ -465,7 +705,7 @@ onMounted(() => {
<a-card
size="small"
:bordered="false"
:body-style="{ maxHeight: '680px', 'overflow-y': 'auto' }"
:body-style="{ maxHeight: '600px', 'overflow-y': 'auto' }"
:loading="treeState.selectLoading"
>
<template #title>
@@ -484,12 +724,42 @@ onMounted(() => {
{{ treeState.selectNode.paramDisplay }}
</a-typography-text>
<a-typography-text type="danger" v-else>
{{ t('views.ne.neConfig.treeSelectTip') }}
{{ t('views.configManage.configParamForm.treeSelectTip') }}
</a-typography-text>
</template>
<template #extra>
<a-space :size="8" align="center" v-show="!treeState.selectLoading">
<a-tooltip placement="topRight">
<span v-roles:has="['teacher', 'student']">
<a-tooltip>
<template #title>
{{ t('views.configManage.configParamForm.ptDiff') }}
</template>
<a-button
type="default"
size="small"
@click.prevent="fnDataDiff()"
>
<template #icon>
<BlockOutlined />
</template>
</a-button>
</a-tooltip>
</span>
<a-tooltip>
<template #title>
{{ t('views.configManage.configParamForm.ptHistory') }}
</template>
<a-button
type="default"
size="small"
@click.prevent="openOpeateDrawer()"
>
<template #icon>
<BarsOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button
type="default"
@@ -518,7 +788,7 @@ onMounted(() => {
>
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'value'">
<a-tooltip placement="bottomLeft">
<a-tooltip placement="topLeft">
<template #title v-if="record.comment">
{{ record.comment }}
</template>
@@ -572,9 +842,10 @@ onMounted(() => {
<template #title> {{ t('common.ok') }} </template>
<a-popconfirm
:title="
t('views.ne.neConfig.editOkTip', {
num: record['display'],
})
t(
'views.configManage.configParamForm.editOkTip',
{ num: record['display'] }
)
"
placement="topRight"
:disabled="listState.confirmLoading"
@@ -612,18 +883,19 @@ onMounted(() => {
@dblclick="listEdit(record)"
>
<template v-if="record['type'] === 'enum'">
{{ JSON.parse(record['filter'])[text] }}
{{ JSON.parse(record['filter'])[text] || '&nbsp;' }}
</template>
<template v-else>{{ `${text}` }}</template>
&nbsp;
<!-- 空格占位 -->
<template v-else>{{ `${text}` || '&nbsp;' }}</template>
<EditOutlined
class="editable-cell__icon"
@click="listEdit(record)"
style="margin-left: 18px"
v-if="
!listState.confirmLoading &&
!['read-only', 'read', 'ro'].includes(record.access)
!['read-only', 'read', 'ro'].includes(
record.access
) &&
!(hasRoles(['teacher']) && classState.student) &&
!listState.confirmLoading
"
/>
</div>
@@ -634,7 +906,7 @@ onMounted(() => {
</a-table>
<!-- array类型 -->
<template v-else-if="treeState.selectNode.paramType === 'array'">
<template v-if="treeState.selectNode.paramType === 'array'">
<a-table
class="table"
row-key="index"
@@ -643,7 +915,7 @@ onMounted(() => {
:size="arrayState.size"
:pagination="tablePagination"
:bordered="true"
:scroll="{ x: arrayState.columnsDnd.length * 200, y: '500px' }"
:scroll="{ x: arrayState.columnsDnd.length * 200, y: 480 }"
@resizeColumn="(w:number, col:any) => (col.width = w)"
:show-expand-column="false"
v-model:expanded-row-keys="arrayState.arrayChildExpandKeys"
@@ -655,48 +927,20 @@ onMounted(() => {
type="primary"
@click.prevent="arrayAdd()"
size="small"
v-if="treeState.selectNode.paramPerms.includes('post')"
v-if="
treeState.selectNode.paramPerms.includes('post') &&
!(hasRoles(['teacher']) && classState.student)
"
>
<template #icon> <PlusOutlined /> </template>
{{ t('common.addText') }}
</a-button>
<TableColumnsDnd
type="ghost"
:columns="treeState.selectNode.paramPerms.includes('get') ? [...arrayState.columns.filter((s:any)=>s.key !== 'index')] : arrayState.columns"
:cache-id="treeState.selectNode.key"
:columns="(treeState.selectNode.paramPerms.includes('get') || (hasRoles(['teacher']) && classState.student)) ? [...arrayState.columns.filter((s:any)=>s.key !== 'index')] : arrayState.columns"
v-model:columns-dnd="arrayState.columnsDnd"
></TableColumnsDnd>
<!-- 特殊导入删除-->
<template
v-if="
['AMF', 'MME'].includes(neTypeSelect[0]) &&
['white_list', 'imeiWhitelist', 'whitelist'].includes(
treeState.selectNode.paramName
)
"
>
<a-button
@click.prevent="
modalImportOpen(
neTypeSelect[0],
treeState.selectNode.paramName
)
"
size="small"
>
<template #icon><ImportOutlined /></template>
{{ t('common.import') }}
</a-button>
<a-button
danger
@click.prevent="
modalBatchOpen(treeState.selectNode.paramName)
"
size="small"
>
<template #icon><DeleteOutlined /></template>
{{ t('views.neData.common.batchDelText') }}
</a-button>
</template>
</a-space>
</template>
@@ -705,7 +949,10 @@ onMounted(() => {
<template v-if="column?.key === 'index'">
<a-space :size="16" align="center">
<a-tooltip
v-if="treeState.selectNode.paramPerms.includes('put')"
v-if="
treeState.selectNode.paramPerms.includes('put') &&
!(hasRoles(['teacher']) && classState.student)
"
>
<template #title>{{ t('common.editText') }}</template>
<a-button type="link" @click.prevent="arrayEdit(text)">
@@ -715,11 +962,7 @@ onMounted(() => {
<a-tooltip
v-if="
treeState.selectNode.paramPerms.includes('delete') &&
!(
neTypeSelect[0] === 'IMS' &&
treeState.selectNode.paramName === 'plmn' &&
text['value'] === 0
)
!(hasRoles(['teacher']) && classState.student)
"
>
<template #title>{{ t('common.deleteText') }}</template>
@@ -730,7 +973,7 @@ onMounted(() => {
</a-space>
</template>
<template v-else-if="text">
<a-tooltip placement="bottomLeft">
<a-tooltip placement="topLeft">
<template #title v-if="text.comment">
{{ text.comment }}
</template>
@@ -744,7 +987,9 @@ onMounted(() => {
"
>
<template #icon><BarsOutlined /></template>
{{ t('views.ne.neConfig.arrayMore') }}
{{
t('views.configManage.configParamForm.arrayMore')
}}
</a-button>
<!--特殊字段拓展显示-->
<span
@@ -768,7 +1013,7 @@ onMounted(() => {
{{ JSON.parse(text['filter'])[text.value] }}
</template>
<template v-else>
{{ `${text.value}` }}
{{ `${text.value}` || '&nbsp;' }}
</template>
</div>
</div>
@@ -804,6 +1049,7 @@ onMounted(() => {
</a-button>
<TableColumnsDnd
type="ghost"
:cache-id="`${treeState.selectNode.key}:${arrayChildState.loc}`"
:columns="[...arrayChildState.columns]"
v-model:columns-dnd="arrayChildState.columnsDnd"
v-if="arrayChildState.loc"
@@ -836,7 +1082,7 @@ onMounted(() => {
</a-space>
</template>
<template v-else-if="text">
<a-tooltip placement="bottomLeft">
<a-tooltip placement="topLeft">
<template #title v-if="text.comment">
{{ text.comment }}
</template>
@@ -844,7 +1090,11 @@ onMounted(() => {
<template v-if="text.array">
<a-button type="default" size="small">
<template #icon><BarsOutlined /></template>
{{ t('views.ne.neConfig.arrayMore') }}
{{
t(
'views.configManage.configParamForm.arrayMore'
)
}}
</a-button>
</template>
@@ -853,7 +1103,7 @@ onMounted(() => {
{{ JSON.parse(text['filter'])[text.value] }}
</template>
<template v-else>
{{ `${text.value}` }}
{{ `${text.value}` || '&nbsp;' }}
</template>
</div>
</div>
@@ -864,10 +1114,6 @@ onMounted(() => {
</template>
</a-table>
</template>
<template v-else>
<a-alert type="warning" show-icon message="No Data" />
</template>
</a-card>
</a-col>
</a-row>
@@ -917,7 +1163,7 @@ onMounted(() => {
<!-- 特殊SMF-upfid选择 -->
<a-select
v-if="
neTypeSelect[0] === 'SMF' &&
treeState.neType === 'SMF' &&
modalState.from[item.name]['name'] === 'upfId'
"
v-model:value="modalState.from[item.name]['value']"
@@ -974,83 +1220,43 @@ onMounted(() => {
</a-form>
</ProModal>
<!-- 上传导入表格数据文件框 -->
<UploadModal
:title="t('common.import')"
@upload="modalImportUpload"
@close="modalImportClose"
v-model:open="importState.open"
: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="modalImportTemplate"
>
{{ t('views.system.user.downloadObj') }}
</a-button>
</a-col>
</a-row>
<a-textarea
:disabled="true"
:hidden="importState.msgArr.length <= 0"
:value="importState.msgArr.join('\r\n')"
:auto-size="{ minRows: 2, maxRows: 8 }"
style="background-color: transparent; color: rgba(0, 0, 0, 0.85)"
/>
</template>
</UploadModal>
<!-- 批量删除框 -->
<ProModal
:drag="true"
<!-- 与示例对比差异 -->
<a-modal
:width="800"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:open="batchState.open"
:title="t('views.neData.common.batchDelText')"
:confirm-loading="batchState.loading"
@ok="modalBatchOk"
@cancel="modalBatchClose"
v-model:open="dataDiffState.open"
:title="dataDiffState.title"
:footer="null"
:body-style="{ padding: 0, maxHeight: '650px', 'overflow-y': 'auto' }"
>
<a-form
name="batchStateForm"
:model="batchState"
layout="horizontal"
:label-col="{ span: 6 }"
:labelWrap="true"
<div
style="
display: flex;
flex-direction: row;
justify-content: space-between;
"
>
<a-row>
<a-col :lg="24" :md="24" :xs="24">
<a-form-item label="Start Index" name="startIndex" required>
<a-input-number
v-model:value="batchState.startIndex"
style="width: 100%"
allow-clear
:min="0"
:maxlength="5"
></a-input-number>
</a-form-item>
</a-col>
<a-col :lg="24" :md="24" :xs="24">
<a-form-item label="Num" name="num" required>
<a-input-number
v-model:value="batchState.num"
style="width: 100%"
:min="1"
:maxlength="5"
placeholder="<=500"
></a-input-number>
</a-form-item>
</a-col>
</a-row>
</a-form>
</ProModal>
<div style="flex: 1; text-align: center">
{{ t('views.configManage.configParamForm.ptDiffExample') }}
</div>
<div style="flex: 1; text-align: center">
{{ t('views.configManage.configParamForm.ptDiffSelf') }}
</div>
</div>
<CodemirrorEditeDiff
:old-area="dataDiffState.exampleData"
:new-area="dataDiffState.data"
></CodemirrorEditeDiff>
</a-modal>
<!-- 历史记录抽屉 -->
<OpeateDrawer
v-model:open="operateDrawer"
:ne-type="treeState.neType"
:param-name="treeState.selectNode.paramName"
:student="classState.student"
></OpeateDrawer>
</PageContainer>
</template>
@@ -1079,4 +1285,11 @@ onMounted(() => {
align-items: center;
}
}
//
.student-name {
font-size: 18px;
color: var(--ant-primary-color);
margin-left: 12px;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,442 @@
<script setup lang="ts">
import { PageContainer } from 'antdv-pro-layout';
import { ColumnsType } from 'ant-design-vue/es/table';
import { message } from 'ant-design-vue/es';
import { reactive, ref, onMounted, onBeforeUnmount, markRaw } from 'vue';
import useI18n from '@/hooks/useI18n';
import { TooltipComponent } from 'echarts/components';
import { GaugeChart } from 'echarts/charts';
import { CanvasRenderer } from 'echarts/renderers';
import * as echarts from 'echarts/core';
import { TitleComponent, LegendComponent } from 'echarts/components';
import { PieChart } from 'echarts/charts';
import { LabelLayout } from 'echarts/features';
import { useRoute } from 'vue-router';
import useAppStore from '@/store/modules/app';
import useDictStore from '@/store/modules/dict';
import { listAllNeInfo } from '@/api/ne/neInfo';
import { parseDateToStr } from '@/utils/date-utils';
const { getDict } = useDictStore();
const appStore = useAppStore();
const route = useRoute();
const { t } = useI18n();
echarts.use([
TooltipComponent,
GaugeChart,
TitleComponent,
LegendComponent,
PieChart,
CanvasRenderer,
LabelLayout,
]);
/**图DOM节点实例对象 */
const statusBar = ref<HTMLElement | undefined>(undefined);
/**图实例对象 */
const statusBarChart = ref<any>(null);
/**网元状态字典数据 */
let indexColor = ref<DictType[]>([
{ label: 'Normal', value: 'normal', tagType: '', tagClass: '#91cc75' },
{
label: 'Abnormal',
value: 'abnormal',
tagType: '',
tagClass: '#ee6666',
},
]);
/**表格字段列 */
//customRender(){} ----单元格处理
let tableColumns: ColumnsType = [
{
title: t('views.index.object'),
dataIndex: 'neName',
align: 'left',
},
{
title: t('views.index.realNeStatus'),
dataIndex: 'serverState',
align: 'left',
key: 'status',
},
{
title: t('views.index.reloadTime'),
dataIndex: 'serverState',
align: 'left',
customRender(opt) {
if (opt.value?.refreshTime) return parseDateToStr(opt.value?.refreshTime);
return '-';
},
},
{
title: t('views.index.version'),
dataIndex: 'serverState',
align: 'left',
customRender(opt) {
return opt.value?.version || '-';
},
},
{
title: t('views.index.serialNum'),
dataIndex: 'serverState',
align: 'left',
customRender(opt) {
return opt.value?.sn || '-';
},
},
{
title: t('views.index.expiryDate'),
dataIndex: 'serverState',
align: 'left',
customRender(opt) {
return opt.value?.expire || '-';
},
},
{
title: t('views.index.ipAddress'),
dataIndex: 'serverState',
align: 'left',
customRender(opt) {
return opt.value?.neIP || '-';
},
},
];
/**表格状态类型 */
type TabeStateType = {
/**加载等待 */
loading: boolean;
/**记录数据 */
data: object[];
/**勾选记录 */
selectedRowKeys: (string | number)[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
data: [],
selectedRowKeys: [],
});
/**表格状态 */
let nfInfo: any = reactive({
obj: 'OMC',
version: appStore.version,
status: t('views.index.normal'),
outTimeDate: '',
serialNum: appStore.serialNum,
});
/**表格状态类型 */
type nfStateType = {
/**主机名 */
hostName: string;
/**操作系统信息 */
osInfo: string;
/**IP地址 */
ipAddress: string;
/**版本 */
version: string;
/**CPU利用率 */
cpuUse: string;
/**内存使用 */
memoryUse: string;
/**用户容量 */
capability: number;
/**序列号 */
serialNum: string;
/**许可证到期日期 */
/* selectedRowKeys: (string | number)[];*/
expiryDate: string;
};
/**网元详细信息 */
let pronInfo: nfStateType = reactive({
hostName: '5gc',
osInfo: 'Linux 5gc 4.15.0-112-generic 2020 x86_64 GNU/Linux',
ipAddress: '-',
version: '-',
cpuUse: '-',
memoryUse: '-',
capability: 0,
serialNum: '-',
expiryDate: '-',
});
/**查询网元状态列表 */
function fnGetList(one: boolean) {
if (tableState.loading) return;
one && (tableState.loading = true);
listAllNeInfo({ bandStatus: true }).then(res => {
tableState.data = res.data;
tableState.loading = false;
var rightNum = 0;
var errorNum = 0;
res.data.forEach((item: any) => {
if (item.serverState.online) {
rightNum++;
} else {
errorNum++;
}
});
const optionData: any = {
title: {
text: '',
subtext: '',
left: 'center',
},
tooltip: {
trigger: 'item',
},
legend: {
orient: 'vertical',
left: 'left',
},
color: indexColor.value.map(item => item.tagClass),
series: [
{
name: t('views.index.realNeStatus'),
type: 'pie',
radius: '70%',
center: ['50%', '50%'],
data: [
{ value: rightNum, name: t('views.index.normal') },
{ value: errorNum, name: t('views.index.abnormal') },
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
label: {},
},
],
};
fnDesign(statusBar.value, optionData);
});
}
function fnDesign(container: HTMLElement | undefined, option: any) {
if (!container) return;
if (!statusBarChart.value) {
statusBarChart.value = markRaw(echarts.init(container, 'light'));
}
option && statusBarChart.value.setOption(option);
// 创建 ResizeObserver 实例
var observer = new ResizeObserver(entries => {
if (statusBarChart.value) {
statusBarChart.value.resize();
}
});
// 监听元素大小变化
observer.observe(container);
}
/**抽屉 网元详细信息 */
const open = ref(false);
const closeDrawer = () => {
open.value = false;
};
/**抽屉 网元详细信息 */
/**监听表格行事件*/
function rowClick(record: any, index: any) {
return {
onClick: (event: any) => {
let pronData = JSON.parse(JSON.stringify(record.serverState));
if (!pronData.online) {
message.error(t('views.index.neStatus'), 2);
return false;
} else {
const totalMemInKB = pronData.mem?.totalMem;
const nfUsedMemInKB = pronData.mem?.nfUsedMem;
const sysMemUsageInKB = pronData.mem?.sysMemUsage;
// 将KB转换为MB
const totalMemInMB = Math.round((totalMemInKB / 1024) * 100) / 100;
const nfUsedMemInMB = Math.round((nfUsedMemInKB / 1024) * 100) / 100;
const sysMemUsageInMB =
Math.round((sysMemUsageInKB / 1024) * 100) / 100;
//渲染详细信息
pronInfo = {
hostName: pronData.hostname,
osInfo: pronData.os,
ipAddress: pronData.neIP,
version: pronData.version,
cpuUse:
pronData.neName +
':' +
pronData.cpu?.nfCpuUsage / 100 +
'%; ' +
'SYS:' +
pronData.cpu?.sysCpuUsage / 100 +
'%',
memoryUse:
'Total:' +
totalMemInMB +
'MB; ' +
pronData.name +
':' +
nfUsedMemInMB +
'MB; SYS:' +
sysMemUsageInMB +
'MB',
capability: pronData.capability,
serialNum: pronData.sn,
expiryDate: pronData.expire,
};
}
open.value = true;
},
};
}
let timer: any;
/**
* 国际化翻译转换
*/
function fnLocale() {
let title = route.meta.title as string;
if (title.indexOf('router.') !== -1) {
title = t(title);
}
appStore.setTitle(title);
}
onMounted(() => {
getDict('index_status')
.then(res => {
if (res.length > 0) {
indexColor.value = res;
}
})
.finally(() => {
fnLocale();
fnGetList(true);
timer = setInterval(() => fnGetList(false), 10000); // 每隔10秒执行一次
});
});
// 在组件卸载之前清除定时器
onBeforeUnmount(() => {
clearInterval(timer);
});
</script>
<template>
<PageContainer :breadcrumb="{}">
<div>
<a-drawer :open="open" @close="closeDrawer" :width="700">
<a-descriptions bordered :column="1" :label-style="{ width: '160px' }">
<a-descriptions-item :label="t('views.index.hostName')">{{
pronInfo.hostName
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.osInfo')">{{
pronInfo.osInfo
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.ipAddress')">{{
pronInfo.ipAddress
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.version')">{{
pronInfo.version
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.capability')">{{
pronInfo.capability
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.cpuUse')">{{
pronInfo.cpuUse
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.memoryUse')">{{
pronInfo.memoryUse
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.serialNum')">{{
pronInfo.serialNum
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.expiryDate')">{{
pronInfo.expiryDate
}}</a-descriptions-item>
</a-descriptions>
</a-drawer>
</div>
<a-row :gutter="16">
<a-col :lg="14" :md="16" :xs="24">
<!-- 表格列表 -->
<a-table
class="table"
row-key="id"
size="small"
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:pagination="false"
:scroll="{ x: true }"
:customRow="rowClick"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'status'">
<div v-if="record.serverState.online">
<a-tag color="blue">{{ t('views.index.normal') }}</a-tag>
</div>
<div v-else>
<a-tag color="pink">{{ t('views.index.abnormal') }}</a-tag>
</div>
</template>
</template>
</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"
>
<div style="width: 100%; min-height: 200px" ref="statusBar"></div>
</a-card>
<a-card
:title="t('views.index.mark')"
style="margin-top: 16px"
size="small"
>
<a-descriptions
bordered
:column="1"
:label-style="{ width: '160px' }"
>
<a-descriptions-item :label="t('views.index.object')">{{
nfInfo.obj
}}</a-descriptions-item>
<template v-if="nfInfo.obj === 'OMC'">
<a-descriptions-item :label="t('views.index.versionNum')">{{
nfInfo.version
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.systemStatus')">{{
nfInfo.status
}}</a-descriptions-item>
</template>
<template v-else>
<a-descriptions-item :label="t('views.index.serialNum')">{{
nfInfo.serialNum
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.expiryDate')">{{
nfInfo.outTimeDate
}}</a-descriptions-item>
</template>
</a-descriptions>
</a-card>
</a-col>
</a-row>
</PageContainer>
</template>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,728 @@
<script setup lang="ts">
import { reactive, onMounted, toRaw, ref, onBeforeUnmount } 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';
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import { ColumnsType } from 'ant-design-vue/es/table';
import useI18n from '@/hooks/useI18n';
import {
RESULT_CODE_ERROR,
RESULT_CODE_SUCCESS,
} from '@/constants/result-constants';
import useDictStore from '@/store/modules/dict';
import { listAMFDataUE, delAMFDataUE, exportAMFDataUE } from '@/api/neData/amf';
import { OptionsType, WS } from '@/plugins/ws-websocket';
import saveAs from 'file-saver';
import PQueue from 'p-queue';
import { hasRoles } from '@/plugins/auth-user';
const { t } = useI18n();
const { getDict } = useDictStore();
const ws = new WS();
const queue = new PQueue({ concurrency: 1, autoStart: true });
/**字典数据 */
let dict: {
/**UE 事件认证代码类型 */
ueAauthCode: DictType[];
/**UE 事件类型 */
ueEventType: DictType[];
/**UE 事件CM状态 */
ueEventCmState: DictType[];
} = reactive({
ueAauthCode: [],
ueEventType: [],
ueEventCmState: [],
});
/**开始结束时间 */
let queryRangePicker = ref<[string, string]>(['', '']);
/**查询参数 */
let queryParams = reactive({
/**网元类型 */
neType: 'AMF',
neId: '001',
eventType: '',
imsi: '',
sortField: 'timestamp',
sortOrder: 'desc',
/**开始时间 */
startTime: '',
/**结束时间 */
endTime: '',
/**当前页数 */
pageNum: 1,
/**每页条数 */
pageSize: 20,
});
/**查询参数重置 */
function fnQueryReset() {
eventTypes.value = [];
queryParams = Object.assign(queryParams, {
eventType: '',
imsi: '',
startTime: '',
endTime: '',
pageNum: 1,
pageSize: 20,
});
queryRangePicker.value = ['', ''];
tablePagination.current = 1;
tablePagination.pageSize = 20;
fnGetList();
}
/**记录类型 */
const eventTypes = ref<string[]>([]);
/**查询记录类型变更 */
function fnQueryEventTypeChange(value: any) {
if (Array.isArray(value)) {
queryParams.eventType = value.join(',');
}
}
/**表格状态类型 */
type TabeStateType = {
/**加载等待 */
loading: boolean;
/**紧凑型 */
size: SizeType;
/**搜索栏 */
seached: boolean;
/**记录数据 */
data: object[];
/**勾选记录 */
selectedRowKeys: (string | number)[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
size: 'middle',
seached: true,
data: [],
selectedRowKeys: [],
});
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
{
title: 'IMSI',
dataIndex: 'eventJSON',
align: 'left',
width: 150,
customRender(opt) {
const eventJSON = opt.value;
return eventJSON.imsi;
},
},
{
title: t('views.dashboard.ue.eventType'),
dataIndex: 'eventType',
key: 'eventType',
align: 'left',
width: 150,
},
{
title: t('views.dashboard.ue.result'),
dataIndex: 'eventJSON',
key: 'result',
align: 'left',
width: 150,
},
{
title: t('views.dashboard.ue.time'),
dataIndex: 'eventJSON',
key: 'time',
align: 'left',
width: 150,
},
{
title: t('common.operate'),
key: 'id',
align: 'left',
},
];
/**表格分页器参数 */
let tablePagination = reactive({
/**当前页数 */
current: 1,
/**每页条数 */
pageSize: 20,
/**默认的每页条数 */
defaultPageSize: 20,
/**指定每页可以显示多少条 */
pageSizeOptions: ['10', '20', '50', '100'],
/**只有一页时是否隐藏分页器 */
hideOnSinglePage: false,
/**是否可以快速跳转至某页 */
showQuickJumper: true,
/**是否可以改变 pageSize */
showSizeChanger: true,
/**数据总数 */
total: 0,
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
onChange: (page: number, pageSize: number) => {
tablePagination.current = page;
tablePagination.pageSize = pageSize;
queryParams.pageNum = page;
queryParams.pageSize = pageSize;
fnGetList();
},
});
/**表格紧凑型变更操作 */
function fnTableSize({ key }: MenuInfo) {
tableState.size = key as SizeType;
}
/**表格多选 */
function fnTableSelectedRowKeys(keys: (string | number)[]) {
tableState.selectedRowKeys = keys;
}
/**对话框对象信息状态类型 */
type ModalStateType = {
/**确定按钮 loading */
confirmLoading: boolean;
/**最大ID值 */
maxId: number;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
confirmLoading: false,
maxId: 0,
});
/**
* 记录删除
* @param id 编号
*/
function fnRecordDelete(id: string) {
if (!id || modalState.confirmLoading) return;
let msg = id;
if (id === '0') {
msg = `${id}... ${tableState.selectedRowKeys.length}`;
id = tableState.selectedRowKeys.join(',');
}
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.dashboard.ue.delTip', { msg }),
onOk() {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
delAMFDataUE(id)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
fnGetList(1);
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
},
});
}
/**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (tableState.loading) return;
tableState.loading = true;
if (pageNum) {
queryParams.pageNum = pageNum;
}
if (!queryRangePicker.value) {
queryRangePicker.value = ['', ''];
}
queryParams.startTime = queryRangePicker.value[0];
queryParams.endTime = queryRangePicker.value[1];
listAMFDataUE(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
// 取消勾选
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
tablePagination.total = res.total;
// 遍历处理cdr字符串数据
tableState.data = res.rows.map(item => {
let eventJSON = item.eventJSON;
if (!eventJSON) {
Reflect.set(item, 'eventJSON', {});
}
try {
eventJSON = JSON.parse(eventJSON);
Reflect.set(item, 'eventJSON', eventJSON);
} catch (error) {
console.error(error);
Reflect.set(item, 'eventJSON', {});
}
return item;
});
// 取最大值ID用作实时累加
if (res.total > 0) {
modalState.maxId = Number(res.rows[0].id);
}
}
tableState.loading = false;
});
}
/**列表导出 */
function fnExportList() {
if (modalState.confirmLoading) return;
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.dashboard.ue.exportTip'),
onOk() {
const hide = message.loading(t('common.loading'), 0);
const querys = toRaw(queryParams);
querys.pageSize = 10000;
exportAMFDataUE(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
saveAs(res.data, `amf_ue_event_export_${Date.now()}.xlsx`);
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
},
});
}
/**实时数据开关 */
const realTimeData = ref<boolean>(false);
/**
* 实时数据
*/
function fnRealTime() {
realTimeData.value = !realTimeData.value;
if (realTimeData.value) {
// 建立链接
const options: OptionsType = {
url: '/ws',
params: {
/**订阅通道组
*
* AMF_UE会话事件(GroupID:1010)
*/
subGroupID: '1010',
},
onmessage: wsMessage,
onerror: wsError,
};
ws.connect(options);
} else {
ws.close();
}
}
/**接收数据后回调 */
function wsError(ev: any) {
// 接收数据后回调
console.error(ev);
}
/**接收数据后回调 */
function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res;
if (code === RESULT_CODE_ERROR) {
console.warn(res.msg);
return;
}
// 订阅组信息
if (!data?.groupId) {
return;
}
// ueEvent AMF_UE会话事件
if (data.groupId === '1010') {
const ueEvent = data.data;
queue.add(async () => {
modalState.maxId += 1;
tableState.data.unshift({
id: modalState.maxId,
neType: ueEvent.neType,
neName: ueEvent.neName, // 空
rmUID: ueEvent.rmUID, // 空
timestamp: ueEvent.timestamp,
eventType: ueEvent.eventType,
eventJSON: ueEvent.eventJSON,
});
tablePagination.total += 1;
if (tableState.data.length > 100) {
tableState.data.pop();
}
await new Promise(resolve => setTimeout(resolve, 800));
});
}
}
onMounted(() => {
// 初始字典数据
Promise.allSettled([
getDict('ue_auth_code'),
getDict('ue_event_type'),
getDict('ue_event_cm_state'),
])
.then(resArr => {
if (resArr[0].status === 'fulfilled') {
dict.ueAauthCode = resArr[0].value;
}
if (resArr[1].status === 'fulfilled') {
dict.ueEventType = resArr[1].value;
}
if (resArr[2].status === 'fulfilled') {
dict.ueEventCmState = resArr[2].value;
}
})
.finally(() => {
// 获取列表数据
fnGetList();
});
});
onBeforeUnmount(() => {
if (ws.state() !== -1) {
ws.close();
}
});
</script>
<template>
<PageContainer>
<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.dashboard.ue.eventType')"
name="eventType "
>
<a-select
v-model:value="eventTypes"
mode="multiple"
:options="dict.ueEventType"
:placeholder="t('common.selectPlease')"
@change="fnQueryEventTypeChange"
></a-select>
</a-form-item>
</a-col>
<a-col :lg="4" :md="12" :xs="24">
<a-form-item label="IMSI" name="imsi ">
<a-input
v-model:value="queryParams.imsi"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="8" :md="12" :xs="24">
<a-form-item
:label="t('views.dashboard.cdr.time')"
name="queryRangePicker"
>
<a-range-picker
v-model:value="queryRangePicker"
allow-clear
bordered
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
value-format="x"
style="width: 100%"
></a-range-picker>
</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>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
{{ t('common.reset') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-card>
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-popconfirm
placement="bottomLeft"
:title="
!realTimeData
? t('views.dashboard.ue.realTimeDataStart')
: t('views.dashboard.ue.realTimeDataStop')
"
ok-text="Yes"
cancel-text="No"
@confirm="fnRealTime()"
>
<a-button type="primary" :danger="realTimeData">
<template #icon><FundOutlined /> </template>
{{
!realTimeData
? t('views.dashboard.ue.realTimeDataStart')
: t('views.dashboard.ue.realTimeDataStop')
}}
</a-button>
</a-popconfirm>
<a-button
type="default"
danger
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')"
v-if="!hasRoles(['student'])"
>
<template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }}
</a-button>
<a-button type="dashed" @click.prevent="fnExportList()">
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
</a-space>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<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-tooltip>
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
<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>
</a-button>
<template #overlay>
<a-menu
:selected-keys="[tableState.size as string]"
@click="fnTableSize"
>
<a-menu-item key="default">
{{ t('common.size.default') }}
</a-menu-item>
<a-menu-item key="middle">
{{ t('common.size.middle') }}
</a-menu-item>
<a-menu-item key="small">
{{ t('common.size.small') }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</template>
<!-- 表格列表 -->
<a-table
class="table"
row-key="id"
:columns="hasRoles(['student']) ? tableColumns.filter((s:any)=>s.key !== 'id'): tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: tableColumns.length * 120, y: 'calc(100vh - 480px)' }"
:row-selection="{
type: 'checkbox',
columnWidth: '48px',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'eventType'">
<DictTag :options="dict.ueEventType" :value="record.eventType" />
</template>
<template v-if="column.key === 'result'">
<span v-if="record.eventType === 'auth-result'">
<DictTag
:options="dict.ueAauthCode"
:value="record.eventJSON.authCode"
/>
</span>
<span v-if="record.eventType === 'detach'">
<span>{{ t('views.dashboard.ue.resultOk') }}</span>
</span>
<span v-if="record.eventType === 'cm-state'">
<DictTag
:options="dict.ueEventCmState"
:value="record.eventJSON.status"
/>
</span>
</template>
<template v-if="column.key === 'time'">
<span
v-if="record.eventType === 'auth-result'"
:title="record.eventJSON.authTime"
>
{{ record.eventJSON.authTime }}
</span>
<span
v-if="record.eventType === 'detach'"
:title="record.eventJSON.detachTime"
>
{{ record.eventJSON.detachTime }}
</span>
<span
v-if="record.eventType === 'cm-state'"
:title="record.eventJSON.changeTime"
>
{{ record.eventJSON.changeTime }}
</span>
</template>
<template v-if="column.key === 'id'">
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.deleteText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordDelete(record.id)"
>
<template #icon>
<DeleteOutlined />
</template>
</a-button>
</a-tooltip>
</a-space>
</template>
</template>
<template #expandedRowRender="{ record }">
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
<a-divider orientation="left">
{{ t('views.dashboard.ue.ueInfo') }}
</a-divider>
<div>
<span>{{ t('views.ne.common.neName') }}: </span>
<span>{{ record.neName }}</span>
</div>
<div>
<span>{{ t('views.ne.common.rmUid') }}: </span>
<span>{{ record.rmUID }}</span>
</div>
<a-divider orientation="left">
{{ t('views.dashboard.ue.rowInfo') }}
</a-divider>
<div>
<span>{{ t('views.dashboard.ue.time') }}: </span>
<span
v-if="record.eventType === 'auth-result'"
:title="record.eventJSON.authTime"
>
{{ record.eventJSON.authTime }}
</span>
<span
v-if="record.eventType === 'detach'"
:title="record.eventJSON.detachTime"
>
{{ record.eventJSON.detachTime }}
</span>
<span
v-if="record.eventType === 'cm-state'"
:title="record.eventJSON.changeTime"
>
{{ record.eventJSON.changeTime }}
</span>
</div>
<div>
<span>{{ t('views.dashboard.ue.eventType') }}: </span>
<DictTag :options="dict.ueEventType" :value="record.eventType" />
</div>
<div>
<span>{{ t('views.dashboard.ue.result') }}: </span>
<span v-if="record.eventType === 'auth-result'">
<DictTag
:options="dict.ueAauthCode"
:value="record.eventJSON.authCode"
/>
</span>
<span v-if="record.eventType === 'detach'">
{{ t('views.dashboard.ue.resultOk') }}
</span>
<span v-if="record.eventType === 'cm-state'">
<DictTag
:options="dict.ueEventCmState"
:value="record.eventJSON.status"
/>
</span>
</div>
</div>
</template>
</a-table>
</a-card>
</PageContainer>
</template>
<style lang="less" scoped>
.table :deep(.ant-pagination) {
padding: 0 24px;
}
</style>

View File

@@ -5,78 +5,59 @@ import { message, Modal } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider';
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import { ColumnsType } from 'ant-design-vue/es/table';
import useDictStore from '@/store/modules/dict';
import useI18n from '@/hooks/useI18n';
import {
RESULT_CODE_ERROR,
RESULT_CODE_SUCCESS,
} from '@/constants/result-constants';
import useNeStore from '@/store/modules/ne';
import useDictStore from '@/store/modules/dict';
import useNeInfoStore from '@/store/modules/neinfo';
import {
delSMSCDataCDR,
exportSMSCDataCDR,
listSMSCDataCDR,
} from '@/api/neDataNf/smsc';
import { parseDateToStr } from '@/utils/date-utils';
delIMSDataCDR,
exportIMSDataCDR,
listIMSDataCDR,
} from '@/api/neData/ims';
import { parseDateToStr, parseDuration } from '@/utils/date-utils';
import { OptionsType, WS } from '@/plugins/ws-websocket';
import saveAs from 'file-saver';
import PQueue from 'p-queue';
import { useClipboard } from '@vueuse/core';
import { hasPermissions } from '@/plugins/auth-user';
import dayjs, { type Dayjs } from 'dayjs';
import { WS_GROUP_SMSC_CDR } from '@/constants/ne-constants';
const { copy } = useClipboard({ legacy: true });
const { getDict } = useDictStore();
const neStore = useNeStore();
import { hasRoles } from '@/plugins/auth-user';
const { t } = useI18n();
const { getDict } = useDictStore();
const ws = new WS();
const queue = new PQueue({ concurrency: 1, autoStart: true });
/**字典数据 */
let dict: {
/**CDR 响应原因代码类别类型 */
cdrCauseCode: DictType[];
/**CDR SIP响应代码类别类型 */
cdrSipCode: DictType[];
/**CDR 呼叫类型 */
cdrCallType: DictType[];
} = reactive({
cdrCauseCode: [],
cdrSipCode: [],
cdrCallType: [],
});
/**网元可选 */
let neOtions = ref<Record<string, any>[]>([]);
/**开始结束时间 */
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([
dayjs().startOf('hour'),
dayjs().endOf('hour'),
]);
/**时间范围 */
let rangePickerPresets = ref([
{
label: 'Now hour',
value: [dayjs().startOf('hour'), dayjs().endOf('hour')],
},
{ label: 'Today', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
{
label: 'Yesterday',
value: [
dayjs().subtract(1, 'day').startOf('day'),
dayjs().subtract(1, 'day').endOf('day'),
],
},
]);
let queryRangePicker = ref<[string, string]>(['', '']);
/**查询参数 */
let queryParams = reactive({
/**网元标识 */
neUid: undefined,
/**网元类型 */
neType: 'IMS',
neId: '001',
recordType: '',
callerParty: '',
calledParty: '',
sortField: 'timestamp',
sortOrder: 'desc',
/**开始时间 */
beginTime: undefined as undefined | number,
startTime: '',
/**结束时间 */
endTime: undefined as undefined | number,
endTime: '',
/**当前页数 */
pageNum: 1,
/**每页条数 */
@@ -90,12 +71,12 @@ function fnQueryReset() {
recordType: '',
callerParty: '',
calledParty: '',
beginTime: undefined,
endTime: undefined,
startTime: '',
endTime: '',
pageNum: 1,
pageSize: 20,
});
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')];
queryRangePicker.value = ['', ''];
tablePagination.current = 1;
tablePagination.pageSize = 20;
fnGetList();
@@ -155,19 +136,16 @@ let tableColumns: ColumnsType = [
{
title: t('views.dashboard.cdr.type'),
dataIndex: 'cdrJSON',
key: 'callType',
align: 'left',
width: 150,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.serviceType;
},
width: 100,
},
{
title: t('views.dashboard.cdr.caller'),
dataIndex: 'cdrJSON',
key: 'callerParty',
align: 'left',
width: 150,
width: 120,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.callerParty;
@@ -178,31 +156,56 @@ let tableColumns: ColumnsType = [
dataIndex: 'cdrJSON',
key: 'calledParty',
align: 'left',
width: 150,
width: 120,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.calledParty;
},
},
{
title: t('views.dashboard.cdr.result'),
dataIndex: 'cdrJSON',
key: 'cause',
align: 'left',
width: 200,
width: 150,
},
{
title: t('views.dashboard.cdr.time'),
title: t('views.dashboard.cdr.duration'),
dataIndex: 'cdrJSON',
key: 'callDuration',
align: 'left',
width: 100,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.callType === 'sms'
? '-'
: parseDuration(cdrJSON.callDuration);
},
},
{
title: t('views.dashboard.cdr.seizureTime'),
dataIndex: 'cdrJSON',
align: 'left',
width: 200,
customRender(opt) {
const cdrJSON = opt.value;
if (typeof cdrJSON.updateTime === 'number') {
return parseDateToStr(+cdrJSON.updateTime * 1000);
if (typeof cdrJSON.seizureTime === 'number') {
return parseDateToStr(+cdrJSON.seizureTime * 1000);
}
return cdrJSON.updateTime;
return cdrJSON.seizureTime;
},
},
{
title: t('views.dashboard.cdr.releaseTime'),
dataIndex: 'cdrJSON',
align: 'left',
width: 200,
customRender(opt) {
const cdrJSON = opt.value;
if (typeof cdrJSON.releaseTime === 'number') {
return parseDateToStr(+cdrJSON.releaseTime * 1000);
}
return cdrJSON.releaseTime;
},
},
{
@@ -254,11 +257,14 @@ function fnTableSelectedRowKeys(keys: (string | number)[]) {
type ModalStateType = {
/**确定按钮 loading */
confirmLoading: boolean;
/**最大ID值 */
maxId: number;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
confirmLoading: false,
maxId: 0,
});
/**
@@ -279,7 +285,7 @@ function fnRecordDelete(id: string) {
onOk() {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
delSMSCDataCDR(id)
delIMSDataCDR(id)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
@@ -302,18 +308,6 @@ function fnRecordDelete(id: string) {
});
}
/**
* 复制CDR
* @param jsonStr JSON字符串
*/
function fnRecordCopy(jsonStr: string) {
if (!jsonStr) return;
const text = JSON.stringify(jsonStr, null, 2);
copy(text).then(() => {
message.success(t('common.copyOk'), 3);
});
}
/**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (tableState.loading) return;
@@ -321,30 +315,20 @@ function fnGetList(pageNum?: number) {
if (pageNum) {
queryParams.pageNum = pageNum;
}
//
if (
Array.isArray(queryRangePicker.value) &&
queryRangePicker.value.length > 0
) {
queryParams.beginTime = queryRangePicker.value[0].valueOf();
queryParams.endTime = queryRangePicker.value[1].valueOf();
} else {
queryParams.beginTime = undefined;
queryParams.endTime = undefined;
if (!queryRangePicker.value) {
queryRangePicker.value = ['', ''];
}
listSMSCDataCDR(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data.rows)) {
queryParams.startTime = queryRangePicker.value[0];
queryParams.endTime = queryRangePicker.value[1];
listIMSDataCDR(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
//
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
const { total, rows } = res.data;
tablePagination.total = total;
// tableState.data = rows;
tablePagination.total = res.total;
// cdr
tableState.data = rows.map((item: any) => {
tableState.data = res.rows.map(item => {
let cdrJSON = item.cdrJSON;
if (!cdrJSON) {
Reflect.set(item, 'cdrJSON', {});
@@ -360,6 +344,11 @@ function fnGetList(pageNum?: number) {
return item;
});
// ID
if (res.total > 0) {
modalState.maxId = Number(res.rows[0].id);
}
}
tableState.loading = false;
});
@@ -372,18 +361,17 @@ 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;
exportSMSCDataCDR(querys)
exportIMSDataCDR(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
saveAs(res.data, `smsc_cdr_event_export_${Date.now()}.xlsx`);
saveAs(res.data, `ims_cdr_event_export_${Date.now()}.xlsx`);
} else {
message.error({
content: `${res.msg}`,
@@ -408,7 +396,6 @@ const realTimeData = ref<boolean>(false);
function fnRealTime() {
realTimeData.value = !realTimeData.value;
if (realTimeData.value) {
const neUid = queryParams.neUid;
tableState.seached = false;
//
const options: OptionsType = {
@@ -416,14 +403,12 @@ function fnRealTime() {
params: {
/**
*
* SMSC_CDR会话事件(GroupID:1007_neId)
* IMS_CDR会话事件(GroupID:1005)
*/
subGroupID: `${WS_GROUP_SMSC_CDR}_${neUid}`,
subGroupID: `1005_${queryParams.neId}`,
},
onmessage: wsMessage,
onerror: (ev: any) => {
console.error(ev);
},
onerror: wsError,
};
ws.connect(options);
} else {
@@ -433,6 +418,12 @@ function fnRealTime() {
}
}
/**接收数据后回调 */
function wsError(ev: any) {
//
console.error(ev);
}
/**接收数据后回调 */
function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res;
@@ -445,22 +436,18 @@ function wsMessage(res: Record<string, any>) {
if (!data?.groupId) {
return;
}
const neUid = queryParams.neUid;
// cdrEvent CDR
if (data.groupId === `${WS_GROUP_SMSC_CDR}_${neUid}`) {
if (data.groupId === `1005_${queryParams.neId}`) {
const cdrEvent = data.data;
let cdrJSON = {};
try {
cdrJSON = JSON.parse(cdrEvent.cdrJSON);
} catch (e) {}
queue.add(async () => {
modalState.maxId += 1;
tableState.data.unshift({
id: cdrEvent.id,
id: modalState.maxId,
neType: cdrEvent.neType,
neName: cdrEvent.neName,
rmUID: cdrEvent.rmUID,
timestamp: cdrEvent.timestamp,
cdrJSON: cdrJSON,
cdrJSON: cdrEvent.CDR,
});
tablePagination.total += 1;
if (tableState.data.length > 100) {
@@ -473,36 +460,44 @@ function wsMessage(res: Record<string, any>) {
onMounted(() => {
//
Promise.allSettled([getDict('cdr_cause_code')]).then(resArr => {
if (resArr[0].status === 'fulfilled') {
dict.cdrCauseCode = resArr[0].value;
Promise.allSettled([getDict('cdr_sip_code'), getDict('cdr_call_type')]).then(
resArr => {
if (resArr[0].status === 'fulfilled') {
dict.cdrSipCode = resArr[0].value;
}
if (resArr[1].status === 'fulfilled') {
dict.cdrCallType = resArr[1].value;
}
}
});
);
//
neStore.neCascaderOptions.forEach(item => {
if (item.value === 'SMSC') {
neOtions.value = JSON.parse(JSON.stringify(item.children));
}
});
if (neOtions.value.length === 0) {
message.warning({
content: t('common.noData'),
duration: 2,
useNeInfoStore()
.fnNelist()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
let arr: Record<string, any>[] = [];
res.data.forEach(i => {
if (i.neType === 'IMS') {
arr.push({ value: i.neId, label: i.neName });
}
});
neOtions.value = arr;
if (arr.length > 0) {
queryParams.neId = arr[0].value;
}
}
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
})
.finally(() => {
//
fnGetList();
});
return;
}
if (neOtions.value.length > 0) {
queryParams.neUid = neOtions.value[0].value;
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
//
fnGetList();
});
onBeforeUnmount(() => {
@@ -523,12 +518,11 @@ onBeforeUnmount(() => {
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item label="SMSC" name="neUid ">
<a-form-item label="IMS" name="neId ">
<a-select
v-model:value="queryParams.neUid"
v-model:value="queryParams.neId"
:options="neOtions"
:placeholder="t('common.selectPlease')"
@change="fnQueryReset()"
/>
</a-form-item>
</a-col>
@@ -578,25 +572,25 @@ onBeforeUnmount(() => {
<a-select
v-model:value="recordTypes"
mode="multiple"
:options="['MOSM', 'MTSM'].map(v => ({ value: v }))"
:options="['MOC', 'MTC'].map(v => ({ value: v }))"
:placeholder="t('common.selectPlease')"
@change="fnQueryRecordTypeChange"
></a-select>
</a-form-item>
</a-col>
<a-col :lg="10" :md="12" :xs="24">
<a-col :lg="8" :md="12" :xs="24">
<a-form-item
:label="t('views.dashboard.cdr.time')"
name="queryRangePicker"
>
<a-range-picker
v-model:value="queryRangePicker"
:presets="rangePickerPresets"
:bordered="true"
:allow-clear="false"
style="width: 100%"
allow-clear
bordered
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
value-format="x"
style="width: 100%"
></a-range-picker>
</a-form-item>
</a-col>
@@ -635,7 +629,7 @@ onBeforeUnmount(() => {
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')"
v-perms:has="['cdr:ne:remove']"
v-if="!hasRoles(['student'])"
>
<template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }}
@@ -698,12 +692,12 @@ onBeforeUnmount(() => {
<a-table
class="table"
row-key="id"
:columns="tableColumns"
:columns="hasRoles(['student']) ? tableColumns.filter((s:any)=>s.key !== 'id'): tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: tableColumns.length * 180, y: 'calc(100vh - 480px)' }"
:scroll="{ x: tableColumns.length * 150, y: 'calc(100vh - 480px)' }"
:row-selection="{
type: 'checkbox',
columnWidth: '48px',
@@ -712,11 +706,16 @@ onBeforeUnmount(() => {
}"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'callType'">
<DictTag
:options="dict.cdrCallType"
:value="record.cdrJSON.callType"
/>
</template>
<template v-if="column.key === 'cause'">
<span v-if="record.cdrJSON.result === 0">
{{ t('views.dashboard.cdr.resultFail') }},
<span v-if="record.cdrJSON.callType !== 'sms'">
<DictTag
:options="dict.cdrCauseCode"
:options="dict.cdrSipCode"
:value="record.cdrJSON.cause"
value-default="0"
/>
@@ -727,23 +726,11 @@ onBeforeUnmount(() => {
</template>
<template v-if="column.key === 'id'">
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.copyText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordCopy(record.cdrJSON)"
>
<template #icon>
<CopyOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.deleteText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordDelete(record.id)"
v-perms:has="['cdr:ne:remove']"
>
<template #icon>
<DeleteOutlined />
@@ -755,7 +742,7 @@ onBeforeUnmount(() => {
</template>
<template #expandedRowRender="{ record }">
<a-row :gutter="16">
<a-col :lg="8" :md="12" :xs="22" :offset="2">
<a-col :lg="5" :md="12" :xs="24">
<a-divider orientation="left">
{{ t('views.dashboard.cdr.cdrInfo') }}
</a-divider>
@@ -771,20 +758,30 @@ onBeforeUnmount(() => {
<span>{{ t('views.dashboard.cdr.time') }}: </span>
<span>
{{
typeof record.cdrJSON.updateTime === 'number'
? parseDateToStr(+record.cdrJSON.updateTime * 1000)
: record.cdrJSON.updateTime
typeof record.cdrJSON.releaseTime === 'number'
? parseDateToStr(+record.cdrJSON.releaseTime * 1000)
: record.cdrJSON.releaseTime
}}
</span>
</div>
</a-col>
<a-col :lg="8" :md="12" :xs="24">
<a-col :lg="6" :md="12" :xs="24">
<a-divider orientation="left">
{{ t('views.dashboard.cdr.rowInfo') }}
</a-divider>
<div>
<span>{{ t('views.dashboard.cdr.type') }}: </span>
<span>{{ record.cdrJSON.serviceType }}</span>
<DictTag
:options="dict.cdrCallType"
:value="record.cdrJSON.callType"
/>
</div>
<div>
<span>{{ t('views.dashboard.cdr.duration') }}: </span>
<span v-if="record.cdrJSON.callType !== 'sms'">
{{ parseDuration(record.cdrJSON.callDuration) }}
</span>
<span v-else> - </span>
</div>
<div>
<span>{{ t('views.dashboard.cdr.caller') }}: </span>
@@ -796,10 +793,9 @@ onBeforeUnmount(() => {
</div>
<div>
<span>{{ t('views.dashboard.cdr.result') }}: </span>
<span v-if="record.cdrJSON.result === 0">
{{ t('views.dashboard.cdr.resultFail') }},
<span v-if="record.cdrJSON.callType !== 'sms'">
<DictTag
:options="dict.cdrCauseCode"
:options="dict.cdrSipCode"
:value="record.cdrJSON.cause"
value-default="0"
/>
@@ -808,23 +804,26 @@ onBeforeUnmount(() => {
{{ t('views.dashboard.cdr.resultOk') }}
</span>
</div>
</a-col>
<a-col
:lg="16"
:md="16"
:xs="22"
:offset="2"
v-if="
record.cdrJSON?.smsContent &&
hasPermissions(['cdr:smsc:content'])
"
>
<a-divider orientation="left"> Content </a-divider>
<a-typography-paragraph
copyable
:content="record.cdrJSON.smsContent"
>
</a-typography-paragraph>
<div>
<span>{{ t('views.dashboard.cdr.seizureTime') }}: </span>
<span>
{{
typeof record.cdrJSON.seizureTime === 'number'
? parseDateToStr(+record.cdrJSON.seizureTime * 1000)
: record.cdrJSON.seizureTime
}}
</span>
</div>
<div>
<span>{{ t('views.dashboard.cdr.releaseTime') }}: </span>
<span>
{{
typeof record.cdrJSON.releaseTime === 'number'
? parseDateToStr(+record.cdrJSON.releaseTime * 1000)
: record.cdrJSON.releaseTime
}}
</span>
</div>
</a-col>
</a-row>
</template>

View File

@@ -1,5 +1,4 @@
<script setup lang="ts">
import dayjs, { Dayjs } from 'dayjs';
import { reactive, onMounted, toRaw, ref, onBeforeUnmount } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { message, Modal } from 'ant-design-vue/es';
@@ -12,22 +11,15 @@ import {
RESULT_CODE_SUCCESS,
} from '@/constants/result-constants';
import useDictStore from '@/store/modules/dict';
import useNeStore from '@/store/modules/ne';
import {
listMMEDataUE,
delMMEDataUE,
exportMMEDataUE,
} from '@/api/neDataNf/mme';
import useNeInfoStore from '@/store/modules/neinfo';
import { listMMEDataUE, delMMEDataUE, exportMMEDataUE } from '@/api/neData/mme';
import { parseDateToStr } from '@/utils/date-utils';
import { OptionsType, WS } from '@/plugins/ws-websocket';
import saveAs from 'file-saver';
import PQueue from 'p-queue';
import { useClipboard } from '@vueuse/core';
import { WS_GROUP_MME_UE } from '@/constants/ne-constants';
const { copy } = useClipboard({ legacy: true });
import { hasRoles } from '@/plugins/auth-user';
const { t } = useI18n();
const { getDict } = useDictStore();
const neStore = useNeStore();
const ws = new WS();
const queue = new PQueue({ concurrency: 1, autoStart: true });
@@ -49,38 +41,21 @@ let dict: {
});
/**开始结束时间 */
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([
dayjs().startOf('hour'),
dayjs().endOf('hour'),
]);
/**时间范围 */
let rangePickerPresets = ref([
{
label: 'Now hour',
value: [dayjs().startOf('hour'), dayjs().endOf('hour')],
},
{ label: 'Today', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
{
label: 'Yesterday',
value: [
dayjs().subtract(1, 'day').startOf('day'),
dayjs().subtract(1, 'day').endOf('day'),
],
},
]);
let queryRangePicker = ref<[string, string]>(['', '']);
/**查询参数 */
let queryParams = reactive({
/**网元标识 */
neUid: undefined,
/**网元类型 */
neType: 'MME',
neId: '001',
eventType: '',
imsi: '',
sortField: 'createdTime',
sortField: 'timestamp',
sortOrder: 'desc',
/**开始时间 */
beginTime: undefined as undefined | number,
startTime: '',
/**结束时间 */
endTime: undefined as undefined | number,
endTime: '',
/**当前页数 */
pageNum: 1,
/**每页条数 */
@@ -93,12 +68,12 @@ function fnQueryReset() {
queryParams = Object.assign(queryParams, {
eventType: '',
imsi: '',
beginTime: undefined,
endTime: undefined,
startTime: '',
endTime: '',
pageNum: 1,
pageSize: 20,
});
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')];
queryRangePicker.value = ['', ''];
tablePagination.current = 1;
tablePagination.pageSize = 20;
fnGetList();
@@ -175,11 +150,8 @@ let tableColumns: ColumnsType = [
align: 'left',
width: 150,
customRender(opt) {
const record = opt.value;
if (record?.time) {
return record.time;
}
return parseDateToStr(record.timestamp);
const cdrJSON = opt.value;
return parseDateToStr(+cdrJSON.timestamp * 1000);
},
},
{
@@ -289,30 +261,20 @@ function fnGetList(pageNum?: number) {
if (pageNum) {
queryParams.pageNum = pageNum;
}
//
if (
Array.isArray(queryRangePicker.value) &&
queryRangePicker.value.length > 0
) {
queryParams.beginTime = queryRangePicker.value[0].valueOf();
queryParams.endTime = queryRangePicker.value[1].valueOf();
} else {
queryParams.beginTime = undefined;
queryParams.endTime = undefined;
if (!queryRangePicker.value) {
queryRangePicker.value = ['', ''];
}
queryParams.startTime = queryRangePicker.value[0];
queryParams.endTime = queryRangePicker.value[1];
listMMEDataUE(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data.rows)) {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
//
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
const { total, rows } = res.data;
tablePagination.total = total;
// tableState.data = rows;
tablePagination.total = res.total;
// cdr
tableState.data = rows.map((item: any) => {
tableState.data = res.rows.map(item => {
let eventJSON = item.eventJSON;
if (!eventJSON) {
Reflect.set(item, 'eventJSON', {});
@@ -330,8 +292,8 @@ function fnGetList(pageNum?: number) {
});
// ID
if (total > 0) {
modalState.maxId = Number(rows[0].id);
if (res.total > 0) {
modalState.maxId = Number(res.rows[0].id);
}
}
tableState.loading = false;
@@ -345,7 +307,6 @@ 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;
@@ -372,18 +333,6 @@ function fnExportList() {
});
}
/**
* 复制CDR
* @param jsonStr JSON字符串
*/
function fnRecordCopy(jsonStr: string) {
if (!jsonStr) return;
const text = JSON.stringify(jsonStr, null, 2);
copy(text).then(() => {
message.success(t('common.copyOk'), 3);
});
}
/**实时数据开关 */
const realTimeData = ref<boolean>(false);
@@ -393,7 +342,6 @@ const realTimeData = ref<boolean>(false);
function fnRealTime() {
realTimeData.value = !realTimeData.value;
if (realTimeData.value) {
const neUid = queryParams.neUid;
tableState.seached = false;
//
const options: OptionsType = {
@@ -401,14 +349,12 @@ function fnRealTime() {
params: {
/**
*
* MME_UE会话事件(GroupID:1011_neUid)
* MME_UE会话事件(GroupID:1011)
*/
subGroupID: `${WS_GROUP_MME_UE}_${neUid}`,
subGroupID: `1011_${queryParams.neId}`,
},
onmessage: wsMessage,
onerror: (ev: any) => {
console.error(ev);
},
onerror: wsError,
};
ws.connect(options);
} else {
@@ -418,6 +364,12 @@ function fnRealTime() {
}
}
/**接收数据后回调 */
function wsError(ev: any) {
//
console.error(ev);
}
/**接收数据后回调 */
function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res;
@@ -431,7 +383,7 @@ function wsMessage(res: Record<string, any>) {
return;
}
// ueEvent MME_UE
if (data.groupId === `${WS_GROUP_MME_UE}_${queryParams.neUid}`) {
if (data.groupId === `1011_${queryParams.neId}`) {
const ueEvent = data.data;
queue.add(async () => {
modalState.maxId += 1;
@@ -464,9 +416,9 @@ onMounted(() => {
dict.ueAauthCode = resArr[0].value;
}
if (resArr[1].status === 'fulfilled') {
const ueEventType: any[] = JSON.parse(JSON.stringify(resArr[1].value));
const ueEventType: any[] = JSON.parse(JSON.stringify(resArr[1]));
dict.ueEventType = ueEventType.map(item => {
if (item.value === 'CM') {
if (item.value === 'cm-state') {
item.label = item.label.replace('CM', 'ECM');
}
return item;
@@ -478,29 +430,33 @@ onMounted(() => {
});
//
neStore.neCascaderOptions.forEach(item => {
if (item.value === 'MME') {
neOtions.value = JSON.parse(JSON.stringify(item.children));
}
});
if (neOtions.value.length === 0) {
message.warning({
content: t('common.noData'),
duration: 2,
useNeInfoStore()
.fnNelist()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
let arr: Record<string, any>[] = [];
res.data.forEach(i => {
if (i.neType === 'MME') {
arr.push({ value: i.neId, label: i.neName });
}
});
neOtions.value = arr;
if (arr.length > 0) {
queryParams.neId = arr[0].value;
}
}
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
})
.finally(() => {
//
fnGetList();
});
return;
}
if (neOtions.value.length > 0) {
queryParams.neUid = neOtions.value[0].value;
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
//
fnGetList();
});
onBeforeUnmount(() => {
@@ -521,12 +477,11 @@ onBeforeUnmount(() => {
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item label="MME" name="neUid ">
<a-form-item label="MME" name="neId ">
<a-select
v-model:value="queryParams.neUid"
v-model:value="queryParams.neId"
:options="neOtions"
:placeholder="t('common.selectPlease')"
@change="fnGetList(1)"
/>
</a-form-item>
</a-col>
@@ -544,7 +499,7 @@ onBeforeUnmount(() => {
></a-select>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-col :lg="4" :md="12" :xs="24">
<a-form-item label="IMSI" name="imsi ">
<a-input
v-model:value="queryParams.imsi"
@@ -553,6 +508,22 @@ onBeforeUnmount(() => {
></a-input>
</a-form-item>
</a-col>
<a-col :lg="8" :md="12" :xs="24">
<a-form-item
:label="t('views.dashboard.cdr.time')"
name="queryRangePicker"
>
<a-range-picker
v-model:value="queryRangePicker"
allow-clear
bordered
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
value-format="x"
style="width: 100%"
></a-range-picker>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
@@ -567,22 +538,6 @@ onBeforeUnmount(() => {
</a-space>
</a-form-item>
</a-col>
<a-col :lg="8" :md="12" :xs="24">
<a-form-item
:label="t('views.dashboard.cdr.time')"
name="queryRangePicker"
>
<a-range-picker
v-model:value="queryRangePicker"
:presets="rangePickerPresets"
:bordered="true"
:allow-clear="false"
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-row>
</a-form>
</a-card>
@@ -618,7 +573,7 @@ onBeforeUnmount(() => {
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')"
v-perms:has="['cdr:ne:remove']"
v-if="!hasRoles(['student'])"
>
<template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }}
@@ -681,7 +636,7 @@ onBeforeUnmount(() => {
<a-table
class="table"
row-key="id"
:columns="tableColumns"
:columns="hasRoles(['student']) ? tableColumns.filter((s:any)=>s.key !== 'id'): tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
@@ -699,16 +654,16 @@ onBeforeUnmount(() => {
<DictTag :options="dict.ueEventType" :value="record.eventType" />
</template>
<template v-if="column.key === 'result'">
<span v-if="record.eventType === 'Auth'">
<span v-if="record.eventType === 'auth-result'">
<DictTag
:options="dict.ueAauthCode"
:value="record.eventJSON.result"
/>
</span>
<span v-if="record.eventType === 'Detach'">
<span v-if="record.eventType === 'detach'">
<span>{{ t('views.dashboard.ue.resultOk') }}</span>
</span>
<span v-if="record.eventType === 'CM'">
<span v-if="record.eventType === 'cm-state'">
<DictTag
:options="dict.ueEventCmState"
:value="record.eventJSON.result"
@@ -717,23 +672,11 @@ onBeforeUnmount(() => {
</template>
<template v-if="column.key === 'id'">
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.copyText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordCopy(record.eventJSON)"
>
<template #icon>
<CopyOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.deleteText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordDelete(record.id)"
v-perms:has="['cdr:ne:remove']"
>
<template #icon>
<DeleteOutlined />
@@ -744,60 +687,48 @@ onBeforeUnmount(() => {
</template>
</template>
<template #expandedRowRender="{ record }">
<a-row :gutter="16">
<a-col :lg="8" :md="12" :xs="24" :offset="2">
<a-divider orientation="left">
{{ t('views.dashboard.ue.ueInfo') }}
</a-divider>
<div>
<span>{{ t('views.ne.common.neName') }}: </span>
<span>{{ record.neName }}</span>
</div>
<div>
<span>{{ t('views.ne.common.rmUid') }}: </span>
<span>{{ record.rmUID }}</span>
</div>
</a-col>
<a-col :lg="8" :md="12" :xs="24">
<a-divider orientation="left">
{{ t('views.dashboard.ue.rowInfo') }}
</a-divider>
<div>
<span>{{ t('views.dashboard.ue.time') }}: </span>
<template v-if="record.eventJSON?.time">
{{ record.eventJSON.time }}
</template>
<template v-else>
{{ parseDateToStr(record.eventJSON.timestamp * 1000) }}
</template>
</div>
<div>
<span>{{ t('views.dashboard.ue.eventType') }}: </span>
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
<a-divider orientation="left">
{{ t('views.dashboard.ue.ueInfo') }}
</a-divider>
<div>
<span>{{ t('views.ne.common.neName') }}: </span>
<span>{{ record.neName }}</span>
</div>
<div>
<span>{{ t('views.ne.common.rmUid') }}: </span>
<span>{{ record.rmUID }}</span>
</div>
<a-divider orientation="left">
{{ t('views.dashboard.ue.rowInfo') }}
</a-divider>
<div>
<span>{{ t('views.dashboard.ue.time') }}: </span>
{{ parseDateToStr(record.eventJSON.timestamp * 1000) }}
</div>
<div>
<span>{{ t('views.dashboard.ue.eventType') }}: </span>
<DictTag :options="dict.ueEventType" :value="record.eventType" />
</div>
<div>
<span>{{ t('views.dashboard.ue.result') }}: </span>
<span v-if="record.eventType === 'auth-result'">
<DictTag
:options="dict.ueEventType"
:value="record.eventType"
:options="dict.ueAauthCode"
:value="record.eventJSON.result"
/>
</div>
<div>
<span>{{ t('views.dashboard.ue.result') }}: </span>
<span v-if="record.eventType === 'Auth'">
<DictTag
:options="dict.ueAauthCode"
:value="record.eventJSON.result"
/>
</span>
<span v-if="record.eventType === 'Detach'">
{{ t('views.dashboard.ue.resultOk') }}
</span>
<span v-if="record.eventType === 'CM'">
<DictTag
:options="dict.ueEventCmState"
:value="record.eventJSON.result"
/>
</span>
</div>
</a-col>
</a-row>
</span>
<span v-if="record.eventType === 'detach'">
{{ t('views.dashboard.ue.resultOk') }}
</span>
<span v-if="record.eventType === 'cm-state'">
<DictTag
:options="dict.ueEventCmState"
:value="record.eventJSON.result"
/>
</span>
</div>
</div>
</template>
</a-table>
</a-card>

View File

@@ -78,21 +78,21 @@ const alarmTypeType = ref<any>([
/**告警类型Top数据 */
const alarmTypeTypeTop = ref<any>([
{ neType: 'AMF', total: 0 },
{ neType: 'UDM', total: 0 },
{ neType: 'SMF', total: 0 },
{ name: 'AMF', value: 0 },
{ name: 'UDM', value: 0 },
{ name: 'SMF', value: 0 },
]);
//
function initPicture() {
Promise.allSettled([origGet('Active'), top3Sel('Active')])
Promise.allSettled([origGet(), top3Sel()])
.then(resArr => {
if (resArr[0].status === 'fulfilled') {
const res0 = resArr[0].value;
if (res0.code === RESULT_CODE_SUCCESS && Array.isArray(res0.data)) {
for (const item of res0.data) {
let index = 0;
switch (item.severity) {
switch (item.name) {
case 'Critical':
index = 0;
break;
@@ -109,7 +109,7 @@ function initPicture() {
// index = 4;
// break;
}
alarmTypeType.value[index].value = Number(item.total);
alarmTypeType.value[index].value = Number(item.value);
}
}
}
@@ -119,7 +119,7 @@ function initPicture() {
alarmTypeTypeTop.value = alarmTypeTypeTop.value
.concat(res1.data)
.sort((a: any, b: any) => {
return b.total - a.total;
return b.value - a.value;
})
.slice(0, 3);
}
@@ -210,7 +210,7 @@ function initPicture() {
{ offset: 1, color: '#2f54eb' },
]), //
},
data: alarmTypeTypeTop.value.map((item: any) => item.total),
data: alarmTypeTypeTop.value,
},
],
//
@@ -237,7 +237,7 @@ function initPicture() {
show: false,
},
inverse: true,
data: alarmTypeTypeTop.value.map((item: any) => item.neType),
data: alarmTypeTypeTop.value.map((item: any) => item.name),
axisLabel: {
color: '#A7D6F4',
fontSize: 14,

View File

@@ -52,11 +52,11 @@ const graphNodeTooltip = new Tooltip({
${neState.refreshTime ?? '--'}
</span></div>
<div>========================</div>
<div><strong>UID</strong><span>${neState.neUid}</span></div>
<div><strong>ID</strong><span>${neState.neId}</span></div>
<div><strong>${t('views.monitor.topology.name')}</strong><span>
${neState.neName ?? '--'}
</span></div>
<div><strong>IP</strong><span>${neState.ipAddr}</span></div>
<div><strong>IP</strong><span>${neState.neIP}</span></div>
<div><strong>${t('views.monitor.topology.version')}</strong><span>
${neState.version ?? '--'}
</span></div>
@@ -103,9 +103,9 @@ function handleRanderGraph(
fitView: true,
fitViewPadding: [20],
autoPaint: true,
// modes: {
// default: ['drag-canvas', 'zoom-canvas'],
// },
modes: {
default: ['drag-canvas', 'zoom-canvas'],
},
groupByTypes: false,
nodeStateStyles: {
selected: {

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { onMounted, ref, watch } from 'vue';
import { listKPIData } from '@/api/neData/kpi';
import { listKPIData } from '@/api/perfManage/goldTarget';
import * as echarts from 'echarts/core';
import {
TooltipComponent,
@@ -15,12 +15,9 @@ import { UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
import { markRaw } from 'vue';
import useI18n from '@/hooks/useI18n';
import { parseDateToStr } from '@/utils/date-utils';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import {
upfNeUId,
upfFlowData,
upfFlowParse,
} from '../../hooks/useUPFTotalFlow';
import { upfFlowData, upfFlowParse } from '../../hooks/useUPFTotalFlow';
const { t } = useI18n();
@@ -154,7 +151,7 @@ function handleRanderChart() {
top: '14%',
left: '4%',
right: '4%',
bottom: '16%',
bottom: '12%',
containLabel: true,
},
xAxis: {
@@ -163,7 +160,7 @@ function handleRanderChart() {
data: lineXTime,
axisLabel: {
formatter: function (params: any) {
return params;
return params.split(' ')[1];
},
fontSize: 14,
},
@@ -210,9 +207,9 @@ function fnGetInitData() {
const nowDate = new Date().getTime();
listKPIData({
neUid: upfNeUId.value,
neType: 'UPF',
beginTime: nowDate - 5 * 60 * 1000,
neId: '001',
startTime: nowDate - 5 * 60 * 1000,
endTime: nowDate,
interval: 5, // 5
sortField: 'timeGroup',
@@ -254,9 +251,7 @@ watch(
);
onMounted(() => {
setTimeout(() => {
fnGetInitData();
}, 1000);
fnGetInitData();
// setInterval(() => {
// upfFlowData.value.lineXTime.push(parseDateToStr(new Date()));

View File

@@ -19,15 +19,12 @@ let dict: {
ueEventType: DictType[];
/**UE 事件CM状态 */
ueEventCmState: DictType[];
/**CDR SIP响应代码类别类型 */
cdrSipCodeCause: DictType[];
} = reactive({
cdrSipCode: [],
cdrCallType: [],
ueAauthCode: [],
ueEventType: [],
ueEventCmState: [],
cdrSipCodeCause: [],
});
onMounted(() => {
@@ -38,7 +35,6 @@ onMounted(() => {
getDict('ue_auth_code'),
getDict('ue_event_type'),
getDict('ue_event_cm_state'),
getDict('cdr_sip_code_cause'),
]).then(resArr => {
if (resArr[0].status === 'fulfilled') {
dict.cdrSipCode = resArr[0].value;
@@ -55,9 +51,6 @@ onMounted(() => {
if (resArr[4].status === 'fulfilled') {
dict.ueEventCmState = resArr[4].value;
}
if (resArr[5].status === 'fulfilled') {
dict.cdrSipCodeCause = resArr[5].value;
}
});
});
</script>
@@ -82,7 +75,12 @@ onMounted(() => {
</span>
</div>
<div></div>
<div></div>
<div>
{{ t('views.dashboard.overview.userActivity.time') }}:&nbsp;
<span :title="parseDateToStr(item.data.releaseTime * 1000)">
{{ parseDateToStr(item.data.releaseTime * 1000) }}
</span>
</div>
</div>
<div class="card-cdr-item">
<div>
@@ -103,16 +101,6 @@ onMounted(() => {
</div>
<div v-else></div>
</div>
<div>
{{ t('views.dashboard.overview.userActivity.time') }}:&nbsp;
<span :title="item.data.releaseTime">
{{
typeof item.data.releaseTime === 'number'
? parseDateToStr(+item.data.releaseTime * 1000)
: parseDateToStr(item.data.releaseTime)
}}
</span>
</div>
<div>
{{ t('views.dashboard.overview.userActivity.result') }}:&nbsp;
<span v-if="item.data.callType !== 'sms'">
@@ -121,12 +109,6 @@ onMounted(() => {
:value="item.data.cause"
value-default="0"
/>
&nbsp;-&nbsp;
<DictTag
:options="dict.cdrSipCodeCause"
:value="item.data.cause"
value-default="0"
/>
</span>
<span v-else>
{{ t('views.dashboard.overview.userActivity.resultOK') }}
@@ -149,41 +131,49 @@ onMounted(() => {
<div>
IMSI: <span :title="item.data.imsi">{{ item.data.imsi }}</span>
</div>
<div></div>
<div>
{{ t('views.dashboard.overview.userActivity.time') }}:
<span
v-if="item.type === 'auth-result'"
:title="item.data.authTime"
>
{{ item.data.authTime }}
</span>
<span v-if="item.type === 'detach'" :title="item.data.detachTime">
{{ item.data.detachTime }}
</span>
<span v-if="item.type === 'cm-state'" :title="item.data.changeTime">
{{ item.data.changeTime }}
</span>
</div>
</div>
<div class="card-ue-w33" v-if="item.type === 'Auth'">
<div class="card-ue-w33" v-if="item.type === 'auth-result'">
<div>
GNB ID: <span>{{ item.data.nbId }}</span>
GNB ID: <span>{{ item.data.gNBID }}</span>
</div>
<div>
Cell ID: <span>{{ item.data.cellId }}</span>
Cell ID: <span>{{ item.data.cellID }}</span>
</div>
<div>
TAC ID: <span>{{ item.data.tac }}</span>
TAC ID: <span>{{ item.data.tacID }}</span>
</div>
</div>
<div>
{{ t('views.dashboard.overview.userActivity.time') }}:
<template v-if="item.data?.recordTime">
{{ parseDateToStr(item.data.recordTime) }}
</template>
<template v-else> - </template>
</div>
<div v-if="item.type === 'Auth'">
<div v-if="item.type === 'auth-result'">
{{ t('views.dashboard.overview.userActivity.result') }}:&nbsp;
<span>
<DictTag :options="dict.ueAauthCode" :value="item.data.result" />
<DictTag :options="dict.ueAauthCode" :value="item.data.authCode" />
</span>
</div>
<div v-if="item.type === 'Detach'">
<div v-if="item.type === 'detach'">
{{ t('views.dashboard.overview.userActivity.result') }}:
<span>{{ t('views.dashboard.overview.userActivity.resultOK') }}</span>
</div>
<div class="card-ue-w33" v-if="item.type === 'CM'">
<div class="card-ue-w33" v-if="item.type === 'cm-state'">
{{ t('views.dashboard.overview.userActivity.result') }}:&nbsp;
<span>
<DictTag :options="dict.ueEventCmState" :value="item.data.result" />
<DictTag :options="dict.ueEventCmState" :value="item.data.status" />
</span>
</div>
</div>
@@ -196,7 +186,7 @@ onMounted(() => {
<div class="card-ue-item">
<div>
{{ t('views.dashboard.overview.userActivity.type') }}:&nbsp;
<span v-if="item.type === 'CM'">
<span v-if="item.type === 'cm-state'">
{{
dict.ueEventType
.find(s => s.value === item.type)
@@ -208,40 +198,38 @@ onMounted(() => {
</span>
</div>
<div>
IMSI: <span :title="item.data?.imsi">{{ item.data?.imsi }}</span>
IMSI: <span :title="item.data.imsi">{{ item.data.imsi }}</span>
</div>
<div>
{{ t('views.dashboard.overview.userActivity.time') }}:
<span :title="item.data.timestamp">
{{ parseDateToStr(+item.data.timestamp * 1000) }}
</span>
</div>
<div></div>
</div>
<div class="card-ue-w33" v-if="item.type === 'Auth'">
<div class="card-ue-w33" v-if="item.type === 'auth-result'">
<div>
ENB ID: <span>{{ item.data.nbId }}</span>
ENB ID: <span>{{ item.data.eNBID }}</span>
</div>
<div>
Cell ID: <span>{{ item.data.cellId }}</span>
Cell ID: <span>{{ item.data.cellID }}</span>
</div>
<div>
TAC ID: <span>{{ item.data.tac }}</span>
TAC ID: <span>{{ item.data.tacID }}</span>
</div>
</div>
<div>
{{ t('views.dashboard.overview.userActivity.time') }}:
<template v-if="item.data?.recordTime">
{{ parseDateToStr(item.data.recordTime) }}
</template>
<template v-else> - </template>
</div>
<div v-if="item.type === 'Auth'">
<div v-if="item.type === 'auth-result'">
{{ t('views.dashboard.overview.userActivity.result') }}:&nbsp;
<span>
<DictTag :options="dict.ueAauthCode" :value="item.data.result" />
</span>
</div>
<div v-if="item.type === 'Detach'">
<div v-if="item.type === 'detach'">
{{ t('views.dashboard.overview.userActivity.result') }}:
<span>{{ t('views.dashboard.overview.userActivity.resultOK') }}</span>
</div>
<div class="card-ue-w33" v-if="item.type === 'CM'">
<div class="card-ue-w33" v-if="item.type === 'cm-state'">
{{ t('views.dashboard.overview.userActivity.result') }}:&nbsp;
<span>
<DictTag :options="dict.ueEventCmState" :value="item.data.result" />

View File

@@ -16,8 +16,6 @@
.column {
flex: 3;
position: relative;
display: flex;
flex-direction: column;
}
/* 边框 */
@@ -81,14 +79,13 @@
.upfFlow .inner .chart {
width: 100%;
height: 100%;
margin-top: 0rem;
margin-top: 1rem;
}
/* 网络拓扑 */
.topology {
/* min-height: 27.8rem; */
height: 56.4%;
flex: 1;
}
.topology .inner h3 {
display: flex;
@@ -182,7 +179,6 @@
.userActivity {
/* min-height: 35.8rem; */
height: 54.6%;
flex: 1;
}
.userActivity .inner .chart {
width: 100%;
@@ -263,7 +259,6 @@
.alarmType {
/* min-height: 25rem; */
height: 46%;
flex: 1;
}
.alarmType .inner .chart {
width: 100%;

View File

@@ -52,8 +52,8 @@ export const graphNodeStateNum = computed(() => {
let normal = 0;
let abnormal = 0;
for (const item of graphState.data.nodes) {
const neUid = item.neState.neUid;
if (neUid) {
const neId = item.neState.neId;
if (neId) {
if (item.neState.online) {
normal += 1;
} else {

View File

@@ -1,10 +1,7 @@
import { parseDateToStr } from '@/utils/date-utils';
import { parseSizeFromByte, parseSizeFromKbs } from '@/utils/parse-utils';
import { parseSizeFromBits, parseSizeFromKbs } from '@/utils/parse-utils';
import { ref } from 'vue';
/**当前选的upf的neUid */
export const upfNeUId = ref<any>('');
type FDType = {
/**时间 */
lineXTime: string[];
@@ -26,9 +23,7 @@ export const upfFlowData = ref<FDType>({
/**UPF-流量数据 数据解析 */
export function upfFlowParse(data: Record<string, string>) {
upfFlowData.value.lineXTime.push(
parseDateToStr(+data['timeGroup'], 'HH:mm:ss')
);
upfFlowData.value.lineXTime.push(parseDateToStr(+data['timeGroup']));
const upN3 = parseSizeFromKbs(+data['UPF.03'], 5);
upfFlowData.value.lineYUp.push(upN3[0]);
const downN6 = parseSizeFromKbs(+data['UPF.06'], 5);
@@ -41,6 +36,11 @@ export function upfFlowParse(data: Record<string, string>) {
upfFlowData.value.lineYDown.shift();
upfFlowData.value.cap -= 1;
}
// UPF-总流量数0天 当天24小时
upfTFParse('0', {
up: upfTotalFlow.value['0'].up + +data['UPF.03'],
down: upfTotalFlow.value['0'].down + +data['UPF.06'],
});
}
type TFType = {
@@ -84,9 +84,9 @@ export function upfTFParse(day: string, data: Record<string, number>) {
let { up, down } = data;
upfTotalFlow.value[day] = {
up: up,
upFrom: parseSizeFromByte(up),
upFrom: parseSizeFromBits(up),
down: down,
downFrom: parseSizeFromByte(down),
downFrom: parseSizeFromBits(down),
requestFlag: false,
};
}

View File

@@ -1,12 +1,5 @@
import { ref } from 'vue';
/**当前选的事件的neUid */
export const eventNeUid = ref<Record<string, any>>({
AMF: '',
MME: '',
IMS: '',
});
/**ueEventAMFParse UE会话事件AMF 数据解析 */
function ueEventAMFParse(
item: Record<string, any>
@@ -103,7 +96,11 @@ export function eventListParse(
eventData.value.push(v);
}
}
// 激活选中
// 有数据进行排序
if (eventData.value.length > 5) {
eventData.value.sort((a, b) => b.eTime - a.eTime);
}
if (eventData.value.length > 0) {
eventId.value = eventData.value[0].eId;
}

View File

@@ -1,28 +1,19 @@
import { RESULT_CODE_ERROR } from '@/constants/result-constants';
import { OptionsType, WS } from '@/plugins/ws-websocket';
import { onBeforeUnmount } from 'vue';
import { onBeforeUnmount, onMounted } from 'vue';
import {
eventData,
eventListParse,
eventItemParseAndPush,
userActivityReset,
eventNeUid,
} from './useUserActivity';
import {
upfNeUId,
upfTotalFlow,
upfTFParse,
upfFlowParse,
upfTotalFlowReset,
} from './useUPFTotalFlow';
import { topologyReset, neStateParse, neStateRequestMap } from './useTopology';
import { topologyReset, neStateParse } from './useTopology';
import PQueue from 'p-queue';
import {
WS_GROUP_AMF_UE,
WS_GROUP_IMS_CDR,
WS_GROUP_KPI,
WS_GROUP_MME_UE,
} from '@/constants/ne-constants';
/**websocket连接 */
export default function useWS() {
@@ -36,15 +27,16 @@ export default function useWS() {
/**接收数据后回调 */
function wsMessage(res: Record<string, any>) {
//console.log(res);
// console.log(res);
const { code, requestId, data } = res;
if (code === RESULT_CODE_ERROR) {
console.warn(res.msg);
return;
}
// 网元状态
if (requestId && requestId.startsWith('neState')) {
const neType = requestId.split('_')[2];
const neType = requestId.split('_')[1];
neStateParse(neType, data);
return;
}
@@ -52,62 +44,60 @@ export default function useWS() {
// 普通信息
switch (requestId) {
// AMF_UE会话事件
case 'amf_1010_001':
case 'amf_1010':
if (Array.isArray(data.rows)) {
eventListParse('amf_ue', data);
eventData.value.sort((a, b) => b.eTime - a.eTime);
}
break;
// MME_UE会话事件
case 'mme_1011_001':
if (Array.isArray(data.rows)) {
eventListParse('mme_ue', data);
eventData.value.sort((a, b) => b.eTime - a.eTime);
}
break;
// IMS_CDR会话事件
case 'ims_1005_001':
if (Array.isArray(data.rows)) {
eventListParse('ims_cdr', data);
eventData.value.sort((a, b) => b.eTime - a.eTime);
}
break;
//UPF-总流量数
case `upf_${upfNeUId.value}_0`:
case 'upf_001_0':
upfTFParse('0', data);
break;
case `upf_${upfNeUId.value}_7`:
case 'upf_001_7':
upfTFParse('7', data);
break;
case `upf_${upfNeUId.value}_30`:
case 'upf_001_30':
upfTFParse('30', data);
break;
}
// 订阅组信息
if (!data?.groupId) {
return;
}
switch (data.groupId) {
// kpiEvent 指标UPF
case `${WS_GROUP_KPI}_${upfNeUId.value}`:
case '12_001':
if (data.data) {
upfFlowParse(data.data);
}
break;
// AMF_UE会话事件
case `${WS_GROUP_AMF_UE}_${eventNeUid.value.AMF}`:
case '1010':
if (data.data) {
queue.add(() => eventItemParseAndPush('amf_ue', data.data));
}
break;
// MME_UE会话事件
case `${WS_GROUP_MME_UE}_${eventNeUid.value.MME}`:
case '1011_001':
if (data.data) {
queue.add(() => eventItemParseAndPush('mme_ue', data.data));
}
break;
// IMS_CDR会话事件
case `${WS_GROUP_IMS_CDR}_${eventNeUid.value.IMS}`:
case '1005_001':
if (data.data) {
queue.add(() => eventItemParseAndPush('ims_cdr', data.data));
}
@@ -122,12 +112,13 @@ export default function useWS() {
return;
}
upfTotalFlow.value[day].requestFlag = true;
ws.send({
requestId: `upf_${upfNeUId.value}_${day}`,
requestId: `upf_001_${day}`,
type: 'upf_tf',
data: {
neUid: upfNeUId.value,
neType: 'UPF',
neId: '001',
day: Number(day),
},
});
@@ -137,11 +128,11 @@ export default function useWS() {
function userActivitySend() {
// AMF_UE会话事件
ws.send({
requestId: 'amf_1010_001',
requestId: 'amf_1010',
type: 'amf_ue',
data: {
neUid: eventNeUid.value.AMF,
neType: 'AMF',
neId: '001',
sortField: 'timestamp',
sortOrder: 'desc',
pageNum: 1,
@@ -153,8 +144,8 @@ export default function useWS() {
requestId: 'mme_1011_001',
type: 'mme_ue',
data: {
neUid: eventNeUid.value.MME,
neType: 'MME',
neId: '001',
sortField: 'timestamp',
sortOrder: 'desc',
pageNum: 1,
@@ -166,8 +157,8 @@ export default function useWS() {
requestId: 'ims_1005_001',
type: 'ims_cdr',
data: {
neUid: eventNeUid.value.IMS,
neType: 'IMS',
neId: '001',
recordType: 'MOC',
sortField: 'timestamp',
sortOrder: 'desc',
@@ -177,32 +168,18 @@ export default function useWS() {
});
}
/**重新发送至UPF 10_UPF_neId */
function reSendUPF() {
//初始时时无需还原全部属性以及关闭
if (ws.state() === WebSocket.OPEN) {
ws.close();
// userActivityReset();
upfTotalFlowReset();
neStateRequestMap.value = new Map();
//topologyReset();
}
let subGroupIDArr: string[] = [];
subGroupIDArr.push(`${WS_GROUP_KPI}_${upfNeUId.value}`);
subGroupIDArr.push(`${WS_GROUP_AMF_UE}_${eventNeUid.value.AMF}`);
subGroupIDArr.push(`${WS_GROUP_MME_UE}_${eventNeUid.value.MME}`);
subGroupIDArr.push(`${WS_GROUP_IMS_CDR}_${eventNeUid.value.IMS}`);
onMounted(() => {
const options: OptionsType = {
url: '/ws',
params: {
/**
*
* UPF (GroupID:10_neType_neId)
* AMF_UE会话事件(GroupID:1010_neId)
* UPF (GroupID:12_neId)
* AMF_UE会话事件(GroupID:1010)
* MME_UE会话事件(GroupID:1011_neId)
* IMS_CDR会话事件(GroupID:1005_neId)
*/
subGroupID: subGroupIDArr.join(','),
subGroupID: '12_001,1010,1011_001,1005_001',
},
onmessage: wsMessage,
onerror: (ev: any) => {
@@ -210,20 +187,18 @@ export default function useWS() {
},
};
ws.connect(options);
}
});
onBeforeUnmount(() => {
ws.close();
userActivityReset();
upfTotalFlowReset();
topologyReset();
upfNeUId.value = '';
});
return {
wsSend,
userActivitySend,
upfTFSend,
reSendUPF,
};
}

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 237 B

After

Width:  |  Height:  |  Size: 237 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -9,11 +9,10 @@ import NeResources from './components/NeResources/index.vue';
import UserActivity from './components/UserActivity/index.vue';
import AlarnTypeBar from './components/AlarnTypeBar/index.vue';
import UPFFlow from './components/UPFFlow/index.vue';
import { listUDMSub } from '@/api/neDataNf/udm_sub';
import { listIMSSessionNum } from '@/api/neDataNf/ims';
import { listAMFNblist } from '@/api/neDataNf/amf';
import { listMMENblist } from '@/api/neDataNf/mme';
import { listSMFSubNum } from '@/api/neDataNf/smf';
import { listUDMSub } from '@/api/neData/udm_sub';
import { listUENumBySMF } from '@/api/neUser/smf';
import { listUENumByIMS } from '@/api/neUser/ims';
import { listBase5G } from '@/api/neUser/base5G';
import {
graphNodeClickID,
graphState,
@@ -21,21 +20,17 @@ import {
graphNodeStateNum,
neStateRequestMap,
} from './hooks/useTopology';
import { upfNeUId, upfTotalFlow, upfTFActive } from './hooks/useUPFTotalFlow';
import { upfTotalFlow, upfTFActive } from './hooks/useUPFTotalFlow';
import { useFullscreen } from '@vueuse/core';
import useWS from './hooks/useWS';
import useAppStore from '@/store/modules/app';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { useRouter } from 'vue-router';
import useNeStore from '@/store/modules/ne';
import { message } from 'ant-design-vue';
import { eventNeUid } from './hooks/useUserActivity';
import { hasRoles } from '@/plugins/auth-user';
const router = useRouter();
const appStore = useAppStore();
const neStore = useNeStore();
const { t } = useI18n();
const { wsSend, userActivitySend, upfTFSend, reSendUPF } = useWS();
const { wsSend, userActivitySend, upfTFSend } = useWS();
/**概览状态类型 */
type SkimStateType = {
@@ -66,16 +61,13 @@ let skimState: SkimStateType = reactive({
enbUeNum: 0,
});
/**网元参数 */
let neCascaderOptions = ref<Record<string, any>[]>([]);
/**总览节点 */
const viewportDom = ref<HTMLElement | null>(null);
const { isFullscreen, toggle } = useFullscreen(viewportDom);
let initFlag = false;
/**10s调度器 */
const interval10s = ref<any>(null);
/**5s调度器 */
const interval5s = ref<any>(null);
@@ -84,18 +76,18 @@ function fnGetNeState() {
//
for (const node of graphState.data.nodes) {
if (notNeNodes.includes(node.id)) continue;
const { neType, neUid } = node.neInfo;
if (!neType || !neUid) continue;
const { neType, neId } = node.neInfo;
if (!neType || !neId) continue;
//
if (neStateRequestMap.value.get(neType)) continue;
neStateRequestMap.value.set(neType, true);
wsSend({
requestId: `neState_${neUid}_${neType}`,
requestId: `neState_${neType}_${neId}`,
type: 'ne_state',
data: {
neType: neType,
neUid: neUid,
neId: neId,
},
});
}
@@ -103,133 +95,62 @@ function fnGetNeState() {
/**获取概览信息 */
async function fnGetSkim() {
const neHandlers = new Map([
// [
// 'UDM',
// {
// request: (neUid: string) => listUDMSub({ neUid, pageNum: 1, pageSize: 1 }),
// process: (res: any) =>
// res.code === RESULT_CODE_SUCCESS &&
// (skimState.udmSubNum += res.data.total),
// },
// ],
[
'SMF',
{
request: (neUid: string) => {
eventNeUid.value.SMF = neUid;
return listSMFSubNum(neUid);
},
process: (res: any) => {
if (
res.code === RESULT_CODE_SUCCESS &&
typeof res.data === 'number'
) {
skimState.smfUeNum += res.data;
}
},
},
],
[
'IMS',
{
request: (neUid: string) => {
eventNeUid.value.IMS = neUid;
return listIMSSessionNum(neUid);
},
process: (res: any) => {
if (
res.code === RESULT_CODE_SUCCESS &&
typeof res.data === 'number'
) {
skimState.imsUeNum += res.data;
}
},
},
],
[
'AMF',
{
request: (neUid: string) => {
eventNeUid.value.AMF = neUid;
return listAMFNblist({ neUid });
},
process: (res: any) => {
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
);
}
},
},
],
[
'MME',
{
request: (neUid: string) => {
eventNeUid.value.MME = neUid;
return listMMENblist({ neUid });
},
process: (res: any) => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
skimState.enbNum += res.data.length;
skimState.enbUeNum += res.data.reduce(
(sum: number, item: any) => sum + item.ueNum,
0
);
}
},
},
],
const resArr = await Promise.allSettled([
listUDMSub({
neid: '001',
pageNum: 1,
pageSize: 1,
}),
listUENumBySMF('001'),
listUENumByIMS('001'),
listBase5G({
neType: 'AMF',
neId: '001',
}),
listBase5G({
neType: 'MME',
neId: '001',
}),
]);
const requests = neCascaderOptions.value.flatMap(
(ne: any) =>
ne.children
?.map((child: any) => {
const handler = neHandlers.get(child.neType);
return handler
? {
promise: handler.request(child.neUid),
process: handler.process,
}
: null;
})
.filter(Boolean) || []
);
const results = await Promise.allSettled(requests.map(r => r.promise));
//
Object.assign(skimState, {
// udmSubNum: 0,
smfUeNum: 0,
imsUeNum: 0,
gnbNum: 0,
gnbUeNum: 0,
enbNum: 0,
enbUeNum: 0,
});
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
requests[index].process(result.value);
} else {
requests[index].process(0);
if (resArr[0].status === 'fulfilled') {
const res0 = resArr[0].value;
if (res0.code === RESULT_CODE_SUCCESS) {
skimState.udmSubNum = res0.total;
}
});
// UDM
listUDMSub({
neUid: udmNeUid.value,
pageNum: 1,
pageSize: 1,
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
skimState.udmSubNum = res.data.total;
}
if (resArr[1].status === 'fulfilled') {
const res1 = resArr[1].value;
if (res1.code === RESULT_CODE_SUCCESS) {
skimState.smfUeNum = res1.data;
}
});
}
if (resArr[2].status === 'fulfilled') {
const res2 = resArr[2].value;
if (res2.code === RESULT_CODE_SUCCESS) {
skimState.imsUeNum = res2.data;
}
}
if (resArr[3].status === 'fulfilled') {
const res3 = resArr[3].value;
if (res3.code === RESULT_CODE_SUCCESS) {
skimState.gnbNum = res3.total;
skimState.gnbUeNum = 0;
res3.rows.map((item: any) => {
skimState.gnbUeNum += item.ueNum;
});
}
}
if (resArr[4].status === 'fulfilled') {
const res4 = resArr[4].value;
if (res4.code === RESULT_CODE_SUCCESS) {
skimState.enbNum = res4.total;
skimState.enbUeNum = 0;
res4.rows.map((item: any) => {
skimState.enbUeNum += item.ueNum;
});
}
}
}
/**初始数据函数 */
@@ -242,102 +163,34 @@ function loadData() {
clearInterval(interval10s.value);
interval10s.value = setInterval(() => {
if (!interval10s.value || !initFlag) return;
if (!interval10s.value) 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);
interval5s.value = setInterval(() => {
if (!interval5s.value || !initFlag) return;
if (!interval5s.value) return
fnGetSkim(); //
fnGetNeState(); //
}, 10_000);
}, 5_000);
}
/**栏目信息跳转 */
function fnToRouter(name: string, query?: any) {
if (hasRoles(['student'])) return;
router.push({ name, query });
}
/**网元参数 */
let upfOtions = ref<Record<string, any>[]>([]);
// UPF
function fnSelectNe(value: any, option: any) {
upfNeUId.value = value;
reSendUPF();
// upfTotalFlow.value.map((item: any) => {
// item.requestFlag = false;
// });
for (var key in upfTotalFlow.value) {
upfTotalFlow.value[key].requestFlag = false;
}
// loadData();
}
let udmNeUid = ref<string>('');
let udmOtions = ref<Record<string, any>[]>([]);
/**用户数量-选择UDM */
function fnSelectUDM(e: any) {
udmNeUid.value = e.key;
listUDMSub({
neUid: e.key,
pageNum: 1,
pageSize: 1,
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
skimState.udmSubNum = res.data.total;
}
});
}
// views
const getPopupContainer = () => {
// 使 ref views
// views 使 ref
//
return document.querySelector('.viewport');
};
onMounted(() => {
//
neStore.neCascaderOptions.forEach(item => {
if (item.value === 'UPF') {
upfOtions.value = JSON.parse(JSON.stringify(item.children));
}
if (item.value === 'UDM') {
udmOtions.value = JSON.parse(JSON.stringify(item.children));
}
});
if (upfOtions.value.length > 0) {
fnSelectNe(upfOtions.value[0].value, upfOtions.value[0]);
}
if (udmOtions.value.length > 0) {
fnSelectUDM({ key: udmOtions.value[0].value });
}
//
neCascaderOptions.value = neStore.getNeCascaderOptions.filter((item: any) => {
return ['UDM', 'SMF', 'IMS', 'AMF', 'MME'].includes(item.value);
});
if (neCascaderOptions.value.length === 0) {
message.warning({
content: t('common.noData'),
duration: 2,
});
return;
}
initFlag = true;
fnGetSkim().then(() => {
loadData();
});
@@ -348,7 +201,6 @@ onBeforeUnmount(() => {
interval10s.value = null;
clearInterval(interval5s.value);
interval5s.value = null;
initFlag = false;
});
</script>
@@ -378,45 +230,24 @@ onBeforeUnmount(() => {
<div class="data">
<div
class="item toRouter"
@click="fnToRouter('Sub_2010')"
:title="t('views.dashboard.overview.toRouter')"
v-if="neStore.fnHasNe(['udm'])"
>
<div @click="fnToRouter('UdmSub_2001')">
<div>
<UserOutlined
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
/>
{{ skimState.udmSubNum }}
</div>
<span>
<a-dropdown
:trigger="['click']"
:get-Popup-Container="getPopupContainer"
>
<div class="toDeep-text">
{{ t('views.dashboard.overview.skim.users') }}
<DownOutlined style="margin-left: 12px; font-size: 12px" />
</div>
<template #overlay>
<a-menu @click="fnSelectUDM">
<a-menu-item
v-for="v in udmOtions"
:key="v.value"
:disabled="udmNeUid === v.value"
>
{{ v.label }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
{{ t('views.dashboard.overview.skim.users') }}
</span>
</div>
<div
class="item toRouter"
@click="fnToRouter('ImsSub_2004')"
@click="fnToRouter('Ims_2080')"
:title="t('views.dashboard.overview.toRouter')"
style="margin: 0 12px"
v-perms:has="['dashboard:overview:imsUeNum']"
v-if="neStore.fnHasNe(['ims'])"
>
<div>
<img :src="svgUserIMS" style="width: 18px; margin-right: 8px" />
@@ -428,10 +259,8 @@ onBeforeUnmount(() => {
</div>
<div
class="item toRouter"
@click="fnToRouter('SmfSub_2005')"
@click="fnToRouter('Ue_2081')"
:title="t('views.dashboard.overview.toRouter')"
v-perms:has="['dashboard:overview:smfUeNum']"
v-if="neStore.fnHasNe(['smf'])"
>
<div>
<img :src="svgUserSMF" style="width: 18px; margin-right: 8px" />
@@ -444,11 +273,7 @@ onBeforeUnmount(() => {
</div>
</div>
</div>
<div
class="skim panel base"
v-perms:has="['dashboard:overview:gnbBase']"
v-if="neStore.fnHasNe(['amf'])"
>
<div class="skim panel base">
<div class="inner">
<h3>
<GlobalOutlined style="color: #68d8fe" />&nbsp;&nbsp; 5G
@@ -457,7 +282,7 @@ onBeforeUnmount(() => {
<div class="data">
<div
class="item toRouter"
@click="fnToRouter('BaseStation_2007', { neType: 'AMF' })"
@click="fnToRouter('Base5G_2082', { neType: 'AMF' })"
:title="t('views.dashboard.overview.toRouter')"
>
<div style="align-items: flex-start">
@@ -471,7 +296,7 @@ onBeforeUnmount(() => {
</div>
<div
class="item toRouter"
@click="fnToRouter('BaseStation_2007', { neType: 'AMF' })"
@click="fnToRouter('Base5G_2082', { neType: 'AMF' })"
:title="t('views.dashboard.overview.toRouter')"
>
<div style="align-items: flex-start">
@@ -485,11 +310,7 @@ onBeforeUnmount(() => {
</div>
</div>
</div>
<div
class="skim panel base"
v-perms:has="['dashboard:overview:enbBase']"
v-if="neStore.fnHasNe(['mme'])"
>
<div class="skim panel base">
<div class="inner">
<h3>
<GlobalOutlined style="color: #68d8fe" />&nbsp;&nbsp; 4G
@@ -498,7 +319,7 @@ onBeforeUnmount(() => {
<div class="data">
<div
class="item toRouter"
@click="fnToRouter('BaseStation_2007', { neType: 'MME' })"
@click="fnToRouter('Base5G_2082', { neType: 'MME' })"
:title="t('views.dashboard.overview.toRouter')"
>
<div style="align-items: flex-start">
@@ -512,7 +333,7 @@ onBeforeUnmount(() => {
</div>
<div
class="item toRouter"
@click="fnToRouter('BaseStation_2007', { neType: 'MME' })"
@click="fnToRouter('Base5G_2082', { neType: 'MME' })"
:title="t('views.dashboard.overview.toRouter')"
>
<div style="align-items: flex-start">
@@ -546,23 +367,12 @@ onBeforeUnmount(() => {
<div class="inner">
<h3
class="toRouter"
@click="fnToRouter('GoldTarget_2104')"
:title="t('views.dashboard.overview.toRouter')"
style="display: flex; align-items: center"
>
<AreaChartOutlined style="color: #68d8fe" />&nbsp;&nbsp;
<span @click="fnToRouter('GoldTarget_2104')">{{
t('views.dashboard.overview.upfFlow.title')
}}</span>
<a-select
v-model:value="upfNeUId"
:options="upfOtions"
:get-Popup-Container="getPopupContainer"
class="toDeep"
style="width: 100px; color: #fff; margin-left: auto"
@change="fnSelectNe"
/>
{{ t('views.dashboard.overview.upfFlow.title') }}
</h3>
<div class="chart">
<UPFFlow />
</div>
@@ -632,14 +442,14 @@ onBeforeUnmount(() => {
<ArrowUpOutlined style="color: #597ef7" />
{{ t('views.dashboard.overview.upfFlowTotal.up') }}
</span>
<h4>{{ upfTotalFlow[upfTFActive].upFrom }}</h4>
<h4>{{ upfTotalFlow[upfTFActive].up }}</h4>
</div>
<div class="item">
<span>
<ArrowDownOutlined style="color: #52c41a" />
{{ t('views.dashboard.overview.upfFlowTotal.down') }}
</span>
<h4>{{ upfTotalFlow[upfTFActive].downFrom }}</h4>
<h4>{{ upfTotalFlow[upfTFActive].down }}</h4>
</div>
</div>
</div>
@@ -680,31 +490,4 @@ onBeforeUnmount(() => {
<style lang="less" scoped>
@import url('./css/index.css');
.toDeep {
--editor-background-color: blue;
}
.toDeep :deep(.ant-select-selector) {
background-color: #101129;
border: none;
}
.toDeep :deep(.ant-select-arrow) {
color: #fff;
}
.toDeep :deep(.ant-select-selection-item) {
color: #fff;
}
.toDeep-text {
color: #4c9bfd !important;
font-size: 0.844rem !important;
position: relative !important;
line-height: 2rem !important;
white-space: nowrap !important;
text-align: start !important;
text-overflow: ellipsis !important;
overflow: hidden !important;
}
</style>

View File

@@ -5,7 +5,7 @@ import { Modal, message } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider';
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import { ColumnsType } from 'ant-design-vue/es/table';
import useNeStore from '@/store/modules/ne';
import useNeInfoStore from '@/store/modules/neinfo';
import useI18n from '@/hooks/useI18n';
import {
RESULT_CODE_ERROR,
@@ -15,16 +15,12 @@ import {
delSMFDataCDR,
exportSMFDataCDR,
listSMFDataCDR,
} from '@/api/neDataNf/smf';
} from '@/api/neData/smf';
import { OptionsType, WS } from '@/plugins/ws-websocket';
import PQueue from 'p-queue';
import saveAs from 'file-saver';
import dayjs, { type Dayjs } from 'dayjs';
import { useClipboard } from '@vueuse/core';
import { WS_GROUP_SMF_CDR } from '@/constants/ne-constants';
const { copy } = useClipboard({ legacy: true });
import { hasRoles } from '@/plugins/auth-user';
const { t } = useI18n();
const neStore = useNeStore();
const ws = new WS();
const queue = new PQueue({ concurrency: 1, autoStart: true });
@@ -32,37 +28,20 @@ const queue = new PQueue({ concurrency: 1, autoStart: true });
let neOtions = ref<Record<string, any>[]>([]);
/**开始结束时间 */
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([
dayjs().startOf('hour'),
dayjs().endOf('hour'),
]);
/**时间范围 */
let rangePickerPresets = ref([
{
label: 'Now hour',
value: [dayjs().startOf('hour'), dayjs().endOf('hour')],
},
{ label: 'Today', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
{
label: 'Yesterday',
value: [
dayjs().subtract(1, 'day').startOf('day'),
dayjs().subtract(1, 'day').endOf('day'),
],
},
]);
let queryRangePicker = ref<[string, string]>(['', '']);
/**查询参数 */
let queryParams = reactive({
/**网元标识 */
neUid: undefined,
/**网元类型 */
neType: 'SMF',
neId: '001',
subscriberID: '',
sortField: 'createdTime',
sortField: 'timestamp',
sortOrder: 'desc',
/**开始时间 */
beginTime: undefined as undefined | number,
startTime: '',
/**结束时间 */
endTime: undefined as undefined | number,
endTime: '',
/**当前页数 */
pageNum: 1,
/**每页条数 */
@@ -73,10 +52,12 @@ let queryParams = reactive({
function fnQueryReset() {
queryParams = Object.assign(queryParams, {
subscriberID: '',
startTime: '',
endTime: '',
pageNum: 1,
pageSize: 20,
});
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')];
queryRangePicker.value = ['', ''];
tablePagination.current = 1;
tablePagination.pageSize = 20;
fnGetList();
@@ -106,7 +87,7 @@ let tableState: TabeStateType = reactive({
});
/**表格字段列 */
let tableColumns = ref<ColumnsType>([
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
@@ -114,7 +95,7 @@ let tableColumns = ref<ColumnsType>([
width: 100,
},
{
title: t('views.dashboard.cdr.chargingID'), // ID
title: t('views.dashboard.cdr.smfChargingID'), // ID
dataIndex: 'cdrJSON',
align: 'left',
width: 100,
@@ -157,15 +138,11 @@ let tableColumns = ref<ColumnsType>([
) {
return 0;
}
let dataVolumeUplink = 0;
for (const v of listOfMultipleUnitUsage) {
if (Array.isArray(v.usedUnitContainer)) {
for (const used of v.usedUnitContainer) {
dataVolumeUplink += +used.dataVolumeUplink;
}
}
const usedUnitContainer = listOfMultipleUnitUsage[0].usedUnitContainer;
if (!Array.isArray(usedUnitContainer) || usedUnitContainer.length < 1) {
return 0;
}
return dataVolumeUplink;
return usedUnitContainer[0].dataVolumeUplink;
},
},
{
@@ -182,15 +159,11 @@ let tableColumns = ref<ColumnsType>([
) {
return 0;
}
let dataVolumeDownlink = 0;
for (const v of listOfMultipleUnitUsage) {
if (Array.isArray(v.usedUnitContainer)) {
for (const used of v.usedUnitContainer) {
dataVolumeDownlink += +used.dataVolumeDownlink;
}
}
const usedUnitContainer = listOfMultipleUnitUsage[0].usedUnitContainer;
if (!Array.isArray(usedUnitContainer) || usedUnitContainer.length < 1) {
return 0;
}
return dataVolumeDownlink;
return usedUnitContainer[0].dataVolumeDownlink;
},
},
{
@@ -207,19 +180,15 @@ let tableColumns = ref<ColumnsType>([
) {
return 0;
}
let dataTotalVolume = 0;
for (const v of listOfMultipleUnitUsage) {
if (Array.isArray(v.usedUnitContainer)) {
for (const used of v.usedUnitContainer) {
dataTotalVolume += +used.dataTotalVolume;
}
}
const usedUnitContainer = listOfMultipleUnitUsage[0].usedUnitContainer;
if (!Array.isArray(usedUnitContainer) || usedUnitContainer.length < 1) {
return 0;
}
return dataTotalVolume;
return usedUnitContainer[0].dataTotalVolume;
},
},
{
title: t('views.dashboard.cdr.durationTime'), //
title: t('views.dashboard.cdr.smfDuration'), //
dataIndex: 'cdrJSON',
align: 'left',
width: 100,
@@ -229,7 +198,7 @@ let tableColumns = ref<ColumnsType>([
},
},
{
title: t('views.dashboard.cdr.invocationTime'), //
title: t('views.dashboard.cdr.smfInvocationTime'), //
dataIndex: 'cdrJSON',
align: 'left',
width: 200,
@@ -243,7 +212,7 @@ let tableColumns = ref<ColumnsType>([
key: 'id',
align: 'left',
},
]);
];
/**表格分页器参数 */
let tablePagination = reactive({
@@ -287,11 +256,14 @@ function fnTableSelectedRowKeys(keys: (string | number)[]) {
type ModalStateType = {
/**确定按钮 loading */
confirmLoading: boolean;
/**最大ID值 */
maxId: number;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
confirmLoading: false,
maxId: 0,
});
/**
@@ -335,18 +307,6 @@ function fnRecordDelete(id: string) {
});
}
/**
* 复制CDR
* @param jsonStr JSON字符串
*/
function fnRecordCopy(jsonStr: string) {
if (!jsonStr) return;
const text = JSON.stringify(jsonStr, null, 2);
copy(text).then(() => {
message.success(t('common.copyOk'), 3);
});
}
/**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (tableState.loading) return;
@@ -354,30 +314,20 @@ function fnGetList(pageNum?: number) {
if (pageNum) {
queryParams.pageNum = pageNum;
}
//
if (
Array.isArray(queryRangePicker.value) &&
queryRangePicker.value.length > 0
) {
queryParams.beginTime = queryRangePicker.value[0].valueOf();
queryParams.endTime = queryRangePicker.value[1].valueOf();
} else {
queryParams.beginTime = undefined;
queryParams.endTime = undefined;
if (!queryRangePicker.value) {
queryRangePicker.value = ['', ''];
}
queryParams.startTime = queryRangePicker.value[0];
queryParams.endTime = queryRangePicker.value[1];
listSMFDataCDR(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data.rows)) {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
//
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
const { total, rows } = res.data;
tablePagination.total = total;
// tableState.data = rows;
tablePagination.total = res.total;
// cdr
tableState.data = rows.map((item: any) => {
tableState.data = res.rows.map(item => {
let cdrJSON = item.cdrJSON;
if (!cdrJSON) {
Reflect.set(item, 'cdrJSON', {});
@@ -393,6 +343,11 @@ function fnGetList(pageNum?: number) {
return item;
});
// ID
if (res.total > 0) {
modalState.maxId = Number(res.rows[0].id);
}
}
tableState.loading = false;
});
@@ -405,7 +360,6 @@ 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;
@@ -441,7 +395,6 @@ const realTimeData = ref<boolean>(false);
function fnRealTime() {
realTimeData.value = !realTimeData.value;
if (realTimeData.value) {
const neUid = queryParams.neUid;
tableState.seached = false;
//
const options: OptionsType = {
@@ -451,12 +404,10 @@ function fnRealTime() {
*
* CDR会话事件-SMF (GroupID:1006)
*/
subGroupID: `${WS_GROUP_SMF_CDR}_${neUid}`,
subGroupID: `1006_${queryParams.neId}`,
},
onmessage: wsMessage,
onerror: (ev: any) => {
console.error(ev);
},
onerror: wsError,
};
ws.connect(options);
} else {
@@ -466,6 +417,12 @@ function fnRealTime() {
}
}
/**接收数据后回调 */
function wsError(ev: any) {
//
console.error(ev);
}
/**接收数据后回调 */
function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res;
@@ -478,22 +435,18 @@ function wsMessage(res: Record<string, any>) {
if (!data?.groupId) {
return;
}
const neUid = queryParams.neUid;
// cdrEvent CDR
if (data.groupId === `${WS_GROUP_SMF_CDR}_${neUid}`) {
if (data.groupId === `1006_${queryParams.neId}`) {
const cdrEvent = data.data;
let cdrJSON = {};
try {
cdrJSON = JSON.parse(cdrEvent.cdrJSON);
} catch (e) {}
queue.add(async () => {
modalState.maxId += 1;
tableState.data.unshift({
id: cdrEvent.id,
id: modalState.maxId,
neType: cdrEvent.neType,
neName: cdrEvent.neName,
rmUID: cdrEvent.rmUID,
timestamp: cdrEvent.timestamp,
cdrJSON: cdrJSON,
cdrJSON: cdrEvent.CDR,
});
tablePagination.total += 1;
if (tableState.data.length > 100) {
@@ -506,29 +459,33 @@ function wsMessage(res: Record<string, any>) {
onMounted(() => {
//
neStore.neCascaderOptions.forEach(item => {
if (item.value === 'SMF') {
neOtions.value = JSON.parse(JSON.stringify(item.children));
}
});
if (neOtions.value.length === 0) {
message.warning({
content: t('common.noData'),
duration: 2,
useNeInfoStore()
.fnNelist()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
let arr: Record<string, any>[] = [];
res.data.forEach(i => {
if (i.neType === 'SMF') {
arr.push({ value: i.neId, label: i.neName });
}
});
neOtions.value = arr;
if (arr.length > 0) {
queryParams.neId = arr[0].value;
}
}
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
})
.finally(() => {
//
fnGetList();
});
return;
}
if (neOtions.value.length > 0) {
queryParams.neUid = neOtions.value[0].value;
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
//
fnGetList();
});
onBeforeUnmount(() => {
@@ -549,12 +506,11 @@ onBeforeUnmount(() => {
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item label="SMF" name="neUid ">
<a-form-item label="SMF" name="neId ">
<a-select
v-model:value="queryParams.neUid"
v-model:value="queryParams.neId"
:options="neOtions"
:placeholder="t('common.selectPlease')"
@change="fnQueryReset()"
/>
</a-form-item>
</a-col>
@@ -578,12 +534,12 @@ onBeforeUnmount(() => {
>
<a-range-picker
v-model:value="queryRangePicker"
:presets="rangePickerPresets"
:bordered="true"
:allow-clear="false"
style="width: 100%"
allow-clear
bordered
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
value-format="x"
style="width: 100%"
></a-range-picker>
</a-form-item>
</a-col>
@@ -636,7 +592,7 @@ onBeforeUnmount(() => {
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')"
v-perms:has="['cdr:ne:remove']"
v-if="!hasRoles(['student'])"
>
<template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }}
@@ -699,7 +655,7 @@ onBeforeUnmount(() => {
<a-table
class="table"
row-key="id"
:columns="tableColumns"
:columns="hasRoles(['student']) ? tableColumns.filter((s:any)=>s.key !== 'id'): tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
@@ -715,23 +671,11 @@ onBeforeUnmount(() => {
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'id'">
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.copyText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordCopy(record.cdrJSON)"
>
<template #icon>
<CopyOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.deleteText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordDelete(record.id)"
v-perms:has="['cdr:ne:remove']"
>
<template #icon>
<DeleteOutlined />
@@ -800,33 +744,24 @@ onBeforeUnmount(() => {
<a-divider orientation="left">
List Of Multiple Unit Usage
</a-divider>
<div v-for="u in record.cdrJSON.listOfMultipleUnitUsage">
<!-- <div>RatingGroup: {{ u.ratingGroup }}</div> -->
<div
v-for="(udata, i) in u.usedUnitContainer"
style="display: flex"
>
<!-- <strong style="margin-right: 12px">
{{ i }}
</strong> -->
<div>RatingGroup: {{ u.ratingGroup }}</div>
<div v-for="udata in u.usedUnitContainer">
<div>
<div>
<span>Data Total Volume: </span>
<span>{{ udata.dataTotalVolume }}</span>
</div>
<div>
<span>Data Volume Downlink: </span>
<span>{{ udata.dataVolumeDownlink }}</span>
</div>
<div>
<span>Data Volume Uplink: </span>
<span>{{ udata.dataVolumeUplink }}</span>
</div>
<!-- <div>
<span>Time: </span>
<span>{{ udata.time }}</span>
</div> -->
<span>Data Total Volume: </span>
<span>{{ udata.dataTotalVolume }}</span>
</div>
<div>
<span>Data Volume Downlink: </span>
<span>{{ udata.dataVolumeDownlink }}</span>
</div>
<div>
<span>Data Volume Uplink: </span>
<span>{{ udata.dataVolumeUplink }}</span>
</div>
<div>
<span>Time: </span>
<span>{{ udata.time }}</span>
</div>
</div>
</div>

View File

@@ -0,0 +1,770 @@
<script setup lang="ts">
import { reactive, onMounted, toRaw, ref, onBeforeUnmount } 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';
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import { ColumnsType } from 'ant-design-vue/es/table';
import useDictStore from '@/store/modules/dict';
import useI18n from '@/hooks/useI18n';
import {
RESULT_CODE_ERROR,
RESULT_CODE_SUCCESS,
} from '@/constants/result-constants';
import useNeInfoStore from '@/store/modules/neinfo';
import {
delSMSCDataCDR,
exportSMSCDataCDR,
listSMSCDataCDR,
} from '@/api/neData/smsc';
import { parseDateToStr } from '@/utils/date-utils';
import { OptionsType, WS } from '@/plugins/ws-websocket';
import saveAs from 'file-saver';
import PQueue from 'p-queue';
import { hasRoles } from '@/plugins/auth-user';
const { getDict } = useDictStore();
const { t } = useI18n();
const ws = new WS();
const queue = new PQueue({ concurrency: 1, autoStart: true });
/**字典数据 */
let dict: {
/**CDR 响应原因代码类别类型 */
cdrCauseCode: DictType[];
} = reactive({
cdrCauseCode: [],
});
/**网元可选 */
let neOtions = ref<Record<string, any>[]>([]);
/**开始结束时间 */
let queryRangePicker = ref<[string, string]>(['', '']);
/**查询参数 */
let queryParams = reactive({
/**网元类型 */
neType: 'SMSC',
neId: '001',
recordType: '',
callerParty: '',
calledParty: '',
sortField: 'timestamp',
sortOrder: 'desc',
/**开始时间 */
startTime: '',
/**结束时间 */
endTime: '',
/**当前页数 */
pageNum: 1,
/**每页条数 */
pageSize: 20,
});
/**查询参数重置 */
function fnQueryReset() {
recordTypes.value = [];
queryParams = Object.assign(queryParams, {
recordType: '',
callerParty: '',
calledParty: '',
startTime: '',
endTime: '',
pageNum: 1,
pageSize: 20,
});
queryRangePicker.value = ['', ''];
tablePagination.current = 1;
tablePagination.pageSize = 20;
fnGetList();
}
/**记录类型 */
const recordTypes = ref<string[]>([]);
/**查询记录类型变更 */
function fnQueryRecordTypeChange(value: any) {
if (Array.isArray(value)) {
queryParams.recordType = value.join(',');
}
}
/**表格状态类型 */
type TabeStateType = {
/**加载等待 */
loading: boolean;
/**紧凑型 */
size: SizeType;
/**搜索栏 */
seached: boolean;
/**记录数据 */
data: object[];
/**勾选记录 */
selectedRowKeys: (string | number)[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
size: 'middle',
seached: true,
data: [],
selectedRowKeys: [],
});
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
{
title: t('views.dashboard.cdr.recordType'),
dataIndex: 'cdrJSON',
align: 'left',
width: 150,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.recordType;
},
},
{
title: t('views.dashboard.cdr.type'),
dataIndex: 'cdrJSON',
align: 'left',
width: 150,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.serviceType;
},
},
{
title: t('views.dashboard.cdr.caller'),
dataIndex: 'cdrJSON',
key: 'callerParty',
align: 'left',
width: 120,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.callerParty;
},
},
{
title: t('views.dashboard.cdr.called'),
dataIndex: 'cdrJSON',
key: 'calledParty',
align: 'left',
width: 120,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.calledParty;
},
},
{
title: t('views.dashboard.cdr.result'),
dataIndex: 'cdrJSON',
key: 'cause',
align: 'left',
width: 200,
},
{
title: t('views.dashboard.cdr.time'),
dataIndex: 'cdrJSON',
align: 'left',
width: 150,
customRender(opt) {
const cdrJSON = opt.value;
if (typeof cdrJSON.updateTime === 'number') {
return parseDateToStr(+cdrJSON.updateTime * 1000);
}
return cdrJSON.updateTime;
},
},
{
title: t('common.operate'),
key: 'id',
align: 'left',
},
];
/**表格分页器参数 */
let tablePagination = reactive({
/**当前页数 */
current: 1,
/**每页条数 */
pageSize: 20,
/**默认的每页条数 */
defaultPageSize: 20,
/**指定每页可以显示多少条 */
pageSizeOptions: ['10', '20', '50', '100'],
/**只有一页时是否隐藏分页器 */
hideOnSinglePage: false,
/**是否可以快速跳转至某页 */
showQuickJumper: true,
/**是否可以改变 pageSize */
showSizeChanger: true,
/**数据总数 */
total: 0,
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
onChange: (page: number, pageSize: number) => {
tablePagination.current = page;
tablePagination.pageSize = pageSize;
queryParams.pageNum = page;
queryParams.pageSize = pageSize;
fnGetList();
},
});
/**表格紧凑型变更操作 */
function fnTableSize({ key }: MenuInfo) {
tableState.size = key as SizeType;
}
/**表格多选 */
function fnTableSelectedRowKeys(keys: (string | number)[]) {
tableState.selectedRowKeys = keys;
}
/**对话框对象信息状态类型 */
type ModalStateType = {
/**确定按钮 loading */
confirmLoading: boolean;
/**最大ID值 */
maxId: number;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
confirmLoading: false,
maxId: 0,
});
/**
* 记录删除
* @param id 编号
*/
function fnRecordDelete(id: string) {
if (!id || modalState.confirmLoading) return;
let msg = id;
if (id === '0') {
msg = `${id}... ${tableState.selectedRowKeys.length}`;
id = tableState.selectedRowKeys.join(',');
}
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.dashboard.cdr.delTip', { msg }),
onOk() {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
delSMSCDataCDR(id)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
fnGetList(1);
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
},
});
}
/**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (tableState.loading) return;
tableState.loading = true;
if (pageNum) {
queryParams.pageNum = pageNum;
}
if (!queryRangePicker.value) {
queryRangePicker.value = ['', ''];
}
queryParams.startTime = queryRangePicker.value[0];
queryParams.endTime = queryRangePicker.value[1];
listSMSCDataCDR(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
// 取消勾选
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
tablePagination.total = res.total;
// 遍历处理cdr字符串数据
tableState.data = res.rows.map(item => {
let cdrJSON = item.cdrJSON;
if (!cdrJSON) {
Reflect.set(item, 'cdrJSON', {});
}
try {
cdrJSON = JSON.parse(cdrJSON);
Reflect.set(item, 'cdrJSON', cdrJSON);
} catch (error) {
console.error(error);
Reflect.set(item, 'cdrJSON', {});
}
return item;
});
// 取最大值ID用作实时累加
if (res.total > 0) {
modalState.maxId = Number(res.rows[0].id);
}
}
tableState.loading = false;
});
}
/**列表导出 */
function fnExportList() {
if (modalState.confirmLoading) return;
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.dashboard.cdr.exportTip'),
onOk() {
const hide = message.loading(t('common.loading'), 0);
const querys = toRaw(queryParams);
querys.pageSize = 10000;
exportSMSCDataCDR(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
saveAs(res.data, `smsc_cdr_event_export_${Date.now()}.xlsx`);
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
},
});
}
/**实时数据开关 */
const realTimeData = ref<boolean>(false);
/**
* 实时数据
*/
function fnRealTime() {
realTimeData.value = !realTimeData.value;
if (realTimeData.value) {
tableState.seached = false;
// 建立链接
const options: OptionsType = {
url: '/ws',
params: {
/**订阅通道组
*
* SMSC_CDR会话事件(GroupID:1007_neId)
*/
subGroupID: `1007_${queryParams.neId}`,
},
onmessage: wsMessage,
onerror: wsError,
};
ws.connect(options);
} else {
ws.close();
tableState.seached = true;
fnGetList(1);
}
}
/**接收数据后回调 */
function wsError(ev: any) {
// 接收数据后回调
console.error(ev);
}
/**接收数据后回调 */
function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res;
if (code === RESULT_CODE_ERROR) {
console.warn(res.msg);
return;
}
// 订阅组信息
if (!data?.groupId) {
return;
}
// cdrEvent CDR会话事件
if (data.groupId === `1007_${queryParams.neId}`) {
const cdrEvent = data.data;
queue.add(async () => {
modalState.maxId += 1;
tableState.data.unshift({
id: modalState.maxId,
neType: cdrEvent.neType,
neName: cdrEvent.neName,
rmUID: cdrEvent.rmUID,
timestamp: cdrEvent.timestamp,
cdrJSON: cdrEvent.CDR,
});
tablePagination.total += 1;
if (tableState.data.length > 100) {
tableState.data.pop();
}
await new Promise(resolve => setTimeout(resolve, 800));
});
}
}
onMounted(() => {
// 初始字典数据
Promise.allSettled([getDict('cdr_cause_code')]).then(resArr => {
if (resArr[0].status === 'fulfilled') {
dict.cdrCauseCode = resArr[0].value;
}
});
// 获取网元网元列表
useNeInfoStore()
.fnNelist()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
let arr: Record<string, any>[] = [];
res.data.forEach(i => {
if (i.neType === 'SMSC') {
arr.push({ value: i.neId, label: i.neName });
}
});
neOtions.value = arr;
if (arr.length > 0) {
queryParams.neId = arr[0].value;
}
}
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
})
.finally(() => {
// 获取列表数据
fnGetList();
});
});
onBeforeUnmount(() => {
if (ws.state() !== -1) {
ws.close();
}
});
</script>
<template>
<PageContainer>
<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="SMSC" name="neId ">
<a-select
v-model:value="queryParams.neId"
:options="neOtions"
:placeholder="t('common.selectPlease')"
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item
:label="t('views.dashboard.cdr.called')"
name="calledParty"
>
<a-input
v-model:value="queryParams.calledParty"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item
:label="t('views.dashboard.cdr.caller')"
name="callerParty "
>
<a-input
v-model:value="queryParams.callerParty"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="4" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList(1)">
<template #icon><SearchOutlined /></template>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
{{ t('common.reset') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
<a-col :lg="8" :md="12" :xs="24">
<a-form-item
:label="t('views.dashboard.cdr.recordType')"
name="recordType"
>
<a-select
v-model:value="recordTypes"
mode="multiple"
:options="['MOSM', 'MTSM'].map(v => ({ value: v }))"
:placeholder="t('common.selectPlease')"
@change="fnQueryRecordTypeChange"
></a-select>
</a-form-item>
</a-col>
<a-col :lg="8" :md="12" :xs="24">
<a-form-item
:label="t('views.dashboard.cdr.time')"
name="queryRangePicker"
>
<a-range-picker
v-model:value="queryRangePicker"
allow-clear
bordered
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
value-format="x"
style="width: 100%"
></a-range-picker>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-card>
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-popconfirm
placement="bottomLeft"
:title="
!realTimeData
? t('views.dashboard.cdr.realTimeDataStart')
: t('views.dashboard.cdr.realTimeDataStop')
"
ok-text="Yes"
cancel-text="No"
@confirm="fnRealTime()"
>
<a-button type="primary" :danger="realTimeData">
<template #icon><FundOutlined /> </template>
{{
!realTimeData
? t('views.dashboard.cdr.realTimeDataStart')
: t('views.dashboard.cdr.realTimeDataStop')
}}
</a-button>
</a-popconfirm>
<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="fnExportList()">
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
</a-space>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<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"
:disabled="realTimeData"
/>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
<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>
</a-button>
<template #overlay>
<a-menu
:selected-keys="[tableState.size as string]"
@click="fnTableSize"
>
<a-menu-item key="default">
{{ t('common.size.default') }}
</a-menu-item>
<a-menu-item key="middle">
{{ t('common.size.middle') }}
</a-menu-item>
<a-menu-item key="small">
{{ t('common.size.small') }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</template>
<!-- 表格列表 -->
<a-table
class="table"
row-key="id"
:columns="hasRoles(['student']) ? tableColumns.filter((s:any)=>s.key !== 'id'): tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: tableColumns.length * 150, y: 'calc(100vh - 480px)' }"
:row-selection="{
type: 'checkbox',
columnWidth: '48px',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
>
<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>
</template>
<template v-if="column.key === 'id'">
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.deleteText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordDelete(record.id)"
>
<template #icon>
<DeleteOutlined />
</template>
</a-button>
</a-tooltip>
</a-space>
</template>
</template>
<template #expandedRowRender="{ record }">
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
<a-divider orientation="left">
{{ t('views.dashboard.cdr.cdrInfo') }}
</a-divider>
<div>
<span>{{ t('views.ne.common.neName') }}: </span>
<span>{{ record.neName }}</span>
</div>
<div>
<span>{{ t('views.ne.common.rmUid') }}: </span>
<span>{{ record.rmUID }}</span>
</div>
<div>
<span>{{ t('views.dashboard.cdr.time') }}: </span>
<span>
{{
typeof record.cdrJSON.updateTime === 'number'
? parseDateToStr(+record.cdrJSON.updateTime * 1000)
: record.cdrJSON.updateTime
}}
</span>
</div>
<a-divider orientation="left">
{{ t('views.dashboard.cdr.rowInfo') }}
</a-divider>
<div>
<span>{{ t('views.dashboard.cdr.type') }}: </span>
<span>{{ record.cdrJSON.serviceType }}</span>
</div>
<div>
<span>{{ t('views.dashboard.cdr.caller') }}: </span>
<span>{{ record.cdrJSON.callerParty }}</span>
</div>
<div>
<span>{{ t('views.dashboard.cdr.called') }}: </span>
<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') }},
<DictTag
:options="dict.cdrCauseCode"
:value="record.cdrJSON.cause"
value-default="0"
/>
</span>
<span v-else>
{{ t('views.dashboard.cdr.resultOk') }}
</span>
</div>
</div>
</template>
</a-table>
</a-card>
</PageContainer>
</template>
<style lang="less" scoped>
.table :deep(.ant-pagination) {
padding: 0 24px;
}
</style>

View File

@@ -0,0 +1,554 @@
<script setup lang="ts">
import { reactive, onMounted, ref, onBeforeUnmount } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import {
RESULT_CODE_ERROR,
RESULT_CODE_SUCCESS,
} from '@/constants/result-constants';
import { listAllNeInfo } from '@/api/ne/neInfo';
import { message } from 'ant-design-vue/es';
import { getGraphData } from '@/api/monitor/topology';
import { parseDateToStr } from '@/utils/date-utils';
import { Graph, GraphData, Menu, Tooltip } from '@antv/g6';
import {
edgeCubicAnimateCircleMove,
edgeCubicAnimateLineDash,
edgeLineAnimateState,
} from '../topologyBuild/hooks/registerEdge';
import {
nodeCircleAnimateShapeR,
nodeCircleAnimateShapeStroke,
nodeImageAnimateState,
nodeRectAnimateState,
} from '../topologyBuild/hooks/registerNode';
import useNeOptions from '@/views/ne/neInfo/hooks/useNeOptions';
import useI18n from '@/hooks/useI18n';
import { OptionsType, WS } from '@/plugins/ws-websocket';
import { hasRoles } from '@/plugins/auth-user';
import { parseBasePath } from '@/plugins/file-static-url';
const { t } = useI18n();
const { fnNeRestart, fnNeStop, fnNeLogFile } = useNeOptions();
const ws = new WS();
/**图DOM节点实例对象 */
const graphG6Dom = ref<HTMLElement | undefined>(undefined);
/**图状态 */
const graphState = reactive<Record<string, any>>({
/**当前图组名 */
group: '5GC System Architecture',
/**图数据 */
data: {
combos: [],
edges: [],
nodes: [],
},
});
/**非网元元素 */
const notNeNodes = [
'5GC',
'DN',
'UE',
'Base',
'lan',
'lan1',
'lan2',
'lan3',
'lan4',
'lan5',
'lan6',
'lan7',
'LAN',
'NR',
];
/**图实例对象 */
const graphG6 = ref<any>(null);
/**图节点右击菜单 */
const graphNodeMenu = new Menu({
offsetX: 6,
offseY: 10,
itemTypes: ['node'],
getContent(evt) {
if (!evt) return t('views.monitor.topologyBuild.graphNotInfo');
const { id, label, neState }: any = evt.item?.getModel();
if (notNeNodes.includes(id)) {
return `<div><span>${label || id}</span></div>`;
}
if (!neState) {
return `<div><span>${label || id}</span></div>`;
}
if (hasRoles(['student'])) {
return 'Student';
}
return `
<div
style="
display: flex;
flex-direction: column;
width: 140px;
"
>
<h3 style="margin-bottom: 8px">
${t('views.monitor.topology.name')}:
${neState.neName ?? '--'}
</h3>
<div id="restart" style="cursor: pointer; margin-bottom: 4px">
> ${t('views.ne.common.restart')}
</div>
<div id="stop" style="cursor: pointer; margin-bottom: 4px;">
> ${t('views.ne.common.stop')}
</div>
<div id="log" style="cursor: pointer; margin-bottom: 4px;">
> ${t('views.ne.common.log')}
</div>
</div>
`;
},
handleMenuClick(target, item) {
const { neInfo }: any = item?.getModel();
const { neName, neType, neId } = neInfo;
const targetId = target.id;
switch (targetId) {
case 'restart':
fnNeRestart({ neName, neType, neId });
break;
case 'stop':
fnNeStop({ neName, neType, neId });
break;
case 'log':
fnNeLogFile({ neType, neId });
break;
}
},
});
/**图节点展示 */
const graphNodeTooltip = new Tooltip({
offsetX: 10,
offsetY: 20,
getContent(evt) {
if (!evt) return t('views.monitor.topologyBuild.graphNotInfo');
const { id, label, neState }: any = evt.item?.getModel();
if (notNeNodes.includes(id)) {
return `<div><span>${label || id}</span></div>`;
}
if (!neState) {
return `<div><span>${label || id}</span></div>`;
}
let notStudentInfo = '';
if (hasRoles(['teacher', 'admin'])) {
notStudentInfo = `
<div><strong>${t('views.monitor.topology.serialNum')}</strong><span>
${neState.sn ?? '--'}
</span></div>
<div><strong>${t('views.monitor.topology.expiryDate')}</strong><span>
${neState.expire ?? '--'}
</span></div> `;
}
return `
<div
style="
display: flex;
flex-direction: column;
width: 200px;
"
>
<div><strong>${t('views.monitor.topology.state')}</strong><span>
${
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 ?? '--'}
</span></div>
<div>========================</div>
<div><strong>ID</strong><span>${neState.neId}</span></div>
<div><strong>${t('views.monitor.topology.name')}</strong><span>
${neState.neName ?? '--'}
</span></div>
<div><strong>IP</strong><span>${neState.neIP}</span></div>
<div><strong>${t('views.monitor.topology.version')}</strong><span>
${neState.version ?? '--'}
</span></div>
${notStudentInfo}
</div>
`;
},
itemTypes: ['node'],
});
/**注册自定义边或节点 */
function registerEdgeNode() {
// 边
edgeCubicAnimateLineDash();
edgeCubicAnimateCircleMove();
edgeLineAnimateState();
// 节点
nodeCircleAnimateShapeR();
nodeCircleAnimateShapeStroke();
nodeRectAnimateState();
nodeImageAnimateState();
}
/**图数据渲染 */
function handleRanderGraph(
container: HTMLElement | undefined,
data: GraphData
) {
if (!container) return;
const { clientHeight, clientWidth } = container;
// 注册自定义边或节点
registerEdgeNode();
const graph = new Graph({
container: container,
width: clientWidth,
height: clientHeight,
fitCenter: true,
fitView: true,
fitViewPadding: [40],
autoPaint: true,
modes: {
default: [
'drag-combo',
'drag-canvas',
'zoom-canvas',
'collapse-expand-combo',
],
},
groupByTypes: false,
nodeStateStyles: {
selected: {
fill: 'transparent',
},
},
plugins: [graphNodeMenu, graphNodeTooltip],
animate: true, // 是否使用动画过度,默认为 false
animateCfg: {
duration: 500, // Number一次动画的时长
easing: 'linearEasing', // String动画函数
},
});
graph.data(data);
graph.render();
graphG6.value = graph;
// 创建 ResizeObserver 实例
var observer = new ResizeObserver(function (entries) {
// 当元素大小发生变化时触发回调函数
entries.forEach(function (entry) {
if (!graphG6.value) {
return;
}
graphG6.value.changeSize(
entry.contentRect.width,
entry.contentRect.height - 30
);
graphG6.value.fitCenter();
});
});
// 监听元素大小变化
observer.observe(container);
return graph;
}
/**
* 获取图组数据渲染到画布
* @param reload 是否重载数据
*/
function fnGraphDataLoad(reload: boolean = false) {
Promise.all([
getGraphData(graphState.group),
listAllNeInfo({
bandStatus: false,
}),
])
.then(resArr => {
const graphRes = resArr[0];
const neRes = resArr[1];
if (
graphRes.code === RESULT_CODE_SUCCESS &&
Array.isArray(graphRes.data.nodes) &&
graphRes.data.nodes.length > 0 &&
neRes.code === RESULT_CODE_SUCCESS &&
Array.isArray(neRes.data) &&
neRes.data.length > 0
) {
return {
graphData: graphRes.data,
neList: neRes.data,
};
} else {
message.warning({
content: t('views.monitor.topology.noData'),
duration: 5,
});
}
})
.then(res => {
if (!res) return;
const { combos, edges, nodes } = res.graphData;
// 节点过滤
const nf: Record<string, any>[] = nodes.filter(
(node: Record<string, any>) => {
Reflect.set(node, 'neState', { online: false });
// 图片路径处理
if (node.img) node.img = parseBasePath(node.img);
if (node.icon.show && node.icon?.img)
node.icon.img = parseBasePath(node.icon.img);
// 遍历是否有网元数据
const nodeID: string = node.id;
const hasNe = res.neList.some(ne => {
Reflect.set(node, 'neInfo', ne.neType === nodeID ? ne : {});
return ne.neType === nodeID;
});
if (hasNe) {
return true;
}
if (notNeNodes.includes(nodeID)) {
return true;
}
return false;
}
);
// 边过滤
const ef: Record<string, any>[] = edges.filter(
(edge: Record<string, any>) => {
const edgeSource: string = edge.source;
const edgeTarget: string = edge.target;
const hasNeS = nf.some(n => n.id === edgeSource);
const hasNeT = nf.some(n => n.id === edgeTarget);
// console.log(hasNeS, edgeSource, hasNeT, edgeTarget);
if (hasNeS && hasNeT) {
return true;
}
if (hasNeS && notNeNodes.includes(edgeTarget)) {
return true;
}
if (hasNeT && notNeNodes.includes(edgeSource)) {
return true;
}
return false;
}
);
// 分组过滤
combos.forEach((combo: Record<string, any>) => {
const comboChildren: Record<string, any>[] = combo.children;
combo.children = comboChildren.filter(c => nf.some(n => n.id === c.id));
return combo;
});
// 图数据
graphState.data = { combos, edges: ef, nodes: nf };
})
.finally(() => {
if (graphState.data.length < 0) return;
// 重载数据
if (reload) {
graphG6.value.read(graphState.data);
} else {
handleRanderGraph(graphG6Dom.value, graphState.data);
}
clearInterval(interval10s.value);
interval10s.value = null;
fnGetState();
interval10s.value = setInterval(async () => {
if (!interval10s.value) return;
fnGetState(); // 获取网元状态
}, 20_000);
});
}
/**网元状态调度器 */
const interval10s = ref<any>(null);
/**查询网元状态 */
function fnGetState() {
// 获取节点状态
for (const node of graphState.data.nodes) {
if (notNeNodes.includes(node.id)) continue;
const { neType, neId } = node.neInfo;
if (!neType || !neId) continue;
ws.send({
requestId: `${neType}_${neId}`,
type: 'ne_state',
data: {
neType: neType,
neId: neId,
},
});
}
}
/**接收数据后回调 */
function wsError(ev: any) {
// 接收数据后回调
console.error(ev);
}
/**接收数据后回调 */
function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res;
if (code === RESULT_CODE_ERROR) {
console.warn(res.msg);
return;
}
if (!requestId) return;
const [neType, neId] = requestId.split('_');
const { combos, edges, nodes } = graphState.data;
const node = nodes.find((item: Record<string, any>) => item.id === neType);
// 更新网元状态
const newNeState = Object.assign(node.neState, data, {
refreshTime: parseDateToStr(data.refreshTime, 'HH:mm:ss'),
online: !!data.cpu,
});
// 通过 ID 查询节点实例
const item = graphG6.value.findById(node.id);
if (item) {
const stateColor = newNeState.online ? '#52c41a' : '#f5222d'; // 状态颜色
// 图片类型不能填充
if (node.type.startsWith('image')) {
// 更新节点
if (node.label !== newNeState.neName) {
graphG6.value.updateItem(item, {
label: newNeState.neName,
});
}
// 设置状态
graphG6.value.setItemState(item, 'top-right-dot', stateColor);
} else {
// 更新节点
graphG6.value.updateItem(item, {
label: newNeState.neName,
// neState: newNeState,
style: {
fill: stateColor, // 填充色
stroke: stateColor, // 填充色
},
// labelCfg: {
// style: {
// fill: '#ffffff', // 标签文本色
// },
// },
});
// 设置状态
graphG6.value.setItemState(item, 'stroke', newNeState.online);
}
}
// 设置边状态
for (const edge of edges) {
const edgeSource: string = edge.source;
const edgeTarget: string = edge.target;
const neS = nodes.find((n: any) => n.id === edgeSource);
const neT = nodes.find((n: any) => n.id === edgeTarget);
// console.log(neS, edgeSource, neT, edgeTarget);
if (neS && neT) {
// 通过 ID 查询节点实例
// const item = graphG6.value.findById(edge.id);
// console.log(
// `${edgeSource} - ${edgeTarget}`,
// neS.neState.online && neT.neState.online
// );
// const stateColor = neS.neState.online && neT.neState.online ? '#000000' : '#ff4d4f'; // 状态颜色
// 更新边
// graphG6.value.updateItem(item, {
// label: `${edgeSource} - ${edgeTarget}`,
// style: {
// stroke: stateColor, // 填充色
// },
// labelCfg: {
// style: {
// fill: '#ffffff', // 标签文本色
// },
// },
// });
// 设置状态
graphG6.value.setItemState(
edge.id,
'circle-move',
neS.neState.online && neT.neState.online
);
}
if (neS && notNeNodes.includes(edgeTarget)) {
graphG6.value.setItemState(edge.id, 'line-dash', neS.neState.online);
}
if (neT && notNeNodes.includes(edgeSource)) {
graphG6.value.setItemState(edge.id, 'line-dash', neT.neState.online);
}
}
}
onMounted(() => {
fnGraphDataLoad(false);
// 建立链接
const options: OptionsType = {
url: '/ws',
onmessage: wsMessage,
onerror: wsError,
};
ws.connect(options);
});
onBeforeUnmount(() => {
ws.close();
clearInterval(interval10s.value);
interval10s.value = null;
});
</script>
<template>
<PageContainer>
<a-card
:bordered="false"
:body-style="{ marginBottom: '24px' }"
size="small"
>
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<span>
{{ t('views.monitor.topologyBuild.graphGroup') }}
{{ graphState.group }}
</span>
</a-space>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-button
type="default"
size="small"
@click.prevent="fnGraphDataLoad(true)"
>
<template #icon><ReloadOutlined /></template>
{{ t('common.reloadText') }}
</a-button>
</template>
<div ref="graphG6Dom" class="chart"></div>
</a-card>
</PageContainer>
</template>
<style lang="less" scoped>
.chart {
width: 100%;
height: calc(100vh - 300px);
background-color: rgb(43, 47, 51);
}
</style>

View File

@@ -20,7 +20,8 @@ const props = defineProps({
type: Boolean,
default: false,
},
neUid: {
/**网元ID */
neId: {
type: String,
default: '',
},
@@ -41,17 +42,17 @@ const importState = reactive({
/**查询网元远程服务器备份文件 */
function backupSearch(name?: string) {
const { neType, neUid } = props;
const { neType, neId } = modalState.from;
listNeConfigBackup({
neType,
neUid,
neId,
name,
pageNum: 1,
pageSize: 20,
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
importState.backupData = [];
res.data.rows.forEach((item: any) => {
res.rows.forEach((item: any) => {
importState.backupData.push({
label: item.name,
value: item.path,
@@ -85,7 +86,7 @@ type ModalStateType = {
/**表单数据 */
from: {
neType: string;
neUid: string;
neId: string;
type: 'upload' | 'backup';
path: string | undefined;
};
@@ -101,7 +102,7 @@ let modalState: ModalStateType = reactive({
title: '配置文件导入',
from: {
neType: '',
neUid: '',
neId: '',
type: 'upload',
path: undefined,
},
@@ -210,7 +211,8 @@ function fnUploadFile(up: UploadRequestOption) {
file.percent = 100;
file.status = 'done';
//
modalState.from.path = res.data.filePath;
const { fileName } = res.data;
modalState.from.path = fileName;
} else {
message.error(res.msg, 3);
}
@@ -226,9 +228,9 @@ watch(
() => props.open,
val => {
if (val) {
if (props.neType && props.neUid) {
if (props.neType && props.neId) {
modalState.from.neType = props.neType;
modalState.from.neUid = props.neUid;
modalState.from.neId = props.neId;
modalState.title = t('views.ne.neInfo.backConf.title');
modalState.openByEdit = true;
}
@@ -240,13 +242,13 @@ watch(
* 网元导出配置
* @param row 网元编号ID
*/
function fnExportConf(neUid: string, neType: string) {
function fnExportConf(neType: string, neId: string) {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.ne.neInfo.backConf.exportTip'),
onOk() {
const hide = message.loading(t('common.loading'), 0);
exportNeConfigBackup({ neType, neUid })
exportNeConfigBackup({ neType, neId })
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
notification.success({
@@ -255,7 +257,7 @@ function fnExportConf(neUid: string, neType: string) {
});
saveAs(
res.data,
`${neType}_${neUid}_config_backup_${Date.now()}.zip`
`${neType}_${neId}_config_backup_${Date.now()}.zip`
);
} else {
message.error(`${res.msg}`, 3);
@@ -279,7 +281,7 @@ defineExpose({
<template>
<ProModal
:drag="true"
:width="500"
:width="800"
:keyboard="false"
:mask-closable="false"
:open="modalState.openByEdit"
@@ -289,77 +291,82 @@ defineExpose({
@cancel="fnModalCancel"
>
<a-form name="modalStateFrom" layout="horizontal" :label-col="{ span: 6 }">
<a-form-item :label="t('views.ne.common.neType')" name="neType">
{{ modalState.from.neType }}
</a-form-item>
<a-form-item :label="t('views.ne.common.neUid')" name="neId">
{{ modalState.from.neUid }}
</a-form-item>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.ne.common.neType')" name="neType">
{{ modalState.from.neType }}
</a-form-item>
<a-form-item
:label="t('views.ne.neInfo.backConf.importType')"
name="type"
>
<a-select
v-model:value="modalState.from.type"
default-value="server"
:options="importState.typeOption"
@change="typeChange"
>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.ne.common.neId')" name="neId">
{{ modalState.from.neId }}
</a-form-item>
<a-form-item
:label="t('views.ne.neInfo.backConf.server')"
name="fileName"
v-bind="modalStateFrom.validateInfos.path"
v-if="modalState.from.type === 'backup'"
>
<a-select
v-model:value="modalState.from.path"
:options="importState.backupData"
:placeholder="t('common.selectPlease')"
:show-search="true"
:default-active-first-option="false"
:show-arrow="false"
:allow-clear="true"
:filter-option="false"
:not-found-content="null"
@search="backupSearch"
@change="backupChange"
>
</a-select>
</a-form-item>
<a-form-item
:label="t('views.ne.neInfo.backConf.importType')"
name="type"
>
<a-select
v-model:value="modalState.from.type"
default-value="server"
:options="importState.typeOption"
@change="typeChange"
>
</a-select>
</a-form-item>
<a-form-item
:label="t('views.ne.neInfo.backConf.server')"
name="fileName"
v-bind="modalStateFrom.validateInfos.path"
v-if="modalState.from.type === 'backup'"
>
<a-select
v-model:value="modalState.from.path"
:options="importState.backupData"
:placeholder="t('common.selectPlease')"
:show-search="true"
:default-active-first-option="false"
:show-arrow="false"
:allow-clear="true"
:filter-option="false"
:not-found-content="null"
@search="backupSearch"
@change="backupChange"
>
</a-select>
</a-form-item>
<a-form-item
:label="t('views.ne.neInfo.backConf.local')"
name="file"
v-bind="modalStateFrom.validateInfos.path"
v-if="modalState.from.type === 'upload'"
>
<a-upload
name="file"
v-model:file-list="modalState.uploadFiles"
accept=".zip"
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.neInfo.backConf.localUpload') }}
</a-button>
</a-upload>
</a-form-item>
<a-form-item
:label="t('views.ne.neInfo.backConf.local')"
name="file"
v-bind="modalStateFrom.validateInfos.path"
v-if="modalState.from.type === 'upload'"
>
<a-upload
name="file"
v-model:file-list="modalState.uploadFiles"
accept=".zip"
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.neInfo.backConf.localUpload') }}
</a-button>
</a-upload>
</a-form-item>
</a-col>
</a-row>
</a-form>
</ProModal>
</template>

View File

@@ -5,7 +5,7 @@ import { message, Form, Modal } from 'ant-design-vue/es';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { NE_TYPE_LIST } from '@/constants/ne-constants';
import { regExpIPv4, regExpIPv6 } from '@/utils/regular-utils';
import { addNeInfo, updateNeInfo, getNeInfo } from '@/api/ne/neInfo';
import { getNeInfo, addNeInfo, updateNeInfo } from '@/api/ne/neInfo';
import { neHostAuthorizedRSA, testNeHost } from '@/api/ne/neHost';
import useDictStore from '@/store/modules/dict';
import useI18n from '@/hooks/useI18n';
@@ -17,9 +17,9 @@ const props = defineProps({
type: Boolean,
default: false,
},
id: {
type: Number,
default: 0,
editId: {
type: String,
default: '',
},
});
@@ -103,21 +103,22 @@ let modalState: ModalStateType = reactive({
title: '网元',
from: {
id: undefined,
neId: '001',
neType: 'AMF',
neName: '',
ipAddr: '',
ip: '',
port: 33030,
pvFlag: 'PNF',
neUid: '',
macAddr: '',
rmUid: '4400HXAMF001',
neAddress: '',
dn: '',
vendorName: '',
province: 'Area',
province: '',
remark: '',
//
hosts: [
{
id: undefined,
hostId: undefined,
hostType: 'ssh',
groupId: '1',
title: 'SSH_NE_22',
@@ -131,7 +132,7 @@ let modalState: ModalStateType = reactive({
remark: '',
},
{
id: undefined,
hostId: undefined,
hostType: 'telnet',
groupId: '1',
title: 'Telnet_NE_4100',
@@ -157,18 +158,30 @@ const modalStateFrom = Form.useForm(
message: t('views.ne.common.neTypePlease'),
},
],
neId: [
{
required: true,
message: t('views.ne.common.neIdPlease'),
},
],
rmUid: [
{
required: true,
message: t('views.ne.common.rmUidPlease'),
},
],
ip: [
{
required: true,
validator: modalStateFromEqualIPV4AndIPV6,
},
],
neName: [
{
required: true,
message: t('views.ne.common.neNamePlease'),
},
],
ipAddr: [
{
required: true,
validator: modalStateFromEqualIPV4AndIPV6,
},
],
})
);
@@ -196,10 +209,10 @@ function modalStateFromEqualIPV4AndIPV6(
/**
* 对话框弹出显示为 新增或者修改
* @param id 网元ID
* @param editId 网元id, 不传为新增
*/
function fnModalVisibleByEdit(id: number) {
if (!id) {
function fnModalVisibleByEdit(editId: string) {
if (!editId) {
modalStateFrom.resetFields();
modalState.title = t('views.ne.neInfo.addTitle');
modalState.openByEdit = true;
@@ -207,7 +220,7 @@ function fnModalVisibleByEdit(id: number) {
if (modalState.confirmLoading) return;
const hide = message.loading(t('common.loading'), 0);
modalState.confirmLoading = true;
getNeInfo(id).then(res => {
getNeInfo(editId).then(res => {
modalState.confirmLoading = false;
hide();
if (res.code === RESULT_CODE_SUCCESS) {
@@ -279,7 +292,7 @@ function fnNeTypeChange(v: any) {
// UPF5002
if (hostsLen === 2 && v === 'UPF') {
modalState.from.hosts.push({
id: undefined,
hostId: undefined,
hostType: 'telnet',
groupId: '1',
title: 'Telnet_NE_5002',
@@ -294,7 +307,7 @@ function fnNeTypeChange(v: any) {
// UDM6379
if (hostsLen === 2 && v === 'UDM') {
modalState.from.hosts.push({
id: undefined,
hostId: undefined,
hostType: 'redis',
groupId: '1',
title: 'REDIS_NE_6379',
@@ -307,6 +320,15 @@ function fnNeTypeChange(v: any) {
remark: '',
});
}
modalState.from.rmUid = `4400HX${v}${modalState.from.neId}`; // 4400HX1AMF001
}
/**表单修改网元neId */
function fnNeIdChange(e: any) {
const v = e.target.value;
if (v.length < 1) return;
modalState.from.rmUid = `4400HX${modalState.from.neType}${v}`; // 4400HX1AMF001
}
/**表单修改网元IP */
@@ -322,7 +344,7 @@ function fnNeIPChange(e: any) {
watch(
() => props.open,
val => {
if (val) fnModalVisibleByEdit(props.id);
if (val) fnModalVisibleByEdit(props.editId);
}
);
@@ -377,7 +399,6 @@ onMounted(() => {
v-model:value="modalState.from.neType"
:options="NE_TYPE_LIST.map(v => ({ value: v }))"
@change="fnNeTypeChange"
:disabled="!!modalState.from.id"
>
<a-input
allow-clear
@@ -417,31 +438,57 @@ onMounted(() => {
</a-col>
</a-row>
<a-form-item
:label="t('views.ne.common.neName')"
name="neName"
:label-col="{ span: 3 }"
:label-wrap="true"
v-bind="modalStateFrom.validateInfos.neName"
>
<a-input
v-model:value="modalState.from.neName"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="64"
>
</a-input>
</a-form-item>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.ne.common.neId')"
name="neId"
v-bind="modalStateFrom.validateInfos.neId"
>
<a-input
v-model:value="modalState.from.neId"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
@change="fnNeIdChange"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>
{{ t('views.ne.common.neIdTip') }}
</template>
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.ne.common.neName')"
name="neName"
v-bind="modalStateFrom.validateInfos.neName"
>
<a-input
v-model:value="modalState.from.neName"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="64"
>
</a-input>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.ne.common.ipAddr')"
name="ipAddr"
v-bind="modalStateFrom.validateInfos.ipAddr"
name="ip"
v-bind="modalStateFrom.validateInfos.ip"
>
<a-input
v-model:value="modalState.from.ipAddr"
v-model:value="modalState.from.ip"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="128"
@@ -487,11 +534,37 @@ onMounted(() => {
</a-col>
</a-row>
<a-form-item
:label="t('views.ne.common.rmUid')"
name="rmUid"
v-bind="modalStateFrom.validateInfos.rmUid"
:label-col="{ span: 3 }"
:labelWrap="true"
>
<a-input
v-model:value="modalState.from.rmUid"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="40"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>
<div>
{{ t('views.ne.common.rmUidTip') }}
</div>
</template>
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.ne.neInfo.macAddr')" name="macAddr">
<a-form-item :label="t('views.ne.neInfo.neAddress')" name="neAddress">
<a-input
v-model:value="modalState.from.macAddr"
v-model:value="modalState.from.neAddress"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="64"
@@ -499,7 +572,7 @@ onMounted(() => {
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>
<div>{{ t('views.ne.neInfo.macAddrTip') }}</div>
<div>{{ t('views.ne.neInfo.neAddressTip') }}</div>
</template>
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
</a-tooltip>

View File

@@ -4,7 +4,7 @@ import { ProModal } from 'antdv-pro-modal';
import { message, Form } from 'ant-design-vue/es';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { getOAMFile, saveOAMFile, serviceNeAction } from '@/api/ne/neAction';
import { getOAMFile, saveOAMFile } from '@/api/ne/neInfo';
const { t } = useI18n();
const emit = defineEmits(['ok', 'cancel', 'update:open']);
const props = defineProps({
@@ -12,7 +12,8 @@ const props = defineProps({
type: Boolean,
default: false,
},
neUid: {
/**网元ID */
neId: {
type: String,
default: '',
},
@@ -28,8 +29,8 @@ type ModalStateType = {
openByEdit: boolean;
/**标题 */
title: string;
/**是否重启 */
restart: boolean;
/**是否同步 */
sync: boolean;
/**表单数据 */
from: Record<string, any>;
/**确定按钮 loading */
@@ -40,7 +41,7 @@ type ModalStateType = {
let modalState: ModalStateType = reactive({
openByEdit: false,
title: 'OAM Configuration',
restart: false,
sync: true,
from: {
omcIP: '',
oamEnable: true,
@@ -71,25 +72,20 @@ const modalStateFrom = Form.useForm(
* @param neType 网元类型
* @param neId 网元ID
*/
function fnModalVisibleByTypeAndId(neUid: string) {
function fnModalVisibleByTypeAndId(neType: string, neId: string) {
const hide = message.loading(t('common.loading'), 0);
getOAMFile(neUid)
getOAMFile(neType, neId)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
const data = res.data;
const ipType = data?.oamConfig?.ipType || 'ipv4';
let omcIP = '127.0.0.1';
if (data.oamConfig && Reflect.has(data.oamConfig, ipType)) {
omcIP = data?.oamConfig[ipType];
}
Object.assign(modalState.from, {
omcIP: omcIP,
oamEnable: data?.oamConfig?.enable || false,
oamPort: data?.oamConfig?.port || 33030,
snmpEnable: data?.snmpConfig?.enable || false,
snmpPort: data?.snmpConfig?.port || 4957,
kpiEnable: data?.kpiConfig?.enable || false,
kpiTimer: data?.kpiConfig?.timer || 60,
omcIP: data.oamConfig[data.oamConfig.ipType],
oamEnable: data.oamConfig.enable,
oamPort: data.oamConfig.port,
snmpEnable: data.snmpConfig.enable,
snmpPort: data.snmpConfig.port,
kpiEnable: data.kpiConfig.enable,
kpiTimer: data.kpiConfig.timer,
});
modalState.title = t('views.ne.neInfo.oam.title');
modalState.openByEdit = true;
@@ -115,21 +111,15 @@ function fnModalOk() {
const hide = message.loading(t('common.loading'), 0);
const from = toRaw(modalState.from);
saveOAMFile({
neUid: props.neUid,
neType: props.neType,
neId: props.neId,
content: from,
sync: true,
sync: modalState.sync,
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.operateOk'), 3);
emit('ok');
if (modalState.restart) {
serviceNeAction({
neType: props.neType,
neUid: props.neUid,
action: 'restart',
});
}
fnModalCancel();
} else {
message.error({
@@ -155,7 +145,6 @@ function fnModalOk() {
function fnModalCancel() {
modalState.openByEdit = false;
modalState.confirmLoading = false;
modalState.restart = false;
modalStateFrom.resetFields();
emit('cancel');
emit('update:open', false);
@@ -166,8 +155,8 @@ watch(
() => props.open,
val => {
if (val) {
if (props.neUid) {
fnModalVisibleByTypeAndId(props.neUid);
if (props.neType && props.neId) {
fnModalVisibleByTypeAndId(props.neType, props.neId);
}
}
}
@@ -194,15 +183,15 @@ watch(
:labelWrap="true"
>
<a-form-item
:label="t('views.ne.neInfo.oam.restart')"
name="restart"
:label="t('views.ne.neInfo.oam.sync')"
name="sync"
:label-col="{ span: 6 }"
:labelWrap="true"
>
<a-switch
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
v-model:checked="modalState.restart"
v-model:checked="modalState.sync"
></a-switch>
</a-form-item>

View File

@@ -2,8 +2,8 @@ import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { Modal, message } from 'ant-design-vue/es';
import useI18n from '@/hooks/useI18n';
import { useRouter } from 'vue-router';
import { updateNeConfigReload } from '@/api/tool/mml';
import { serviceNeAction } from '@/api/ne/neAction';
import { updateNeConfigReload } from '@/api/configManage/configParam';
import { serviceNeAction } from '@/api/ne/neInfo';
import useMaskStore from '@/store/modules/mask';
export default function useNeOptions() {
@@ -14,31 +14,24 @@ export default function useNeOptions() {
/**
*
* @param row {neName,neType,neId}
* @param callback
*/
function fnNeStart(row: Record<string, any>, callback?: () => void) {
function fnNeStart(row: Record<string, any>) {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.ne.common.startTip', { ne: row.neName }),
content: t('views.ne.common.startTip'),
onOk() {
const hide = message.loading(t('common.loading'), 0);
serviceNeAction({
neType: row.neType,
neUid: row.neUid,
neId: row.neId,
action: 'start',
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(
`${t('views.ne.common.start')} ${row.neName} ${t(
'common.operateOk'
)}`,
3
);
message.success(t('common.operateOk'), 3);
} else {
message.error(`${res.msg}`, 3);
}
callback && callback();
})
.finally(() => {
hide();
@@ -50,17 +43,16 @@ export default function useNeOptions() {
/**
*
* @param row {neName,neType,neId}
* @param callback
*/
function fnNeRestart(row: Record<string, any>, callback?: () => void) {
function fnNeRestart(row: Record<string, any>) {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.ne.common.restartTip', { ne: row.neName }),
content: t('views.ne.common.restartTip'),
onOk() {
const hide = message.loading(t('common.loading'), 0);
serviceNeAction({
neType: row.neType,
neUid: row.neUid,
neId: row.neId,
action: 'restart',
})
.then(res => {
@@ -77,16 +69,10 @@ export default function useNeOptions() {
}
return;
}
message.success(
`${t('views.ne.common.restart')} ${row.neName} ${t(
'common.operateOk'
)}`,
3
);
message.success(t('common.operateOk'), 3);
} else {
message.error(`${res.msg}`, 3);
}
callback && callback();
})
.finally(() => {
hide();
@@ -98,31 +84,24 @@ export default function useNeOptions() {
/**
*
* @param row {neName,neType,neId}
* @param callback
*/
function fnNeStop(row: Record<string, any>, callback?: () => void) {
function fnNeStop(row: Record<string, any>) {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.ne.common.stopTip', { ne: row.neName }),
content: t('views.ne.common.stopTip'),
onOk() {
const hide = message.loading(t('common.loading'), 0);
serviceNeAction({
neType: row.neType,
neUid: row.neUid,
neId: row.neId,
action: 'stop',
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(
`${t('views.ne.common.stop')} ${row.neName} ${t(
'common.operateOk'
)}`,
3
);
message.success(t('common.operateOk'), 3);
} else {
message.error(`${res.msg}`, 3);
}
callback && callback();
})
.finally(() => {
hide();
@@ -141,7 +120,7 @@ export default function useNeOptions() {
content: t('views.ne.common.reloadTip'),
onOk() {
const hide = message.loading(t('common.loading'), 0);
updateNeConfigReload(row.neUid)
updateNeConfigReload(row.neType, row.neId)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.operateOk'), 3);
@@ -165,67 +144,10 @@ export default function useNeOptions() {
name: 'NeFile_2123',
query: {
neType: row.neType,
neUid: row.neUid,
neId: row.neId,
},
});
}
/**
*
* @param neState {cpu,mem,disk}
*/
function parseResouresUsage(neState: Record<string, any>) {
let sysCpuUsage = 0;
let nfCpuUsage = 0;
if (neState.cpu) {
nfCpuUsage = neState.cpu.nfCpuUsage;
const nfCpu = +(nfCpuUsage / 100);
nfCpuUsage = +nfCpu.toFixed(2);
if (nfCpuUsage > 100) {
nfCpuUsage = 100;
}
sysCpuUsage = neState.cpu.sysCpuUsage;
const sysCpu = +(sysCpuUsage / 100);
sysCpuUsage = +sysCpu.toFixed(2);
if (sysCpuUsage > 100) {
sysCpuUsage = 100;
}
}
let sysMemUsage = 0;
if (neState.mem) {
const men = neState.mem.sysMemUsage;
sysMemUsage = +(men / 100).toFixed(2);
if (sysMemUsage > 100) {
sysMemUsage = 100;
}
}
let sysDiskUsage = 0;
if (neState.disk && Array.isArray(neState.disk.partitionInfo)) {
let disks: any[] = 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);
}
}
return {
sysDiskUsage,
sysMemUsage,
sysCpuUsage,
nfCpuUsage,
};
}
return {
fnNeStart,
fnNeRestart,
fnNeStop,
fnNeReload,
fnNeLogFile,
parseResouresUsage,
};
return { fnNeStart, fnNeRestart, fnNeStop, fnNeReload, fnNeLogFile };
}

View File

@@ -7,38 +7,26 @@ import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import { ColumnsType } from 'ant-design-vue/es/table';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useNeStore from '@/store/modules/ne';
import { listNeInfo, delNeInfo } from '@/api/ne/neInfo';
import { stateNeInfo } from '@/api/ne/neAction';
import useNeInfoStore from '@/store/modules/neinfo';
import { listNeInfo, delNeInfo, stateNeInfo } from '@/api/ne/neInfo';
import { NE_TYPE_LIST } from '@/constants/ne-constants';
import { hasRoles } from '@/plugins/auth-user';
import useDictStore from '@/store/modules/dict';
import useNeOptions from '@/views/ne/info/hooks/useNeOptions';
import useCoreStore from '@/store/modules/core';
import useNeOptions from './hooks/useNeOptions';
const { getDict } = useDictStore();
const neStore = useNeStore();
const coreStore = useCoreStore();
const { t } = useI18n();
const {
fnNeStart,
fnNeRestart,
fnNeStop,
fnNeReload,
fnNeLogFile,
parseResouresUsage,
} = useNeOptions();
const { fnNeStart, fnNeRestart, fnNeStop, fnNeReload, fnNeLogFile } =
useNeOptions();
//
const EditModal = defineAsyncComponent(
() => import('@/views/ne/info/components/EditModal.vue')
() => import('./components/EditModal.vue')
);
const OAMModal = defineAsyncComponent(
() => import('@/views/ne/info/components/OAMModal.vue')
);
//
const LicenseEditModal = defineAsyncComponent(
() => import('@/views/ne/info/components/LicenseEditModal.vue')
() => import('./components/OAMModal.vue')
);
//
const BackConfModal = defineAsyncComponent(
() => import('@/views/ne/info/components/BackConfModal.vue')
() => import('./components/BackConfModal.vue')
);
const backConf = ref(); //
@@ -52,7 +40,6 @@ let dict: {
/**查询参数 */
let queryParams = reactive({
coreUid: coreStore.currentCoreUid,
/**网元类型 */
neType: '',
/**带状态信息 */
@@ -87,34 +74,37 @@ type TabeStateType = {
data: Record<string, any>[];
/**勾选记录 */
selectedRowKeys: (string | number)[];
/**勾选记录 */
selectedRows: Record<string, any>[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
size: 'middle',
seached: true,
seached: false,
data: [],
selectedRowKeys: [],
selectedRows: [],
});
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('views.ne.common.neUid'),
dataIndex: 'neUid',
align: 'left',
width: 100,
},
{
title: t('views.ne.common.neType'),
dataIndex: 'neType',
align: 'left',
width: 100,
},
{
title: t('views.ne.common.neId'),
dataIndex: 'neId',
align: 'left',
width: 100,
},
{
title: t('views.ne.common.rmUid'),
dataIndex: 'rmUid',
align: 'left',
width: 150,
},
{
title: t('views.ne.common.neName'),
dataIndex: 'neName',
@@ -123,7 +113,7 @@ let tableColumns: ColumnsType = [
},
{
title: t('views.ne.common.ipAddr'),
dataIndex: 'ipAddr',
dataIndex: 'ip',
align: 'left',
width: 150,
},
@@ -133,42 +123,6 @@ let tableColumns: ColumnsType = [
align: 'left',
width: 100,
},
{
title: t('views.ne.common.serialNum'),
dataIndex: 'serialNum',
align: 'left',
width: 120,
},
{
title: t('views.ne.common.expiryDate'),
dataIndex: 'expiryDate',
align: 'left',
width: 150,
},
{
title: t('views.ne.common.ueNumber'),
dataIndex: 'ueNumber',
align: 'left',
customRender(opt) {
if (['UDM', 'AMF', 'MME'].includes(opt.record.neType)) {
return opt.value;
}
return '';
},
width: 120,
},
{
title: t('views.ne.common.nbNumber'),
dataIndex: 'nbNumber',
align: 'left',
customRender(opt) {
if (['AMF', 'MME'].includes(opt.record.neType)) {
return opt.value;
}
return '';
},
width: 120,
},
{
title: t('views.ne.neInfo.state'),
dataIndex: 'status',
@@ -217,21 +171,12 @@ function fnTableSize({ key }: MenuInfo) {
}
/**表格多选 */
function fnTableSelectedRowKeys(keys: (string | number)[], rows: any[]) {
function fnTableSelectedRowKeys(keys: (string | number)[]) {
tableState.selectedRowKeys = keys;
tableState.selectedRows = rows.map(item => {
return {
id: item.id,
neUid: item.neUid,
neType: item.neType,
};
});
}
/**对话框对象信息状态类型 */
type ModalStateType = {
/**软件授权上传框是否显示 */
openByLicense: boolean;
/**配置备份框是否显示 */
openByBackConf: boolean;
/**OAM文件配置框是否显示 */
@@ -239,8 +184,9 @@ type ModalStateType = {
/**新增框或修改框是否显示 */
openByEdit: boolean;
/**新增框或修改框ID */
id: number;
neUid: string;
editId: string;
/**OAM框网元类型ID */
neId: string;
neType: string;
/**确定按钮 loading */
confirmLoading: boolean;
@@ -248,12 +194,11 @@ type ModalStateType = {
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
openByLicense: false,
openByBackConf: false,
openByOAM: false,
openByEdit: false,
id: 0,
neUid: '',
editId: '',
neId: '',
neType: '',
confirmLoading: false,
});
@@ -264,13 +209,9 @@ let modalState: ModalStateType = reactive({
*/
function fnModalVisibleByEdit(row?: Record<string, any>) {
if (!row) {
modalState.id = 0;
modalState.neUid = '';
modalState.neType = '';
modalState.editId = '';
} else {
modalState.id = row.id;
modalState.neUid = row.neUid;
modalState.neType = row.neType;
modalState.editId = row.id;
}
modalState.openByEdit = !modalState.openByEdit;
}
@@ -281,33 +222,24 @@ function fnModalVisibleByEdit(row?: Record<string, any>) {
*/
function fnModalEditOk(from: Record<string, any>) {
//
if (!from.neUid) {
if (!from.id) {
fnGetList();
return;
}
//
reloadRowInfo(from);
}
/**局部更新信息 */
function reloadRowInfo(row: Record<string, any>) {
stateNeInfo(row.neUid)
stateNeInfo(from.neType, from.neId)
.then(res => {
//
const item = tableState.data.find(s => s.id === row.id);
const item = tableState.data.find(s => s.id === from.id);
if (item && res.code === RESULT_CODE_SUCCESS) {
item.neType = row.neType;
item.neUid = row.neUid;
item.neName = row.neName;
item.ipAddr = row.ipAddr;
item.port = row.port;
if (res.data.online) {
item.status = '1';
if (res.data.standby) {
item.status = '3';
}
} else {
item.status = '0';
item.neType = from.neType;
item.neId = from.neId;
item.rmUid = from.rmUid;
item.neName = from.neName;
item.ip = from.ip;
item.port = from.port;
if (item.status !== '2') {
item.status = res.data.online ? '1' : '0';
}
Object.assign(item.serverState, res.data);
const resouresUsage = parseResouresUsage(item.serverState);
@@ -315,7 +247,7 @@ function reloadRowInfo(row: Record<string, any>) {
}
})
.finally(() => {
neStore.fnNelistRefresh();
useNeInfoStore().fnRefreshNelist();
});
}
@@ -324,8 +256,7 @@ function reloadRowInfo(row: Record<string, any>) {
* 进行表达规则校验
*/
function fnModalEditCancel() {
modalState.neUid = '';
modalState.neType = '';
modalState.editId = '';
modalState.openByEdit = false;
modalState.openByOAM = false;
modalState.openByBackConf = false;
@@ -336,10 +267,11 @@ function fnModalEditCancel() {
* @param id 编号
*/
function fnRecordDelete(id: string) {
if (modalState.confirmLoading) return;
if (!id || modalState.confirmLoading) return;
let msg = t('views.ne.neInfo.delTip');
if (id === '0') {
msg = `${msg} ...${tableState.selectedRowKeys.length}`;
id = tableState.selectedRowKeys.join(',');
}
Modal.confirm({
@@ -348,38 +280,23 @@ function fnRecordDelete(id: string) {
onOk() {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
let reqArr: any = [];
if (id === '0') {
const ids = tableState.selectedRowKeys.join(',');
delNeInfo({ id: ids });
} else {
tableState.data.forEach(item => {
if (item.id === id) {
reqArr.push(
delNeInfo({
id: item.id,
})
);
}
});
}
Promise.all(reqArr)
.then(resArr => {
if (resArr.every((item: any) => item.code === RESULT_CODE_SUCCESS)) {
delNeInfo(id)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.operateOk'), 3);
// id
tableState.data = tableState.data.filter(item => {
if (tableState.selectedRowKeys.length > 0) {
if (id.indexOf(',') > -1) {
return !tableState.selectedRowKeys.includes(item.id);
} else {
return item.id !== id;
}
});
//
neStore.fnNelistRefresh();
useNeInfoStore().fnRefreshNelist();
} else {
message.error({
content: t('common.operateErr'),
content: `${res.msg}`,
duration: 3,
});
}
@@ -401,13 +318,13 @@ function fnRecordMore(type: string | number, row: Record<string, any>) {
fnRecordDelete(row.id);
break;
case 'start':
fnNeStart(row, () => reloadRowInfo(row));
fnNeStart(row);
break;
case 'restart':
fnNeRestart(row, () => reloadRowInfo(row));
fnNeRestart(row);
break;
case 'stop':
fnNeStop(row, () => reloadRowInfo(row));
fnNeStop(row);
break;
case 'reload':
fnNeReload(row);
@@ -416,21 +333,15 @@ function fnRecordMore(type: string | number, row: Record<string, any>) {
fnNeLogFile(row);
break;
case 'oam':
modalState.neUid = row.neUid;
modalState.neId = row.neId;
modalState.neType = row.neType;
modalState.openByOAM = !modalState.openByOAM;
break;
case 'license':
modalState.id = row.id;
modalState.neUid = row.neUid;
modalState.neType = row.neType;
modalState.openByLicense = !modalState.openByLicense;
break;
case 'backConfExport':
backConf.value.exportConf(row.neUid, row.neType);
backConf.value.exportConf(row.neType, row.neId);
break;
case 'backConfImport':
modalState.neUid = row.neUid;
modalState.neId = row.neId;
modalState.neType = row.neType;
modalState.openByBackConf = !modalState.openByBackConf;
break;
@@ -447,39 +358,80 @@ function fnGetList(pageNum?: number) {
if (pageNum) {
queryParams.pageNum = pageNum;
}
listNeInfo(toRaw(queryParams))
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data.rows)) {
//
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
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;
});
listNeInfo(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
//
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
tableState.loading = false;
})
.finally(() => {
//
neStore.fnNelistRefresh();
});
tablePagination.total = res.total;
//
tableState.data = res.rows.map(item => {
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.loading = false;
});
}
/**解析网元状态携带的资源利用率 */
function parseResouresUsage(neState: Record<string, any>) {
let sysCpuUsage = 0;
let nfCpuUsage = 0;
if (neState.cpu) {
nfCpuUsage = neState.cpu.nfCpuUsage;
const nfCpu = +(nfCpuUsage / 100);
nfCpuUsage = +nfCpu.toFixed(2);
if (nfCpuUsage > 100) {
nfCpuUsage = 100;
}
sysCpuUsage = neState.cpu.sysCpuUsage;
const sysCpu = +(sysCpuUsage / 100);
sysCpuUsage = +sysCpu.toFixed(2);
if (sysCpuUsage > 100) {
sysCpuUsage = 100;
}
}
let sysMemUsage = 0;
if (neState.mem) {
const men = neState.mem.sysMemUsage;
sysMemUsage = +(men / 100).toFixed(2);
if (sysMemUsage > 100) {
sysMemUsage = 100;
}
}
let sysDiskUsage = 0;
if (neState.disk && Array.isArray(neState.disk.partitionInfo)) {
let disks: any[] = 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);
}
}
return {
sysDiskUsage,
sysMemUsage,
sysCpuUsage,
nfCpuUsage,
};
}
onMounted(() => {
@@ -490,8 +442,13 @@ onMounted(() => {
}
});
//
fnGetList();
//
useNeInfoStore()
.fnRefreshNelist()
.finally(() => {
//
fnGetList();
});
});
</script>
@@ -509,7 +466,7 @@ onMounted(() => {
<a-form-item :label="t('views.ne.common.neType')" name="neType ">
<a-auto-complete
v-model:value="queryParams.neType"
:options="neStore.getNeSelectOtions"
:options="NE_TYPE_LIST.map(v => ({ value: v }))"
allow-clear
:placeholder="t('common.inputPlease')"
/>
@@ -536,7 +493,7 @@ onMounted(() => {
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-space :size="8" align="center" v-roles:has="['admin']">
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
<template #icon><PlusOutlined /></template>
{{ t('common.addText') }}
@@ -622,26 +579,31 @@ onMounted(() => {
</template>
<template v-if="column.key === 'id'">
<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>
</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>
</a-tooltip>
<span v-roles:has="['admin']">
<a-tooltip>
<template #title>{{ t('common.editText') }}</template>
<a-button
type="link"
@click.prevent="fnModalVisibleByEdit(record)"
>
<template #icon><FormOutlined /></template>
</a-button>
</a-tooltip>
</span>
<span v-roles:has="['admin', 'teacher']">
<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>
</a-tooltip>
</span>
<a-tooltip placement="left">
<template #title>{{ t('common.moreText') }}</template>
<a-dropdown placement="bottomRight" trigger="click">
@@ -654,36 +616,33 @@ onMounted(() => {
<FileTextOutlined />
{{ t('views.ne.common.log') }}
</a-menu-item>
<a-menu-item key="start">
<a-menu-item key="start" v-if="hasRoles(['admin'])">
<ThunderboltOutlined />
{{ t('views.ne.common.start') }}
</a-menu-item>
<a-menu-item key="stop">
<a-menu-item key="stop" v-if="hasRoles(['admin'])">
<CloseSquareOutlined />
{{ t('views.ne.common.stop') }}
</a-menu-item>
<a-menu-item key="reload" v-if="false">
<a-menu-item
key="reload"
v-if="
!['OMC', 'PCF', 'IMS', 'MME'].includes(
record.neType
) && hasRoles(['admin'])
"
>
<SyncOutlined />
{{ t('views.ne.common.reload') }}
</a-menu-item>
<a-menu-item key="delete">
<a-menu-item key="delete" v-if="hasRoles(['admin'])">
<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="hasRoles(['admin'])">
<FileTextOutlined />
{{ t('views.ne.common.oam') }}
</a-menu-item>
<a-menu-item
key="license"
v-if="!['OMC'].includes(record.neType)"
>
<FileTextOutlined />
{{ t('views.ne.common.license') }}
</a-menu-item>
<!-- 配置备份 -->
<a-menu-item key="backConfExport">
<ExportOutlined />
@@ -701,14 +660,22 @@ onMounted(() => {
</template>
</template>
<template #expandedRowRender="{ record }">
<a-row>
<a-row :gutter="16">
<a-col :offset="2" :lg="8" :md="8" :xs="8">
<a-divider orientation="left">
{{ t('views.ne.neInfo.info') }}
</a-divider>
<div>
<span>{{ t('views.ne.neInfo.serviceState') }}</span>
<DictTag :options="dict.neInfoStatus" :value="record.status" />
<a-tag
:color="record.serverState.online ? 'processing' : 'error'"
>
{{
record.serverState.online
? t('views.ne.common.normalcy')
: t('views.ne.common.exceptions')
}}
</a-tag>
</div>
<div>
<span>{{ t('views.ne.neVersion.version') }}</span>
@@ -722,21 +689,6 @@ onMounted(() => {
<span>{{ t('views.ne.common.expiryDate') }}</span>
<span>{{ record.serverState.expire }}</span>
</div>
<div>
<span>{{ t('views.ne.common.ueNumber') }}</span>
<span
v-if="
['UDM', 'AMF', 'MME'].includes(record.serverState.neType)
"
>
{{ record.serverState.ueNumber }}
</span>
<span v-else> - </span>
</div>
<div v-if="['AMF', 'MME'].includes(record.serverState.neType)">
<span>{{ t('views.ne.common.nbNumber') }}</span>
<span> {{ record.serverState.nbNumber }} </span>
</div>
</a-col>
<a-col :offset="2" :lg="8" :md="8" :xs="8">
<a-divider orientation="left">
@@ -807,7 +759,7 @@ onMounted(() => {
<!-- 新增框或修改框 -->
<EditModal
v-model:open="modalState.openByEdit"
:id="modalState.id"
:edit-id="modalState.editId"
@ok="fnModalEditOk"
@cancel="fnModalEditCancel"
></EditModal>
@@ -815,7 +767,7 @@ onMounted(() => {
<!-- OAM编辑框 -->
<OAMModal
v-model:open="modalState.openByOAM"
:ne-uid="modalState.neUid"
:ne-id="modalState.neId"
:ne-type="modalState.neType"
@cancel="fnModalEditCancel"
></OAMModal>
@@ -824,20 +776,10 @@ onMounted(() => {
<BackConfModal
ref="backConf"
v-model:open="modalState.openByBackConf"
:ne-uid="modalState.neUid"
:ne-id="modalState.neId"
:ne-type="modalState.neType"
@cancel="fnModalEditCancel"
></BackConfModal>
<!-- 文件上传框 -->
<LicenseEditModal
v-model:open="modalState.openByLicense"
:id="modalState.id"
:ne-uid="modalState.neUid"
:ne-type="modalState.neType"
@ok="fnModalEditOk"
@cancel="fnModalEditCancel"
></LicenseEditModal>
</PageContainer>
</template>

View File

@@ -8,9 +8,6 @@ import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import { ColumnsType } from 'ant-design-vue/es/table';
import UploadModal from '@/components/UploadModal/index.vue';
import {
importData,
importTemplate,
exportUser,
changeUserStatus,
listUser,
resetUserPwd,
@@ -19,7 +16,8 @@ import {
updateUser,
addUser,
} from '@/api/system/user';
import { deptTree } from '@/api/system/dept';
import { importData, importTemplate, exportUser } from '@/api/pt/user';
import { deptTreeSelect } from '@/api/system/dept';
import { saveAs } from 'file-saver';
import useI18n from '@/hooks/useI18n';
import { parseDateToStr } from '@/utils/date-utils';
@@ -34,8 +32,6 @@ import useDictStore from '@/store/modules/dict';
import useUserStore from '@/store/modules/user';
import { DataNode } from 'ant-design-vue/es/tree';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { uploadFile } from '@/api/tool/file';
import type { Dayjs } from 'dayjs';
const { getDict } = useDictStore();
const userStore = useUserStore();
const { t } = useI18n();
@@ -44,35 +40,30 @@ const { t } = useI18n();
let dict: {
/**状态 */
sysNormalDisable: DictType[];
/**用户类型 */
sysUserType: DictType[];
/**性别 */
sysUserSex: DictType[];
} = reactive({
sysNormalDisable: [],
sysUserType: [],
sysUserSex: [],
});
/**开始结束时间 */
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>(undefined);
let queryRangePicker = ref<[string, string]>(['', '']);
/**查询参数 */
let queryParams = reactive({
/**登录账号 */
userName: '',
/**手机号 */
phone: '',
phonenumber: '',
/**部门ID */
deptId: undefined,
/**用户状态 */
statusFlag: undefined,
/**用户类型 */
userType: undefined,
status: undefined,
/**记录开始时间 */
beginTime: undefined as number | undefined,
beginTime: '',
/**记录结束时间 */
endTime: undefined as number | undefined,
endTime: '',
/**当前页数 */
pageNum: 1,
/**每页条数 */
@@ -83,16 +74,15 @@ let queryParams = reactive({
function fnQueryReset() {
queryParams = Object.assign(queryParams, {
userName: '',
phone: '',
phonenumber: '',
deptId: undefined,
statusFlag: undefined,
userType: undefined,
beginTime: undefined,
endTime: undefined,
status: undefined,
beginTime: '',
endTime: '',
pageNum: 1,
pageSize: 20,
});
queryRangePicker.value = undefined;
queryRangePicker.value = ['', ''];
tablePagination.current = 1;
tablePagination.pageSize = 20;
fnGetList();
@@ -154,13 +144,6 @@ let tableColumns: ColumnsType = [
align: 'left',
width: 200,
},
{
title: t('views.system.user.userType'),
dataIndex: 'userType',
key: 'userType',
align: 'left',
width: 200,
},
{
title: t('views.system.user.loginIp'),
dataIndex: 'loginIp',
@@ -170,9 +153,9 @@ let tableColumns: ColumnsType = [
},
{
title: t('views.system.user.loginTime'),
dataIndex: 'loginTime',
dataIndex: 'loginDate',
align: 'left',
width: 200,
width: 150,
customRender(opt) {
if (+opt.value <= 0) return '';
return parseDateToStr(+opt.value);
@@ -180,8 +163,8 @@ let tableColumns: ColumnsType = [
},
{
title: t('views.system.user.status'),
dataIndex: 'statusFlag',
key: 'statusFlag',
dataIndex: 'status',
key: 'status',
align: 'left',
width: 100,
},
@@ -268,14 +251,14 @@ let modalState: ModalStateType = reactive({
password: '',
deptId: '100',
email: '',
loginTime: 0,
loginDate: 0,
loginIp: '',
nickName: '',
phone: '',
phonenumber: '',
postIds: [],
roleIds: [],
sex: '0',
statusFlag: '0',
status: '0',
remark: '',
createTime: 0,
},
@@ -318,7 +301,7 @@ const modalStateFrom = Form.useForm(
message: t('views.system.user.emailTip'),
},
],
phone: [
phonenumber: [
{
required: false,
pattern: regExpMobile,
@@ -345,12 +328,12 @@ function fnModalVisibleByVive(userId: string | number) {
hide();
if (res.code === RESULT_CODE_SUCCESS && res.data) {
const roles = res.data.roles.map((m: Record<string, any>) => {
const disabled = m.statusFlag === '0';
const disabled = m.status === '0';
Reflect.set(m, 'disabled', disabled);
return m;
});
const posts = res.data.posts.map((m: Record<string, any>) => {
const disabled = m.statusFlag === '0';
const disabled = m.status === '0';
Reflect.set(m, 'disabled', disabled);
return m;
});
@@ -386,12 +369,12 @@ function fnModalVisibleByEdit(userId?: string | number) {
hide();
if (res.code === RESULT_CODE_SUCCESS && res.data) {
const roles = res.data.roles.map((m: Record<string, any>) => {
const disabled = m.statusFlag === '0';
const disabled = m.status === '0';
Reflect.set(m, 'disabled', disabled);
return m;
});
const posts = res.data.posts.map((m: Record<string, any>) => {
const disabled = m.statusFlag === '0';
const disabled = m.status === '0';
Reflect.set(m, 'disabled', disabled);
return m;
});
@@ -418,12 +401,12 @@ function fnModalVisibleByEdit(userId?: string | number) {
hide();
if (res.code === RESULT_CODE_SUCCESS && res.data) {
const roles = res.data.roles.map((m: Record<string, any>) => {
const disabled = m.statusFlag === '0';
const disabled = m.status === '0';
Reflect.set(m, 'disabled', disabled);
return m;
});
const posts = res.data.posts.map((m: Record<string, any>) => {
const disabled = m.statusFlag === '0';
const disabled = m.status === '0';
Reflect.set(m, 'disabled', disabled);
return m;
});
@@ -448,7 +431,7 @@ function fnModalVisibleByEdit(userId?: string | number) {
* 进行表达规则校验
*/
function fnModalOk() {
let validateName = ['nickName', 'email', 'phone'];
let validateName = ['nickName', 'email', 'phonenumber'];
if (!modalState.from.userId) {
validateName.push('userName', 'password');
}
@@ -457,6 +440,11 @@ function fnModalOk() {
.then(() => {
modalState.confirmLoading = true;
const from = toRaw(modalState.from);
//
if (!from.userId && userStore.roles.includes('teacher')) {
from.postIds = ['3'];
from.roleIds = ['4'];
}
const user = from.userId ? updateUser(from) : addUser(from);
const key = 'user';
message.loading({ content: t('common.loading'), key });
@@ -507,7 +495,7 @@ function fnModalCancel() {
*/
function fnModalOkResetPwd() {
modalStateFrom
.validate(['password'])
.validate(['userName', 'password'])
.then(() => {
modalState.confirmLoading = true;
const key = 'user';
@@ -557,7 +545,7 @@ function fnRecordResetPwd(row: Record<string, string>) {
*/
function fnRecordStatus(row: Record<string, string>) {
const text =
row.statusFlag === '1'
row.status === '1'
? t('views.system.user.start')
: t('views.system.user.stop');
Modal.confirm({
@@ -569,7 +557,7 @@ function fnRecordStatus(row: Record<string, string>) {
onOk() {
const key = 'changeUserStatus';
message.loading({ content: t('common.loading'), key });
changeUserStatus(row.userId, row.statusFlag).then(res => {
changeUserStatus(row.userId, row.status).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: `${row.userName} ${t('common.msgSuccess', { msg: text })}`,
@@ -587,7 +575,7 @@ function fnRecordStatus(row: Record<string, string>) {
});
},
onCancel() {
row.statusFlag = row.statusFlag === '1' ? '0' : '1';
row.status = row.status === '1' ? '0' : '1';
},
});
}
@@ -632,27 +620,26 @@ function fnExportList() {
title: t('common.tipTitle'),
content: t('views.system.user.exportSure'),
onOk() {
const hide = message.loading(t('common.loading'), 0);
exportUser(toRaw(queryParams))
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', {
msg: t('views.system.user.export'),
}),
duration: 3,
});
saveAs(res.data, `user_${Date.now()}.xlsx`);
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
});
const key = 'exportUser';
message.loading({ content: t('common.loading'), key });
exportUser(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', {
msg: t('views.system.user.export'),
}),
key,
duration: 2,
});
saveAs(res.data, `user_${Date.now()}.xlsx`);
} else {
message.error({
content: `${res.msg}`,
key,
duration: 2,
});
}
});
},
});
}
@@ -697,30 +684,17 @@ function fnModalUploadImportClose() {
/**对话框表格信息导入上传 */
function fnModalUploadImportUpload(file: File) {
//
const hide = message.loading(t('common.loading'), 0);
uploadImportState.loading = true;
let formData = new FormData();
formData.append('file', file);
formData.append('subPath', 'import');
uploadFile(formData)
formData.append('updateSupport', `${uploadImportState.updateSupport}`);
importData(formData)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
return res.data.filePath;
}
return '';
uploadImportState.msg = res.msg?.replaceAll(/<br\/>+/g, '\r');
})
.then(filePath => {
if (filePath === '') return undefined;
return importData(filePath, uploadImportState.updateSupport);
})
.then(res => {
if (res === undefined) return;
if (res.code === RESULT_CODE_SUCCESS) {
uploadImportState.msg = res.msg?.replaceAll(/<br\/>+/g, '\r');
} else {
message.error(res.msg, 3);
}
.catch((err: { code: number; msg: string }) => {
message.error(` ${err.msg}`);
})
.finally(() => {
hide();
@@ -761,28 +735,19 @@ function fnGetList(pageNum?: number) {
if (pageNum) {
queryParams.pageNum = pageNum;
}
//
if (
Array.isArray(queryRangePicker.value) &&
queryRangePicker.value.length > 0
) {
queryParams.beginTime = queryRangePicker.value[0].valueOf();
queryParams.endTime = queryRangePicker.value[1].valueOf();
} else {
queryParams.beginTime = undefined;
queryParams.endTime = undefined;
if (!queryRangePicker.value) {
queryRangePicker.value = ['', ''];
}
queryParams.beginTime = queryRangePicker.value[0];
queryParams.endTime = queryRangePicker.value[1];
listUser(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
//
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
const { total, rows } = res.data;
tablePagination.total = total;
tableState.data = rows;
tablePagination.total = res.total;
tableState.data = res.rows;
}
tableState.loading = false;
});
@@ -794,28 +759,34 @@ let deptTreeData = ref<DataNode[]>([]);
/**查询部门下拉树结构 */
function fnGetDeptTree() {
if (deptTreeData.value.length > 0) return;
deptTree().then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
deptTreeSelect().then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
deptTreeData.value = res.data;
}
});
}
/**选择指定只能一个 */
function fnSelectOne(v: string, type: string) {
if (type === 'postId') {
modalState.from.postIds = [v];
}
if (type === 'roleId') {
modalState.from.roleIds = [v];
}
}
onMounted(() => {
//
Promise.allSettled([
getDict('sys_normal_disable'),
getDict('sys_user_type'),
getDict('sys_user_sex'),
]).then(resArr => {
if (resArr[0].status === 'fulfilled') {
dict.sysNormalDisable = resArr[0].value;
}
if (resArr[1].status === 'fulfilled') {
dict.sysUserType = resArr[1].value;
}
if (resArr[2].status === 'fulfilled') {
dict.sysUserSex = resArr[2].value;
dict.sysUserSex = resArr[1].value;
}
});
//
@@ -889,22 +860,22 @@ onMounted(() => {
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item :label="t('views.system.user.phone')" name="phone">
<a-form-item
:label="t('views.system.user.phone')"
name="phonenumber"
>
<a-input
v-model:value="queryParams.phone"
v-model:value="queryParams.phonenumber"
allow-clear
:maxlength="11"
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item
:label="t('views.system.user.status')"
name="statusFlag"
>
<a-col :lg="4" :md="12" :xs="24">
<a-form-item :label="t('views.system.user.status')" name="status">
<a-select
v-model:value="queryParams.statusFlag"
v-model:value="queryParams.status"
allow-clear
:options="dict.sysNormalDisable"
:placeholder="t('common.selectPlease')"
@@ -912,20 +883,6 @@ onMounted(() => {
</a-select>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item
:label="t('views.system.user.userType')"
name="userType"
>
<a-select
v-model:value="queryParams.userType"
allow-clear
:options="dict.sysUserType"
:placeholder="t('common.selectPlease')"
>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="8" :md="12" :xs="24">
<a-form-item
:label="t('views.system.user.loginTime')"
@@ -937,6 +894,7 @@ onMounted(() => {
bordered
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
value-format="x"
style="width: 100%"
></a-range-picker>
</a-form-item>
@@ -1053,27 +1011,21 @@ onMounted(() => {
{{ r.roleName }}
</a-tag>
</template>
<template v-if="column.key === 'userType'">
<DictTag :options="dict.sysUserType" :value="record.userType" />
<a-tag :bordered="false" v-if="record.userSource != '#'">
{{ record.userSource }}
</a-tag>
</template>
<template v-if="column.key === 'deptId'">
{{ record.dept?.deptName }}
</template>
<template v-if="column.key === 'statusFlag'">
<template v-if="column.key === 'status'">
<DictTag
v-if="
record.userId === 1 || record.userName === userStore.userName
record.userId === '1' || record.userName === userStore.userName
"
:options="dict.sysNormalDisable"
:value="record.statusFlag"
:value="record.status"
/>
<a-switch
v-else
v-perms:has="['system:user:edit']"
v-model:checked="record.statusFlag"
v-model:checked="record.status"
checked-value="1"
:checked-children="dict.sysNormalDisable[0].label"
un-checked-value="0"
@@ -1083,7 +1035,7 @@ onMounted(() => {
/>
</template>
<template v-if="column.key === 'userId'">
<a-space :size="8" align="center" v-if="record.userId !== 1">
<a-space :size="8" align="center" v-if="record.userId !== '1'">
<a-tooltip>
<template #title>{{ t('common.viewText') }}</template>
<a-button
@@ -1104,12 +1056,7 @@ onMounted(() => {
<template #icon><FormOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip
placement="topLeft"
v-if="
record.userId != userStore.userId && record.userSource == '#'
"
>
<a-tooltip>
<template #title>{{ t('common.deleteText') }}</template>
<a-button
type="link"
@@ -1119,12 +1066,7 @@ onMounted(() => {
<template #icon><DeleteOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip
placement="topLeft"
v-if="
record.userId != userStore.userId && record.userSource == '#'
"
>
<a-tooltip placement="topLeft">
<template #title>
{{ t('views.system.user.resetPwd') }}
</template>
@@ -1178,10 +1120,10 @@ onMounted(() => {
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.system.user.loginTime')"
name="loginTime"
name="loginDate"
>
<span v-if="+modalState.from.loginTime > 0">
{{ parseDateToStr(+modalState.from.loginTime) }}
<span v-if="+modalState.from.loginDate > 0">
{{ parseDateToStr(+modalState.from.loginDate) }}
</span>
</a-form-item>
</a-col>
@@ -1225,22 +1167,22 @@ onMounted(() => {
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.system.user.status')"
name="statusFlag"
>
<a-form-item :label="t('views.system.user.status')" name="status">
<DictTag
:options="dict.sysNormalDisable"
:value="modalState.from.statusFlag"
:value="modalState.from.status"
/>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-row :gutter="16" v-if="false">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.system.user.phone')" name="phone">
{{ modalState.from.phone }}
<a-form-item
:label="t('views.system.user.phone')"
name="phonenumber"
>
{{ modalState.from.phonenumber }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -1422,21 +1364,22 @@ onMounted(() => {
:options="modalState.options.posts"
:field-names="{ label: 'postName', value: 'postId' }"
:placeholder="t('common.selectPlease')"
@select="(value:any) => fnSelectOne(value, 'postId')"
>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-row :gutter="16" v-if="false">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.system.user.phone')"
name="phone"
v-bind="modalStateFrom.validateInfos.phone"
name="phonenumber"
v-bind="modalStateFrom.validateInfos.phonenumber"
>
<IntlTelInput
v-model:value="modalState.from.phone"
v-model:value="modalState.from.phonenumber"
allow-clear
:maxlength="20"
:placeholder="t('common.inputPlease')"
@@ -1476,16 +1419,13 @@ onMounted(() => {
:md="12"
:xs="24"
v-if="
modalState.from.userId !== 1 &&
modalState.from.userId !== '1' &&
modalState.from.userId !== userStore.userId
"
>
<a-form-item
:label="t('views.system.user.status')"
name="statusFlag"
>
<a-form-item :label="t('views.system.user.status')" name="status">
<a-select
v-model:value="modalState.from.statusFlag"
v-model:value="modalState.from.status"
default-value="0"
:options="dict.sysNormalDisable"
:placeholder="t('common.selectPlease')"
@@ -1511,6 +1451,7 @@ onMounted(() => {
:options="modalState.options.roles"
:field-names="{ label: 'roleName', value: 'roleId' }"
:placeholder="t('common.selectPlease')"
@select="(value:any) => fnSelectOne(value, 'roleId')"
>
</a-select>
</a-form-item>
@@ -1519,6 +1460,7 @@ onMounted(() => {
:label="t('views.system.user.fromClass')"
name="deptId"
:label-col="{ span: 3 }"
v-perms:has="['system:user:editPost']"
>
<a-tree-select
v-model:value="modalState.from.deptId"

View File

@@ -10,19 +10,12 @@
*
*/
(function () {
// baseUrl = protocol://ip:port
// baseUrl = 'http://192.168.8.100:33030';
const protocol = window.location.protocol
let wsprotocol = "ws:"
const hostname = window.location.hostname
let host = `${hostname}:33030`;
if (protocol === 'https:') {
host = `${hostname}:33443`;
wsprotocol = "wss:"
}
// host = ip:port
// const host = '192.168.8.100:33030';
const host = `${window.location.hostname}:33030`;
// Service Address
sessionStorage.setItem('baseUrl', `${protocol}//${host}`);
sessionStorage.setItem('baseUrl', `http://${host}`);
// websocket Address
sessionStorage.setItem('wsUrl', `${wsprotocol}//${host}`);
})();
sessionStorage.setItem('wsUrl', `ws://${host}`);
})();

Binary file not shown.

Binary file not shown.

View File

@@ -1,3 +0,0 @@
# !!! Remove lines containing the # symbol when importing files.
#imsi,msisdn,sess_rules,pcc_rules,hdr_enrich,rfsp,sar,qos_audio,qos_video
460996650000580,62357000580,internet|ims_sig,internet|ims_sig,dnn,1,def_sar,qos_audio,qos_video,0,0

View File

@@ -1,4 +0,0 @@
# !!! Remove lines containing the # symbol when importing files.
#imsi,ki,aigoIndex,amf,opc
460996650000580,1234567890ABCDEF1234567890ABCDEF,0,8000
460996650000581,1234567890ABCDEF1234567890ABCDEF,0,8000

View File

@@ -1,4 +0,0 @@
# !!! Remove lines containing the # symbol when importing files.
#IMSI,MSISDN,UeAmbrTpl,NssaiTpl,AreaForbiddenTpl,ServiceAreaRestrictionTpl,RatRestrictions,CnTypeRestrictions,SmfSel,SmData,EPSDat
460996650000580,62357000580,def_ambr,def_nssai,def_arfb,def_sar,0,3,def_snssai,1-000001&content&ims,1,64,24,65,def_eps,1,010200000000,-
460996650000581,62357000581,def_ambr,def_nssai,def_arfb,def_sar,0,3,def_snssai,1-000001&content&ims,1,64,24,65,def_eps,1,010200000000,-

View File

@@ -1,4 +0,0 @@
# !!! Remove lines containing the # symbol when importing files.
#username,password
62357000580,123456
62357000581,123456

View File

@@ -1,5 +0,0 @@
# !!! Remove lines containing the # symbol when importing files.
#vlote=0 MSISDN and IMSI need to be filled in the same way.
#imsi,msisdn,vlote,vni
460996650000580,62357000580,1,ims.mnc000.mcc460.3gppnetwork.org
62357000581,62357000581,0,ims.mnc000.mcc460.3gppnetwork.org

View File

@@ -1,28 +0,0 @@
<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg">
<!-- Created with Method Draw - http://github.com/duopixel/Method-Draw/ -->
<g>
<title>background</title>
<rect fill="none" id="canvas_background" height="1026" width="1026" y="-1" x="-1"/>
<g display="none" overflow="visible" y="0" x="0" height="100%" width="100%" id="canvasGrid">
<rect fill="url(#gridpattern)" stroke-width="0" y="1" x="1" height="768" width="1024"/>
</g>
</g>
<g>
<title>Layer 1</title>
<g stroke="null" id="svg_15">
<path stroke="null" id="svg_4" fill="#B5D6FB" d="m512.094844,961.632039c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-405.588164,-251.575028c-2.275921,-1.419037 -3.698372,-4.054392 -3.698372,-6.892467l0,-90.007504c0,-2.838074 1.422451,-5.473429 3.698372,-6.892467l405.588164,-255.426701c1.137961,-0.709519 2.465582,-1.114958 3.793202,-1.114958s2.655242,0.405439 3.793202,1.114958l405.493334,255.426701c2.275921,1.419037 3.698372,4.054392 3.698372,6.892467l0,90.007504c0,2.838074 -1.422451,5.473429 -3.698372,6.892467l-405.588164,251.575028c-1.137961,0.709519 -2.465582,1.114958 -3.698372,1.114958z"/>
<path stroke="null" id="svg_5" fill="#0276F7" d="m512.094844,356.615382l405.398504,255.426701l0,90.007504l-66.096551,40.94936l-339.301952,210.625668l-339.491613,-210.625668l-66.096551,-40.94936l0,-90.007504l405.588164,-255.426701m0,-16.014849c-2.655242,0 -5.215653,0.709519 -7.586405,2.229916l-405.588164,255.426701c-4.551843,2.838074 -7.396745,8.108784 -7.396745,13.784933l0,90.007504c0,5.676149 2.844902,10.946859 7.491575,13.886293l66.096551,41.05072l339.491613,210.625668c2.275921,1.419037 4.931163,2.128556 7.491575,2.128556s5.215653,-0.709519 7.491575,-2.128556l339.301952,-210.625668l66.096551,-40.94936c4.646673,-2.838074 7.491575,-8.108784 7.491575,-13.886293l0,-90.007504c0,-5.676149 -2.844902,-10.946859 -7.396745,-13.784933l-405.398504,-255.426701c-2.370751,-1.520397 -5.025993,-2.331275 -7.586405,-2.331275z"/>
<path stroke="null" id="svg_6" fill="#FFFFFF" d="m106.50668,612.042083l405.493334,253.298145l405.493334,-253.298145l-405.398504,-255.426701l-405.588164,255.426701z"/>
<path stroke="null" id="svg_7" fill="#D4E4FC" d="m501.473877,64.192353l-254.9032,498.487506l263.343075,161.162085l266.662127,-162.074323l-275.102002,-497.575268z"/>
<path stroke="null" id="svg_8" fill="#0276F7" d="m229.975417,602.311542c-1.232791,0 -2.465582,-0.304079 -3.698372,-1.013598c-3.603542,-2.128556 -4.931163,-6.993826 -2.844902,-10.845499l279.653845,-532.13896c1.327621,-2.533995 3.793202,-4.054392 6.543274,-4.054392c2.655242,0 5.120823,1.520397 6.543274,4.054392l284.395348,532.13896c2.086261,3.851672 0.75864,8.716943 -2.750072,10.946859c-3.603542,2.128556 -8.155385,0.810878 -10.241646,-2.939434l-277.852074,-519.874424l-273.205401,519.671704c-1.422451,2.635355 -3.982862,4.054392 -6.543274,4.054392z"/>
<path stroke="null" id="svg_9" fill="#0276F7" d="m509.913752,755.567562c-4.172523,0 -7.491575,-3.547593 -7.491575,-8.007424l0,-666.744777c0,-4.459831 3.319052,-8.007424 7.491575,-8.007424s7.491575,3.547593 7.491575,8.007424l0,666.846137c0,4.358471 -3.413882,7.906065 -7.491575,7.906065z"/>
<path stroke="null" id="svg_10" fill="#0276F7" d="m509.913752,731.849369c-1.327621,0 -2.560412,-0.405439 -3.698372,-1.013598l-263.343075,-161.162085c-3.603542,-2.229916 -4.836333,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.939434l259.549873,158.83081l262.963755,-159.844408c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-266.662127,162.074323c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598z"/>
<path stroke="null" id="svg_11" fill="#0276F7" d="m509.913752,579.708306c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-201.988026,-125.686154c-3.603542,-2.229916 -4.741503,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.838074l198.289654,123.354879l201.798366,-122.138561c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.845499l-205.496739,124.469837c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598zm-2.465582,-157.513132c-1.232791,0 -2.370751,-0.304079 -3.508712,-0.912238l-140.917468,-79.668804c-3.698372,-2.128556 -5.025993,-6.892467 -3.129392,-10.845499c1.896601,-3.953032 6.448444,-5.37207 10.146816,-3.344873l137.503586,77.742968l143.00373,-79.871524c3.698372,-2.027196 8.155385,-0.506799 10.146816,3.344873c1.896601,3.953032 0.47415,8.716943 -3.129392,10.845499l-146.512442,81.79736c-1.232791,0.608159 -2.370751,0.912238 -3.603542,0.912238zm2.465582,-148.49211c-1.232791,0 -2.465582,-0.304079 -3.508712,-0.912238l-82.312492,-47.436387c-3.603542,-2.128556 -5.025993,-6.993826 -3.034562,-10.845499c1.991431,-3.953032 6.543274,-5.27071 10.146816,-3.243514l78.708949,45.409191l78.329629,-47.537747c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-81.933171,49.666303c-1.232791,0.608159 -2.560412,0.912238 -3.793202,0.912238z"/>
<path stroke="null" id="svg_12" fill="#0276F7" d="m509.913752,579.708306l-0.28449,0l-263.248245,-9.021022c-4.172523,-0.10136 -7.396745,-3.851672 -7.207085,-8.210144c0.09483,-4.459831 4.077693,-7.703345 7.681235,-7.703345l263.343075,9.021022c4.172523,0.10136 7.396745,3.851672 7.207085,8.210144c-0.18966,4.257112 -3.508712,7.703345 -7.491575,7.703345zm0,152.141063c-1.612111,0 -3.224222,-0.608159 -4.646673,-1.723117c-3.224222,-2.736715 -3.793202,-7.804705 -1.232791,-11.250938l205.496739,-276.610899c2.560412,-3.446233 7.301915,-4.054392 10.526137,-1.317677c3.224222,2.736715 3.793202,7.804705 1.232791,11.250938l-205.496739,276.610899c-1.517281,2.027196 -3.698372,3.040794 -5.879464,3.040794z"/>
<path stroke="null" id="svg_13" fill="#0276F7" d="m509.913752,579.708306c-1.422451,0 -2.750072,-0.405439 -4.077693,-1.216318c-3.508712,-2.432635 -4.457013,-7.297906 -2.275921,-11.048218l144.14169,-239.310492c2.275921,-3.750313 6.922594,-4.763911 10.336476,-2.432635c3.508712,2.432635 4.457013,7.297906 2.275921,11.048218l-144.14169,239.310492c-1.422451,2.331275 -3.793202,3.648953 -6.258784,3.648953zm-2.465582,-157.513132c-1.043131,0 -2.086261,-0.20272 -3.129392,-0.709519c-3.793202,-1.824476 -5.405313,-6.588387 -3.698372,-10.642779l84.398753,-198.158413c1.706941,-4.054392 6.069124,-5.777509 9.957156,-3.953032c3.793202,1.824476 5.405313,6.588387 3.698372,10.642779l-84.303923,198.158413c-1.327621,2.939434 -4.077693,4.662551 -6.922594,4.662551z"/>
<path stroke="null" id="svg_14" fill="#0276F7" d="m591.846924,375.062866c-2.750072,0 -5.405313,-1.621757 -6.732934,-4.459831c-1.801771,-3.953032 -0.28449,-8.716943 3.413882,-10.642779l129.253371,-67.302908l-365.759539,-178.089172l20.862613,208.091673l133.994874,-64.262114c3.698372,-1.824476 8.155385,0 9.862326,4.054392c1.706941,4.054392 0,8.716943 -3.793202,10.541419l-143.38305,68.823305c-2.181091,1.013598 -4.646673,0.912238 -6.827764,-0.405439c-2.086261,-1.317677 -3.413882,-3.547593 -3.698372,-6.081588l-23.328195,-233.026185c-0.28449,-2.838074 0.853471,-5.676149 3.034562,-7.297906c2.181091,-1.621757 5.025993,-2.027196 7.491575,-0.810878l392.217126,190.961867c2.655242,1.317677 4.362183,4.054392 4.362183,7.196546c0,3.142154 -1.612111,5.980228 -4.172523,7.297906l-143.57271,74.600814c-1.043131,0.608159 -2.181091,0.810878 -3.224222,0.810878zm-283.921198,78.959286c-3.603542,0 -6.827764,-2.838074 -7.396745,-6.791107c-0.56898,-4.358471 2.181091,-8.412864 6.258784,-9.122382l199.617275,-31.826978c4.077693,-0.608159 7.870895,2.331275 8.534705,6.689747c0.56898,4.358471 -2.181091,8.412864 -6.258784,9.122382l-199.617275,31.826978c-0.47415,0.10136 -0.853471,0.10136 -1.137961,0.10136z"/>
</g>
<text stroke="null" font-style="italic" transform="matrix(6.577099502228161,0,0,7.449448263868419,-1073.2057632249744,-908.8606073938396) " xml:space="preserve" text-anchor="start" font-family="Arvo, sans-serif" font-size="24" id="svg_16" y="177.898525" x="178.621382" stroke-width="0" fill="#B5D6FB">4G</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.9 KiB

View File

@@ -1,28 +0,0 @@
<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg">
<!-- Created with Method Draw - http://github.com/duopixel/Method-Draw/ -->
<g>
<title>background</title>
<rect fill="none" id="canvas_background" height="1026" width="1026" y="-1" x="-1"/>
<g display="none" overflow="visible" y="0" x="0" height="100%" width="100%" id="canvasGrid">
<rect fill="url(#gridpattern)" stroke-width="0" y="1" x="1" height="768" width="1024"/>
</g>
</g>
<g>
<title>Layer 1</title>
<g stroke="null" id="svg_15">
<path stroke="null" id="svg_4" fill="#B5D6FB" d="m512.094844,961.632039c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-405.588164,-251.575028c-2.275921,-1.419037 -3.698372,-4.054392 -3.698372,-6.892467l0,-90.007504c0,-2.838074 1.422451,-5.473429 3.698372,-6.892467l405.588164,-255.426701c1.137961,-0.709519 2.465582,-1.114958 3.793202,-1.114958s2.655242,0.405439 3.793202,1.114958l405.493334,255.426701c2.275921,1.419037 3.698372,4.054392 3.698372,6.892467l0,90.007504c0,2.838074 -1.422451,5.473429 -3.698372,6.892467l-405.588164,251.575028c-1.137961,0.709519 -2.465582,1.114958 -3.698372,1.114958z"/>
<path stroke="null" id="svg_5" fill="#0276F7" d="m512.094844,356.615382l405.398504,255.426701l0,90.007504l-66.096551,40.94936l-339.301952,210.625668l-339.491613,-210.625668l-66.096551,-40.94936l0,-90.007504l405.588164,-255.426701m0,-16.014849c-2.655242,0 -5.215653,0.709519 -7.586405,2.229916l-405.588164,255.426701c-4.551843,2.838074 -7.396745,8.108784 -7.396745,13.784933l0,90.007504c0,5.676149 2.844902,10.946859 7.491575,13.886293l66.096551,41.05072l339.491613,210.625668c2.275921,1.419037 4.931163,2.128556 7.491575,2.128556s5.215653,-0.709519 7.491575,-2.128556l339.301952,-210.625668l66.096551,-40.94936c4.646673,-2.838074 7.491575,-8.108784 7.491575,-13.886293l0,-90.007504c0,-5.676149 -2.844902,-10.946859 -7.396745,-13.784933l-405.398504,-255.426701c-2.370751,-1.520397 -5.025993,-2.331275 -7.586405,-2.331275z"/>
<path stroke="null" id="svg_6" fill="#FFFFFF" d="m106.50668,612.042083l405.493334,253.298145l405.493334,-253.298145l-405.398504,-255.426701l-405.588164,255.426701z"/>
<path stroke="null" id="svg_7" fill="#D4E4FC" d="m501.473877,64.192353l-254.9032,498.487506l263.343075,161.162085l266.662127,-162.074323l-275.102002,-497.575268z"/>
<path stroke="null" id="svg_8" fill="#0276F7" d="m229.975417,602.311542c-1.232791,0 -2.465582,-0.304079 -3.698372,-1.013598c-3.603542,-2.128556 -4.931163,-6.993826 -2.844902,-10.845499l279.653845,-532.13896c1.327621,-2.533995 3.793202,-4.054392 6.543274,-4.054392c2.655242,0 5.120823,1.520397 6.543274,4.054392l284.395348,532.13896c2.086261,3.851672 0.75864,8.716943 -2.750072,10.946859c-3.603542,2.128556 -8.155385,0.810878 -10.241646,-2.939434l-277.852074,-519.874424l-273.205401,519.671704c-1.422451,2.635355 -3.982862,4.054392 -6.543274,4.054392z"/>
<path stroke="null" id="svg_9" fill="#0276F7" d="m509.913752,755.567562c-4.172523,0 -7.491575,-3.547593 -7.491575,-8.007424l0,-666.744777c0,-4.459831 3.319052,-8.007424 7.491575,-8.007424s7.491575,3.547593 7.491575,8.007424l0,666.846137c0,4.358471 -3.413882,7.906065 -7.491575,7.906065z"/>
<path stroke="null" id="svg_10" fill="#0276F7" d="m509.913752,731.849369c-1.327621,0 -2.560412,-0.405439 -3.698372,-1.013598l-263.343075,-161.162085c-3.603542,-2.229916 -4.836333,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.939434l259.549873,158.83081l262.963755,-159.844408c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-266.662127,162.074323c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598z"/>
<path stroke="null" id="svg_11" fill="#0276F7" d="m509.913752,579.708306c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-201.988026,-125.686154c-3.603542,-2.229916 -4.741503,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.838074l198.289654,123.354879l201.798366,-122.138561c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.845499l-205.496739,124.469837c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598zm-2.465582,-157.513132c-1.232791,0 -2.370751,-0.304079 -3.508712,-0.912238l-140.917468,-79.668804c-3.698372,-2.128556 -5.025993,-6.892467 -3.129392,-10.845499c1.896601,-3.953032 6.448444,-5.37207 10.146816,-3.344873l137.503586,77.742968l143.00373,-79.871524c3.698372,-2.027196 8.155385,-0.506799 10.146816,3.344873c1.896601,3.953032 0.47415,8.716943 -3.129392,10.845499l-146.512442,81.79736c-1.232791,0.608159 -2.370751,0.912238 -3.603542,0.912238zm2.465582,-148.49211c-1.232791,0 -2.465582,-0.304079 -3.508712,-0.912238l-82.312492,-47.436387c-3.603542,-2.128556 -5.025993,-6.993826 -3.034562,-10.845499c1.991431,-3.953032 6.543274,-5.27071 10.146816,-3.243514l78.708949,45.409191l78.329629,-47.537747c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-81.933171,49.666303c-1.232791,0.608159 -2.560412,0.912238 -3.793202,0.912238z"/>
<path stroke="null" id="svg_12" fill="#0276F7" d="m509.913752,579.708306l-0.28449,0l-263.248245,-9.021022c-4.172523,-0.10136 -7.396745,-3.851672 -7.207085,-8.210144c0.09483,-4.459831 4.077693,-7.703345 7.681235,-7.703345l263.343075,9.021022c4.172523,0.10136 7.396745,3.851672 7.207085,8.210144c-0.18966,4.257112 -3.508712,7.703345 -7.491575,7.703345zm0,152.141063c-1.612111,0 -3.224222,-0.608159 -4.646673,-1.723117c-3.224222,-2.736715 -3.793202,-7.804705 -1.232791,-11.250938l205.496739,-276.610899c2.560412,-3.446233 7.301915,-4.054392 10.526137,-1.317677c3.224222,2.736715 3.793202,7.804705 1.232791,11.250938l-205.496739,276.610899c-1.517281,2.027196 -3.698372,3.040794 -5.879464,3.040794z"/>
<path stroke="null" id="svg_13" fill="#0276F7" d="m509.913752,579.708306c-1.422451,0 -2.750072,-0.405439 -4.077693,-1.216318c-3.508712,-2.432635 -4.457013,-7.297906 -2.275921,-11.048218l144.14169,-239.310492c2.275921,-3.750313 6.922594,-4.763911 10.336476,-2.432635c3.508712,2.432635 4.457013,7.297906 2.275921,11.048218l-144.14169,239.310492c-1.422451,2.331275 -3.793202,3.648953 -6.258784,3.648953zm-2.465582,-157.513132c-1.043131,0 -2.086261,-0.20272 -3.129392,-0.709519c-3.793202,-1.824476 -5.405313,-6.588387 -3.698372,-10.642779l84.398753,-198.158413c1.706941,-4.054392 6.069124,-5.777509 9.957156,-3.953032c3.793202,1.824476 5.405313,6.588387 3.698372,10.642779l-84.303923,198.158413c-1.327621,2.939434 -4.077693,4.662551 -6.922594,4.662551z"/>
<path stroke="null" id="svg_14" fill="#0276F7" d="m591.846924,375.062866c-2.750072,0 -5.405313,-1.621757 -6.732934,-4.459831c-1.801771,-3.953032 -0.28449,-8.716943 3.413882,-10.642779l129.253371,-67.302908l-365.759539,-178.089172l20.862613,208.091673l133.994874,-64.262114c3.698372,-1.824476 8.155385,0 9.862326,4.054392c1.706941,4.054392 0,8.716943 -3.793202,10.541419l-143.38305,68.823305c-2.181091,1.013598 -4.646673,0.912238 -6.827764,-0.405439c-2.086261,-1.317677 -3.413882,-3.547593 -3.698372,-6.081588l-23.328195,-233.026185c-0.28449,-2.838074 0.853471,-5.676149 3.034562,-7.297906c2.181091,-1.621757 5.025993,-2.027196 7.491575,-0.810878l392.217126,190.961867c2.655242,1.317677 4.362183,4.054392 4.362183,7.196546c0,3.142154 -1.612111,5.980228 -4.172523,7.297906l-143.57271,74.600814c-1.043131,0.608159 -2.181091,0.810878 -3.224222,0.810878zm-283.921198,78.959286c-3.603542,0 -6.827764,-2.838074 -7.396745,-6.791107c-0.56898,-4.358471 2.181091,-8.412864 6.258784,-9.122382l199.617275,-31.826978c4.077693,-0.608159 7.870895,2.331275 8.534705,6.689747c0.56898,4.358471 -2.181091,8.412864 -6.258784,9.122382l-199.617275,31.826978c-0.47415,0.10136 -0.853471,0.10136 -1.137961,0.10136z"/>
</g>
<text stroke="null" font-style="italic" transform="matrix(6.577099502228161,0,0,7.449448263868419,-1073.2057632249744,-908.8606073938396) " xml:space="preserve" text-anchor="start" font-family="Arvo, sans-serif" font-size="24" id="svg_16" y="177.898525" x="178.621382" stroke-width="0" fill="#D4E4FC">5G</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.9 KiB

View File

@@ -5,11 +5,13 @@ import zhCN from 'ant-design-vue/es/locale/zh_CN';
import enUS from 'ant-design-vue/es/locale/en_US';
import { usePrefersColorScheme, viewTransitionTheme } from 'antdv-pro-layout';
import dayjs from 'dayjs';
import advancedFormat from 'dayjs/plugin/advancedFormat';
import useLayoutStore from '@/store/modules/layout';
import useAppStore from '@/store/modules/app';
import useI18n from '@/hooks/useI18n';
const { t, currentLocale } = useI18n();
const { themeConfig, initPrimaryColor, changeConf } = useLayoutStore();
dayjs.extend(advancedFormat);
// dayjs.locale('zh-cn'); // 默认中文
let locale = ref(enUS); // 国际化初始中文

View File

@@ -1,156 +0,0 @@
import { request } from '@/plugins/http-fetch';
import { sessionGet } from '@/utils/cache-session-utils';
/**
* 登录方法
* @param data 数据
* @returns 结果
*/
export function login(data: Record<string, string>) {
return request({
url: '/auth/login',
method: 'POST',
data: data,
whithToken: false,
});
}
/**
* 退出方法
* @returns object
*/
export function logout() {
return request({
url: '/auth/logout',
method: 'POST',
repeatSubmit: false,
});
}
/**
* 注册方法
* @param data 注册对象
* @returns object
*/
export function register(data: Record<string, any>) {
return request({
url: '/auth/register',
method: 'POST',
data: data,
whithToken: false,
});
}
/**
* 刷新登录令牌
* @param data 数据
* @returns 结果
*/
export function refreshToken(refreshToken: string) {
return request({
url: '/auth/refresh-token',
method: 'POST',
data: { refreshToken },
whithToken: false,
});
}
/**
* 获取用户详细信息
* @returns object
*/
export function getInfo() {
return request({
url: '/me',
method: 'GET',
});
}
/**
* 获取路由
* @returns object
*/
export const getRouter = () => {
return request({
url: '/router',
method: 'GET',
});
};
/**
* 获取验证码
* @returns object
*/
export function getCaptchaImage() {
return request({
url: '/captcha-image',
method: 'GET',
whithToken: false,
});
}
/**
* 登录认证源
* @returns object
*/
export function getLoginSource() {
return request({
url: '/auth/login/source',
method: 'GET',
whithToken: false,
});
}
/**
* LDAP登录
* @returns object
*/
export function loginLDAP(data: Record<string, string>) {
return request({
url: '/auth/login/ldap',
method: 'POST',
data: data,
whithToken: false,
});
}
/**
* SMTP登录
* @returns object
*/
export function loginSMTP(data: Record<string, string>) {
return request({
url: '/auth/login/smtp',
method: 'POST',
data: data,
whithToken: false,
});
}
/**
* 登录认证源OAuth2跳转登录URL
* @returns object
*/
export function loginOAuth2URL(state: string): string {
// 兼容旧前端可改配置文件
const baseUrl = import.meta.env.PROD
? sessionGet('baseUrl') || import.meta.env.VITE_API_BASE_URL
: import.meta.env.VITE_API_BASE_URL;
return `${baseUrl}/auth/login/oauth2/authorize?state=${state}`;
}
/**
* 登录认证源OAuth2认证登录
* @returns object
*/
export function loginOAuth2Token(code: string, state: string) {
return request({
url: '/auth/login/oauth2/token',
method: 'POST',
data: {
code,
state,
},
whithToken: false,
});
}

View File

@@ -0,0 +1,95 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
/**
* 查询备份列表
* @param query 查询参数
* @returns object
*/
export async function listNeBackup(query: Record<string, any>) {
let totalSQL = 'select count(id) as total from ne_backup ';
let rowsSQL = ' select * from ne_backup ';
// 查询
let querySQL = 'where 1=1';
if (query.neType) {
querySQL += ` and ne_type like '%${query.neType}%' `;
}
// 分页
const pageNum = (query.pageNum - 1) * query.pageSize;
const limtSql = ` order by create_time desc limit ${pageNum},${query.pageSize} `;
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/ne_backup`,
method: 'get',
params: {
totalSQL: totalSQL + querySQL,
rowsSQL: rowsSQL + querySQL + limtSql,
},
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
const data: DataList = {
total: 0,
rows: [],
code: result.code,
msg: result.msg,
};
result.data.data.forEach((item: any) => {
const itemData = item['ne_backup'];
if (Array.isArray(itemData)) {
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
data.total = itemData[0]['total'];
} else {
data.rows = itemData.map(v => parseObjLineToHump(v));
}
}
});
return data;
}
return result;
}
/**
* 删除备份信息
* @param noticeId 网元ID
* @returns object
*/
export async function delNeBackup(data: Record<string, any>) {
return request({
url: `/api/rest/systemManagement/v1/${data.neType}/neBackup/${data.fileName}`,
method: 'delete',
});
}
/**
* 获取备份信息文件
* @param menuId 网元ID
* @returns object
*/
export async function downloadNeBackup(data: Record<string, any>) {
return await request({
url: `/api/rest/systemManagement/v1/${data.neType}/neBackup/${data.fileName}`,
method: 'get',
responseType: 'blob',
timeout: 180_000,
});
}
/**
* 修改备份说明
* @param menuId 网元ID
* @returns object
*/
export async function updateBackInfo(data:Record<string,any>){
return request({
url: `/api/rest/databaseManagement/v1/omc_db/ne_backup?WHERE=id=${data.id}`,
method: 'put',
data: { data: { comment: data.backupInfo } },
});
}

View File

@@ -0,0 +1,85 @@
import {
RESULT_CODE_ERROR,
RESULT_CODE_SUCCESS,
RESULT_MSG_ERROR,
} from '@/constants/result-constants';
import { language, request } from '@/plugins/http-fetch';
/**
* 更新网元配置重新载入
* @param neType 网元类型
* @param neId 网元ID
* @returns
*/
export async function updateNeConfigReload(neType: string, neId: string) {
// 发起请求
const result = await request({
url: `/api/rest/operationManagement/v1/elementType/${neType}/objectType/mml?ne_id=${neId}`,
method: 'post',
data: { mml: ['reload'] },
timeout: 180_000,
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
const v = result.data.data[0];
const str = v.toLowerCase();
if (str.indexOf('ok') !== -1) {
delete result.data;
} else if (str.indexOf('success') !== -1) {
delete result.data;
} else {
return { code: RESULT_CODE_ERROR, msg: RESULT_MSG_ERROR[language] };
}
}
return result;
}
/**
* 从参数配置PCF中获取对应信息提供给PCC用户策略输入框
* @param neId
* @returns object {pccRules,sessionRules,qosTemplate,headerEnrichTemplate,serviceAreaRestriction}
*/
export async function getPCCRule(neId: any) {
const paramNameArr = [
'pccRules',
'sessionRules',
'qosTemplate',
'headerEnrichTemplate',
'serviceAreaRestriction',
];
const reqArr = [];
for (const paramName of paramNameArr) {
reqArr.push(
request({
url: `/ne/config/data`,
params: { neType: 'PCF', neId, paramName },
method: 'get',
})
);
}
return await Promise.allSettled(reqArr).then(resArr => {
// 规则数据
const obj: any = {};
resArr.forEach((item, i: number) => {
if (item.status === 'fulfilled') {
const res = item.value;
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
const key = paramNameArr[i];
obj[key] = res.data.map((item: any) => {
if ('qosTemplate' === key) {
return { value: item.qosId, label: item.qosId };
}
if ('headerEnrichTemplate' === key) {
return { value: item.templateName, label: item.templateName };
}
if ('serviceAreaRestriction' === key) {
return { value: item.name, label: item.name };
}
return { value: item.ruleId, label: item.ruleId };
});
}
}
});
return obj;
});
}

View File

@@ -0,0 +1,242 @@
import {
RESULT_CODE_ERROR,
RESULT_CODE_SUCCESS,
RESULT_MSG_ERROR,
} from '@/constants/result-constants';
import { language, request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
import { NE_TYPE_LIST } from '@/constants/ne-constants';
/**
* 查询网元列表
* @param query 查询参数
* @returns object
*/
export async function listNeInfo(query: Record<string, any>) {
let totalSQL = 'select count(*) as total from ne_info where 1=1 ';
let rowsSQL = 'select * from ne_info where 1=1 ';
// 查询
let querySQL = '';
if (query.neType) {
querySQL += ` and ne_type = '${query.neType}' `;
}
// 分页
const pageNum = (query.pageNum - 1) * query.pageSize;
const limtSql = ` limit ${pageNum},${query.pageSize} `;
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/ne_info`,
method: 'get',
params: {
totalSQL: totalSQL + querySQL,
rowsSQL: rowsSQL + querySQL + limtSql,
},
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
const data: DataList = {
total: 0,
rows: [],
code: result.code,
msg: result.msg,
};
result.data.data.forEach((item: any) => {
const itemData = item['ne_info'];
if (Array.isArray(itemData)) {
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
data.total = itemData[0]['total'];
} else {
data.rows = itemData.map(v => parseObjLineToHump(v));
//通过sort进行冒泡排序
data.rows.sort((a: any, b: any) => {
const typeA = NE_TYPE_LIST.indexOf(a.neType);
const typeB = NE_TYPE_LIST.indexOf(b.neType);
if (typeA === -1) return 1; // 如果不在特定顺序中,排到后面
if (typeB === -1) return -1; // 如果不在特定顺序中,排到后面
return typeA - typeB;
});
}
}
});
return data;
}
return result;
}
/**
* 查询网元详细
* @param menuId 网元ID
* @returns object
*/
export async function getNeInfo(id: string | number) {
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/ne_info`,
method: 'get',
params: {
SQL: `select * from ne_info where id = ${id}`,
},
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
let neInfo = result.data.data[0]['ne_info'];
if (neInfo) {
return Object.assign(result, {
data: parseObjLineToHump(neInfo[0]),
});
}
return { code: RESULT_CODE_ERROR, msg: RESULT_MSG_ERROR[language] };
}
return result;
}
/**
* 新增网元
* @param data 网元对象
* @returns object
*/
export function addNeInfo(data: Record<string, any>) {
data.port = `${data.port}`;
return request({
url: `/api/rest/systemManagement/v1/elementType/${data.neType.toLowerCase()}/objectType/neInfo?sync2ne=${
data.sync
}`,
method: 'post',
data: data,
});
}
/**
* 修改网元
* @param data 网元对象
* @returns object
*/
export function updateNeInfo(data: Record<string, any>) {
data.port = `${data.port}`;
return request({
url: `/api/rest/systemManagement/v1/elementType/${data.neType}/objectType/neInfo?sync2ne=${data.sync}`,
method: 'put',
data: data,
});
}
/**
* 删除网元
* @param noticeId 网元ID
* @returns object
*/
export async function delNeInfo(data: Record<string, any>) {
return request({
url: `/api/rest/systemManagement/v1/elementType/${data.neType}/objectType/neInfo?ne_id=${data.neId}`,
method: 'delete',
timeout: 60 * 1000,
});
}
/**
* 导出网元配置文件
* @param data data {neType neId}
* @returns bolb
*/
export function exportSet(data: Record<string, any>) {
return request({
url: `/api/rest/systemManagement/v1/elementType/${data.neType}/objectType/cm?ne_id=${data.neId}`,
method: 'get',
responseType: 'blob',
headers: {
'Content-Type': 'application/octet-stream',
},
timeout: 180_000,
});
}
/**
* 导入网元配置文件
* @param data 网元对象
* @returns object
*/
export function importFile(data: Record<string, any>) {
let dataType: 'json' | 'form-data' = 'json';
let url = `/api/rest/systemManagement/v1/elementType/${data.neType}/objectType/cm?ne_id=${data.neId}`;
let obj: any = { fileName: data.fileName };
if (data.importType === 'local') {
let formData = new FormData();
formData.append('nfType', data.neType);
formData.append('nfId', data.neId);
formData.append('file', data.file);
obj = formData;
dataType = 'form-data';
}
// 处理FormData类型的data
return request({
url,
method: 'post',
data: obj,
dataType,
timeout: 180_000,
});
}
/**
* 查询远程服务器上网元配置文件
* @param data 网元对象
* @returns object
*/
export async function listServerFile(data: Record<string, any>) {
const result = await request({
url: `/api/rest/databaseManagement/v1/omc_db/ne_backup?SQL= select * from ne_backup where ne_type ='${data.neType}'`,
method: 'get',
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
let data = result.data.data[0];
return Object.assign(result, {
data: parseObjLineToHump(data['ne_backup']),
});
}
return result;
}
/**
* 启动网元
* @data {neType,neId}
* @returns bolb
*/
export function startNf(data: Record<string, any>) {
return request({
url: `/api/rest/systemManagement/v1/elementType/${data.neType}/objectType/service/start?neId=${data.neId}`,
method: 'post',
timeout: 180_000,
});
}
/**
* 重启网元
* @data {neType,neId}
* @returns bolb
*/
export function restartNf(data: Record<string, any>) {
return request({
url: `/api/rest/systemManagement/v1/elementType/${data.neType}/objectType/service/restart?neId=${data.neId}`,
method: 'post',
timeout: 180_000,
});
}
/**
* 停止网元
* @data {neType,neId}
* @returns bolb
*/
export function stopNf(data: Record<string, any>) {
return request({
url: `/api/rest/systemManagement/v1/elementType/${data.neType}/objectType/service/stop?neId=${data.neId}`,
method: 'post',
timeout: 180_000,
});
}

View File

@@ -0,0 +1,220 @@
import {
RESULT_CODE_ERROR,
RESULT_CODE_SUCCESS,
RESULT_MSG_ERROR,
} from '@/constants/result-constants';
import { language, request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
/**
* 查询软件列表
* @param query 查询参数
* @returns object
*/
export async function listNeSoftware(query: Record<string, any>) {
let totalSQL = 'select count(id) as total from ne_software ';
let rowsSQL = ' select * from ne_software ';
// 查询
let querySQL = 'where 1=1';
if (query.neType) {
querySQL += ` and ne_type like '%${query.neType}%' `;
}
// 分页
const pageNum = (query.pageNum - 1) * query.pageSize;
const limtSql = ` order by update_time desc limit ${pageNum},${query.pageSize} `;
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/ne_software`,
method: 'get',
params: {
totalSQL: totalSQL + querySQL,
rowsSQL: rowsSQL + querySQL + limtSql,
},
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
const data: DataList = {
total: 0,
rows: [],
code: result.code,
msg: result.msg,
};
result.data.data.forEach((item: any) => {
const itemData = item['ne_software'];
if (Array.isArray(itemData)) {
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
data.total = itemData[0]['total'];
} else {
data.rows = itemData.map(v => parseObjLineToHump(v));
}
}
});
return data;
}
return result;
}
/**
* 删除软件信息
* @param noticeId 网元ID
* @returns object
*/
export async function delNeSoftware(data: Record<string, any>) {
return request({
url: `/api/rest/systemManagement/v1/${data.neType}/software/${data.version}`,
method: 'delete',
});
}
/**
* 获取软件信息文件
* @param menuId 网元ID
* @returns object
*/
export async function downloadNeSoftware(data: Record<string, any>) {
return await request({
url: `/api/rest/systemManagement/v1/${data.neType}/software/${data.version}`,
method: 'get',
responseType: 'blob',
timeout: 180_000,
});
}
/**
* 上传文件
* @param data 表单数据对象
* @returns object
*/
export function uploadNeSoftware(data: FormData) {
return request({
url: `/api/rest/systemManagement/v1/${data.get('nf')}/software/${data.get(
'version'
)}`,
method: 'post',
data,
dataType: 'form-data',
timeout: 180_000,
});
}
/**
* 下发文件
* @param data 数据对象
* @returns object
*/
export async function sendNeSoftware(data: Record<string, any>) {
const result = await request({
url: `/api/rest/systemManagement/v1/${data.neType}/software/${data.version}/${data.neId}`,
method: 'post',
timeout: 180_000,
repeatSubmit: false,
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
delete result.data;
return result;
}
return { code: RESULT_CODE_ERROR, msg: RESULT_MSG_ERROR[language] };
}
/**
* 激活文件
* @param data 数据对象
* @returns object
*/
export async function runNeSoftware(data: Record<string, any>) {
const result = await request({
url: `/api/rest/systemManagement/v1/${data.neType}/software/${data.version}/${data.neId}`,
method: 'put',
timeout: 180_000,
repeatSubmit: false,
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
delete result.data;
return result;
}
return { code: RESULT_CODE_ERROR, msg: RESULT_MSG_ERROR[language] };
}
/**
* 回退文件
* @param data 数据对象
* @returns object
*/
export async function backNeSoftware(data: Record<string, any>) {
const result = await request({
url: `/api/rest/systemManagement/v1/${data.neType}/software/${data.version}/${data.neId}`,
method: 'PATCH',
timeout: 180_000,
repeatSubmit: false,
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
delete result.data;
return result;
}
return { code: RESULT_CODE_ERROR, msg: RESULT_MSG_ERROR[language] };
}
/**
* 查询版本列表
* @param query 查询参数
* @returns object
*/
export async function listNeVersion(query: Record<string, any>) {
let totalSQL = 'select count(id) as total from ne_version ';
let rowsSQL = 'select * from ne_version ';
// 查询
let querySQL = 'where 1=1';
if (query.neType) {
querySQL += ` and ne_type like '%${query.neType}%' `;
}
if (query.status) {
querySQL += ` and status = '${query.status}' `;
}
if (query.beginTime && query.endTime) {
querySQL += ` and update_time BETWEEN '${query.beginTime}' AND '${query.endTime}' `;
}
// 分页
const pageNum = (query.pageNum - 1) * query.pageSize;
const limtSql = ` order by update_time desc limit ${pageNum},${query.pageSize} `;
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/ne_version`,
method: 'get',
params: {
totalSQL: totalSQL + querySQL,
rowsSQL: rowsSQL + querySQL + limtSql,
},
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
const data: DataList = {
total: 0,
rows: [],
code: result.code,
msg: result.msg,
};
result.data.data.forEach((item: any) => {
const itemData = item['ne_version'];
if (Array.isArray(itemData)) {
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
data.total = itemData[0]['total'];
} else {
data.rows = itemData.map(v => parseObjLineToHump(v));
}
}
});
return data;
}
return result;
}

View File

@@ -1,30 +1,36 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
import { parseDateToStr } from '@/utils/date-utils';
import useUserStore from '@/store/modules/user';
/**
* 获取活动告警数
* @returns object
*/
export async function getActiveAlarmTotal() {
let totalSQL = `select count(*) as total from alarm where alarm_status='1'`;
// 发起请求
const result = await request({
url: `/ne/data/alarm/list`,
method: 'GET',
url: `/api/rest/databaseManagement/v1/select/omc_db/alarm`,
method: 'get',
params: {
alarmStatus: 'Active',
sortField: 'createdTime',
sortOrder: 'desc',
pageNum: 1,
pageSize: 1,
SQL: totalSQL,
},
});
return {
code: result.code,
msg: result.msg,
data: result.data.total ?? 0,
};
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
const itemData = result.data.data;
if (Array.isArray(itemData)) {
const v = itemData[0]['alarm'];
if (Array.isArray(v)) {
result.data = v[0]['total'];
}
}
}
return result;
}
/**
@@ -32,25 +38,125 @@ export async function getActiveAlarmTotal() {
* @param query 查询参数
* @returns object
*/
export async function listAct(query: Record<string, any>) {
return await request({
url: `/ne/data/alarm/list`,
method: 'GET',
params: query,
export async function listAct(query: Record<string, any>, filterSQl: string) {
let totalSQL = `select count(*) as total from alarm where alarm_status='1' ${filterSQl} `;
let rowsSQL = `select * from alarm where alarm_status='1' ${filterSQl}`;
// 查询
let querySQL = '';
if (query.alarmCode) {
querySQL += ` and alarm_code = '${query.alarmCode}' `;
}
if (query.alarmType) {
querySQL += ` and alarm_type = '${query.alarmType}' `;
}
if (query.pvFlag) {
querySQL += ` and pv_flag = '${query.pvFlag}' `;
}
if (query.origSeverity) {
querySQL += ` and orig_severity in('${query.origSeverity}' )`;
}
if (query.neId) {
querySQL += ` and ne_id like '%${query.neId}%' `;
}
if (query.neName) {
querySQL += ` and ne_name like '%${query.neName}%' `;
}
if (query.neType) {
querySQL += ` and ne_type like '%${query.neType}%' `;
}
if (query.beginTime && query.endTime) {
querySQL += ` and event_time BETWEEN '${query.beginTime}' and ' ${query.endTime}'`;
}
// 分页
const pageNum = (query.pageNum - 1) * query.pageSize;
const limtSql = ` order by event_time desc limit ${pageNum},${query.pageSize} `;
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/alarm`,
method: 'get',
params: {
SQL: totalSQL + querySQL,
rowsSQL: rowsSQL + querySQL + limtSql,
},
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
const data: DataList = {
total: 0,
rows: [],
code: result.code,
msg: result.msg,
};
result.data.data.forEach((item: any) => {
const itemData = item['alarm'];
if (Array.isArray(itemData)) {
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
data.total = itemData[0]['total'];
} else {
data.rows = itemData.map(v => parseObjLineToHump(v));
}
}
});
return data;
}
return result;
}
/**
* 确认告警信息
* @param id 记录ID
* @param state 状态 true确认 false取消确认
* @param data 鉴权对象
* @returns object
*/
export function ackAlarm(data: Record<string, any>) {
export function updateConfirm(data: Record<string, any>) {
var time = new Date();
const userName = useUserStore().userName;
let finalData = {
alarm: {
ack_time: parseDateToStr(time),
ack_user: userName,
ack_state: '1',
},
};
return request({
url: `/ne/data/alarm/ack'`,
method: 'PUT',
data,
url: `/api/rest/databaseManagement/v1/update/omc_db/alarm?WHERE=id='${data.id}'`,
method: 'put',
data: finalData,
});
}
/**
* 取消确认告警
* @param data 鉴权对象
* @returns object
*/
export function cancelConfirm(data: (string | number)[]) {
var time = new Date();
const userName = useUserStore().userName;
let finalData = {
alarm: {
ack_time: parseDateToStr(time),
ack_user: '',
ack_state: '0',
},
};
return request({
url: `/api/rest/databaseManagement/v1/update/omc_db/alarm?WHERE=id in(${data.join(
','
)})`,
method: 'put',
data: finalData,
});
}
@@ -81,7 +187,7 @@ export function showPass(data: Record<string, any>) {
return request({
url: `/api/rest/databaseManagement/v1/omc_db/config?WHERE=config_tag='displayFilter'`,
method: 'PUT',
method: 'put',
data: toBackJson,
});
}
@@ -93,7 +199,7 @@ export function showPass(data: Record<string, any>) {
export function getPass() {
return request({
url: `/api/rest/databaseManagement/v1/select/omc_db/config`,
method: 'GET',
method: 'get',
params: {
SQL: "SELECT value_json,value FROM config WHERE config_tag ='displayFilter'",
},
@@ -102,29 +208,27 @@ export function getPass() {
/**
* 清除告警信息
* @param id 记录ID
* @param data 鉴权对象
* @returns object
*/
export function clearAlarm(id: number | string) {
return request({
url: `/ne/data/alarm/clear`,
method: 'PUT',
data: { id },
});
}
export function clearAlarm(data: Record<string, any>) {
var time = new Date();
const userName = useUserStore().userName;
let finalData = {
data: {
clear_time: parseDateToStr(time),
clear_type: '2',
alarm_status: '0',
clear_user: userName,
},
};
/**
* 告警信息导出
* @param params 查询列表条件
* @returns object
*/
export function exportAlarm(params: Record<string, any>) {
return request({
url: '/ne/data/alarm/export',
method: 'GET',
params: params,
responseType: 'blob',
timeout: 180_000,
url: `/api/rest/databaseManagement/v1/update/omc_db/alarm?WHERE=id in(${data.join(
','
)})`,
method: 'put',
data: finalData,
});
}
@@ -136,7 +240,7 @@ export function exportAlarm(params: Record<string, any>) {
export function listSync() {
return request({
url: `/api/rest/faultManagement/v1/elementType/all/objectType/alarms`,
method: 'GET',
method: 'get',
timeout: 180_000,
});
}
@@ -171,7 +275,7 @@ export async function exportAll(query: Record<string, any>) {
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/alarm`,
method: 'GET',
method: 'get',
params: {
rowsSQL: rowsSQL + querySQL,
},
@@ -190,14 +294,34 @@ export async function exportAll(query: Record<string, any>) {
* @param query 查询参数
* @returns bolb
*/
export async function origGet(alarmStatus: 'Active' | 'Clear') {
return await request({
url: `/ne/data/alarm/count/severity`,
method: 'GET',
export async function origGet() {
let totalSQL = `select count(*) as value,orig_severity as name from alarm WHERE alarm_status='1' and orig_severity!='Event' group by orig_severity`;
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/alarm`,
method: 'get',
params: {
alarmStatus: alarmStatus,
SQL: totalSQL,
},
timeout: 30_000,
});
////
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
const itemData = result.data.data;
if (Array.isArray(itemData)) {
const v = itemData[0]['alarm'];
if (Array.isArray(v)) {
result.data = v;
}
if (v === null) {
result.data = [];
}
}
}
return result;
}
/**
@@ -205,13 +329,34 @@ export async function origGet(alarmStatus: 'Active' | 'Clear') {
* @param filterFlag 查询参数
* @returns object
*/
export async function top3Sel(alarmStatus: 'Active' | 'Clear') {
return await request({
url: `/ne/data/alarm/count/top`,
method: 'GET',
export async function top3Sel(filterFlag?: string) {
let filter = ` WHERE alarm_status='1'and orig_severity='${filterFlag}'`;
if (!filterFlag) filter = "WHERE alarm_status='1'";
let top3SQL = `select count(*) as value,ne_type as name from alarm ${filter} and orig_severity!='Event' group by ne_type ORDER BY value desc limit 0,3 `;
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/alarm`,
method: 'get',
params: {
alarmStatus: alarmStatus,
top: 3,
SQL: top3SQL,
},
timeout: 30_000,
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
const itemData = result.data.data;
if (Array.isArray(itemData)) {
const v = itemData[0]['alarm'];
if (Array.isArray(v)) {
result.data = v;
}
if (v === null) {
result.data = [];
}
}
}
return result;
}

View File

@@ -1,6 +1,8 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
import { parseDateToStr } from '@/utils/date-utils';
import useUserStore from '@/store/modules/user';
/**
* 查询列表
@@ -8,11 +10,73 @@ import { parseObjLineToHump } from '@/utils/parse-utils';
* @returns object
*/
export async function listAct(query: Record<string, any>) {
return await request({
url: `/ne/data/alarm/log/event`,
method: 'GET',
params: query,
let totalSQL = `select count(*) as total from alarm_event where 1=1 `;
let rowsSQL = `select * from alarm_event where 1=1 `;
// 查询
let querySQL = '';
if (query.alarmCode) {
querySQL += ` and alarm_code = '${query.alarmCode}' `;
}
if (query.alarmType) {
querySQL += ` and alarm_type = '${query.alarmType}' `;
}
if (query.pvFlag) {
querySQL += ` and pv_flag = '${query.pvFlag}' `;
}
if (query.neId) {
querySQL += ` and ne_id like '%${query.neId}%' `;
}
if (query.neName) {
querySQL += ` and ne_name like '%${query.neName}%' `;
}
if (query.neType) {
querySQL += ` and ne_type like '%${query.neType}%' `;
}
if (query.beginTime && query.endTime) {
querySQL += ` and event_time BETWEEN '${query.beginTime}' and ' ${query.endTime}'`;
}
// 分页
const pageNum = (query.pageNum - 1) * query.pageSize;
const limtSql = ` order by event_time desc limit ${pageNum},${query.pageSize} `;
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/alarm_event`,
method: 'get',
params: {
SQL: totalSQL + querySQL,
rowsSQL: rowsSQL + querySQL + limtSql,
},
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
const data: DataList = {
total: 0,
rows: [],
code: result.code,
msg: result.msg,
};
result.data.data.forEach((item: any) => {
const itemData = item['alarm_event'];
if (Array.isArray(itemData)) {
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
data.total = itemData[0]['total'];
} else {
data.rows = itemData.map(v => parseObjLineToHump(v));
}
}
});
return data;
}
return result;
}
/**
@@ -45,7 +109,7 @@ export async function exportAll(query: Record<string, any>) {
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/alarm_event`,
method: 'GET',
method: 'get',
params: {
rowsSQL: rowsSQL + querySQL,
},

View File

@@ -18,7 +18,7 @@ export async function getAlarmSet() {
// 历史告警保存时间
const logDurationResult = await request({
url: `/api/rest/databaseManagement/v1/omc_db/config`,
method: 'GET',
method: 'get',
params: {
SQL: `SELECT * FROM config WHERE config_tag = 'historyDuration'`,
},
@@ -27,7 +27,7 @@ export async function getAlarmSet() {
// 同步设置
const logCapacityResult = await request({
url: `/api/rest/databaseManagement/v1/omc_db/config`,
method: 'GET',
method: 'get',
params: {
SQL: `SELECT * FROM config WHERE config_tag = 'syncTaskPeriod'`,
},
@@ -63,11 +63,7 @@ export async function getAlarmSet() {
}
}
if (Object.keys(resultData).length === 0) {
return {
code: RESULT_CODE_ERROR,
msg: RESULT_MSG_ERROR[language],
data: {},
};
return { code: RESULT_CODE_ERROR, msg: RESULT_MSG_ERROR[language], data: {} };
}
return {
code: RESULT_CODE_SUCCESS,
@@ -90,14 +86,14 @@ export async function updateAlarmSet(data: Record<string, any>) {
// 历史告警保存时间
const historyDurationResult = await request({
url: `/api/rest/databaseManagement/v1/omc_db/config?WHERE=config_tag='historyDuration'`,
method: 'PUT',
method: 'put',
data: { data: { value: data.historyDuration.toString() } },
});
arr.push(historyDurationResult);
// 同步设置
const syncTaskPeriodResult = await request({
url: `/api/rest/databaseManagement/v1/omc_db/config?WHERE=config_tag='syncTaskPeriod'`,
method: 'PUT',
method: 'put',
data: { data: { value_json: JSON.stringify(syncTaskPeriodJson) } },
});
arr.push(syncTaskPeriodResult);
@@ -121,11 +117,7 @@ export async function updateAlarmSet(data: Record<string, any>) {
}
// 无变更时
if (resultNum === 0) {
return {
code: RESULT_CODE_ERROR,
msg: RESULT_MSG_ERROR[language],
data: 0,
};
return { code: RESULT_CODE_ERROR, msg: RESULT_MSG_ERROR[language], data: 0 };
}
return {
code: RESULT_CODE_SUCCESS,
@@ -145,7 +137,7 @@ export async function getForwardSet() {
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/omc_db/config`,
method: 'GET',
method: 'get',
params: {
SQL: `SELECT * FROM config WHERE config_tag = 'forwardAlarm'`,
},
@@ -174,13 +166,14 @@ export async function getForwardSet() {
*/
export async function updateForwardSet(data: Record<string, any>) {
// return false;
let obj: any = [
{ interface: 'Email', to_user: data.emailObj },
{ interface: 'SMS', to_user: data.smsObj },
];
console.log(data)
let obj:any=[
{interface:"Email",to_user:data.emailObj},
{interface:"SMS",to_user:data.smsObj}
]
const result = await request({
url: `/api/rest/databaseManagement/v1/omc_db/config?WHERE=config_tag='forwardAlarm'`,
method: 'PUT',
method: 'put',
data: { data: { value_json: JSON.stringify(obj) } },
});
// 解析数据

View File

@@ -4,6 +4,71 @@ import { parseObjLineToHump } from '@/utils/parse-utils';
import { parseDateToStr } from '@/utils/date-utils';
import useUserStore from '@/store/modules/user';
/**
* 查询列表
* @param query 查询参数
* @returns object
*/
export async function listAct(query: Record<string, any>) {
let totalSQL = `select count(*) as total from alarm where alarm_status='0'`;
let rowsSQL = `select * from alarm where alarm_status='0'`;
// 查询
let querySQL = '';
querySQL += query.alarm_code
? ` and alarm_code = '${query.alarm_code}' `
: '';
querySQL += query.alarm_type
? ` and alarm_type = '${query.alarm_type}' `
: '';
querySQL += query.pv_flag ? ` and pv_flag = '${query.pv_flag}' ` : '';
querySQL += query.orig_severity
? ` and orig_severity in('${query.orig_severity}' )`
: '';
querySQL += query.ne_id ? ` and ne_id like '%${query.ne_id}%' ` : '';
querySQL += query.ne_name ? ` and ne_name like '%${query.ne_name}%' ` : '';
querySQL += query.ne_type ? ` and ne_type like '%${query.ne_type}%' ` : '';
querySQL +=
query.beginTime && query.endTime
? ` and event_time BETWEEN '${query.beginTime}' and ' ${query.endTime}'`
: '';
// 分页
const pageNum = (query.pageNum - 1) * query.pageSize;
const limtSql = ` order by clear_time desc limit ${pageNum},${query.pageSize} `;
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/alarm`,
method: 'get',
params: {
SQL: totalSQL + querySQL,
rowsSQL: rowsSQL + querySQL + limtSql,
},
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
const data: DataList = {
total: 0,
rows: [],
code: result.code,
msg: result.msg,
};
result.data.data.forEach((item: any) => {
const itemData = item['alarm'];
if (Array.isArray(itemData)) {
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
data.total = itemData[0]['total'];
} else {
data.rows = itemData.map(v => parseObjLineToHump(v));
}
}
});
return data;
}
return result;
}
/**
* 确认告警信息
* @param data 鉴权对象
@@ -22,7 +87,7 @@ export function updateConfirm(data: Record<string, any>) {
return request({
url: `/api/rest/databaseManagement/v1/update/omc_db/alarm?WHERE=id='${data.id}'`,
method: 'PUT',
method: 'put',
data: finalData,
});
}
@@ -46,7 +111,7 @@ export function cancelConfirm(data: (string | number)[]) {
url: `/api/rest/databaseManagement/v1/update/omc_db/alarm?WHERE=id in(${data.join(
','
)})`,
method: 'PUT',
method: 'put',
data: finalData,
});
}
@@ -81,7 +146,7 @@ export async function exportAll(query: Record<string, any>) {
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/alarm`,
method: 'GET',
method: 'get',
params: {
rowsSQL: rowsSQL + querySQL,
},

View File

@@ -1,15 +1,83 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch';
import { parseDateToStr } from '@/utils/date-utils';
import { NE_TYPE_LIST } from '@/constants/ne-constants';
/**
* 服务器时间
* 查询公告列表
* @param query 查询参数
* @returns object
*/
export async function listMain() {
const result = await request({
url: '/api/rest/systemManagement/v1/elementType/all/objectType/systemState',
method: 'get',
timeout: 60_000,
});
// console.log(result);
let realData = result.data.data;
const mergedData = realData.map((obj: any) => {
// console.log(obj);
const [key, value] = Object.entries(obj)[0];
const ipAddress = (value as any).ipAddress;
const systemState = (value as any).systemState;
const serialNum = (value as any).serialNum;
const version = (value as any).version;
const errCode = systemState && systemState['errorCode'];
var time = new Date();
// console.log(key, value);
let mergedObj;
if (errCode === undefined && systemState) {
mergedObj = {
...systemState,
refresh: parseDateToStr(time),
ipAddress: ipAddress,
name: key.split('/').join('_'),
status: 'Normal',
};
} else {
mergedObj = {
version,
refresh: parseDateToStr(time),
ipAddress,
serialNum,
name: key.split('/').join('_'),
expiryDate: '-',
status: 'Abnormal',
};
}
return mergedObj;
});
//通过sort进行冒泡排序
mergedData.sort((a: any, b: any) => {
const typeA = NE_TYPE_LIST.indexOf(a.name.split('_')[0]);
const typeB = NE_TYPE_LIST.indexOf(b.name.split('_')[0]);
if (typeA === -1) return 1; // 如果不在特定顺序中,排到后面
if (typeB === -1) return -1; // 如果不在特定顺序中,排到后面
return typeA - typeB;
});
return mergedData;
}
/**
* 获取服务器时间
* @returns object
*/
export async function getServerTime() {
return request({
url: `/time`,
method: 'GET',
whithToken: false,
// 发起请求
const result = await request({
url: `/api/rest/systemManagement/v1/elementType/OMC/objectType/time`,
method: 'get',
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && result.data) {
return Object.assign(result, {
data: result.data.data,
});
}
return result;
}
/**
@@ -19,7 +87,7 @@ export async function getServerTime() {
export function getSysConf() {
return request({
url: `/sys-conf`,
method: 'GET',
method: 'get',
whithToken: false,
});
}

View File

@@ -1,4 +1,6 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
/**
* 查询日志列表
@@ -6,9 +8,65 @@ import { request } from '@/plugins/http-fetch';
* @returns object
*/
export async function listAlarm(query: Record<string, any>) {
return await request({
url: `/ne/data/alarm/log/list`,
method: 'GET',
params: query,
let totalSQL = 'select count(*) as total from alarm_log where 1=1 ';
let rowsSQL = 'select * from alarm_log where 1=1 ';
// 查询
let querySQL = '';
if (query.neType) {
querySQL += ` and ne_type like '%${query.neType}%' `;
}
if (query.status) {
querySQL += ` and alarm_status = '${query.status}' `;
}
if (query.beginTime) {
querySQL += ` and log_time >= '${query.beginTime}' `;
}
if (query.endTime) {
querySQL += ` and log_time <= '${query.endTime}' `;
}
// 排序
let sortSql = ' order by log_time ';
if (query.sortOrder === 'asc') {
sortSql += ' asc ';
} else {
sortSql += ' desc ';
}
// 分页
const pageNum = (query.pageNum - 1) * query.pageSize;
const limtSql = ` limit ${pageNum},${query.pageSize} `;
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/alarm_log`,
method: 'get',
params: {
totalSQL: totalSQL + querySQL,
rowsSQL: rowsSQL + querySQL + sortSql + limtSql,
},
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
const data: DataList = {
total: 0,
rows: [],
code: result.code,
msg: result.msg,
};
result.data.data.forEach((item: any) => {
const itemData = item['alarm_log'];
if (Array.isArray(itemData)) {
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
data.total = itemData[0]['total'];
} else {
data.rows = itemData.map(v => parseObjLineToHump(v));
}
}
});
return data;
}
return result;
}

View File

@@ -0,0 +1,53 @@
import { request } from '@/plugins/http-fetch';
/**
* 获取下拉框数据
* @returns object
*/
export function getBakFile() {
return request({
url: '/lm/table/list',
method: 'get',
});
}
/**
* 获取对应类型的文件列表
* @param query 查询参数
* @returns object
*/
export function getBakFileList(query: Record<string, any>) {
return request({
url: '/lm/file/list',
method: 'get',
params: query,
});
}
/**
* 下载远端文件
* @param query 查询参数
* @returns object
*/
export function downFile(query: Record<string, any>) {
return request({
url: `/lm/file/${query.fileName}`,
method: 'get',
params: query,
responseType: 'blob',
timeout: 180_000,
});
}
/**
* 删除远端获取文件
* @param query 查询参数
* @returns object
*/
export function delFile(query: Record<string, any>) {
return request({
url: `/lm/file/${query.fileName}`,
method: 'delete',
params: query,
});
}

View File

@@ -1,4 +1,6 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
/**
* 查询日志列表
@@ -6,9 +8,62 @@ import { request } from '@/plugins/http-fetch';
* @returns object
*/
export async function listForwarding(query: Record<string, any>) {
return await request({
url: `/ne/data/alarm/forward/log/list`,
method: 'GET',
params: query,
let totalSQL = 'select count(*) as total from alarm_forward_log where 1=1 ';
let rowsSQL = 'select * from alarm_forward_log where 1=1 ';
// 查询
let querySQL = '';
if (query.neType) {
querySQL += ` and ne_type like '%${query.neType}%' `;
}
if (query.beginTime) {
querySQL += ` and log_time >= '${query.beginTime}' `;
}
if (query.endTime) {
querySQL += ` and log_time <= '${query.endTime}' `;
}
// 排序
let sortSql = ' order by log_time ';
if (query.sortOrder === 'asc') {
sortSql += ' asc ';
} else {
sortSql += ' desc ';
}
// 分页
const pageNum = (query.pageNum - 1) * query.pageSize;
const limtSql = ` limit ${pageNum},${query.pageSize} `;
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/alarm_forward_log`,
method: 'get',
params: {
totalSQL: totalSQL + querySQL,
rowsSQL: rowsSQL + querySQL + sortSql + limtSql,
},
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
const data: DataList = {
total: 0,
rows: [],
code: result.code,
msg: result.msg,
};
result.data.data.forEach((item: any) => {
const itemData = item['alarm_forward_log'];
if (Array.isArray(itemData)) {
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
data.total = itemData[0]['total'];
} else {
data.rows = itemData.map(v => parseObjLineToHump(v));
}
}
});
return data;
}
return result;
}

View File

@@ -18,7 +18,7 @@ export async function getLogSet() {
// 日志保存时间
const logDurationResult = await request({
url: `/api/rest/databaseManagement/v1/omc_db/config`,
method: 'GET',
method: 'get',
params: {
SQL: `SELECT * FROM config WHERE config_tag = 'logDuration'`,
},
@@ -27,7 +27,7 @@ export async function getLogSet() {
// 日志最大容量
const logCapacityResult = await request({
url: `/api/rest/databaseManagement/v1/omc_db/config`,
method: 'GET',
method: 'get',
params: {
SQL: `SELECT * FROM config WHERE config_tag = 'logCapacity'`,
},
@@ -76,7 +76,7 @@ export async function updateLogSet(data: Record<string, any>) {
const value = `${data[key]}`;
const result = request({
url: `/api/rest/databaseManagement/v1/omc_db/config?WHERE=config_tag='${key}'`,
method: 'PUT',
method: 'put',
data: { data: { value } },
});
arr.push(result);
@@ -120,7 +120,7 @@ export async function getFtpLogSet() {
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/omc_db/config`,
method: 'GET',
method: 'get',
params: {
SQL: `SELECT * FROM config WHERE config_tag = 'ftpLogSet'`,
},
@@ -151,7 +151,7 @@ export async function getFtpLogSet() {
export async function updateFtpLogSet(data: Record<string, any>) {
const result = await request({
url: `/api/rest/databaseManagement/v1/omc_db/config?WHERE=config_tag='ftpLogSet'`,
method: 'PUT',
method: 'put',
data: { data: { value_json: JSON.stringify(data) } },
});
// 解析数据
@@ -176,7 +176,7 @@ export async function getRemoteOut() {
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/omc_db/config`,
method: 'GET',
method: 'get',
params: {
SQL: `SELECT * FROM config WHERE config_tag = 'remoteLogSet'`,
},
@@ -206,7 +206,7 @@ export async function getRemoteOut() {
export async function updateRemoteOut(data: Record<string, any>) {
const result = await request({
url: `/api/rest/databaseManagement/v1/omc_db/config?WHERE=config_tag='remoteLogSet'`,
method: 'PUT',
method: 'put',
data: { data: { value_json: JSON.stringify(data) } },
});
// 解析数据
@@ -254,7 +254,7 @@ export async function exportLog(query: Record<string, any>) {
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/${query.logType}`,
method: 'GET',
method: 'get',
params: {
SQL: querySQL,
},
@@ -277,7 +277,7 @@ export async function exportLog(query: Record<string, any>) {
export async function backupLog(logType: string) {
const result = await request({
url: `/api/rest/dataManagement/v1/omc_db/${logType}/backup`,
method: 'POST',
method: 'post',
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && result.data.data) {
@@ -300,7 +300,7 @@ export async function backupLog(logType: string) {
export async function backupDownload(path: string) {
return request({
url: `/api/rest/fileManagement/v1/path/file?path=${path}`,
method: 'GET',
method: 'get',
responseType: 'blob',
timeout: 180_000,
});
@@ -314,7 +314,7 @@ export async function backupDownload(path: string) {
export function backupFileList() {
return request({
url: `/api/rest/fileManagement/v1/files/listFiles`,
method: 'POST',
method: 'post',
data: {
path: '/usr/local/omc/database',
expand: true,

69
src/api/logManage/mml.ts Normal file
View File

@@ -0,0 +1,69 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
/**
* 查询日志列表
* @param query 查询参数
* @returns object
*/
export async function listMML(query: Record<string, any>) {
let totalSQL = 'select count(*) as total from mml_log where 1=1 ';
let rowsSQL = 'select * from mml_log where 1=1 ';
// 查询
let querySQL = '';
if (query.accountName) {
querySQL += ` and user like '%${query.accountName}%' `;
}
if (query.beginTime) {
querySQL += ` and log_time >= '${query.beginTime}' `;
}
if (query.endTime) {
querySQL += ` and log_time <= '${query.endTime}' `;
}
// 排序
let sortSql = ' order by log_time ';
if (query.sortOrder === 'asc') {
sortSql += ' asc ';
} else {
sortSql += ' desc ';
}
// 分页
const pageNum = (query.pageNum - 1) * query.pageSize;
const limtSql = ` limit ${pageNum},${query.pageSize} `;
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/mml_log`,
method: 'get',
params: {
totalSQL: totalSQL + querySQL,
rowsSQL: rowsSQL + querySQL + sortSql + limtSql,
},
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
const data: DataList = {
total: 0,
rows: [],
code: result.code,
msg: result.msg,
};
result.data.data.forEach((item: any) => {
const itemData = item['mml_log'];
if (Array.isArray(itemData)) {
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
data.total = itemData[0]['total'];
} else {
data.rows = itemData.map(v => parseObjLineToHump(v));
}
}
});
return data;
}
return result;
}

View File

@@ -41,7 +41,7 @@ export async function listOperationLog(query: Record<string, any>) {
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/operation_log`,
method: 'GET',
method: 'get',
params: {
totalSQL: totalSQL + querySQL,
rowsSQL: rowsSQL + querySQL + sortSql + limtSql,
@@ -50,8 +50,9 @@ export async function listOperationLog(query: Record<string, any>) {
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
const data = {
data: { total: 0, rows: [] as any },
const data: DataList = {
total: 0,
rows: [],
code: result.code,
msg: result.msg,
};
@@ -59,9 +60,9 @@ export async function listOperationLog(query: Record<string, any>) {
const itemData = item['operation_log'];
if (Array.isArray(itemData)) {
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
data.data.total = itemData[0]['total'];
data.total = itemData[0]['total'];
} else {
data.data.rows = itemData.map(v => parseObjLineToHump(v));
data.rows = itemData.map(v => parseObjLineToHump(v));
}
}
});

View File

@@ -0,0 +1,72 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
/**
* 查询日志列表
* @param query 查询参数
* @returns object
*/
export async function listSecurityLog(query: Record<string, any>) {
let totalSQL = 'select count(*) as total from security_log where 1=1 ';
let rowsSQL = 'select * from security_log where 1=1 ';
// 查询
let querySQL = '';
if (query.accountName) {
querySQL += ` and account_name like '%${query.accountName}%' `;
}
if (query.opType) {
querySQL += ` and op_type = '${query.opType}' `;
}
if (query.beginTime) {
querySQL += ` and op_time >= '${query.beginTime}' `;
}
if (query.endTime) {
querySQL += ` and op_time <= '${query.endTime}' `;
}
// 排序
let sortSql = ' order by op_time ';
if (query.sortOrder === 'asc') {
sortSql += ' asc ';
} else {
sortSql += ' desc ';
}
// 分页
const pageNum = (query.pageNum - 1) * query.pageSize;
const limtSql = ` limit ${pageNum},${query.pageSize} `;
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/security_log`,
method: 'get',
params: {
totalSQL: totalSQL + querySQL,
rowsSQL: rowsSQL + querySQL + sortSql + limtSql,
},
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
const data: DataList = {
total: 0,
rows: [],
code: result.code,
msg: result.msg,
};
result.data.data.forEach((item: any) => {
const itemData = item['security_log'];
if (Array.isArray(itemData)) {
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
data.total = itemData[0]['total'];
} else {
data.rows = itemData.map(v => parseObjLineToHump(v));
}
}
});
return data;
}
return result;
}

61
src/api/login.ts Normal file
View File

@@ -0,0 +1,61 @@
import { request } from '@/plugins/http-fetch';
// 登录方法
export function login(data: Record<string, string>) {
return request({
url: '/login',
method: 'post',
data: data,
whithToken: false,
crypto: true,
});
}
/**
* 注册方法
* @param data 注册对象
* @returns object
*/
export function register(data: Record<string, any>) {
return request({
url: '/register',
method: 'post',
data: data,
whithToken: false,
crypto: true,
});
}
/**
* 获取用户详细信息
* @returns object
*/
export function getInfo() {
return request({
url: '/getInfo',
method: 'get',
});
}
/**
* 退出方法
* @returns object
*/
export function logout() {
return request({
url: '/logout',
method: 'post',
});
}
/**
* 获取验证码
* @returns object
*/
export function getCaptchaImage() {
return request({
url: '/captchaImage',
method: 'get',
whithToken: false,
});
}

View File

@@ -15,7 +15,7 @@ export async function getOperationSet() {
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/omc_db/config`,
method: 'GET',
method: 'get',
params: {
SQL: `SELECT * FROM config WHERE config_tag = 'operationSet'`,
},
@@ -45,7 +45,7 @@ export async function getOperationSet() {
export async function updateOperationSet(data: Record<string, any>) {
const result = await request({
url: `/api/rest/databaseManagement/v1/omc_db/config?WHERE=config_tag='operationSet'`,
method: 'PUT',
method: 'put',
data: { data: { value_json: JSON.stringify(data) } },
});
// 解析数据

View File

@@ -1,4 +1,6 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
/**
* 查询网元可用cmd命令
@@ -6,70 +8,48 @@ import { request } from '@/plugins/http-fetch';
* @returns object
*/
export async function getMMLByNE(neType: string) {
return request({
url: '/tool/mml/system/list',
method: 'GET',
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/elementType/omc_db/objectType/mml_system`,
method: 'get',
params: {
neType: neType,
status: 'Active',
pageNum: 1,
pageSize: 1000,
SQL: `select * from mml_system where ne_type = '${neType}' and status = 'Active'`,
},
timeout: 60_000,
});
}
/**
* 查询UDM可用cmd命令
* @returns object
*/
export async function getMMLByUDM() {
return request({
url: '/tool/mml/subscriber/list',
method: 'GET',
params: {
neType: 'UDM',
status: 'Active',
pageNum: 1,
pageSize: 1000,
},
timeout: 60_000,
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
let data = result.data.data[0];
return Object.assign(result, {
data: parseObjLineToHump(data['mml_system']),
});
}
return result;
}
/**
* 发送网元的mml命令
* @param neType 网元类型
* @param coreUid 网元类型
* @param neId 网元ID
* @param objectType 接口类型
* @param cmdStr 命令串
* @returns
*/
export async function sendMML(data: Record<string, any>) {
return request({
url: '/tool/mml/command',
method: 'POST',
data: data,
timeout: 180_000,
});
}
/**
* 发送网元的mml命令
* @param neUid 网元ID
* @param cmdArr 命令数组
* @returns
*/
export async function sendMMlByGeneral(neUid: string, cmdArr: string[]) {
return request({
url: '/tool/mml/command',
method: 'POST',
data: {
neUid: neUid,
type: 'General',
command: cmdArr,
},
export async function sendMMlByNE(
neType: string,
neId: string,
objectType: string,
cmdArr: string[]
) {
// 发起请求
const result = await request({
url: `/api/rest/operationManagement/v1/elementType/${neType}/objectType/${objectType}?ne_id=${neId}`,
method: 'post',
data: { mml: cmdArr },
timeout: 180_000,
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
result.data = result.data.data;
}
return result;
}

View File

@@ -10,7 +10,7 @@ export async function getMMLByOMC() {
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/elementType/omc_db/objectType/mml_command`,
method: 'GET',
method: 'get',
params: {
SQL: `select * from mml_command where ne_type = 'OMC' and status = 'Active'`,
},
@@ -35,7 +35,7 @@ export async function sendMMlByOMC(neId: string, cmdArr: string[]) {
// 发起请求
const result = await request({
url: `/api/rest/operationManagement/v1/elementType/OMC/objectType/mml?ne_id=${neId}`,
method: 'POST',
method: 'post',
data: { mml: cmdArr },
timeout: 180_000,
});

View File

@@ -0,0 +1,47 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
/**
* 查询UDM可用cmd命令
* @returns object
*/
export async function getMMLByUDM() {
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/elementType/omc_db/objectType/mml_subscriber`,
method: 'get',
params: {
SQL: `select * from mml_subscriber where ne_type = 'UDM' and status = 'Active'`,
},
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
let data = result.data.data[0];
return Object.assign(result, {
data: parseObjLineToHump(data['mml_subscriber']),
});
}
return result;
}
/**
* 发送UDM的mml命令
* @param neId 网元ID
* @param cmdStr 命令串
* @returns
*/
export async function sendMMlByUDM(neId: string, cmdArr: string[]) {
// 发起请求
const result = await request({
url: `/api/rest/operationManagement/v1/elementType/UDM/objectType/mml?ne_id=${neId}`,
method: 'post',
data: { mml: cmdArr },
timeout: 180_000,
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
result.data = result.data.data;
}
return result;
}

View File

@@ -7,7 +7,7 @@ import { request } from '@/plugins/http-fetch';
export function getCache() {
return request({
url: '/monitor/cache',
method: 'GET',
method: 'get',
});
}
@@ -17,8 +17,8 @@ export function getCache() {
*/
export function listCacheName() {
return request({
url: '/monitor/cache/names',
method: 'GET',
url: '/monitor/cache/getNames',
method: 'get',
});
}
@@ -29,9 +29,8 @@ export function listCacheName() {
*/
export function listCacheKey(cacheName: string) {
return request({
url: `/monitor/cache/keys`,
method: 'GET',
params: { cacheName },
url: `/monitor/cache/getKeys/${cacheName}`,
method: 'get',
});
}
@@ -43,22 +42,8 @@ export function listCacheKey(cacheName: string) {
*/
export function getCacheValue(cacheName: string, cacheKey: string) {
return request({
url: `/monitor/cache/value`,
method: 'GET',
params: { cacheName, cacheKey },
});
}
/**
* 缓存名称列表安全删除
*
* 指定可清理的缓存key
* @returns object
*/
export function clearCacheSafe() {
return request({
url: '/monitor/cache/names',
method: 'DELETE',
url: `/monitor/cache/getValue/${cacheName}/${cacheKey}`,
method: 'get',
});
}
@@ -69,9 +54,8 @@ export function clearCacheSafe() {
*/
export function clearCacheName(cacheName: string) {
return request({
url: `/monitor/cache/keys`,
method: 'DELETE',
params: { cacheName },
url: `/monitor/cache/clearCacheName/${cacheName}`,
method: 'delete',
});
}
@@ -83,8 +67,20 @@ export function clearCacheName(cacheName: string) {
*/
export function clearCacheKey(cacheName: string, cacheKey: string) {
return request({
url: `/monitor/cache/value`,
method: 'DELETE',
params: { cacheName, cacheKey },
url: `/monitor/cache/clearCacheKey/${cacheName}/${cacheKey}`,
method: 'delete',
});
}
/**
* 安全清理缓存名称
*
* 指定可清理的缓存key
* @returns object
*/
export function clearCacheSafe() {
return request({
url: '/monitor/cache/clearCacheSafe',
method: 'delete',
});
}

View File

@@ -8,8 +8,8 @@ import { request } from '@/plugins/http-fetch';
export function exportJob(query: Record<string, any>) {
return request({
url: '/monitor/job/export',
method: 'GET',
params: query,
method: 'post',
data: query,
responseType: 'blob',
});
}
@@ -22,7 +22,7 @@ export function exportJob(query: Record<string, any>) {
export function listJob(query: Record<string, any>) {
return request({
url: '/monitor/job/list',
method: 'GET',
method: 'get',
params: query,
});
}
@@ -35,7 +35,7 @@ export function listJob(query: Record<string, any>) {
export function getJob(jobId: string | number) {
return request({
url: `/monitor/job/${jobId}`,
method: 'GET',
method: 'get',
});
}
@@ -47,7 +47,7 @@ export function getJob(jobId: string | number) {
export function addJob(data: Record<string, any>) {
return request({
url: '/monitor/job',
method: 'POST',
method: 'post',
data: data,
});
}
@@ -60,7 +60,7 @@ export function addJob(data: Record<string, any>) {
export function updateJob(data: Record<string, any>) {
return request({
url: '/monitor/job',
method: 'PUT',
method: 'put',
data: data,
});
}
@@ -73,7 +73,7 @@ export function updateJob(data: Record<string, any>) {
export function delJob(jobId: string | number) {
return request({
url: `/monitor/job/${jobId}`,
method: 'DELETE',
method: 'delete',
});
}
@@ -85,14 +85,14 @@ export function delJob(jobId: string | number) {
*/
export function changeJobStatus(
jobId: string | number,
statusFlag: string | number
status: string | number
) {
return request({
url: '/monitor/job/status',
method: 'PUT',
url: '/monitor/job/changeStatus',
method: 'put',
data: {
jobId,
statusFlag,
status,
},
});
}
@@ -105,7 +105,7 @@ export function changeJobStatus(
export function runJob(jobId: string) {
return request({
url: `/monitor/job/run/${jobId}`,
method: 'PUT',
method: 'put',
});
}
@@ -115,7 +115,7 @@ export function runJob(jobId: string) {
*/
export function resetQueueJob() {
return request({
url: '/monitor/job/reset',
method: 'PUT',
url: '/monitor/job/resetQueueJob',
method: 'put',
});
}

View File

@@ -5,11 +5,13 @@ import { request } from '@/plugins/http-fetch';
* @param query 查询参数
* @returns bolb
*/
export function exportJobLog(query: Record<string, any>) {
export function exportJobLog(
query: Record<string, any>
) {
return request({
url: '/monitor/job/log/export',
method: 'GET',
params: query,
url: '/monitor/jobLog/export',
method: 'post',
data: query,
responseType: 'blob',
});
}
@@ -21,8 +23,8 @@ export function exportJobLog(query: Record<string, any>) {
*/
export function listJobLog(query: Record<string, any>) {
return request({
url: '/monitor/job/log/list',
method: 'GET',
url: '/monitor/jobLog/list',
method: 'get',
params: query,
});
}
@@ -34,8 +36,8 @@ export function listJobLog(query: Record<string, any>) {
*/
export function delJobLog(jobLogId: string) {
return request({
url: `/monitor/job/log/${jobLogId}`,
method: 'DELETE',
url: `/monitor/jobLog/${jobLogId}`,
method: 'delete',
});
}
@@ -45,7 +47,7 @@ export function delJobLog(jobLogId: string) {
*/
export function cleanJobLog() {
return request({
url: '/monitor/job/log/clean',
method: 'DELETE',
url: '/monitor/jobLog/clean',
method: 'delete',
});
}

View File

@@ -4,7 +4,7 @@ import { request } from '@/plugins/http-fetch';
export function getLoad(query: Record<string, any>) {
return request({
url: '/monitor/load',
method: 'GET',
method: 'get',
params: query,
timeout: 60_000,
});

View File

@@ -7,8 +7,8 @@ import { request } from '@/plugins/http-fetch';
*/
export function listOnline(query: Record<string, any>) {
return request({
url: '/monitor/user-online/list',
method: 'GET',
url: '/monitor/online/list',
method: 'get',
params: query,
});
}
@@ -20,7 +20,7 @@ export function listOnline(query: Record<string, any>) {
*/
export function forceLogout(tokenId: string) {
return request({
url: `/monitor/user-online/logout/${tokenId}`,
method: 'DELETE',
url: `/monitor/online/${tokenId}`,
method: 'delete',
});
}

View File

@@ -3,7 +3,8 @@ import { request } from '@/plugins/http-fetch';
/**服务器服务信息 */
export function getSystemInfo() {
return request({
url: '/monitor/system',
method: 'GET',
url: '/monitor/system-info',
method: 'get',
timeout: 60_000,
});
}

View File

@@ -4,7 +4,7 @@ import { request } from '@/plugins/http-fetch';
export function getGraphGroups() {
return request({
url: '/chart/graph/groups',
method: 'GET',
method: 'get',
});
}
@@ -12,7 +12,7 @@ export function getGraphGroups() {
export function getGraphData(group: string) {
return request({
url: '/chart/graph',
method: 'GET',
method: 'get',
params: {
group,
},
@@ -23,7 +23,7 @@ export function getGraphData(group: string) {
export function saveGraphData(group: string, data: Record<string, any>) {
return request({
url: '/chart/graph',
method: 'POST',
method: 'post',
data: {
group,
data,
@@ -35,6 +35,6 @@ export function saveGraphData(group: string, data: Record<string, any>) {
export function delGraphData(group: string) {
return request({
url: `/chart/graph/${group}`,
method: 'DELETE',
method: 'delete',
});
}

View File

@@ -1,96 +0,0 @@
import { request } from '@/plugins/http-fetch';
/**
* 查询网元状态
* @param neUid 网元ID
* @returns object
*/
export function stateNeInfo(neUid: string) {
return request({
url: '/ne/info/state',
method: 'GET',
params: { neUid },
});
}
/**
* 查询网元信息
* @param neUid 网元ID
* @returns object
*/
export function getNeInfoByNF(neUid: string) {
return request({
url: '/ne/info/nf',
method: 'GET',
params: { neUid },
});
}
/**
* 网元端OAM配置文件读取
* @param neUid 网元ID
* @returns object
*/
export function getOAMFile(neUid: string) {
return request({
url: '/ne/info/file/oam',
method: 'GET',
params: { neUid },
});
}
/**
* 网元端配置文件写入
* @param neUid 网元ID
* @param content 用json对象
* @param sync 同步到网元
* @returns object
*/
export function saveOAMFile(data: Record<string, any>) {
return request({
url: `/ne/info/file/oam`,
method: 'PUT',
data: data,
timeout: 60_000,
});
}
/**
* 网元端公共配置文件读取
* @returns object
*/
export function getPara5GFilee() {
return request({
url: '/ne/info/file/para5g',
method: 'GET',
});
}
/**
* 网元端公共配置文件写入
* @param content txt内容为字符串 其他文件格式都用json对象
* @param syncNe 同步到网元端 neUid
* @returns object
*/
export function savePara5GFile(data: Record<string, any>) {
return request({
url: `/ne/info/file/para5g`,
method: 'PUT',
data: data,
timeout: 60_000,
});
}
/**
* 网元服务操作
* @param data 对象 {neUid, action}
* @returns object
*/
export function serviceNeAction(data: Record<string, any>) {
return request({
url: `/ne/action/service`,
method: 'PUT',
data: data,
timeout: 60_000,
});
}

View File

@@ -1,4 +1,3 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch';
/**
@@ -9,7 +8,7 @@ import { request } from '@/plugins/http-fetch';
export function getAllNeConfig(neType: string) {
return request({
url: `/ne/config/list/${neType}`,
method: 'GET',
method: 'get',
timeout: 60_000,
});
}
@@ -23,7 +22,7 @@ export function getNeConfigData(params: Record<string, any>) {
return request({
url: `/ne/config/data`,
params,
method: 'GET',
method: 'get',
});
}
@@ -35,7 +34,7 @@ export function getNeConfigData(params: Record<string, any>) {
export function editNeConfigData(data: Record<string, any>) {
return request({
url: `/ne/config/data`,
method: 'PUT',
method: 'put',
data: data,
});
}
@@ -48,7 +47,7 @@ export function editNeConfigData(data: Record<string, any>) {
export function addNeConfigData(data: Record<string, any>) {
return request({
url: `/ne/config/data`,
method: 'POST',
method: 'post',
data: data,
});
}
@@ -61,57 +60,7 @@ export function addNeConfigData(data: Record<string, any>) {
export function delNeConfigData(params: Record<string, any>) {
return request({
url: `/ne/config/data`,
method: 'DELETE',
method: 'delete',
params,
});
}
/**
* 从参数配置PCF中获取对应信息提供给PCF用户策略输入框
* @param neUid 网元标识
* @returns object {pccRules,sessionRules,qosTemplate,headerEnrichTemplate,serviceAreaRestriction}
*/
export async function getPCFRule(neUid: string) {
const paramNameArr = [
'pccRules',
'sessionRules',
'qosTemplate',
'headerEnrichTemplate',
'serviceAreaRestriction',
];
const reqArr = [];
for (const paramName of paramNameArr) {
reqArr.push(
request({
url: `/ne/config/data`,
params: { neType: 'PCF', neUid, paramName },
method: 'GET',
})
);
}
return await Promise.allSettled(reqArr).then(resArr => {
// 规则数据
const obj: any = {};
resArr.forEach((item, i: number) => {
if (item.status === 'fulfilled') {
const res = item.value;
if (res.code === RESULT_CODE_SUCCESS) {
const key = paramNameArr[i];
obj[key] = res.data.map((item: any) => {
if ('qosTemplate' === key) {
return { value: item.qosId, label: item.qosId };
}
if ('headerEnrichTemplate' === key) {
return { value: item.templateName, label: item.templateName };
}
if ('serviceAreaRestriction' === key) {
return { value: item.name, label: item.name };
}
return { value: item.ruleId, label: item.ruleId };
});
}
}
});
return obj;
});
}

View File

@@ -1,6 +1,4 @@
import { CACHE_SESSION_CRYPTO_API } from '@/constants/cache-keys-constants';
import { request } from '@/plugins/http-fetch';
import { sessionGet } from '@/utils/cache-session-utils';
/**
* 网元配置文件备份记录列表
@@ -10,7 +8,7 @@ import { sessionGet } from '@/utils/cache-session-utils';
export function listNeConfigBackup(query: Record<string, any>) {
return request({
url: '/ne/config/backup/list',
method: 'GET',
method: 'get',
params: query,
});
}
@@ -23,7 +21,7 @@ export function listNeConfigBackup(query: Record<string, any>) {
export function updateNeConfigBackup(data: Record<string, any>) {
return request({
url: '/ne/config/backup',
method: 'PUT',
method: 'put',
data: data,
});
}
@@ -33,11 +31,11 @@ export function updateNeConfigBackup(data: Record<string, any>) {
* @param id 记录ID
* @returns object
*/
export async function downNeConfigBackup(query: Record<string, any>) {
export async function downNeConfigBackup(id: string) {
return await request({
url: '/ne/config/backup/download',
method: 'GET',
params: query,
method: 'get',
params: { id },
responseType: 'blob',
timeout: 180_000,
});
@@ -48,11 +46,11 @@ export async function downNeConfigBackup(query: Record<string, any>) {
* @param id 记录ID
* @returns object
*/
export async function delNeConfigBackup(query: Record<string, any>) {
export async function delNeConfigBackup(id: string) {
return request({
url: '/ne/config/backup',
method: 'DELETE',
params: query,
method: 'delete',
params: { id },
});
}
@@ -64,7 +62,7 @@ export async function delNeConfigBackup(query: Record<string, any>) {
export function exportNeConfigBackup(data: Record<string, any>) {
return request({
url: '/ne/config/backup/export',
method: 'POST',
method: 'post',
data: data,
responseType: 'blob',
timeout: 180_000,
@@ -79,7 +77,7 @@ export function exportNeConfigBackup(data: Record<string, any>) {
export function importNeConfigBackup(data: Record<string, any>) {
return request({
url: '/ne/config/backup/import',
method: 'POST',
method: 'post',
data: data,
});
}
}

View File

@@ -8,7 +8,7 @@ import { request } from '@/plugins/http-fetch';
export function listNeHost(query: Record<string, any>) {
return request({
url: '/ne/host/list',
method: 'GET',
method: 'get',
params: query,
});
}
@@ -21,7 +21,7 @@ export function listNeHost(query: Record<string, any>) {
export function getNeHost(hostId: string | number) {
return request({
url: `/ne/host/${hostId}`,
method: 'GET',
method: 'get',
});
}
@@ -33,7 +33,7 @@ export function getNeHost(hostId: string | number) {
export function addNeHost(data: Record<string, any>) {
return request({
url: '/ne/host',
method: 'POST',
method: 'post',
data: data,
});
}
@@ -46,7 +46,7 @@ export function addNeHost(data: Record<string, any>) {
export function updateNeHost(data: Record<string, any>) {
return request({
url: '/ne/host',
method: 'PUT',
method: 'put',
data: data,
});
}
@@ -59,7 +59,7 @@ export function updateNeHost(data: Record<string, any>) {
export function delNeHost(hostId: string | number) {
return request({
url: `/ne/host/${hostId}`,
method: 'DELETE',
method: 'delete',
});
}
@@ -71,7 +71,7 @@ export function delNeHost(hostId: string | number) {
export function testNeHost(data: Record<string, any>) {
return request({
url: '/ne/host/test',
method: 'POST',
method: 'post',
data: data,
});
}
@@ -84,7 +84,7 @@ export function testNeHost(data: Record<string, any>) {
export function neHostCheckInfo(data: Record<string, any>) {
return request({
url: '/ne/host/checkBySSH',
method: 'POST',
method: 'post',
data: data,
});
}
@@ -97,7 +97,7 @@ export function neHostCheckInfo(data: Record<string, any>) {
export function neHostAuthorizedRSA(data: Record<string, any>) {
return request({
url: '/ne/host/authorizedBySSH',
method: 'POST',
method: 'post',
data: data,
});
}

View File

@@ -8,7 +8,7 @@ import { request } from '@/plugins/http-fetch';
export function listNeHostCmd(query: Record<string, any>) {
return request({
url: '/ne/hostCmd/list',
method: 'GET',
method: 'get',
params: query,
});
}
@@ -21,7 +21,7 @@ export function listNeHostCmd(query: Record<string, any>) {
export function getNeHostCmd(cmdId: string | number) {
return request({
url: `/ne/hostCmd/${cmdId}`,
method: 'GET',
method: 'get',
});
}
@@ -33,7 +33,7 @@ export function getNeHostCmd(cmdId: string | number) {
export function addNeHostCmd(data: Record<string, any>) {
return request({
url: '/ne/hostCmd',
method: 'POST',
method: 'post',
data: data,
});
}
@@ -46,7 +46,7 @@ export function addNeHostCmd(data: Record<string, any>) {
export function updateNeHostCmd(data: Record<string, any>) {
return request({
url: '/ne/hostCmd',
method: 'PUT',
method: 'put',
data: data,
});
}
@@ -59,6 +59,6 @@ export function updateNeHostCmd(data: Record<string, any>) {
export function delNeHostCmd(cmdId: string | number) {
return request({
url: `/ne/hostCmd/${cmdId}`,
method: 'DELETE',
method: 'delete',
});
}

View File

@@ -1,5 +1,3 @@
import { CACHE_SESSION_CRYPTO_API } from '@/constants/cache-keys-constants';
import { sessionGet } from '@/utils/cache-session-utils';
import { request } from '@/plugins/http-fetch';
/**
@@ -10,7 +8,7 @@ import { request } from '@/plugins/http-fetch';
export function listNeInfo(query: Record<string, any>) {
return request({
url: '/ne/info/list',
method: 'GET',
method: 'get',
params: query,
timeout: 60_000,
});
@@ -21,11 +19,10 @@ export function listNeInfo(query: Record<string, any>) {
* @param infoId 信息ID
* @returns object
*/
export function getNeInfo(id: string | number) {
export function getNeInfo(infoId: string | number) {
return request({
url: `/ne/info`,
method: 'GET',
params: { id },
url: `/ne/info/${infoId}`,
method: 'get',
});
}
@@ -37,9 +34,9 @@ export function getNeInfo(id: string | number) {
export function addNeInfo(data: Record<string, any>) {
return request({
url: `/ne/info`,
method: 'POST',
method: 'post',
data: data,
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
crypto: true,
timeout: 30_000,
});
}
@@ -52,78 +49,135 @@ export function addNeInfo(data: Record<string, any>) {
export function updateNeInfo(data: Record<string, any>) {
return request({
url: `/ne/info`,
method: 'PUT',
method: 'put',
data: data,
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
crypto: true,
timeout: 30_000,
});
}
/**
* 网元信息删除
* @param query 查询参数 id
* @param id 信息ID
* @returns object
*/
export function delNeInfo(query: Record<string, any>) {
export function delNeInfo(infoIds: string | number) {
return request({
url: `/ne/info`,
method: 'DELETE',
params: query,
url: `/ne/info/${infoIds}`,
method: 'delete',
timeout: 60_000,
});
}
/**
* 查询网元列表全部无分页
* @param query 查询参数 coreId, neUid bandStatus bandHost
* @param query 查询参数 neType neId bandStatus bandHost
* @returns object
*/
export function listAllNeInfo(query: Record<string, any>) {
return request({
url: '/ne/info/list/all',
method: 'GET',
url: '/ne/info/listAll',
method: 'get',
params: query,
timeout: 60_000,
});
}
/**
* 网元授权激活授权申请码
* 查询网元状态
* @param neType 网元类型
* @param neId 网元id
* @param neId 网元ID
* @returns object
*/
export function codeNeLicense(neUid: string) {
export function stateNeInfo(neType: string, neId: string) {
return request({
url: `/ne/info/license/code`,
method: 'GET',
params: { neUid },
url: '/ne/info/state',
method: 'get',
params: { neType, neId },
});
}
/**
* 网元授权激活授权文件替换
* @param data 网元对象 {"neUid": "", "licensePath": "", "reload": true}
* 查询网元信息
* @param neType 网元类型
* @param neId 网元ID
* @returns object
*/
export function updateNeLicense(data: Record<string, any>) {
export function getNeInfoByTypeAndID(neType: string, neId: string) {
return request({
url: `/ne/info/license/update`,
method: 'POST',
data: data,
timeout: 180_000,
url: '/ne/info/byTypeAndID',
method: 'get',
params: { neType, neId },
});
}
/**
* 网元核心关联
* @param data 网元对象 {"neUid": "", "coreUid": ""}
* 网元端OAM配置文件读取
* @param neType 网元类型
* @param neId 网元ID
* @returns object
*/
export function changeNeCore(data: Record<string, any>) {
export function getOAMFile(neType: string, neId: string) {
return request({
url: `/ne/info/core`,
method: 'PUT',
data: data,
url: '/ne/info/oamFile',
method: 'get',
params: { neType, neId },
});
}
/**
* 网元端配置文件写入
* @param neType 网元类型
* @param neId 网元ID
* @param content 用json对象
* @param sync 同步到网元
* @returns object
*/
export function saveOAMFile(data: Record<string, any>) {
return request({
url: `/ne/info/oamFile`,
method: 'put',
data: data,
timeout: 60_000,
});
}
/**
* 网元端公共配置文件读取
* @returns object
*/
export function getPara5GFilee() {
return request({
url: '/ne/info/para5GFile',
method: 'get',
});
}
/**
* 网元端公共配置文件写入
* @param content txt内容为字符串 其他文件格式都用json对象
* @param syncNe 同步到网元端 NeType@ NeId
* @returns object
*/
export function savePara5GFile(data: Record<string, any>) {
return request({
url: `/ne/info/para5GFile`,
method: 'put',
data: data,
timeout: 60_000,
});
}
/**
* 网元服务操作
* @param data 对象 {neType,neId,action}
* @returns object
*/
export function serviceNeAction(data: Record<string, any>) {
return request({
url: `/ne/action/service`,
method: 'put',
data: data,
timeout: 60_000,
});
}

Some files were not shown because too many files have changed in this diff Show More