391 Commits

Author SHA1 Message Date
TsMask
c1187383b6 fix: 系统引导跳转不重复引导 2024-08-03 10:09:16 +08:00
lai
8d8605e0cd 调整网元配置模块 2024-08-02 18:55:53 +08:00
TsMask
8af48936e5 chore: 更新版本号 2.240801 2024-08-02 10:11:42 +08:00
TsMask
e21a8dc898 perf: 优化快速安装配置公共参数页面 2024-08-01 17:34:16 +08:00
TsMask
3c1ee63359 fix: 开站网元安装信息保存触发操作不一致 2024-07-30 18:23:51 +08:00
TsMask
bc3940016a perf: 网元公共参数合并到网元快速安装,移除相关多语言翻译 2024-07-30 18:22:32 +08:00
TsMask
6ab4fbea6f fix: UDM签约数据cnType改5GC Flag 2024-07-30 11:27:12 +08:00
TsMask
c73fcbd91c chore: 更新版本号 2.240729 2024-07-29 18:27:15 +08:00
TsMask
023e317b0d feat: 网元配置备份操作权限控制删除/编辑按钮 2024-07-29 14:36:19 +08:00
lai
543a2b7434 新增 统一配置网元 2024-07-29 10:38:14 +08:00
TsMask
4ae9051411 style: 系统设置LOGO和文档判断是否开启语言切换显示控件 2024-07-27 15:01:56 +08:00
TsMask
532546c3f5 chore: 更新版本号 2.240727 2024-07-27 10:28:23 +08:00
TsMask
ab0d26513c perf: 网元备份导入导出弹窗表单重构 2024-07-26 18:27:15 +08:00
TsMask
8e9498ec83 feat: 网元备份文件表格页面 2024-07-26 18:25:15 +08:00
TsMask
ca048f223c feat: UDM鉴权文件导入K4文件支持 2024-07-26 15:28:45 +08:00
TsMask
e9ff6493dd fix: 看板UPF流量吞吐初始查询和ws监听12_neId 2024-07-25 18:48:01 +08:00
TsMask
396fb0b124 fix: KPI接收指定对应neId,实时数据不支持搜索 2024-07-25 18:23:28 +08:00
TsMask
3a854be8fe fix: 查询IMS在线用户数接口数据格式兼容 2024-07-25 10:50:54 +08:00
TsMask
c1f34f56ac fix: 对使用手册,官网进行限制 2024-07-24 18:10:38 +08:00
lai
b80bae0126 Merge branch 'main' of http://192.168.2.166:3180/OMC/ems_frontend_vue3 2024-07-24 17:18:18 +08:00
lai
6c383b58c8 对使用手册,官网进行限制 2024-07-24 17:18:15 +08:00
TsMask
06db1344c3 fix: 日志事件cm结果多语言显示错误 2024-07-23 14:48:53 +08:00
TsMask
fac47279b5 fix: 日志IMS-CDR类型查询默认空进行全查询,表格宽度增加 2024-07-23 14:26:09 +08:00
TsMask
a36a12f783 fix: 日志事件类型查询默认空进行全查询 2024-07-23 14:25:08 +08:00
TsMask
acd19ebdbd style: 看板基站数量分开展示避免分不清数 2024-07-19 14:41:40 +08:00
TsMask
d53c4c34f0 style: 新增网元信息时ssh默认密码初始 2024-07-18 15:44:35 +08:00
TsMask
2d9011cf6b fix: 看板资源图无网元状体数据取值异常 2024-07-17 18:11:22 +08:00
TsMask
fcf53d0995 fix: 网元删除记录自动移除不要刷新列表 2024-07-17 18:06:59 +08:00
TsMask
a2d93ddafe fix: 系统名称横向滚动衔接动画 2024-07-16 11:21:16 +08:00
TsMask
79920542c1 chore: 更新版本号240712 2024-07-12 21:23:12 +08:00
TsMask
6efd9cb61a fix: 根据浏览器地址栏hostname加33030端口连接后端服务 2024-07-11 17:56:24 +08:00
TsMask
4dc6699974 style: UDM用户数据限制imsi长度输入 2024-07-11 15:02:53 +08:00
TsMask
3bdae264e1 fix: 网元信息单行更新局部信息变更失效 2024-07-10 15:47:28 +08:00
TsMask
9e0a99d160 style: 数据CDR的表头宽度调整 2024-07-09 19:02:54 +08:00
TsMask
5ac0ca41eb style: 签约用户IMSI编辑时禁止修改,cnType添加提示 2024-07-09 18:36:27 +08:00
TsMask
a7de701d4d fix: 支持kvdb依赖包安装,db_ip默认0.0.0.0 2024-07-09 16:11:58 +08:00
TsMask
f0561242ca fix: fetch请求拼接地址栏参数不区分请求方法 2024-07-09 10:04:46 +08:00
TsMask
6102972373 fix: 网元公共配置OMC的IP去除覆盖 2024-07-05 14:42:45 +08:00
TsMask
d095531952 style: 系统角色分配标题多语言翻译 2024-07-04 17:02:33 +08:00
TsMask
d49dc9ebfd chore: 更新版本号240704 2024-07-04 10:24:01 +08:00
TsMask
43a99e9328 fix: 系统管理员角色用system表示避免admin混用 2024-07-03 15:13:41 +08:00
TsMask
d333221620 style: 在线用户列表文字靠左对齐 2024-07-03 14:50:25 +08:00
TsMask
481734cfe1 fix: 看板MME资源内存溢出100% 2024-07-03 14:49:11 +08:00
TsMask
1fa8601675 fix: UDM Object下拉数据显示错误 2024-07-01 16:02:12 +08:00
TsMask
c1aabdbe42 fix: 网元信息新增时刷新列表,编辑时局部更新信息 2024-06-28 14:55:40 +08:00
TsMask
953a36f142 style: 网元软件上传按钮文本多语言处理 2024-06-28 12:01:07 +08:00
TsMask
c5f62e8d76 fix: 网元信息新增OMC配置应该没有telnet 2024-06-28 11:11:41 +08:00
TsMask
946139facb fix: 用户无操作一段时间后进行锁屏 2024-06-27 20:46:58 +08:00
TsMask
95931b2b6e fix: 用户无操作一段时间后进行锁屏 2024-06-27 20:45:26 +08:00
TsMask
e31c85835d chore: 更新版本号240627 2024-06-27 18:32:02 +08:00
TsMask
847bae4c77 fix: SMF UE在线信息兼容旧数据 2024-06-27 18:31:22 +08:00
TsMask
e69f43a0c2 style: SMFCDR多语言smfSubscriptionID订阅 ID 2024-06-27 18:02:16 +08:00
TsMask
d96e1ad259 fix: SMF UE在线信息固定翻页50条 2024-06-27 17:28:23 +08:00
TsMask
3829d339e7 fix: SMF UE在线信息固定翻页50条 2024-06-27 17:10:30 +08:00
TsMask
cd4073bec3 fix: 网元主机信息默认填写固定用户名 2024-06-27 14:59:45 +08:00
TsMask
e67126c57a style: 调整SMFCDR输入框lg:6 2024-06-27 14:58:41 +08:00
TsMask
2f651b5d1f style: 快速开站系统admin密码默认Abcd@1234.. 2024-06-26 21:09:30 +08:00
TsMask
525854604f fix: 网元信息新增OMC配置应该没有telnet 2024-06-26 16:48:46 +08:00
TsMask
d42a8701da style: 参数配置规则校验示例写法 2024-06-26 16:47:45 +08:00
TsMask
2b680d6d20 fix: 参数配置可选项按id升序 2024-06-26 11:59:00 +08:00
TsMask
de79760a0e chore: 更新版本号240626 2024-06-26 10:50:19 +08:00
TsMask
a5b5269b91 chore: 更新依赖版本 2024-06-26 10:45:06 +08:00
TsMask
8a2d0ccfa1 style: 调整网元软件批量上传框标题统一为上传软件 2024-06-26 10:44:32 +08:00
TsMask
650b02dc30 style: 调整网元软件单上传框宽度650 2024-06-26 10:43:50 +08:00
TsMask
b42d8cb370 style: 多语言翻译网元版本Update Softwares改成Batch Upload 2024-06-26 10:43:12 +08:00
TsMask
a213be0d64 fix: 网元软件多文件上传文件删除副本记录 2024-06-25 17:54:50 +08:00
TsMask
2a2b441e09 fix: 系统重置框倒计时无变化 2024-06-25 17:53:45 +08:00
TsMask
84bdb44286 fix: 网元操作接口超时时间60s 2024-06-25 15:37:58 +08:00
TsMask
06b6175a76 fix: 用户管理刷新不应重置页数1 2024-06-24 17:11:49 +08:00
TsMask
11b790e140 fix: 告警帮助文档高度调整88vh 2024-06-24 10:12:32 +08:00
TsMask
296a9ba02b feat: UDM签约数据添加CN Type可选类型 2024-06-21 20:49:34 +08:00
TsMask
1801a46396 style:: 操作日志多语言变更 2024-06-20 14:45:33 +08:00
TsMask
f6644a0d97 style: 全局配置消息距离顶部的位置 2024-06-20 14:41:02 +08:00
TsMask
57f575aa84 fix: 用户账号规则修改允许数字开头至少6位 2024-06-20 10:16:04 +08:00
TsMask
68cbbe7133 chore: 更新版本号240619 2024-06-19 17:58:17 +08:00
TsMask
c11d814312 fix: 模态框的footer底部按钮null会显示的处理 2024-06-19 17:29:59 +08:00
TsMask
e1548d2c98 fix: UE事件MME 看板推送显示/类型结果保持和AMF一致 2024-06-19 16:57:07 +08:00
TsMask
1ec374eb26 style: UE在线数据临时模拟数据 2024-06-19 16:55:02 +08:00
TsMask
0955a79965 style: 移除无用console输出 2024-06-19 14:51:28 +08:00
TsMask
374a9bde7e fix: 用户管理不能改自己状态,权限控制分配岗位 2024-06-19 14:02:24 +08:00
TsMask
639a4f0b1f feat: 用户数据保存userId信息做判断 2024-06-19 12:03:02 +08:00
TsMask
d1b9d7b9ba style: 网元公共参数同步窗口最小高度不设置 2024-06-19 12:01:10 +08:00
TsMask
3298203f60 fix: 用户部门默认选取自身拥有的 2024-06-18 19:12:44 +08:00
TsMask
8f3cce52c0 fix: UDM鉴权重载提示不关闭 2024-06-18 15:41:47 +08:00
TsMask
ce06a88e89 fix: 主页饼图颜色显示问题 2024-06-18 14:30:53 +08:00
TsMask
749a04972d style: UDM签约用户编辑框top0 2024-06-18 11:01:17 +08:00
TsMask
a311f0a09b fix: 移除拖拽组件,全局注册ProModal替换默认AModal 2024-06-18 10:26:38 +08:00
TsMask
c74d311537 chore: 升级依赖库 2024-06-18 10:24:12 +08:00
TsMask
1492f18791 style: 字典类型数据结构体变更 2024-06-18 10:23:09 +08:00
TsMask
cbc81643a5 chore: 更新版本号240617 2024-06-17 17:43:37 +08:00
TsMask
c18b557c97 fix: mocn页面引用函数异常导致编译错误 2024-06-17 17:42:48 +08:00
TsMask
b6bd9bc3d5 fix: 角色编辑修改框销毁后新建避免菜单勾选失败 2024-06-17 17:03:12 +08:00
TsMask
199607e322 style: 告警网元类型下拉自动提示输入 2024-06-17 11:35:11 +08:00
TsMask
ee10119d77 feat: 看板用户行为添加MME的UE事件 2024-06-17 11:21:44 +08:00
TsMask
1b1a56e49b feat: MME用户事件日志页面功能实现 2024-06-15 18:41:39 +08:00
TsMask
afdc188e17 fix: 调整tcpdump接口 2024-06-15 14:47:53 +08:00
TsMask
5de7d0a73f fix: 调整WS数据通道类型指定 2024-06-15 14:29:38 +08:00
TsMask
8f73d80a42 chore: 更新版本号240615 2024-06-15 10:41:01 +08:00
TsMask
5a85f245b0 style: 系统名称超出范围进行滚动 2024-06-15 10:37:55 +08:00
TsMask
ff600c49f6 chore: 编译类型错误提示 2024-06-14 19:00:38 +08:00
TsMask
4c7e99fdd7 fix: 引用UDM数据列表接口错误 2024-06-14 18:58:45 +08:00
TsMask
a6037a9737 del: 移除旧的UDM用户数据接口 2024-06-14 17:22:45 +08:00
TsMask
1d55c092b1 feat: UDM用户数据接口调整,页面优化加载等待 2024-06-14 17:02:33 +08:00
TsMask
8bdfd7ea28 style: 终端telnet编辑窗口单行120个字符 2024-06-14 16:46:51 +08:00
TsMask
39ad75fbd3 style: 多语言锁屏输入提示enUs 2024-06-14 14:33:29 +08:00
TsMask
7ef775209d fix: 网元信息更新记录显示状态变更,不刷新列表 2024-06-13 14:36:07 +08:00
TsMask
a8e0f36562 fix: UDM签约数据Template输入长度16改为50 2024-06-12 14:42:16 +08:00
TsMask
be50fc9c5c style: 代码格式化 2024-06-12 14:36:16 +08:00
TsMask
375f236e32 feat: 重构锁屏功能 2024-06-12 14:35:07 +08:00
TsMask
c9eb0240d8 fix: 网元信息更新删除不刷新列表,进行单个更新并清除网元列表缓存 2024-06-12 10:16:35 +08:00
TsMask
b67d591d0a feat: 锁屏页面 2024-06-11 18:31:26 +08:00
TsMask
7275a87fba style: CDR页面多语言处理 2024-06-11 18:30:53 +08:00
TsMask
c103222b65 fix: 轻量化upf不需要配置pci和mac 2024-06-11 17:00:34 +08:00
TsMask
5133d09ec2 feat: SMF CDR数据列表展示和导出,未进行多语言处理 2024-06-11 16:23:57 +08:00
TsMask
d88cec34df chore: 更新版本号240611 2024-06-11 10:29:24 +08:00
TsMask
eadd4709ee fix: 开站网元信息无终端配置时不显示,没有则新建个默认的占位 2024-06-11 10:28:16 +08:00
TsMask
04cbdc6b11 fix: 网元信息多选删除id取值异常 2024-06-11 10:26:46 +08:00
TsMask
233496e184 feat: 网元数据CDR支持导出功能 2024-06-07 19:50:02 +08:00
TsMask
919f8ef2a5 Merge remote-tracking branch 'origin/main' into lichang 2024-06-07 10:35:40 +08:00
lai
8f8d056f45 smf cdr 2024-06-06 22:28:46 +08:00
lai
8fcd7974e4 与smf协商后 暂且不进行中英文翻译 2024-06-06 22:28:03 +08:00
TsMask
cb7fc418a5 fix: 网元版本OMC安装遮罩和提示多语言 2024-06-06 15:13:45 +08:00
TsMask
e6d4a898a8 fix: 开站系统logo类型选择回显 2024-06-06 14:34:34 +08:00
TsMask
be4fc896d7 style: 网元公共参数EMS->OMC 2024-06-06 11:51:49 +08:00
TsMask
e5a5ef6f96 style: 多语言-部门状态 2024-06-05 17:00:24 +08:00
TsMask
530662bf5d fix: 开站网元授权文件变更成功提示 2024-06-03 11:57:35 +08:00
TsMask
2e7514d3ca feat: 全局布局组件升级到3.3.5 2024-06-03 11:42:42 +08:00
TsMask
1db61a5d4e chore: 更新依赖版本 2024-06-03 11:41:04 +08:00
TsMask
618499e777 style: 网元公共参数EMS_IP标记必填红 2024-06-01 16:18:50 +08:00
TsMask
71c0306587 fix: 文件切片操作超时时间统一60s 2024-06-01 15:11:46 +08:00
TsMask
a3cd8f05fb style: 编译类型声明 2024-05-31 19:17:54 +08:00
TsMask
d8ca0ca5ef feat: 网元安装IMS预输入mf包参数 2024-05-31 19:06:42 +08:00
TsMask
da08e1cc5b style: 开战公共参数保存提示成功 2024-05-31 17:36:09 +08:00
TsMask
8e4c5d0b90 feat: 网元信息配置文件导入导出 2024-05-31 15:08:55 +08:00
TsMask
467c93f710 fix: 锁屏重启检查404时重试 2024-05-31 11:05:03 +08:00
TsMask
22ced8bdb2 fix: 网元公共参数初始数据组件回填,adb绑定0.00.0 2024-05-30 21:20:10 +08:00
TsMask
2ee26a6958 Merge remote-tracking branch 'origin/main' into lichang 2024-05-30 18:07:52 +08:00
TsMask
c63e892544 style: 系统操作日志去除请求方法列 2024-05-30 18:00:50 +08:00
TsMask
cce3088f73 Merge remote-tracking branch 'origin/main' into lichang 2024-05-30 17:55:22 +08:00
TsMask
2211896768 Merge remote-tracking branch 'origin/main' into lichang 2024-05-30 17:53:51 +08:00
TsMask
5bb3cd814c fix: 网元参数配置属性监听异常 2024-05-30 17:53:26 +08:00
lai
266d816a21 修改文件默认名 2024-05-30 17:49:39 +08:00
TsMask
90c64c029a Merge remote-tracking branch 'origin/main' into lichang 2024-05-30 17:29:45 +08:00
TsMask
d06e2da72e chore: 更新版本240530 2024-05-30 17:23:49 +08:00
TsMask
7714d506c4 feat: 网元参数配置smf选择upfId 2024-05-30 17:08:55 +08:00
lai
0b7a198235 Merge branch 'main' of http://192.168.2.166:3180/OMC/ems_frontend_vue3 2024-05-30 15:44:41 +08:00
TsMask
68362ba3e3 feat: 网元参数配置可收起左侧菜单 2024-05-30 15:41:17 +08:00
lai
a9633e652f Merge branch 'main' of http://192.168.2.166:3180/OMC/ems_frontend_vue3 2024-05-30 15:34:27 +08:00
TsMask
6592561bca style: 参数校验多语言翻译 2024-05-30 15:32:42 +08:00
lai
09e0353053 增加事件告警 2024-05-30 15:25:08 +08:00
lai
c1e95fd1e9 仪表盘去除事件告警 2024-05-30 14:49:45 +08:00
TsMask
89857e0c1b style: 开站页面样式居中 2024-05-30 11:38:36 +08:00
TsMask
7aa37ee330 chore: 更新版本号240530 2024-05-30 11:00:57 +08:00
TsMask
4c16888184 fix: 网元快速安装授权检查是否可用后在进行文件上传 2024-05-29 18:06:26 +08:00
TsMask
a61716d40d feat: 网元公共参数配置表单组件 2024-05-29 16:32:11 +08:00
TsMask
3f02cb628b feat: 网元软件包支持携带依赖进行记录 2024-05-28 15:31:41 +08:00
TsMask
22805924fb fix: 网元软件多上传同开站逻辑 2024-05-28 15:29:45 +08:00
TsMask
e00229e5ff revt: 回退网元软件包类型支持adb,mf,rtproxy 2024-05-27 18:49:41 +08:00
TsMask
939a235a87 style: 网元软件代码优化 2024-05-27 18:33:46 +08:00
TsMask
cd49162dfc fix: 网元安装去除默认agtuser输入 2024-05-27 18:31:33 +08:00
TsMask
b7f2df5d1c fix: 网元版本升级勾选逐个进行,多语言翻译 2024-05-27 18:28:15 +08:00
TsMask
8b24bc55b9 fix: 网元授权上传进行重启网元 2024-05-27 17:09:39 +08:00
TsMask
9f2b80718e fix: 网元软件包类型支持adb,mf,rtproxy 2024-05-27 17:08:56 +08:00
TsMask
38cb406687 chore: 更新版本号240524 2024-05-24 20:54:55 +08:00
TsMask
c25fe63a26 style: 开站安装关闭按钮禁用 2024-05-24 20:54:37 +08:00
TsMask
b84d6bb9fc fix: 开站网元信息删除无id触发undefined 2024-05-24 17:54:39 +08:00
TsMask
b325cde5c0 fix: 国际化切换选择控件根据配置隐藏 2024-05-24 17:53:36 +08:00
TsMask
34266da44e style: 移除无效引用 2024-05-24 17:42:28 +08:00
TsMask
d82d6b7b47 fix: 网元授权勾选重置 2024-05-24 17:41:47 +08:00
TsMask
b1c7a068cf perf: 网元授权勾选多上传更新 2024-05-24 17:41:08 +08:00
TsMask
c155160cf7 style: 开站网元安装多语言翻译 2024-05-24 16:21:44 +08:00
TsMask
2ae4559958 fix: 开站软件安装逐个进行避免dpkg look错误 2024-05-24 16:21:08 +08:00
TsMask
782283bba6 pref: 网元软件多上传选择弹窗组件 2024-05-24 16:19:53 +08:00
TsMask
1b50708786 fix: 移除无用loding和危险操作高亮红色 2024-05-24 16:18:52 +08:00
TsMask
206d3755a0 fix: 网元公共参数设置omcIP进行更新 2024-05-24 16:15:09 +08:00
TsMask
6470effbda style: 网元相关页面多语言翻译 2024-05-24 09:41:56 +08:00
TsMask
f94b1ef44a style: 快速开站多语言翻译 2024-05-24 09:41:20 +08:00
TsMask
7c78eab431 style: 快速开站多语言翻译 2024-05-21 17:10:45 +08:00
TsMask
814b7b0058 feat: 网元快速安装多语言翻译 2024-05-20 20:18:00 +08:00
TsMask
624f67ecca feat: 网元公共配置多语言翻译 2024-05-20 16:20:58 +08:00
TsMask
c1a9497b77 feat: 网元信息表单多语言翻译 2024-05-20 16:10:13 +08:00
TsMask
1ad2ed2d26 feat: 网元信息表单多语言翻译 2024-05-20 15:14:23 +08:00
TsMask
48f278997f feat: 网元信息OAM多语言翻译 2024-05-20 11:58:25 +08:00
TsMask
9b9d6222e1 feat: 网元信息多语言翻译 2024-05-20 10:56:55 +08:00
TsMask
2e8a878365 Merge remote-tracking branch 'origin/main' into lichang 2024-05-17 20:23:52 +08:00
TsMask
bc59dc67e6 style: IMS用户话单表格宽度 2024-05-17 18:15:16 +08:00
TsMask
df6022b4a8 Merge remote-tracking branch 'origin/main' into lichang 2024-05-17 18:11:53 +08:00
TsMask
f2c67d087b chore: 更新版本240517 2024-05-17 18:02:22 +08:00
TsMask
7a0298a419 feat: IMS用户话单支持号码查询 2024-05-17 18:00:56 +08:00
TsMask
b40e798090 feat: AMF用户事件支持IMSI查询 2024-05-17 18:00:28 +08:00
TsMask
d48d5d6c95 feat: 网元软件多语言翻译 2024-05-17 17:14:14 +08:00
TsMask
69f3347a4d feat: 网元版本多语言翻译 2024-05-17 16:04:44 +08:00
TsMask
85ae7dc5ea fix: 网元授权多语言翻译 2024-05-17 15:18:01 +08:00
TsMask
531cd6d03d fix: 开站网元信息telnet不显示 2024-05-17 11:32:30 +08:00
TsMask
9fd8c4597a fix: 开站网元信息telnet默认值 2024-05-17 11:06:19 +08:00
TsMask
7060a3d887 fix: 网元授权文件变更提供重启功能,排除OMC类型 2024-05-17 11:04:59 +08:00
TsMask
c12ffa583c fix: 开站时网元主机可不填备注 2024-05-16 19:21:06 +08:00
TsMask
05aabb8c61 chore: 更新版本号240516 2024-05-16 14:46:59 +08:00
TsMask
6b461ead7c fix: 网元信息编辑添加备注输入 2024-05-16 14:43:51 +08:00
TsMask
7c54e372e5 feat: 开站网元授权状态标签字典 2024-05-16 14:43:18 +08:00
TsMask
5be95a7af6 feat: 网元版本状态标签字典 2024-05-16 14:42:10 +08:00
TsMask
08515541b4 style: 网元公共参数配置初始值 2024-05-16 10:00:50 +08:00
TsMask
3948924f61 del: 删除旧系统页面 2024-05-15 19:21:49 +08:00
TsMask
ce1944881a style: 隐藏IP地址区域信息 2024-05-15 19:21:24 +08:00
lai
f6122c0758 调整为活动告警 2024-05-15 14:44:35 +08:00
lai
3d3f4c2cc6 看板微调 2024-05-15 14:43:07 +08:00
TsMask
e8e5b92a57 Merge remote-tracking branch 'origin/main' into lichang 2024-05-14 19:14:13 +08:00
TsMask
4ad704398d fix: 网元授权安装结果遍历message提示 2024-05-14 19:13:58 +08:00
TsMask
26289b7787 fix: 文件上传接口超时60s 2024-05-14 19:13:21 +08:00
TsMask
7ad8c1706e fix: 网元OAM端口输入限制5 2024-05-14 19:12:54 +08:00
TsMask
424a596613 fix: 看板定时器初始化前清除后设置 2024-05-14 19:12:16 +08:00
TsMask
6d5b9f417c fix: 网元特定排序添加SMSC 2024-05-14 17:24:50 +08:00
TsMask
7278e24dce Merge remote-tracking branch 'origin/main' into lichang 2024-05-14 15:02:11 +08:00
TsMask
76839ae18a fix: 系统重置操作等待遮罩 2024-05-14 15:01:30 +08:00
TsMask
c8a6aa3210 feat: 锁屏遮罩添加系统重置等待 2024-05-14 14:58:35 +08:00
TsMask
9b4bbcedc5 fix: 黄金指标图表项数据切换重复累积问题 2024-05-14 14:47:09 +08:00
TsMask
3b793c107c style: 角色键值输入提示变更多语言 2024-05-14 12:01:23 +08:00
lai
a076e6d079 疑似{}引发的问题 2024-05-14 10:02:43 +08:00
TsMask
fb2a7c51f9 Merge remote-tracking branch 'origin/main' into lichang 2024-05-13 09:41:19 +08:00
TsMask
1e2c2b5170 fix: 拓扑信息首个非OMC的异常显示问题 2024-05-11 18:03:49 +08:00
lai
56833b654e 更改提示语 2024-05-11 16:59:40 +08:00
lai
f50e02e6e7 为dnn,st添加星号 2024-05-11 16:58:22 +08:00
TsMask
52cc24813f Merge remote-tracking branch 'origin/main' into lichang 2024-05-11 16:42:13 +08:00
TsMask
c8a0d4c3f7 fix: 网元主机ssh私钥长度3000限制 2024-05-11 16:41:51 +08:00
TsMask
7560f21f11 style: 注释修改 2024-05-11 10:45:34 +08:00
TsMask
8a5f80fe47 feat: 网元配置OAM弹窗表单 2024-05-11 10:45:13 +08:00
TsMask
29eb1f08b1 del: 移除旧的参数配置页面 2024-05-10 14:54:36 +08:00
TsMask
9981de2271 del: 移除旧在线会话页面 2024-05-10 14:49:37 +08:00
TsMask
207acb3f3d fix: 网元信息IP地址输入校验 2024-05-10 14:43:57 +08:00
TsMask
873a76f48f fix: 开站排除omc类型选择 2024-05-10 14:42:26 +08:00
TsMask
4ca700092a fix: 登录后重定向页面 2024-05-10 14:19:30 +08:00
TsMask
ad9f8574bd del: 移除网元无用接口 2024-05-10 10:17:11 +08:00
TsMask
75faae6d7c Merge remote-tracking branch 'origin/main' into lichang 2024-05-09 19:26:20 +08:00
TsMask
c88c146a5e style: 网元版本页面多语言翻译 2024-05-09 19:25:14 +08:00
TsMask
c65cda1468 fix: 网元快速安装页面初始表单简化 2024-05-09 19:24:51 +08:00
TsMask
d8059341fe fix: 网元公共参数配置页面补充mme_ip 2024-05-09 19:22:03 +08:00
TsMask
ad86bee5f9 fix: 开站页面同步公共参数配置,优化页面样式 2024-05-09 19:18:21 +08:00
TsMask
f44cc44a1b style: 网元类型列表排序 2024-05-09 18:29:08 +08:00
TsMask
ad93588796 fix: 参数配置方法权限控制post,put,delete 2024-05-09 18:27:32 +08:00
TsMask
b5f6a5d24a feat: 网元公共参数配置数据名称调整 2024-05-09 15:54:48 +08:00
TsMask
fa4be253f7 fix: 系统配置重置操作多语言翻译 2024-05-08 16:09:39 +08:00
TsMask
2db0220c98 chore: 更新版本240508 2024-05-08 11:35:52 +08:00
TsMask
05fd678ce2 fix: SMF在线用户模拟数据变更/RAT Type文字换行 2024-05-08 11:35:33 +08:00
TsMask
165b28bab1 style: 开站页面样式调整 2024-05-08 11:05:35 +08:00
TsMask
dc67cdc262 fix: 看站系统配置新增管理员账号变更 2024-05-08 11:04:17 +08:00
TsMask
c97394a0ed fix: 看站完成确认提示 2024-05-08 11:03:03 +08:00
TsMask
6b9297d30e style: 看站开始页面样式调整 2024-05-08 11:02:30 +08:00
TsMask
ace7f26b53 style: 引导接口函数变更 2024-05-08 11:01:11 +08:00
TsMask
976f72f79a fix: 网元授权接口变更字段属性调整 2024-05-07 18:39:56 +08:00
TsMask
530e7fd43f feat: 系统引导重置操作 2024-05-07 16:28:10 +08:00
TsMask
b76fed7dcf feat: 系统引导使用接口变更 2024-05-07 16:27:45 +08:00
TsMask
60a26dd015 Merge remote-tracking branch 'origin/main' into lichang 2024-05-07 12:10:07 +08:00
TsMask
7bdfc257c1 chore: 依赖更新 2024-05-07 12:06:38 +08:00
TsMask
e3395b47cf feat: 更新xterm依赖库@xterm/xterm 2024-05-07 12:06:03 +08:00
TsMask
4c9b4de12f fix: 看板用户行为初始查询数据排序 2024-05-07 12:01:04 +08:00
lai
1cfe5e2777 主页饼图无感更新数据 2024-05-07 11:21:57 +08:00
TsMask
0cae26a1ee pref: 网元快速安装页面重构 2024-05-07 10:33:52 +08:00
TsMask
ed4d556384 pref: 网元授权页面的组件重构 2024-05-07 10:28:14 +08:00
TsMask
6d9d7362a4 fix: 网元公共配置参数文件页面 2024-05-07 09:59:42 +08:00
TsMask
5a40733f20 Merge remote-tracking branch 'origin/main' into lichang 2024-04-30 20:25:27 +08:00
TsMask
45ee884529 style: 查询网元信息接口函数命名 2024-04-30 20:08:55 +08:00
TsMask
7de3f7f05d fix: 网元软件上传窗口响应引用无效 2024-04-30 20:08:05 +08:00
TsMask
97bb4db1eb pref: 重构网元安装页面流程 2024-04-30 20:07:21 +08:00
TsMask
fc6dfe9894 style: 开站网元信息函数命名 2024-04-30 20:03:55 +08:00
TsMask
607660124a fix: 开站步骤hooks移除无用属性 2024-04-30 20:03:12 +08:00
TsMask
c50b3add95 feat: 网元安装授权页面 2024-04-30 20:02:24 +08:00
TsMask
2f7f0e9e20 feat: 网元安装配置页面 2024-04-30 20:00:56 +08:00
TsMask
6875ffd113 feat: 网元安装信息编辑页面 2024-04-30 19:59:45 +08:00
TsMask
44844bfa7f feat: 网元安装检查页面组件 2024-04-30 19:59:06 +08:00
TsMask
21ab066761 fix: 网元授权编辑弹出窗优化支持类型查询 2024-04-30 19:58:22 +08:00
TsMask
83bdf289fa feat: 网元信息免密直连授权操作 2024-04-30 19:56:17 +08:00
lai
d7aa0fc91a 更正-详情信息添加upState字段 2024-04-30 10:22:43 +08:00
lai
a6be5190ec 详情信息添加upState字段 2024-04-30 10:15:02 +08:00
TsMask
03cd06e835 style: 开站安装网元改为下一步 2024-04-29 18:18:11 +08:00
TsMask
2d7fe5a73b feat: 网元授权文件支持勾选指定网元类型替换 2024-04-29 18:17:45 +08:00
TsMask
dd5604c8b7 perf: 重构网元公共文件编辑参数 2024-04-29 18:16:39 +08:00
TsMask
7bf2672981 fix: 移除OAM编辑也慢慢 2024-04-29 18:15:26 +08:00
TsMask
6e616c63f0 fix: UDM签约数据sst-sd格式无sd也带- 2024-04-28 11:46:30 +08:00
TsMask
3ded481cce docs: 补充参数说明 2024-04-28 10:24:31 +08:00
TsMask
1baff582d8 feat: 网元授权编辑查看弹出窗口 2024-04-28 10:24:09 +08:00
TsMask
957868cc27 feat: 开站网元安装步骤进度的显示 2024-04-28 10:23:14 +08:00
TsMask
efb98ac577 feat: 补充授权文件按类型多上传 2024-04-28 10:22:14 +08:00
TsMask
ff228daae7 fix: UE SMF状态查询Up State可选 2024-04-26 19:21:44 +08:00
TsMask
c93e693517 fix: pcf新增编辑表单数据组装异常,上传超时30s 2024-04-26 17:13:07 +08:00
TsMask
55863c1c58 fix: pcf新增表单数据新增回显问题 2024-04-26 12:15:01 +08:00
lai
8a2d6c91ba 中英文翻译 2024-04-25 19:54:00 +08:00
lai
86a939f206 中英文翻译修正 2024-04-25 19:53:35 +08:00
TsMask
2b86f724b1 style: 系统菜单图标选择编号icon-001 2024-04-25 19:37:03 +08:00
TsMask
68fbc45b7c feat: 新增oam配置文件读写接口 2024-04-25 17:19:05 +08:00
TsMask
4629ef28ff fix: 接口变更 2024-04-25 17:18:46 +08:00
lai
e3a11b0ede 增加一个搜索字段 2024-04-25 17:17:02 +08:00
lai
3df9ee4f3a QOS=>Qos 2024-04-25 17:16:12 +08:00
lai
e64d558c27 批量新增 2024-04-25 17:10:22 +08:00
lai
5a2aa383fe 图标名字为icon001一直往下 2024-04-25 14:32:53 +08:00
lai
fe9f458465 pcf动态提示 2024-04-25 14:30:59 +08:00
TsMask
8e2e9aec67 style: 开站网元软件信息高度70vh 2024-04-25 14:22:09 +08:00
TsMask
ad48e8e2e1 feat: 网元软件安装页面 2024-04-25 14:21:35 +08:00
TsMask
6f8b1000ba feat: 开站网元公告参数配置表单填写 2024-04-25 14:21:08 +08:00
TsMask
f5549992c2 style: 网元公共参数表单名称修改 2024-04-25 14:19:29 +08:00
TsMask
19771ea6dc fix: 网元版本勾选升级关闭提示信息 2024-04-25 14:19:05 +08:00
TsMask
5965737384 feat: 网元软件包勾选删除记录 2024-04-25 14:17:59 +08:00
TsMask
b3cb40fd8c fix: 文件上传后关闭窗口复位上传结果 2024-04-25 14:16:41 +08:00
TsMask
0ab5141369 chore: 版本日期240425 2024-04-25 11:59:19 +08:00
TsMask
a23284da4c fix: 鉴权用户sst-sd输入限制1-3,sd补零 2024-04-25 11:56:41 +08:00
TsMask
1c8cc13436 style: PCF批量操作点击展开 2024-04-24 16:04:22 +08:00
TsMask
e6a439faea feat: 开站目录变更quick-start 2024-04-24 15:57:03 +08:00
TsMask
f8b1b1f6c3 Merge remote-tracking branch 'origin/main' into lichang 2024-04-24 15:40:48 +08:00
TsMask
3844e07258 feat: 开站目录变更quick-start 2024-04-24 15:34:07 +08:00
TsMask
1fab9b3d97 feat: 开站页面的部分修改 2024-04-24 15:30:38 +08:00
TsMask
1bfacbf2e0 feat: 开站系统信息页面逻辑对接 2024-04-24 15:29:51 +08:00
TsMask
c01f9f3830 feat: 开站网元信息页面逻辑对接 2024-04-24 15:29:29 +08:00
TsMask
28f9d365fd style: 网元新增编辑操作信息 2024-04-24 15:28:11 +08:00
TsMask
1085fa16aa feat: 初始访问时跳转开站引导页面 2024-04-24 15:27:23 +08:00
TsMask
e351960229 feat: 网元公共参数页面同步操作改弹出窗显示操作信息 2024-04-24 15:25:56 +08:00
TsMask
03caa354da feat: 开站开始页面获取接口授权 2024-04-24 15:24:45 +08:00
TsMask
34754ea0b6 feat: 开站引导开始完成接口 2024-04-24 15:24:00 +08:00
TsMask
86e1f07f08 feat: 开站结束页面标记完成 2024-04-24 15:22:27 +08:00
TsMask
2add7547d6 style: 删除console 2024-04-23 16:03:39 +08:00
TsMask
ecb89e9f26 feat: 用户管理和个人信息手机号输入改为国际电话输入 2024-04-23 14:56:35 +08:00
TsMask
7a9b38dc66 feat: 国际手机号输入框 2024-04-23 14:55:57 +08:00
TsMask
979b18092d fix: 个人信息手机号提示翻译出错 2024-04-23 14:53:37 +08:00
TsMask
ba43a647dd style: 查询年月日控件改为年月日时分秒 2024-04-22 17:17:29 +08:00
TsMask
3be14590c6 style: 告警alarmSeq改Sequence Number 2024-04-22 17:16:09 +08:00
TsMask
857f8c4313 feat: 系统首次使用引导页面 2024-04-19 19:54:07 +08:00
TsMask
c9c39d2d4a fix: 网元信息新增默认amf示例,安装默认y 2024-04-19 19:52:19 +08:00
TsMask
845084f77f feat: 系统首次使用引导标记 2024-04-19 19:51:01 +08:00
TsMask
becba4919e style: 多语言翻译 2024-04-19 17:22:37 +08:00
lai
b797055df9 KB转换成MB 2024-04-19 16:05:14 +08:00
TsMask
8101c852c8 style: 告警前传日志alarmSeq改Sequence 2024-04-19 14:07:35 +08:00
TsMask
9bbeb9fc9a fix: 锁屏reload延迟解除防止二次重启导致服务失败 2024-04-18 09:34:25 +08:00
TsMask
19a9bd3f5c fix: 锁屏reload不解除问题 2024-04-17 18:20:37 +08:00
TsMask
399bddc635 Merge remote-tracking branch 'origin/main' into lichang 2024-04-17 15:39:33 +08:00
TsMask
5a67a1a51c chore: 控制台版本号240418 2024-04-17 15:32:42 +08:00
TsMask
9a168d2ba0 fix: 登录页无网络隐藏多言选择 2024-04-17 15:32:07 +08:00
TsMask
e42a620aed feat: 快速开始建站页面 2024-04-17 15:07:02 +08:00
TsMask
eb902594b8 style: 网元菜单表格调整 2024-04-17 15:05:17 +08:00
TsMask
817fd97496 fix: 表字段列排序组件首位排序无效问题 2024-04-17 14:32:06 +08:00
TsMask
96aab47003 Merge remote-tracking branch 'origin/main' into lichang 2024-04-17 10:54:26 +08:00
TsMask
432666b1ab fix: 看板Hooks数据复位 2024-04-17 10:48:53 +08:00
TsMask
662583c73b fix: 拓扑组件监听窗口变化 2024-04-17 10:47:21 +08:00
TsMask
f73c21e6e6 fix: 页面卸载前判断是否断开ws 2024-04-17 10:45:45 +08:00
TsMask
17e72f1db1 fix: 岗位搜索标签positionCode显示不对 2024-04-17 09:47:45 +08:00
TsMask
55d4a36efb fix: 终端telnet支持调整窗口行宽 2024-04-16 19:49:44 +08:00
TsMask
d3794ba904 feat: 网元公共配置文件配置页面 2024-04-16 19:38:16 +08:00
TsMask
832f18e2ee style: 移动网元快速安装到ne目录 2024-04-16 19:37:37 +08:00
TsMask
cd2e282624 fix: 网元软件包新增去除防重复校验 2024-04-16 11:44:29 +08:00
TsMask
3db88f1d3a style: 网元授权信息去除斑马纹样式 2024-04-16 11:43:38 +08:00
TsMask
f291bb0907 feat: 软件版本勾选多升级 2024-04-16 11:42:45 +08:00
TsMask
69c32b2593 feat: 软件包多文件上传 2024-04-16 11:41:59 +08:00
TsMask
1a2939ab01 fix: 网元管理OMC禁止start stop,开启重启等待 2024-04-16 11:12:54 +08:00
TsMask
8bf4a2a9ce Merge remote-tracking branch 'origin/main' into lichang 2024-04-15 20:18:59 +08:00
TsMask
efd389f98a fix: 查询时间搜索值为时间戳 2024-04-15 20:17:39 +08:00
TsMask
f1125cc042 Merge remote-tracking branch 'origin/main' into lichang 2024-04-15 10:08:43 +08:00
TsMask
f1a0e200dc del: 移除表格斑马纹 2024-04-15 10:08:04 +08:00
TsMask
70c025f0f5 Merge remote-tracking branch 'origin/main' into lichang 2024-04-12 21:49:11 +08:00
TsMask
b9f0a3923d feat: 网元版本记录多勾选 2024-04-12 20:27:11 +08:00
TsMask
5c942c9ef7 style: 更新接口注释 2024-04-12 20:19:04 +08:00
TsMask
4294fe82c5 fix: 网元版本操作升级回退 2024-04-12 20:18:46 +08:00
TsMask
4e7fb90544 fix: 网元服务操作接口 2024-04-12 20:18:01 +08:00
TsMask
f050e500e9 fix: pcf页面添加分页支持勾选删除 2024-04-12 17:47:52 +08:00
TsMask
6bc10babba feat: 软件包上传组件 2024-04-12 17:43:26 +08:00
TsMask
4d36f9952a fix: 网元安装接口参数变更 2024-04-12 17:42:43 +08:00
lai
b03f4bb1d6 PCF策略中英文翻译 2024-04-12 10:35:53 +08:00
TsMask
4aaedf0f77 style: 网元安装授权检查接受提示 2024-04-12 10:23:48 +08:00
TsMask
c1ea851705 style: 网元信息修改弹出窗口改为异步组件 2024-04-12 10:23:12 +08:00
TsMask
47103249ef fix: 网元快速安装页面接口变更 2024-04-11 20:09:24 +08:00
TsMask
2642f18204 fix: 网元软件/版本/授权页面 2024-04-11 17:36:38 +08:00
TsMask
e2e22eabf5 style: 下拉触发改为点击显示 2024-04-11 17:36:07 +08:00
TsMask
325ecc8029 fix: 软件包表字段更新 2024-04-11 17:35:37 +08:00
TsMask
dc6f4560d5 feat: 快速安装页面 2024-04-11 17:35:19 +08:00
TsMask
3970797d5b feat: BA需要的mocn数据看板 2024-04-11 11:58:34 +08:00
TsMask
73776ed0f9 feat: kpi指标跳转指定网元类型 2024-04-11 11:54:48 +08:00
TsMask
aca842f8c2 feat: 参数配置access只读属性支持['read-only', 'read', 'ro'] 2024-04-11 11:53:17 +08:00
TsMask
612fb77861 fix: 网元管理修改网元类型属性绑定 2024-04-11 10:16:42 +08:00
TsMask
6ba695ceaf fix: 下拉展开取消悬浮,支持点击 2024-04-10 19:52:12 +08:00
TsMask
070f77d3b8 feat: 配置-网元管理添加Column settings 2024-04-10 19:38:14 +08:00
TsMask
15de63212f style: 网元管理-Province改为Region 2024-04-10 19:37:27 +08:00
lai
2202540763 更新用户策略提示信息 2024-04-10 19:00:56 +08:00
TsMask
c674b9b3f9 fix: KPI排除不可用网元类型的选择 2024-04-10 18:54:36 +08:00
TsMask
43d30e7bfa fix: 性能任务激活停止放出,编辑删除放到更多 2024-04-10 18:42:12 +08:00
TsMask
cd0633d519 fix: 性能任务激活停止放出,编辑删除放到更多 2024-04-10 18:40:56 +08:00
TsMask
b8bff2a159 style: 网元信息拓展信息显示横向排版 2024-04-10 16:50:56 +08:00
TsMask
638f890228 style: 移除多语言=暂无网元列表数据 2024-04-10 16:49:46 +08:00
TsMask
4460f7201c style: 网元配置类型可选择或输入 2024-04-10 16:47:40 +08:00
TsMask
d0bbfafedc fix: 系统日志的删除多语言提示 2024-04-09 19:41:12 +08:00
TsMask
16dc1c2e20 style: 中英文翻译父子联动->节点联动 2024-04-09 15:46:16 +08:00
TsMask
73ed5f5285 style: 网元新增初始参数 2024-04-09 15:39:04 +08:00
TsMask
50cb92a95b fix: 去除锁屏 2024-04-09 15:33:28 +08:00
TsMask
3888fd2bca fix: 软件管理下发激活回退关闭防重复提交判断 2024-04-09 15:25:34 +08:00
TsMask
4194d8cca6 fix: 用户昵称和手机号正则校验限长度 2024-04-08 15:03:53 +08:00
TsMask
ac84a0ca0a feat: 新增终端文本显示组件 2024-04-07 19:52:37 +08:00
202 changed files with 19611 additions and 12692 deletions

View File

@@ -5,13 +5,13 @@ VITE_HISTORY_HASH = false
VITE_HISTORY_BASE_URL = "/"
# 应用名称
VITE_APP_NAME = "Core Network EMS"
VITE_APP_NAME = "Core Network OMC"
# 应用标识
VITE_APP_CODE = "CN EMS"
VITE_APP_CODE = "OMC"
# 应用版本
VITE_APP_VERSION = "2.240330.1"
VITE_APP_VERSION = "2.240801"
# 接口基础URL地址-不带/后缀
VITE_API_BASE_URL = "/omc-api"

View File

@@ -5,13 +5,13 @@ VITE_HISTORY_HASH = true
VITE_HISTORY_BASE_URL = "/"
# 应用名称
VITE_APP_NAME = "Core Network EMS"
VITE_APP_NAME = "Core Network OMC"
# 应用标识
VITE_APP_CODE = "CN EMS"
VITE_APP_CODE = "OMC"
# 应用版本
VITE_APP_VERSION = "2.240330.1"
VITE_APP_VERSION = "2.240801"
# 接口基础URL地址-不带/后缀
VITE_API_BASE_URL = "/omc-api"

View File

@@ -2,7 +2,6 @@
## 简介
- 系统布局使用 [@ant-design-vue/pro-layout](https://github.com/vueComponent/pro-components)
- 图标来源 [@ant-design/icons-vue](https://ant.design/components/icon)
- 菜单图标使用自定义 iconfont `font_8d5l8fzk5b87iudi.js`图标文件
@@ -11,7 +10,7 @@
```text
Jenkins: http://192.168.2.166:3185/
Nginx: http://192.168.2.166:3188/#/index
后端暴露端口: http://192.168.2.166:3186 \ http://192.168.2.166:3187
后端暴露端口: http://192.168.2.166:33030
新网管192.168.5.13
@@ -73,10 +72,3 @@ https://192.168.5.27:31325/#/workloads?namespace=default
```text
eyJhbGciOiJSUzI1NiIsImtpZCI6ImZFVUhIb1puLW04M1dfSUYyRU8zWlZueXBpNUh4T0hTRVlzU19jNlVGQ0kifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLW44ZzRtIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI2M2NmYjAyNS01ZmQ0LTQ0ZTgtOTdiNC0yYWRiYWIxNzc5M2MiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.R3GRygFOjngTj-mEMBAHDeBxm3lpsXZYvC6cdTxByONtLrcMXDebwNVeKtAZ1V9qh2OrjD8n9CIygjULGPdfV6S520vjMh7Oa2q68nOyW49DNWQyYD8xLo-dQ6sX07fI7X_I3H35YUWW80jJAXjJawqIGXBSMG5intlo4tLTUSXmjCfhoQvFsgeRWu0j76pDvhMAvLPcgEXfTCi9tyL3yqJBIKONcKwmMlJeaKSR3pQk3KiibqrBO0MZclRozpke6J0ulfzTemwDDyCqBZmLsRPZ2yDd5hVBIJ9bHEcK0a25NmSFFzmd8XWQPZwg3Y4IbbY-8UhByGq0p9xS-7pGCQ
```
```ssh
# https://blog.csdn.net/m0_54706625/article/details/129721121
sudo chmod 700 -/.ssh/
sudo chmod 700 /home/mask/.ssh #这个尤其容易忽视掉,我就是从这个坑里爬出来。有木有很高兴呀!
sudo chmod 600 ~/.ssh/authorized_keys
```

View File

@@ -1,7 +1,7 @@
{
"name": "ems_frontend_vue3",
"type": "module",
"description": "核心网管理平台",
"description": "Core Network EMS",
"author": "TsMask",
"engines": {
"node": ">=18.0.0"
@@ -14,43 +14,45 @@
"dependencies": {
"@ant-design/icons-vue": "^7.0.1",
"@antv/g6": "~4.8.24",
"@codemirror/lang-javascript": "^6.2.1",
"@codemirror/lang-yaml": "^6.0.0",
"@codemirror/merge": "^6.6.1",
"@codemirror/lang-javascript": "^6.2.2",
"@codemirror/lang-yaml": "^6.1.1",
"@codemirror/merge": "^6.6.3",
"@codemirror/theme-one-dark": "^6.1.2",
"@tato30/vue-pdf": "^1.9.6",
"@vueuse/core": "~10.9.0",
"@tato30/vue-pdf": "~1.9.7",
"@vueuse/core": "~10.10.1",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"ant-design-vue": "^3.2.20",
"antdv-pro-layout": "^3.2.6",
"antdv-pro-layout": "~3.3.5",
"antdv-pro-modal": "^3.1.0",
"codemirror": "^6.0.1",
"dayjs": "^1.11.10",
"dayjs": "^1.11.11",
"echarts": "~5.5.0",
"file-saver": "^2.0.5",
"intl-tel-input": "~23.0.12",
"js-base64": "^3.7.7",
"js-cookie": "^3.0.5",
"localforage": "^1.10.0",
"nprogress": "^0.2.0",
"p-queue": "^8.0.1",
"p-queue": "~8.0.1",
"pinia": "^2.1.7",
"vue": "~3.3.13",
"vue-i18n": "~9.10.0",
"vue-router": "^4.2.5",
"vue-i18n": "^9.13.1",
"vue-router": "^4.4.0",
"vue3-smooth-dnd": "^0.0.6",
"xlsx": "^0.18.5",
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0"
"xlsx": "~0.18.5"
},
"devDependencies": {
"@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.0.4",
"@vitejs/plugin-vue": "^5.0.5",
"less": "^4.2.0",
"typescript": "^5.4.3",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.2.6",
"vite-plugin-compression": "^0.5.1",
"vue-tsc": "^1.8.27"
"typescript": "~5.4.5",
"unplugin-vue-components": "~0.26.0",
"vite": "~5.3.1",
"vite-plugin-compression": "~0.5.1",
"vue-tsc": "~2.0.22"
}
}

View File

@@ -11,8 +11,9 @@
*/
(function () {
// host = ip:port
const host = '192.168.8.100:33030';
// const host = '192.168.8.100:33030';
const host = `${window.location.hostname}:33030`;
// Service Address
sessionStorage.setItem('baseUrl', `http://${host}`);
// websocket Address

View File

@@ -4,6 +4,7 @@ import { usePrimaryColor } from '@/hooks/useTheme';
import zhCN from 'ant-design-vue/lib/locale/zh_CN';
import enUS from 'ant-design-vue/lib/locale/en_US';
import dayjs from 'dayjs';
import advancedFormat from 'dayjs/plugin/advancedFormat';
import 'dayjs/locale/zh-cn';
import { ref, watch } from 'vue';
import useAppStore from '@/store/modules/app';
@@ -11,6 +12,7 @@ import useI18n from '@/hooks/useI18n';
const { t, currentLocale } = useI18n();
const appStore = useAppStore();
dayjs.extend(advancedFormat);
dayjs.locale('zh-cn'); // 默认中文
usePrimaryColor(); // 载入用户自定义主题色
@@ -50,10 +52,11 @@ console.info(
</ConfigProvider>
</template>
<style>
<style lang="css">
#app {
height: 100%;
}
body .ant-pro-basicLayout {
display: flex;
flex-direction: column;
@@ -61,10 +64,6 @@ body .ant-pro-basicLayout {
min-height: 100vh;
}
.ant-pro-sider {
z-index: 20;
}
.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,

View File

@@ -18,7 +18,7 @@ export async function getParamConfigTopTab(neType: string) {
url: `/api/rest/databaseManagement/v1/elementType/omc_db/objectType/param_config`,
method: 'get',
params: {
SQL: `SELECT top_display,top_tag,method FROM param_config WHERE ne_type = '${neType}'`,
SQL: `SELECT id,top_display,top_tag,method FROM param_config WHERE ne_type = '${neType}' ORDER BY id ASC`,
},
});
// 解析数据
@@ -38,13 +38,13 @@ export async function getParamConfigTopTab(neType: string) {
}
/**
* 查询配置参数标签栏对应信息
* 查询配置参数标签栏对应信息和规则
* @param neType 网元类型
* @param topTag
* @param neId
* @returns object { wrRule, dataArr }
*/
async function getParamConfigInfo(
async function getParamConfigInfoAndRule(
neType: string,
topTag: string,
neId: string
@@ -57,7 +57,6 @@ async function getParamConfigInfo(
params: {
SQL: `SELECT param_json FROM param_config WHERE ne_type = '${neType}' AND top_tag='${topTag}'`,
},
timeout: 1_000,
}),
// 获取对应信息
request({
@@ -66,7 +65,6 @@ async function getParamConfigInfo(
params: {
ne_id: neId,
},
timeout: 1_000,
}),
]).then(resArr => {
let wrRule: Record<string, any> = {};
@@ -108,159 +106,6 @@ async function getParamConfigInfo(
});
}
/**
* 查询配置参数标签栏对应信息-表格处理
* @param neType 网元类型
* @param topTag
* @param neId
* @returns object
*/
export async function getParamConfigInfoTable(
neType: string,
topTag: string,
neId: string
) {
const { wrRule, dataArr } = await getParamConfigInfo(neType, topTag, neId);
// UPF参数不统一
// if (neType === 'UPF') {
// if (Reflect.has(wrRule, 'list')) {
// for (const arr of wrRule['list']) {
// arr['name'] = parseFirstLower(arr['name']);
// }
// for (const item of dataArr) {
// for (const k in item) {
// item[parseFirstLower(k)] = item[k];
// Reflect.deleteProperty(item, k);
// }
// }
// }
// if (Reflect.has(wrRule, 'array')) {
// for (const arr of wrRule['array']) {
// if (Array.isArray(arr['array'])) {
// for (const child of arr['array']) {
// child['name'] = parseFirstLower(child['name']);
// }
// }
// arr['name'] = parseFirstLower(arr['name']);
// }
// for (const item of dataArr) {
// for (const k in item) {
// // 处理子列表
// if (Array.isArray(item[k])) {
// for (const child of item[k]) {
// for (const childKey in child) {
// child[parseFirstLower(childKey)] = child[childKey];
// Reflect.deleteProperty(child, childKey);
// }
// }
// }
// item[parseFirstLower(k)] = item[k];
// Reflect.deleteProperty(item, k);
// }
// }
// }
// }
// 拼装数据
const result = {
code: RESULT_CODE_SUCCESS,
msg: RESULT_MSG_SUCCESS,
data: {
type: 'list' as 'list' | 'array',
data: [] as any[],
dataRule: {},
columns: [] as any[],
},
};
// kv单列表
if (Reflect.has(wrRule, 'list')) {
result.data.type = 'list';
const ruleArr = Object.freeze(wrRule['list']);
// 列表项数据
let dataList = [];
for (const item of dataArr) {
for (const key of Object.keys(item)) {
// 规则为准
for (const rule of ruleArr) {
if (rule['name'] === key) {
const ruleItem = Object.assign({ optional: 'true' }, rule, {
value: item[key],
});
dataList.push(ruleItem);
break;
}
}
}
}
result.data.data = dataList;
// 列表字段
result.data.columns = [
{
title: 'Key',
dataIndex: 'display',
align: 'left',
width: '30%',
},
{
title: 'Value',
dataIndex: 'value',
align: 'left',
width: '70%',
},
];
}
// 多列表
if (Reflect.has(wrRule, 'array')) {
result.data.type = 'array';
const ruleArr = Object.freeze(wrRule['array']);
// 列表项数据
const dataArray = [];
for (const item of dataArr) {
let record: Record<string, any> = {};
for (const key of Object.keys(item)) {
// 规则为准
for (const rule of ruleArr) {
if (rule['name'] === key) {
const ruleItem = Object.assign({ optional: 'true' }, rule, {
value: item[key],
});
record[ruleItem.name] = ruleItem;
break;
}
}
}
dataArray.push(record);
}
result.data.data = dataArray;
// 无数据时,用于新增
let dataRule: Record<string, any> = {};
for (const rule of ruleArr) {
dataRule[rule.name] = rule;
}
result.data.dataRule = dataRule;
// 列表字段
const columns: Record<string, any>[] = [];
for (const rule of ruleArr) {
columns.push({
title: rule.display,
dataIndex: rule.name,
align: 'left',
width: 5,
});
}
result.data.columns = columns;
}
return result;
}
/**
* 查询配置参数标签栏对应信息-表单结构处理
* @param neType 网元类型
@@ -273,7 +118,11 @@ export async function getParamConfigInfoForm(
topTag: string,
neId: string
) {
const { wrRule, dataArr } = await getParamConfigInfo(neType, topTag, neId);
const { wrRule, dataArr } = await getParamConfigInfoAndRule(
neType,
topTag,
neId
);
// 拼装数据
const result = {
@@ -340,6 +189,35 @@ export async function getParamConfigInfoForm(
return result;
}
/**
* 查询配置参数标签栏对应信息
* @param neType 网元类型
* @param topTag
* @param neId
* @returns object
*/
export async function getParamConfigInfo(
neType: string,
topTag: string,
neId: string
) {
// 发起请求
const result = await request({
url: `/api/rest/systemManagement/v1/elementType/${neType.toLowerCase()}/objectType/config/${topTag}`,
method: 'get',
params: {
ne_id: neId,
},
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
return Object.assign(result, {
data: parseObjLineToHump(result.data.data),
});
}
return result;
}
/**
* 查询配置参数标签栏对应信息子节点
* @param neType 网元类型
@@ -462,3 +340,144 @@ export async function updateNeConfigReload(neType: string, neId: string) {
}
return result;
}
/**
* 从参数配置PCF中获取对应信息提供给PCC用户策略输入框
* @param neType 网元类型
* @param topTag
* @param neId
* @returns object { wrRule, dataArr }
*/
export async function getPCCRule(neId: any) {
return await Promise.allSettled([
// 获取参数规则
request({
url: `/api/rest/systemManagement/v1/elementType/pcf/objectType/config/pccRules`,
method: 'get',
params: {
ne_id: neId,
},
timeout: 1_000,
}),
// 获取对应信息
request({
url: `/api/rest/systemManagement/v1/elementType/pcf/objectType/config/sessionRules`,
method: 'get',
params: {
ne_id: neId,
},
timeout: 1_000,
}),
request({
url: `/api/rest/systemManagement/v1/elementType/pcf/objectType/config/qosTemplate`,
method: 'get',
params: {
ne_id: neId,
},
timeout: 1_000,
}),
request({
url: `/api/rest/systemManagement/v1/elementType/pcf/objectType/config/headerEnrichTemplate`,
method: 'get',
params: {
ne_id: neId,
},
timeout: 1_000,
}),
request({
url: `/api/rest/systemManagement/v1/elementType/pcf/objectType/config/serviceAreaRestriction`,
method: 'get',
params: {
ne_id: neId,
},
timeout: 1_000,
}),
]).then(resArr => {
let pccJson: any = new Map();
let sessJson: any = new Map();
let qosJson: any = new Map();
let headerJson: any = new Map();
let sarJson: any = new Map();
// 规则数据
if (resArr[0].status === 'fulfilled') {
const itemV = resArr[0].value;
// 解析数据
if (
itemV.code === RESULT_CODE_SUCCESS &&
Array.isArray(itemV.data?.data)
) {
let itemData = itemV.data.data;
itemData.forEach((item: any) => {
pccJson.set(item.ruleId, { value: item.ruleId, label: item.ruleId });
});
}
}
if (resArr[1].status === 'fulfilled') {
const itemV = resArr[1].value;
// 解析数据
if (
itemV.code === RESULT_CODE_SUCCESS &&
Array.isArray(itemV.data?.data)
) {
let itemData = itemV.data.data;
itemData.forEach((item: any) => {
sessJson.set(item.ruleId, { value: item.ruleId, label: item.ruleId });
});
}
}
if (resArr[2].status === 'fulfilled') {
const itemV = resArr[2].value;
// 解析数据
if (
itemV.code === RESULT_CODE_SUCCESS &&
Array.isArray(itemV.data?.data)
) {
let itemData = itemV.data.data;
itemData.forEach((item: any) => {
qosJson.set(item.qosId, { value: item.qosId, label: item.qosId });
});
}
}
if (resArr[3].status === 'fulfilled') {
const itemV = resArr[3].value;
// 解析数据
if (
itemV.code === RESULT_CODE_SUCCESS &&
Array.isArray(itemV.data?.data)
) {
let itemData = itemV.data.data;
itemData.forEach((item: any) => {
headerJson.set(item.templateName, {
value: item.templateName,
label: item.templateName,
});
});
}
}
if (resArr[4].status === 'fulfilled') {
const itemV = resArr[4].value;
// 解析数据
if (
itemV.code === RESULT_CODE_SUCCESS &&
Array.isArray(itemV.data?.data)
) {
let itemData = itemV.data.data;
itemData.forEach((item: any) => {
sarJson.set(item.name, { value: item.name, label: item.name });
});
}
}
pccJson = Array.from(pccJson.values());
sessJson = Array.from(sessJson.values());
qosJson = Array.from(qosJson.values());
headerJson = Array.from(headerJson.values());
sarJson = Array.from(sarJson.values());
return { pccJson, sessJson, qosJson, headerJson, sarJson };
});
}

View File

@@ -19,7 +19,7 @@ export async function listLicense(query: Record<string, any>) {
// 分页
const pageNum = (query.pageNum - 1) * query.pageSize;
const limtSql = ` order by created_at desc limit ${pageNum},${query.pageSize} `;
const limtSql = ` order by create_time desc limit ${pageNum},${query.pageSize} `;
// 发起请求
const result = await request({

View File

@@ -5,6 +5,7 @@ import {
} 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';
/**
* 查询网元列表
@@ -15,24 +16,6 @@ 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 ';
// 系统特定顺序
const specificOrder = [
'OMC',
'MME',
'AMF',
'AUSF',
'UDM',
'SMF',
'PCF',
'UPF',
'NRF',
'NSSF',
'IMS',
'N3IWF',
'NEF',
'LMF',
];
// 查询
let querySQL = '';
if (query.neType) {
@@ -70,8 +53,8 @@ export async function listNeInfo(query: Record<string, any>) {
data.rows = itemData.map(v => parseObjLineToHump(v));
//通过sort进行冒泡排序
data.rows.sort((a: any, b: any) => {
const typeA = specificOrder.indexOf(a.neType);
const typeB = specificOrder.indexOf(b.neType);
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;
@@ -156,7 +139,7 @@ export async function delNeInfo(data: Record<string, any>) {
/**
* 导出网元配置文件
* @param
* @param data data {neType neId}
* @returns bolb
*/
export function exportSet(data: Record<string, any>) {

View File

@@ -111,6 +111,7 @@ export async function sendNeSoftware(data: Record<string, any>) {
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) {
@@ -130,6 +131,7 @@ export async function runNeSoftware(data: Record<string, any>) {
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) {
@@ -149,6 +151,7 @@ export async function backNeSoftware(data: Record<string, any>) {
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) {

View File

@@ -294,7 +294,7 @@ export async function exportAll(query: Record<string, any>) {
* @returns bolb
*/
export async function origGet() {
let totalSQL = `select count(*) as value,orig_severity as name from alarm group by orig_severity`;
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({
@@ -329,10 +329,10 @@ export async function origGet() {
* @returns object
*/
export async function top3Sel(filterFlag?: string) {
let filter = ` WHERE orig_severity='${filterFlag}'`;
if (!filterFlag) filter = '';
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} group by ne_type ORDER BY value desc limit 0,3 `;
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({

View File

@@ -0,0 +1,139 @@
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';
/**
* 查询列表
* @param query 查询参数
* @returns object
*/
export async function listAct(query: Record<string, any>) {
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) => {
console.log(item)
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;
}
/**
* 事件告警导出
* @param query 查询参数
* @returns bolb
*/
export async function exportAll(query: Record<string, any>) {
let rowsSQL = `select * from alarm_event where 1=1`;
// 查询
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 result = await request({
url: `/api/rest/databaseManagement/v1/select/omc_db/alarm_event`,
method: 'get',
params: {
rowsSQL: rowsSQL + querySQL,
},
});
if (result.code === RESULT_CODE_SUCCESS) {
let v = result.data.data[0];
const vArr = parseObjLineToHump(v['alarm_event']);
result.data = vArr == null ? [] : vArr;
}
return result;
}

View File

@@ -1,29 +1,14 @@
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 specificOrder = [
'OMC',
'MME',
'AMF',
'AUSF',
'UDM',
'SMF',
'PCF',
'UPF',
'NRF',
'NSSF',
'IMS',
'N3IWF',
'NEF',
'LMF',
];
const result = await request({
url: '/api/rest/systemManagement/v1/elementType/all/objectType/systemState',
method: 'get',
@@ -39,7 +24,6 @@ export async function listMain() {
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);
@@ -59,7 +43,7 @@ export async function listMain() {
ipAddress,
serialNum,
name: key.split('/').join('_'),
expiryDate:'-',
expiryDate: '-',
status: 'Abnormal',
};
}
@@ -67,8 +51,8 @@ export async function listMain() {
});
//通过sort进行冒泡排序
mergedData.sort((a: any, b: any) => {
const typeA = specificOrder.indexOf(a.name.split('_')[0]);
const typeB = specificOrder.indexOf(b.name.split('_')[0]);
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;
@@ -104,5 +88,6 @@ export function getSysConf() {
return request({
url: `/sys-conf`,
method: 'get',
whithToken: false,
});
}

View File

@@ -1,93 +0,0 @@
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 listSession(query: Record<string, any>) {
let totalSQL = 'select count(*) as total from session where 1=1 ';
let rowsSQL = 'select * from session where 1=1 ';
// 查询
let querySQL = '';
if (query.accountId) {
querySQL += ` and account_id like '%${query.accountId}%' `;
}
if (query.ip) {
querySQL += ` and host like '%${query.ip}%' `;
}
// 分页
const pageNum = (query.pageNum - 1) * query.pageSize;
const limtSql = ` limit ${pageNum},${query.pageSize} `;
// 排序
let sortSql = ' order by login_time ';
if (query.sortOrder === 'desc') {
sortSql += ' desc ';
} else {
sortSql += ' asc ';
}
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/omc_db/session`,
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['session'];
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 tokenId 授权标识
* @returns object
*/
export async function logoutSession(id: string) {
const result = await request({
url: `/api/rest/databaseManagement/v1/omc_db/session?WHERE=id='${id}'`,
method: 'delete',
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && result.data.data) {
let rows = result.data.data.affectedRows;
if (rows) {
delete result.data;
return result;
} else {
return { code: RESULT_CODE_ERROR, msg: RESULT_MSG_ERROR[language] };
}
}
return result;
}

View File

@@ -0,0 +1,83 @@
import { request } from '@/plugins/http-fetch';
/**
* 网元配置文件备份记录列表
* @param query 查询参数
* @returns object
*/
export function listNeConfigBackup(query: Record<string, any>) {
return request({
url: '/ne/config/backup/list',
method: 'get',
params: query,
});
}
/**
* 网元配置文件备份记录修改
* @param data 数据 { id, name, remark }
* @returns object
*/
export function updateNeConfigBackup(data: Record<string, any>) {
return request({
url: '/ne/config/backup',
method: 'put',
data: data,
});
}
/**
* 网元配置文件备份记录下载
* @param id 记录ID
* @returns object
*/
export async function downNeConfigBackup(id: string) {
return await request({
url: '/ne/config/backup/download',
method: 'get',
params: { id },
responseType: 'blob',
timeout: 180_000,
});
}
/**
* 网元配置文件备份记录删除
* @param id 记录ID
* @returns object
*/
export async function delNeConfigBackup(id: string) {
return request({
url: '/ne/config/backup',
method: 'delete',
params: { id },
});
}
/**
* 网元配置文件备份导出
* @param data 数据 { neType, neId }
* @returns object
*/
export function exportNeConfigBackup(data: Record<string, any>) {
return request({
url: '/ne/config/backup/export',
method: 'post',
data: data,
responseType: 'blob',
timeout: 180_000,
});
}
/**
* 网元配置文件备份导入
* @param data 数据 { neType, neId, type, path }
* @returns object
*/
export function importNeConfigBackup(data: Record<string, any>) {
return request({
url: '/ne/config/backup/import',
method: 'post',
data: data,
});
}

View File

@@ -67,7 +67,7 @@ export function delNeInfo(infoIds: string | number) {
/**
* 查询网元列表全部无分页
* @param query 查询参数 neType neId bandStatus
* @param query 查询参数 neType neId bandStatus bandHost
* @returns object
*/
export function listAllNeInfo(query: Record<string, any>) {
@@ -99,7 +99,7 @@ export function stateNeInfo(neType: string, neId: string) {
* @param neId 网元ID
* @returns object
*/
export function getTypeAndIDNeInfo(neType: string, neId: string) {
export function getNeInfoByTypeAndID(neType: string, neId: string) {
return request({
url: '/ne/info/byTypeAndID',
method: 'get',
@@ -108,29 +108,72 @@ export function getTypeAndIDNeInfo(neType: string, neId: string) {
}
/**
* 网元端配置文件读取
* 网元端OAM配置文件读取
* @param neType 网元类型
* @param neId 网元ID
* @param filePath 不带文件时重新覆盖返回目录列表
* @returns object
*/
export function getConfigFile(neType: string, neId: string, filePath: string) {
export function getOAMFile(neType: string, neId: string) {
return request({
url: '/ne/info/configFile',
url: '/ne/info/oamFile',
method: 'get',
params: { neType, neId, filePath },
params: { neType, neId },
});
}
/**
* 网元端配置文件写入
* @param data neType网元类型 neId网元ID filePath文件 content内容 sync同步到网元
* @param neType 网元类型
* @param neId 网元ID
* @param content 用json对象
* @param sync 同步到网元
* @returns object
*/
export function saveConfigFile(data: Record<string, any>) {
export function saveOAMFile(data: Record<string, any>) {
return request({
url: `/ne/info/configFile`,
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,
});
}

View File

@@ -27,41 +27,16 @@ export function getNeLicense(licenseId: string | number) {
}
/**
* 网元授权新增
* @param data 网元对象
* 网元neType和neID查询
* @param neType 网元类型
* @param neId 网元ID
* @returns object
*/
export function addNeLicense(data: Record<string, any>) {
export function getNeLicenseByTypeAndID(neType: string, neId: string) {
return request({
url: `/ne/license`,
method: 'post',
data: data,
});
}
/**
* 网元授权修改
* @param data 网元对象
* @returns object
*/
export function updateNeLicense(data: Record<string, any>) {
return request({
url: `/ne/license`,
method: 'put',
data: data,
});
}
/**
* 网元授权删除
* @param id 信息ID
* @returns object
*/
export function delNeLicense(licenseIds: string | number) {
return request({
url: `/ne/license/${licenseIds}`,
method: 'delete',
timeout: 60_000,
url: `/ne/license/byTypeAndID`,
method: 'get',
params: { neType, neId },
});
}
@@ -81,7 +56,7 @@ export function codeNeLicense(neType: string, neId: string) {
/**
* 网元授权激活授权文件替换
* @param data 网元对象
* @param data 网元对象 {"neType": "", "neId": "", "licensePath": "", "reload": true}
* @returns object
*/
export function changeNeLicense(data: Record<string, any>) {

View File

@@ -36,6 +36,7 @@ export function addNeSoftware(data: Record<string, any>) {
url: `/ne/software`,
method: 'post',
data: data,
repeatSubmit: false,
});
}
@@ -66,15 +67,14 @@ export function delNeSoftware(softwareIds: string | number) {
}
/**
* 网元软件包安装检查
* @param data 网元对象
* 网元软件包设为网元新版本
* @param data data { "version": "2.2404.18", "neType": "SMF", "name": "smf-r2.2404.18-ub22.deb"}
* @returns object
*/
export function checkInstallNeSoftware(data: Record<string, any>) {
export function newNeVersion(data: Record<string, any>) {
return request({
url: `/ne/software/checkInstall`,
url: `/ne/software/newNeVersion`,
method: 'post',
data: data,
timeout: 180_000,
});
}
}

View File

@@ -27,40 +27,15 @@ export function getNeVersion(versionId: string | number) {
}
/**
* 网元版本新增
* @param data 网元对象
* 网元版本操作
* @param data {neType,neId,preinput:{参数},action:"upgrade"}
* @returns object
*/
export function addNeVersion(data: Record<string, any>) {
export function operateNeVersion(data: Record<string, any>) {
return request({
url: `/ne/version`,
url: `/ne/version/operate`,
method: 'post',
data: data,
});
}
/**
* 网元版本修改
* @param data 网元对象
* @returns object
*/
export function updateNeVersion(data: Record<string, any>) {
return request({
url: `/ne/version`,
method: 'put',
data: data,
});
}
/**
* 网元版本删除
* @param id 信息ID
* @returns object
*/
export function delNeVersion(versionIds: string | number) {
return request({
url: `/ne/version/${versionIds}`,
method: 'delete',
timeout: 60_000,
timeout: 180_000,
});
}

View File

@@ -19,10 +19,24 @@ export function listAMFDataUE(query: Record<string, any>) {
* @returns object
*/
export function delAMFDataUE(ueIds: string | number) {
return request({
url: `/neData/amf/ue/${ueIds}`,
method: 'delete',
timeout: 60_000,
});
}
return request({
url: `/neData/amf/ue/${ueIds}`,
method: 'delete',
timeout: 60_000,
});
}
/**
* AMF-UE会话列表导出
* @param data 查询列表条件
* @returns object
*/
export function exportAMFDataUE(data: Record<string, any>) {
return request({
url: '/neData/amf/ue/export',
method: 'post',
data,
responseType: 'blob',
timeout: 60_000,
});
}

View File

@@ -19,10 +19,24 @@ export function listIMSDataCDR(query: Record<string, any>) {
* @returns object
*/
export function delIMSDataCDR(cdrIds: string | number) {
return request({
url: `/neData/ims/cdr/${cdrIds}`,
method: 'delete',
timeout: 60_000,
});
}
return request({
url: `/neData/ims/cdr/${cdrIds}`,
method: 'delete',
timeout: 60_000,
});
}
/**
* IMS-CDR会话列表导出
* @param data 查询列表条件
* @returns object
*/
export function exportIMSDataCDR(data: Record<string, any>) {
return request({
url: '/neData/ims/cdr/export',
method: 'post',
data,
responseType: 'blob',
timeout: 60_000,
});
}

42
src/api/neData/mme.ts Normal file
View File

@@ -0,0 +1,42 @@
import { request } from '@/plugins/http-fetch';
/**
* 查询MME-UE会话事件列表
* @param query 查询参数
* @returns object
*/
export function listMMEDataUE(query: Record<string, any>) {
return request({
url: '/neData/mme/ue/list',
method: 'get',
params: query,
});
}
/**
* MME-UE会话删除
* @param id 信息ID
* @returns object
*/
export function delMMEDataUE(ueIds: string | number) {
return request({
url: `/neData/mme/ue/${ueIds}`,
method: 'delete',
timeout: 60_000,
});
}
/**
* MME-UE会话列表导出
* @param data 查询列表条件
* @returns object
*/
export function exportMMEDataUE(data: Record<string, any>) {
return request({
url: '/neData/mme/ue/export',
method: 'post',
data,
responseType: 'blob',
timeout: 60_000,
});
}

42
src/api/neData/smf.ts Normal file
View File

@@ -0,0 +1,42 @@
import { request } from '@/plugins/http-fetch';
/**
* 查询SMF-CDR会话事件
* @param query 查询参数
* @returns object
*/
export function listSMFDataCDR(query: Record<string, any>) {
return request({
url: '/neData/smf/cdr/list',
method: 'get',
params: query,
});
}
/**
* SMF-CDR会话删除
* @param id 信息ID
* @returns object
*/
export function delSMFDataCDR(cdrIds: string | number) {
return request({
url: `/neData/smf/cdr/${cdrIds}`,
method: 'delete',
timeout: 60_000,
});
}
/**
* SMF-CDR会话列表导出
* @param data 查询列表条件
* @returns object
*/
export function exportSMFDataCDR(data: Record<string, any>) {
return request({
url: '/neData/smf/cdr/export',
method: 'post',
data,
responseType: 'blob',
timeout: 60_000,
});
}

141
src/api/neData/udm_auth.ts Normal file
View File

@@ -0,0 +1,141 @@
import { request } from '@/plugins/http-fetch';
/**
* UDM鉴权用户重载数据
* @param neId 网元ID
* @returns object
*/
export function resetUDMAuth(neId: string) {
return request({
url: `/neData/udm/auth/resetData/${neId}`,
method: 'put',
timeout: 180_000,
});
}
/**
* UDM鉴权用户列表
* @param query 查询参数
* @returns object
*/
export function listUDMAuth(query: Record<string, any>) {
return request({
url: '/neData/udm/auth/list',
method: 'get',
params: query,
});
}
/**
* UDM鉴权用户信息
* @param neId 网元ID
* @param imsi IMSI
* @returns object
*/
export function getUDMAuth(neId: string, imsi: string) {
return request({
url: `/neData/udm/auth/${neId}/${imsi}`,
method: 'get',
});
}
/**
* UDM鉴权用户新增
* @param data 鉴权对象
* @returns object
*/
export function addUDMAuth(data: Record<string, any>) {
return request({
url: `/neData/udm/auth/${data.neId}`,
method: 'post',
data: data,
timeout: 180_000,
});
}
/**
* UDM鉴权用户批量新增
* @param data 鉴权对象
* @param num 数量
* @returns object
*/
export function batchAddUDMAuth(data: Record<string, any>, num: number) {
return request({
url: `/neData/udm/auth/${data.neId}/${num}`,
method: 'post',
data: data,
timeout: 180_000,
});
}
/**
* UDM鉴权用户修改
* @param data 鉴权对象
* @returns object
*/
export function updateUDMAuth(data: Record<string, any>) {
return request({
url: `/neData/udm/auth/${data.neId}`,
method: 'put',
data: data,
timeout: 180_000,
});
}
/**
* UDM鉴权用户删除
* @param neId 网元ID
* @param imsi IMSI
* @returns object
*/
export function delUDMAuth(neId: string, imsi: string) {
return request({
url: `/neData/udm/auth/${neId}/${imsi}`,
method: 'delete',
timeout: 180_000,
});
}
/**
* UDM鉴权用户批量删除
* @param neId 网元ID
* @param imsi IMSI
* @param num 数量
* @returns object
*/
export function batchDelUDMAuth(neId: string, imsi: string, num: number) {
return request({
url: `/neData/udm/auth/${neId}/${imsi}/${num}`,
method: 'delete',
timeout: 180_000,
});
}
/**
* UDM鉴权用户导入
* @param data 表单数据对象
* @returns object
*/
export function importUDMAuth(data: Record<string, any>) {
return request({
url: `/neData/udm/auth/import`,
method: 'post',
data,
timeout: 180_000,
});
}
/**
* UDM鉴权用户导出
* @param data 数据参数
* @returns bolb
*/
export function exportUDMAuth(data: Record<string, any>) {
return request({
url: '/neData/udm/auth/export',
method: 'post',
data,
responseType: 'blob',
timeout: 180_000,
});
}

140
src/api/neData/udm_sub.ts Normal file
View File

@@ -0,0 +1,140 @@
import { request } from '@/plugins/http-fetch';
/**
* UDM签约用户重载数据
* @param neId 网元ID
* @returns object
*/
export function resetUDMSub(neId: string) {
return request({
url: `/neData/udm/sub/resetData/${neId}`,
method: 'put',
timeout: 180_000,
});
}
/**
* UDM签约用户列表
* @param query 查询参数
* @returns object
*/
export function listUDMSub(query: Record<string, any>) {
return request({
url: '/neData/udm/sub/list',
method: 'get',
params: query,
});
}
/**
* UDM签约用户信息
* @param neId 网元ID
* @param imsi IMSI
* @returns object
*/
export function getUDMSub(neId: string, imsi: string) {
return request({
url: `/neData/udm/sub/${neId}/${imsi}`,
method: 'get',
});
}
/**
* UDM签约用户新增
* @param data 签约对象
* @returns object
*/
export function addUDMSub(data: Record<string, any>) {
return request({
url: `/neData/udm/sub/${data.neId}`,
method: 'post',
data: data,
timeout: 180_000,
});
}
/**
* UDM签约用户批量新增
* @param data 签约对象
* @param num 数量
* @returns object
*/
export function batchAddUDMSub(data: Record<string, any>, num: number) {
return request({
url: `/neData/udm/sub/${data.neId}/${num}`,
method: 'post',
data: data,
timeout: 180_000,
});
}
/**
* UDM签约用户修改
* @param data 签约对象
* @returns object
*/
export function updateUDMSub(data: Record<string, any>) {
return request({
url: `/neData/udm/sub/${data.neId}`,
method: 'put',
data: data,
timeout: 180_000,
});
}
/**
* UDM签约用户删除
* @param data 签约对象
* @returns object
*/
export function delUDMSub(neId: string, imsi: string) {
return request({
url: `/neData/udm/sub/${neId}/${imsi}`,
method: 'delete',
timeout: 180_000,
});
}
/**
* UDM签约用户批量删除
* @param neId 网元ID
* @param imsi IMSI
* @param num 数量
* @returns object
*/
export function batchDelUDMSub(neId: string, imsi: string, num: number) {
return request({
url: `/neData/udm/sub/${neId}/${imsi}/${num}`,
method: 'delete',
timeout: 180_000,
});
}
/**
* UDM签约用户导出
* @param data 数据参数
* @returns bolb
*/
export function exportUDMSub(data: Record<string, any>) {
return request({
url: '/neData/udm/sub/export',
method: 'post',
data,
responseType: 'blob',
timeout: 180_000,
});
}
/**
* UDM签约用户导入
* @param data 表单数据对象
* @returns object
*/
export function importUDMSub(data: Record<string, any>) {
return request({
url: `/neData/udm/sub/import`,
method: 'post',
data,
timeout: 180_000,
});
}

View File

@@ -1,138 +0,0 @@
import { request } from '@/plugins/http-fetch';
/**
* 签约鉴权导出
* @param query 查询参数
* @returns bolb
*/
export function exportAuth(query: Record<string, any>) {
return request({
url: '/ne/udm/auth/export',
method: 'post',
data: query,
responseType: 'blob',
timeout: 180_000,
});
}
/**
* 导入鉴权数据
* @param neId 网元ID
* @param data 表单数据对象
* @returns object
*/
export function importAuthData(data: FormData) {
return request({
url: `/ne/udm/auth/import`,
method: 'post',
data,
dataType: 'form-data',
timeout: 180_000,
});
}
/**
* 查询鉴权列表
* @param query 查询参数
* @returns object
*/
export function listAuth(query: Record<string, any>) {
return request({
url: '/ne/udm/auth/list',
method: 'get',
params: query,
});
}
/**
* 查询重新更新加载全部
* @param neId 网元ID
* @returns object
*/
export function loadAuth(neId: string) {
return request({
url: `/ne/udm/auth/resetData/${neId}`,
method: 'put',
timeout: 180_000,
});
}
/**
* 查询鉴权详细
* @param neId 网元ID
* @returns object
*/
export function getAuth(neId: string, imsi: string) {
return request({
url: `/ne/udm/auth/${neId}/${imsi}`,
method: 'get',
});
}
/**
* 修改鉴权
* @param data 鉴权对象
* @returns object
*/
export function updateAuth(data: Record<string, any>) {
return request({
url: `/ne/udm/auth/${data.neId}`,
method: 'put',
data: data,
timeout: 180_000,
});
}
/**
* 新增鉴权
* @param data 鉴权对象
* @returns object
*/
export function addAuth(data: Record<string, any>) {
return request({
url: `/ne/udm/auth/${data.neId}`,
method: 'post',
data: data,
timeout: 180_000,
});
}
/**
* 批量新增鉴权
* @param data 鉴权对象
* @returns object
*/
export function batchAuth(data: Record<string, any>) {
return request({
url: `/ne/udm/auth/${data.neID}/${data.num}`,
method: 'post',
data: data,
timeout: 180_000,
});
}
/**
* 删除鉴权
* @param data 鉴权对象
* @returns object
*/
export function delAuth(neId: string, imsi: string) {
return request({
url: `/ne/udm/auth/${neId}/${imsi}`,
method: 'delete',
timeout: 180_000,
});
}
/**
* 批量删除鉴权
* @param data 鉴权对象
* @returns object
*/
export function batchDelAuth(data: Record<string, any>) {
return request({
url: `/ne/udm/auth/${data.neID}/${data.imsi}/${data.num}`,
method: 'delete',
timeout: 180_000,
});
}

View File

@@ -25,8 +25,10 @@ export async function listBase5G(query: Record<string, any>) {
data.total = rows.length;
data.rows = rows;
}
// 模拟数据
// data.rows =[{"address":"192.168.1.137:38412","id":"217","name":"attach-enb-100000-20","ueNum":0}]
// data.rows = [{"address":"192.168.1.137:38412","id":"217","name":"attach-enb-100000-20","ueNum":0}]
// data.rows = [{address: "192.168.8.223", id: 257, name: "SmallCell", ueNum: 0}]
return data;
}

View File

@@ -52,9 +52,11 @@ export async function listUENumByIMS(neId: String) {
method: 'get',
});
if (result.code === RESULT_CODE_SUCCESS) {
return Object.assign(result, {
data: result.data['ueNum'],
});
let num = result.data['ueNum'] || 0;
if (num === 0) {
num = result.data.data['ueNum'] || 0;
}
return Object.assign(result, { data: num });
}
// 模拟数据

View File

@@ -136,6 +136,7 @@ export async function batchUpdateRule(data: Record<string, any>) {
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/${data.num}?neId=${data.neId}`,
method: 'put',
data: data,
timeout: 60_000,
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
@@ -164,6 +165,7 @@ export async function addRule(data: Record<string, any>) {
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo?neId=${data.neId}`,
method: 'post',
data: data,
timeout: 60_000,
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && result.data?.status) {
@@ -186,6 +188,7 @@ export async function batchAddRule(data: Record<string, any>) {
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/${data.num}?neId=${data.neId}`,
method: 'post',
data: data,
timeout: 60_000,
});
// 解析数据
if (result.code === RESULT_CODE_SUCCESS) {
@@ -209,10 +212,11 @@ export async function batchAddRule(data: Record<string, any>) {
* @param data 规则对象
* @returns object
*/
export function delRule(neId: string, data: Record<string, any>) {
export function delRule(neId: string, imsi: string) {
return request({
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo?neId=${neId}&imsi=${data.imsi}`,
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo?neId=${neId}&imsi=${imsi}`,
method: 'delete',
timeout: 60_000,
});
}
@@ -225,5 +229,6 @@ export async function batchDelRule(data: Record<string, any>) {
return request({
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/${data.num}?neId=${data.neId}&imsi=${data.imsi}`,
method: 'delete',
timeout: 60_000,
});
}

View File

@@ -1,6 +1,5 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
/**
* 查询列表
@@ -21,39 +20,66 @@ export async function listUEInfoBySMF(query: Record<string, any>) {
msg: result.msg,
};
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
const rows = parseObjLineToHump(result.data.data);
data.total = rows.length;
data.rows = rows;
if (result.code === RESULT_CODE_SUCCESS && result.data) {
if (Array.isArray(result.data.data)) {
const rows = result.data.data;
data.total = rows.length;
data.rows = rows;
} else {
Object.assign(data, result.data);
}
}
// 模拟数据
// data.code = RESULT_CODE_SUCCESS;
// data.total = 2;
// data.rows = [
// {
// imsi: 'imsi-460029004200044',
// msisdn: 'msisdn-12346002044',
// imsi: 'imsi-460000100000090',
// msisdn: 'msisdn-12307550090',
// pduSessionInfo: [
// {
// activeTime: '2023-11-29 18:39:06',
// activeTime: '2024-06-19 14:35:26',
// dnn: 'ims',
// ipv4: '10.10.48.97',
// ipv4: '10.10.48.8',
// ipv6: '',
// pduSessionID: 6,
// ranN3IP: '192.168.8.223',
// ranN3IP: '192.168.1.137',
// sstSD: '1-000001',
// tai: '46000-0001',
// tai: '46000-001124',
// upState: 'Active',
// upfN3IP: '192.168.1.161',
// },
// {
// activeTime: '2023-11-29 18:39:05',
// activeTime: '2024-06-19 14:35:26',
// dnn: 'cmnet',
// ipv4: '10.10.48.62',
// ipv4: '10.10.48.9',
// ipv6: '2001:4860:4860::/64',
// pduSessionID: 7,
// ranN3IP: '192.168.1.137',
// sstSD: '1-000001',
// tai: '46000-001124',
// upState: 'Active',
// upfN3IP: '192.168.1.161',
// },
// ],
// ratType: 'NR',
// },
// {
// imsi: 'imsi-460602072701180',
// msisdn: 'msisdn-123460600080',
// pduSessionInfo: [
// {
// activeTime: '2024-06-19 14:31:09',
// dnn: 'cmnet',
// ipv4: '10.10.48.4',
// ipv6: '',
// pduSessionID: 5,
// ranN3IP: '192.168.8.223',
// sstSD: '1-000001',
// tai: '46000-0001',
// upfN3IP: '192.168.1.163',
// tai: '46060-0001',
// upState: 'Active',
// upfN3IP: '192.168.1.161',
// },
// ],
// ratType: 'EUTRAN',

View File

@@ -1,139 +0,0 @@
import { request } from '@/plugins/http-fetch';
/**
* 签约列表导出
* @param query 查询参数
* @returns bolb
*/
export function exportSub(query: Record<string, any>) {
return request({
url: '/ne/udm/sub/export',
method: 'post',
data: query,
responseType: 'blob',
timeout: 180_000,
});
}
/**
* 导入签约数据
* @param neId 网元ID
* @param data 表单数据对象
* @returns object
*/
export function importSubData(data: FormData) {
return request({
url: `/ne/udm/sub/import`,
method: 'post',
data,
dataType: 'form-data',
timeout: 180_000,
});
}
/**
* 查询重新更新加载全部
* @param neId 网元ID
* @returns object
*/
export function loadSub(neId: string) {
return request({
url: `/ne/udm/sub/resetData/${neId}`,
method: 'put',
timeout: 180_000,
});
}
/**
* 查询签约列表
* @param query 查询参数
* @returns object
*/
export function listSub(query: Record<string, any>) {
return request({
url: '/ne/udm/sub/list',
method: 'get',
params: query,
});
}
/**
* 查询签约详细
* @param neId 网元ID
* @returns object
*/
export function getSub(neId: string, imsi: string) {
return request({
url: `/ne/udm/sub/${neId}/${imsi}`,
method: 'get',
});
}
/**
* 修改签约
* @param data 签约对象
* @param neId 网元ID
* @returns object
*/
export function updateSub(neId: string, data: Record<string, any>) {
return request({
url: `/ne/udm/sub/${neId}`,
method: 'put',
data: data,
timeout: 180_000,
});
}
/**
* 新增签约
* @param data 签约对象
* @returns object
*/
export function addSub(neID: string, data: Record<string, any>) {
return request({
url: `/ne/udm/sub/${neID}`,
method: 'post',
data: data,
timeout: 180_000,
});
}
/**
* 批量新增新增签约
* @param data 签约对象
* @returns object
*/
export function batchAddSub(data: Record<string, any>) {
return request({
url: `/ne/udm/sub/${data.neID}/${data.num}`,
method: 'post',
data: data,
timeout: 180_000,
});
}
/**
* 删除签约
* @param data 签约对象
* @returns object
*/
export function delSub(neId: string, imsi: string) {
return request({
url: `/ne/udm/sub/${neId}/${imsi}`,
method: 'delete',
timeout: 180_000,
});
}
/**
* 批量删除签约
* @param data 签约对象
* @returns object
*/
export function batchDelSub(data: Record<string, any>) {
return request({
url: `/ne/udm/sub/${data.neID}/${data.imsi}/${data.num}`,
method: 'delete',
timeout: 180_000,
});
}

View File

@@ -0,0 +1,52 @@
import { request } from '@/plugins/http-fetch';
/**
* 首次引导开始
* @returns object
*/
export function bootloaderStart() {
return request({
url: `/bootloader`,
method: 'post',
whithToken: false,
repeatSubmit: false,
});
}
/**
* 首次引导完成
* @returns object
*/
export function bootloaderDone() {
return request({
url: `/bootloader`,
method: 'put',
});
}
/**
* 引导系统数据重置
* @returns object
*/
export function bootloaderReset() {
return request({
url: `/bootloader`,
method: 'delete',
timeout: 180_000
});
}
/**
* 管理员账号变更
* @returns object
*/
export function bootloaderAccount(username: string, password: string) {
return request({
url: `/bootloader/account`,
method: 'put',
data: {
username,
password,
},
});
}

View File

@@ -18,6 +18,7 @@ export async function downloadFile(filePath: string, range?: string) {
method: 'get',
headers: range ? { range } : {},
responseType: 'blob',
timeout: 60_000,
});
}
@@ -79,6 +80,7 @@ export function uploadFile(data: FormData) {
method: 'post',
data,
dataType: 'form-data',
timeout: 180_000,
});
}
@@ -169,6 +171,7 @@ export function chunkCheck(identifier: string, fileName: string) {
url: '/file/chunkCheck',
method: 'post',
data: { identifier, fileName },
timeout: 60_000,
});
}
@@ -188,6 +191,7 @@ export function chunkMerge(
url: '/file/chunkMerge',
method: 'post',
data: { identifier, fileName, subPath },
timeout: 60_000,
});
}
@@ -202,6 +206,7 @@ export function chunkUpload(data: FormData) {
method: 'post',
data,
dataType: 'form-data',
timeout: 60_000,
});
}
@@ -214,6 +219,7 @@ export function transferStaticFile(data: Record<string, any>) {
url: `/file/transferStaticFile`,
method: 'post',
data,
timeout: 60_000,
});
}
@@ -241,6 +247,7 @@ export async function uploadFileToNE(
neType,
neId,
},
timeout: 60_000,
});
return transferToNeFileRes;
}

View File

@@ -3,7 +3,7 @@ import { request } from '@/plugins/http-fetch';
// 网元抓包PACP 开始
export function dumpStart(data: Record<string, string>) {
return request({
url: '/tcpdump/start',
url: '/trace/tcpdump/start',
method: 'post',
data: data,
});
@@ -12,7 +12,7 @@ export function dumpStart(data: Record<string, string>) {
// 网元抓包PACP 结束
export function dumpStop(data: Record<string, string>) {
return request({
url: '/tcpdump/stop',
url: '/trace/tcpdump/stop',
method: 'post',
data: data,
});
@@ -21,7 +21,7 @@ export function dumpStop(data: Record<string, string>) {
// UPF标准版内部抓包
export function traceUPF(data: Record<string, string>) {
return request({
url: '/tcpdump/traceUPF',
url: '/trace/tcpdump/traceUPF',
method: 'post',
data: data,
});

View File

@@ -1,9 +1,10 @@
<template>
<a-modal
<ProModal
:drag="true"
:destroyOnClose="true"
:title="t('components.CronModal.title')"
:visible="props.visible"
:body-style="{ padding: '0 24px' }"
:destroy-on-close="true"
@cancel="fnCronModal(false)"
@ok="fnCronModal(true)"
>
@@ -31,7 +32,7 @@
v-model:value="cronStr"
disabled
/>
</a-modal>
</ProModal>
</template>
<script lang="ts" setup>
import CronSecond from './components/Second.vue';

View File

@@ -1,13 +1,13 @@
<script setup lang="ts">
import { computed } from 'vue';
import { computed, PropType } from 'vue';
const props = defineProps({
/**数据 */
options: {
type: Array,
type: Array as PropType<DictType[]>,
},
/**当前的值对应数据中的项字段 */
valueField: {
type: String,
type: String as PropType<keyof DictType>,
default: 'value',
},
/**当前的值 */
@@ -16,7 +16,7 @@ const props = defineProps({
default: '',
},
/**数据默认值,当前值不存在时 */
valueOption: {
valueDefault: {
type: [Number, String],
},
});
@@ -24,13 +24,13 @@ const props = defineProps({
/**遍历找到对应值数据项 */
const item = computed(() => {
if (Array.isArray(props.options) && props.options.length > 0) {
let option = (props.options as any[]).find(
let option = props.options.find(
item => `${item[props.valueField]}` === `${props.value}`
);
// 数据默认值
if (props.valueOption != undefined && !option) {
option = (props.options as any[]).find(
item => `${item[props.valueField]}` === `${props.valueOption }`
if (!option && props.valueDefault != undefined) {
option = props.options.find(
item => `${item[props.valueField]}` === `${props.valueDefault}`
);
}
return option;
@@ -41,14 +41,10 @@ const item = computed(() => {
<template>
<template v-if="item">
<a-tag
v-if="item.elTagType"
:class="item.elTagClass"
:color="item.elTagType"
>
<a-tag v-if="item.tagType" :class="item.tagClass" :color="item.tagType">
{{ item.label }}
</a-tag>
<span v-else :class="item.elTagClass">
<span v-else :class="item.tagClass">
{{ item.label }}
</span>
</template>

View File

@@ -1,168 +0,0 @@
<script setup lang="ts">
import { ref, watch, watchEffect, CSSProperties, computed } from 'vue';
import { useDraggable } from '@vueuse/core';
const emit = defineEmits(['update:visible', 'ok', 'cancel']);
/**于a-modal保持一致 */
const props = defineProps({
/**是否弹出显示,必传 */
visible: {
type: Boolean,
required: true,
},
/**窗口标题 */
title: {
type: String,
default: '',
},
/**窗口宽度 */
width: {
type: [String, Number],
default: '520px',
},
bodyStyle: {
type: Object,
default: {},
},
keyboard: {
type: Boolean,
default: true,
},
mask: {
type: Boolean,
default: true,
},
maskClosable: {
type: Boolean,
default: true,
},
maskStyle: {
type: Object,
default: {},
},
destroyOnClose: {
type: Boolean,
default: false,
},
confirmLoading: {
type: Boolean,
default: false,
},
footer: {
type: Object as any,
},
});
// 对标题进行监听
const modalTitleRef = ref<HTMLElement | null>(null);
const { x, y, isDragging } = useDraggable(modalTitleRef);
const startX = ref<number>(0);
const startY = ref<number>(0);
const startedDrag = ref(false);
const transformX = ref(0);
const transformY = ref(0);
const preTransformX = ref(0);
const preTransformY = ref(0);
const dragRect = ref({ left: 0, right: 0, top: 0, bottom: 0 });
watch([x, y], () => {
if (!startedDrag.value) {
startX.value = x.value;
startY.value = y.value;
const bodyRect = document.body.getBoundingClientRect();
const titleRectEl = modalTitleRef.value;
if (titleRectEl) {
const titleRect = titleRectEl.getBoundingClientRect();
dragRect.value.right = bodyRect.width - (titleRect.width + 24);
dragRect.value.bottom = bodyRect.height - (titleRect.height + 16);
}
preTransformX.value = transformX.value;
preTransformY.value = transformY.value;
}
startedDrag.value = true;
});
watchEffect(() => {
if (!isDragging.value) {
startedDrag.value = false;
}
if (startedDrag.value) {
const dragRectX = Math.min(
Math.max(dragRect.value.left + 24, x.value),
dragRect.value.right
);
transformX.value = preTransformX.value + dragRectX - startX.value;
const dragRectY = Math.min(
Math.max(dragRect.value.top + 16, y.value),
dragRect.value.bottom
);
transformY.value = preTransformY.value + dragRectY - startY.value;
}
});
// 位移
const transformStyle = computed<CSSProperties>(() => {
return {
transform: `translate(${transformX.value}px, ${transformY.value}px)`,
};
});
/**监听是否显示,位置还原 */
watch(
() => props.visible,
val => {
if (val) {
transformX.value = 0;
transformY.value = 0;
}
}
);
</script>
<template>
<a-modal
wrapClassName="draggable-modal"
:width="props.width"
:keyboard="props.keyboard"
:mask="props.mask"
:mask-closable="props.maskClosable"
:visible="props.visible"
:confirm-loading="props.confirmLoading"
:body-style="props.bodyStyle"
:mask-style="props.maskStyle"
:destroy-on-close="props.destroyOnClose"
:footer="props.footer"
@ok="(e:any) => emit('ok', e)"
@cancel="(e:any) => emit('cancel', e)"
>
<template #title>
<div
ref="modalTitleRef"
class="draggable-modal-title"
v-text="title"
></div>
</template>
<template #modalRender="{ originVNode }">
<div :style="transformStyle">
<component :is="originVNode" />
</div>
</template>
<slot></slot>
</a-modal>
</template>
<style lang="less">
.draggable-modal {
// 穿透选择文字
// 给a-modal设置 get-container=".ant-pro-page-container"
// 防止跳转显示
// &.ant-modal-wrap {
// pointer-events: none;
// }
&-title {
width: 100%;
cursor: move;
}
}
</style>

View File

@@ -0,0 +1,141 @@
<script setup lang="ts">
import { onMounted, onUnmounted, computed } from 'vue';
import useI18n from '@/hooks/useI18n';
import useMaskStore from '@/store/modules/mask';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { useRoute, useRouter } from 'vue-router';
import { useThrottleFn } from '@vueuse/core';
import { getConfigKey } from '@/api/system/config';
const maskStore = useMaskStore();
const route = useRoute();
const router = useRouter();
const { t } = useI18n();
/**显示遮罩 */
const isVisible = computed(() => !['none', 'lock'].includes(maskStore.type));
// 用户无操作一段时间后进行锁屏
function idleTimeout(time: number, callback: Function) {
if (time === 0) return;
let timeoutId: any;
let idleTime = 0;
function resetIdleTime() {
idleTime = 0;
}
// 监听用户活动事件
document.addEventListener('mousemove', useThrottleFn(resetIdleTime, 1000));
document.addEventListener('keydown', useThrottleFn(resetIdleTime, 1000));
document.addEventListener('click', useThrottleFn(resetIdleTime, 1000));
// 定时检查用户是否长时间无操作
timeoutId = setInterval(() => {
idleTime += 1000;
if (idleTime >= time) {
clearTimeout(timeoutId);
callback();
}
}, 1000);
}
/**组件实例挂载之后调用 */
onMounted(() => {
// 本地锁定类型设置;
maskStore.handleMaskType(maskStore.type);
getConfigKey('sys.lockTime')
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && res.data) {
maskStore.lockTimeout = +res.data;
}
maskStore.handleMaskType(maskStore.type);
// 是锁屏的调整到页面
if (maskStore.type === 'lock') {
maskStore.handleMaskType('lock');
router.push({ name: 'LockScreen', query: { redirect: route.path } });
}
})
.finally(() => {
// 无操作x秒后跳转锁屏
idleTimeout(maskStore.lockTimeout * 1000, () => {
if (route.name === 'LockScreen') return;
maskStore.handleMaskType('lock');
router.push({ name: 'LockScreen', query: { redirect: route.path } });
});
});
});
/**组件实例被卸载之后调用 */
onUnmounted(() => {});
</script>
<template>
<a-modal
v-model:visible="isVisible"
get-container="#app"
:footer="null"
:zIndex="1008"
:closable="false"
:keyboard="false"
:centered="true"
:maskClosable="false"
:maskStyle="{
backdropFilter: 'blur(10px)',
WebkitBackdropFilter: 'blur(10px)',
}"
wrapClassName="lock-screen"
:wrap-style="{
background: 'rgba(0, 0, 0, 0.85)',
}"
>
<!-- 锁屏-OMC重启升级 -->
<div class="lock-screen_reload" v-if="maskStore.type === 'reload'">
<LoadingOutlined style="font-size: 56px" />
<div class="text">
{{ t('components.LockScreen.backReload') }}
</div>
<div class="desc">
{{ t('components.LockScreen.backReload2') }}
</div>
</div>
<!-- 锁屏-OMC系统重置 -->
<div class="lock-screen_reload" v-if="maskStore.type === 'reset'">
<LoadingOutlined style="font-size: 56px" />
<div class="text">
{{ t('components.LockScreen.systemReset') }}
</div>
<div class="desc">
{{ t('components.LockScreen.systemReset2') }}
</div>
</div>
</a-modal>
</template>
<style lang="less" scoped>
.lock-screen_reload {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: transparent;
color: #fff;
& .text {
font-size: 24px;
font-weight: bold;
letter-spacing: 4px;
margin-top: 24px;
}
& .desc {
margin-top: 8px;
font-size: 16px;
}
}
</style>
<style lang="less">
.lock-screen {
.ant-modal-content {
background-color: transparent;
box-shadow: none;
}
}
</style>

View File

@@ -0,0 +1,252 @@
//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.
export default {
af: "Afghanistan",
ax: "Åland Islands",
al: "Albania",
dz: "Algeria",
as: "American Samoa",
ad: "Andorra",
ao: "Angola",
ai: "Anguilla",
aq: "Antarctica",
ag: "Antigua & Barbuda",
ar: "Argentina",
am: "Armenia",
aw: "Aruba",
au: "Australia",
at: "Austria",
az: "Azerbaijan",
bs: "Bahamas",
bh: "Bahrain",
bd: "Bangladesh",
bb: "Barbados",
by: "Belarus",
be: "Belgium",
bz: "Belize",
bj: "Benin",
bm: "Bermuda",
bt: "Bhutan",
bo: "Bolivia",
ba: "Bosnia & Herzegovina",
bw: "Botswana",
bv: "Bouvet Island",
br: "Brazil",
io: "British Indian Ocean Territory",
vg: "British Virgin Islands",
bn: "Brunei",
bg: "Bulgaria",
bf: "Burkina Faso",
bi: "Burundi",
kh: "Cambodia",
cm: "Cameroon",
ca: "Canada",
cv: "Cape Verde",
bq: "Caribbean Netherlands",
ky: "Cayman Islands",
cf: "Central African Republic",
td: "Chad",
cl: "Chile",
cn: "China",
cx: "Christmas Island",
cc: "Cocos (Keeling) Islands",
co: "Colombia",
km: "Comoros",
cg: "Congo - Brazzaville",
cd: "Congo - Kinshasa",
ck: "Cook Islands",
cr: "Costa Rica",
ci: "Côte dIvoire",
hr: "Croatia",
cu: "Cuba",
cw: "Curaçao",
cy: "Cyprus",
cz: "Czechia",
dk: "Denmark",
dj: "Djibouti",
dm: "Dominica",
do: "Dominican Republic",
ec: "Ecuador",
eg: "Egypt",
sv: "El Salvador",
gq: "Equatorial Guinea",
er: "Eritrea",
ee: "Estonia",
sz: "Eswatini",
et: "Ethiopia",
fk: "Falkland Islands",
fo: "Faroe Islands",
fj: "Fiji",
fi: "Finland",
fr: "France",
gf: "French Guiana",
pf: "French Polynesia",
tf: "French Southern Territories",
ga: "Gabon",
gm: "Gambia",
ge: "Georgia",
de: "Germany",
gh: "Ghana",
gi: "Gibraltar",
gr: "Greece",
gl: "Greenland",
gd: "Grenada",
gp: "Guadeloupe",
gu: "Guam",
gt: "Guatemala",
gg: "Guernsey",
gn: "Guinea",
gw: "Guinea-Bissau",
gy: "Guyana",
ht: "Haiti",
hm: "Heard & McDonald Islands",
hn: "Honduras",
hk: "Hong Kong SAR China",
hu: "Hungary",
is: "Iceland",
in: "India",
id: "Indonesia",
ir: "Iran",
iq: "Iraq",
ie: "Ireland",
im: "Isle of Man",
il: "Israel",
it: "Italy",
jm: "Jamaica",
jp: "Japan",
je: "Jersey",
jo: "Jordan",
kz: "Kazakhstan",
ke: "Kenya",
ki: "Kiribati",
kw: "Kuwait",
kg: "Kyrgyzstan",
la: "Laos",
lv: "Latvia",
lb: "Lebanon",
ls: "Lesotho",
lr: "Liberia",
ly: "Libya",
li: "Liechtenstein",
lt: "Lithuania",
lu: "Luxembourg",
mo: "Macao SAR China",
mg: "Madagascar",
mw: "Malawi",
my: "Malaysia",
mv: "Maldives",
ml: "Mali",
mt: "Malta",
mh: "Marshall Islands",
mq: "Martinique",
mr: "Mauritania",
mu: "Mauritius",
yt: "Mayotte",
mx: "Mexico",
fm: "Micronesia",
md: "Moldova",
mc: "Monaco",
mn: "Mongolia",
me: "Montenegro",
ms: "Montserrat",
ma: "Morocco",
mz: "Mozambique",
mm: "Myanmar (Burma)",
na: "Namibia",
nr: "Nauru",
np: "Nepal",
nl: "Netherlands",
nc: "New Caledonia",
nz: "New Zealand",
ni: "Nicaragua",
ne: "Niger",
ng: "Nigeria",
nu: "Niue",
nf: "Norfolk Island",
kp: "North Korea",
mk: "North Macedonia",
mp: "Northern Mariana Islands",
no: "Norway",
om: "Oman",
pk: "Pakistan",
pw: "Palau",
ps: "Palestinian Territories",
pa: "Panama",
pg: "Papua New Guinea",
py: "Paraguay",
pe: "Peru",
ph: "Philippines",
pn: "Pitcairn Islands",
pl: "Poland",
pt: "Portugal",
pr: "Puerto Rico",
qa: "Qatar",
re: "Réunion",
ro: "Romania",
ru: "Russia",
rw: "Rwanda",
ws: "Samoa",
sm: "San Marino",
st: "São Tomé & Príncipe",
sa: "Saudi Arabia",
sn: "Senegal",
rs: "Serbia",
sc: "Seychelles",
sl: "Sierra Leone",
sg: "Singapore",
sx: "Sint Maarten",
sk: "Slovakia",
si: "Slovenia",
sb: "Solomon Islands",
so: "Somalia",
za: "South Africa",
gs: "South Georgia & South Sandwich Islands",
kr: "South Korea",
ss: "South Sudan",
es: "Spain",
lk: "Sri Lanka",
bl: "St. Barthélemy",
sh: "St. Helena",
kn: "St. Kitts & Nevis",
lc: "St. Lucia",
mf: "St. Martin",
pm: "St. Pierre & Miquelon",
vc: "St. Vincent & Grenadines",
sd: "Sudan",
sr: "Suriname",
sj: "Svalbard & Jan Mayen",
se: "Sweden",
ch: "Switzerland",
sy: "Syria",
tw: "Taiwan",
tj: "Tajikistan",
tz: "Tanzania",
th: "Thailand",
tl: "Timor-Leste",
tg: "Togo",
tk: "Tokelau",
to: "Tonga",
tt: "Trinidad & Tobago",
tn: "Tunisia",
tr: "Turkey",
tm: "Turkmenistan",
tc: "Turks & Caicos Islands",
tv: "Tuvalu",
um: "U.S. Outlying Islands",
vi: "U.S. Virgin Islands",
ug: "Uganda",
ua: "Ukraine",
ae: "United Arab Emirates",
gb: "United Kingdom",
us: "United States",
uy: "Uruguay",
uz: "Uzbekistan",
vu: "Vanuatu",
va: "Vatican City",
ve: "Venezuela",
vn: "Vietnam",
wf: "Wallis & Futuna",
eh: "Western Sahara",
ye: "Yemen",
zm: "Zambia",
zw: "Zimbabwe",
};

View File

@@ -0,0 +1,4 @@
import countryTranslations from './countries';
import interfaceTranslations from './interface';
export default { ...countryTranslations, ...interfaceTranslations };

View File

@@ -0,0 +1,14 @@
//* English. Translated by: Jack O'Connor (jackocnr).
export default {
selectedCountryAriaLabel: "Selected country",
noCountrySelected: "No country selected",
countryListAriaLabel: "List of countries",
searchPlaceholder: "Search",
zeroSearchResults: "No results found",
oneSearchResult: "1 result found",
multipleSearchResults: "${count} results found",
// additional countries (not supported by country-list library)
ac: "Ascension Island",
xk: "Kosovo",
};

View File

@@ -0,0 +1,252 @@
//* THIS FILE IS AUTO-GENERATED. DO NOT EDIT.
export default {
al: "阿尔巴尼亚",
dz: "阿尔及利亚",
af: "阿富汗",
ar: "阿根廷",
ae: "阿拉伯联合酋长国",
aw: "阿鲁巴",
om: "阿曼",
az: "阿塞拜疆",
eg: "埃及",
et: "埃塞俄比亚",
ie: "爱尔兰",
ee: "爱沙尼亚",
ad: "安道尔",
ao: "安哥拉",
ai: "安圭拉",
ag: "安提瓜和巴布达",
at: "奥地利",
ax: "奥兰群岛",
au: "澳大利亚",
bb: "巴巴多斯",
pg: "巴布亚新几内亚",
bs: "巴哈马",
pk: "巴基斯坦",
py: "巴拉圭",
ps: "巴勒斯坦领土",
bh: "巴林",
pa: "巴拿马",
br: "巴西",
by: "白俄罗斯",
bm: "百慕大",
bg: "保加利亚",
mp: "北马里亚纳群岛",
mk: "北马其顿",
bj: "贝宁",
be: "比利时",
is: "冰岛",
pr: "波多黎各",
pl: "波兰",
ba: "波斯尼亚和黑塞哥维那",
bo: "玻利维亚",
bz: "伯利兹",
bw: "博茨瓦纳",
bt: "不丹",
bf: "布基纳法索",
bi: "布隆迪",
bv: "布韦岛",
kp: "朝鲜",
gq: "赤道几内亚",
dk: "丹麦",
de: "德国",
tl: "东帝汶",
tg: "多哥",
do: "多米尼加共和国",
dm: "多米尼克",
ru: "俄罗斯",
ec: "厄瓜多尔",
er: "厄立特里亚",
fr: "法国",
fo: "法罗群岛",
pf: "法属波利尼西亚",
gf: "法属圭亚那",
tf: "法属南部领地",
mf: "法属圣马丁",
va: "梵蒂冈",
ph: "菲律宾",
fj: "斐济",
fi: "芬兰",
cv: "佛得角",
fk: "福克兰群岛",
gm: "冈比亚",
cg: "刚果(布)",
cd: "刚果(金)",
co: "哥伦比亚",
cr: "哥斯达黎加",
gd: "格林纳达",
gl: "格陵兰",
ge: "格鲁吉亚",
gg: "根西岛",
cu: "古巴",
gp: "瓜德罗普",
gu: "关岛",
gy: "圭亚那",
kz: "哈萨克斯坦",
ht: "海地",
kr: "韩国",
nl: "荷兰",
bq: "荷属加勒比区",
sx: "荷属圣马丁",
hm: "赫德岛和麦克唐纳群岛",
me: "黑山",
hn: "洪都拉斯",
ki: "基里巴斯",
dj: "吉布提",
kg: "吉尔吉斯斯坦",
gn: "几内亚",
gw: "几内亚比绍",
ca: "加拿大",
gh: "加纳",
ga: "加蓬",
kh: "柬埔寨",
cz: "捷克",
zw: "津巴布韦",
cm: "喀麦隆",
qa: "卡塔尔",
ky: "开曼群岛",
cc: "科科斯(基林)群岛",
km: "科摩罗",
ci: "科特迪瓦",
kw: "科威特",
hr: "克罗地亚",
ke: "肯尼亚",
ck: "库克群岛",
cw: "库拉索",
lv: "拉脱维亚",
ls: "莱索托",
la: "老挝",
lb: "黎巴嫩",
lt: "立陶宛",
lr: "利比里亚",
ly: "利比亚",
li: "列支敦士登",
re: "留尼汪",
lu: "卢森堡",
rw: "卢旺达",
ro: "罗马尼亚",
mg: "马达加斯加",
im: "马恩岛",
mv: "马尔代夫",
mt: "马耳他",
mw: "马拉维",
my: "马来西亚",
ml: "马里",
mh: "马绍尔群岛",
mq: "马提尼克",
yt: "马约特",
mu: "毛里求斯",
mr: "毛里塔尼亚",
us: "美国",
um: "美国本土外小岛屿",
as: "美属萨摩亚",
vi: "美属维尔京群岛",
mn: "蒙古",
ms: "蒙特塞拉特",
bd: "孟加拉国",
pe: "秘鲁",
fm: "密克罗尼西亚",
mm: "缅甸",
md: "摩尔多瓦",
ma: "摩洛哥",
mc: "摩纳哥",
mz: "莫桑比克",
mx: "墨西哥",
na: "纳米比亚",
za: "南非",
aq: "南极洲",
gs: "南乔治亚和南桑威奇群岛",
ss: "南苏丹",
nr: "瑙鲁",
ni: "尼加拉瓜",
np: "尼泊尔",
ne: "尼日尔",
ng: "尼日利亚",
nu: "纽埃",
no: "挪威",
nf: "诺福克岛",
pw: "帕劳",
pn: "皮特凯恩群岛",
pt: "葡萄牙",
jp: "日本",
se: "瑞典",
ch: "瑞士",
sv: "萨尔瓦多",
ws: "萨摩亚",
rs: "塞尔维亚",
sl: "塞拉利昂",
sn: "塞内加尔",
cy: "塞浦路斯",
sc: "塞舌尔",
sa: "沙特阿拉伯",
bl: "圣巴泰勒米",
cx: "圣诞岛",
st: "圣多美和普林西比",
sh: "圣赫勒拿",
kn: "圣基茨和尼维斯",
lc: "圣卢西亚",
sm: "圣马力诺",
pm: "圣皮埃尔和密克隆群岛",
vc: "圣文森特和格林纳丁斯",
lk: "斯里兰卡",
sk: "斯洛伐克",
si: "斯洛文尼亚",
sj: "斯瓦尔巴和扬马延",
sz: "斯威士兰",
sd: "苏丹",
sr: "苏里南",
sb: "所罗门群岛",
so: "索马里",
tj: "塔吉克斯坦",
tw: "台湾",
th: "泰国",
tz: "坦桑尼亚",
to: "汤加",
tc: "特克斯和凯科斯群岛",
tt: "特立尼达和多巴哥",
tn: "突尼斯",
tv: "图瓦卢",
tr: "土耳其",
tm: "土库曼斯坦",
tk: "托克劳",
wf: "瓦利斯和富图纳",
vu: "瓦努阿图",
gt: "危地马拉",
ve: "委内瑞拉",
bn: "文莱",
ug: "乌干达",
ua: "乌克兰",
uy: "乌拉圭",
uz: "乌兹别克斯坦",
es: "西班牙",
eh: "西撒哈拉",
gr: "希腊",
sg: "新加坡",
nc: "新喀里多尼亚",
nz: "新西兰",
hu: "匈牙利",
sy: "叙利亚",
jm: "牙买加",
am: "亚美尼亚",
ye: "也门",
iq: "伊拉克",
ir: "伊朗",
il: "以色列",
it: "意大利",
in: "印度",
id: "印度尼西亚",
gb: "英国",
vg: "英属维尔京群岛",
io: "英属印度洋领地",
jo: "约旦",
vn: "越南",
zm: "赞比亚",
je: "泽西岛",
td: "乍得",
gi: "直布罗陀",
cl: "智利",
cf: "中非共和国",
cn: "中国",
mo: "中国澳门特别行政区",
hk: "中国香港特别行政区",
};

View File

@@ -0,0 +1,4 @@
import countryTranslations from './countries';
import interfaceTranslations from './interface';
export default { ...countryTranslations, ...interfaceTranslations };

View File

@@ -0,0 +1,15 @@
//* Chinese (Simplified). Translated by: Google Translate.
export default {
selectedCountryAriaLabel: "所选国家",
noCountrySelected: "未选择国家/地区",
countryListAriaLabel: "国家名单",
searchPlaceholder: "搜索",
zeroSearchResults: "未找到结果",
oneSearchResult: "找到 1 个结果",
multipleSearchResults: "找到 ${count} 个结果",
// additional countries (not supported by country-list library)
ac: "阿森松岛",
xk: "科索沃",
};

View File

@@ -0,0 +1,145 @@
<!-- https://github.com/jackocnr/intl-tel-input/blob/master/react/src/intl-tel-input/react.tsx -->
<script lang="ts" setup>
import intlTelInput, { Iti, SomeOptions } from 'intl-tel-input';
import 'intl-tel-input/build/css/intlTelInput.min.css';
import 'intl-tel-input/build/js/utils.js';
import { ref, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
import useI18n from '@/hooks/useI18n';
const { currentLocale } = useI18n();
const emit = defineEmits(['update:value', 'update:change']);
const props = defineProps({
/**有效检验 */
preciseValidation: {
type: Boolean,
default: false,
},
/**手机号 */
value: {
type: String,
default: '',
},
/**禁用输入 */
disabled: {
type: Boolean,
default: false,
},
/**手机号输入提示 */
placeholder: {
type: String,
default: '',
},
/**手机号输入最大字符串长度 */
maxlength: {
type: Number,
default: 255,
},
/**允许清空手机号输入框 */
allowClear: {
type: Boolean,
},
});
const inputRef = ref<HTMLInputElement | null>(null);
const itiRef = ref<Iti | null>(null);
function fnChange() {
if (!itiRef.value) return;
const num = itiRef.value?.getNumber() || '';
const countryIso = itiRef.value?.getSelectedCountryData().iso2 || '';
// note: this number will be in standard E164 format, but any container component can use
// intlTelInputUtils.formatNumber() to convert this to another format
// as well as intlTelInputUtils.getNumberType() etc. if need be
let data = {
num,
countryIso,
validity: false,
errorCode: -1,
};
const isValid = props.preciseValidation
? itiRef.value.isValidNumberPrecise()
: itiRef.value.isValidNumber();
if (isValid) {
data.validity = true;
data.errorCode = 0;
} else {
const errorCode = itiRef.value.getValidationError();
data.validity = false;
data.errorCode = errorCode;
}
// console.log(data);
emit('update:value', num);
emit('update:change', data);
}
watch(
() => props.value,
v => {
if (v) {
itiRef.value?.setNumber(v);
} else {
itiRef.value?.setNumber('');
}
}
);
onMounted(() => {
nextTick(async () => {
if (inputRef.value) {
let i18n = undefined;
let initialCountry = 'us';
// 语言文件导入问题只能复制到项目内处理
// import fr from "intl-tel-input/i18n/fr";
if (currentLocale.value.startsWith('zh')) {
const { default: zh } = await import('./i18n/zh');
i18n = zh;
initialCountry = 'cn';
} else {
const { default: en } = await import('./i18n/en');
i18n = en;
initialCountry = 'us';
}
itiRef.value = intlTelInput(inputRef.value, {
initialCountry: initialCountry,
formatOnDisplay: true,
autoPlaceholder: 'polite',
i18n: i18n,
} as SomeOptions);
inputRef.value.addEventListener('countrychange', fnChange);
}
});
});
onBeforeUnmount(() => {
if (inputRef.value) {
inputRef.value.removeEventListener('countrychange', fnChange);
}
itiRef.value?.destroy();
});
</script>
<template>
<input
type="tel"
class="ant-input"
ref="inputRef"
:value="value"
:disabled="disabled"
:placeholder="placeholder"
:maxlength="maxlength"
:allow-clear="allowClear"
@input="fnChange"
/>
</template>
<style lang="css">
.iti {
display: block;
}
.iti .iti__country-container .iti__search-input {
padding: 4px 8px;
}
</style>

View File

@@ -1,217 +0,0 @@
<script setup lang="ts">
import { ref, toRaw, onMounted, onUnmounted } from 'vue';
import useI18n from '@/hooks/useI18n';
import { message } from 'ant-design-vue/lib';
import useUserStore from '@/store/modules/user';
import useLockedStore from '@/store/modules/locked';
import { getConfigKey } from '@/api/system/config';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { useRouter } from 'vue-router';
import { computed } from 'vue';
const lockedStore = useLockedStore();
const userStore = useUserStore();
const router = useRouter();
const { t } = useI18n();
const password = ref('');
/**设置定时器ID */
let timeoutDuration = 10 * 60 * 1000; // 设置超时时间为10分钟单位为毫秒
let timeoutId: any = null;
// 超时锁屏
function resetTimeout() {
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(() => {
lockedStore.fnLock('lock');
}, timeoutDuration);
}
/**解锁 */
function handleUnlock() {
let lockFrom: any = {};
lockFrom.username = userStore.userName;
lockFrom.password = password.value;
userStore.fnLogin(toRaw(lockFrom)).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('components.LockScreen.validSucc'), 3);
password.value = '';
lockedStore.fnLock('none');
} else {
message.error(t('components.LockScreen.validError'), 3);
}
});
}
/**返回登录界面 */
function backLogin() {
lockedStore.fnLock('none');
userStore.fnLogOut().finally(() => router.push({ name: 'Login' }));
}
const isLocked = computed(() => lockedStore.type !== 'none');
onMounted(() => {
getConfigKey('sys.lockTime')
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && res.data) {
lockedStore.lockTimeout = res.data;
timeoutDuration = res.data * 1000;
}
// 本地锁定类型设置
lockedStore.fnLock(lockedStore.type);
})
.finally(() => {
if (timeoutDuration !== 0) {
resetTimeout();
// 监听用户的操作,重置超时时间
window.addEventListener('mousemove', resetTimeout);
window.addEventListener('keydown', resetTimeout);
}
});
});
/**组件实例被卸载之后调用 */
onUnmounted(() => {
if (timeoutId) {
clearTimeout(timeoutId);
}
});
</script>
<template>
<a-modal
v-model:visible="isLocked"
get-container="#app"
:footer="null"
:zIndex="1008"
:closable="false"
:keyboard="false"
:centered="true"
:maskClosable="false"
:maskStyle="{
backdropFilter: 'blur(10px)',
WebkitBackdropFilter: 'blur(10px)',
}"
wrapClassName="lock-screen"
:wrap-style="{
background: 'rgba(0, 0, 0, 0.85)',
}"
>
<!-- 锁屏-登录 -->
<div class="lock-screen_login" v-if="lockedStore.type === 'lock'">
<div class="lock-screen_login-user">
<a-avatar
shape="circle"
:size="100"
:src="userStore.getAvatar"
:alt="userStore.userName"
></a-avatar>
<span class="nick">
{{ userStore.nickName }}
</span>
</div>
<div class="lock-screen_login-from">
<a-input-group compact>
<a-input
type="password"
v-model:value="password"
:placeholder="t('components.LockScreen.inputPlacePwd')"
:maxlength="32"
style="width: calc(100% - 50px)"
@keyup.enter="handleUnlock"
/>
<a-button type="primary" @click="handleUnlock">
<LoginOutlined />
</a-button>
</a-input-group>
<a-button type="text" class="logout" @click="backLogin">
{{ t('components.LockScreen.backLogin') }}
</a-button>
</div>
</div>
<!-- 锁屏-OMC重启升级 -->
<div class="lock-screen_reload" v-if="lockedStore.type === 'reload'">
<LoadingOutlined style="font-size: 56px" />
<div class="text">
{{ t('components.LockScreen.backReload') }}
</div>
<div class="desc">
{{ t('components.LockScreen.backReload2') }}
</div>
</div>
</a-modal>
</template>
<style lang="less" scoped>
.lock-screen_login {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: transparent;
&-user {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.nick {
font-size: 28px;
max-width: 164px;
white-space: nowrap;
text-align: start;
text-overflow: ellipsis;
overflow: hidden;
color: #fff;
}
}
&-from {
display: flex;
flex-flow: column;
width: 256px;
margin-top: 30px;
.logout {
margin-top: 8px;
color: rgba(255, 255, 255, 0.85);
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
&:hover {
color: var(--ant-primary-color);
}
}
}
}
.lock-screen_reload {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: transparent;
color: #fff;
& .text {
font-size: 24px;
font-weight: bold;
letter-spacing: 4px;
margin-top: 24px;
}
& .desc {
margin-top: 8px;
font-size: 16px;
}
}
</style>
<style lang="less">
.lock-screen {
.ant-modal-content {
background-color: transparent;
box-shadow: none;
}
}
</style>

View File

@@ -62,7 +62,7 @@ function fnTableColumnsCheckAllChange(e: any) {
/**表格字段列拖拽操作 */
function fnTableColumnsDrop(dropResult: any) {
const { removedIndex, addedIndex, payload } = dropResult;
if (!removedIndex || !addedIndex) {
if (removedIndex === null || addedIndex === null) {
return;
}
let itemToAdd = payload;
@@ -184,6 +184,7 @@ onMounted(() => {
<a-button
v-if="c.fixed !== undefined"
size="small"
:title="c.fixed ? `Fixed ${c.fixed} side` : ''"
:type="c.fixed ? 'primary' : 'dashed'"
@click="fnTableColumnsFixed(c)"
>

View File

@@ -1,8 +1,8 @@
<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
import { FitAddon } from 'xterm-addon-fit';
import { Terminal } from 'xterm';
import 'xterm/css/xterm.css';
import { FitAddon } from '@xterm/addon-fit';
import { Terminal } from '@xterm/xterm';
import '@xterm/xterm/css/xterm.css';
import { RESULT_CODE_ERROR } from '@/constants/result-constants';
import { OptionsType, WS } from '@/plugins/ws-websocket';
const ws = new WS();

View File

@@ -1,9 +1,9 @@
<script lang="ts" setup>
import { message } from 'ant-design-vue/lib';
import { ref, reactive, onMounted, onBeforeUnmount, nextTick } from 'vue';
import { FitAddon } from 'xterm-addon-fit';
import { Terminal } from 'xterm';
import 'xterm/css/xterm.css';
import { FitAddon } from '@xterm/addon-fit';
import { Terminal } from '@xterm/xterm';
import '@xterm/xterm/css/xterm.css';
import { RESULT_CODE_ERROR } from '@/constants/result-constants';
import { OptionsType, WS } from '@/plugins/ws-websocket';
const ws = new WS();
@@ -19,6 +19,16 @@ const props = defineProps({
type: String,
required: true,
},
/**窗口单行字符数 */
cols: {
type: Number,
default: 120,
},
/**窗口行数 */
rows: {
type: Number,
default: 128,
},
/**禁止输入 */
disable: {
type: Boolean,
@@ -238,6 +248,8 @@ onMounted(() => {
url: '/ws/telnet',
params: {
hostId: props.hostId,
cols: props.cols,
rows: props.rows,
},
onmessage: wsMessage,
onerror: wsError,

View File

@@ -0,0 +1,97 @@
<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
import { FitAddon } from '@xterm/addon-fit';
import { Terminal } from '@xterm/xterm';
import '@xterm/xterm/css/xterm.css';
const emit = defineEmits(['update:value']);
const props = defineProps({
/**终端ID必传 */
id: {
type: String,
required: true,
},
/**窗口单行字符数 */
cols: {
type: Number,
default: 80,
},
/**窗口行数 */
rows: {
type: Number,
default: 40,
},
/**信息 */
value: {
type: String,
default: '',
},
});
/**终端输入DOM节点实例对象 */
const terminalDom = ref<HTMLElement | undefined>(undefined);
/**终端输入实例对象 */
const terminal = ref<any>(null);
/**终端输入渲染 */
function handleRanderXterm(container: HTMLElement | undefined) {
if (!container) return;
const xterm = new Terminal({
cols: props.cols,
rows: props.rows,
lineHeight: 1.2,
fontSize: 12,
fontFamily: "Monaco, Menlo, Consolas, 'Courier New', monospace",
theme: {
background: '#000000',
},
cursorBlink: false, // 光标闪烁
cursorStyle: 'block',
scrollback: 1000,
scrollSensitivity: 15,
tabStopWidth: 4,
disableStdin: true, // 禁止输入
});
// 挂载
xterm.open(container);
// 自适应尺寸
const fitAddon = new FitAddon();
xterm.loadAddon(fitAddon);
// 创建 ResizeObserver 实例
var observer = new ResizeObserver(entries => {
fitAddon.fit();
});
// 监听元素大小变化
observer.observe(container);
terminal.value = xterm;
}
onMounted(() => {
nextTick(() => {
handleRanderXterm(terminalDom.value);
// 初始发送命令
if (typeof props.value === 'string') {
terminal.value.write(props.value);
}
});
});
onBeforeUnmount(() => {
if (terminal.value != null) {
terminal.value.dispose();
}
});
</script>
<template>
<div ref="terminalDom" :id="id" class="terminal"></div>
</template>
<style lang="css" scoped>
.terminal {
width: 100%;
height: 100%;
}
</style>

View File

@@ -35,19 +35,22 @@ const props = defineProps({
/**弹框关闭事件 */
function fnModalClose() {
if(props.loading) return
if (props.loading) return;
emit('close');
}
/**上传前检查或转换压缩 */
function fnBeforeUpload(file: FileType) {
if (props.loading) return false;
// 检查文件大小
if (props.size > 0) {
// 检查文件大小
if (props.size > 0) {
const fileSize = file.size;
const isLtM = fileSize / 1024 / 1024 < props.size;
if (!isLtM) {
message.error(`${t('components.UploadModal.allowFilter')} ${props.size}MB`, 3);
message.error(
`${t('components.UploadModal.allowFilter')} ${props.size}MB`,
3
);
return false;
}
}
@@ -56,7 +59,10 @@ function fnBeforeUpload(file: FileType) {
const fileName = file.name;
const isAllowType = props.ext.some(v => fileName.endsWith(v));
if (!isAllowType) {
message.error(`${t('components.UploadModal.onlyAllow')} ${props.ext.join('、')}`, 3);
message.error(
`${t('components.UploadModal.onlyAllow')} ${props.ext.join('、')}`,
3
);
return false;
}
}
@@ -65,19 +71,20 @@ function fnBeforeUpload(file: FileType) {
/**上传请求发出 */
function fnUpload(up: UploadRequestOption) {
emit('upload', up.file)
emit('upload', up.file);
}
</script>
<template>
<a-modal
width="500px"
<ProModal
:drag="true"
:destroyOnClose="true"
:title="props.title"
:visible="props.visible"
:keyboard="false"
:mask-closable="false"
:confirm-loading="props.loading"
:footer="null"
:footer="false"
@cancel="fnModalClose"
>
<a-space :size="8" direction="vertical" style="width: 100%">
@@ -93,20 +100,26 @@ function fnUpload(up: UploadRequestOption) {
<p class="ant-upload-drag-icon">
<inbox-outlined></inbox-outlined>
</p>
<p class="ant-upload-text">{{t('components.UploadModal.uploadTip')}}</p>
<p class="ant-upload-text">
{{ t('components.UploadModal.uploadTip') }}
</p>
<p class="ant-upload-hint">
<div v-if="props.size > 0">
{{t('components.UploadModal.allowSize')}} {{ props.size }} MB
</div>
<div v-if="props.ext.length > 0">
{{t('components.UploadModal.allowFormat')}} {{ props.ext.join('') }}
</div>
<template v-if="props.size > 0">
<div>
{{ t('components.UploadModal.allowSize') }} {{ props.size }} MB
</div>
</template>
<template v-if="props.ext.length > 0">
<div>
{{ t('components.UploadModal.allowFormat') }}
{{ props.ext.join('、') }}
</div>
</template>
</p>
</a-upload-dragger>
<slot></slot>
</a-space>
</a-modal>
</ProModal>
</template>
<style lang="less" scoped></style>

View File

@@ -1,5 +1,5 @@
/**管理员-系统指定角色KEY */
export const ADMIN_ROLE_KEY = 'admin';
/**系统管理员-系统指定角色KEY */
export const ADMIN_ROLE_KEY = 'system';
/**管理员-系统指定权限 */
/**系统管理员-系统指定权限 */
export const ADMIN_PERMISSION = '*:*:*';

View File

@@ -10,8 +10,11 @@ export const CACHE_LOCAL_PRIMARY_COLOR = 'cache:local:primaryColor';
/**本地缓存-多语言 */
export const CACHE_LOCAL_I18N = 'cache:local:i18n';
/**本地缓存-锁屏设置 */
export const CACHE_LOCAL_LOCK = 'cache:local:Lock';
/**本地缓存-遮罩设置 */
export const CACHE_LOCAL_MASK = 'cache:local:mask';
/**本地缓存-锁屏密码 */
export const CACHE_LOCAL_LOCK_PASSWD = 'cache:local:lock:passwd';
/**数据缓存表-表格排序 */
export const CACHE_DB_TABLE_DND = 'tbl_dnd';

View File

@@ -1,18 +1,26 @@
/**网元列表,默认顺序 */
export const NE_TYPE_LIST = [
'OMC',
'MME',
'IMS',
'AMF',
'AUSF',
'UDM',
'SMF',
'PCF',
'UPF',
'NRF',
'NSSF',
'IMS',
'N3IWF',
'NEF',
'NRF',
'UPF',
'LMF',
'NEF',
'MME',
'N3IWF',
'MOCNGW',
'SMSC',
];
/**
* 网元拓展包列表,默认顺序
* IMS-adb/kvdb/rtproxy/mf
* UDM-adb/kvdb
*/
export const NE_EXPAND_LIST = ['ADB', 'KVDB', 'RTPROXY', 'MF'];

View File

@@ -1,5 +1,5 @@
import { onBeforeMount } from 'vue';
import { ConfigProvider } from 'ant-design-vue/lib';
import { ConfigProvider, message } from 'ant-design-vue/lib';
import { CACHE_LOCAL_PRIMARY_COLOR } from '@/constants/cache-keys-constants';
import { localGet, localSet } from '@/utils/cache-local-utils';
@@ -8,6 +8,7 @@ import { localGet, localSet } from '@/utils/cache-local-utils';
*/
export const usePrimaryColor = () => {
onBeforeMount(() => {
message.config({ top: '100px' }); // 全局配置消息距离顶部的位置
changePrimaryColor(getLocalColor());
});
};

View File

@@ -2,6 +2,7 @@ export default {
// 语言
i18n: 'English',
hello: 'Hello',
welcome: 'Welcome, Core Network Management Platform',
// 通用
common: {
@@ -34,7 +35,6 @@ export default {
unableNull:' Cannot be empty',
moreText: 'More',
searchBarText: 'Search bar',
tableStripedText: 'Form Zebra',
reloadText: 'Refresh',
columnSetText: 'Column Setting',
columnSetTitle: 'Column Display / Sorting',
@@ -53,9 +53,15 @@ export default {
fold: 'Fold',
},
rowId: 'ID',
operate: 'Operation',
createTime: 'Create Time',
updateTime: 'Update Time',
remark: 'Remark',
description: 'Description',
operate: 'More Action',
operateOk: 'Operation Successful!',
operateErr: 'Operation Failed!',
copyText: "Copy",
copyOk: 'Copy Successful!',
units: {
second: 'Second',
minute: 'Minute',
@@ -124,12 +130,14 @@ export default {
onlyAllow:'Only supports upload file formats',
},
LockScreen: {
inputPlacePwd:'Please enter login password',
inputPlacePwd:'Lock Screen Password',
validSucc:'Validation Passed',
validError:'Validation Failure',
backLogin:'Logout to Relogin',
backReload:'Restarting now, please wait...',
backReload2:'When ready, your browser will automatically refresh.',
backReload2:'When ready, Your browser will automatically refresh.',
systemReset:'Resetting now, please wait...',
systemReset2:'Data information is being reset.',
},
},
@@ -141,6 +149,7 @@ export default {
page403: 'No Access',
page404: 'Match Page Not Found',
helpDoc: 'System User Documentation',
lockScreen: 'Lock Screen',
account: {
index: "Personal Center",
profile: "Personal Info",
@@ -164,6 +173,9 @@ export default {
codeHit: 'Verification code',
codeText: 'Obtain verification code',
codeSmsSend: 'Successfully sent, please pay attention to checking the SMS',
ipPlease: 'Please enter a valid IP address',
ipv4Reg: 'Not a valid IPv4 address',
ipv6Reg: 'Not a valid IPv6 address',
},
// 布局
@@ -174,7 +186,9 @@ export default {
},
rightContent: {
lock: "Lock Screen",
lockTip: "Confirmed to perform a lock screen?",
lockTip: "Confirmation of the lock screen?",
lockPasswd: "Unlock Password",
lockPasswdTip: "No password can be set",
helpDoc: "System User Documentation",
fullscreen: "Full Screen",
logout: "Logout",
@@ -356,7 +370,7 @@ export default {
pvflag:'PV Flag',
pnf:'Physical Network Element',
vnf:'Virtual Network Element',
province:'Province',
province:'Region',
vendorName:'Vendor Name',
dn:'Network Identification',
reload: 'Reload',
@@ -424,15 +438,14 @@ export default {
letUpTime:'Activation time',
createTime:'Creation time',
onlyAble:'Only upload file format {fileText} is supported',
nullData:'No network element list data yet',
nullVersion:'There is no rollback version for the current network element.',
},
license: {
neTypePlease: 'Select network element type',
neType: 'NE Type',
fileName: 'File Name',
createTime: 'Uploaded Time',
comment: 'File Description',
serialNum: 'Serial Num',
createTime: 'Time',
comment: 'Description',
updateComment: 'License Description',
updateCommentPlease: 'Please enter a license description',
updateFile: 'License File',
@@ -471,21 +484,21 @@ export default {
neType: 'NE Type',
neTypePleace: "Please select the network element type",
noConfigData: "No data on configuration items",
updateValue: "The value of the {num} attribute was modified successfully.",
updateValue: "[ {num} ] parameter value modified successfully.",
updateValueErr: "Attribute value modification failure",
updateItem: "Modify Index to {num}.",
updateItemErr: "Record modification failure",
delItemOk: "Deleting Index as {num} succeeded",
addItemOk: "Add Index as {num} Record Succeeded",
addItemErr: "Record addition failure",
requireUn: "{display} input value is of unknown type",
requireString: "The {display} parameter value is invalid.",
requireInt: "{display} Parameter value not in reasonable range {filter}",
requireIpv4: "{display} not a legitimate IPV4 address",
requireIpv6: "{display} Not a legitimate IPV6 address.",
requireEnum: "{display} is not a reasonable enumeration value.",
requireBool: "{display} is not a reasonable boolean value.",
editOkTip: "Confirm updating the value of this {num} attribute?",
requireUn: "[ {display} ] input value is of unknown type",
requireString: "[ {display} ] parameter value is invalid.",
requireInt: "[ {display} ] parameter value not in reasonable range {filter}",
requireIpv4: "[ {display} ] not a legitimate IPV4 address",
requireIpv6: "[ {display} ] not a legitimate IPV6 address.",
requireEnum: "[ {display} ] is not a reasonable enumeration value.",
requireBool: "[ {display} ] is not a reasonable boolean value.",
editOkTip: "Confirm updating the value of this [ {num} ] attribute?",
updateItemTip: "Confirm updating the data item with Index [{num}]?",
delItemTip: "Confirm deleting the data item with Index [{num}]?",
arrayMore: "Expand",
@@ -549,8 +562,6 @@ export default {
realTimeDataStart: "Turn on real-time data",
realTimeDataStop: "Turn off real-time data",
cdrInfo: "CDR Info",
neName: "NE name",
rmUID: "UID",
time: "Time",
rowInfo: "Info",
type: "Type",
@@ -559,30 +570,66 @@ export default {
called: "Called",
result: "Result",
delTip: "Confirm deletion of the data item numbered [{msg}]?",
exportTip: "Do you confirm to export the current query conditions of the CDR data? (Maximum 10,000 items can be exported.)",
smfChargingID: 'Charging ID',
smfSubscriptionIDData: 'Subscription ID Data',
smfSubscriptionIDType: 'Subscription ID Type',
smfDataVolumeUplink: 'Data Volume Uplink',
smfDataVolumeDownlink: 'Data Volume Downlink',
smfDataTotalVolume: 'Data Total Volume',
smfDuration: 'Duration',
smfInvocationTime: 'Invocation Time',
},
ue: {
eventType: "Event Type",
realTimeDataStart: "Turn on real-time data",
realTimeDataStop: "Turn off real-time data",
ueInfo: "UE Info",
neName: "NE name",
rmUID: "UID",
time: "Time",
rowInfo: "Info",
result: "Result",
resultOk: "Successes",
delTip: "Confirm deletion of the data item numbered [{msg}]?",
exportTip: "Do you confirm to export the event data of the current query condition? (Maximum 10,000 items can be exported.)",
},
},
ne: {
neInfo: {
version: "Version",
common: {
neType: 'NE Type',
neTypePlease: "Please select network element type",
neTypeTip: 'Fill in the type of network element to be created, e.g. SMF.',
neId: 'NE ID',
neIdPlease: 'Please enter the network element identification',
neIdTip: 'Fill in the unique identifier of the network element binding',
rmUid: 'Resource Unique ID',
rmUidPlease: 'Please enter a resource unique ID',
rmUidTip: "Tagging for data reporting of network element logs, alarms, metrics, etc.",
neName: 'NE Name',
neNamePlease: 'Please enter the name of the network element',
ipAddr: 'IP Addr',
ipAddrPlease: 'Please enter the IP address of the network element',
ipAddrTip: "Support IPV4/IPV6, synchronized change of configuration address",
port: 'Port',
portTip: "Network element port default:33030",
serialNum: 'Serial Number',
expiryDate: 'Expiry Date',
normalcy: 'Normal',
exceptions: 'Abnormal',
restart: 'Restart',
restartTip: 'Are you sure you want to restart the network element service?',
start: 'Start',
startTip: 'Are you sure you want to start the network element service?',
stop: 'Stop',
stopTip: 'Are you sure you want to stop the network element service?',
reload: 'Reload',
reloadTip: 'Confirm that you want to reload the network element configuration information?',
oam: 'OAM',
log: 'Logs',
},
neInfo: {
version: "Version",
state: "State",
serviceState: "Service Status",
normalcy: "Normal",
exceptions: "Abnormal",
serviceState: "Service Status",
info: 'Status Message',
resourceInfo: 'Resource Situation',
sysMem: "SYS Mem",
@@ -590,8 +637,40 @@ export default {
sysDisk: "SYS Store",
neCpu: "NE CPU",
hostConfig: "Connection Configuration",
rmUID: "Data resource location identifiers are used for data tagging such as logging, alarm reporting, etc.",
ipAddr: "Support IPV4/IPV6, synchronize the change of the configuration address of the following configuration",
pvflag: 'NE Virtualization',
pnf: 'physical network element',
vnf: 'virtual network element',
neAddress: 'MAC',
neAddressTip: 'Record the physical address (MAC) of the network card of the network element',
dn: 'network identifier',
vendorName: 'provider',
province: 'Service Area',
addTitle: 'New network element information',
editTitle: 'Edit network element information',
delTip: 'Confirm deletion of network element information data items?',
oam: {
title: 'OAM Configuration',
sync: 'Sync to NE',
oamEnable: 'Service',
oamPort: 'Port',
snmpEnable: 'Service',
snmpPort: 'Port',
kpiEnable: 'Report',
kpiTimer: 'Reporting Cycle',
kpiTimerPlease: 'Please enter the reporting period (in seconds)',
},
backConf: {
export: 'Config Export',
import: 'Config Import',
title: 'Configuration File Import',
importType: 'Source of File',
server:'Server File',
local:'Local File',
localUpload:'Local Upload',
exportTip:'Confirm that you want to export the network element configuration file?',
exportMsg:'Exporting Network Element Configuration Information to a File Succeeded',
pathPlease: 'No Backup File Found',
},
},
neHost: {
hostType: "Type",
@@ -610,13 +689,13 @@ export default {
privateKey: "Private Key",
privateKeyPlease: "Please fill in the private key characters correctly ~/.ssh/id_rsa",
passPhrase: "Private Key Cipher",
remark: "Remark",
createTime: "Time",
delTip: "Confirm that you want to delete the host number [{num}]?",
addTitle: "Add Host Connection",
editTitle: "Edit Host Connection",
test: "Test Connection To Host",
test: "Test Connection",
testOk: "Test Connection Successful",
authRSA: 'Secret Authorization',
authRSATip: "Do I have to configure secret-free authorization?",
},
neHostCmd: {
cmdType: "Type",
@@ -625,12 +704,124 @@ export default {
titlePlease: "Please fill in the command name correctly",
command: "Command",
commandPlease: "Please enter a valid command string correctly",
remark: "Remark",
createTime: "Time",
delTip: "Are you sure you want to delete the message with command number [{num}]?",
addTitle: "New Host Commands",
editTitle: "Edit Host Commands",
},
neSoftware: {
uploadTitle: "Update Software",
upload: "Upload",
uploadNotFile: "No software files uploaded",
uploadBatch: "Batch Upload",
uploadBatchMax: "Multiple packages can be uploaded, with up to {txt} selected at the same time.",
uploadFileName: "Parses file names in the format of: amf-r2.240x.xx-xxx",
name: "File Name",
path: "Software File",
pathPlease: "Please upload the software package file",
version: "Software Version",
versionPlease: "Please enter the software version number",
delTip: "Confirmed to remove the package?",
downTip: "Confirmation to download package [{txt}]?",
fileCheckType: 'The corresponding network element type is not resolved',
fileCheckVer: 'The corresponding version number is not resolved',
fileTypeNotEq: 'Not a specified network element type {txt}',
fileTypeExists: 'Same type of file already exists',
fileNameExists: 'File with same name already exists',
fileCheckTypeDep: 'The specified dependency package type is not resolved',
dependFile: 'Software Dependencies',
dependFileTip: 'File name resolution is the same as above, and installation is based on the order of uploading.',
},
neVersion: {
upgrade: "Upgrade To New Version",
upgradeTip: "Confirmed to upgrade to the new version?",
upgradeTipEmpty: "There are currently no new versions available",
upgradeTipEqual: "Current version is the same as the new version",
rollback: 'Switch to previous version',
rollbackTip: "Confirm switching to the previous version?",
rollbackTipEmpty: "There is currently no previous version available",
rollbackTipEqual: 'The current version is the same as the previous version',
version: "Current Version",
preVersion: "Previous Version",
newVersion: "New Version",
status: "Revision Status",
upgradeBatch: "Batch Upgrade",
upgradeBatchTip: "Do you perform new version upgrades on checked records?",
upgradeNotNewVer: 'No new version found',
upgradeOMCVer: 'Rejection of batch operation upgrades OMC',
upgradeDone: 'Update complete, service being reloaded',
upgradeFail: 'The update fails, please check whether the software file exists and whether the service terminal environment is available!',
upgradeModal: 'Network Element Version Updates',
},
neLicense: {
status: "License Status",
change: "Change License",
reload: "Refresh Info",
reloadTip: "Confirmed to refresh license information?",
reloadBatch: "Batch Refresh",
reloadBatchTip: "Do you do an information refresh on checked records?",
updateTtile: "Update License",
downCodeTop: "Confirmed to save the license activation code to a file?",
activationRequestCode: "License Activation Code",
licensePath: "License File",
licensePathTip: "Please upload license file",
upload: 'Upload',
uploadFile: "Upload License",
uploadChangeOk: 'Network Element renewed license successfully and is being calibrated in the background!',
uploadChangeFail: "Some network elements failed to update the license, please check whether the service terminal environment is available!",
},
neConfigBackup: {
name: "Name",
downTip: 'Confirmed to download the backup file [{txt}]?',
title: "Modify Backup {txt}",
},
neQuickSetup: {
reloadPara5G: 'Reload',
stepPrev: 'Previous',
stepPrevTip: 'Confirm that you want to abandon the current change and return to the previous step?',
stepNext: 'Next',
stepSave: 'Save',
startTitle: 'Service Terminal Environment',
startDesc: 'Test connectivity to network element services',
startStepNext: 'Confirm that you want to proceed to the next step to configure the network element information?',
addr: 'Terminal IP',
kernelName: 'System',
kernelRelease: 'Kernel',
machine: 'Framework',
prettyName: 'Platform',
prettyNameTip: 'Support Ubuntu',
nodename: 'Node Name',
auth: 'Competence Grant',
sudo: 'sudo',
sudoErr: 'To ensure that you have permission to install packages, configure to grant the current user permission to allow passwordless sudo privileges.',
sshLink: 'confidentiality',
configTitle: "Configuring Network Elements",
configDesc: "Fill in the basic information of the network element",
configAddTitle: 'New Tips',
configAddTip: 'Is it added as new network element information and continue?',
configUpdateTitle: 'Update Tips',
configUpdateTip: 'Does it update to the already existing network element information and continue?',
configStepNext: 'Confirm that you want to proceed to the next step for the Network Element software installation?',
installTitle: "Network Element Installation",
installDesc: "Installation to Service Terminal",
installConfirmTip: 'Are you sure you want to install package [{name}]?',
installStepNext: 'Confirm that you want to proceed to the next step for network element authorization?',
installSource: 'Software Source',
installSourceOption: 'Uploaded',
installSourceUpload: 'New Upload',
installSelect: 'Select Record',
installUpload: 'Upload File',
installText: 'Installed',
licenseTitle: "Licenses",
licenseDesc: "Network element service authorization certification",
licenseResultTitle: "Whether to authorize activation immediately",
licenseResultTitleOk: 'Successful Activation',
licenseUpload: 'License',
licenseEnd: 'Finish',
licenseEndTip: "Confirmed to end the installation?",
licenseCheack: 'Waiting for network element validation',
licenseTip1: '1. Click [License] to get the license activation code, and then contact the network element vendor for activation.',
licenseTip2: '2. Clicking [Finish] will end the installation process.',
},
},
neUser: {
auth: {
@@ -644,7 +835,7 @@ export default {
import: 'Import',
loadDataConfirm: 'Are you sure you want to reload the data?',
loadData: 'Load Data',
loadDataTip: 'Successfully fetched load data: {num} entries, the system is internally updating the data. You can click reset to refresh the data list after the loading is finished, please don it repeat click to get update!!!!',
loadDataTip: 'Successfully fetched load data: {num} entries, the system is internally updating the data. Please wait a moment!!!!',
startIMSI: 'Start IMSI',
batchAddText: 'Batch Add',
batchDelText: 'Batch Delete',
@@ -671,14 +862,14 @@ export default {
import: 'Import',
loadDataConfirm: 'Are you sure you want to reload the data?',
loadData: 'Load Data',
loadDataTip: 'Successfully fetched load data: {num} entries, the system is internally updating the data. You can click reset to refresh the data list after the loading is finished, please don it repeat click to get update!!!!',
loadDataTip: 'Successfully fetched load data: {num} entries, the system is internally updating the data. Please wait a moment!!!!',
numAdd: 'Number of releases',
numDel: 'Number of deleted',
checkDel: 'Check Delete',
batchAddText: 'Batch Add',
batchDelText: 'Batch Delete',
enable:'Enable',
disable:'Disable',
enable:'Enabled',
disable:'Disabled',
startIMSI: 'Start IMSI',
imsiTip: 'IMSI=MCC+MNC+MSIN',
imsiTip1: 'MCC=Mobile Country Code, consisting of three digits.',
@@ -691,7 +882,8 @@ export default {
micoTip: 'Signed MICO business flag bits',
rfspTip:'RFSP index, in NG-RAN, the index of a specific RRM configuration, parameter between 0 and 127',
ueTypeTip: 'Operator-defined subscriber UE Usage Type, integer, parameter between 0 and 127',
epsFlagTip: 'Enable or disable 4G EPS service',
cnFlag: 'Whether to enable 5G Core Network service',
epsFlagTip: 'Whether to enable 4G EPS service',
contextIdTip: 'To sign up for an APN Context ID, you must select it from the APN Context list.',
apnContextTip: 'The list of APNs available to the phone, up to six, is defined in the HSS.',
staticIpTip: 'Specify the static IP address to be used by the cell phone user to access the Internet, and "-" means dynamic IP address is used.',
@@ -709,6 +901,7 @@ export default {
addTitle: 'Adding Policy Control Information',
updateTitle: '{imsi} Policy control information',
startIMSI: 'Start IMSI',
batchOper: 'Batch Operations',
batchAddText: 'Batch Add',
batchDelText: 'Batch Delete',
batchUpdateText: 'Batch Modify',
@@ -717,9 +910,19 @@ export default {
imsiTip1: 'MCC=Mobile Country Code, consisting of three digits.',
imsiTip2: 'MNC = Mobile Network Number, consisting of two digits',
imsiTip3: 'MSIN = Mobile Subscriber Identification Number, consisting of 10 equal digits.',
checkDel: 'Check Delete',
delSure:'Are you sure you want to delete the user with IMSI number: {imsi}?',
uploadFileOk: 'File Upload Successful',
uploadFileErr: 'File Upload Failed',
pccRuleTip:'PCC policy rule template (corresponding to parameter configuration -PCC Rules)',
sessRuleTip:' Session policy rule template (corresponding to parameter configuration-session Rules)',
qosAudioTip:' Voice call QoS(corresponding parameter configuration -QoS Template QoS ID)',
qosVideoTip:' Video call QoS(corresponding parameter configuration -QoS Template QoS ID)',
hdrTip:'HTTP Header enhancement (corresponding parameter configuration -Header Enrich Template)',
ueTip:'UE policy template (example: uep_001)',
sarTip1:' Service area Restriction ',
sarTip2:'(corresponding parameter setting -Service Area Restriction)',
rfsfTip:'RAT Frequency Selection Priority',
},
base5G: {
neType: 'NE Object',
@@ -755,7 +958,7 @@ export default {
viewTask:'View Task',
editTask:'Edit Task',
addTask:'Add Task',
stopTask:'Pending',
stopTask:'Stop',
errorTaskInfo: 'Failed to obtain task information',
granulOptionPlease:'Please select the measurement granularity',
letupSure:'Confirm activation of task with number [{id}]?',
@@ -815,7 +1018,7 @@ export default {
exportSure:'Confirm whether to export all statistical data',
exportEmpty: "Export data is empty",
showChartSelected: "Show All",
realTimeData: "Real Time 5s Data",
realTimeData: "Real Time Data",
},
customTarget:{
kpiId:' Custom Indicator',
@@ -944,7 +1147,7 @@ export default {
alarmSeq:'Sequence Number',
objectName:'Object Name',
locationInfo:'Location Info',
province:'Province',
province:'Region',
addInfo:'Additional Info',
specificProblemId:'Cause ID',
specificProblem:'Cause',
@@ -965,6 +1168,7 @@ export default {
showSet:'Show filter settings',
exportSure:'Confirm whether to export all active alarm information',
viewIdInfo:'View {alarmId} record information',
closeModal:'Close',
},
historyAlarm:{
exportSure:'Confirm whether to export all historical alarm information',
@@ -977,13 +1181,16 @@ export default {
noChange:'There is no change in the alarm forwarding settings.',
forwardSet:'Alarm Forwarding Setting',
},
eventAlarm:{
exportSure:'Confirm whether to export all event alarm information',
}
},
logManage:{
alarm:{
type:'NE Type',
neId:'NE UID',
alarmId:'Alarm ID',
alarmSeq:'Sequece Number',
alarmSeq:'Sequence Number',
alarmCode:'Alarm Code',
alarmStatus:'Status',
eventTime:'Event Time',
@@ -1002,12 +1209,13 @@ export default {
type:'NE Type',
neId:'NE UID',
alarmId:'Alarm ID',
alarmSeq:'Sequece Number',
alarmObj:'Object',
alarmSeq:'Sequence Number',
alarmObj:'Forward Users',
alarmInter:'Forward Interface',
alarmTitle:'Alarm Title',
alarmInfo:'Alarm Content',
eventTime:'Generation Time',
logTime:'Record Time'
alarmInfo:'Operation Results',
eventTime:'Event Time',
logTime:'Log Time'
},
neFile: {
neType:'NE Type',
@@ -1152,12 +1360,12 @@ export default {
jobLog: {
jobName: "Job Name",
jobGroup: "Job Group",
invokeTarget: "Invoke Target",
invokeTarget: "Job Invoke",
status: "Status",
status0: "Failures",
status1: "Active",
createTime: "Create Time",
costTime: "Cost Time",
createTime: "Time",
costTime: "Time Lap",
viewLog: "Scheduling log Info",
delTip: "Are you sure you want to delete the scheduling log entry with the number [{num}]?",
delOk: "Deleted Successfully",
@@ -1191,7 +1399,7 @@ export default {
cacheInfo: {
baseInfo: "Basic Info",
version: "Service Versions",
mode: "Perating Mode",
mode: "Operating Mode",
modeStandalone: "stand-alone",
modeClusters: "clusters",
port: "Port",
@@ -1392,15 +1600,15 @@ export default {
userInfo:' User Info',
userNum: 'User Number',
account: 'Account',
userName: 'User Name',
userName: 'Nick Name',
permission: 'Role',
className: 'Department',
loginIp: 'Login Address',
loginTime: 'Login Time',
status: 'Status',
userNameTip:'The account cannot start with a number and can contain uppercase and lowercase letters, numbers, and no less than 5 digits',
userNameTip:'The account number can only contain strings of uppercase letters, lowercase letters and numbers with a minimum length of 6 digits',
passwdTip:'The password should contain at least uppercase and lowercase letters, numbers, special symbols, and no less than 6 digits',
nickNameTip:'Nicknames can only contain letters, numbers, Chinese characters, and underscores, with no less than 2 digits',
nickNameTip:'Nicknames no less than 2 digits',
emailTip:'Please enter the correct email address',
phoneTip:'Please enter the correct phone number',
resetPwd:'Reset Password',
@@ -1423,7 +1631,7 @@ export default {
userWork:'User position',
userWorkPlease: 'Please select user post',
userTip:'User Description',
loginPwd:'Login password',
loginPwd:'Password',
updateSure:'Do you want to update existing data',
downloadObj:'Download Tpl',
importTitle:'User Import',
@@ -1509,6 +1717,9 @@ export default {
i18nOpen: "Display Switch",
i18nDefault: "Default Languages",
i18nInstruction: 'Whether to display the internationalization switch and set the system default language',
reset: "System Reset",
resetInstruction: "A system reset will erase all data in the current system, please proceed with caution!!!!",
resetTipContent: 'Are you sure you want to clear all data from the current system and insist on continuing?',
},
role:{
allScopeOptions:'All data permissions',
@@ -1516,11 +1727,11 @@ export default {
onlyClassScopeOptions:'Data permissions of this department',
classAllScopeOptions:'Data permissions for this department and the following',
myselfScopeOptions:'Only personal data permissions',
roleId:'Role ID',
roleId:'Role Number',
roleName:'Role Name',
roleKey:'Role Key',
roleSort:'Role Order',
roleStatus:'Status',
roleSort:'Role Sort',
roleStatus:'Role Status',
createTime:'Creation Time',
trueValue:'Please enter {msg} correctly',
roleInfo:'Role information',
@@ -1533,10 +1744,10 @@ export default {
distributeUser:'Assign Users',
roleMark:'Role Description',
menu:'Menu Permissions',
roleKeyTip:"Example of permission identification: Use permission identification in dba controller, such as: @PreAuthorize({ hasRoles: ['dba'] })",
roleKeyTip:"Privilege identifiers are used to control page controls or routing interfaces",
openSwitch:'Expand/Collapse',
selAllSwitch:'Select all/Deselect all',
relationSwitch:'Father and son linkage',
selAllSwitch:'Select All/Deselect All',
relationSwitch:'Node Linkage',
normal:'Active',
stop:'Pending',
preScope:'Scope of authority',
@@ -1559,7 +1770,7 @@ export default {
className:'Department Name',
classId:'Department Number',
classSort:'Department Sorting',
status:'Position Status',
status:'Department Status',
createTime:'Creation Time',
highClass:'Higher Office',
emailTip:'Please input the correct email address',
@@ -1578,7 +1789,7 @@ export default {
positionId:'Position Number',
positionCode:'Position Code',
positionName:'Position Name',
positionSort:'Position Sorting',
positionSort:'Position Sort',
positionStatus:'Position Status',
positionMark:'Position Description',
createTime:'Creation Time',
@@ -1588,15 +1799,15 @@ export default {
},
log:{
operate:{
operId:'ID',
operId:'Log ID',
moduleName:'Module Name',
workType:'Business Type',
workType:'Operation',
operUser:'Operator',
requestMe:'Request Method',
host:'Request Host',
operStatus:'Operation Status',
operDate:'Operation Date',
useTime:'Consumption Time',
operStatus:'Status',
operDate:'Time',
useTime:'Time Lap',
logInfo:'Operation Log Information',
delSure:'Are you sure to delete the data item with access number [{ids}]?',
delAllSure:'Confirm to clear all login log data items?',
@@ -1703,6 +1914,66 @@ export default {
exportOk: "Completed export",
typeDataErr: "Failed to get dictionary type information",
},
quickStart: {
start: 'Start Setup',
skip: 'Skip',
finish: 'Complete Setup',
stepPrev: 'Previous',
stepNext: 'Next',
exit: 'Exit',
save: 'Save Info',
sysTitle: 'System Configuration',
sysAdmin: 'Administrator',
sysInfo: 'System Info',
sysLogo: 'System Logo',
sysLogoTip: 'Show the image to the system logo area to see the effect, please use a transparent background, the size of the proportion to adapt to the size of the area',
sysName: 'System Name',
sysNameTip: 'Limit system name to 20 characters in length',
sysUploadLogo: 'Confirmation of uploading the system logo file?',
sysUploadOk: 'File uploaded successfully, please save the information',
sysSave: 'Save',
sysSaveOk: 'Info saved successfully!',
sysPrevTip: 'Confirmed to go back to the previous step?',
sysNextNe: 'Confirming that you want to do a network element installation?',
sysNextDone: 'Confirmed to skip the network element installation?',
stepNeInfoTitle: "Service Configuration",
stepNeInfoDesc: "Setting the service terminal corresponding to a network element",
stepNeInfoStepPrev: 'Confirming that you want to exit the network element installation step?',
stepNeInfoStepNext: 'Confirm that you want to proceed to the next step to configure the parameters of the network element?',
stepPara5GTitle: "Configuration Parameter",
stepPara5GDesc: "Setting network element global parameter information",
savePara5GOk: 'Save Success!',
stepPara5GStepPrev: 'Confirm that you want to abandon the current change and return to the previous step?',
stepPara5GStepNext: 'Confirm that you want to proceed to the next step for the network element service installation?',
stepInstallTitle: "Service Install",
stepInstallDesc: "Installation of network element services to service terminals",
stepInstallStepPrev: 'Confirm that you want to abandon the current change and return to the previous step?',
stepInstallStepNext: 'Confirm that you want to proceed to the next step for network element license authorization?',
stepInstallText: 'Select Install',
stepInstallTip: 'Confirm the installation of the new version of the chosen Net Meta?',
stepInstallModal: 'Network Element For Install',
stepInstallNotNewVer: 'No new version found',
stepInstallDone: 'Installation complete, service initialized',
stepInstallFail: 'Installation fails, check if the service terminal environment is available!',
stepLicenseTitle: "Service License",
stepLicenseDesc: "Obtaining a license activation code for authorization authentication",
stepLicenseReload: 'Select Refresh',
stepLicenseReloadTip: 'Confirm refreshing selected license information?',
stepLicenseDownCode: 'Select Download Activation Code',
stepLicenseDownCodeTip: 'Confirmed to download the selected network element license activation code to a text file?',
stepLicenseStepPrev: 'Confirm that you want to abandon the current change and return to the previous step?',
stepLicenseStepNext: 'Confirmed to end the network element installation step?',
stepLicenseStepNext2: 'Please download the Net Element License Authorization Code file to save it and contact the Net Element vendor to get the authorization license',
stepLicenseEnd: 'End',
doneTitle: "Completing the Configuration",
doneTip: 'Please enter the system and configure it as appropriate.',
doneNETitle: 'Configuration of the network element installation has been performed',
doneNEDesc: 'In case of abnormal network elements, the license can be reinstalled in the system',
doneSkipTitle: 'No network element installation configuration',
doneSkipDesc: 'The system will initialize the network element information by default, which can be modified or installed within the system.',
donePrevTip: 'Confirmed to go back to the previous step?',
doneOkTip: 'Confirm that you have completed the setup and started using it?',
},
},
mmlManage: {
cmdTitle: "Command Navigator",
@@ -1710,14 +1981,14 @@ export default {
cmdOpTip: "Select the item to be operated in the left command navigation!",
cmdNoTip: "{num} no optional command operation",
require: "Mandatory parameter: {num}",
requireUn: "{display} input value is of unknown type",
requireString: "The {display} parameter value is invalid.",
requireInt: "{display} Parameter value not in reasonable range {filter}",
requireIpv4: "{display} not a legitimate IPV4 address",
requireIpv6: "{display} Not a legitimate IPV6 address.",
requireEnum: "{display} is not a reasonable enumeration value.",
requireBool: "{display} is not a reasonable boolean value.",
requireFile: "{display} is not a value that matches the parameter file type.",
requireUn: "[ {display} ] input value is of unknown type",
requireString: "[ {display} ] parameter value is invalid.",
requireInt: "[ {display} ] parameter value not in reasonable range {filter}",
requireIpv4: "[ {display} ] not a legitimate IPV4 address",
requireIpv6: "[ {display} ] not a legitimate IPV6 address.",
requireEnum: "[ {display} ] is not a reasonable enumeration value.",
requireBool: "[ {display} ] is not a reasonable boolean value.",
requireFile: "[ {display} ] is not a value that matches the parameter file type.",
cmdQuickEntry: "Command Quick Entry",
cmdQuickEntryHelp: "Line feed (Shift + Enter), Execute (Enter)",
cmdParamPanel: "Parameter Panel",

View File

@@ -2,6 +2,7 @@ export default {
// 语言
i18n: '中文',
hello: '你好',
welcome: '欢迎使用,核心网管理平台',
// 通用
common: {
@@ -34,7 +35,6 @@ export default {
unableNull:'不能为空',
moreText: '更多',
searchBarText: '搜索栏',
tableStripedText: '表格斑马纹',
reloadText: '刷新',
columnSetText: '列设置',
columnSetTitle: '列展示/排序',
@@ -53,9 +53,15 @@ export default {
fold: '折叠',
},
rowId: '编号',
operate: '操作',
createTime: '创建时间',
updateTime: '更新时间',
remark: '备注',
description: '说明',
operate: '更多操作',
operateOk: '操作成功!',
operateErr: '操作失败!',
copyText: "复制",
copyOk: '复制成功!',
units: {
second: '秒',
minute: '分钟',
@@ -124,12 +130,14 @@ export default {
onlyAllow:'只支持上传文件格式',
},
LockScreen: {
inputPlacePwd:'请输入登录密码',
inputPlacePwd:'请输入锁屏密码',
validSucc:'校验通过',
validError:'校验失败',
backLogin:'退出并重新登录',
backReload:'正在重启,请稍等...',
backReload2:'当准备就绪的时候,你的浏览器会自动刷新。',
systemReset:'正在重置,请稍等...',
systemReset2:'数据信息正在重置',
},
},
@@ -141,6 +149,7 @@ export default {
page403: '没有访问权限',
page404: '找不到匹配页面',
helpDoc: '系统使用文档',
lockScreen: '锁屏',
account: {
index: "个人中心",
profile: "个人信息",
@@ -164,6 +173,9 @@ export default {
codeHit: '验证码',
codeText: '获取验证码',
codeSmsSend: '发送成功,请注意查看短信',
ipPlease: '请输入有效的IP地址',
ipv4Reg: '不是有效IPv4地址',
ipv6Reg: '不是有效IPv6地址',
},
// 布局
@@ -175,6 +187,8 @@ export default {
rightContent: {
lock: "锁屏",
lockTip: "确认要进行锁屏吗?",
lockPasswd: "解锁密码",
lockPasswdTip: "可不设置密码",
helpDoc: "系统使用文档",
fullscreen: "全屏显示",
logout: "退出登录",
@@ -262,10 +276,10 @@ export default {
email: "电子邮箱",
emailPleace: "请输入正确的电子邮箱",
phonenumber: "手机号码",
phonenumberPleace: "请输入正确的电子邮箱",
phonenumberPleace: "请输入正确的手机号码",
nick: "用户昵称",
nickPleace: "请输入用户昵称",
nickTip: "昵称只能包含字母、数字、中文和下划线,且不少于2位",
nickTip: "昵称少于2位",
profileTip: "确认要提交修改用户基本信息吗?",
profileOk: "用户基本信息修改成功!",
know: "我知道了",
@@ -424,15 +438,14 @@ export default {
letUpTime:'激活时间',
createTime:'创建时间',
onlyAble:'只支持上传文件格式 {fileText}',
nullData:'暂无网元列表数据',
nullVersion:'当前网元无可回退版本',
},
license: {
neTypePlease: '选择网元类型',
neType: '网元类型',
fileName: '文件名',
createTime: '上传时间',
comment: '文件说明',
serialNum: '序列号',
createTime: '时间',
comment: '说明',
updateComment: 'License说明',
updateCommentPlease: '请输入License说明',
updateFile: 'License文件',
@@ -471,21 +484,21 @@ export default {
neType: "网元类型",
neTypePleace: "请选择网元类型",
noConfigData: "暂无配置项数据",
updateValue: "{num} 属性值修改成功",
updateValue: "{num} 属性值修改成功",
updateValueErr: "属性值修改失败",
updateItem: "修改 Index 为 {num} 记录成功",
updateItemErr: "记录修改失败",
delItemOk: "删除 Index 为 {num} 记录成功",
addItemOk: "新增 Index 为 {num} 记录成功",
addItemErr: "记录新增失败",
requireUn: "{display} 输入值是未知类型",
requireString: "{display} 参数值不合理",
requireInt: "{display} 参数值不在合理范围 {filter}",
requireIpv4: "{display} 不是合法的IPV4地址",
requireIpv6: "{display} 不是合法的IPV6地址",
requireEnum: "{display} 不是合理的枚举值",
requireBool: "{display} 不是合理的布尔类型的值",
editOkTip: "确认更新该{num}属性值吗?",
requireUn: "{display} 输入值是未知类型",
requireString: "{display} 参数值不合理",
requireInt: "{display} 参数值不在合理范围 {filter}",
requireIpv4: "{display} 不是合法的IPV4地址",
requireIpv6: "{display} 不是合法的IPV6地址",
requireEnum: "{display} 不是合理的枚举值",
requireBool: "{display} 不是合理的布尔类型的值",
editOkTip: "确认更新该{num}属性值吗?",
updateItemTip: "确认更新Index为 【{num}】 的数据项?",
delItemTip: "确认删除Index为 【{num}】 的数据项?",
arrayMore: "展开",
@@ -549,8 +562,6 @@ export default {
realTimeDataStart: "开启实时数据",
realTimeDataStop: "关闭实时数据",
cdrInfo: "CDR信息",
neName: "网元名称",
rmUID: "资源标识",
time: "记录时间",
rowInfo: "记录信息",
type: "记录类型",
@@ -559,30 +570,66 @@ export default {
called: "被叫",
result: "结果",
delTip: "确认删除编号为【{msg}】的数据项?",
exportTip: "确认导出当前查询条件的话单数据吗?(导出最大支持一万条)",
smfChargingID: '计费ID',
smfSubscriptionIDData: '订阅 ID 数据',
smfSubscriptionIDType: '订阅 ID 类型',
smfDataVolumeUplink: '数据量上行链路',
smfDataVolumeDownlink: '数据量下行链路',
smfDataTotalVolume: '数据总量',
smfDuration: '持续时间',
smfInvocationTime: '调用时间',
},
ue: {
eventType: "事件类型",
realTimeDataStart: "开启实时数据",
realTimeDataStop: "关闭实时数据",
ueInfo: "UE信息",
neName: "网元名称",
rmUID: "资源标识",
rowInfo: "记录信息",
time: "记录时间",
result: "结果",
resultOk: "成功",
delTip: "确认删除编号为【{msg}】的数据项?",
exportTip: "确认导出当前查询条件的事件数据吗?(导出最大支持一万条)",
},
},
ne: {
neInfo: {
version: "网元版本",
common: {
neType: '网元类型',
neTypePlease: "请选择网元类型",
neTypeTip: '填写创建的网元类型,如:SMF',
neId: '网元标识',
neIdPlease: '请输入网元标识',
neIdTip: '填写网元绑定的唯一标识',
rmUid: '资源唯一标识',
rmUidPlease: '请输入资源唯一标识',
rmUidTip: "用于网元日志、告警、指标等数据上报的标记",
neName: '网元名称',
neNamePlease: '请输入网元名称',
ipAddr: '服务IP',
ipAddrPlease: '请输入网元服务IP地址',
ipAddrTip: "支持IPV4/IPV6,同步变更配置地址",
port: '服务端口',
portTip: "网元服务端口,默认:33030",
serialNum: '序列号',
expiryDate: '许可证到期日期',
normalcy: '正常',
exceptions: '异常',
restart: '重启',
restartTip: '确认要重新启动网元服务吗?',
start: '启动',
startTip: '确认要启动网元服务吗?',
stop: '停止',
stopTip: '确认要停止网元服务吗?',
reload: '重载',
reloadTip: '确认要重载网元配置信息吗?',
oam: 'OAM',
log: '日志',
},
neInfo: {
version: "网元版本",
state: "网元状态",
serviceState: "服务状态",
normalcy: "正常",
exceptions: "异常",
serviceState: "服务状态",
info: '状态信息',
resourceInfo: '资源情况',
sysMem: "系统内存",
@@ -590,8 +637,40 @@ export default {
sysDisk: "系统存储",
neCpu: "网元CPU",
hostConfig: "终端连接配置",
rmUID: "数据资源定位标识符用于日志、告警上报等数据标记",
ipAddr: "支持IPV4/IPV6,同步变更下方配置的配置地址",
pvflag: '网元虚拟化标识',
pnf: '物理网元',
vnf: '虚拟网元',
neAddress: '物理地址(MAC)',
neAddressTip: '记录网元的网卡物理地址(MAC)',
dn: '网络标识',
vendorName: '提供厂商',
province: '服务地域',
addTitle: '新增网元信息',
editTitle: '编辑网元信息',
delTip: '确认删除网元信息数据项吗?',
oam: {
title: 'OAM配置',
sync: '同步到网元',
oamEnable: '服务',
oamPort: '端口',
snmpEnable: '服务',
snmpPort: '端口',
kpiEnable: '上报',
kpiTimer: '上报周期',
kpiTimerPlease: '请输入上报周期(单位秒)',
},
backConf: {
export: '配置导出',
import: '配置导入',
title: '配置文件导入',
importType: '文件来源',
server:'服务器文件',
local:'本地文件',
localUpload:'本地上传',
exportTip:'确认要导出网元配置信息到文件?',
exportMsg:'导出网元配置信息到文件成功',
pathPlease: '未发现文件',
},
},
neHost: {
hostType: "主机类型",
@@ -610,13 +689,13 @@ export default {
privateKey: "私钥",
privateKeyPlease: "请正确填写私钥字符内容 ~/.ssh/id_rsa",
passPhrase: "私钥密码",
remark: "备注信息",
createTime: "创建时间",
delTip: "确认要删除主机编号为【{num}】的信息吗?",
addTitle: "新增主机连接",
editTitle: "编辑主机连接",
test: "测试连接",
testOk: "测试连接成功",
authRSA: "免密授权",
authRSATip: "是否要配置免密授权?",
},
neHostCmd: {
cmdType: "命令类型",
@@ -625,12 +704,124 @@ export default {
titlePlease: "请正确填写命令名称",
command: "命令",
commandPlease: "请正确输入有效命令字符串",
remark: "备注",
createTime: "创建时间",
delTip: "确认要删除命令编号为【{num}】的信息吗?",
addTitle: "新增主机命令",
editTitle: "编辑主机命令",
},
neSoftware: {
uploadTitle: "上传软件",
upload: "上传",
uploadNotFile: "未上传软件文件",
uploadBatch: "批量上传",
uploadBatchMax: "可上传多个软件包,最多同时选择{txt}个。",
uploadFileName: "解析文件名称格式如: amf-r2.240x.xx-xxx",
name: "文件名",
path: "软件文件",
pathPlease: "请上传软件包文件",
version: "软件版本",
versionPlease: "请输入软件版本号",
delTip: "确认要删除软件包吗?",
downTip: "确认要下载软件包【{txt}】吗?",
fileCheckType: '未解析出对应的网元类型',
fileCheckVer: '未解析出对应的版本号',
fileTypeNotEq: '不是指定网元类型 {txt}',
fileTypeExists: '已存在相同类型文件',
fileNameExists: '已存在相同名称文件',
fileCheckTypeDep: '未解析出对应指定的依赖包类型',
dependFile: '软件包依赖',
dependFileTip: '文件名解析同上,依据上传顺序安装',
},
neVersion: {
upgrade: "升级到新版本",
upgradeTip: "确认要升级到新版本吗?",
upgradeTipEmpty: "当前没有可用的新版本",
upgradeTipEqual: "当前版本与新版本相同",
rollback: '切换到上一个版本',
rollbackTip: "确认切换到上一个版本吗?",
rollbackTipEmpty: "目前没有可用的上一个版本",
rollbackTipEqual: '当前版本与之前版本相同',
version: "当前版本",
preVersion: "上一个版本",
newVersion: "新版本",
status: "版本状态",
upgradeBatch: "批量更新",
upgradeBatchTip: "对勾选的记录进行新版本升级吗?",
upgradeNotNewVer: '没有发现新版本',
upgradeOMCVer: '拒绝批量操作升级OMC',
upgradeDone: '更新完成,服务正在重载',
upgradeFail: '更新失败,请检查软件文件是否存在且服务终端环境是否可用!',
upgradeModal: '网元版本更新',
},
neLicense: {
status: "许可证状态",
change: "变更许可证",
reload: "刷新信息",
reloadTip: "确认要刷新许可证信息吗?",
reloadBatch: "批量刷新",
reloadBatchTip: "对勾选的记录进行信息刷新吗?",
updateTtile: "更新许可证",
downCodeTop: "确认要将许可激活码保存到文件吗?",
activationRequestCode: "许可激活码",
licensePath: "许可证文件",
licensePathTip: "请上传许可证文件",
upload: '上传',
uploadFile: "上传许可证",
uploadChangeOk: '网元更新许可证成功,正在后台校验!',
uploadChangeFail: "部分网元更新许可证失败,请检查服务终端环境是否可用!",
},
neConfigBackup: {
name: "名称",
downTip: '确认要下载备份文件【{txt}】吗?',
title: "修改备份信息 {txt}",
},
neQuickSetup: {
reloadPara5G: '刷新',
stepPrev: '上一步',
stepPrevTip: '确认要放弃当前变更返回上一步吗?',
stepNext: '下一步',
stepSave: '保存信息',
startTitle: '服务终端环境',
startDesc: '测试连接网元服务',
startStepNext: '确认要下一步进行配置网元信息?',
addr: '终端IP',
kernelName: '系统',
kernelRelease: '内核',
machine: '架构',
prettyName: '平台',
prettyNameTip: '支持 Ubuntu',
nodename: '主机名',
auth: '权限授予',
sudo: '提权',
sudoErr: '确保有权限进行软件包安装,请配置授予当前用户允许无密码 sudo 权限。',
sshLink: '免密直连',
configTitle: "配置网元",
configDesc: "填写网元基础信息",
configAddTitle: '新增提示',
configAddTip: '是否新增为新的网元信息并继续?',
configUpdateTitle: '更新提示',
configUpdateTip: '是否更新到已存在网元信息并继续?',
configStepNext: '确认要下一步进行网元软件安装?',
installTitle: "网元安装",
installDesc: "安装到服务终端",
installConfirmTip: '确认要安装软件包【{name}】吗?',
installStepNext: '确认要下一步进行网元授权吗?',
installSource: '软件来源',
installSourceOption: '已上传',
installSourceUpload: '新上传',
installSelect: '选择记录',
installUpload: '上传文件',
installText: '安装',
licenseTitle: "授权许可",
licenseDesc: "网元服务授权认证",
licenseResultTitle: "是否立即授权激活",
licenseResultTitleOk: '成功激活',
licenseUpload: '许可证',
licenseEnd: '结束',
licenseEndTip: "确认要结束安装吗?",
licenseCheack: '等待网元验证',
licenseTip1: '1. 点击【许可证】可获取许可激活码,随后联系网元厂商进行激活',
licenseTip2: '2. 点击【结束】将结束安装过程',
},
},
neUser: {
auth: {
@@ -644,7 +835,7 @@ export default {
import: '导入',
loadDataConfirm: '确认要重新加载数据吗?',
loadData: '加载数据',
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新。加载结束后可点击重置刷新数据列表,请勿重复点击获取更新!!!',
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新,请稍候!!!',
startIMSI: '起始IMSI',
batchAddText: '批量新增',
batchDelText: '批量删除',
@@ -671,7 +862,7 @@ export default {
import: '导入',
loadDataConfirm: '确认要重新加载数据吗?',
loadData: '加载数据',
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新。加载结束后可点击重置刷新数据列表,请勿重复点击获取更新!!!',
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新,请稍候!!!',
numAdd: '放号个数',
numDel: '删除个数',
checkDel:'勾选删除',
@@ -691,7 +882,8 @@ export default {
micoTip: '签约的 MICO 业务标志位',
rfspTip:'RFSP 索引,在 NG-RAN 中,特定 RRM 配置的索引,参数介于0到127之间',
ueTypeTip: '运营商定义的用户 UE Usage Type整型参数介于0到127之间',
epsFlagTip: '是否开启4G EPS 服务',
cnFlag: '是否开启 5G Core Network 服务',
epsFlagTip: '是否开启 4G EPS 服务',
contextIdTip: '签约APN 上下文ID必须从APN Context list 中选择。',
apnContextTip: '手机可用的APN列表最多六个在HSS中定义。',
staticIpTip: '指定手机用户上网时使用的静态IP地址,为"-"时表示使用动态IP地址',
@@ -709,6 +901,7 @@ export default {
addTitle: '新增策略控制信息',
updateTitle: '{imsi} 策略控制信息',
startIMSI: '起始IMSI',
batchOper: '批量操作',
batchAddText: '批量新增',
batchDelText: '批量删除',
batchUpdateText: '批量更新',
@@ -717,9 +910,19 @@ export default {
imsiTip1: 'MCC=移动国家号码, 由三位数字组成',
imsiTip2: 'MNC=移动网络号,由两位数字组成',
imsiTip3: 'MSIN=移动客户识别码采用等长10位数字构成',
delSure:'确认删除IMSI编号为: {imsi} 的用户吗?',
checkDel:'勾选删除',
delSure:'确认删除IMSI编号为: {imsi} 的数据项吗?',
uploadFileOk: '文件上传成功',
uploadFileErr: '文件上传失败',
pccRuleTip:'PCC策略规则模板(对应参数配置-PCC Rules)',
sessRuleTip:'会话策略规则模板(对应参数配置-Session Rules)',
qosAudioTip:'语音呼叫QoS(对应参数配置-QoS Template的QoS ID)',
qosVideoTip:'视频呼叫QoS(对应参数配置-QoS Template的QoS ID)',
hdrTip:'HTTP头增强(对应参数配置-Header Enrich Template)',
ueTip:'UE策略模板(样例: uep_001)',
sarTip1:'服务区限制',
sarTip2:'(对应参数配置-Service Area Restriction)',
rfsfTip:'无线频率选择优先级',
},
base5G: {
neType: '网元对象',
@@ -755,7 +958,7 @@ export default {
viewTask:'查看任务',
editTask:'修改任务',
addTask:'新增任务',
stopTask:'挂起',
stopTask:'停止',
errorTaskInfo: '获取任务信息失败',
granulOptionPlease:'请选择测量粒度',
letupSure:'确认激活编号为 【{id}】 的任务?',
@@ -815,7 +1018,7 @@ export default {
exportSure:'确认是否导出全部统计数据',
exportEmpty: "导出数据为空",
showChartSelected: "显示全部",
realTimeData: "实时5s数据",
realTimeData: "实时数据",
},
customTarget:{
kpiId:'自定义指标项',
@@ -965,6 +1168,7 @@ export default {
showSet:'显示过滤设置',
exportSure:'确认是否导出全部活动告警信息',
viewIdInfo:'查看{alarmId} 记录信息',
closeModal:'关闭',
},
historyAlarm:{
exportSure:'确认是否导出全部历史告警信息?',
@@ -976,6 +1180,9 @@ export default {
save:'保存设置',
noChange:'告警前转接口设置无变更',
forwardSet:'告警前转接口设置',
},
eventAlarm:{
exportSure:'确认是否导出全部事件告警信息?',
}
},
logManage:{
@@ -1004,8 +1211,9 @@ export default {
alarmId:'告警唯一标识',
alarmSeq:'告警流水号',
alarmObj:'告警前转对象',
alarmInter:'告警前转接口',
alarmTitle:'告警标题',
alarmInfo:'告警内容',
alarmInfo:'操作结果',
eventTime:'告警产生时间',
logTime:'记录时间'
},
@@ -1398,9 +1606,9 @@ export default {
loginIp: '登录地址',
loginTime: '登录时间',
status: '用户状态',
userNameTip:'账号不能以数字开头,可包含大写小写字母,数字,且不少于5位',
userNameTip:'账号只能包含大写字母、小写字母数字的字符串长度至少为6位',
passwdTip:'密码至少包含大小写字母、数字、特殊符号,且不少于6位',
nickNameTip:'昵称只能包含字母、数字、中文和下划线,且不少于2位',
nickNameTip:'昵称不少于2位',
emailTip:'请输入正确的邮箱地址',
phoneTip:'请输入正确的手机号码',
resetPwd:'重置密码',
@@ -1423,7 +1631,7 @@ export default {
userWork:'用户岗位',
userWorkPlease: '请选择用户岗位',
userTip:'用户说明',
loginPwd:'登密码',
loginPwd:'登密码',
updateSure:'是否更新已经存在的数据',
downloadObj:'下载模板',
importTitle:'用户导入',
@@ -1509,6 +1717,9 @@ export default {
i18nOpen: "显示切换",
i18nDefault: "默认语言",
i18nInstruction: '是否显示国际化切换,设置系统默认语言',
reset: "系统重置",
resetInstruction: "系统重置将会清除当前系统内所有数据,请谨慎操作!!!",
resetTipContent: '确认要清除当前系统内所有数据并坚持继续吗?',
},
role:{
allScopeOptions:'全部数据权限',
@@ -1533,10 +1744,10 @@ export default {
distributeUser:'分配用户',
roleMark:'角色说明',
menu:'菜单权限',
roleKeyTip:"权限标识示例:dba 控制器中使用权限标识,如: @PreAuthorize({ hasRoles: ['dba'] })",
roleKeyTip:"权限标识用于控制页面控件或路由接口",
openSwitch:'展开/折叠',
selAllSwitch:'全选/全不选',
relationSwitch:'父子联动',
relationSwitch:'节点联动',
normal:'正常',
stop:'暂停',
preScope:'权限范围',
@@ -1559,7 +1770,7 @@ export default {
className:'部门名称',
classId:'部门编号',
classSort:'部门排序',
status:'岗位状态',
status:'部门状态',
createTime:'创建时间',
highClass:'上级部门',
emailTip:'请输入正确的邮箱地址',
@@ -1590,7 +1801,7 @@ export default {
operate:{
operId:'日志编号',
moduleName:'模块名称',
workType:'业务类型',
workType:'操作类型',
operUser:'操作人员',
requestMe:'请求方式',
host:'请求主机',
@@ -1703,6 +1914,66 @@ export default {
exportOk: "已完成导出",
typeDataErr: "获取字典类型信息失败",
},
quickStart: {
start: '开始设置',
skip: '跳过',
finish: '完成设置',
stepPrev: '上一步',
stepNext: '下一步',
exit: '退出',
save: '保存信息',
sysTitle: '系统配置',
sysAdmin: '管理员',
sysInfo: '系统信息',
sysLogo: '系统LOGO',
sysLogoTip: '将图片展示到系统LOGO区域查看效果请使用透明背景尺寸比例适应区域大小',
sysName: '系统名称',
sysNameTip: '系统名称限制20个字符长度',
sysUploadLogo: '确认要上传系统LOGO文件吗?',
sysUploadOk: '文件上传成功,请进行保存信息',
sysSave: '保存',
sysSaveOk: '信息保存成功!',
sysPrevTip: '确认要返回上一个步骤吗?',
sysNextNe: '确认要进行网元安装吗?',
sysNextDone: '确认要跳过网元安装吗?',
stepNeInfoTitle: "网元服务配置",
stepNeInfoDesc: "设置网元对应的服务终端",
stepNeInfoStepPrev: '确认要退出网元安装步骤吗?',
stepNeInfoStepNext: '确认要下一步进行网元配置参数?',
stepPara5GTitle: "网元配置参数",
stepPara5GDesc: "设置网元全局参数信息",
savePara5GOk: '保存成功!',
stepPara5GStepPrev: '确认要放弃当前变更返回上一步吗?',
stepPara5GStepNext: '确认要下一步进行网元服务安装吗?',
stepInstallTitle: "网元服务安装",
stepInstallDesc: "将网元服务安装到服务终端",
stepInstallStepPrev: '确认要放弃当前变更返回上一步吗?',
stepInstallStepNext: '确认要下一步进行网元许可授权吗?',
stepInstallText: '选择安装',
stepInstallTip: '确认安装选择的网元新版本吗?',
stepInstallModal: '网元进行安装',
stepInstallNotNewVer: '没有发现新版本',
stepInstallDone: '安装完成,服务进入初始化',
stepInstallFail: '安装失败,请检查服务终端环境是否可用!',
stepLicenseTitle: "网元许可授权",
stepLicenseDesc: "获取网元许可激活码进行授权认证",
stepLicenseReload: '选择刷新许可证',
stepLicenseReloadTip: '确认刷新选择的许可证信息吗?',
stepLicenseDownCode: '选择下载网元许可激活码',
stepLicenseDownCodeTip: '确认下载选择的网元许可激活码到文本文件吗?',
stepLicenseStepPrev: '确认要放弃当前变更返回上一步吗?',
stepLicenseStepNext: '确认要结束网元安装步骤吗?',
stepLicenseStepNext2: '请下载网元许可授权码文件保存,并联系网元厂商获取授权许可证',
stepLicenseEnd: '结束',
doneTitle: "完成配置",
doneTip: '请进入系统后,根据情况进行更多相关配置',
doneNETitle: '已经进行网元安装配置',
doneNEDesc: '如有异常网元,可在系统内重新安装授权许可',
doneSkipTitle: '未进行网元安装配置',
doneSkipDesc: '系统将会默认初始网元信息,可在系统内自行修改或进行安装',
donePrevTip: '确认要返回上一个步骤吗?',
doneOkTip: '确认完成设置并开始使用吗?',
},
},
mmlManage: {
cmdTitle: "命令导航",
@@ -1710,14 +1981,14 @@ export default {
cmdOpTip: "左侧命令导航中选择要操作项!",
cmdNoTip: "{num} 无可选命令操作",
require: "必填参数:{num}",
requireUn: "{display} 输入值是未知类型",
requireString: "{display} 参数值不合理",
requireInt: "{display} 参数值不在合理范围 {filter}",
requireIpv4: "{display} 不是合法的IPV4地址",
requireIpv6: "{display} 不是合法的IPV6地址",
requireEnum: "{display} 不是合理的枚举值",
requireBool: "{display} 不是合理的布尔类型的值",
requireFile: "{display} 不是符合参数文件类型的值",
requireUn: "{display} 输入值是未知类型",
requireString: "{display} 参数值不合理",
requireInt: "{display} 参数值不在合理范围 {filter}",
requireIpv4: "{display} 不是合法的IPV4地址",
requireIpv6: "{display} 不是合法的IPV6地址",
requireEnum: "{display} 不是合理的枚举值",
requireBool: "{display} 不是合理的布尔类型的值",
requireFile: "{display} 不是符合参数文件类型的值",
cmdQuickEntry: "命令快速输入",
cmdQuickEntryHelp: "换行(Shift + Enter) 执行发送(Enter)",
cmdParamPanel: "参数面板",

View File

@@ -4,12 +4,20 @@ import {
WaterMark,
getMenuData,
clearMenuItem,
MenuDataItem,
type MenuDataItem,
} from 'antdv-pro-layout';
import RightContent from './components/RightContent.vue';
import Tabs from './components/Tabs.vue';
import GlobalMask from '@/components/GlobalMask/index.vue';
import { scriptUrl } from '@/assets/js/icon_font_8d5l8fzk5b87iudi';
import { computed, reactive, watch, onMounted, onUnmounted } from 'vue';
import {
computed,
reactive,
watch,
onMounted,
onUnmounted,
nextTick,
} from 'vue';
import useLayoutStore from '@/store/modules/layout';
import useRouterStore from '@/store/modules/router';
import useTabsStore from '@/store/modules/tabs';
@@ -21,6 +29,7 @@ const { proConfig, waterMarkContent } = useLayoutStore();
import useI18n from '@/hooks/useI18n';
import { getServerTime } from '@/api';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { parseDateToStr } from '@/utils/date-utils';
import { parseUrlPath } from '@/plugins/file-static-url';
const { t, currentLocale } = useI18n();
@@ -159,8 +168,28 @@ function fnLocale(m: MenuDataItem) {
return title;
}
/**检查系统名称是否超出范围进行滚动 */
function fnCheckAppNameOverflow() {
const container: HTMLDivElement | null = document.querySelector('.app-name');
if (!container) return;
const text: HTMLDivElement | null = container.querySelector('.marquee');
if (!text) return;
if (text.offsetWidth > container.offsetWidth) {
text.classList.add('app-name_scrollable');
text.setAttribute('data-content', text.innerText);
} else {
text.classList.remove('app-name_scrollable');
}
}
watch(
() => appStore.appName,
() => nextTick(fnCheckAppNameOverflow)
);
//
onMounted(() => {
fnCheckAppNameOverflow();
fnGetServerTime();
useAlarmStore().fnGetActiveAlarmInfo();
});
@@ -224,12 +253,10 @@ onUnmounted(() => {
v-model:selectedKeys="layoutState.selectedKeys"
v-model:openKeys="layoutState.openKeys"
:menu-data="menuData"
:breadcrumb="{ routes: breadcrumb } as any"
disable-content-margin
:breadcrumb="{ routes: breadcrumb }"
v-bind="proConfig"
:iconfont-url="scriptUrl"
:sider-width="208"
:locale="(fnLocale as any)"
:locale="fnLocale"
>
<!--插槽-菜单头-->
<template #menuHeaderRender>
@@ -246,8 +273,10 @@ onUnmounted(() => {
:alt="appStore.appName"
:title="appStore.appName"
/>
<h1 class="title" :title="appStore.appName">
{{ appStore.appName }}
<h1 class="app-name" :title="appStore.appName">
<span class="marquee app-name_scrollable">
{{ appStore.appName }}
</span>
</h1>
</template>
<template v-if="appStore.logoType === 'brand'">
@@ -265,7 +294,7 @@ onUnmounted(() => {
<template #headerContentRender></template>
<!--插槽-顶部右侧-->
<template #rightContentRender>
<template #headerContentRightRender>
<RightContent />
</template>
@@ -300,9 +329,6 @@ onUnmounted(() => {
</transition>
</RouterView>
<!-- 锁屏遮罩 -->
<LockScreen />
<!--插槽-内容底部-->
<template #footerRender="{ width }">
<footer class="footer">
@@ -317,11 +343,17 @@ onUnmounted(() => {
:href="appStore.officialUrl"
target="_blank"
size="small"
v-perms:has="['system:setting:official']"
v-if="appStore.officialUrl !== '#'"
>
{{ t('loayouts.basic.officialUrl') }}
</a-button>
<a-button type="link" size="small" @click="fnClickHelpDoc()">
<a-button
type="link"
size="small"
v-perms:has="['system:setting:doc']"
@click="fnClickHelpDoc()"
>
{{ t('loayouts.basic.helpDoc') }}
</a-button>
</a-space>
@@ -329,6 +361,9 @@ onUnmounted(() => {
</footer>
</template>
</ProLayout>
<!-- 全局遮罩 -->
<GlobalMask />
</WaterMark>
</template>
@@ -349,18 +384,40 @@ onUnmounted(() => {
margin-right: 16px;
}
.title {
.app-name {
overflow: hidden;
text-overflow: ellipsis;
// text-overflow: ellipsis;
white-space: nowrap;
width: 130px;
width: 148px;
> .app-name_scrollable {
padding-right: 12px;
display: inline-block;
animation: scrollable-animation linear 6s infinite both;
&::after {
content: attr(data-content);
position: absolute;
top: 0;
right: -100%;
transition: right 0.3s ease;
}
@keyframes scrollable-animation {
0% {
transform: translateX(0);
}
100% {
transform: translateX(calc(-100% - 12px));
}
}
}
}
.footer {
z-index: 16;
margin: 0px;
width: auto;
margin-top: 52px;
margin-top: 32px;
&-fixed {
position: fixed;
bottom: 0;

View File

@@ -1,19 +1,20 @@
<script setup lang="ts">
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
import Modal from 'ant-design-vue/lib/modal/Modal';
import { useRouter } from 'vue-router';
import { useRoute, useRouter } from 'vue-router';
import { useFullscreen } from '@vueuse/core';
import useI18n from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import useUserStore from '@/store/modules/user';
import useAlarmStore from '@/store/modules/alarm';
import useLockedStore from '@/store/modules/locked';
import useMaskStore from '@/store/modules/mask';
import { hasPermissions } from '@/plugins/auth-user';
import { ref } from 'vue';
const { isFullscreen, toggle } = useFullscreen();
const { t, changeLocale, optionsLocale } = useI18n();
const lockedStore = useLockedStore();
const maskStore = useMaskStore();
const userStore = useUserStore();
const appStore = useAppStore();
const route = useRoute();
const router = useRouter();
/**头像展开项点击 */
@@ -31,15 +32,24 @@ function fnClick({ key }: MenuInfo) {
}
}
/**锁屏确认 */
const lockConfirm = ref<boolean>(false);
/**锁屏密码 */
const lockPasswd = ref<string>('');
/**锁屏按钮提示 */
function fnClickLock() {
Modal.confirm({
title: t('common.tipTitle'),
content: t('loayouts.rightContent.lockTip'),
onOk() {
lockedStore.fnLock('lock');
},
});
lockConfirm.value = true;
lockPasswd.value = '';
maskStore.lockPasswd = '';
}
/**锁屏确认跳转锁屏页面 */
function fnClickLockToPage() {
lockConfirm.value = false;
maskStore.lockPasswd = lockPasswd.value;
maskStore.handleMaskType('lock');
router.push({ name: 'LockScreen', query: { redirect: route.path } });
}
/**告警数按钮提示跳转 */
@@ -65,39 +75,72 @@ function fnChangeLocale(e: any) {
<template>
<a-space :size="12" align="center">
<a-button type="text" @click="fnClickAlarm">
<a-button type="text" style="color: inherit" @click="fnClickAlarm">
<template #icon>
<a-badge
:count="useAlarmStore().activeAlarmTotal"
:overflow-count="99"
status="warning"
style="color: inherit"
>
<BellOutlined />
</a-badge>
</template>
</a-button>
<a-tooltip placement="bottom">
<template #title>{{ t('loayouts.rightContent.lock') }}</template>
<a-button type="text" @click="fnClickLock">
<template #icon>
<LockOutlined />
</template>
</a-button>
</a-tooltip>
<!-- 锁屏操作 -->
<span v-perms:has="['system:setting:lock']">
<a-tooltip placement="bottom">
<template #title>{{ t('loayouts.rightContent.lock') }}</template>
<a-button type="text" style="color: inherit" @click="fnClickLock()">
<template #icon>
<LockOutlined />
</template>
</a-button>
<ProModal
:drag="true"
:width="400"
:minHeight="200"
:mask-closable="false"
v-model:visible="lockConfirm"
:title="t('loayouts.rightContent.lockTip')"
@ok="fnClickLockToPage()"
>
<a-space>
{{ t('loayouts.rightContent.lockPasswd') }}
<a-input-password
v-model:value="lockPasswd"
:placeholder="t('common.inputPlease')"
>
<template #prefix>
<a-tooltip
:title="t('loayouts.rightContent.lockPasswdTip')"
placement="topLeft"
>
<UnlockOutlined />
</a-tooltip>
</template>
</a-input-password>
</a-space>
</ProModal>
</a-tooltip>
</span>
<a-tooltip placement="bottom">
<template #title>{{ t('loayouts.rightContent.helpDoc') }}</template>
<a-button type="text" @click="fnClickHelpDoc()">
<template #icon>
<QuestionCircleOutlined />
</template>
</a-button>
</a-tooltip>
<!-- 用户帮助手册 -->
<span v-perms:has="['system:setting:doc']">
<a-tooltip placement="bottom">
<template #title>{{ t('loayouts.rightContent.helpDoc') }}</template>
<a-button type="text" style="color: inherit" @click="fnClickHelpDoc()">
<template #icon>
<QuestionCircleOutlined />
</template>
</a-button>
</a-tooltip>
</span>
<a-tooltip placement="bottom">
<template #title>{{ t('loayouts.rightContent.fullscreen') }}</template>
<a-button type="text" @click="toggle">
<a-button type="text" style="color: inherit" @click="toggle">
<template #icon>
<FullscreenExitOutlined v-if="isFullscreen" />
<FullscreenOutlined v-else />
@@ -107,7 +150,7 @@ function fnChangeLocale(e: any) {
<a-dropdown
placement="bottom"
:trigger="['click', 'hover']"
trigger="click"
v-if="appStore.i18nOpen && hasPermissions(['system:setting:i18n'])"
>
<a-button size="small" type="default">
@@ -123,7 +166,7 @@ function fnChangeLocale(e: any) {
</template>
</a-dropdown>
<a-dropdown placement="bottomRight" :trigger="['click', 'hover']">
<a-dropdown placement="bottomRight" trigger="click">
<div class="user">
<a-avatar
shape="circle"

View File

@@ -95,7 +95,7 @@ function fnTabClose(path: string) {
/**
* 国际化翻译转换
*/
function fnLocale(title: string) {
function fnLocale(title: string) {
if (title.indexOf('router.') !== -1) {
title = t(title);
}
@@ -150,7 +150,7 @@ watch(router.currentRoute, v => tabsStore.tabOpen(v), { immediate: true });
</a-tooltip>
<a-tooltip placement="topRight">
<template #title>{{ t('loayouts.tabs.more') }}</template>
<a-dropdown :trigger="['click', 'hover']" placement="bottomRight">
<a-dropdown trigger="click" placement="bottomRight">
<a-button type="ghost" shape="circle" size="small">
<template #icon><DownOutlined /></template>
</a-button>

View File

@@ -4,7 +4,9 @@ import App from './App.vue';
import router from './router';
import directive from './directive';
import i18n from './i18n';
import ProModal from "antdv-pro-modal";
import 'antdv-pro-layout/dist/style.css';
import 'antdv-pro-modal/dist/style.css';
import 'ant-design-vue/dist/antd.variable.min.css';
const app = createApp(App);
@@ -12,5 +14,6 @@ app.use(store);
app.use(router);
app.use(directive);
app.use(i18n);
app.use(ProModal);
app.mount('#app');

View File

@@ -1,7 +1,7 @@
import Cookies from 'js-cookie';
import { TOKEN_COOKIE } from '@/constants/token-constants';
import { localRemove, localSet } from '@/utils/cache-local-utils';
import { CACHE_LOCAL_LOCK } from '@/constants/cache-keys-constants';
import { CACHE_LOCAL_LOCK_PASSWD, CACHE_LOCAL_MASK } from '@/constants/cache-keys-constants';
/**获取cookis中Token字符串 */
export function getToken(): string {
@@ -11,11 +11,12 @@ export function getToken(): string {
/**设置cookis中Token字符串 */
export function setToken(token: string): void {
Cookies.set(TOKEN_COOKIE, token);
localSet(CACHE_LOCAL_LOCK, 'none');
localSet(CACHE_LOCAL_MASK, 'none');
}
/**移除cookis中Token字符串,localStorage中锁屏字符串 */
export function removeToken(): void {
Cookies.remove(TOKEN_COOKIE);
localRemove(CACHE_LOCAL_LOCK);
localRemove(CACHE_LOCAL_MASK);
localRemove(CACHE_LOCAL_LOCK_PASSWD);
}

View File

@@ -165,8 +165,8 @@ function beforeRequest(options: OptionsType): OptionsType | Promise<any> {
}
}
// get请求拼接地址栏参数
if (options.method === 'get' && options.params) {
// 请求拼接地址栏参数
if (options.params) {
let paramStr = '';
const params = options.params;
for (const key in params) {

View File

@@ -87,6 +87,18 @@ const constantRoutes: RouteRecordRaw[] = [
meta: { title: 'router.helpDoc' },
component: () => import('@/views/tool/help/index.vue'),
},
{
path: '/quick-start',
name: 'QuickStart',
meta: { title: 'router.quickStart' },
component: () => import('@/views/system/quick-start/index.vue'),
},
{
path: '/lock-screen',
name: 'LockScreen',
meta: { title: 'router.lockScreen' },
component: () => import('@/views/tool/lockScreen/index.vue'),
},
{
path: '/redirect',
name: 'Redirect',
@@ -136,18 +148,35 @@ router.afterEach((to, from, failure) => {
});
/**无Token可访问页面地址白名单 */
const WHITE_LIST: string[] = ['/login', '/auth-redirect', '/help', '/register'];
const WHITE_LIST: string[] = [
'/login',
'/auth-redirect',
'/help',
'/register',
'/quick-start',
];
/**全局路由-前置守卫 */
router.beforeEach((to, from, next) => {
router.beforeEach(async (to, from, next) => {
NProgress.start();
const token = getToken();
// 获取系统配置信息
const appStore = useAppStore();
if (!appStore.loginBackground) {
appStore.fnSysConf();
await appStore.fnSysConf();
}
// 需要系统引导跳转
if (appStore.bootloader && to.path !== '/quick-start') {
next({ name: 'QuickStart' });
}
// 不重复引导
if (!appStore.bootloader && to.path === '/quick-start') {
next({ name: 'Index' });
}
const token = getToken();
// 没有token
if (!token) {
if (WHITE_LIST.includes(to.path)) {
@@ -168,31 +197,27 @@ router.beforeEach((to, from, next) => {
// 判断当前用户是否有角色信息
const user = useUserStore();
if (user.roles && user.roles.length === 0) {
// 获取用户信息
user
.fnGetInfo()
.then(() => {
return useRouterStore().generateRoutes();
})
.then(accessRoutes => {
// 根据后台配置生成可访问的路由表
if (accessRoutes && accessRoutes.length !== 0) {
for (const route of accessRoutes) {
// 动态添加可访问路由表http开头会异常
if (!validHttp(route.path)) {
router.addRoute(route);
}
try {
// 获取用户信息
await user.fnGetInfo();
// 获取路由信息
const accessRoutes = await useRouterStore().generateRoutes();
// 根据后台配置生成可访问的路由表
if (accessRoutes && accessRoutes.length !== 0) {
for (const route of accessRoutes) {
// 动态添加可访问路由表http开头会异常
if (!validHttp(route.path)) {
router.addRoute(route);
}
}
// 刷新替换原先路由确保addRoutes已完成
next({ ...to, replace: true });
})
.catch(e => {
console.error(`[${to.path}]: ${e.message}`);
user.fnLogOut().finally(() => {
next({ name: 'Login' });
});
});
}
// 刷新替换原先路由确保addRoutes已完成
next({ ...to, replace: true });
} catch (error: any) {
console.error(`[${to.path}]: ${error.message}`);
await user.fnLogOut();
next({ name: 'Login' });
}
} else {
next();
}

View File

@@ -1,6 +1,7 @@
import { getSysConf } from '@/api';
import { CACHE_LOCAL_I18N } from '@/constants/cache-keys-constants';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { removeToken } from '@/plugins/auth-token';
import { parseUrlPath } from '@/plugins/file-static-url';
import { localGet, localSet } from '@/utils/cache-local-utils';
import { defineStore } from 'pinia';
@@ -17,6 +18,8 @@ type AppStore = {
/**服务版本 */
version: string;
buildTime: string;
/**系统引导使用 */
bootloader: boolean;
// 序列号
serialNum: string;
/**应用版权声明 */
@@ -48,6 +51,7 @@ const useAppStore = defineStore('app', {
version: `-`,
buildTime: `-`,
bootloader: false,
serialNum: `-`,
copyright: `Copyright ©2023 For ${import.meta.env.VITE_APP_NAME}`,
logoType: 'icon',
@@ -57,7 +61,7 @@ const useAppStore = defineStore('app', {
loginBackground: '',
helpDoc: '',
officialUrl: '',
i18nOpen: true,
i18nOpen: false,
i18nDefault: 'en_US',
}),
getters: {},
@@ -70,16 +74,17 @@ const useAppStore = defineStore('app', {
document.title = this.appName;
}
},
/**设置版权声明 */
setCopyright(text: string) {
this.copyright = text;
},
// 获取系统配置信息
async fnSysConf() {
const res = await getSysConf();
if (res.code === RESULT_CODE_SUCCESS && res.data) {
this.version = res.data.version;
this.buildTime = res.data.buildTime;
this.bootloader = res.data.bootloader === 'true';
// 引导时
if (this.bootloader) {
removeToken();
}
this.serialNum = res.data.serialNum;
this.appName = res.data.title;
this.copyright = res.data.copyright;

View File

@@ -32,8 +32,8 @@ const useDictStore = defineStore('dict', {
{
label: data.dictLabel,
value: data.dictValue,
elTagType: data.tagType,
elTagClass: data.tagClass,
tagType: data.tagType,
tagClass: data.tagClass,
},
];
},
@@ -47,8 +47,8 @@ const useDictStore = defineStore('dict', {
const dictData: DictType[] = res.data.map(d => ({
label: d.dictLabel,
value: d.dictValue,
elTagType: d.tagType,
elTagClass: d.tagClass,
tagType: d.tagType,
tagClass: d.tagClass,
}));
this.dicts.set(key, dictData);
disct = dictData;

View File

@@ -10,10 +10,10 @@ type LayoutStore = {
proConfig: {
/**导航布局 */
layout: 'side' | 'top' | 'mix';
/**导航菜单主题色 */
navTheme: 'dark' | 'light';
/**顶部导航主题仅导航布局为mix时生效 */
headerTheme: 'dark' | 'light';
/**全局主题色,需要导入样式文件 */
theme: 'dark' | 'light';
/**菜单导航主题 */
menuTheme: 'dark' | 'light';
/**固定顶部栏 */
fixedHeader: boolean;
/**固定菜单栏 */
@@ -41,8 +41,8 @@ const proConfigLocal: LayoutStore['proConfig'] = localGetJSON(
CACHE_LOCAL_PROCONFIG
) || {
layout: 'mix',
headerTheme: 'light',
navTheme: 'light',
theme: 'light',
menuTheme: 'light',
fixSiderbar: true,
fixedHeader: true,
splitMenus: true,
@@ -53,8 +53,8 @@ const useLayoutStore = defineStore('layout', {
visible: false,
proConfig: {
layout: proConfigLocal.layout,
navTheme: proConfigLocal.navTheme,
headerTheme: proConfigLocal.headerTheme,
theme: proConfigLocal.theme,
menuTheme: proConfigLocal.menuTheme,
fixedHeader: Boolean(proConfigLocal.fixedHeader),
fixSiderbar: Boolean(proConfigLocal.fixSiderbar),
splitMenus: Boolean(proConfigLocal.splitMenus),
@@ -77,10 +77,6 @@ const useLayoutStore = defineStore('layout', {
/**修改布局设置 */
changeConf(key: string, value: boolean | string | number | undefined) {
if (Reflect.has(this.proConfig, key)) {
// 同时修改mix混合菜单的导航主题
if (key === 'navTheme') {
Reflect.set(this.proConfig, 'headerTheme', value);
}
Reflect.set(this.proConfig, key, value);
localSetJSON(CACHE_LOCAL_PROCONFIG, this.proConfig);
}

View File

@@ -1,51 +0,0 @@
import { getSysConf } from '@/api';
import { CACHE_LOCAL_LOCK } from '@/constants/cache-keys-constants';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { localGet, localSet } from '@/utils/cache-local-utils';
import { defineStore } from 'pinia';
/**锁屏信息类型 */
type Locked = {
/**锁屏类型 */
type: 'none' | 'lock' | 'reload' | string;
/**lock 超时锁屏时间,秒*/
lockTimeout: number;
};
const useLockedStore = defineStore('locked', {
state: (): Locked => ({
type: localGet(CACHE_LOCAL_LOCK) || 'none',
lockTimeout: 0,
}),
getters: {},
actions: {
// 重启等待-轮询
async relaodWait() {
try {
const res = await getSysConf();
if (res.code === RESULT_CODE_SUCCESS && res.data) {
this.fnLock('none');
window.location.reload();
}
} catch (error) {
// 延迟5秒
setTimeout(() => {
this.relaodWait();
}, 5_000);
}
},
// 设置锁定
async fnLock(type: 'none' | 'lock' | 'reload' | string) {
this.type = type;
localSet(CACHE_LOCAL_LOCK, type);
if (type === 'reload') {
// 延迟5秒
setTimeout(() => {
this.relaodWait();
}, 2_000);
}
},
},
});
export default useLockedStore;

70
src/store/modules/mask.ts Normal file
View File

@@ -0,0 +1,70 @@
import { getSysConf } from '@/api';
import {
CACHE_LOCAL_LOCK_PASSWD,
CACHE_LOCAL_MASK,
} from '@/constants/cache-keys-constants';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { localGet, localRemove, localSet } from '@/utils/cache-local-utils';
import { defineStore } from 'pinia';
/**全局遮罩信息类型 */
type MaskStateType = {
/**锁屏类型 */
type: 'none' | 'lock' | 'reload' | 'reset';
/**lock 锁屏密码*/
lockPasswd: string;
/**lock 超时锁屏时间,秒*/
lockTimeout: number;
};
const useMaskStore = defineStore('mask', {
state: (): MaskStateType => ({
type: (localGet(CACHE_LOCAL_MASK) || 'none') as MaskStateType['type'],
lockPasswd: localGet(CACHE_LOCAL_LOCK_PASSWD) || '',
lockTimeout: 0,
}),
getters: {},
actions: {
// 检查服务等待-轮询
async checkServiceWait() {
try {
const res = await getSysConf();
if (res.code === RESULT_CODE_SUCCESS && res.data) {
// 延迟5秒
setTimeout(() => {
this.handleMaskType('none');
window.location.reload();
}, 2_000);
} else {
// 延迟5秒
setTimeout(() => {
this.checkServiceWait();
}, 5_000);
}
} catch (error) {
// 延迟5秒
setTimeout(() => {
this.checkServiceWait();
}, 5_000);
}
},
// 设置遮罩类型
async handleMaskType(type: MaskStateType['type']) {
this.type = type;
localSet(CACHE_LOCAL_MASK, type);
if (type === 'reload') {
// 延迟5秒
setTimeout(() => {
this.checkServiceWait();
}, 5_000);
}
if (type === 'lock') {
localSet(CACHE_LOCAL_LOCK_PASSWD, this.lockPasswd);
} else {
localRemove(CACHE_LOCAL_LOCK_PASSWD);
}
},
},
});
export default useMaskStore;

View File

@@ -1,5 +1,5 @@
import { defineStore } from 'pinia';
import {
import type {
RouteComponent,
RouteLocationRaw,
RouteMeta,

View File

@@ -9,8 +9,8 @@ import { parseUrlPath } from '@/plugins/file-static-url';
/**用户信息类型 */
type UserInfo = {
/**授权凭证 */
token: string;
/**用户ID */
userId: string;
/**登录账号 */
userName: string;
/**用户角色 字符串数组 */
@@ -27,13 +27,13 @@ type UserInfo = {
email: string;
/**用户性别 */
sex: string | undefined;
/**个人化设置 */
/**其他信息 */
profile: Record<string, any>;
};
const useUserStore = defineStore('user', {
state: (): UserInfo => ({
token: getToken(),
userId: '',
userName: '',
roles: [],
permissions: [],
@@ -104,7 +104,6 @@ const useUserStore = defineStore('user', {
if (res.code === RESULT_CODE_SUCCESS && res.data) {
const token = res.data[TOKEN_RESPONSE_FIELD];
setToken(token);
this.token = token;
}
return res;
},
@@ -113,6 +112,7 @@ const useUserStore = defineStore('user', {
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;
// 用户头像
@@ -154,7 +154,6 @@ const useUserStore = defineStore('user', {
} catch (error) {
throw error;
} finally {
this.token = '';
this.roles = [];
this.permissions = [];
removeToken();

View File

@@ -2,6 +2,6 @@
type DictType = {
label: string;
value: string;
elTagType: string;
elTagClass: string;
tagType?: string;
tagClass?: string;
};

View File

@@ -19,9 +19,9 @@ export const regExpPort =
/**
* 有效账号格式
*
* 账号不能以数字开头,可包含大写小写字母数字且不少于5
* 账号只能包含大写字母、小写字母数字的字符串长度至少为6
*/
export const regExpUserName = /^[a-zA-Z][a-z0-9A-Z]{4,}$/;
export const regExpUserName = /^[A-Za-z0-9]{6,}$/;
/**
* 有效密码格式
@@ -34,7 +34,7 @@ export const regExpPasswd =
/**
* 有效手机号格式
*/
export const regExpMobile = /^1[3|4|5|6|7|8|9][0-9]\d{8}$/;
export const regExpMobile = /^.{3,}$/; // /^1[3|4|5|6|7|8|9][0-9]\d{8}$/;
/**
* 有效邮箱格式
@@ -47,7 +47,7 @@ export const regExpEmail =
*
* 用户昵称只能包含字母、数字、中文和下划线且不少于2位
*/
export const regExpNick = /^[\w\u4e00-\u9fa5-]{2,}$/;
export const regExpNick = /^.{2,}$/; // /^[\w\u4e00-\u9fa5-]{2,}$/;
/**
* 是否为http(s)://开头
@@ -84,13 +84,14 @@ export function validURL(str: string) {
// http:// is appended so url.Parse will succeed, strTemp used so it does not impact rxURL.MatchString
strTemp = 'http://' + str;
}
debugger;
let u = { host: '', pathname: '' };
try {
new URL(strTemp);
} catch (error) {
return false;
}
const u = new URL(strTemp);
if (u.host.startsWith('.')) {
return false;
}

View File

@@ -15,9 +15,9 @@ const { getDict } = useDictStore();
/**用户性别字典 */
let sysUserSex = ref<DictType[]>([
{ label: '未知', value: '0', elTagType: '', elTagClass: '' },
{ label: '男', value: '1', elTagType: '', elTagClass: '' },
{ label: '女', value: '2', elTagType: '', elTagClass: '' },
{ label: '未知', value: '0', tagType: '', tagClass: '' },
{ label: '男', value: '1', tagType: '', tagClass: '' },
{ label: '女', value: '2', tagType: '', tagClass: '' },
]);
/**表单数据状态 */
@@ -166,12 +166,12 @@ onMounted(() => {
},
]"
>
<a-input
<IntlTelInput
v-model:value="stateForm.form.phonenumber"
allow-clear
:maxlength="11"
:maxlength="16"
:placeholder="t('views.account.settings.phonenumberPleace')"
></a-input>
></IntlTelInput>
</a-form-item>
<a-form-item

View File

@@ -72,9 +72,9 @@ function fnColorChange(e: Event) {
<a-switch
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
:checked="proConfig.navTheme === 'dark'"
:checked="proConfig.menuTheme === 'dark'"
@change="
(checked:any) => changeConf('navTheme', checked ? 'dark' : 'light')
(checked:any) => changeConf('menuTheme', checked ? 'dark' : 'light')
"
></a-switch>
</template>

View File

@@ -227,7 +227,11 @@ function fnGetList(pageNum?: number) {
}
tablePagination.total = res.total;
tableState.data = res.rows;
if (tablePagination.total <=(queryParams.pageNum - 1) * tablePagination.pageSize &&queryParams.pageNum !== 1) {
if (
tablePagination.total <=
(queryParams.pageNum - 1) * tablePagination.pageSize &&
queryParams.pageNum !== 1
) {
tableState.loading = false;
fnGetList(queryParams.pageNum - 1);
}
@@ -477,8 +481,10 @@ onMounted(() => {
</a-card>
<!-- 新增框或修改框 -->
<a-modal
width="800px"
<ProModal
:drag="true"
:width="800"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:visible="modalState.visibleByEdit"
@@ -501,7 +507,7 @@ onMounted(() => {
/>
</a-form-item>
</a-form>
</a-modal>
</ProModal>
</PageContainer>
</template>

File diff suppressed because it is too large Load Diff

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,165 @@
import useI18n from '@/hooks/useI18n';
import { regExpIPv4, regExpIPv6, validURL } from '@/utils/regular-utils';
export default function useOptions() {
const { t } = useI18n();
/**规则校验 */
function ruleVerification(row: Record<string, any>): (string | boolean)[] {
let result = [true, ''];
const type = row.type;
const value = row.value;
const filter = row.filter;
const display = row.display;
// 子嵌套的不检查
if (row.array) {
return result;
}
// 可选的同时没有值不检查
if (row.optional === 'true' && !value) {
return result;
}
switch (type) {
case 'int':
// filter: "0~128"
if (filter && filter.indexOf('~') !== -1) {
const filterArr = filter.split('~');
const minInt = parseInt(filterArr[0]);
const maxInt = parseInt(filterArr[1]);
const valueInt = parseInt(value);
if (valueInt < minInt || valueInt > maxInt) {
return [
false,
t('views.configManage.configParamForm.requireInt', {
display,
filter,
}),
];
}
}
break;
case 'ipv4':
if (!regExpIPv4.test(value)) {
return [
false,
t('views.configManage.configParamForm.requireIpv4', { display }),
];
}
break;
case 'ipv6':
if (!regExpIPv6.test(value)) {
return [
false,
t('views.configManage.configParamForm.requireIpv6', { display }),
];
}
break;
case 'enum':
if (filter && filter.indexOf('{') === 1) {
let filterJson: Record<string, any> = {};
try {
filterJson = JSON.parse(filter); //string---json
} catch (error) {
console.error(error);
}
if (!Object.keys(filterJson).includes(`${value}`)) {
return [
false,
t('views.configManage.configParamForm.requireEnum', { display }),
];
}
}
break;
case 'bool':
// filter: '{"0":"false", "1":"true"}'
if (filter && filter.indexOf('{') === 1) {
let filterJson: Record<string, any> = {};
try {
filterJson = JSON.parse(filter); //string---json
} catch (error) {
console.error(error);
}
if (!Object.values(filterJson).includes(`${value}`)) {
return [
false,
t('views.configManage.configParamForm.requireBool', { display }),
];
}
}
break;
case 'string':
// filter: "0~128"
// 字符串长度判断
if (filter && filter.indexOf('~') !== -1) {
try {
const filterArr = filter.split('~');
let rule = new RegExp(
'^\\S{' + filterArr[0] + ',' + filterArr[1] + '}$'
);
if (!rule.test(value)) {
return [
false,
t('views.configManage.configParamForm.requireString', {
display,
}),
];
}
} catch (error) {
console.error(error);
}
}
// 字符串http判断
if (value.startsWith('http')) {
try {
if (!validURL(value)) {
return [
false,
t('views.configManage.configParamForm.requireString', {
display,
}),
];
}
} catch (error) {
console.error(error);
}
}
break;
case 'regex':
// filter: "^[0-9]{3}$"
if (filter) {
try {
let regex = new RegExp(filter);
if (!regex.test(value)) {
return [
false,
t('views.configManage.configParamForm.requireString', {
display,
}),
];
}
} catch (error) {
console.error(error);
}
}
break;
default:
return [
false,
t('views.configManage.configParamForm.requireUn', { display }),
];
}
return result;
}
return { ruleVerification };
}

View File

@@ -0,0 +1,22 @@
import { getParamConfigInfo } from '@/api/configManage/configParam';
import { ref } from 'vue';
export default function useSMFOptions() {
/**upfId可选择 */
const optionsUPFIds = ref<{ value: string; label: string }[]>([]);
/**初始加载upfId */
function initUPFIds() {
getParamConfigInfo('smf', 'upfConfig', '001').then(res => {
optionsUPFIds.value = [];
for (const s of res.data) {
optionsUPFIds.value.push({
value: s.id,
label: s.id,
});
}
});
}
return { initUPFIds, optionsUPFIds };
}

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { reactive, ref, onMounted, watch, toRaw, nextTick } from 'vue';
import { reactive, ref, onMounted, toRaw, nextTick, watch } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { Modal, message } from 'ant-design-vue/lib';
import useI18n from '@/hooks/useI18n';
@@ -13,11 +13,14 @@ import {
addParamConfigInfo,
} from '@/api/configManage/configParam';
import useNeInfoStore from '@/store/modules/neinfo';
import { regExpIPv4, regExpIPv6, validURL } from '@/utils/regular-utils';
import useOptions from './hooks/useOptions';
import useSMFOptions from './hooks/useSMFOptions';
import { SizeType } from 'ant-design-vue/lib/config-provider';
import { DataNode } from 'ant-design-vue/lib/tree';
const neInfoStore = useNeInfoStore();
const { t } = useI18n();
const { ruleVerification } = useOptions();
const { initUPFIds, optionsUPFIds } = useSMFOptions();
/**网元参数 */
let neCascaderOptions = ref<Record<string, any>[]>([]);
@@ -25,6 +28,14 @@ let neCascaderOptions = ref<Record<string, any>[]>([]);
/**网元类型选择 type,id */
let neTypeSelect = ref<string[]>(['', '']);
/**左侧导航是否可收起 */
let collapsible = ref<boolean>(true);
/**改变收起状态 */
function changeCollapsible() {
collapsible.value = !collapsible.value;
}
/**对象信息状态类型 */
type TreeStateType = {
/**网元 loading */
@@ -43,7 +54,7 @@ let treeState: TreeStateType = reactive({
selectNode: {
topDisplay: '' as string,
topTag: '' as string,
method: '' as string,
method: [] as string[],
//
title: '' as string,
key: '' as string,
@@ -57,7 +68,11 @@ function fnSelectConfigNode(_: any, info: any) {
const { title, key, method } = info.node;
treeState.selectNode.topDisplay = title;
treeState.selectNode.topTag = key;
treeState.selectNode.method = method;
if (method) {
treeState.selectNode.method = method.split(',');
} else {
treeState.selectNode.method = ['post', 'put', 'delete'];
}
treeState.selectNode.title = title;
treeState.selectNode.key = key;
fnActiveConfigNode(key);
@@ -329,14 +344,6 @@ let arrayState: ArrayStateType = reactive({
dataRule: {},
});
/**监听表格字段列排序变化关闭展开 */
watch(
() => arrayState.columnsDnd,
() => {
arrayEditClose();
}
);
/**多列表编辑 */
function arrayEdit(rowIndex: Record<string, any>) {
const item = arrayState.data.find((s: any) => s.key === rowIndex.value);
@@ -941,155 +948,6 @@ function arrayAddInit(data: any[], dataRule: any) {
return ruleFrom;
}
/**规则校验 */
function ruleVerification(row: Record<string, any>): (string | boolean)[] {
let result = [true, ''];
const type = row.type;
const value = row.value;
const filter = row.filter;
const display = row.display;
// 子嵌套的不检查
if (row.array) {
return result;
}
// 可选的同时没有值不检查
if (row.optional === 'true' && !value) {
return result;
}
switch (type) {
case 'int':
if (filter && filter.indexOf('~') !== -1) {
const filterArr = filter.split('~');
const minInt = parseInt(filterArr[0]);
const maxInt = parseInt(filterArr[1]);
const valueInt = parseInt(value);
if (valueInt < minInt || valueInt > maxInt) {
return [
false,
t('views.configManage.configParamForm.requireInt', {
display,
filter,
}),
];
}
}
break;
case 'ipv4':
if (!regExpIPv4.test(value)) {
return [
false,
t('views.configManage.configParamForm.requireIpv4', { display }),
];
}
break;
case 'ipv6':
if (!regExpIPv6.test(value)) {
return [
false,
t('views.configManage.configParamForm.requireIpv6', { display }),
];
}
break;
case 'enum':
if (filter && filter.indexOf('{') === 1) {
let filterJson: Record<string, any> = {};
try {
filterJson = JSON.parse(filter); //string---json
} catch (error) {
console.error(error);
}
if (!Object.keys(filterJson).includes(`${value}`)) {
return [
false,
t('views.configManage.configParamForm.requireEnum', { display }),
];
}
}
break;
case 'bool':
if (filter && filter.indexOf('{') === 1) {
let filterJson: Record<string, any> = {};
try {
filterJson = JSON.parse(filter); //string---json
} catch (error) {
console.error(error);
}
if (!Object.values(filterJson).includes(`${value}`)) {
return [
false,
t('views.configManage.configParamForm.requireBool', { display }),
];
}
}
break;
case 'string':
// 字符串长度判断
if (filter && filter.indexOf('~') !== -1) {
try {
const filterArr = filter.split('~');
let rule = new RegExp(
'^\\S{' + filterArr[0] + ',' + filterArr[1] + '}$'
);
if (!rule.test(value)) {
return [
false,
t('views.configManage.configParamForm.requireString', {
display,
}),
];
}
} catch (error) {
console.error(error);
}
}
// 字符串http判断
if (value.startsWith('http')) {
try {
if (!validURL(value)) {
return [
false,
t('views.configManage.configParamForm.requireString', {
display,
}),
];
}
} catch (error) {
console.error(error);
}
}
break;
case 'regex':
if (filter) {
try {
let regex = new RegExp(filter);
if (!regex.test(value)) {
return [
false,
t('views.configManage.configParamForm.requireString', {
display,
}),
];
}
} catch (error) {
console.error(error);
}
}
break;
default:
return [
false,
t('views.configManage.configParamForm.requireUn', { display }),
];
}
return result;
}
/**对话框对象信息状态类型 */
type ModalStateType = {
/**添加框是否显示 */
@@ -1151,6 +1009,25 @@ function fnModalCancel() {
modalState.data = [];
}
// 监听表格字段列排序变化关闭展开
watch(
() => arrayState.columnsDnd,
() => {
arrayEditClose();
}
);
// 监听新增编辑弹窗
watch(
() => modalState.visible,
val => {
// SMF需要选择配置的UPF id
if (val && neTypeSelect.value[0] === 'SMF') {
initUPFIds();
}
}
);
onMounted(() => {
// 获取网元网元列表
neInfoStore.fnNelist().then(res => {
@@ -1193,7 +1070,13 @@ onMounted(() => {
<template>
<PageContainer>
<a-row :gutter="16">
<a-col :lg="6" :md="6" :xs="24" style="margin-bottom: 24px">
<a-col
:lg="6"
:md="6"
:xs="24"
style="margin-bottom: 24px"
v-show="collapsible"
>
<!-- 网元类型 -->
<a-card
size="small"
@@ -1221,7 +1104,7 @@ onMounted(() => {
</a-form>
</a-card>
</a-col>
<a-col :lg="18" :md="18" :xs="24">
<a-col :lg="collapsible ? 18 : 24" :md="collapsible ? 18 : 24" :xs="24">
<a-card
size="small"
:bordered="false"
@@ -1229,6 +1112,13 @@ onMounted(() => {
:loading="treeState.selectLoading"
>
<template #title>
<a-button type="text" @click.prevent="changeCollapsible()">
<template #icon>
<MenuFoldOutlined v-show="collapsible" />
<MenuUnfoldOutlined v-show="!collapsible" />
</template>
</a-button>
<a-typography-text strong v-if="treeState.selectNode.topDisplay">
{{ treeState.selectNode.topDisplay }}
</a-typography-text>
@@ -1349,7 +1239,9 @@ onMounted(() => {
<EditOutlined
class="editable-cell__icon"
@click="listEdit(record)"
v-if="!['read-only', 'ro'].includes(record.access)"
v-if="
!['read-only', 'read', 'ro'].includes(record.access)
"
/>
</div>
</div>
@@ -1363,7 +1255,7 @@ onMounted(() => {
<a-table
class="table"
row-key="index"
:columns="treeState.selectNode.method === 'get' ? arrayState.columnsDnd.filter((s:any)=>s.key !== 'index') : arrayState.columnsDnd"
:columns="treeState.selectNode.method.includes('get') ? arrayState.columnsDnd.filter((s:any)=>s.key !== 'index') : arrayState.columnsDnd"
:data-source="arrayState.columnsData"
:size="arrayState.size"
:pagination="tablePagination"
@@ -1380,7 +1272,7 @@ onMounted(() => {
type="primary"
@click.prevent="arrayAdd()"
size="small"
v-if="treeState.selectNode.method !== 'get'"
v-if="treeState.selectNode.method.includes('post')"
>
<template #icon> <PlusOutlined /> </template>
{{ t('common.addText') }}
@@ -1388,7 +1280,7 @@ onMounted(() => {
<TableColumnsDnd
type="ghost"
:cache-id="treeState.selectNode.key"
:columns="treeState.selectNode.method === 'get' ? [...arrayState.columns.filter((s:any)=>s.key !== 'index')] : arrayState.columns"
:columns="treeState.selectNode.method.includes('get') ? [...arrayState.columns.filter((s:any)=>s.key !== 'index')] : arrayState.columns"
v-model:columns-dnd="arrayState.columnsDnd"
></TableColumnsDnd>
</a-space>
@@ -1398,13 +1290,17 @@ onMounted(() => {
<template #bodyCell="{ column, text, record }">
<template v-if="column?.key === 'index'">
<a-space :size="16" align="center">
<a-tooltip>
<a-tooltip
v-if="treeState.selectNode.method.includes('put')"
>
<template #title>{{ t('common.editText') }}</template>
<a-button type="link" @click.prevent="arrayEdit(text)">
<template #icon><FormOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip>
<a-tooltip
v-if="treeState.selectNode.method.includes('delete')"
>
<template #title>{{ t('common.deleteText') }}</template>
<a-button type="link" @click.prevent="arrayDelete(text)">
<template #icon><DeleteOutlined /></template>
@@ -1509,9 +1405,9 @@ onMounted(() => {
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{
t('common.deleteText')
}}</template>
<template #title>
{{ t('common.deleteText') }}
</template>
<a-button
type="link"
@click.prevent="arrayChildDelete(text)"
@@ -1559,8 +1455,10 @@ onMounted(() => {
</a-row>
<!-- 新增框或修改框 -->
<DraggableModal
width="800px"
<ProModal
:drag="true"
:width="800"
:destroyOnClose="true"
:body-style="{ maxHeight: '650px', 'overflow-y': 'auto' }"
:keyboard="false"
:mask-closable="false"
@@ -1598,10 +1496,24 @@ onMounted(() => {
modalState.from[item.name] !== undefined
"
>
<a-input-number
v-if="item['type'] === 'int'"
<!-- 特殊SMF-upfid选择 -->
<a-select
v-if="
neTypeSelect[0] === 'SMF' &&
modalState.from[item.name]['name'] === 'upfId'
"
v-model:value="modalState.from[item.name]['value']"
:disabled="['read-only', 'ro'].includes(item.access)"
:options="optionsUPFIds"
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
:allow-clear="true"
style="width: 100%"
>
</a-select>
<!-- 常规 -->
<a-input-number
v-else-if="item['type'] === 'int'"
v-model:value="modalState.from[item.name]['value']"
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
style="width: 100%"
></a-input-number>
<a-switch
@@ -1609,12 +1521,12 @@ onMounted(() => {
v-model:checked="modalState.from[item.name]['value']"
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
:disabled="['read-only', 'ro'].includes(item.access)"
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
></a-switch>
<a-select
v-else-if="item['type'] === 'enum'"
v-model:value="modalState.from[item.name]['value']"
:disabled="['read-only', 'ro'].includes(item.access)"
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
:allow-clear="true"
style="width: 100%"
>
@@ -1629,7 +1541,7 @@ onMounted(() => {
<a-input
v-else
v-model:value="modalState.from[item.name]['value']"
:disabled="['read-only', 'ro'].includes(item.access)"
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
></a-input>
</div>
<div v-else>
@@ -1639,7 +1551,7 @@ onMounted(() => {
</a-tooltip>
</a-form-item>
</a-form>
</DraggableModal>
</ProModal>
</PageContainer>
</template>

View File

@@ -11,6 +11,7 @@ import useI18n from '@/hooks/useI18n';
import useNeInfoStore from '@/store/modules/neinfo';
import { FileType } from 'ant-design-vue/lib/upload/interface';
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
import { parseDateToStr } from '@/utils/date-utils';
const neInfoStore = useNeInfoStore();
const { t } = useI18n();
@@ -74,21 +75,25 @@ let tableColumns: ColumnsType = [
width: 2,
},
{
title: t('views.configManage.license.fileName'),
dataIndex: 'fileName',
title: t('views.configManage.license.serialNum'),
dataIndex: 'serialNum',
align: 'center',
width: 3,
},
{
title: t('views.configManage.license.comment'),
dataIndex: 'comment',
dataIndex: 'remark',
align: 'center',
width: 5,
},
{
title: t('views.configManage.license.createTime'),
dataIndex: 'createdAt',
dataIndex: 'createTime',
align: 'center',
customRender(opt) {
if (!opt.value) return '';
return parseDateToStr(opt.value);
},
width: 2,
},
];
@@ -141,7 +146,11 @@ function fnGetList(pageNum?: number) {
}
tablePagination.total = res.total;
tableState.data = res.rows;
if (tablePagination.total <=(queryParams.pageNum - 1) * tablePagination.pageSize &&queryParams.pageNum !== 1) {
if (
tablePagination.total <=
(queryParams.pageNum - 1) * tablePagination.pageSize &&
queryParams.pageNum !== 1
) {
tableState.loading = false;
fnGetList(queryParams.pageNum - 1);
}
@@ -301,7 +310,7 @@ onMounted(() => {
fnGetList();
} else {
message.warning({
content: t('views.configManage.softwareManage.nullData'),
content: t('common.noData'),
duration: 2,
});
}
@@ -421,8 +430,10 @@ onMounted(() => {
</a-card>
<!-- 上传框 -->
<a-modal
width="800px"
<ProModal
:drag="true"
:width="800"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:visible="modalState.visibleByEdit"
@@ -478,7 +489,7 @@ onMounted(() => {
</a-upload>
</a-form-item>
</a-form>
</a-modal>
</ProModal>
</PageContainer>
</template>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { reactive, onMounted, toRaw } from 'vue';
import { reactive, onMounted, toRaw, ref } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { message, Modal, Form } from 'ant-design-vue/lib';
import { SizeType } from 'ant-design-vue/lib/config-provider';
@@ -22,8 +22,12 @@ import { updateNeConfigReload } from '@/api/configManage/configParam';
import useI18n from '@/hooks/useI18n';
import { FileType } from 'ant-design-vue/lib/upload/interface';
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { NE_TYPE_LIST } from '@/constants/ne-constants';
import useNeInfoStore from '@/store/modules/neinfo';
import useMaskStore from '@/store/modules/mask';
const maskStore = useMaskStore();
const { t } = useI18n();
/**表格所需option */
@@ -86,71 +90,74 @@ let tableColumns: ColumnsType = [
title: t('views.configManage.neManage.neType'),
dataIndex: 'neType',
align: 'center',
width: 3,
width: 100,
},
{
title: t('views.configManage.neManage.neId'),
dataIndex: 'neId',
align: 'center',
width: 3,
},
{
title: t('views.configManage.neManage.uid'),
dataIndex: 'rmUid',
align: 'center',
width: 5,
},
{
title: t('views.configManage.neManage.neName'),
dataIndex: 'neName',
align: 'center',
width: 5,
width: 100,
},
{
title: t('views.configManage.neManage.ip'),
dataIndex: 'ip',
align: 'center',
width: 5,
width: 150,
},
{
title: t('views.configManage.neManage.port'),
dataIndex: 'port',
align: 'center',
width: 3,
width: 100,
},
{
title: t('views.configManage.neManage.neName'),
dataIndex: 'neName',
align: 'center',
width: 150,
},
{
title: t('views.configManage.neManage.uid'),
dataIndex: 'rmUid',
align: 'center',
width: 200,
},
{
title: t('views.configManage.neManage.pvflag'),
dataIndex: 'pvFlag',
align: 'center',
width: 5,
width: 100,
},
{
title: t('views.configManage.neManage.province'),
dataIndex: 'province',
align: 'center',
width: 5,
width: 100,
},
{
title: t('views.configManage.neManage.vendorName'),
dataIndex: 'vendorName',
align: 'center',
width: 5,
width: 150,
},
{
title: t('views.configManage.neManage.dn'),
dataIndex: 'dn',
align: 'center',
width: 5,
width: 200,
},
{
title: t('common.operate'),
key: 'id',
align: 'center',
fixed: 'right',
width: 5,
width: 150,
},
];
/**表格字段列排序 */
let tableColumnsDnd = ref<ColumnsType>([]);
/**表格分页器参数 */
let tablePagination = reactive({
/**当前页数 */
@@ -215,11 +222,11 @@ let modalState: ModalStateType = reactive({
neAddress: '',
neId: '',
neName: '',
neType: 'OMC',
port: '3030',
neType: 'AMF',
port: '33030',
province: '',
pvFlag: 'PNF',
rmUid: '4400HX1OMC001',
rmUid: '4400HX1AMF001',
vendorName: '',
sync: true,
},
@@ -541,6 +548,11 @@ function fnRecordRestart(row: Record<string, any>) {
restartNf(row)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
// OMC自升级
if (row.neType.toLowerCase() === 'omc') {
maskStore.handleMaskType('reload');
return;
}
message.success({
content: t('common.msgSuccess', {
msg: t('views.configManage.neManage.restart'),
@@ -785,11 +797,12 @@ onMounted(() => {
:label="t('views.configManage.neManage.neType')"
name="neType "
>
<a-input
<a-auto-complete
v-model:value="queryParams.neType"
:options="NE_TYPE_LIST.map(v => ({ value: v }))"
allow-clear
:placeholder="t('views.configManage.neManage.neTypePlease')"
></a-input>
:placeholder="t('common.inputPlease')"
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
@@ -839,6 +852,11 @@ onMounted(() => {
<template #icon><ReloadOutlined /></template>
</a-button>
</a-tooltip>
<TableColumnsDnd
cache-id="neManageData"
:columns="tableColumns"
v-model:columns-dnd="tableColumnsDnd"
></TableColumnsDnd>
<a-tooltip>
<template #title>{{ t('common.sizeText') }}</template>
<a-dropdown trigger="click" placement="bottomRight">
@@ -870,15 +888,15 @@ onMounted(() => {
<a-table
class="table"
row-key="id"
:columns="tableColumns"
:columns="tableColumnsDnd"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: 2000, y: 480 }"
:scroll="{ y: 'calc(100vh - 480px)' }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'id'">
<template v-if="column?.key === 'id'">
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.editText') }}</template>
@@ -902,10 +920,7 @@ onMounted(() => {
</a-tooltip>
<a-tooltip placement="left">
<template #title>{{ t('common.moreText') }}</template>
<a-dropdown
placement="bottomRight"
:trigger="['hover', 'click']"
>
<a-dropdown placement="bottomRight" trigger="click">
<a-button type="link">
<template #icon><EllipsisOutlined /> </template>
</a-button>
@@ -919,11 +934,17 @@ onMounted(() => {
<ImportOutlined />
{{ t('views.configManage.neManage.import') }}
</a-menu-item>
<a-menu-item key="start">
<a-menu-item
key="start"
v-if="!['OMC'].includes(record.neType)"
>
<thunderbolt-outlined />
{{ t('views.configManage.neManage.start') }}
</a-menu-item>
<a-menu-item key="stop">
<a-menu-item
key="stop"
v-if="!['OMC'].includes(record.neType)"
>
<pause-outlined />
{{ t('views.configManage.neManage.stop') }}
</a-menu-item>
@@ -951,8 +972,10 @@ onMounted(() => {
</a-card>
<!-- 新增框或修改框 -->
<DraggableModal
width="800px"
<ProModal
:drag="true"
:width="800"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:visible="modalState.visibleByEdit"
@@ -974,20 +997,21 @@ onMounted(() => {
name="neType"
v-bind="modalStateFrom.validateInfos.neType"
>
<a-input
<a-auto-complete
v-model:value="modalState.from.neType"
allow-clear
:placeholder="t('views.configManage.neManage.neTypePlease')"
:options="NE_TYPE_LIST.map(v => ({ value: v }))"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>{{
t('views.configManage.neManage.neTypeTip')
}}</template>
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
</a-input>
<a-input allow-clear :placeholder="t('common.inputPlease')">
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>{{
t('views.configManage.neManage.neTypeTip')
}}</template>
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
</a-input>
</a-auto-complete>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -1135,7 +1159,7 @@ onMounted(() => {
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.configManage.neManage.sync')"
name="province"
name="sync"
>
<a-switch
v-model:checked="modalState.from.sync"
@@ -1146,11 +1170,13 @@ onMounted(() => {
</a-col>
</a-row>
</a-form>
</DraggableModal>
</ProModal>
<!-- 导入框 -->
<a-modal
width="800px"
<ProModal
:drag="true"
:width="800"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:visible="modalState.visibleByImport"
@@ -1237,7 +1263,7 @@ onMounted(() => {
</a-upload>
</a-form-item>
</a-form>
</a-modal>
</ProModal>
</PageContainer>
</template>

View File

@@ -167,7 +167,11 @@ function fnGetList(pageNum?: number) {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
tablePagination.total = res.total;
tableState.data = res.rows;
if (tablePagination.total <=(queryParams.pageNum - 1) * tablePagination.pageSize &&queryParams.pageNum !== 1) {
if (
tablePagination.total <=
(queryParams.pageNum - 1) * tablePagination.pageSize &&
queryParams.pageNum !== 1
) {
tableState.loading = false;
fnGetList(queryParams.pageNum - 1);
}
@@ -197,15 +201,16 @@ watch(
</script>
<template>
<a-modal
width="100%"
wrap-class-name="full-modal"
<ProModal
:drag="true"
:forceFullscreen="true"
:destroyOnClose="true"
:title="props.title"
:visible="props.visible"
:keyboard="false"
:mask-closable="false"
@cancel="fnModalCancel"
:footer="null"
:footer="false"
>
<!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal">
@@ -265,7 +270,7 @@ watch(
:pagination="tablePagination"
>
</a-table>
</a-modal>
</ProModal>
</template>
<style lang="less" scoped>
@@ -273,22 +278,3 @@ watch(
padding: 0 24px;
}
</style>
<style lang="less">
.full-modal {
.ant-modal {
max-width: 100%;
top: 0;
padding-bottom: 0;
margin: 0;
}
.ant-modal-content {
display: flex;
flex-direction: column;
height: calc(100vh);
}
.ant-modal-body {
flex: 1;
}
}
</style>

View File

@@ -23,8 +23,8 @@ import useI18n from '@/hooks/useI18n';
import useNeInfoStore from '@/store/modules/neinfo';
import { FileType } from 'ant-design-vue/lib/upload/interface';
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
import useLockedStore from '@/store/modules/locked';
const lockedStore = useLockedStore();
import useMaskStore from '@/store/modules/mask';
const maskStore = useMaskStore();
const { t } = useI18n();
/**查询参数 */
@@ -82,7 +82,7 @@ let tableColumns: ColumnsType = [
},
{
title: t('views.configManage.softwareManage.fileName'),
dataIndex: 'fileName',
dataIndex: 'name',
align: 'center',
width: 2,
},
@@ -94,7 +94,7 @@ let tableColumns: ColumnsType = [
},
{
title: t('views.configManage.softwareManage.updateTime'),
dataIndex: 'updateTime',
dataIndex: 'createTime',
align: 'center',
customRender(opt) {
if (!opt.value) return '';
@@ -104,7 +104,7 @@ let tableColumns: ColumnsType = [
},
{
title: t('views.configManage.softwareManage.description'),
dataIndex: 'comment',
dataIndex: 'name',
align: 'center',
width: 2,
},
@@ -274,6 +274,7 @@ function fnFileModalVisible(type: string | number, row: Record<string, any>) {
* 进行表达规则校验
*/
function fnFileModalOk() {
if (fileModalState.confirmLoading) return;
fileModalStateFrom
.validate()
.then(e => {
@@ -298,7 +299,7 @@ function fnFileModalOk() {
if (type === 'run' && from.neType.toLowerCase() === 'omc') {
if (res.code === RESULT_CODE_SUCCESS) {
fnFileModalCancel();
lockedStore.fnLock('reload');
maskStore.handleMaskType('reload');
} else {
message.error({
content: `${fileModalState.title} ${res.msg}`,
@@ -730,13 +731,15 @@ onMounted(() => {
}
} else {
message.warning({
content: t('views.configManage.softwareManage.nullData'),
content: t('common.noData'),
duration: 2,
});
}
})
.finally(() => {
// 获取列表数据
fnGetList();
});
// 获取列表数据
fnGetList();
});
</script>
@@ -889,10 +892,7 @@ onMounted(() => {
</a-tooltip>
<a-tooltip placement="left">
<template #title>{{ t('common.moreText') }}</template>
<a-dropdown
placement="bottomRight"
:trigger="['hover', 'click']"
>
<a-dropdown placement="bottomRight" trigger="click">
<a-button type="link">
<template #icon><EllipsisOutlined /> </template>
</a-button>
@@ -919,8 +919,9 @@ onMounted(() => {
</a-card>
<!-- 上传框 -->
<DraggableModal
width="800px"
<ProModal
:drag="true"
:width="800"
:keyboard="false"
:mask-closable="false"
:visible="modalState.visibleByEdit"
@@ -1012,7 +1013,7 @@ onMounted(() => {
</a-upload>
</a-form-item>
</a-form>
</DraggableModal>
</ProModal>
<!-- 上传激活历史 -->
<SoftwareHistory
@@ -1022,8 +1023,8 @@ onMounted(() => {
/>
<!-- 文件框 下发激活 -->
<a-modal
width="600px"
<ProModal
:drag="true"
:keyboard="false"
:mask-closable="false"
:visible="fileModalState.visible"
@@ -1051,11 +1052,11 @@ onMounted(() => {
/>
</a-form-item>
</a-form>
</a-modal>
</ProModal>
<!-- 回退框 -->
<a-modal
width="800px"
<ProModal
:drag="true"
:keyboard="false"
:mask-closable="false"
:visible="fileModalState.visibleByBack"
@@ -1086,7 +1087,7 @@ onMounted(() => {
/>
</a-form-item>
</a-form>
</a-modal>
</ProModal>
</PageContainer>
</template>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { reactive, onMounted, toRaw, ref, onUnmounted } from 'vue';
import { reactive, onMounted, toRaw, ref, onBeforeUnmount } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { message, Modal } from 'ant-design-vue/lib';
import { SizeType } from 'ant-design-vue/lib/config-provider';
@@ -11,8 +11,9 @@ import {
RESULT_CODE_SUCCESS,
} from '@/constants/result-constants';
import useDictStore from '@/store/modules/dict';
import { listAMFDataUE, delAMFDataUE } from '@/api/neData/amf';
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';
const { t } = useI18n();
const { getDict } = useDictStore();
@@ -33,14 +34,22 @@ let dict: {
ueEventCmState: [],
});
/**开始结束时间 */
let queryRangePicker = ref<[string, string]>(['', '']);
/**查询参数 */
let queryParams = reactive({
/**网元类型 */
neType: 'AMF',
neId: '001',
eventType: 'auth-result',
eventType: '',
imsi: '',
sortField: 'timestamp',
sortOrder: 'desc',
/**开始时间 */
startTime: '',
/**结束时间 */
endTime: '',
/**当前页数 */
pageNum: 1,
/**每页条数 */
@@ -49,19 +58,23 @@ let queryParams = reactive({
/**查询参数重置 */
function fnQueryReset() {
eventTypes.value = ['auth-result'];
eventTypes.value = [];
queryParams = Object.assign(queryParams, {
eventType: 'auth-result',
eventType: '',
imsi: '',
startTime: '',
endTime: '',
pageNum: 1,
pageSize: 20,
});
queryRangePicker.value = ['', ''];
tablePagination.current = 1;
tablePagination.pageSize = 20;
fnGetList();
}
/**记录类型 */
const eventTypes = ref<string[]>(['auth-result']);
const eventTypes = ref<string[]>([]);
/**查询记录类型变更 */
function fnQueryEventTypeChange(value: any) {
@@ -76,8 +89,6 @@ type TabeStateType = {
loading: boolean;
/**紧凑型 */
size: SizeType;
/**斑马纹 */
striped: boolean;
/**搜索栏 */
seached: boolean;
/**记录数据 */
@@ -90,7 +101,6 @@ type TabeStateType = {
let tableState: TabeStateType = reactive({
loading: false,
size: 'middle',
striped: false,
seached: true,
data: [],
selectedRowKeys: [],
@@ -108,7 +118,7 @@ let tableColumns: ColumnsType = [
title: 'IMSI',
dataIndex: 'eventJSON',
align: 'left',
width: 100,
width: 150,
customRender(opt) {
const eventJSON = opt.value;
return eventJSON.imsi;
@@ -119,20 +129,20 @@ let tableColumns: ColumnsType = [
dataIndex: 'eventType',
key: 'eventType',
align: 'left',
width: 100,
width: 150,
},
{
title: t('views.dashboard.ue.result'),
dataIndex: 'eventJSON',
key: 'result',
align: 'left',
width: 100,
width: 150,
},
{
title: t('views.dashboard.ue.time'),
dataIndex: 'eventJSON',
key: 'time',
align: 'center',
align: 'left',
width: 150,
},
{
@@ -175,11 +185,6 @@ function fnTableSize({ key }: MenuInfo) {
tableState.size = key as SizeType;
}
/**表格斑马纹 */
function fnTableStriped(_record: unknown, index: number): any {
return tableState.striped && index % 2 === 1 ? 'table-striped' : undefined;
}
/**表格多选 */
function fnTableSelectedRowKeys(keys: (string | number)[]) {
tableState.selectedRowKeys = keys;
@@ -247,6 +252,11 @@ function fnGetList(pageNum?: number) {
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)) {
// 取消勾选
@@ -281,6 +291,39 @@ function fnGetList(pageNum?: number) {
});
}
/**列表导出 */
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);
@@ -296,7 +339,7 @@ function fnRealTime() {
params: {
/**订阅通道组
*
* UE会话事件-AMF (GroupID:1010)
* AMF_UE会话事件(GroupID:1010)
*/
subGroupID: '1010',
},
@@ -327,7 +370,7 @@ function wsMessage(res: Record<string, any>) {
if (!data?.groupId) {
return;
}
// ueEvent CDR会话事件
// ueEvent AMF_UE会话事件
if (data.groupId === '1010') {
const ueEvent = data.data;
queue.add(async () => {
@@ -374,8 +417,10 @@ onMounted(() => {
});
});
onUnmounted(() => {
ws.close();
onBeforeUnmount(() => {
if (ws.state() !== -1) {
ws.close();
}
});
</script>
@@ -389,20 +434,45 @@ onUnmounted(() => {
<!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<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="tags"
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">
@@ -456,6 +526,11 @@ onUnmounted(() => {
<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>
@@ -471,15 +546,6 @@ onUnmounted(() => {
size="small"
/>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.tableStripedText') }}</template>
<a-switch
v-model:checked="tableState.striped"
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
size="small"
/>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
@@ -522,7 +588,6 @@ onUnmounted(() => {
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:row-class-name="fnTableStriped"
:scroll="{ x: tableColumns.length * 120, y: 'calc(100vh - 480px)' }"
:row-selection="{
type: 'checkbox',
@@ -542,10 +607,7 @@ onUnmounted(() => {
:value="record.eventJSON.authCode"
/>
</span>
<span
v-if="record.eventType === 'detach'"
:title="record.eventJSON.detachTime"
>
<span v-if="record.eventType === 'detach'">
<span>{{ t('views.dashboard.ue.resultOk') }}</span>
</span>
<span v-if="record.eventType === 'cm-state'">
@@ -597,11 +659,11 @@ onUnmounted(() => {
{{ t('views.dashboard.ue.ueInfo') }}
</a-divider>
<div>
<span>{{ t('views.dashboard.ue.neName') }}: </span>
<span>{{ t('views.ne.common.neName') }}: </span>
<span>{{ record.neName }}</span>
</div>
<div>
<span>{{ t('views.dashboard.ue.rmUID') }}: </span>
<span>{{ t('views.ne.common.rmUid') }}: </span>
<span>{{ record.rmUID }}</span>
</div>
<a-divider orientation="left">
@@ -641,7 +703,7 @@ onUnmounted(() => {
/>
</span>
<span v-if="record.eventType === 'detach'">
{{ t('views.dashboard.ue.resultOK') }}
{{ t('views.dashboard.ue.resultOk') }}
</span>
<span v-if="record.eventType === 'cm-state'">
<DictTag
@@ -658,10 +720,6 @@ onUnmounted(() => {
</template>
<style lang="less" scoped>
.table :deep(.table-striped) td {
background-color: #fafafa;
}
.table :deep(.ant-pagination) {
padding: 0 24px;
}

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { reactive, onMounted, toRaw, ref, onUnmounted } from 'vue';
import { reactive, onMounted, toRaw, ref, onBeforeUnmount } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { message, Modal } from 'ant-design-vue/lib';
import { SizeType } from 'ant-design-vue/lib/config-provider';
@@ -11,9 +11,14 @@ import {
RESULT_CODE_SUCCESS,
} from '@/constants/result-constants';
import useDictStore from '@/store/modules/dict';
import { delIMSDataCDR, listIMSDataCDR } from '@/api/neData/ims';
import {
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';
const { t } = useI18n();
const { getDict } = useDictStore();
@@ -31,14 +36,23 @@ let dict: {
cdrCallType: [],
});
/**开始结束时间 */
let queryRangePicker = ref<[string, string]>(['', '']);
/**查询参数 */
let queryParams = reactive({
/**网元类型 */
neType: 'IMS',
neId: '001',
recordType: 'MOC',
recordType: '',
callerParty: '',
calledParty: '',
sortField: 'timestamp',
sortOrder: 'desc',
/**开始时间 */
startTime: '',
/**结束时间 */
endTime: '',
/**当前页数 */
pageNum: 1,
/**每页条数 */
@@ -47,19 +61,24 @@ let queryParams = reactive({
/**查询参数重置 */
function fnQueryReset() {
recordTypes.value = ['MOC'];
recordTypes.value = [];
queryParams = Object.assign(queryParams, {
recordType: 'MOC',
recordType: '',
callerParty: '',
calledParty: '',
startTime: '',
endTime: '',
pageNum: 1,
pageSize: 20,
});
queryRangePicker.value = ['', ''];
tablePagination.current = 1;
tablePagination.pageSize = 20;
fnGetList();
}
/**记录类型 */
const recordTypes = ref<string[]>(['MOC']);
const recordTypes = ref<string[]>([]);
/**查询记录类型变更 */
function fnQueryRecordTypeChange(value: any) {
@@ -74,8 +93,6 @@ type TabeStateType = {
loading: boolean;
/**紧凑型 */
size: SizeType;
/**斑马纹 */
striped: boolean;
/**搜索栏 */
seached: boolean;
/**记录数据 */
@@ -88,7 +105,6 @@ type TabeStateType = {
let tableState: TabeStateType = reactive({
loading: false,
size: 'middle',
striped: false,
seached: true,
data: [],
selectedRowKeys: [],
@@ -106,7 +122,7 @@ let tableColumns: ColumnsType = [
title: t('views.dashboard.cdr.recordType'),
dataIndex: 'cdrJSON',
align: 'left',
width: 100,
width: 150,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.recordType;
@@ -124,7 +140,7 @@ let tableColumns: ColumnsType = [
dataIndex: 'cdrJSON',
key: 'calledParty',
align: 'left',
width: 100,
width: 120,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.calledParty;
@@ -135,7 +151,7 @@ let tableColumns: ColumnsType = [
dataIndex: 'cdrJSON',
key: 'callerParty',
align: 'left',
width: 100,
width: 120,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.callerParty;
@@ -159,7 +175,7 @@ let tableColumns: ColumnsType = [
dataIndex: 'cdrJSON',
key: 'cause',
align: 'left',
width: 100,
width: 150,
},
{
title: t('views.dashboard.cdr.time'),
@@ -211,11 +227,6 @@ function fnTableSize({ key }: MenuInfo) {
tableState.size = key as SizeType;
}
/**表格斑马纹 */
function fnTableStriped(_record: unknown, index: number): any {
return tableState.striped && index % 2 === 1 ? 'table-striped' : undefined;
}
/**表格多选 */
function fnTableSelectedRowKeys(keys: (string | number)[]) {
tableState.selectedRowKeys = keys;
@@ -283,6 +294,11 @@ function fnGetList(pageNum?: number) {
if (pageNum) {
queryParams.pageNum = pageNum;
}
if (!queryRangePicker.value) {
queryRangePicker.value = ['', ''];
}
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)) {
// 取消勾选
@@ -317,6 +333,39 @@ function fnGetList(pageNum?: number) {
});
}
/**列表导出 */
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;
exportIMSDataCDR(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
saveAs(res.data, `ims_cdr_event_export_${Date.now()}.xlsx`);
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
},
});
}
/**实时数据开关 */
const realTimeData = ref<boolean>(false);
@@ -332,7 +381,7 @@ function fnRealTime() {
params: {
/**订阅通道组
*
* CDR会话事件-IMS (GroupID:1005)
* IMS_CDR会话事件(GroupID:1005)
*/
subGroupID: '1005',
},
@@ -402,8 +451,10 @@ onMounted(() => {
});
});
onUnmounted(() => {
ws.close();
onBeforeUnmount(() => {
if (ws.state() !== -1) {
ws.close();
}
});
</script>
@@ -417,14 +468,14 @@ onUnmounted(() => {
<!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<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="tags"
mode="multiple"
:options="
['MOC', 'MTC', 'MOSM', 'MTSM'].map(v => ({ value: v }))
"
@@ -433,7 +484,47 @@ onUnmounted(() => {
></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="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="4" :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="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="4" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList(1)">
@@ -486,6 +577,11 @@ onUnmounted(() => {
<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>
@@ -501,15 +597,6 @@ onUnmounted(() => {
size="small"
/>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.tableStripedText') }}</template>
<a-switch
v-model:checked="tableState.striped"
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
size="small"
/>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
@@ -552,8 +639,7 @@ onUnmounted(() => {
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:row-class-name="fnTableStriped"
:scroll="{ x: tableColumns.length * 120, y: 'calc(100vh - 480px)' }"
:scroll="{ x: tableColumns.length * 150, y: 'calc(100vh - 480px)' }"
:row-selection="{
type: 'checkbox',
columnWidth: '48px',
@@ -573,7 +659,7 @@ onUnmounted(() => {
<DictTag
:options="dict.cdrSipCode"
:value="record.cdrJSON.cause"
value-option="0"
value-default="0"
/>
</span>
<span v-else>
@@ -602,11 +688,11 @@ onUnmounted(() => {
{{ t('views.dashboard.cdr.cdrInfo') }}
</a-divider>
<div>
<span>{{ t('views.dashboard.cdr.neName') }}: </span>
<span>{{ t('views.ne.common.neName') }}: </span>
<span>{{ record.neName }}</span>
</div>
<div>
<span>{{ t('views.dashboard.cdr.rmUID') }}: </span>
<span>{{ t('views.ne.common.rmUid') }}: </span>
<span>{{ record.rmUID }}</span>
</div>
<div>
@@ -644,7 +730,7 @@ onUnmounted(() => {
<DictTag
:options="dict.cdrSipCode"
:value="record.cdrJSON.cause"
value-option="0"
value-default="0"
/>
</span>
<span v-else>
@@ -659,10 +745,6 @@ onUnmounted(() => {
</template>
<style lang="less" scoped>
.table :deep(.table-striped) td {
background-color: #fafafa;
}
.table :deep(.ant-pagination) {
padding: 0 24px;
}

View File

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

View File

@@ -0,0 +1,202 @@
<script setup lang="ts">
import * as echarts from 'echarts/core';
import {
TitleComponent,
TitleComponentOption,
TooltipComponent,
TooltipComponentOption,
GridComponent,
GridComponentOption,
LegendComponent,
LegendComponentOption,
} from 'echarts/components';
import {
PieChart,
PieSeriesOption,
BarChart,
BarSeriesOption,
} from 'echarts/charts';
import { LabelLayout } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
import { markRaw, onMounted, ref } from 'vue';
import { origGet, top3Sel } from '@/api/faultManage/actAlarm';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
const { t } = useI18n();
echarts.use([
TitleComponent,
TooltipComponent,
GridComponent,
LegendComponent,
PieChart,
BarChart,
CanvasRenderer,
LabelLayout,
]);
type EChartsOption = echarts.ComposeOption<
| TitleComponentOption
| TooltipComponentOption
| GridComponentOption
| LegendComponentOption
| PieSeriesOption
| BarSeriesOption
>;
/**图DOM节点实例对象 */
const alarmTypeBar = ref<HTMLElement | undefined>(undefined);
/**图实例对象 */
const alarmTypeBarChart = ref<any>(null);
/**告警类型数据 */
const alarmTypeType = ref<any>([
{
value: 0,
name: t('views.index.Critical'),
},
{
value: 0,
name: t('views.index.Major'),
},
{
value: 0,
name: t('views.index.Minor'),
},
{
value: 0,
name: t('views.index.Warning'),
},
{
value: 0,
name: t('views.index.Event'),
},
]);
//
function initPicture() {
Promise.allSettled([origGet()])
.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.name) {
case 'Critical':
index = 0;
break;
case 'Major':
index = 1;
break;
case 'Minor':
index = 2;
break;
case 'Warning':
index = 3;
break;
case 'Event':
index = 4;
break;
}
alarmTypeType.value[index].value = Number(item.value);
}
}
}
})
.then(() => {
const optionData: EChartsOption = {
title: [
{
show: false,
},
],
tooltip: {
trigger: 'item',
formatter: '{b} : {c}',
},
legend: {
orient: 'vertical',
right: '2%',
top: '10%',
data: alarmTypeType.value.map((item: any) => item.name), //label数组
textStyle: {
color: '#A7D6F4', // 设置图例文字颜色
},
},
grid: [
{
top: '60%',
left: '15%',
right: '25%',
bottom: '10%',
},
],
series: [
//饼图:
{
type: 'pie',
radius: '60%',
color: ['#f5222d', '#fa8c16', '#fadb14', '#1677ff', '#13c2c2'],
label: {
show: true,
position: 'inner',
formatter: (params: any) => {
if (!params.value) return '';
return `${params.value}`;
},
},
labelLine: {
show: false,
},
center: ['30%', '40%'],
data: alarmTypeType.value,
zlevel: 2, // 设置zlevel为1使得柱状图在下层显示
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
],
};
fnDesign(alarmTypeBar.value, optionData);
});
}
function fnDesign(container: HTMLElement | undefined, option: any) {
if (!container) return;
alarmTypeBarChart.value = markRaw(echarts.init(container, 'light'));
option && alarmTypeBarChart.value.setOption(option);
// 创建 ResizeObserver 实例
var observer = new ResizeObserver(entries => {
if (alarmTypeBarChart.value) {
alarmTypeBarChart.value.resize();
}
});
// 监听元素大小变化
observer.observe(container);
}
onMounted(() => {
initPicture();
});
</script>
<template>
<div ref="alarmTypeBar" class="chart-container"></div>
</template>
<style lang="less" scoped>
.chart-container {
/* 设置图表容器大小和位置 */
width: 100%;
height: 100%;
}
</style>

View File

@@ -0,0 +1,91 @@
<script setup lang="ts">
import { reactive, toRaw, watch } from 'vue';
import { dbGetJSON, dbSetJSON } from '@/utils/cache-db-utils';
const emit = defineEmits(['ok', 'cancel', 'update:visible']);
const props = defineProps({
title: {
type: String,
default: '标题',
},
visible: {
type: Boolean,
default: false,
},
});
/**数据参数 */
let dataState = reactive({
/**基站数 */
baseNum: 0,
/**核心网数 */
coreNetNum: 0,
/**在线用户数 */
onlineUserNum: 0,
});
/**弹框取消按钮事件 */
function fnModalOk() {
dbSetJSON('tbl_mocn', `tmp`, toRaw(dataState));
emit('ok');
emit('update:visible', false);
}
/**弹框取消按钮事件 */
function fnModalCancel() {
emit('cancel');
emit('update:visible', false);
}
/**显示弹框时初始数据 */
function init() {
// 读取数据
dbGetJSON('tbl_mocn', `tmp`).then(data => {
if (data) {
Object.assign(dataState, data);
}
});
}
/**监听是否显示,初始数据 */
watch(
() => props.visible,
val => {
if (val) init();
}
);
</script>
<template>
<ProModal
:drag="true"
:width="800"
:title="props.title"
:visible="props.visible"
:keyboard="false"
:mask-closable="false"
@cancel="fnModalCancel"
@ok="fnModalOk"
>
<a-form
name="dataState"
layout="horizontal"
:label-col="{ span: 6 }"
:labelWrap="true"
>
<a-form-item label="baseNum" name="baseNum">
<a-input-number v-model:value="dataState.baseNum"> </a-input-number>
</a-form-item>
<a-form-item label="coreNetNum" name="coreNetNum">
<a-input-number v-model:value="dataState.coreNetNum"> </a-input-number>
</a-form-item>
<a-form-item label="onlineUserNum" name="onlineUserNum">
<a-input-number v-model:value="dataState.onlineUserNum">
</a-input-number>
</a-form-item>
</a-form>
</ProModal>
</template>
<style lang="less" scoped></style>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,566 @@
<script setup lang="ts">
import { onBeforeUnmount, onMounted, reactive, ref } from 'vue';
import svgBase from '@/assets/svg/base.svg';
import svgUserIMS from '@/assets/svg/userIMS.svg';
import svgUserSMF from '@/assets/svg/userSMF.svg';
import useI18n from '@/hooks/useI18n';
import Topology from '../overview/components/Topology/index.vue';
import NeResources from '../overview/components/NeResources/index.vue';
import UserActivity from '../overview/components/UserActivity/index.vue';
import AlarnTypeBar from './components/AlarnTypeBar/index.vue';
import setting from './components/setting.vue';
import UPFFlow from '../overview/components/UPFFlow/index.vue';
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,
notNeNodes,
graphNodeStateNum,
neStateRequestMap,
} from '../overview/hooks/useTopology';
import { upfTotalFlow, upfTFActive } from '../overview/hooks/useUPFTotalFlow';
import { useFullscreen } from '@vueuse/core';
import useWS from '../overview/hooks/useWS';
import useAppStore from '@/store/modules/app';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { useRouter } from 'vue-router';
import { dbGetJSON } from '@/utils/cache-db-utils';
const router = useRouter();
const appStore = useAppStore();
const { t } = useI18n();
const { wsSend, userActivitySend, upfTFSend } = useWS();
/**概览状态类型 */
type SkimStateType = {
/**UDM签约用户数量 */
udmSubNum: number;
/**SMF在线用户数 */
smfUeNum: number;
/**IMS在线用户数 */
imsUeNum: number;
/**5G基站数量 */
gnbNum: number;
/**5G在线用户数量 */
gnbUeNum: number;
/**4G基站数量 */
enbNum: number;
/**4G在线用户数量 */
enbUeNum: number;
};
/**概览状态信息 */
let skimState: SkimStateType = reactive({
udmSubNum: 0,
smfUeNum: 0,
imsUeNum: 0,
gnbNum: 0,
gnbUeNum: 0,
enbNum: 0,
enbUeNum: 0,
});
/**总览节点 */
const viewportDom = ref<HTMLElement | null>(null);
const { isFullscreen, toggle } = useFullscreen(viewportDom);
/**10s调度器 */
const interval10s = ref<any>(null);
/**5s调度器 */
const interval5s = ref<any>(null);
/**查询网元状态 */
function fnGetNeState() {
// 获取节点状态
for (const node of graphState.data.nodes) {
if (notNeNodes.includes(node.id)) continue;
const { neType, neId } = node.neInfo;
if (!neType || !neId) continue;
// 请求标记检查避免重复发送
if (neStateRequestMap.value.get(neType)) continue;
neStateRequestMap.value.set(neType, true);
wsSend({
requestId: `neState_${neType}_${neId}`,
type: 'ne_state',
data: {
neType: neType,
neId: neId,
},
});
}
}
/**获取概览信息 */
async function fnGetSkim() {
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',
}),
]);
if (resArr[0].status === 'fulfilled') {
const res0 = resArr[0].value;
if (res0.code === RESULT_CODE_SUCCESS) {
skimState.udmSubNum = res0.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;
});
}
}
}
/**初始数据函数 */
function loadData() {
fnGetNeState(); // 获取网元状态
userActivitySend();
upfTFSend(0);
upfTFSend(7);
upfTFSend(30);
interval10s.value = setInterval(() => {
upfTFActive.value = upfTFActive.value >= 2 ? 0 : upfTFActive.value + 1;
if (upfTFActive.value === 0) {
upfTFSend(7);
} else if (upfTFActive.value === 1) {
upfTFSend(30);
} else if (upfTFActive.value === 2) {
upfTFSend(0);
}
}, 10_000);
interval5s.value = setInterval(() => {
fnGetSkim(); // 获取概览信息
fnGetNeState(); // 获取网元状态
}, 5_000);
}
/**栏目信息跳转 */
function fnToRouter(name: string, query?: any) {
router.push({ name, query });
}
onMounted(() => {
fnGetSkim().then(() => {
loadData();
});
// 读取数据
dbGetJSON('tbl_mocn', `tmp`).then(data => {
if (data) {
Object.assign(mocnState.data, data);
}
});
});
onBeforeUnmount(() => {
clearInterval(interval10s.value);
clearInterval(interval5s.value);
});
/**MOCN状态 */
const mocnState = reactive({
title: 'Set MOCN Data',
visible: false,
data: {
/**基站数 */
baseNum: 0,
/**核心网数 */
coreNetNum: 0,
/**在线用户数 */
onlineUserNum: 0,
},
});
/**MOCN 右击设置 */
function fnRightClick() {
mocnState.visible = true;
}
</script>
<template>
<div class="viewport" ref="viewportDom">
<div class="brand">
<div
class="brand-title"
@click="toggle"
:title="t('views.dashboard.overview.fullscreen')"
>
{{ t('views.dashboard.overview.title') }}
<FullscreenExitOutlined v-if="isFullscreen" />
<FullscreenOutlined v-else />
</div>
<div class="brand-desc">{{ appStore.appName }}</div>
</div>
<div class="column">
<!--概览-->
<div class="skim panel">
<div class="inner">
<h3>
<IdcardOutlined style="color: #68d8fe" />&nbsp;&nbsp;
{{ t('views.dashboard.overview.skim.userTitle') }}
</h3>
<div class="data">
<div
class="item toRouter"
@click="fnToRouter('Sub_2010')"
:title="t('views.dashboard.overview.toRouter')"
>
<div>
<UserOutlined
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
/>
{{ skimState.udmSubNum }}
</div>
<span>
{{ t('views.dashboard.overview.skim.users') }}
</span>
</div>
<div
class="item toRouter"
@click="fnToRouter('Ims_2080')"
:title="t('views.dashboard.overview.toRouter')"
style="margin: 0 12px"
>
<div>
<img :src="svgUserIMS" style="width: 18px; margin-right: 8px" />
{{ skimState.imsUeNum }}
</div>
<span>
{{ t('views.dashboard.overview.skim.imsUeNum') }}
</span>
</div>
<div
class="item toRouter"
@click="fnToRouter('Ue_2081')"
:title="t('views.dashboard.overview.toRouter')"
>
<div>
<img :src="svgUserSMF" style="width: 18px; margin-right: 8px" />
{{ skimState.smfUeNum }}
</div>
<span>
{{ t('views.dashboard.overview.skim.smfUeNum') }}
</span>
</div>
</div>
</div>
</div>
<div class="skim panel base">
<div class="inner">
<h3>
<GlobalOutlined style="color: #68d8fe" />&nbsp;&nbsp;
{{ t('views.dashboard.overview.skim.baseTitle') }}
</h3>
<div class="data">
<div
class="item toRouter"
@click="fnToRouter('Base5G_2082', { neType: 'AMF' })"
:title="t('views.dashboard.overview.toRouter')"
>
<div style="align-items: flex-start">
<img
:src="svgBase"
style="width: 18px; margin-right: 8px; height: 2rem"
/>
{{ skimState.gnbNum }}
</div>
<span>{{ t('views.dashboard.overview.skim.gnbBase') }}</span>
</div>
<div
class="item toRouter"
@click="fnToRouter('Base5G_2082', { neType: 'AMF' })"
:title="t('views.dashboard.overview.toRouter')"
>
<div style="align-items: flex-start">
<UserOutlined
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
/>
{{ skimState.gnbUeNum }}
</div>
<span>{{ t('views.dashboard.overview.skim.gnbUeNum') }}</span>
</div>
</div>
<div class="data">
<div
class="item toRouter"
@click="fnToRouter('Base5G_2082', { neType: 'MME' })"
:title="t('views.dashboard.overview.toRouter')"
>
<div style="align-items: flex-start">
<img
:src="svgBase"
style="width: 18px; margin-right: 8px; height: 2rem"
/>
{{ skimState.enbNum }}
</div>
<span>{{ t('views.dashboard.overview.skim.enbBase') }}</span>
</div>
<div
class="item toRouter"
@click="fnToRouter('Base5G_2082', { neType: 'MME' })"
:title="t('views.dashboard.overview.toRouter')"
>
<div style="align-items: flex-start">
<UserOutlined
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
/>
{{ skimState.enbUeNum }}
</div>
<span>{{ t('views.dashboard.overview.skim.enbUeNum') }}</span>
</div>
</div>
</div>
</div>
<!-- 用户行为 -->
<div class="userActivity panel">
<div class="inner">
<h3>
<WhatsAppOutlined style="color: #68d8fe" />&nbsp;&nbsp;
{{ t('views.dashboard.overview.userActivity.title') }}
</h3>
<div class="chart">
<UserActivity />
</div>
</div>
</div>
</div>
<div class="column" style="flex: 4; margin: 1.333rem 0.833rem 0">
<!-- 实时流量 -->
<div class="upfFlow panel">
<div class="inner">
<h3
class="toRouter"
@click="fnToRouter('GoldTarget_2104')"
:title="t('views.dashboard.overview.toRouter')"
>
<AreaChartOutlined style="color: #68d8fe" />&nbsp;&nbsp;
{{ t('views.dashboard.overview.upfFlow.title') }}
</h3>
<div class="chart">
<UPFFlow />
</div>
</div>
</div>
<!-- 网络拓扑 -->
<div class="topology panel">
<div class="inner">
<h3
class="toRouter"
@click="fnToRouter('TopologyArchitecture_2128')"
:title="t('views.dashboard.overview.toRouter')"
>
<span>
<ApartmentOutlined style="color: #68d8fe" />&nbsp;&nbsp;
{{ t('views.dashboard.overview.topology.title') }}
</span>
<span>
{{ t('views.dashboard.overview.topology.normal') }}:
<span class="normal"> {{ graphNodeStateNum[0] }} </span>
{{ t('views.dashboard.overview.topology.abnormal') }}:
<span class="abnormal"> {{ graphNodeStateNum[1] }} </span>
</span>
</h3>
<div class="chart">
<Topology />
</div>
</div>
</div>
</div>
<div class="column">
<!-- 流量统计 -->
<div class="upfFlowTotal panel">
<div class="inner">
<h3>
<span>
<SwapOutlined style="color: #68d8fe" />&nbsp;&nbsp;
{{ t('views.dashboard.overview.upfFlowTotal.title') }}
</span>
<!-- 筛选 -->
<div class="filter">
<span
:data-key="v"
:class="{ active: upfTFActive === i }"
v-for="(v, i) in ['0', '7', '30']"
:key="v"
@click="
() => {
upfTFActive = i;
}
"
>
{{
v === '0'
? '24' + t('common.units.hour')
: v + t('common.units.day')
}}
</span>
</div>
</h3>
<div class="chart">
<!-- 数据 -->
<div class="data">
<div class="item">
<span>
<ArrowUpOutlined style="color: #597ef7" />
{{ t('views.dashboard.overview.upfFlowTotal.up') }}
</span>
<h4>{{ upfTotalFlow[upfTFActive].up }}</h4>
</div>
<div class="item">
<span>
<ArrowDownOutlined style="color: #52c41a" />
{{ t('views.dashboard.overview.upfFlowTotal.down') }}
</span>
<h4>{{ upfTotalFlow[upfTFActive].down }}</h4>
</div>
</div>
</div>
</div>
</div>
<!-- MOCN -->
<div class="skim panel mocn">
<div class="inner">
<h3
class="toRouter"
@contextmenu.prevent="fnRightClick()"
@click="fnToRouter('GoldTarget_2104', { neType: 'MOCNGW' })"
:title="t('views.dashboard.overview.toRouter')"
>
<PieChartOutlined style="color: #68d8fe" />&nbsp;&nbsp; MOCN
Information
</h3>
<div class="chart">
<div class="data">
<div class="item" title="NodeB">
<div>
<img :src="svgBase" style="width: 18px; margin-right: 8px" />
{{ mocnState.data.baseNum }}
</div>
<span> NodeB </span>
</div>
<div class="item" title="CoreNet">
<div>
<img
:src="svgUserSMF"
style="width: 18px; margin-right: 8px"
/>
{{ mocnState.data.coreNetNum }}
</div>
<span> CoreNet </span>
</div>
</div>
<div class="data">
<div class="item" title="OnlineUser">
<div>
<UserOutlined
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
/>
{{ mocnState.data.onlineUserNum }}
</div>
<span> OnlineUser </span>
</div>
</div>
</div>
</div>
</div>
<!-- 告警统计 -->
<div class="alarmType panel">
<div class="inner">
<h3
class="toRouter"
@click="fnToRouter('HistoryAlarm_2097')"
:title="t('views.dashboard.overview.toRouter')"
>
<PieChartOutlined style="color: #68d8fe" />&nbsp;&nbsp;
{{ t('views.dashboard.overview.alarmTypeBar.alarmSum') }}
</h3>
<div class="chart">
<AlarnTypeBar />
</div>
</div>
</div>
<!-- 资源情况 -->
<div class="resources panel">
<div class="inner">
<h3>
<DashboardOutlined style="color: #68d8fe" />&nbsp;&nbsp;
{{ t('views.dashboard.overview.resources.title') }}
{{ graphNodeClickID }}
</h3>
<div class="chart">
<NeResources />
</div>
</div>
</div>
</div>
<setting
:title="mocnState.title"
v-model:visible="mocnState.visible"
></setting>
</div>
</template>
<style lang="less" scoped>
@import url('../overview/css/index.css');
.mocn {
height: 20.6%;
}
.mocn .inner .chart .data {
height: unset;
}
.mocn .inner .chart .data .item {
width: 50%;
}
.alarmType {
height: 24.4%;
}
</style>

View File

@@ -70,10 +70,10 @@ const alarmTypeType = ref<any>([
value: 0,
name: t('views.index.Warning'),
},
{
value: 0,
name: t('views.index.Event'),
},
// {
// value: 0,
// name: t('views.index.Event'),
// },
]);
/**告警类型Top数据 */
@@ -105,9 +105,9 @@ function initPicture() {
case 'Warning':
index = 3;
break;
case 'Event':
index = 4;
break;
// case 'Event':
// index = 4;
// break;
}
alarmTypeType.value[index].value = Number(item.value);
}
@@ -149,7 +149,7 @@ function initPicture() {
legend: {
orient: 'vertical',
right: '2%',
top: '10%',
top: '12%',
data: alarmTypeType.value.map((item: any) => item.name), //label数组
textStyle: {
color: '#A7D6F4', // 设置图例文字颜色

View File

@@ -237,7 +237,7 @@ function handleRanderChart(
function fnChangeData(data: any[], itemID: string) {
let info = data.find((item: any) => item.id === itemID);
if (!info.neState.online) return;
if (!info || !info.neState.online) return;
// if (!info.neState.online) {
// info = data.find((item: any) => item.id === itemID);
// graphNodeClickID.value = itemID;
@@ -277,6 +277,9 @@ function fnChangeData(data: any[], itemID: string) {
if (men > 100) {
men = +(men / 100).toFixed(2);
}
if (men > 100) {
men = 100;
}
sysMemUsage = men;
}

View File

@@ -76,7 +76,7 @@ function fnGraphEvent(graph: Graph) {
// 节点点击
graph.on('node:click', evt => {
// 获得鼠标当前目标节点
const node = evt.item?.getModel();
const node = evt.item?.getModel();
if (node && node.id && !notNeNodes.includes(node.id)) {
graphNodeClickID.value = node.id;
}
@@ -129,6 +129,9 @@ function handleRanderGraph(
var observer = new ResizeObserver(function (entries) {
// 当元素大小发生变化时触发回调函数
entries.forEach(function (entry) {
if (!graphG6.value) {
return;
}
graphG6.value.changeSize(
entry.contentRect.width,
entry.contentRect.height - 30

View File

@@ -203,17 +203,14 @@ function handleRanderChart() {
/**查询初始UPF数据 */
function fnGetInitData() {
// 查询10分钟前的
const nowDate: Date = new Date();
const tenMinutesAgo = new Date(nowDate.getTime() - 5 * 60 * 1000);
// 查询5分钟前的
const nowDate = new Date().getTime();
listKPIData({
neType: 'UPF',
neId: '001',
startTime: parseDateToStr(tenMinutesAgo),
endTime: parseDateToStr(nowDate),
// startTime: '2024-03-20 19:50:00',
// endTime: '2024-03-20 19:55:00',
startTime: nowDate - 5 * 60 * 1000,
endTime: nowDate,
interval: 5, // 5秒
sortField: 'timeGroup',
sortOrder: 'asc',

View File

@@ -58,11 +58,11 @@ onMounted(() => {
<template>
<div class="activty">
<template v-for="item in eventData" :key="item.eId">
<!-- CDR事件 -->
<!-- CDR事件IMS -->
<div
class="card-cdr"
:class="{ active: item.eId === eventId }"
v-if="item.eType === 'cdr'"
v-if="item.eType === 'ims_cdr'"
>
<div class="card-cdr-item">
<div>
@@ -104,18 +104,22 @@ onMounted(() => {
<div>
{{ t('views.dashboard.overview.userActivity.result') }}:&nbsp;
<span v-if="item.data.callType !== 'sms'">
<DictTag :options="dict.cdrSipCode" :value="item.data.cause" value-option="0" />
<DictTag
:options="dict.cdrSipCode"
:value="item.data.cause"
value-default="0"
/>
</span>
<span v-else>
{{ t('views.dashboard.overview.userActivity.resultOK') }}
</span>
</div>
</div>
<!-- UE事件 -->
<!-- UE事件AMF -->
<div
class="card-ue"
:class="{ active: item.eId === eventId }"
v-if="item.eType === 'ue'"
v-if="item.eType === 'amf_ue'"
>
<div class="card-ue-item">
<div>
@@ -155,6 +159,7 @@ onMounted(() => {
TAC ID: <span>{{ item.data.tacID }}</span>
</div>
</div>
<div v-if="item.type === 'auth-result'">
{{ t('views.dashboard.overview.userActivity.result') }}:&nbsp;
<span>
@@ -172,96 +177,59 @@ onMounted(() => {
</span>
</div>
</div>
<!-- UE事件MME -->
<div
class="card-ue"
:class="{ active: item.eId === eventId }"
v-if="item.eType === 'mme_ue'"
>
<div class="card-ue-item">
<div>
{{ t('views.dashboard.overview.userActivity.type') }}:&nbsp;
<span>
<DictTag :options="dict.ueEventType" :value="item.type" />
</span>
</div>
<div>
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 class="card-ue-w33" v-if="item.type === 'auth-result'">
<div>
ENB ID: <span>{{ item.data.eNBID }}</span>
</div>
<div>
Cell ID: <span>{{ item.data.cellID }}</span>
</div>
<div>
TAC ID: <span>{{ item.data.tacID }}</span>
</div>
</div>
<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'">
{{ 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-state'">
{{ t('views.dashboard.overview.userActivity.result') }}:&nbsp;
<span>
<DictTag :options="dict.ueEventCmState" :value="item.data.result" />
</span>
</div>
</div>
</template>
<!-- <div class="card-cdr active">
<div class="card-cdr-item">
<div>类型: <span>video</span></div>
<div>时长: <span>123s</span></div>
</div>
<div class="card-cdr-item">
<div>主叫: <span>12307550064</span></div>
<div>被叫: <span>12307550064</span></div>
</div>
<div>结果: <span>200</span></div>
</div>
<div class="card-cdr">
<div class="card-cdr-item">
<div>类型: <span>audio</span></div>
<div>时长: <span>123s</span></div>
</div>
<div class="card-cdr-item">
<div>主叫: <span>12307550064</span></div>
<div>被叫: <span>12307550064</span></div>
</div>
<div>结果: <span>200</span></div>
</div>
<div class="card-ue">
<div class="card-ue-item">
<div>类型: <span>auth-result</span></div>
<div>Time: <span>2023-01-16 07:28:11</span></div>
</div>
<div>IMSI: <span>4600212141</span></div>
<div class="card-ue-auth">
<div>GNB ID: <span>31</span></div>
<div>Cell ID: <span>17</span></div>
<div>Tac ID: <span>98</span></div>
</div>
</div>
<div class="card-ue">
<div class="card-ue-item">
<div>类型: <span>cm-state</span></div>
<div>Time: <span>2023-01-16 07:28:11</span></div>
</div>
<div>IMSI: <span>4600212141</span></div>
</div> -->
<!-- <div class="card-cdr">
{ "recordType":"MOC", // MOC, MTC, MOSM, MTSM
"seqNumber":81,
"callReference":"Y6ecb69Bj@10.25.0.210",
"callerParty":"7112",
"calledParty":"7108",
"serviceResult":"ok",
"seizureTime":1706515269,
"answerTime":1706515273,
"releaseTime":1706515294,
"callDuration":21
"callType":"audio" // audio, video
"fwdType": "CFB" // CFU,CFB, CFNR, CFNL
"fwdParty":"7999",
"cause": 200 // 200, 403, 408, 500 .... }
{"neType":"IMS","neName":"IMS_001","rmUID":"4400HX1IMS001","timestamp":1707124616,
"CDR":{"recordType":"MOSM","seqNumber":1,"callReference":"IIocbkeoj@10.10.91.22",
"callerParty":"12307551241","calledParty":"+8613800755000","serviceResult":"ok",
"seizureTime":1707124616,"answerTime":1707124616,"releaseTime":1707124616,
"callDuration":0,"callType":"text","fwdType":"","fwdParty":"","cause":200}}
https://telnyx.com/resources/sip-response-codes-need-know-2-minutes
主叫callerParty
被叫calledParty
时长callDuration
呼叫类型callType
原因cause
信息 主叫 -> 被叫
</div>
<div class="card-ue">ue
事件类型auth-result
imei
GNB ID
Cell ID
Tac ID
authTime
事件类型detach
imsi
detachTime
事件类型cm-state
imsi
changeTime
</div> -->
</div>
</template>

View File

@@ -159,14 +159,14 @@
/* 概览区域 衍生基站信息 */
.skim.base {
height: 20.6%;
height: 12%;
}
.skim.base .inner .data {
display: flex;
flex-direction: row;
align-items: center;
height: 45%;
height: 75%;
}
.skim.base .inner .data .item {
display: flex;
@@ -178,7 +178,7 @@
/* 用户行为 */
.userActivity {
/* min-height: 35.8rem; */
height: 60%;
height: 54.6%;
}
.userActivity .inner .chart {
width: 100%;
@@ -221,7 +221,7 @@
.upfFlowTotal .inner .chart {
width: 100%;
height: 100%;
margin-top: 1rem;
margin-top: 0.1rem;
}
.upfFlowTotal .inner .chart .data {
display: flex;

View File

@@ -158,3 +158,15 @@ export function neStateParse(neType: string, data: Record<string, any>) {
// 请求标记复位
neStateRequestMap.value.set(neType, false);
}
/**属性复位 */
export function topologyReset() {
graphState.data = {
combos: [],
edges: [],
nodes: [],
};
graphG6.value = null;
graphNodeClickID.value = 'UPF';
neStateRequestMap.value = new Map();
}

View File

@@ -1,8 +1,8 @@
import { parseDateToStr } from '@/utils/date-utils';
import { parseSizeFromBits, parseSizeFromKbs } from '@/utils/parse-utils';
import { ref } from 'vue';
/**UPF-流量数据 */
export const upfFlowData = ref<{
type FDType = {
/**时间 */
lineXTime: string[];
/**上行 N3 */
@@ -11,7 +11,10 @@ export const upfFlowData = ref<{
lineYDown: number[];
/**容量 */
cap: number;
}>({
};
/**UPF-流量数据 */
export const upfFlowData = ref<FDType>({
lineXTime: [],
lineYUp: [],
lineYDown: [],
@@ -20,7 +23,7 @@ export const upfFlowData = ref<{
/**UPF-流量数据 数据解析 */
export function upfFlowParse(data: Record<string, string>) {
upfFlowData.value.lineXTime.push(data['timeGroup']);
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);
@@ -74,3 +77,32 @@ export function upfTFParse(data: Record<string, string>) {
/**UPF-总流量数 选中 */
export const upfTFActive = ref<number>(0);
/**属性复位 */
export function upfTotalFlowReset() {
upfFlowData.value = {
lineXTime: [],
lineYUp: [],
lineYDown: [],
cap: 0,
};
upfTotalFlow.value = [
// 0天 当天24小时
{
up: '0 B',
down: '0 B',
requestFlag: false,
},
{
up: '0 B',
down: '0 B',
requestFlag: false,
},
{
up: '0 B',
down: '0 B',
requestFlag: false,
},
];
upfTFActive.value = 0;
}

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