79 Commits

Author SHA1 Message Date
TsMask
8f968839fa fix: 拓扑信息编辑模式控制 2023-12-29 18:43:14 +08:00
TsMask
8523b34c19 chore: 更新版本及说明 2023-12-29 18:42:29 +08:00
lai
d09171187d 更改告警详细信息展示方式 2023-12-29 18:09:24 +08:00
TsMask
7e894c4073 feat: 拓扑编辑功能绘制简单关系图 2023-12-29 18:04:58 +08:00
TsMask
6f5d02e479 fix: 打包编译类型错误导致失败 2023-12-29 18:03:56 +08:00
TsMask
b50c5250bf feat: 拓扑编辑功能操作 2023-12-29 16:20:57 +08:00
TsMask
f0ab5d5ea6 Merge branch 'main' of http://192.168.2.166:3180/OMC/ems_frontend_vue3 2023-12-29 16:19:42 +08:00
TsMask
d9c561e677 feat: 拓扑编辑多语言函数处理 2023-12-29 11:56:52 +08:00
TsMask
e4c829a46d style: 移除参数配置控制台输出 2023-12-29 11:49:07 +08:00
lai
139cc1bd0d 修改挂起英文名 2023-12-29 11:30:45 +08:00
lai
7297bd5d0f 性能提示语错误 2023-12-29 11:21:52 +08:00
TsMask
aa0c92a8d1 feat: 拓扑编辑分组功能 2023-12-29 10:17:31 +08:00
lai
a53fd939e1 新增五分钟颗粒度 2023-12-28 20:34:00 +08:00
lai
25b5212588 新增 删除网元时数据结构的处理 2023-12-28 20:33:35 +08:00
TsMask
5e463a6ce8 feat: 拓扑编辑代码优化 2023-12-28 15:20:04 +08:00
lai
fb4babf09a 活动告警中应答状态英文 2023-12-28 10:54:00 +08:00
lai
04b4c25c38 删除时未能刷新表格以及修复黄金指标警告 2023-12-28 10:53:42 +08:00
TsMask
9c61026c88 feat: 设置语言时同时设置HTML lang 2023-12-28 09:25:03 +08:00
TsMask
24816eb991 feat: 拓扑编辑节点功能 2023-12-27 20:25:08 +08:00
TsMask
b756fe9d56 style: 修改多语言key拼写错误 2023-12-27 17:42:15 +08:00
TsMask
1c8f208453 fix: 多语言key拼写错误 2023-12-27 17:41:19 +08:00
TsMask
5e8547aff8 Merge branch 'main' of http://192.168.2.166:3180/OMC/ems_frontend_vue3 2023-12-27 17:40:28 +08:00
TsMask
e6f0dd9f21 fix: 软件管理上传版本号提取 2023-12-27 17:40:04 +08:00
TsMask
5e8f65f11b fix: PCF批量删除表单校验提示异常问题 2023-12-27 17:32:32 +08:00
lai
ce86edef6c 新增首页异常网元版本号 2023-12-27 17:10:13 +08:00
lai
167cecba84 网元管理中英文 2023-12-27 17:09:36 +08:00
lai
d8d1b53027 网元管理新增同步网元开关 2023-12-27 17:09:09 +08:00
lai
9fdf13676e --黄金指标增加排序 2023-12-27 17:08:33 +08:00
TsMask
f20e9bc000 feat: 拓扑编辑边表单排版 2023-12-26 19:35:35 +08:00
TsMask
45b04975eb feat: 拓扑编辑边 2023-12-26 16:24:54 +08:00
lai
6441a1efaf 修正翻译 2023-12-26 14:14:04 +08:00
lai
7e98f35f5b 添加异常情况网元展示SN 2023-12-26 11:51:11 +08:00
lai
4f34b3bca9 自定义排序导出 2023-12-26 11:21:23 +08:00
TsMask
b8e8c07704 chore: 依赖版本升级 2023-12-26 10:13:19 +08:00
TsMask
3aea520289 feat: 拓扑编辑demo 2023-12-26 10:10:59 +08:00
TsMask
b54027bf0f style: 更多悬浮提示靠左避免遮挡 2023-12-26 10:10:16 +08:00
TsMask
0eb7bc9497 fix: 软件版本管理上传版本号截取 2023-12-26 10:09:44 +08:00
lai
7c69967148 新增导出插件的可排序功能 2023-12-26 10:03:00 +08:00
TsMask
dd14a32329 fix: 终端UDM数据勾选导出和勾选删除 2023-12-25 20:56:43 +08:00
TsMask
864be2ba3b fix: 参数配置值类型为字符串http判断 2023-12-25 20:55:59 +08:00
TsMask
e79d054ceb fix: 拓扑信息OMC默认无信息 2023-12-25 17:07:34 +08:00
TsMask
12f810d890 fix: 配置-参数配置里面的删除按钮,建议点击后增加二次确认避免误触 2023-12-25 17:05:20 +08:00
TsMask
715471f950 fix: 网元管理弹出菜单窗口设置为可移动 2023-12-25 17:04:42 +08:00
a23cfd209f fix: changelog 2.2312.9-20231222 2023-12-22 16:34:42 +08:00
TsMask
0f3df3a80b fix: 主页sn号读取配置 2023-12-22 15:54:38 +08:00
lai
04754a022f 外显SN,取消点击表头 2023-12-22 15:40:34 +08:00
lai
61df24059c 网元删除时表格锁定 2023-12-22 14:58:56 +08:00
TsMask
fbd41e2ebf style: 表格头按钮区域禁用按钮偏移 2023-12-22 12:22:47 +08:00
TsMask
cf7d234f91 chore: 版本说明 2023-12-22 10:43:13 +08:00
TsMask
b556dc36e6 Merge branch 'main' of http://192.168.2.166:3180/OMC/ems_frontend_vue3 2023-12-21 20:44:45 +08:00
TsMask
0c6ea19878 feat: 可编辑拓扑页面 2023-12-21 20:44:42 +08:00
TsMask
0c52f2cf3b fix: 拓扑变换布局 2023-12-21 20:44:05 +08:00
TsMask
2470eed417 feat: 读取服务端提供的版本号 2023-12-21 20:43:45 +08:00
lai
89c7b483d4 修正 2023-12-21 20:37:12 +08:00
lai
b26104b4bc 修正 2023-12-21 20:27:00 +08:00
acd8337606 fix: version 2023-12-21 20:06:22 +08:00
lai
eac0d957aa 优化首页echart代码 2023-12-21 16:03:19 +08:00
TsMask
eae2dd5d30 feat: 拓扑信息简览 2023-12-21 15:41:14 +08:00
TsMask
fa1bb05548 feat: 拓扑图接入数据显示状态 2023-12-21 14:45:03 +08:00
lai
6b855b8ff2 修正expend->expand 2023-12-21 14:10:21 +08:00
TsMask
11bfae8c0f fix: 日志文件切换IMS读取失败 2023-12-20 11:26:49 +08:00
lai
823114a6d0 --调整回退按钮 2023-12-20 11:22:55 +08:00
lai
99c6d233e5 Merge branch 'main' of http://192.168.2.166:3180/OMC/ems_frontend_vue3 2023-12-20 11:20:12 +08:00
TsMask
728d4b00d8 fix: 英文翻译 reload tab 2023-12-20 10:08:11 +08:00
TsMask
8288184f15 fix: 英文翻译tab/expend 2023-12-20 09:45:38 +08:00
lai
6fc9a305ce 修复历史告警-确认告警 2023-12-19 15:23:16 +08:00
lai
e8b95a594b --首页状态颜色自定义 2023-12-19 15:22:40 +08:00
TsMask
859eeda1fe fix: 告警帮助文档全屏弹窗 2023-12-19 14:00:48 +08:00
TsMask
3f20faf5be feat: 参数配置都是可选的参数 2023-12-18 20:23:17 +08:00
TsMask
208d7a4725 fix: PCF文件上传结果显示内容 2023-12-18 15:00:56 +08:00
TsMask
1c44651ad2 feat: 关系图 2023-12-15 18:30:46 +08:00
TsMask
f015f45601 feat: 关系图普通组件 2023-12-15 18:27:13 +08:00
TsMask
dfe06e2098 fix: 注释打包异常问题 2023-12-15 18:23:59 +08:00
TsMask
44c05e964e feat: G6版本使用稳定4.8 2023-12-15 18:16:58 +08:00
TsMask
e210dd7e33 Merge branch 'main' of http://192.168.2.166:3180/OMC/ems_frontend_vue3 2023-12-15 15:20:07 +08:00
TsMask
920bca7912 chore: 标记版本日期 2023-12-15 15:15:14 +08:00
TsMask
b0d7b7d539 feat: 拓扑图Dome 2023-12-15 15:15:09 +08:00
TsMask
cf9968a899 chore: 标记版本日期 2023-12-15 14:25:18 +08:00
TsMask
f67d083e10 feat: 拓扑图Dome 2023-12-15 14:24:04 +08:00
52 changed files with 8306 additions and 1385 deletions

View File

@@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network EMS"
VITE_APP_CODE = "CN EMS"
# 应用版本
VITE_APP_VERSION = "2.231211.9"
VITE_APP_VERSION = "2.231229.10"
# 接口基础URL地址-不带/后缀
VITE_API_BASE_URL = "/omc-api"

36
CHANGELOG.md Normal file
View File

@@ -0,0 +1,36 @@
# 版本发布日志
## 2.2312.10-20231229
- 新增 告警列表记录自定义排序导出
- 新增 黄金指标 KPI 数据列表支持排序
- 新增 黄金指标 KPI 数据支持选择五分钟颗粒度
- 新增 网元管理新增修改时可选同步网元信息的开关
- 新增 终端-UDM 数据勾选记录导出和勾选记录删除功能
- 修复 配置-参数配置里面的删除按钮,增加二次确认避免误触
- 修复 配置-参数配置值类型为字符串 http 判断
- 修复 软件版本管理上传版本号截取
- 修复 PCF 用户策略控制批量删除表单选项校验异常提示
- 修复 打包编译时类型异常问题
- 优化 网元管理弹出菜单窗口设置为可移动
- 优化 网元管理鼠标悬浮更多时提示靠左显示避免遮挡
- 优化 首页网元异常情况展示 SN
- 优化 告警详细信息展示方式
## 2.2312.9-20231222
- 新增 日志管理查看网元日志文件并下载功能页面
- 新增 国际化切换权限控制,系统设置里控制切换默认语言
- 新增 性能管理 KPI 指标集功能页面
- 新增 UE 管理 PCF 用户策略控制功能页面
- 修复 参数配置新增数据类型值不匹配导致的异常失败
- 修复 多处英文显示词义不明确的信息提示
- 修复 UE 管理 UDM 签约用户数据批量新增时 EPSODB 转换十六进制问题
- 优化 网元管理重启接口超时导致网元信息不一致问题
- 优化 参数配置新增编辑参数顺序,参数类型格式化
- 优化 主页网元状态饼图颜色选择自定义
- 优化 MML 操作 UDM 签约用户信息字段,必填项标红
- 优化 软件管理回退功能操作页面
- 优化 静态文件地址追加随机戳避免缓存
- 优化 补充移动端样式适配调整
- 主页修改增加 OMC 的序列号,点击网元信息不再覆盖右边信息框

View File

@@ -56,6 +56,7 @@ eyJhbGciOiJSUzI1NiIsImtpZCI6ImZFVUhIb1puLW04M1dfSUYyRU8zWlZueXBpNUh4T0hTRVlzU19j
```
```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

@@ -13,13 +13,12 @@
},
"dependencies": {
"@ant-design/icons-vue": "^7.0.1",
"@antv/g6": "^5.0.0-beta.27",
"@antv/g6": "^4.8.24",
"@codemirror/lang-javascript": "^6.2.1",
"@codemirror/merge": "^6.1.2",
"@codemirror/merge": "^6.4.0",
"@codemirror/theme-one-dark": "^6.1.2",
"@tato30/vue-pdf": "^1.8.1",
"@vueuse/components": "^10.6.1",
"@vueuse/core": "^10.6.1",
"@vueuse/core": "^10.7.0",
"ant-design-vue": "^3.2.20",
"antdv-pro-layout": "^3.2.6",
"codemirror": "^6.0.1",
@@ -30,7 +29,7 @@
"js-cookie": "^3.0.5",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"vue": "^3.3.10",
"vue": "^3.3.13",
"vue-codemirror": "^6.1.1",
"vue-i18n": "^9.8.0",
"vue-router": "^4.2.5",
@@ -41,12 +40,12 @@
"@types/file-saver": "^2.0.7",
"@types/js-cookie": "^3.0.6",
"@types/node": "^18.0.0",
"@types/nprogress": "^0.2.0",
"@vitejs/plugin-vue": "^4.5.1",
"@types/nprogress": "^0.2.3",
"@vitejs/plugin-vue": "^4.5.2",
"less": "^4.2.0",
"typescript": "^5.2.2",
"typescript": "^5.3.3",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.0.5",
"vite": "^5.0.10",
"vite-plugin-compression": "^0.5.1",
"vue-tsc": "^1.8.25"
}

1
public/svg/base.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1702978675818" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4327" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512.1 955.7c-1.4 0-2.7-0.4-4-1.1L80.4 706.4c-2.4-1.4-3.9-4-3.9-6.8v-88.8c0-2.8 1.5-5.4 3.9-6.8l427.7-252c1.2-0.7 2.6-1.1 4-1.1s2.8 0.4 4 1.1l427.6 252c2.4 1.4 3.9 4 3.9 6.8v88.8c0 2.8-1.5 5.4-3.9 6.8L516 954.6c-1.2 0.7-2.6 1.1-3.9 1.1z" fill="#B5D6FB" p-id="4328"></path><path d="M512.1 358.8l427.5 252v88.8L869.9 740 512.1 947.8 154.1 740l-69.7-40.4v-88.8l427.7-252m0-15.8c-2.8 0-5.5 0.7-8 2.2l-427.7 252c-4.8 2.8-7.8 8-7.8 13.6v88.8c0 5.6 3 10.8 7.9 13.7l69.7 40.5 358 207.8c2.4 1.4 5.2 2.1 7.9 2.1s5.5-0.7 7.9-2.1l357.8-207.8 69.7-40.4c4.9-2.8 7.9-8 7.9-13.7v-88.8c0-5.6-3-10.8-7.8-13.6l-427.5-252c-2.5-1.5-5.3-2.3-8-2.3z" fill="#0276F7" p-id="4329"></path><path d="M84.4 610.8L512 860.7l427.6-249.9-427.5-252z" fill="#FFFFFF" p-id="4330"></path><path d="M500.9 70.3L232.1 562.1l277.7 159L791 561.2z" fill="#D4E4FC" p-id="4331"></path><path d="M214.6 601.2c-1.3 0-2.6-0.3-3.9-1-3.8-2.1-5.2-6.9-3-10.7l294.9-525c1.4-2.5 4-4 6.9-4 2.8 0 5.4 1.5 6.9 4l299.9 525c2.2 3.8 0.8 8.6-2.9 10.8-3.8 2.1-8.6 0.8-10.8-2.9l-293-512.9-288.1 512.7c-1.5 2.6-4.2 4-6.9 4z" fill="#0276F7" p-id="4332"></path><path d="M509.8 752.4c-4.4 0-7.9-3.5-7.9-7.9V86.7c0-4.4 3.5-7.9 7.9-7.9s7.9 3.5 7.9 7.9v657.9c0 4.3-3.6 7.8-7.9 7.8z" fill="#0276F7" p-id="4333"></path><path d="M509.8 729c-1.4 0-2.7-0.4-3.9-1L228.2 569c-3.8-2.2-5.1-7-2.9-10.8 2.2-3.8 7-5.1 10.8-2.9L509.8 712l277.3-157.7c3.8-2.2 8.6-0.8 10.8 3 2.2 3.8 0.8 8.6-3 10.8L513.7 728c-1.2 0.7-2.6 1-3.9 1z" fill="#0276F7" p-id="4334"></path><path d="M509.8 578.9c-1.4 0-2.7-0.4-4-1.1l-213-124c-3.8-2.2-5-7-2.9-10.8 2.2-3.8 7-5.1 10.8-2.8l209.1 121.7 212.8-120.5c3.8-2.2 8.6-0.8 10.8 3 2.2 3.8 0.8 8.6-3 10.7L513.7 577.9c-1.2 0.7-2.6 1-3.9 1zM507.2 423.5c-1.3 0-2.5-0.3-3.7-0.9L354.9 344c-3.9-2.1-5.3-6.8-3.3-10.7 2-3.9 6.8-5.3 10.7-3.3l145 76.7 150.8-78.8c3.9-2 8.6-0.5 10.7 3.3 2 3.9 0.5 8.6-3.3 10.7L511 422.6c-1.3 0.6-2.5 0.9-3.8 0.9zM509.8 277c-1.3 0-2.6-0.3-3.7-0.9l-86.8-46.8c-3.8-2.1-5.3-6.9-3.2-10.7 2.1-3.9 6.9-5.2 10.7-3.2l83 44.8 82.6-46.9c3.8-2.2 8.6-0.8 10.8 3 2.2 3.8 0.8 8.6-3 10.8l-86.4 49c-1.3 0.6-2.7 0.9-4 0.9z" fill="#0276F7" p-id="4335"></path><path d="M509.8 578.9h-0.3L231.9 570c-4.4-0.1-7.8-3.8-7.6-8.1 0.1-4.4 4.3-7.6 8.1-7.6l277.7 8.9c4.4 0.1 7.8 3.8 7.6 8.1-0.2 4.2-3.7 7.6-7.9 7.6zM509.8 729c-1.7 0-3.4-0.6-4.9-1.7-3.4-2.7-4-7.7-1.3-11.1l216.7-272.9c2.7-3.4 7.7-4 11.1-1.3 3.4 2.7 4 7.7 1.3 11.1L516 726c-1.6 2-3.9 3-6.2 3z" fill="#0276F7" p-id="4336"></path><path d="M509.8 578.9c-1.5 0-2.9-0.4-4.3-1.2-3.7-2.4-4.7-7.2-2.4-10.9l152-236.1c2.4-3.7 7.3-4.7 10.9-2.4 3.7 2.4 4.7 7.2 2.4 10.9l-152 236.1c-1.5 2.3-4 3.6-6.6 3.6zM507.2 423.5c-1.1 0-2.2-0.2-3.3-0.7-4-1.8-5.7-6.5-3.9-10.5l89-195.5c1.8-4 6.4-5.7 10.5-3.9 4 1.8 5.7 6.5 3.9 10.5l-88.9 195.5c-1.4 2.9-4.3 4.6-7.3 4.6z" fill="#0276F7" p-id="4337"></path><path d="M596.2 377c-2.9 0-5.7-1.6-7.1-4.4-1.9-3.9-0.3-8.6 3.6-10.5L729 295.7 343.3 120l22 205.3 141.3-63.4c3.9-1.8 8.6 0 10.4 4 1.8 4 0 8.6-4 10.4l-151.2 67.9c-2.3 1-4.9 0.9-7.2-0.4-2.2-1.3-3.6-3.5-3.9-6l-24.6-229.9c-0.3-2.8 0.9-5.6 3.2-7.2 2.3-1.6 5.3-2 7.9-0.8l413.6 188.4c2.8 1.3 4.6 4 4.6 7.1 0 3.1-1.7 5.9-4.4 7.2l-151.4 73.6c-1.1 0.6-2.3 0.8-3.4 0.8zM296.8 454.9c-3.8 0-7.2-2.8-7.8-6.7-0.6-4.3 2.3-8.3 6.6-9l210.5-31.4c4.3-0.6 8.3 2.3 9 6.6 0.6 4.3-2.3 8.3-6.6 9L298 454.8c-0.5 0.1-0.9 0.1-1.2 0.1z" fill="#0276F7" p-id="4338"></path></svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

1
public/svg/cloud.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1702978716365" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4633" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M817.3 505c2.7-14 4.3-28.4 4.3-43.2 0-123.8-100.4-224.2-224.2-224.2-81.9 0-153.3 44.1-192.5 109.7-15.1-5.9-31.5-9.2-48.7-9.2-72.6 0-131.6 57.6-134.3 129.5-69 18.6-119.9 81.5-119.9 156.5 0 89.6 72.7 162.3 162.3 162.3h514.3c79.2 0 143.4-64.2 143.4-143.4 0-65.8-44.3-121-104.7-138z" fill="#D4E4FC" p-id="4634"></path><path d="M778.6 793.2H264.3C171 793.2 95.2 717.3 95.2 624c0-74.5 49.2-140.3 120.2-161.8 5.3-73.9 66.2-131.1 140.9-131.1 15.6 0 30.9 2.6 45.8 7.7 42.7-67.8 115.3-108.1 195.4-108.1 127.4 0 231 103.6 231 231 0 12.7-1.1 25.6-3.3 38.4 61.3 20 103.6 77.8 103.6 142.7 0 83-67.4 150.4-150.2 150.4zM356.3 344.9c-68.9 0-124.9 54-127.4 122.9l-0.2 5.1-4.9 1.3c-67.6 18.3-114.9 79.9-114.9 149.9 0 85.7 69.7 155.4 155.4 155.4h514.3c75.3 0 136.5-61.2 136.5-136.5 0-60.9-41-114.9-99.6-131.3l-6.1-1.7 1.2-6.2c2.7-14 4.1-28.1 4.1-41.9 0-119.8-97.5-217.3-217.3-217.3-77.1 0-146.9 39.7-186.6 106.3l-3 5-5.4-2.1c-14.9-5.9-30.4-8.9-46.1-8.9z" fill="#0276F7" p-id="4635"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

1
public/svg/service.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1702974353127" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4174" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M470.9 954.6c-1.2 0-2.3-0.3-3.4-0.9l-240-132.2c-2.2-1.2-3.6-3.6-3.6-6.1V270.6c0-2.5 1.3-4.7 3.4-6L546.9 70.4c1.1-0.7 2.4-1 3.6-1 1.2 0 2.4 0.3 3.4 0.9l242.6 136.9c2.2 1.2 3.6 3.6 3.6 6.1v552.8c0 2.5-1.4 4.9-3.6 6.1L474.4 953.7c-1.1 0.6-2.3 0.9-3.5 0.9z" fill="#B5D6FB" p-id="4175"></path><path d="M550.5 76.4l242.6 136.9v552.8L470.9 947.6l-240-132.2V270.6L550.5 76.4m0-14c-2.5 0-5 0.7-7.3 2L223.6 258.6c-4.2 2.5-6.7 7.1-6.7 12v544.8c0 5.1 2.8 9.8 7.3 12.3l240 132.2c2.1 1.2 4.4 1.7 6.8 1.7 2.4 0 4.7-0.6 6.9-1.8L800 778.3c4.4-2.5 7.1-7.2 7.1-12.2V213.3c0-5.1-2.7-9.7-7.1-12.2L557.4 64.2c-2.1-1.2-4.5-1.8-6.9-1.8z" fill="#0276F7" p-id="4176"></path><path d="M550.5 76.4L230.9 270.6l240 130.5 322.2-187.8z" fill="#FFFFFF" p-id="4177"></path><path d="M470.9 916V401.1" fill="#B5D6FB" p-id="4178"></path><path d="M470.9 923.1c-3.9 0-7-3.1-7-7v-515c0-3.9 3.1-7 7-7s7 3.1 7 7V916c0 3.9-3.1 7.1-7 7.1z" fill="#0276F7" p-id="4179"></path><path d="M754.5 235.8L470.9 401.1" fill="#B5D6FB" p-id="4180"></path><path d="M470.9 408.1c-2.4 0-4.8-1.2-6.1-3.5-1.9-3.3-0.8-7.6 2.5-9.6L751 229.7c3.4-2 7.6-0.8 9.6 2.5 1.9 3.3 0.8 7.6-2.5 9.6L474.5 407.2c-1.1 0.6-2.4 0.9-3.6 0.9z" fill="#0276F7" p-id="4181"></path><path d="M261.5 287.2l209.4 113.9" fill="#B5D6FB" p-id="4182"></path><path d="M470.9 408.1c-1.1 0-2.3-0.3-3.3-0.8L258.1 293.4c-3.4-1.8-4.7-6.1-2.8-9.5 1.9-3.4 6.1-4.6 9.5-2.8L474.3 395c3.4 1.8 4.7 6.1 2.8 9.5-1.3 2.3-3.7 3.6-6.2 3.6z" fill="#0276F7" p-id="4183"></path><path d="M258.2 744.4l177.2 99.3" fill="#B5D6FB" p-id="4184"></path><path d="M435.4 850.7c-1.2 0-2.3-0.3-3.4-0.9l-177.1-99.3c-3.4-1.9-4.6-6.2-2.7-9.5 1.9-3.4 6.1-4.6 9.5-2.7l177.1 99.3c3.4 1.9 4.6 6.2 2.7 9.5-1.3 2.3-3.7 3.6-6.1 3.6z" fill="#0276F7" p-id="4185"></path><path d="M258.2 694.8l177.2 99.3" fill="#B5D6FB" p-id="4186"></path><path d="M435.4 801.1c-1.2 0-2.3-0.3-3.4-0.9l-177.1-99.3c-3.4-1.9-4.6-6.2-2.7-9.5 1.9-3.4 6.1-4.6 9.5-2.7L438.8 788c3.4 1.9 4.6 6.2 2.7 9.5-1.3 2.3-3.7 3.6-6.1 3.6z" fill="#0276F7" p-id="4187"></path><path d="M258.2 645.1l177.2 99.3" fill="#B5D6FB" p-id="4188"></path><path d="M435.4 751.4c-1.2 0-2.3-0.3-3.4-0.9l-177.1-99.3c-3.4-1.9-4.6-6.2-2.7-9.5 1.9-3.4 6.1-4.6 9.5-2.7l177.1 99.3c3.4 1.9 4.6 6.2 2.7 9.5-1.3 2.3-3.7 3.6-6.1 3.6z" fill="#0276F7" p-id="4189"></path><path d="M222.7 538.9l248.2 135.5" fill="#B5D6FB" p-id="4190"></path><path d="M219.335 545.028l6.708-12.289 248.241 135.49-6.707 12.289z" fill="#0276F7" p-id="4191"></path><path d="M393 430.7l40.3 20.1" fill="#B5D6FB" p-id="4192"></path><path d="M433.3 457.8c-1.1 0-2.1-0.2-3.1-0.7L389.9 437c-3.5-1.7-4.9-5.9-3.1-9.4 1.7-3.5 6-4.9 9.4-3.1l40.3 20.1c3.5 1.7 4.9 5.9 3.1 9.4-1.3 2.4-3.8 3.8-6.3 3.8z" fill="#0276F7" p-id="4193"></path></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@@ -102,12 +102,14 @@ body .ant-pro-basicLayout {
align-items: center;
}
.button-container > button {
.button-container > button,
.button-container > span {
margin-right: 12px;
margin-bottom: 12px;
}
.button-container > button:last-child {
.button-container > button:last-child,
.button-container > span:last-child {
margin-right: 0;
}
/* 平板端 */
@@ -117,7 +119,8 @@ body .ant-pro-basicLayout {
align-items: flex-start;
align-items: left;
}
.button-container > button {
.button-container > button,
.button-container > span {
margin-right: 12px;
margin-bottom: 12px;
}
@@ -129,7 +132,8 @@ body .ant-pro-basicLayout {
align-items: flex-start;
align-items: left;
}
.button-container > button {
.button-container > button,
.button-container > span {
margin-right: 0px;
margin-bottom: 12px;
}

View File

@@ -8,8 +8,8 @@ import { parseObjLineToHump } from '@/utils/parse-utils';
* @returns object
*/
export async function listNeInfo(query: Record<string, any>) {
let totalSQL = 'select count(*) as total from ne_info where status=0 ';
let rowsSQL = 'select * from ne_info where status=0 ';
let totalSQL = 'select count(*) as total from ne_info where (status=0 or status=3)';
let rowsSQL = 'select * from ne_info where (status=0 or status=3) ';
// 系统特定顺序
const specificOrder = [
@@ -91,7 +91,7 @@ export async function getNeInfo(id: string | number) {
url: `/api/rest/databaseManagement/v1/select/omc_db/ne_info`,
method: 'get',
params: {
SQL: `select * from ne_info where status=0 and id = ${id}`,
SQL: `select * from ne_info where (status=0 or status=3) and id = ${id}`,
},
});
// 解析数据
@@ -112,7 +112,7 @@ export async function getNeInfo(id: string | number) {
export function addNeInfo(data: Record<string, any>) {
data.port = `${data.port}`;
return request({
url: `/api/rest/systemManagement/v1/elementType/${data.neType}/objectType/neInfo`,
url: `/api/rest/systemManagement/v1/elementType/${data.neType.toLowerCase()}/objectType/neInfo?sync2ne=${data.sync}`,
method: 'post',
data: data,
});
@@ -126,7 +126,7 @@ export function addNeInfo(data: Record<string, any>) {
export function updateNeInfo(data: Record<string, any>) {
data.port = `${data.port}`;
return request({
url: `/api/rest/systemManagement/v1/elementType/${data.neType}/objectType/neInfo`,
url: `/api/rest/systemManagement/v1/elementType/${data.neType}/objectType/neInfo?sync2ne=${data.sync}`,
method: 'put',
data: data,
});

View File

@@ -36,6 +36,10 @@ export async function listMain() {
const [key, value] = Object.entries(obj)[0];
const ipAddress = (value as any).ipAddress;
const systemState = (value as any).systemState;
const serialNum = (value as any).serialNum;
const version = (value as any).version;
const errCode = systemState && systemState['errorCode'];
var time = new Date();
// console.log(key, value);
@@ -50,9 +54,10 @@ export async function listMain() {
};
} else {
mergedObj = {
version: '-',
version,
refresh: parseDateToStr(time),
ipAddress: ipAddress,
ipAddress,
serialNum,
name: key.split('/').join('_'),
status: 'Abnormal',
};

29
src/api/ne/ne.ts Normal file
View File

@@ -0,0 +1,29 @@
import { request } from '@/plugins/http-fetch';
/**
* 查询网元列表
* @param query 查询参数
* @returns object
*/
export function listNe(query: Record<string, any>) {
return request({
url: '/ne/list',
method: 'get',
params: query,
timeout: 60_000,
});
}
/**
* 查询网元状态
* @param neType 网元类型
* @param neId 网元ID
* @returns object
*/
export function stateNe(neType: string, neId: string) {
return request({
url: '/ne/state',
method: 'get',
params: { neType, neId },
});
}

View File

@@ -111,9 +111,9 @@ export function batchAuth(data: Record<string, any>) {
* @param data 鉴权对象
* @returns object
*/
export function delAuth(neId: string, data: Record<string, any>) {
export function delAuth(neId: string, imsi: string) {
return request({
url: `/ne/udm/auth/${neId}/${data.imsi}`,
url: `/ne/udm/auth/${neId}/${imsi}`,
method: 'delete',
});
}

View File

@@ -91,6 +91,8 @@ export async function goldData(query: Record<string, any>) {
startTime: query.beginTime,
endTime: query.endTime,
interval: query.particle,
sortField:query.sortField,
sortOrder:query.sortOrder
},
timeout: 60_000,
});

View File

@@ -1,25 +1,12 @@
<template>
<div>
<div ref="g6Dom" :style="{ height: height, width: width }"></div>
<button @click="actions['Enable/Disable Node States'].Breathing()">
Breathing()
</button>
<button @click="actions['Enable/Disable Node States'].Scaling()">
Scaling()
</button>
<button @click="actions['Enable/Disable Edge States'].Growing()">
Growing()
</button>
<button @click="actions['Enable/Disable Edge States'].Running()">
Running()
</button>
<div ref="chartGraphG6Dom" :style="{ height: height, width: width }"></div>
</div>
</template>
<script lang="ts" setup>
import { nextTick, watch, onMounted, onBeforeUnmount, ref } from 'vue';
import { Graph, extend, Extensions } from '@antv/g6';
import { Graph } from '@antv/g6';
const props = defineProps({
/**
@@ -43,169 +30,151 @@ const props = defineProps({
},
});
const g6Dom = ref<HTMLElement | undefined>(undefined);
const ExtGraph = extend(Graph, {
layouts: {},
edges: {
'polyline-edge': Extensions.PolylineEdge,
'cubic-edge': Extensions.CubicEdge,
// 'custom-edge': Extensions.CubicEdge,
},
});
const chartGraphG6Dom = ref<HTMLElement | undefined>(undefined);
const data = {
nodes: [
// 0 基站
{
id: '0',
data: {
x: 80,
y: 150,
type: 'circle-node',
color: '#fffff',
keyShape: {
r: 24,
width: 48,
height: 48,
fill: '#9EC9FF',
stroke: '#5B8FF9',
lineWidth: 2,
},
labelShape: {
text: '基站',
position: 'bottom',
maxWidth: '200%',
offsetY: 10,
},
labelBackgroundShape: {},
iconShape: {
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
width: 24,
height: 24,
x: 50,
y: 150,
size: 48,
type: 'circle',
label: '基站',
labelCfg: {
position: 'bottom',
offset: 10,
style: {
fill: '#333',
stroke: '#fff',
lineWidth: 10,
},
},
style: {
fill: '#9EC9FF',
stroke: '#5B8FF9',
lineWidth: 2,
},
icon: {
show: true,
// 可更换为其他图片地址
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
width: 24,
height: 24,
},
},
// 1 DM
{
id: '1',
data: {
label: 'DM',
x: 600,
y: 350,
x: 450,
y: 450,
label: 'DM',
labelCfg: {
position: 'center',
},
style: {
fill: '#00b050',
stroke: '#00b050',
lineWidth: 1,
},
},
// 2 O&M
{
id: '2',
data: {
label: 'O&M',
x: 900,
y: 100,
},
x: 50,
y: 450,
label: 'O&M',
},
// 5GC控制面
// 100 EMS
{
id: '170',
data: {
label: 'NSSF',
parentId: 'combo-5gc',
x: 300,
y: 50,
},
id: '100',
label: 'EMS',
comboId: 'combo-ems',
x: 300,
y: 450,
},
{
id: '130',
data: {
label: 'AUSF',
parentId: 'combo-5gc',
x: 400,
y: 50,
},
},
{
id: '140',
data: {
label: 'UDM',
parentId: 'combo-5gc',
x: 500,
y: 50,
},
},
{
id: '120',
data: {
label: 'AMF',
parentId: 'combo-5gc',
x: 300,
y: 150,
},
},
{
id: '180',
data: {
label: 'NRF',
parentId: 'combo-5gc',
x: 400,
y: 150,
},
},
{
id: '150',
data: {
label: 'SMF',
parentId: 'combo-5gc',
x: 500,
y: 250,
},
},
{
id: '160',
data: {
label: 'PCF',
parentId: 'combo-5gc',
x: 600,
y: 250,
},
},
// 5GC用户面
// 190 UPF
{
id: '190',
data: {
label: 'UPF',
parentId: 'combo-upf',
x: 300,
y: 350,
comboId: 'combo-upf',
x: 300,
y: 350,
label: 'UPF',
labelCfg: {
position: 'center',
},
style: {
fill: '#d580ff',
stroke: '#d580ff',
lineWidth: 1,
},
},
// EP-IMS
{
id: '110',
data: {
label: 'I/S-CSCF',
parentId: 'combo-ims',
x: 800,
y: 350,
comboId: 'combo-ims',
x: 600,
y: 350,
label: 'IMS',
labelCfg: {
position: 'center',
},
style: {
fill: '#ed7d31',
stroke: '#ed7d31',
lineWidth: 1,
},
},
// 5GC控制面
{
id: '111',
data: {
label: 'P-CSCF',
parentId: 'combo-ims',
x: 800,
y: 400,
},
id: '170',
label: 'NSSF',
comboId: 'combo-5gc',
x: 300,
y: 50,
},
// O&M
{
id: '100',
data: {
label: 'EMS',
parentId: 'combo-ems',
x: 800,
y: 100,
},
id: '130',
label: 'AUSF',
comboId: 'combo-5gc',
x: 450,
y: 50,
},
{
id: '140',
label: 'UDM',
comboId: 'combo-5gc',
x: 600,
y: 50,
},
{
id: '120',
label: 'AMF',
comboId: 'combo-5gc',
x: 300,
y: 150,
},
{
id: '180',
label: 'NRF',
comboId: 'combo-5gc',
x: 450,
y: 150,
},
{
id: '150',
label: 'SMF',
comboId: 'combo-5gc',
x: 300,
y: 250,
},
{
id: '160',
label: 'PCF',
comboId: 'combo-5gc',
x: 700,
y: 250,
},
],
edges: [
@@ -213,129 +182,62 @@ const data = {
id: '0-5gc',
source: '0',
target: 'combo-5gc',
data: {
type: 'cubic-edge',
animates: {
update: [
{
// 在 selected 和 active 状态变化导致的 haloShape opacity 变化时,使 opacity 带动画地更新
fields: ['opacity'],
shapeId: 'haloShape',
states: ['selected', 'active'],
duration: 500,
},
],
},
},
},
{
id: '5gc-ems',
source: 'combo-5gc',
target: 'combo-ems',
data: {},
id: '0-upf',
source: '0',
target: 'combo-upf',
},
{
id: 'upf-ems',
id: 'upf-1',
source: 'combo-upf',
target: 'combo-ems',
data: {},
},
{
id: 'ims-ems',
source: 'combo-ims',
target: 'combo-ems',
data: {},
target: '1',
},
{
id: 'ems-2',
source: 'combo-ems',
target: '2',
data: {},
},
{
id: '170-120',
source: '170',
target: '120',
data: {},
},
{
id: '130-120',
source: '130',
target: '120',
data: {},
},
{
id: '140-120',
source: '140',
target: '120',
data: {},
},
{
id: '140-180',
source: '140',
target: '180',
data: {},
},
{
id: '120-180',
source: '120',
target: '180',
data: {
type: 'polyline-edge',
keyShape: {
endArrow: true,
routeCfg: {
/**
* 目前支持正交路由 'orth' 和地铁路由 'er'
*/
// name: 'er',
/**
* 是否开启自动避障,默认为 false
* Whether to enable automatic obstacle avoidance, default is false
*/
enableObstacleAvoidance: true,
},
/**
* 拐弯处的圆角弧度,默认为直角,值为 0
* The radius of the corner rounding, defaults to a right angle
*/
// radius: 20,
/**
* 拐弯处距离节点最小距离, 默认为 2
* Minimum distance from the node at the corner, default is 5.
*/
// offset: 0,
/**
* 控制点数组,不指定时根据 A* 算法自动生成折线。若指定了,则按照 controlPoints 指定的位置进行弯折
* An array of control points that, when not specified, automatically generates the bends according to the A* algorithm. If specified, bends are made at the position specified by controlPoints.
*/
// controlPoints: [],
},
},
},
{
id: '130-180',
source: '130',
target: '180',
data: {
type: 'polyline-edge',
},
},
{
id: '140-150',
source: '140',
target: '150',
data: {
type: 'polyline-edge',
},
},
{
id: '140-110',
source: '140',
target: '110',
data: {
type: 'polyline-edge',
},
},
{
id: '120-150',
@@ -353,56 +255,48 @@ const data = {
id: '150-160',
source: '150',
target: '160',
data: { type: 'polyline-edge' },
},
{
id: '160-120',
source: '160',
target: '120',
data: {},
},
{
id: '160-180',
source: '160',
target: '180',
data: {
keyShape: {
endArrow: true,
},
labelShape: {
text: 'asdf-arrow',
},
},
},
{
id: '160-111',
id: '160-110',
source: '160',
target: '111',
data: {},
},
{
id: '0-upf',
source: '0',
target: 'combo-upf',
data: {},
target: '110',
},
{
id: '150-190',
source: '150',
target: '190',
data: {},
},
{
id: 'upf-1',
source: 'combo-upf',
target: '1',
data: {},
},
{
id: 'upf-ims',
source: 'combo-upf',
target: 'combo-ims',
data: {},
},
{
id: 'ems-5gc',
source: 'combo-ems',
target: 'combo-5gc',
},
{
id: 'ems-upf',
source: 'combo-ems',
target: 'combo-upf',
},
{
id: 'ems-ims',
source: 'combo-ems',
target: 'combo-ims',
},
],
combos: [
@@ -415,354 +309,77 @@ const data = {
{
id: 'combo-upf',
data: {
label: '5GC用户面',
keyShape: {
opacity: 0.8,
padding: [20, 20, 20, 20],
radius: 4,
lineWidth: 1,
stroke: '#d580ff',
},
},
},
{
id: 'combo-ims',
data: {
label: 'EP-IMS用户面',
},
data: {},
},
{
id: 'combo-ems',
data: {
label: 'EMS',
},
data: {},
},
],
};
let graph: any = null;
/**初始化渲染图表 */
function initChart() {
if (!g6Dom.value) return;
graph = new ExtGraph({
container: g6Dom.value,
height: 500,
if (!chartGraphG6Dom.value) return;
console.log(chartGraphG6Dom.value.clientWidth);
console.log(chartGraphG6Dom.value.clientHeight);
graph = new Graph({
container: chartGraphG6Dom.value,
height: chartGraphG6Dom.value.clientHeight,
width: chartGraphG6Dom.value.clientWidth,
fitCenter: true,
modes: {
default: [
{
type: 'drag-node',
enableTransient: false,
updateComboStructure: false,
},
{
type: 'click-select',
itemTypes: ['node', 'edge', 'combo'],
},
{
type: 'drag-combo',
enableTransient: true,
updateComboStructure: true,
},
// 'drag-combo',
'drag-canvas',
// 'click-select',
'zoom-canvas',
] as any,
default: ['drag-canvas', 'zoom-canvas', 'drag-node'], // 允许拖拽画布、放缩画布、拖拽节点
},
// 布局 力向吸引
layout: {
type: 'force',
animated: true,
linkDistance: 280,
maxSpeed: 100,
clustering: true,
nodeClusterBy: 'parentId',
clusterNodeStrength: 80,
},
// 主题
theme: {
type: 'spec',
base: 'light',
specification: {
node: {
dataTypeField: 'parentId',
},
},
} as any,
// 插件
plugins: [
{
// 避免文本过于密集导致的视觉混乱
type: 'lod-controller',
disableLod: true,
},
] as any,
// 全局节点 矩形
node: model => {
const { id, data } = model;
return {
id,
data: {
type: 'rect-node',
keyShape: {
width: 80,
height: 40,
radius: 8,
},
labelShape: {
position: 'center',
text: data.label,
},
animates: {
update: [
{
fields: ['opacity'],
shapeId: 'haloShape',
states: ['breathing'],
iterations: Infinity,
direction: 'alternate',
duration: 500,
},
{
fields: ['lineWidth'],
shapeId: 'keyShape',
states: ['breathing'],
iterations: Infinity,
direction: 'alternate',
duration: 500,
},
{
fields: ['height', 'width'],
shapeId: 'keyShape',
states: ['scaling'],
iterations: Infinity,
direction: 'alternate',
duration: 500,
},
],
},
...data,
},
};
defaultNode: {
type: 'rect',
size: [80, 40],
style: {
fill: '#fff',
lineWidth: 1,
radius: 8,
},
labelCfg: {},
},
// 全局边 三次贝塞尔曲线
edge: model => {
const { id, source, target, data } = model;
return {
id,
source,
target,
data: {
type: 'cubic-edge',
animates: {
update: [
{
fields: ['lineDash'],
shapeId: 'keyShape',
states: ['growing', 'running'],
iterations: Infinity,
duration: 2000,
},
{
fields: ['offsetDistance'],
shapeId: 'buShape',
states: ['circleRunning'],
iterations: Infinity,
duration: 2000,
},
],
},
...data,
},
};
defaultEdge: {
type: 'polyline',
style: {
offset: 20, // 拐弯处距离节点最小距离
radius: 4, // 拐弯处的圆角弧度,若不设置则为直角
lineWidth: 1,
stroke: '#87e8de',
},
},
// 全局框节点 矩形
combo: model => {
const { id, data } = model;
return {
id,
data: {
type: 'rect-combo',
keyShape: {
opacity: 0.8,
padding: [20, 20, 20, 20],
radius: 8,
},
labelShape: {
text: data.label,
offsetY: 8,
},
labelBackgroundShape: {},
buildIn: [
{
fields: ['opacity'],
duration: 500,
delay: 500 + Math.random() * 500,
},
],
buildOut: [
{
fields: ['opacity'],
duration: 200,
},
],
animates: {
update: [
{
fields: ['width', 'height', 'x', 'y'],
shapeId: 'keyShape',
},
],
},
...data,
},
};
},
// 节点状态
nodeState: {
breathing: {
haloShape: {
opacity: 0.25,
lineWidth: 20,
visible: true,
},
keyShape: {
radius: 4,
},
},
scaling: {
keyShape: {
width: 100,
height: 60,
},
},
} as any,
edgeState: {
growing: {
keyShape: {
lineWidth: 2,
lineDash: ['100%', 0],
},
},
running: {
keyShape: {
lineWidth: 2,
lineDash: [2, 2],
// TODO: lineDashOffset
},
defaultCombo: {
type: 'rect', // Combo 类型
size: [40, 40],
// ... 其他配置
style: {
lineWidth: 1,
},
},
data,
});
graph.on('node:click', (e: any) => {
const s = graph.getItemState(e.itemId);
console.log(s);
// graph.updateData('node', {
// id: e.itemId,
// data: {
// cluster: Math.random(),
// keyShape: {
// r: 32 + Math.random() * 10 - 5,
// lineWidth: 6 + Math.random() * 6 - 3,
// stroke: '#000',
// },
// labelShape: {
// fontWeight: 700,
// },
// },
// });
});
graph.on('node:pointerenter', (e: any) => {
const { itemId } = e;
if (graph.getItemState(itemId, 'breathing')) {
graph.setItemState(itemId, 'breathing', false);
} else {
graph.setItemState(itemId, 'scaling', false);
graph.setItemState(itemId, 'breathing', true);
}
// graph.updateData('node', {
// id: itemId,
// data: {
// label: `after been hovered ${itemId}`,
// labelShape: {
// fill: '#0f0',
// },
// },
// });
});
graph.on('node:pointerleave', (e: any) => {
const { itemId } = e;
if (graph.getItemState(itemId, 'breathing')) {
graph.setItemState(itemId, 'breathing', false);
} else {
graph.setItemState(itemId, 'scaling', false);
graph.setItemState(itemId, 'breathing', true);
}
// graph.updateData('node', {
// id: itemId,
// data: {
// label: 'label before been hovered',
// labelShape: {
// fill: '#000',
// },
// },
// });
});
graph.data(data); // 加载数据
graph.render(); // 渲染
}
const actions = {
'Enable/Disable Node States': {
Breathing: () => {
graph.getAllNodesData().forEach((node: any) => {
if (graph.getItemState(node.id, 'breathing')) {
graph.setItemState(node.id, 'breathing', false);
} else {
graph.setItemState(node.id, 'scaling', false);
graph.setItemState(node.id, 'breathing', true);
}
});
},
Scaling: () => {
graph.getAllNodesData().forEach((node: any) => {
if (graph.getItemState(node.id, 'scaling')) {
graph.setItemState(node.id, 'scaling', false);
} else {
graph.setItemState(node.id, 'breathing', false);
graph.setItemState(node.id, 'scaling', true);
}
});
},
},
'Enable/Disable Edge States': {
Growing: () => {
graph.getAllEdgesData().forEach((edge: any) => {
if (graph.getItemState(edge.id, 'growing')) {
graph.setItemState(edge.id, 'growing', false);
} else {
graph.setItemState(edge.id, 'running', false);
graph.setItemState(edge.id, 'growing', true);
}
});
},
Running: () => {
graph.getAllEdgesData().forEach((edge: any) => {
if (graph.getItemState(edge.id, 'running')) {
graph.setItemState(edge.id, 'running', false);
} else {
graph.setItemState(edge.id, 'growing', false);
graph.setItemState(edge.id, 'running', true);
}
});
},
},
};
// watch(
// () => props.option,
// val => {
// if (val) {
// nextTick(() => {
// initChart();
// });
// }
// }
// );
onMounted(() => {
nextTick(() => {
initChart();

View File

@@ -31,11 +31,12 @@ export default function useLocale() {
},
];
// 挂载前根据默认语言设置一次
// 挂载前根据默认语言设置一次
onBeforeMount(() => {
const localI18n = localGet(CACHE_LOCAL_I18N);
if (localI18n) {
i18n.locale.value = localI18n;
document.documentElement.lang = localI18n.replace('_', '-');
}
});

View File

@@ -8,7 +8,7 @@ export default {
title: 'Core Network Management Platform',
desc: 'Core Network Management Platform',
loading: 'Please wait...',
ipnutPlease: 'Please input',
inputPlease: 'Please input',
selectPlease: 'please select',
tipTitle: 'Prompt',
msgSuccess: 'Success {msg}',
@@ -49,7 +49,7 @@ export default {
hide: 'Hidden',
open: 'Open',
shut: 'Shut',
launch: 'Launch',
launch: 'Expand',
fold: 'Fold',
},
rowId: 'ID',
@@ -170,11 +170,11 @@ export default {
settings: "Settings",
},
tabs: {
reload: "Refresh current",
reload: "Refresh Current Tab",
more: "More Options",
closeCurrent: "Close Current",
closeOther: "Close Other",
closeAll: "Close All",
closeCurrent: "Close Current Tab",
closeOther: "Close Other Tabs",
closeAll: "Close All Tabs",
}
},
@@ -354,6 +354,9 @@ export default {
server:'Server File',
local:'Local File',
fileSelect:'Please select the current import file',
sync:'Synchronize to NE',
open:'Open',
close:'Close',
},
backupManage: {
setBackupTask: 'Set automatic backup time',
@@ -402,6 +405,7 @@ export default {
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',
@@ -464,7 +468,7 @@ export default {
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: "commence",
arrayMore: "Expand",
},
},
neUser: {
@@ -474,6 +478,8 @@ export default {
neType: 'UDM Object',
export: 'Export',
exportConfirm: 'Are you sure to export all authentication user data?',
checkExport : 'Check Export',
checkExportConfirm: 'Confirm exporting the checked authenticated user data?',
import: 'Import',
loadDataConfirm: 'Are you sure you want to reload the data?',
loadData: 'Load Data',
@@ -483,6 +489,7 @@ export default {
batchDelText: 'Batch Delete',
numAdd: 'Number of releases',
numDel: 'Number of deleted',
checkDel: 'Check Delete',
imsiTip: 'IMSI=MCC+MNC+MSIN',
imsiTip1: 'MCC=Mobile Country Code, consisting of three digits.',
imsiTip2: 'MNC = Mobile Network Number, consisting of two digits',
@@ -491,19 +498,22 @@ export default {
algoIndexTip: 'Algorithm index, between 0 and 15',
kiTip: 'User signing key information, the maximum length of 32',
opcTip: 'The authentication key, OPC, is calculated from Ki and OP, OP is the root key of the operator, ki is the authentication key, and the maximum length is 32.',
delSure:'Are you sure you want to delete the user with IMSI number: {imsi}?',
delSure:'Are you sure you want to delete the user with IMSI number: {imsi} ?',
},
sub: {
subInfo:' Subscription Info',
neType: 'UDM Object',
export: 'Export',
exportConfirm: 'Are you sure to export all signed user data?',
checkExport : 'Check Export',
checkExportConfirm: 'Are you sure to export the data of the checked subscribers?',
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!!!!',
numAdd: 'Number of releases',
numDel: 'Number of deleted',
checkDel: 'Check Delete',
batchAddText: 'Batch Add',
batchDelText: 'Batch Delete',
enable:'Enable',
@@ -545,6 +555,7 @@ export default {
imsiTip2: 'MNC = Mobile Network Number, consisting of two digits',
imsiTip3: 'MSIN = Mobile Subscriber Identification Number, consisting of 10 equal digits.',
delSure:'Are you sure you want to delete the user with IMSI number: {imsi}?',
uploadFileOk: 'File Upload Successful',
uploadFileErr: 'File Upload Failed',
},
base5G: {
@@ -578,11 +589,11 @@ export default {
viewTask:'View Task',
editTask:'Edit Task',
addTask:'Add Task',
stopTask:'Stop Task',
stopTask:'Pending',
errorTaskInfo: 'Failed to obtain task information',
granulOptionPlease:'Please select the measurement granularity',
letupSure:'Confirm activation of task with number [{id}]?',
stopSure:'Confirm the pending task with number [{row.id}]?',
stopSure:'Confirm the pending task with number [{id}]?',
letUpWarning:'Prohibit activation of activated tasks',
stopWarning: 'Prohibit suspending inactive measurement tasks',
weekPlan:'Weekly Plan',
@@ -629,7 +640,7 @@ export default {
value:'Value',
startTime:'Start Time',
endTime:'End Time',
particle: 'Particle Ssize',
particle: 'Granularity',
timeFrame: 'Time Range',
nullTip:'There are no statistical data within this time range',
kpiTitle:'KPI Statistics Chart',
@@ -747,7 +758,7 @@ export default {
alarmTitle:'Title',
clearUser:'Clear User',
clearType:'Clear Type',
ackState: 'Alarm Status',
ackState: 'Ack Status',
ackUser:'Ack User',
alarmSeq:'Sequence Number',
objectName:'Object Name',
@@ -1302,6 +1313,7 @@ export default {
dictData:'Dictionary Data',
reload:'Refresh Cache',
mark:'Dictionary Description',
colorSelect:'Color Picker',
},
dictData: {
dictType: "Dictionary name",

View File

@@ -8,7 +8,7 @@ export default {
title: '核心网管理平台',
desc: '核心网管理平台',
loading: '请稍等...',
ipnutPlease: '请输入',
inputPlease: '请输入',
selectPlease: '请选择',
tipTitle: '提示',
msgSuccess: '{msg} 成功',
@@ -170,11 +170,11 @@ export default {
settings: "个人设置",
},
tabs: {
reload: "刷新当前",
reload: "刷新当前标签",
more: "更多选项",
closeCurrent: "关闭当前",
closeOther: "关闭其他",
closeAll: "关闭全部",
closeCurrent: "关闭当前标签",
closeOther: "关闭其他标签",
closeAll: "关闭全部标签",
}
},
@@ -354,6 +354,9 @@ export default {
server:'服务器文件',
local:'本地文件',
fileSelect:'请选择当前导入文件',
sync:'同步到网元',
open:'开',
close:'关',
},
backupManage: {
setBackupTask: '设置自动备份时间',
@@ -402,6 +405,7 @@ export default {
createTime:'创建时间',
onlyAble:'只支持上传文件格式 {fileText}',
nullData:'暂无网元列表数据',
nullVersion:'当前网元无可回退版本',
},
license: {
neTypePlease: '选择网元类型',
@@ -462,8 +466,8 @@ export default {
requireEnum: "{display} 不是合理的枚举值",
requireBool: "{display} 不是合理的布尔类型的值",
editOkTip: "确认更新该{num}属性值吗?",
updateItemTip: "确认更新Index为 【{num}】 的数据项?",
delItemTip: "确认删除Index为 【{num}】 的数据项?",
updateItemTip: "确认更新Index为 【{num}】 的数据项",
delItemTip: "确认删除Index为 【{num}】 的数据项",
arrayMore: "展开",
},
},
@@ -473,9 +477,11 @@ export default {
neTypePlease: '查询网元类型',
neType: 'UDM网元对象',
export: '导出',
exportConfirm: '确认导出全部鉴权用户数据吗?',
exportConfirm: '确认导出全部鉴权用户数据吗',
checkExport : '勾选导出',
checkExportConfirm: '确认导出已勾选的鉴权用户数据吗?',
import: '导入',
loadDataConfirm: '确认要重新加载数据吗?',
loadDataConfirm: '确认要重新加载数据吗',
loadData: '加载数据',
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新。加载结束后可点击重置刷新数据列表,请勿重复点击获取更新!!!',
startIMSI: '起始IMSI',
@@ -483,6 +489,7 @@ export default {
batchDelText: '批量删除',
numAdd: '放号个数',
numDel:'删除个数',
checkDel:'勾选删除',
imsiTip: 'IMSI=MCC+MNC+MSIN',
imsiTip1: 'MCC=移动国家号码, 由三位数字组成',
imsiTip2: 'MNC=移动网络号,由两位数字组成',
@@ -497,13 +504,16 @@ export default {
subInfo:'签约信息',
neType: 'UDM网元类型',
export: '导出',
exportConfirm: '确认导出全部签约用户数据吗?',
exportConfirm: '确认导出全部签约用户数据吗',
checkExport : '勾选导出',
checkExportConfirm: '确认导出已勾选的签约用户数据吗?',
import: '导入',
loadDataConfirm: '确认要重新加载数据吗?',
loadDataConfirm: '确认要重新加载数据吗',
loadData: '加载数据',
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新。加载结束后可点击重置刷新数据列表,请勿重复点击获取更新!!!',
numAdd: '放号个数',
numDel: '删除个数',
checkDel:'勾选删除',
batchAddText: '批量新增',
batchDelText: '批量删除',
enable:'开启',
@@ -545,6 +555,7 @@ export default {
imsiTip2: 'MNC=移动网络号,由两位数字组成',
imsiTip3: 'MSIN=移动客户识别码采用等长10位数字构成',
delSure:'确认删除IMSI编号为: {imsi} 的用户吗?',
uploadFileOk: '文件上传成功',
uploadFileErr: '文件上传失败',
},
base5G: {
@@ -1302,6 +1313,7 @@ export default {
dictData:'字典数据',
reload:'刷新缓存',
mark:'字典说明',
colorSelect:'取色器',
},
dictData: {
dictType: "字典名称",

View File

@@ -14,6 +14,11 @@ type AppStore = {
/**应用版本 */
appVersion: string;
/**服务版本 */
version: string;
buildTime: string;
// 序列号
serialNum: string;
/**应用版权声明 */
copyright: string;
/**LOGO显示类型 */
@@ -41,6 +46,9 @@ const useAppStore = defineStore('app', {
appCode: import.meta.env.VITE_APP_CODE,
appVersion: import.meta.env.VITE_APP_VERSION,
version: `-`,
buildTime: `-`,
serialNum: `-`,
copyright: `Copyright ©2023 For ${import.meta.env.VITE_APP_NAME}`,
logoType: 'icon',
filePathIcon: '',
@@ -70,6 +78,9 @@ const useAppStore = defineStore('app', {
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.serialNum = res.data.serialNum;
this.appName = res.data.title;
this.copyright = res.data.copyright;
this.logoType = res.data.logoType;

View File

@@ -1,4 +1,4 @@
import { read, utils, write } from 'xlsx';
import { JSON2SheetOpts, read, utils, write } from 'xlsx';
// 静态资源路径
const baseUrl = import.meta.env.VITE_HISTORY_BASE_URL;
@@ -19,7 +19,10 @@ export const xlsxUrl = `${
* console.log(res)
* });
*/
export async function readLoalXlsx(lang:string,id: string): Promise<Record<string, any>[]> {
export async function readLoalXlsx(
lang: string,
id: string
): Promise<Record<string, any>[]> {
let result = await fetch(`${xlsxUrl}/${lang}/${id}.xlsx`);
let fileBuffer = await result.arrayBuffer();
// 判断是否xlsx文件
@@ -62,11 +65,17 @@ export async function readSheet(
* );
*
*/
export async function writeSheet(data: any[], sheetName: string) {
export async function writeSheet(
data: any[],
sheetName: string,
opts?: JSON2SheetOpts
) {
if (data.length === 0) {
return new Blob([], { type: 'application/octet-stream' });
}
const workSheet = utils.json_to_sheet(data);
const workSheet = utils.json_to_sheet(data, opts);
// 设置列宽度,单位厘米
workSheet['!cols'] = Object.keys(data[0]).map(() => {
return { wch: 20 };

View File

@@ -28,7 +28,8 @@ export const regExpUserName = /^[a-zA-Z][a-z0-9A-Z]{4,}$/;
*
* 密码至少包含大小写字母、数字、特殊符号且不少于6位
*/
export const regExpPasswd = /^(?![A-Za-z0-9]+$)(?![a-z0-9\W]+$)(?![A-Za-z\W]+$)(?![A-Z0-9\W]+$)[a-zA-Z0-9\W]{6,}$/;
export const regExpPasswd =
/^(?![A-Za-z0-9]+$)(?![a-z0-9\W]+$)(?![A-Za-z\W]+$)(?![A-Z0-9\W]+$)[a-zA-Z0-9\W]{6,}$/;
/**
* 有效手机号格式
@@ -63,6 +64,44 @@ export function validHttp(link: string): boolean {
return regExpHttp.test(link);
}
/**
* 判断是否有效URL地址
* @param str 网络链接
* @returns true | false
*/
export function validURL(str: string) {
if (
str === '' ||
str.length >= 2083 ||
str.length <= 3 ||
str.startsWith('.')
) {
return false;
}
let strTemp = str;
if (str.includes(':') && !str.includes('://')) {
// support no indicated urlscheme but with colon for port number
// http:// is appended so url.Parse will succeed, strTemp used so it does not impact rxURL.MatchString
strTemp = 'http://' + str;
}
debugger;
try {
new URL(strTemp);
} catch (error) {
return false;
}
const u = new URL(strTemp);
if (u.host.startsWith('.')) {
return false;
}
if (u.host === '' && u.pathname !== '' && !u.pathname.includes('.')) {
return false;
}
// 正则表达式模式(rxURL)未提供,无法进行具体判断
return true;
}
/**
* 判断是否为有效手机号格式
* @param mobile 手机号字符串

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { reactive, ref, onMounted, watch, toRaw, nextTick } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { message } from 'ant-design-vue/lib';
import { Modal, message } from 'ant-design-vue/lib';
import useI18n from '@/hooks/useI18n';
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
@@ -13,7 +13,7 @@ import {
addParamConfigInfo,
} from '@/api/configManage/configParam';
import useNeInfoStore from '@/store/modules/neinfo';
import { regExpIPv4, regExpIPv6 } from '@/utils/regular-utils';
import { regExpIPv4, regExpIPv6, validURL } from '@/utils/regular-utils';
import { SizeType } from 'ant-design-vue/lib/config-provider';
import { DataNode } from 'ant-design-vue/lib/tree';
const neInfoStore = useNeInfoStore();
@@ -403,27 +403,37 @@ function arrayEditOk(from: Record<string, any>) {
/**多列表删除单行 */
function arrayDelete(rowIndex: Record<string, any>) {
const index = rowIndex.value;
delParamConfigInfo({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
topTag: treeState.selectNode.topTag,
loc: index,
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.configManage.configParamForm.delItemOk', {
num: `${treeState.selectNode.topDisplay} Index-${index}`,
}),
duration: 2,
const title = `${treeState.selectNode.topDisplay} Index-${index}`;
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.configManage.configParamForm.delItemTip', {
num: title,
}),
onOk() {
delParamConfigInfo({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
topTag: treeState.selectNode.topTag,
loc: index,
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.configManage.configParamForm.delItemOk', {
num: title,
}),
duration: 2,
});
arrayEditClose();
fnActiveConfigNode('#');
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
});
arrayEditClose();
fnActiveConfigNode('#');
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
},
});
}
@@ -721,27 +731,37 @@ function arrayChildEditOk(from: Record<string, any>) {
function arrayChildDelete(rowIndex: Record<string, any>) {
const index = rowIndex.value;
const loc = `${arrayChildState.loc}/${index}`;
delParamConfigInfo({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
topTag: treeState.selectNode.topTag,
loc,
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.configManage.configParamForm.delItemOk', {
num: `${arrayChildState.title} Index-${index}`,
}),
duration: 2,
const title = `${arrayChildState.title} Index-${index}`;
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.configManage.configParamForm.delItemTip', {
num: title,
}),
onOk() {
delParamConfigInfo({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
topTag: treeState.selectNode.topTag,
loc,
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.configManage.configParamForm.delItemOk', {
num: title,
}),
duration: 2,
});
arrayEditClose();
fnActiveConfigNode('#');
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
});
arrayEditClose();
fnActiveConfigNode('#');
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
},
});
}
@@ -837,6 +857,8 @@ function arrayEditInit(data: Record<string, any>, dataRule: any) {
if (!item) {
continue;
}
// 可选的
row.optional = 'true';
// 根据规则类型转值
if (['enum', 'int'].includes(row.type)) {
row.value = Number(item.value);
@@ -870,6 +892,8 @@ function arrayAddInit(data: any[], dataRule: any) {
row.value = [];
continue;
}
// 可选的
row.optional = 'true';
// index值
if (row.name === 'index') {
let newIndex = dataLastIndex !== 0 ? dataLastIndex : parseInt(row.value);
@@ -909,7 +933,6 @@ function ruleVerification(row: Record<string, any>): (string | boolean)[] {
if (row.optional === 'true' && !value) {
return result;
}
switch (type) {
case 'int':
if (filter && filter.indexOf('~') !== -1) {
@@ -979,6 +1002,7 @@ function ruleVerification(row: Record<string, any>): (string | boolean)[] {
}
break;
case 'string':
// 字符串长度判断
if (filter && filter.indexOf('~') !== -1) {
try {
const filterArr = filter.split('~');
@@ -997,6 +1021,21 @@ function ruleVerification(row: Record<string, any>): (string | boolean)[] {
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':

View File

@@ -221,6 +221,7 @@ let modalState: ModalStateType = reactive({
pvFlag: '',
rmUid: '',
vendorName: '',
sync: true,
},
importFrom: {
neId: '',
@@ -453,26 +454,36 @@ function fnRecordDelete(row: Record<string, any>) {
title: t('common.tipTitle'),
content: t('views.configManage.neManage.delSure', { msg: row.neName }),
onOk() {
tableState.loading = true;
const key = 'delNotice';
message.loading({ content: t('common.loading'), key });
delNeInfo(row).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', { msg: t('common.deleteText') }),
key,
duration: 2,
});
fnGetList();
// 刷新缓存的网元信息
useNeInfoStore().fnRefreshNelist();
} else {
message.error({
content: `${res.msg}`,
key: key,
duration: 2,
});
}
});
delNeInfo(row)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
if (res.data.data && res.data.data.affectedRows) {
message.success({
content: t('common.msgSuccess', {
msg: t('common.deleteText'),
}),
key,
duration: 2,
});
tableState.loading = false;
fnGetList();
// 刷新缓存的网元信息
useNeInfoStore().fnRefreshNelist();
}
} else {
message.error({
content: `${res.msg}`,
key: key,
duration: 2,
});
}
})
.finally(() => {
tableState.loading = false;
});
},
});
}
@@ -873,7 +884,7 @@ onMounted(() => {
<template #icon><UndoOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip>
<a-tooltip placement="left">
<template #title>{{ t('common.moreText') }}</template>
<a-dropdown
placement="bottomRight"
@@ -902,7 +913,9 @@ onMounted(() => {
</a-menu-item>
<a-menu-item
key="reload"
v-if="!['OMC', 'PCF', 'IMS', 'MME'].includes(record.neType)"
v-if="
!['OMC', 'PCF', 'IMS', 'MME'].includes(record.neType)
"
>
<SyncOutlined />
{{ t('views.configManage.neManage.reload') }}
@@ -922,7 +935,7 @@ onMounted(() => {
</a-card>
<!-- 新增框或修改框 -->
<a-modal
<DraggableModal
width="800px"
:keyboard="false"
:mask-closable="false"
@@ -932,7 +945,12 @@ onMounted(() => {
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form name="modalStateFrom" layout="horizontal">
<a-form
name="modalStateFrom"
layout="horizontal"
:label-col="{ span: 6 }"
:labelWrap="true"
>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
@@ -1098,9 +1116,21 @@ onMounted(() => {
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.configManage.neManage.sync')"
name="province"
>
<a-switch
v-model:checked="modalState.from.sync"
:checked-children="t('views.configManage.neManage.open')"
:un-checked-children="t('views.configManage.neManage.close')"
/>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-modal>
</DraggableModal>
<!-- 导入框 -->
<a-modal

View File

@@ -78,41 +78,38 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title:t('views.configManage.softwareManage.neType'),
title: t('views.configManage.softwareManage.neType'),
dataIndex: 'neType',
align: 'center',
width: 2,
width: 100,
},
{
title: t('views.configManage.neManage.neId'),
dataIndex: 'neId',
align: 'center',
width: 2,
width: 200,
},
{
title: t('views.configManage.softwareManage.versions'),
dataIndex: 'version',
align: 'center',
width: 2,
},
{
title: t('views.configManage.softwareManage.upVersions'),
dataIndex: 'preVersion',
align: 'center',
width: 2,
},
{
title:t('views.configManage.softwareManage.backVersions'),
title: t('views.configManage.softwareManage.backVersions'),
dataIndex: 'newVersion',
align: 'center',
width: 2,
},
{
title: t('views.configManage.softwareManage.status'),
dataIndex: 'status',
key: 'status',
align: 'center',
width: 2,
width: 100,
},
{
title: t('views.configManage.softwareManage.letUpTime'),
@@ -122,7 +119,7 @@ let tableColumns: ColumnsType = [
if (!opt.value) return '';
return parseDateToStr(opt.value);
},
width: 2,
width: 200,
},
];
@@ -158,7 +155,7 @@ let tablePagination = reactive({
function fnGetList(pageNum?: number) {
if (tableState.loading) return;
tableState.loading = true;
if(pageNum){
if (pageNum) {
queryParams.pageNum = pageNum;
}
if (!queryRangePicker.value) {
@@ -197,7 +194,8 @@ watch(
<template>
<a-modal
width="800px"
width="100%"
wrap-class-name="full-modal"
:title="props.title"
:visible="props.visible"
:keyboard="false"
@@ -206,13 +204,9 @@ watch(
:footer="null"
>
<!-- 表格搜索栏 -->
<a-form
:model="queryParams"
name="queryParams"
layout="horizontal"
>
<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="6" :xs="24">
<a-form-item
:label="t('views.configManage.softwareManage.neType')"
name="neType"
@@ -225,8 +219,11 @@ watch(
/>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.configManage.softwareManage.createTime')" name="queryRangePicker">
<a-col :lg="6" :md="6" :xs="24">
<a-form-item
:label="t('views.configManage.softwareManage.createTime')"
name="queryRangePicker"
>
<a-range-picker
v-model:value="queryRangePicker"
allow-clear
@@ -236,7 +233,7 @@ watch(
></a-range-picker>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-col :lg="6" :md="6" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList(1)">
@@ -260,7 +257,7 @@ watch(
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:scroll="{ x: 1200, y: 400 }"
:scroll="{ x: true }"
:pagination="tablePagination"
>
</a-table>
@@ -272,3 +269,22 @@ 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

@@ -8,7 +8,7 @@ import { ColumnsType } from 'ant-design-vue/lib/table';
import { parseDateToStr } from '@/utils/date-utils';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import SoftwareHistory from './components/software-history.vue';
import { listNeVersion } from '@/api/configManage/softwareManage';
import {
listNeSoftware,
delNeSoftware,
@@ -64,8 +64,8 @@ type TabeStateType = {
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
size: 'middle',
seached: true,
size: 'small',
seached: false,
data: [],
selectedRowKeys: [],
});
@@ -154,14 +154,20 @@ type FileStateType = {
visible: boolean;
/**框类型 */
visibleType: string;
/**回退框是否显示 */
visibleByBack: boolean;
/**标题 */
title: string;
/**提示内容 */
content: string;
/**OK按钮是否禁用 */
okDisable: boolean;
/**网元参数 */
neOtions: Record<string, any>[];
/**表单数据 */
from: Record<string, any>;
/**回退表单数据 */
backFrom: Record<string, any>;
/**确定按钮 loading */
confirmLoading: boolean;
};
@@ -170,12 +176,20 @@ type FileStateType = {
let fileModalState: FileStateType = reactive({
visible: false,
visibleType: 'send',
visibleByBack: false,
title: '下发激活回退',
content: '',
okDisable: false,
neOtions: [],
from: {
neId: undefined,
},
backFrom: {
ne: undefined,
neType: undefined,
neId: undefined,
version: '',
},
confirmLoading: false,
});
@@ -192,6 +206,19 @@ const fileModalStateFrom = Form.useForm(
})
);
/**对话框内表单属性和校验规则 */
const fileModalStateBackFrom = Form.useForm(
fileModalState.backFrom,
reactive({
ne: [
{
required: true,
message: t('views.configManage.softwareManage.neIdPlease'),
},
],
})
);
/**
* 文件对话框弹出显示为 下发或激活
*/
@@ -217,13 +244,7 @@ function fnFileModalVisible(type: string | number, row: Record<string, any>) {
fileName: row.fileName,
});
}
if (type === 'back') {
fileModalState.title = t('views.configManage.softwareManage.backTitle');
fileModalState.content = t(
'views.configManage.softwareManage.backContent',
{ fileName: row.fileName }
);
}
if (!fileModalState.content) {
return;
}
@@ -263,9 +284,6 @@ function fnFileModalOk() {
if (type === 'run') {
fnType = runNeSoftware(from);
}
if (type === 'back') {
fnType = backNeSoftware(from);
}
if (fnType === null) {
return;
}
@@ -396,6 +414,7 @@ type ModalStateType = {
visibleByEdit: boolean;
/**网元版本历史框是否显示 */
visibleByHistory: boolean;
/**标题 */
title: string;
/**表单数据 */
@@ -408,6 +427,7 @@ type ModalStateType = {
let modalState: ModalStateType = reactive({
visibleByEdit: false,
visibleByHistory: false,
visibleByBack: false,
title: '上传更新',
from: {
neType: undefined,
@@ -512,8 +532,10 @@ function fnModalOk() {
*/
function fnModalCancel() {
modalState.visibleByEdit = false;
fileModalState.visibleByBack = false;
modalState.visibleByHistory = false;
modalStateFrom.resetFields();
fileModalStateBackFrom.resetFields();
}
/**
@@ -523,6 +545,92 @@ function fnModalVisibleByHistory() {
modalState.visibleByHistory = true;
}
/**
* 对话框弹出显示为 回退框
*/
function fnModalVisibleByBack() {
fileModalState.visibleByBack = true;
//fileModalStateBackFrom.resetFields();
fileModalState.title = t('views.configManage.softwareManage.backTitle');
fileModalState.content = t('views.configManage.softwareManage.neIdPlease');
if (!fileModalState.content) {
return;
}
}
/**回退网元类型选择对应修改 */
function fnNeChange(_: any, item: any) {
fileModalState.backFrom.neType = item[1].neType;
fileModalState.backFrom.neId = item[1].neId;
let queryData: any = fileModalState.backFrom;
queryData.status = 'Active';
queryData.pageNum = 1;
queryData.pageSize = 20;
listNeVersion(toRaw(queryData)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
if (
!res.total ||
!res.rows[0].preVersion ||
res.rows[0].preVersion === '-'
) {
fileModalState.okDisable = true;
fileModalState.content = t(
'views.configManage.softwareManage.nullVersion'
);
tableState.loading = false;
return;
}
fileModalState.content = t(
'views.configManage.softwareManage.backContent',
{ fileName: res.rows[0].preVersion }
);
fileModalState.backFrom.version = res.rows[0].preVersion;
fileModalState.okDisable = false;
}
tableState.loading = false;
});
}
/**
* 回退对话框弹出确认执行函数
*/
function fnBackModalOk() {
fileModalStateBackFrom
.validate()
.then(e => {
const from = toRaw(fileModalState.backFrom);
// 发送请求
fileModalState.confirmLoading = true;
const hide = message.loading({ content: t('common.loading') });
backNeSoftware(from)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', { msg: fileModalState.title }),
duration: 3,
});
fnFileModalCancel();
} else {
message.error({
content: `${fileModalState.title} ${res.msg}`,
duration: 3,
});
}
fileModalState.visibleByBack = false;
fileModalStateBackFrom.resetFields();
})
.finally(() => {
hide();
fileModalState.visibleByBack = false;
fileModalState.confirmLoading = false;
});
})
.catch(e => {
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
});
}
/**上传前检查或转换压缩 */
function fnBeforeUploadFile(file: FileType) {
if (modalState.confirmLoading) return false;
@@ -537,10 +645,14 @@ function fnBeforeUploadFile(file: FileType) {
);
return false;
}
// 根据给定的软件名取版本号 ims-r2.2312.8_u18.deb
const nameArr = fileName.split('.')
if(nameArr.length > 3) {
modalState.from.version = nameArr[1]
// 根据给定的软件名取版本号 ims-r2.2312.x-ub22.deb
const matches = fileName.match(/([0-9.]+[0-9x]+)/);
if (matches) {
modalState.from.version = matches[0];
}
const neTypeIndex = fileName.indexOf('-');
if (neTypeIndex !== -1) {
modalState.from.neType = fileName.substring(0, neTypeIndex).toUpperCase();
}
return true;
}
@@ -653,6 +765,10 @@ onMounted(() => {
<template #icon><UploadOutlined /></template>
{{ t('common.uploadText') }}
</a-button>
<a-button type="default" @click.prevent="fnModalVisibleByBack()">
<template #icon> <UndoOutlined /></template>
{{ t('views.configManage.softwareManage.backBtn') }}
</a-button>
<a-button type="dashed" @click.prevent="fnModalVisibleByHistory()">
<template #icon><HistoryOutlined /></template>
{{ t('views.configManage.softwareManage.historyBtn') }}
@@ -664,11 +780,15 @@ onMounted(() => {
<template #extra>
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<template #title>{{
tableState.seached
? t('common.switch.show')
: t('common.switch.hide')
}}</template>
<a-switch
v-model:checked="tableState.seached"
:checked-children="t('common.switch.show')"
:un-checked-children="t('common.switch.hide')"
:checked-children="t('common.searchBarText')"
:un-checked-children="t('common.searchBarText')"
size="small"
/>
</a-tooltip>
@@ -741,7 +861,7 @@ onMounted(() => {
<template #icon><ThunderboltOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip>
<a-tooltip placement="left">
<template #title>{{ t('common.moreText') }}</template>
<a-dropdown
placement="bottomRight"
@@ -762,10 +882,6 @@ onMounted(() => {
<DeleteOutlined />
{{ t('common.deleteText') }}
</a-menu-item>
<a-menu-item key="back">
<UndoOutlined />
{{ t('views.configManage.softwareManage.backBtn') }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
@@ -777,7 +893,7 @@ onMounted(() => {
</a-card>
<!-- 上传框 -->
<a-modal
<DraggableModal
width="800px"
:keyboard="false"
:mask-closable="false"
@@ -870,7 +986,7 @@ onMounted(() => {
</a-upload>
</a-form-item>
</a-form>
</a-modal>
</DraggableModal>
<!-- 上传激活历史 -->
<SoftwareHistory
@@ -879,7 +995,7 @@ onMounted(() => {
@cancel="fnModalCancel"
/>
<!-- 文件框 下发激活回退 -->
<!-- 文件框 下发激活 -->
<a-modal
width="600px"
:keyboard="false"
@@ -910,6 +1026,41 @@ onMounted(() => {
</a-form-item>
</a-form>
</a-modal>
<!-- 回退框 -->
<a-modal
width="800px"
:keyboard="false"
:mask-closable="false"
:visible="fileModalState.visibleByBack"
:title="fileModalState.title"
:confirm-loading="fileModalState.confirmLoading"
@ok="fnBackModalOk"
:ok-button-props="{ disabled: fileModalState.okDisable }"
@cancel="fnModalCancel"
>
<a-form name="fileModalState" layout="horizontal">
<a-form-item name="content">
<QuestionCircleOutlined class="file-model__icon" />
<span class="file-model__tip">
{{ fileModalState.content }}
</span>
</a-form-item>
<a-form-item
:label="t('views.configManage.license.neType')"
name="ne"
v-bind="fileModalStateBackFrom.validateInfos.ne"
>
<a-cascader
v-model:value="fileModalState.backFrom.ne"
:options="useNeInfoStore().getNeCascaderOptions"
:allow-clear="false"
@change="fnNeChange"
:placeholder="t('views.configManage.softwareManage.neTypePlease')"
/>
</a-form-item>
</a-form>
</a-modal>
</PageContainer>
</template>

View File

@@ -168,6 +168,7 @@ let tableColumns: ColumnsType = [
title: t('views.faultManage.activeAlarm.origLevel'),
align: 'center',
key: 'origSeverity',
dataIndex: 'origSeverity',
width: 5,
},
{
@@ -630,6 +631,15 @@ function fnExportAll() {
onOk() {
const key = 'exportAlarm';
message.loading({ content: t('common.loading'), key });
let sortArr: any = [];
tableColumnsDnd.value.forEach((item: any) => {
if (item.dataIndex) sortArr.push(item.dataIndex);
});
// 排序字段
const sortData = {
header: sortArr,
};
exportAll(queryParams).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
@@ -637,7 +647,7 @@ function fnExportAll() {
key,
duration: 3,
});
writeSheet(res.data, 'alarm').then(fileBlob =>
writeSheet(res.data, 'alarm', sortData).then(fileBlob =>
saveAs(fileBlob, `alarm_${Date.now()}.xlsx`)
);
} else {
@@ -1029,7 +1039,7 @@ onMounted(() => {
:data-source="alarmTableState.data"
:size="alarmTableState.size"
:pagination="false"
:scroll="{ x: 1700, y: 560 }"
:scroll="{ x: 1700, y: '82vh' }"
>
</a-table>
</a-modal>
@@ -1054,12 +1064,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.alarmId')"
name="alarmId"
>
<a-input
v-model:value="modalState.from.alarmId"
disabled
allow-clear
>
</a-input>
{{ modalState.from.alarmId }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -1067,12 +1072,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.alarmSeq')"
name="alarmSeq"
>
<a-input
v-model:value="modalState.from.alarmSeq"
disabled
allow-clear
>
</a-input>
{{ modalState.from.alarmSeq }}
</a-form-item>
</a-col>
</a-row>
@@ -1083,12 +1083,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.neId')"
name="neId"
>
<a-input
v-model:value="modalState.from.neId"
disabled
allow-clear
>
</a-input>
{{ modalState.from.neId }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -1096,12 +1091,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.neName')"
name="neName"
>
<a-input
v-model:value="modalState.from.neName"
disabled
allow-clear
>
</a-input>
{{ modalState.from.neName }}
</a-form-item>
</a-col>
</a-row>
@@ -1112,12 +1102,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.neType')"
name="neType"
>
<a-input
v-model:value="modalState.from.neType"
disabled
allow-clear
>
</a-input>
{{ modalState.from.neType }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -1125,12 +1110,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.alarmCode')"
name="alarmCode"
>
<a-input
v-model:value="modalState.from.alarmCode"
disabled
allow-clear
>
</a-input>
{{ modalState.from.alarmCode }}
</a-form-item>
</a-col>
</a-row>
@@ -1141,12 +1121,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.alarmTitle')"
name="alarmTitle"
>
<a-input
v-model:value="modalState.from.alarmTitle"
disabled
allow-clear
>
</a-input>
{{ modalState.from.alarmTitle }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -1154,85 +1129,37 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.eventTime')"
name="eventTime"
>
<a-input
v-model:value="modalState.from.eventTime"
disabled
allow-clear
>
</a-input>
{{ modalState.from.eventTime }}
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.faultManage.activeAlarm.alarmTitle')"
name="alarmTitle"
>
<a-input
v-model:value="modalState.from.alarmTitle"
disabled
allow-clear
>
</a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.faultManage.activeAlarm.alarmType')"
name="alarmType"
>
<a-select
v-model:value="modalState.from.alarmType"
style="width: 100%"
:options="dict.activeAlarmType"
disabled
>
</a-select>
{{ modalState.from.alarmType }}
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.faultManage.activeAlarm.pvFlag')"
name="pvFlag"
>
<a-input
v-model:value="modalState.from.pvFlag"
disabled
allow-clear
>
</a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.faultManage.activeAlarm.objectName')"
name="objectName"
>
<a-input
v-model:value="modalState.from.objectName"
disabled
allow-clear
>
</a-input>
{{ modalState.from.pvFlag }}
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16"> </a-row>
<a-form-item
:label="t('views.faultManage.activeAlarm.locationInfo')"
name="locationInfo"
>
<a-textarea
v-model:value="modalState.from.locationInfo"
placeholder="Autosize height with minimum and maximum number of lines"
:auto-size="{ minRows: 1, maxRows: 5 }"
disabled
/>
{{ modalState.from.locationInfo }}
</a-form-item>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
@@ -1240,12 +1167,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.province')"
name="province"
>
<a-input
v-model:value="modalState.from.province"
disabled
allow-clear
>
</a-input>
{{ modalState.from.province }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -1253,13 +1175,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.origLevel')"
name="origSeverity"
>
<a-select
v-model:value="modalState.from.origSeverity"
style="width: 100%"
:options="dict.activeAlarmSeverity"
disabled
>
</a-select>
{{ modalState.from.origSeverity }}
</a-form-item>
</a-col>
</a-row>
@@ -1268,33 +1184,20 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.addInfo')"
name="addInfo"
>
<a-textarea
v-model:value="modalState.from.addInfo"
:auto-size="{ minRows: 1, maxRows: 5 }"
disabled
/>
{{ modalState.from.addInfo }}
</a-form-item>
<a-form-item
:label="t('views.faultManage.activeAlarm.specificProblemId')"
name="specificProblemId"
>
<a-input
v-model:value="modalState.from.specificProblemId"
disabled
allow-clear
>
</a-input>
{{ modalState.from.specificProblemId }}
</a-form-item>
<a-form-item
:label="t('views.faultManage.activeAlarm.specificProblem')"
name="specificProblem"
>
<a-textarea
v-model:value="modalState.from.specificProblem"
:auto-size="{ minRows: 1, maxRows: 5 }"
disabled
/>
{{ modalState.from.specificProblem }}
</a-form-item>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
@@ -1302,13 +1205,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.clearType')"
name="clearType"
>
<a-select
v-model:value="modalState.from.clearType"
style="width: 100%"
:options="dict.activeClearType"
disabled
>
</a-select>
{{ modalState.from.clearType }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -1316,12 +1213,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.clearTime')"
name="clearTime"
>
<a-input
v-model:value="modalState.from.clearTime"
disabled
allow-clear
>
</a-input>
{{ modalState.from.clearTime }}
</a-form-item>
</a-col>
</a-row>
@@ -1332,12 +1224,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.ackUser')"
name="ackUser"
>
<a-input
v-model:value="modalState.from.ackUser"
disabled
allow-clear
>
</a-input>
{{ modalState.from.ackUser }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -1345,12 +1232,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.ackState')"
name="ackState"
>
<a-select
v-model:value="modalState.from.ackState"
style="width: 100%"
:options="dict.activeAckState"
disabled
/>
{{ modalState.from.ackState }}
</a-form-item>
</a-col>
</a-row>
@@ -1361,12 +1243,15 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.ackTime')"
name="ackTime"
>
<a-input
v-model:value="modalState.from.ackTime"
disabled
allow-clear
>
</a-input>
{{ modalState.from.ackTime }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.faultManage.activeAlarm.objectName')"
name="objectName"
>
{{ modalState.from.objectName }}
</a-form-item>
</a-col>
</a-row>
@@ -1575,6 +1460,9 @@ onMounted(() => {
.table :deep(.ant-pagination) {
padding: 0 24px;
}
</style>
<style lang="less">
.full-modal {
.ant-modal {
max-width: 100%;

View File

@@ -39,6 +39,9 @@ let dict: {
/**记录开始结束时间 */
let queryRangePicker = ref<[string, string]>(['', '']);
/**表格字段列排序 */
let tableColumnsDnd = ref<ColumnsType>([]);
/**查询参数 */
let queryParams = reactive({
/**告警设备类型 */
@@ -143,7 +146,7 @@ let tableColumns: ColumnsType = [
{
title: t('views.faultManage.activeAlarm.origLevel'),
align: 'left',
dataIndex: 'origLevel',
dataIndex: 'origSeverity',
key: 'origSeverity',
width: 5,
},
@@ -340,7 +343,7 @@ function fnModalVisibleByVive(row: Record<string, any>) {
function fnModalOk() {
modalState.confirmLoading = true;
const from = toRaw(modalState.from);
if (from.ackState) {
if (from.ackState === '1') {
message.error({
content: t('views.faultManage.activeAlarm.ackError'),
duration: 3,
@@ -431,6 +434,14 @@ function fnExportAll() {
onOk() {
const key = 'exportAlarmHis';
message.loading({ content: t('common.loading'), key });
let sortArr: any = [];
tableColumnsDnd.value.forEach((item: any) => {
if (item.dataIndex) sortArr.push(item.dataIndex);
});
// 排序字段
const sortData = {
header: sortArr,
};
exportAll(queryParams).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
@@ -438,7 +449,7 @@ function fnExportAll() {
key,
duration: 3,
});
writeSheet(res.data, 'alarm').then(fileBlob =>
writeSheet(res.data, 'alarm', sortData).then(fileBlob =>
saveAs(fileBlob, `history-alarm_${Date.now()}.xlsx`)
);
} else {
@@ -707,6 +718,10 @@ onMounted(() => {
</template>
</a-dropdown>
</a-tooltip>
<TableColumnsDnd
:columns="tableColumns"
v-model:columns-dnd="tableColumnsDnd"
></TableColumnsDnd>
</a-space>
</template>
@@ -714,7 +729,7 @@ onMounted(() => {
<a-table
class="table"
row-key="id"
:columns="tableColumns"
:columns="tableColumnsDnd"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
@@ -785,12 +800,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.alarmId')"
name="alarmId"
>
<a-input
v-model:value="modalState.from.alarmId"
disabled
allow-clear
>
</a-input>
{{ modalState.from.alarmId }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -798,12 +808,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.alarmSeq')"
name="alarmSeq"
>
<a-input
v-model:value="modalState.from.alarmSeq"
disabled
allow-clear
>
</a-input>
{{ modalState.from.alarmSeq }}
</a-form-item>
</a-col>
</a-row>
@@ -814,12 +819,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.neId')"
name="neId"
>
<a-input
v-model:value="modalState.from.neId"
disabled
allow-clear
>
</a-input>
{{ modalState.from.neId }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -827,12 +827,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.neName')"
name="neName"
>
<a-input
v-model:value="modalState.from.neName"
disabled
allow-clear
>
</a-input>
{{ modalState.from.neName }}
</a-form-item>
</a-col>
</a-row>
@@ -843,12 +838,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.neType')"
name="neType"
>
<a-input
v-model:value="modalState.from.neType"
disabled
allow-clear
>
</a-input>
{{ modalState.from.neType }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -856,12 +846,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.alarmCode')"
name="alarmCode"
>
<a-input
v-model:value="modalState.from.alarmCode"
disabled
allow-clear
>
</a-input>
{{ modalState.from.alarmCode }}
</a-form-item>
</a-col>
</a-row>
@@ -872,12 +857,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.alarmTitle')"
name="alarmTitle"
>
<a-input
v-model:value="modalState.from.alarmTitle"
disabled
allow-clear
>
</a-input>
{{ modalState.from.alarmTitle }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -885,42 +865,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.eventTime')"
name="eventTime"
>
<a-input
v-model:value="modalState.from.eventTime"
disabled
allow-clear
>
</a-input>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.faultManage.activeAlarm.alarmTitle')"
name="alarmTitle"
>
<a-input
v-model:value="modalState.from.alarmTitle"
disabled
allow-clear
>
</a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.faultManage.activeAlarm.alarmType')"
name="alarmType"
>
<a-select
v-model:value="modalState.from.alarmType"
style="width: 100%"
:options="dict.activeAlarmType"
disabled
>
</a-select>
{{ modalState.from.eventTime }}
</a-form-item>
</a-col>
</a-row>
@@ -931,25 +876,16 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.pvFlag')"
name="pvFlag"
>
<a-input
v-model:value="modalState.from.pvFlag"
disabled
allow-clear
>
</a-input>
{{ modalState.from.pvFlag }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.faultManage.activeAlarm.objectName')"
name="objectName"
:label="t('views.faultManage.activeAlarm.alarmType')"
name="alarmType"
>
<a-input
v-model:value="modalState.from.objectName"
disabled
allow-clear
>
</a-input>
{{ modalState.from.alarmType }}
</a-form-item>
</a-col>
</a-row>
@@ -958,12 +894,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.locationInfo')"
name="locationInfo"
>
<a-textarea
v-model:value="modalState.from.locationInfo"
placeholder="Autosize height with minimum and maximum number of lines"
:auto-size="{ minRows: 1, maxRows: 5 }"
disabled
/>
{{ modalState.from.locationInfo }}
</a-form-item>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
@@ -971,12 +902,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.province')"
name="province"
>
<a-input
v-model:value="modalState.from.province"
disabled
allow-clear
>
</a-input>
{{ modalState.from.province }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -984,13 +910,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.origLevel')"
name="origSeverity"
>
<a-select
v-model:value="modalState.from.origSeverity"
style="width: 100%"
:options="dict.activeAlarmSeverity"
disabled
>
</a-select>
{{ modalState.from.origSeverity }}
</a-form-item>
</a-col>
</a-row>
@@ -999,33 +919,20 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.addInfo')"
name="addInfo"
>
<a-textarea
v-model:value="modalState.from.addInfo"
:auto-size="{ minRows: 1, maxRows: 5 }"
disabled
/>
{{ modalState.from.addInfo }}
</a-form-item>
<a-form-item
:label="t('views.faultManage.activeAlarm.specificProblemId')"
name="specificProblemId"
>
<a-input
v-model:value="modalState.from.specificProblemId"
disabled
allow-clear
>
</a-input>
{{ modalState.from.specificProblemId }}
</a-form-item>
<a-form-item
:label="t('views.faultManage.activeAlarm.specificProblem')"
name="specificProblem"
>
<a-textarea
v-model:value="modalState.from.specificProblem"
:auto-size="{ minRows: 1, maxRows: 5 }"
disabled
/>
{{ modalState.from.specificProblem }}
</a-form-item>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
@@ -1033,13 +940,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.clearType')"
name="clearType"
>
<a-select
v-model:value="modalState.from.clearType"
style="width: 100%"
:options="dict.activeClearType"
disabled
>
</a-select>
{{ modalState.from.clearType }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -1047,12 +948,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.clearTime')"
name="clearTime"
>
<a-input
v-model:value="modalState.from.clearTime"
disabled
allow-clear
>
</a-input>
{{ modalState.from.clearTime }}
</a-form-item>
</a-col>
</a-row>
@@ -1063,12 +959,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.ackUser')"
name="ackUser"
>
<a-input
v-model:value="modalState.from.ackUser"
disabled
allow-clear
>
</a-input>
{{ modalState.from.ackUser }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -1076,12 +967,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.ackState')"
name="ackState"
>
<a-select
v-model:value="modalState.from.ackState"
style="width: 100%"
:options="dict.activeAckState"
disabled
/>
{{ modalState.from.ackState }}
</a-form-item>
</a-col>
</a-row>
@@ -1092,12 +978,15 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.ackTime')"
name="ackTime"
>
<a-input
v-model:value="modalState.from.ackTime"
disabled
allow-clear
>
</a-input>
{{ modalState.from.ackTime }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.faultManage.activeAlarm.objectName')"
name="objectName"
>
{{ modalState.from.objectName }}
</a-form-item>
</a-col>
</a-row>

View File

@@ -34,7 +34,12 @@ echarts.use([
/**用户性别字典 */
let indexColor = ref<DictType[]>([
{ label: 'normal', value: 'normal', elTagType: '', elTagClass: '#91cc75' },
{ label: 'abnormal', value: 'abnormal', elTagType: '', elTagClass: '#ee6666' },
{
label: 'abnormal',
value: 'abnormal',
elTagType: '',
elTagClass: '#ee6666',
},
]);
/**表格字段列 */
@@ -65,6 +70,11 @@ let tableColumns: ColumnsType = [
dataIndex: 'version',
align: 'center',
},
{
title: t('views.index.serialNum'),
dataIndex: 'serialNum',
align: 'center',
},
{
title: t('views.index.ipAddress'),
dataIndex: 'ipAddress',
@@ -101,10 +111,10 @@ let tableState: TabeStateType = reactive({
/**表格状态 */
let nfInfo: any = reactive({
obj: 'OMC',
version: '2.2312.8',
version: appStore.version,
status: t('views.index.normal'),
number: '',
outTimeDate: '',
serialNum: appStore.serialNum,
});
/**表格状态类型 */
@@ -158,7 +168,7 @@ function fnGetList(one: boolean) {
tableState.loading = false;
var rightNum = 0;
var errorNum = 0;
// if (res.length) nfInfo.serialNum = res[0].serialNum;
for (let i = 0; i < res.length; i++) {
if (res[i].status == '正常' || res[i].status == 'Normal') {
rightNum++;
@@ -166,9 +176,16 @@ function fnGetList(one: boolean) {
errorNum++;
}
}
echarts.init(document.getElementById('echarts-records')).clear();
var chartDom = document.getElementById('echarts-records');
var myChart = echarts.init(chartDom);
var chartDom: any = document.getElementById('echarts-records');
var existingChart = echarts.getInstanceByDom(chartDom);
var myChart: any;
if (existingChart) {
myChart = existingChart;
myChart.clear(); // 清空图表,重新设置数据
} else {
myChart = echarts.init(chartDom);
}
var option = {
title: {
@@ -218,125 +235,6 @@ function fnGetList(one: boolean) {
});
}
/**點擊網元名 */
function init(e: any) {
let realData = toRaw(e);
var chartDom = document.getElementById('echarts-records');
var myChart = echarts.init(chartDom);
myChart.clear(); //怕遗留以前得元素
let cpuUsage = realData.cpuUsage;
let memUsage = realData.memUsage;
var nfMenUsage =
Math.round((memUsage?.nfUsedMem / memUsage?.totalMem) * 10000) / 100.0;
let partitionInfo = realData.diskSpace?.partitionInfo[1];
var nfMaxDiskSpace =
Math.round((partitionInfo?.used / partitionInfo?.total) * 10000) / 100.0;
let option = {
tooltip: {
formatter: '{a} <br/>{b} : {c}%',
},
series: [
{
name: 'Nf Memory usage',
center: ['17%', '50%'],
radius: '80%',
type: 'gauge',
progress: {
show: true,
},
pointer: {
//仪表盘的指针
//这个show属性好像有问题因为在这次开发中需要去掉指正我设置false的时候还是显示指针估计是BUG吧我用的echarts-3.2.3希望改进。最终我把width属性设置为0成功搞定
show: true,
//指针长度
length: '60%',
},
title: {
offsetCenter: [0, '90%'],
},
detail: {
valueAnimation: true,
formatter: '{value}',
textStyle: {
fontSize: 18,
},
offsetCenter: [0, '60%'],
},
data: [
{
value: nfMenUsage,
name: 'MEM',
},
],
},
{
center: ['50%', '50%'],
name: 'Nf CPU usage',
radius: '80%',
type: 'gauge',
progress: {
show: true,
},
title: {
offsetCenter: [0, '90%'],
},
detail: {
valueAnimation: true,
formatter: '{value}',
textStyle: {
fontSize: 18,
},
offsetCenter: [0, '60%'],
},
data: [
{
value: cpuUsage?.nfCpuUsage / 100,
name: 'CPU',
},
],
},
{
center: ['83%', '50%'],
name: 'NF maximum disk usage',
radius: '80%',
type: 'gauge',
progress: {
show: true,
},
title: {
offsetCenter: [0, '90%'],
},
detail: {
valueAnimation: true,
formatter: '{value}',
textStyle: {
fontSize: 18,
},
offsetCenter: [0, '60%'],
},
data: [
{
value: nfMaxDiskSpace,
name: 'DiskSpace',
},
],
},
],
};
option && myChart.setOption(option);
window.onresize = function () {
// echarts 窗口缩放自适应 随着div--echarts-records的大小来适应
myChart.resize();
};
nfInfo.obj = realData.name;
nfInfo.number = realData.serialNum;
nfInfo.outTimeDate = realData.expiryDate;
}
/**抽屉 网元详细信息 */
const visible = ref(false);
const closeDrawer = () => {
@@ -481,9 +379,7 @@ onBeforeUnmount(() => {
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'status'">
<div v-if="record.status == '正常' || record.status == 'Normal'">
<a-tag color="blue" @click="init(record)">{{
record.name
}}</a-tag>
<a-tag color="blue">{{ record.name }}</a-tag>
</div>
<div v-else>
<a-tag color="pink">{{ record.name }}</a-tag>
@@ -509,6 +405,7 @@ onBeforeUnmount(() => {
nfInfo.obj
}}</a-descriptions-item>
<template v-if="nfInfo.obj === 'OMC'">
<a-descriptions-item :label="t('views.index.versionNum')">{{
nfInfo.version
}}</a-descriptions-item>
@@ -518,7 +415,7 @@ onBeforeUnmount(() => {
</template>
<template v-else>
<a-descriptions-item :label="t('views.index.serialNum')">{{
nfInfo.number
nfInfo.serialNum
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.expiryDate')">{{
nfInfo.outTimeDate

View File

@@ -202,6 +202,7 @@ function fnNeChange(keys: any, _: any) {
queryParams.neId = keys[1];
if (neType === 'IMS') {
nePathArr.value = ['/var/log/ims'];
queryParams.search = '';
} else {
nePathArr.value = ['/var/log'];
queryParams.search = neType.toLowerCase();

View File

@@ -600,7 +600,7 @@ onMounted(() => {
@select="fnAutoCompleteSelect"
@change="fnAutoCompleteChange"
>
<a-textarea :placeholder="t('common.ipnutPlease')" auto-size />
<a-textarea :placeholder="t('common.inputPlease')" auto-size />
</a-auto-complete>
</a-form-item>
</a-form>
@@ -667,7 +667,7 @@ onMounted(() => {
:accept="item.filter"
:max-count="1"
:show-upload-list="false"
:custom-request="v => fnUpload(v, item.name)"
:custom-request="(v:any) => fnUpload(v, item.name)"
>
<a-button
type="primary"

View File

@@ -568,7 +568,7 @@ onMounted(() => {
@select="fnAutoCompleteSelect"
@change="fnAutoCompleteChange"
>
<a-textarea :placeholder="t('common.ipnutPlease')" auto-size />
<a-textarea :placeholder="t('common.inputPlease')" auto-size />
</a-auto-complete>
</a-form-item>
</a-form>
@@ -635,7 +635,7 @@ onMounted(() => {
:accept="item.filter"
:max-count="1"
:show-upload-list="false"
:custom-request="v => fnUpload(v, item.name)"
:custom-request="(v:any) => fnUpload(v, item.name)"
>
<a-button
type="primary"

View File

@@ -573,7 +573,7 @@ onMounted(() => {
@select="fnAutoCompleteSelect"
@change="fnAutoCompleteChange"
>
<a-textarea :placeholder="t('common.ipnutPlease')" auto-size />
<a-textarea :placeholder="t('common.inputPlease')" auto-size />
</a-auto-complete>
</a-form-item>
</a-form>
@@ -640,7 +640,7 @@ onMounted(() => {
:accept="item.filter"
:max-count="1"
:show-upload-list="false"
:custom-request="v => fnUpload(v, item.name)"
:custom-request="(v:any) => fnUpload(v, item.name)"
>
<a-button
type="primary"

View File

@@ -424,7 +424,7 @@ onMounted(() => {
v-model:value="queryParams.jobName"
:allow-clear="jobId === '0'"
:disabled="jobId !== '0'"
:placeholder="t('common.ipnutPlease')"
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,187 @@
import { message, Form } from 'ant-design-vue/lib';
import { reactive, toRaw, watch } from 'vue';
import { graphG6, selectSourceTargetOptions } from './useGraph';
import useI18n from '@/hooks/useI18n';
/**图分组内置类型 */
export const comboTypeOptions = [
{
value: 'circle',
label: '圆形',
},
{
value: 'rect',
label: '矩形',
},
];
/**图分组标签文本位置 */
export const comboPositionOptions = [
{
value: 'top',
label: '上',
},
{
value: 'left',
label: '左',
},
{
value: 'right',
label: '右',
},
{
value: 'bottom',
label: '下',
},
{
value: 'center',
label: '居中',
},
];
export default function useCombo() {
const { t } = useI18n();
/**图分组信息状态类型 */
type ComboStateType = {
/**图分组原始数据 */
origin: Record<string, any>;
/**图分组表单数据 */
form: Record<string, any>;
};
/**图分组信息状态 */
let comboState: ComboStateType = reactive({
origin: {},
form: {
id: '',
type: 'rect',
parentId: '',
size: [40, 40],
padding: [30, 30, 30, 30],
style: {
radius: 2,
fill: '#ffffff',
stroke: '#ffffff',
lineWidth: 1,
cursor: 'grab',
fillOpacity: 0.5,
},
label: '',
labelCfg: {
refX: 10,
refY: 10,
position: 'top',
style: {
fill: '#000000',
fontSize: 12,
fontWeight: 500,
},
},
children: [], // 子元素
},
});
/**图分组对话分组内表单属性和校验规则 */
const comboStateForm = Form.useForm(
comboState.form,
reactive({
id: [{ required: true, message: '分组唯一标识 ID' }],
})
);
/**图分组编辑监听更新视图 */
watch(comboState.form, combo => {
const info = JSON.parse(JSON.stringify(combo));
// 新增不监听变化
const comboOriginId = comboState.origin.id;
const comboId = info.id;
if (comboId && comboId === comboOriginId) {
graphG6.value.clearItemStates(comboId, 'selected');
const data = graphG6.value.save();
const item = data.combos.find((item: any) => item.id === comboId);
Object.assign(item, combo);
// 无父组id时不要设置避免导致绘制失败
if (!combo.parentId) {
Reflect.deleteProperty(item, 'parentId');
}
graphG6.value.read(data);
}
});
/**图分组类型输入限制 */
function handleComboTypeChange(type: any) {
// 类型尺寸和边距
if (type === 'circle') {
comboState.form.size = 30;
comboState.form.padding = 30;
}
if (type === 'rect') {
comboState.form.size = [30, 20];
comboState.form.padding = [10, 20, 10, 20];
}
}
/**图分组新增或更新 */
function handleOkcombo() {
return comboStateForm
.validate()
.then(e => {
const combo = JSON.parse(JSON.stringify(comboState.form));
if (!combo.id) {
message.warn({
content: `分组元素ID错误`,
duration: 2,
});
return false;
}
const item = graphG6.value.findById(combo.id);
if (item) {
const data = graphG6.value.save();
const item = data.combos.find((item: any) => item.id === combo.id);
Object.assign(item, combo);
// 无父组id时不要设置避免导致绘制失败
if (!combo.parentId) {
Reflect.deleteProperty(item, 'parentId');
}
graphG6.value.read(data);
} else {
graphG6.value.createCombo(combo, combo.children);
}
comboStateForm.resetFields();
comboState.origin = {};
return true;
})
.catch(e => {
message.error(
t('common.errorFields', { num: e.errorFields.length }),
3
);
return false;
});
}
/**图分组取消还原 */
function handleCancelcombo() {
// 新增无原始数据
const origin = JSON.parse(JSON.stringify(comboState.origin));
if (origin.id) {
const data = graphG6.value.save();
const item = data.combos.find((combo: any) => combo.id === origin.id);
Object.assign(item, origin);
graphG6.value.read(data);
}
comboStateForm.resetFields();
comboState.origin = {};
}
return {
comboState,
comboStateForm,
handleComboTypeChange,
handleOkcombo,
handleCancelcombo,
};
}

View File

@@ -0,0 +1,169 @@
import { message, Form } from 'ant-design-vue/lib';
import { reactive, watch } from 'vue';
import useI18n from '@/hooks/useI18n';
import { graphG6 } from './useGraph';
/**图边内置边类型 */
export const edgeTypeOptions = [
{
value: 'line',
label: '直线,连接两个节点的直线',
},
{
value: 'polyline',
label: '折线,多段线段构成的折线,连接两个端点',
},
{
value: 'arc',
label: '圆弧线,连接两个节点的一段圆弧',
},
{
value: 'quadratic',
label: '二阶贝塞尔曲线,只有一个控制点的曲线',
},
{
value: 'cubic',
label: '三阶贝塞尔曲线,有两个控制点的曲线',
},
{
value: 'cubic-vertical',
label: '垂直方向的三阶贝塞尔曲线',
},
{
value: 'cubic-horizontal',
label: '水平方向的三阶贝塞尔曲线',
},
{
value: 'loop',
label: '自环',
},
];
/**图边标签文本位置 */
export const edgePositionOptions = [
{
value: 'start',
label: '开头',
},
{
value: 'middle',
label: '中间',
},
{
value: 'end',
label: '末尾',
},
];
export default function useEdge() {
const { t } = useI18n();
/**图边信息状态类型 */
type EdgeStateType = {
/**图边原始数据 */
origin: Record<string, any>;
/**图边表单数据 */
form: Record<string, any>;
};
/**图边信息状态 */
let edgeState: EdgeStateType = reactive({
origin: {},
form: {
id: '',
source: '',
target: '',
type: 'polyline',
style: {
offset: 20,
radius: 2,
stroke: '#ffffff',
lineWidth: 1,
cursor: 'pointer',
},
label: '',
labelCfg: {
refX: 0,
refY: 0,
position: 'middle',
autoRotate: false,
style: {
fill: '#ffffff',
fontSize: 12,
fontWeight: 500,
},
},
},
});
/**图边对话框内表单属性和校验规则 */
const edgeStateForm = Form.useForm(
edgeState.form,
reactive({
id: [{ required: true, message: '边唯一 ID' }],
source: [{ required: true, message: '起始点 id' }],
target: [{ required: true, message: '结束点 id' }],
type: [{ required: true, message: 'line' }],
})
);
/**图边编辑监听更新视图 */
watch(edgeState.form, edge => {
const info = JSON.parse(JSON.stringify(edge));
// 新增id是#不监听变化
const edgeId = info.id;
if (edgeId && edgeId !== '#') {
graphG6.value.clearItemStates(edgeId, 'selected');
graphG6.value.updateItem(edgeId, info);
}
});
/**图边新增或更新 */
function handleOkEdge() {
return edgeStateForm
.validate()
.then(e => {
const edge = JSON.parse(JSON.stringify(edgeState.form));
if (!edge.id) {
message.warn({
content: `边元素ID错误`,
duration: 2,
});
return false;
}
// 存在更新新增id是#不监听变化
const item = graphG6.value.findById(edge.id);
if (item) {
graphG6.value.updateItem(item, edge);
} else {
edge.id = `${edge.source}~${Date.now()}~${edge.target}`;
graphG6.value.addItem('edge', edge);
}
edgeStateForm.resetFields();
edgeState.origin = {};
return true;
})
.catch(e => {
message.error(
t('common.errorFields', { num: e.errorFields.length }),
3
);
return false;
});
}
/**图边取消还原 */
function handleCancelEdge() {
// 新增无原始数据
const origin = JSON.parse(JSON.stringify(edgeState.origin));
if (origin.id) {
graphG6.value.updateItem(origin.id, origin);
}
edgeStateForm.resetFields();
edgeState.origin = {};
}
return { edgeState, edgeStateForm, handleOkEdge, handleCancelEdge };
}

View File

@@ -0,0 +1,581 @@
import useI18n from '@/hooks/useI18n';
import {
Graph,
GraphData,
ICanvas,
IShapeBase,
Item,
Menu,
Tooltip,
} from '@antv/g6';
import { ref } from 'vue';
/**图模式选择项 */
export const graphModeOptions = [
{
value: 'default',
label: '默认',
},
{
value: 'edit',
label: '编辑',
},
];
/**图实例对象 */
export const graphG6 = ref<any>(null);
/**图事件变更 */
export const graphEvent = ref<{
type: string;
target: HTMLElement | (IShapeBase & ICanvas);
item: Item | null;
}>();
/**图元素选择开始结束点 */
export const selectSourceTargetOptions = ref<Record<string, any>[]>([]);
/**图元素选择嵌入分组 */
export const selectComboOptions = ref<Record<string, any>[]>([]);
/**图模式选择项 */
export const graphMode = ref<string>('default');
export default function useGraph() {
//实例化i18n
const { t } = useI18n();
/**图画布右击菜单 */
const graphCanvasMenu = new Menu({
offsetX: 6,
offseY: 10,
itemTypes: ['canvas'],
getContent(evt) {
console.log(evt);
if (!evt) return '无';
const edit = graphMode.value === 'edit';
return `
<div
style="
display: flex;
flex-direction: column;
width: 140px;
"
>
<h3>画布</h3>
<div id="show" style="cursor: pointer; margin-bottom: 2px">
显示所有隐藏项
</div>
<div id="create-edge" style="cursor: pointer; margin-bottom: 2px; display: ${
edit ? 'black' : 'none'
}">
新增边
</div>
<div id="create-node" style="cursor: pointer; margin-bottom: 2px; display: ${
edit ? 'black' : 'none'
}">
新增节点
</div>
<div id="create-combo" style="cursor: pointer; margin-bottom: 2px; display: ${
edit ? 'black' : 'none'
}">
新增分组
</div>
</div>`;
},
handleMenuClick(target, item) {
console.log(target, item);
const targetId = target.id;
switch (targetId) {
case 'create-edge':
case 'create-node':
case 'create-combo':
graphEvent.value = { type: `canvas-${targetId}`, target, item };
break;
case 'show':
// 显示节点
graphG6.value.getNodes().forEach((node: any) => {
if (!node.isVisible()) {
graphG6.value.showItem(node);
graphG6.value.refreshItem(node);
}
});
// 显示边
graphG6.value.getEdges().forEach((edge: any) => {
if (!edge.isVisible()) {
graphG6.value.showItem(edge);
graphG6.value.refreshItem(edge);
}
});
// 显示分组
graphG6.value.getCombos().forEach((combo: any) => {
if (!combo.isVisible()) {
graphG6.value.showItem(combo);
graphG6.value.updateCombo(combo);
}
});
break;
}
},
});
/**图分组Combo 右击菜单 */
const graphComboMenu = new Menu({
offsetX: 6,
offseY: 10,
itemTypes: ['combo'],
getContent(evt) {
console.log(evt);
return `
<div
style="
display: flex;
flex-direction: column;
width: 140px;
background: #e6f7ff;
"
>
<h3>分组</h3>
<div id="edit" style="cursor: pointer; margin-bottom: 2px">
1. 编辑
</div>
<div id="delete" style="cursor: pointer; margin-bottom: 2px">
2. 删除
</div>
<div id="hide" style="cursor: pointer; margin-bottom: 2px">
2. 隐藏
</div>
</div>
`;
},
handleMenuClick(target, item) {
console.log(target, item);
const targetId = target.id;
switch (targetId) {
case 'edit':
graphEvent.value = { type: `combo-${targetId}`, target, item };
break;
case 'delete':
graphG6.value.uncombo(item);
graphG6.value.updateCombos();
break;
case 'hide':
graphG6.value.hideItem(item);
break;
}
},
});
/**图节点右击菜单 */
const graphNodeMenu = new Menu({
offsetX: 6,
offseY: 10,
itemTypes: ['node'],
getContent(evt) {
console.log(evt);
if (!evt) return '无';
const item = evt.item?.getModel();
const edit = graphMode.value === 'edit';
return `
<div
style="
display: flex;
flex-direction: column;
width: 140px;
background: #e6f7ff;
"
>
<h3>节点:${item?.label || '无标签'}</h3>
<div id="hide" style="cursor: pointer; margin-bottom: 2px">
隐藏
</div>
<div id="edit" style="cursor: pointer; margin-bottom: 2px; display: ${
edit ? 'black' : 'none'
}">
编辑
</div>
<div id="delete" style="cursor: pointer; margin-bottom: 2px; display: ${
edit ? 'black' : 'none'
}">
删除
</div>
</div>
`;
},
handleMenuClick(target, item) {
console.log(target, item);
const targetId = target.id;
switch (targetId) {
case 'edit':
graphEvent.value = { type: `node-${targetId}`, target, item };
break;
case 'delete':
graphG6.value.removeItem(item);
break;
case 'hide':
graphG6.value.hideItem(item);
break;
}
},
});
/**图节点展示 */
const graphNodeTooltip = new Tooltip({
offsetX: 10,
offsetY: 20,
getContent(evt) {
console.log(evt);
if (!evt) return '无';
const item = evt.item?.getModel();
if (!item?.label) {
return '无标签';
}
console.log(item);
return `
<div
style="
display: flex;
flex-direction: column;
width: 140px;
background: #e6f7ff;
"
>
<h3>节点: ${item?.label}</h3>
</div>
`;
},
itemTypes: ['node'],
});
/**图边右击菜单 */
const graphEdgeMenu = new Menu({
offsetX: 6,
offseY: 10,
itemTypes: ['edge'],
getContent(evt) {
console.log(evt);
if (!evt) return '无';
const item = evt.item?.getModel();
const edit = graphMode.value === 'edit';
return `
<div
style="
display: flex;
flex-direction: column;
width: 140px;
background: #e6f7ff;
"
>
<strong>边:${item?.label || '无标签'}</strong>
<div id="hide" style="cursor: pointer; margin-bottom: 2px">
隐藏
</div>
<div id="edit" style="cursor: pointer; margin-bottom: 2px; display: ${
edit ? 'black' : 'none'
}">
编辑
</div>
<div id="delete" style="cursor: pointer; margin-bottom: 2px; display: ${
edit ? 'black' : 'none'
}">
删除
</div>
</div>
`;
},
handleMenuClick(target, item) {
console.log(target, item);
const targetId = target.id;
switch (targetId) {
case 'edit':
graphEvent.value = { type: `edge-${targetId}`, target, item };
break;
case 'delete':
graphG6.value.removeItem(item);
break;
case 'hide':
graphG6.value.hideItem(item);
break;
}
},
});
/**图边展示 */
const graphEdgeTooltip = new Tooltip({
offsetX: 10,
offsetY: 20,
getContent(evt) {
console.log(evt);
if (!evt) return '无';
const item = evt.item?.getModel();
if (!item?.label) {
return '无标签';
}
return `
<div
style="
display: flex;
flex-direction: column;
width: 140px;
background: #e6f7ff;
"
>
<h3>边: ${item?.label}</h3>
</div>
`;
},
itemTypes: ['edge'],
});
/**图绑定事件 */
function fnGraphEvent(graph: Graph) {
// 调用 graph.add / graph.addItem 方法之后触发
graph.on('afteradditem', evt => {
fnSelectSourceTargetOptionsData();
});
// 鼠标进入节点事件
graph.on('edge:mouseenter', (ev: any) => {
// 获得鼠标当前目标边
const edge = ev.item;
// 该边的起始点
const source = edge.getSource();
// 该边的结束点
const target = edge.getTarget();
// 先将边提前,再将端点提前。这样该边两个端点还是在该边上层,较符合常规。
// edge.toFront();
// source.toFront();
// target.toFront();
});
graph.on('edge:mouseleave', (ev: any) => {
// 获得图上所有边实例
const edges = graph.getEdges();
// 遍历边,将所有边的层级放置在后方,以恢复原样
// edges.forEach(edge => {
// edge.toBack();
// });
});
graph.on('node:mouseenter', evt => {
// 获得鼠标当前目标节点
const node = evt.item;
// 获取该节点的所有相关边
const edges = node && graph.getEdges();
// 遍历相关边,将所有相关边提前,再将相关边的两个端点提前,以保证相关边的端点在边的上方常规效果
// edges.forEach((edge: any) => {
// edge.toFront();
// edge.getSource().toFront();
// edge.getTarget().toFront();
// });
// graphEvent.value = {
// type: 'node:mouseenter',
// target: evt.target,
// item: evt.item,
// };
});
graph.on('node:mouseleave', (ev: any) => {
// 获得图上所有边实例
const edges = graph.getEdges();
// 遍历边,将所有边的层级放置在后方,以恢复原样
// edges.forEach(edge => {
// edge.toBack();
// });
});
}
/**
* 图元素选择开始结束点数据获取
*/
function fnSelectSourceTargetOptionsData() {
// 节点
selectSourceTargetOptions.value = [];
graphG6.value.getNodes().forEach((node: any) => {
const info = JSON.parse(JSON.stringify(node.getModel()));
selectSourceTargetOptions.value.push({
value: info.id,
label: info.label,
info,
});
});
// 分组
selectComboOptions.value = [
{
value: '',
label: '未分配',
},
];
graphG6.value.getCombos().forEach((combo: any) => {
const info = JSON.parse(JSON.stringify(combo.getModel()));
const comboInfo = {
value: info.id,
label: info.label,
info,
};
selectSourceTargetOptions.value.push(comboInfo);
selectComboOptions.value.push(comboInfo);
});
}
/**图数据渲染 */
function handleRanderGraph(container: HTMLElement, data: GraphData) {
if (!container) return;
const { clientHeight, clientWidth } = container;
const graph = new Graph({
container: container,
width: clientWidth,
height: clientHeight,
animate: true,
fitCenter: true,
modes: {
default: [
{
type: 'click-select',
selectEdge: true,
},
'drag-combo',
{
type: 'drag-node',
onlyChangeComboSize: true,
},
'drag-canvas',
'zoom-canvas',
'collapse-expand-combo',
],
edit: [
{
type: 'click-select',
selectEdge: true,
},
{
type: 'drag-node',
shouldEnd: (e: any) => {
return true;
},
},
{ type: 'drag-combo' },
'drag-canvas',
'zoom-canvas',
{ type: 'create-edge', key: 'alt' },
],
},
groupByTypes: false,
// layout: {
// type: 'dagre',
// sortByCombo: false,
// ranksep: 10,
// nodesep: 10,
// },
// 全局节点
defaultNode: {
type: 'rect',
size: [80, 40],
style: {
radius: 4,
// fill: '#ffffff',
stroke: '#ffffff',
lineWidth: 1,
cursor: 'pointer',
},
labelCfg: {
position: 'center',
offset: 0,
style: {
fill: '#000000',
fontSize: 12,
fontWeight: 500,
},
},
icon: {
show: false,
img: '/svg/service.svg',
width: 25,
height: 25,
offset: 20, // triangle 特有
},
direction: 'up', // triangle 三角形的方向
},
// 全局边
defaultEdge: {
type: 'polyline',
style: {
offset: 20, // 拐弯处距离节点最小距离
radius: 2, // 拐弯处的圆角弧度,若不设置则为直角
stroke: '#ffffff',
lineWidth: 1,
cursor: 'pointer',
},
labelCfg: {
refX: 0,
refY: 0,
position: 'middle',
autoRotate: false,
style: {
fill: '#ffffff',
fontSize: 12,
fontWeight: 500,
},
},
},
// 全局分组节点
defaultCombo: {
type: 'rect', // Combo 类型
size: [40, 40],
padding: [30, 30, 30, 30],
style: {
radius: 2,
fill: '#ffffff',
stroke: '#ffffff',
lineWidth: 1,
cursor: 'grab',
fillOpacity: 0.5,
},
labelCfg: {
refX: 10,
refY: 10,
position: 'top',
style: {
fill: '#000000',
fontSize: 12,
fontWeight: 500,
},
},
},
plugins: [
graphCanvasMenu,
graphComboMenu,
graphNodeMenu,
graphNodeTooltip,
graphEdgeMenu,
graphEdgeTooltip,
],
});
graph.data(data);
graph.render();
// 图绑定事件
fnGraphEvent(graph);
graphG6.value = graph;
// 图元素选择开始结束点数据
fnSelectSourceTargetOptionsData();
return graph;
}
/**图模式改变 default | edit */
function handleChangeMode(value: any) {
console.log(value, JSON.parse(JSON.stringify(graphG6.value.save())));
graphG6.value.setMode(value);
graphMode.value = graphG6.value.getCurrentMode();
}
return {
handleRanderGraph,
handleChangeMode,
};
}

View File

@@ -0,0 +1,323 @@
import { message, Form } from 'ant-design-vue/lib';
import { reactive, watch } from 'vue';
import { graphG6 } from './useGraph';
import useI18n from '@/hooks/useI18n';
/**图节点内置边类型 */
export const nodeTypeOptions = [
{
value: 'circle',
label: '圆形',
},
{
value: 'rect',
label: '矩形',
},
{
value: 'ellipse',
label: '椭圆',
},
{
value: 'diamond',
label: '菱形',
},
{
value: 'triangle',
label: '三角形',
},
{
value: 'star',
label: '星形',
},
{
value: 'image',
label: '图片',
},
{
value: 'donut',
label: '面包圈',
},
];
/**图节点标签文本位置 */
export const nodePositionOptions = [
{
value: 'top',
label: '上',
},
{
value: 'left',
label: '左',
},
{
value: 'right',
label: '右',
},
{
value: 'bottom',
label: '下',
},
{
value: 'center',
label: '居中',
},
];
/**图节点三角形方向 */
export const nodeDirectionOptions = [
{ value: 'up', label: '向上' },
{ value: 'down', label: '向下' },
{ value: 'left', label: '向左' },
{ value: 'right', label: '向右' },
];
/**图节点图片裁剪的形状 */
export const nodeImageClipCfgOptions = [
{ value: 'circle', label: '圆形' },
{ value: 'rect', label: '矩形' },
{ value: 'ellipse', label: '椭圆' },
];
/**图节点图片来源 */
export const nodeImageOptions = [
{ value: '/svg/base.svg', label: '基站' },
{ value: '/svg/cloud.svg', label: '云' },
{ value: '/svg/service.svg', label: '服务器' },
{ value: '/svg/service_db.svg', label: '数据服务器' },
];
export default function useNode() {
const { t } = useI18n();
/**图节点信息状态类型 */
type NodeStateType = {
/**图节点原始数据 */
origin: Record<string, any>;
/**图节点表单数据 */
form: Record<string, any>;
};
/**图节点信息状态 */
let nodeState: NodeStateType = reactive({
origin: {},
form: {
id: '',
comboId: '',
x: 0,
y: 0,
type: 'rect',
size: [80, 40],
anchorPoints: false,
style: {
fill: '#ffffff',
stroke: '#ffffff',
lineWidth: 1,
},
label: '',
labelCfg: {
position: 'center',
offset: 0,
style: {
fill: '#000000',
fontSize: 12,
fontWeight: 500,
},
},
},
});
/**图节点对话框内表单属性和校验规则 */
const nodeStateForm = Form.useForm(
nodeState.form,
reactive({
id: [{ required: true, message: '节点唯一 ID' }],
type: [{ required: true, message: 'line' }],
})
);
/**图节点编辑监听更新视图 */
watch(nodeState.form, node => {
const info = JSON.parse(JSON.stringify(node));
// 新增不监听变化
const nodeOriginId = nodeState.origin.id;
const nodeId = info.id;
if (nodeId && nodeId === nodeOriginId) {
// 图片类型需要移除style属性避免填充
if (info.type === 'image') {
Reflect.deleteProperty(info, 'style');
}
graphG6.value.clearItemStates(nodeId, 'selected');
graphG6.value.updateItem(nodeId, info);
// 三角和图片的样式变更需要重绘才生效
if (
info.type === 'triangle' ||
info.type === 'image' ||
info.comboId !== nodeState.origin.comboId
) {
graphG6.value.read(graphG6.value.save());
}
}
});
/**图节点类型输入限制 */
function handleNodeTypeChange(type: any) {
// 设置图标属性
if (['circle', 'ellipse', 'diamond', 'star', 'donut'].includes(type)) {
let size: number[] | number = [40, 30];
if (['circle', 'star', 'donut'].includes(type)) {
size = 60;
}
const origin = nodeState.origin;
if (origin.icon) {
nodeState.form = Object.assign(nodeState.form, {
size,
icon: origin.icon,
});
} else {
nodeState.form = Object.assign(nodeState.form, {
size,
icon: {
show: false,
img: '',
width: 25,
height: 25,
},
});
}
} else if (type === 'triangle') {
// 三角
const origin = nodeState.origin;
if (origin.icon) {
nodeState.form = Object.assign(nodeState.form, {
size: 40,
direction: origin.direction || 'up', // triangle 三角形的方向
icon: Object.assign({ offset: 20 }, origin.icon),
});
} else {
nodeState.form = Object.assign(nodeState.form, {
size: 30,
direction: 'up', // triangle 三角形的方向
icon: {
show: false,
img: '/svg/service.svg',
width: 25,
height: 25,
offset: 20, // triangle 特有
},
});
}
}
// 设置图片属性
if (type === 'image') {
const origin = nodeState.origin;
if (origin.img) {
nodeState.form = Object.assign(nodeState.form, {
size: [30, 30],
img: origin.img,
clipCfg: origin.clipCfg,
});
} else {
nodeState.form = Object.assign(nodeState.form, {
size: [30, 30],
img: '/svg/service.svg',
clipCfg: {
show: false,
width: 0,
height: 0,
type: 'circle',
},
});
}
Reflect.deleteProperty(nodeState.form, 'style');
} else {
// 当切换非图片时补充style属性
if (!Reflect.has(nodeState.form, 'style')) {
nodeState.form = Object.assign(nodeState.form, {
style: {
fill: '#ffffff',
stroke: '#ffffff',
lineWidth: 1,
},
});
}
}
// 设置矩形大小
if (type === 'rect') {
nodeState.form = Object.assign(nodeState.form, {
size: [80, 40],
});
}
}
/**图节点新增或更新 */
function handleOkNode() {
return nodeStateForm
.validate()
.then(e => {
const node = JSON.parse(JSON.stringify(nodeState.form));
if (!node.id) {
message.warn({
content: `节点元素ID错误`,
duration: 2,
});
return false;
}
// 存在更新,新增不监听变化
const item = graphG6.value.findById(node.id);
if (item) {
graphG6.value.updateItem(item, node);
} else {
graphG6.value.addItem('node', node);
}
// 三角和图片的样式变更需要重绘才生效
if (
node.type === 'triangle' ||
node.type === 'image' ||
node.comboId !== nodeState.origin.comboId
) {
graphG6.value.read(graphG6.value.save());
}
nodeStateForm.resetFields();
nodeState.origin = {};
return true;
})
.catch(e => {
message.error(
t('common.errorFields', { num: e.errorFields.length }),
3
);
return false;
});
}
/**图节点取消还原 */
function handleCancelNode() {
// 新增无原始数据
const origin = JSON.parse(JSON.stringify(nodeState.origin));
if (origin.id) {
graphG6.value.updateItem(origin.id, origin);
// 三角和图片的样式变更需要重绘才生效
if (
origin.type === 'triangle' ||
origin.type === 'image' ||
origin.comboId !== nodeState.form.comboId
) {
graphG6.value.read(graphG6.value.save());
}
}
nodeStateForm.resetFields();
nodeState.origin = {};
}
return {
nodeState,
nodeStateForm,
handleNodeTypeChange,
handleOkNode,
handleCancelNode,
};
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,68 +1,334 @@
<script setup lang="ts">
import { reactive, onMounted } from 'vue';
import { reactive, onMounted, ref } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import ChartGraphG6 from '@/components/ChartGraphG6/index.vue';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { listNe, stateNe } from '@/api/ne/ne';
import message from 'ant-design-vue/lib/message';
import { randerGroph, switchLayout } from './graph';
import { parseDateToStr } from '@/utils/date-utils';
const { t } = useI18n();
/**查询全部网元数据列表 */
function fnGetList() {}
/**图DOM节点实例对象 */
const graphG6Dom = ref<HTMLElement | undefined>(undefined);
onMounted(() => {});
/**图实例对象 */
const graphG6 = ref<any>(null);
/**图数据 */
const graphG6Data = reactive<Record<string, any>>({
nodes: [],
edges: [],
});
/**查询全部网元数据列表 */
function fnRanderData() {
if (!graphG6Dom.value) return;
graphG6.value = randerGroph(graphG6Dom.value, graphG6Data);
}
/**查询网元状态 */
async function fnGetState() {
for (const node of graphG6Data.nodes) {
const ne = node.info;
if (ne.neType === 'OMC') continue;
const result = await stateNe(ne.neType, ne.neId);
if (result.code === RESULT_CODE_SUCCESS) {
ne.serverState = result.data;
ne.serverState.refreshTime = parseDateToStr(
ne.serverState.refreshTime,
'HH:mm:ss'
);
}
// 修改网元状态颜色
const neShape = graphG6.value.findById(ne.neName);
graphG6.value.setItemState(
neShape,
'neState',
result.code === RESULT_CODE_SUCCESS
);
}
setTimeout(() => fnGetState(), 30_000);
}
/**查询全部网元数据列表 */
function fnGetList(refresh: boolean = false) {
listNe({
bandStatus: false,
})
.then(res => {
if (
res.code === RESULT_CODE_SUCCESS &&
Array.isArray(res.data) &&
res.data.length > 0
) {
let rootNode = 'OMC';
const nodes = [];
const edges = [];
for (const item of res.data) {
item.serverState = {};
const nodeIndex = nodes.findIndex(v => v.id === item.neName);
if (nodeIndex === -1) {
// 根网管
if (item.neType === 'OMC') {
rootNode = item.neName;
item.serverState = {
neId: item.neId,
neName: item.neName,
neType: item.neType,
expire: '-',
refreshTime: '-',
sn: '-',
version: '-',
};
nodes.push({
id: item.neName,
label: item.neName,
info: item,
labelCfg: {
position: 'bottom',
offset: 8,
style: {
fill: '#fff',
fontSize: 14,
},
},
size: 60,
icon: {
x: -30,
y: -30,
// 可更换为其他图片地址
img: '/svg/service_db.svg',
width: 60,
height: 60,
},
});
} else {
nodes.push({
id: item.neName,
label: item.neName,
info: item,
size: 48,
icon: {
x: -24,
y: -24,
img: '/svg/service.svg',
width: 48,
height: 48,
},
});
}
}
if (item.neType !== 'OMC') {
const edgeIndex = edges.findIndex(v => v.source === item.neName);
if (edgeIndex === -1) {
edges.push({
source: item.neName,
target: rootNode,
label: `${item.neType}-${rootNode}`,
});
}
}
}
graphG6Data.nodes = nodes;
graphG6Data.edges = edges;
console.log(graphG6Data);
return true;
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
return false;
}
})
.then(hasNeList => {
if (!hasNeList) return;
if (refresh) {
// graphG6.value.get('canvas').set('localRefresh', true);
graphG6.value.destroy();
// graphG6.value.clear();
}
fnRanderData();
fnGetState();
});
}
onMounted(() => {
// 获取网元列表
fnGetList();
});
</script>
<template>
<PageContainer>
<a-card
:bordered="false"
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
:body-style="{ marginBottom: '24px' }"
size="small"
>
<!-- 表格搜索栏 -->
<a-form name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="10" :md="10" :xs="24">
<a-form-item
:label="t('views.monitor.monitor.filter')"
name="neTypeSelect"
>
ssdf
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList()">
<template #icon><SearchOutlined /></template>
{{ t('common.search') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-card>
<!-- 插槽-卡片左侧侧 -->
<template #title v-if="false">
<div class="button-container" style="margin-bottom: -12px">
<a-button type="primary">
<template #icon>
<PlusOutlined />
</template>
{{ t('common.addText') }}
</a-button>
<a-row :gutter="16">
<a-col :lg="24" :md="24" :xs="24">
<a-card :bordered="false" :body-style="{ marginBottom: '24px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>asdf</template>
<!-- 插槽-卡片右侧 -->
<template #extra> adf </template>
<a-button type="primary" danger ghost>
<template #icon>
<DeleteOutlined />
</template>
{{ t('views.neUser.auth.batchDelText') }}
</a-button>
<a-popconfirm
:title="t('views.neUser.sub.loadDataConfirm')"
:ok-text="t('common.ok')"
:cancel-text="t('common.cancel')"
>
<a-button type="dashed" danger>
<template #icon>
<SyncOutlined />
</template>
{{ t('views.neUser.sub.loadData') }}
</a-button>
</a-popconfirm>
<div class="chart">
<ChartGraphG6 :option="{}"></ChartGraphG6>
<a-button type="dashed">
<template #icon>
<ImportOutlined />
</template>
{{ t('views.neUser.sub.import') }}
</a-button>
<a-popconfirm
:title="t('views.neUser.sub.exportConfirm')"
ok-text="TXT"
ok-type="default"
>
<a-button type="dashed">
<template #icon>
<ExportOutlined />
</template>
{{ t('views.neUser.sub.export') }}
</a-button>
</a-popconfirm>
</div>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-button type="default" @click.prevent="switchLayout()">
<template #icon><RetweetOutlined /></template>
Switch Layout
</a-button>
</template>
<div ref="graphG6Dom" class="chart"></div>
<template v-if="false">
<div
style="
display: flex;
flex-direction: column;
width: 200px;
background: #e6f7ff;
"
>
<div><span>状态</span><span>正常</span></div>
<div><span>刷新时间</span><span>18:37:22</span></div>
<div><span>ID</span><span>neID</span></div>
<div><span>名称</span><span>neName</span></div>
<div><span>版本</span><span>2.2312.8</span></div>
<div><span>SN</span><span>13770707</span></div>
<div><span>有效期</span><span>2024-03-31</span></div>
</div>
===
<div
style="
display: flex;
flex-direction: column;
width: 140px;
background: #e6f7ff;
"
>
<div id="show" style="cursor: pointer; margin-bottom: 2px">
1. 显示所有隐藏项
</div>
</a-card>
</a-col>
</a-row>
<div id="collapseAll" style="cursor: pointer; margin-bottom: 2px">
2. 折叠所有集群
</div>
</div>
===
<div
style="
display: flex;
flex-direction: column;
width: 100px;
background: #e6f7ff;
"
>
<div id="expand" style="cursor: pointer; margin-bottom: 2px">
1. 展开集群
</div>
<div id="hide" style="cursor: pointer; margin-bottom: 2px">
2. 隐藏节点
</div>
</div>
===
<div
style="
display: flex;
flex-direction: column;
width: 160px;
background: #e6f7ff;
"
>
<div id="collapse" style="cursor: pointer; margin-bottom: 2px">
1. 折叠集群
</div>
<div id="hide" style="cursor: pointer; margin-bottom: 2px">
2. 隐藏节点
</div>
<div id="restart" style="cursor: pointer; margin-bottom: 2px">
3. 重启
</div>
</div>
===
<div
style="
display: flex;
flex-direction: column;
width: 100px;
background: #e6f7ff;
"
>
<div id="hide" style="cursor: pointer; margin-bottom: 2px">
1. 隐藏边
</div>
</div>
<!-- 组件 -->
<div class="charts">
<ChartGraphG6></ChartGraphG6>
</div>
</template>
</a-card>
</PageContainer>
</template>
<style lang="less" scoped>
.chart {
width: 100%;
height: calc(100vh - 300px);
background-color: rgb(43, 47, 51);
}
.charts {
width: 100%;
height: 500px;
background-color: rgb(238, 237, 237);
margin-top: 32px;
}
</style>

View File

@@ -70,6 +70,8 @@ type TabeStateType = {
seached: boolean;
/**记录数据 */
data: object[];
/**勾选记录 */
selectedRowKeys: (string | number)[];
};
/**表格状态 */
@@ -79,6 +81,7 @@ let tableState: TabeStateType = reactive({
striped: false,
seached: true,
data: [],
selectedRowKeys: [],
});
/**表格字段列 */
@@ -180,6 +183,11 @@ function fnTableChange(pagination: any, filters: any, sorter: any, extra: any) {
fnGetList(1);
}
/**表格多选 */
function fnTableSelectedRowKeys(keys: (string | number)[]) {
tableState.selectedRowKeys = keys;
}
/**对话框对象信息状态类型 */
type ModalStateType = {
/**新增框或修改框是否显示 */
@@ -482,36 +490,79 @@ function fnModalCancel() {
/**
* UDM鉴权用户删除
* @param row 网元编号ID
* @param imsi 编号imsi
*/
function fnRecordDelete(row: Record<string, any>) {
function fnRecordDelete(imsi: string) {
const neID = queryParams.neId;
if (!neID) return;
let imsiMsg = imsi;
if (imsi === '0') {
imsiMsg = `${tableState.selectedRowKeys[0]}... ${t(
'views.neUser.auth.numDel'
)} ${tableState.selectedRowKeys.length}`;
imsi = tableState.selectedRowKeys.join(',');
}
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.neUser.auth.delSure', { imsi: row.imsi }),
content: t('views.neUser.auth.delSure', { imsi: imsiMsg }),
onOk() {
const key = 'delNotice';
message.loading({ content: t('common.loading'), key });
const neID = queryParams.neId || '-';
delAuth(neID, row).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', { msg: t('common.deleteText') }),
key,
duration: 2,
});
modalState.loadDataLoading = true;
const hide = message.loading({ content: t('common.loading') });
delAuth(neID, imsi)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
const msgContent = t('common.msgSuccess', {
msg: t('common.deleteText'),
});
message.success({
content: `${msgContent} : ${imsiMsg}`,
duration: 3,
});
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
fnGetList();
} else {
message.error({
content: `${res.msg}`,
key: key,
duration: 2,
});
}
});
modalState.loadDataLoading = false;
});
},
});
}
/**
* UDM鉴权用户导出
*/
function fnRecordExport(type: string = 'txt') {
const selectLen = tableState.selectedRowKeys.length;
if (selectLen <= 0) return;
const rows: Record<string, any>[] = tableState.data.filter(
(row: Record<string, any>) =>
tableState.selectedRowKeys.indexOf(row.imsi) >= 0
);
let content = '';
if (type == 'txt') {
for (const row of rows) {
content += `${row.imsi},${row.ki},${row.algoIndex},${row.amf},${row.opc}\r\n`;
}
}
if (type == 'csv') {
content = `IMSI,ki,Algo Index,AMF,OPC\r\n`;
for (const row of rows) {
content += `${row.imsi},${row.ki},${row.algoIndex},${row.amf},${row.opc}\r\n`;
}
}
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
saveAs(blob, `UDMAuth_${Date.now()}.${type}`);
}
/**列表导出 */
function fnExportList(type: string) {
const neID = queryParams.neId;
@@ -583,6 +634,10 @@ function fnGetList(pageNum?: number) {
}
listAuth(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;
tableState.data = res.rows;
}
@@ -742,6 +797,18 @@ onMounted(() => {
</template>
{{ t('views.neUser.auth.batchAddText') }}
</a-button>
<a-button
type="default"
danger
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.loadDataLoading"
@click.prevent="fnRecordDelete('0')"
>
<template #icon><DeleteOutlined /></template>
{{ t('views.neUser.auth.checkDel') }}
</a-button>
<a-button
type="primary"
danger
@@ -753,6 +820,7 @@ onMounted(() => {
</template>
{{ t('views.neUser.auth.batchDelText') }}
</a-button>
<a-popconfirm
:title="t('views.neUser.auth.loadDataConfirm')"
:ok-text="t('common.ok')"
@@ -778,6 +846,7 @@ onMounted(() => {
<a-popconfirm
:title="t('views.neUser.auth.exportConfirm')"
placement="topRight"
ok-text="TXT"
ok-type="default"
@confirm="fnExportList('txt')"
@@ -790,6 +859,26 @@ onMounted(() => {
{{ t('views.neUser.auth.export') }}
</a-button>
</a-popconfirm>
<a-popconfirm
:title="t('views.neUser.auth.checkExportConfirm')"
placement="topRight"
ok-text="TXT"
ok-type="default"
@confirm="fnRecordExport('txt')"
:show-cancel="false"
cancel-text="CSV"
@cancel="fnRecordExport('csv')"
:disabled="tableState.selectedRowKeys.length <= 0"
>
<a-button
type="default"
:disabled="tableState.selectedRowKeys.length <= 0"
>
<template #icon><ExportOutlined /></template>
{{ t('views.neUser.auth.checkExport') }}
</a-button>
</a-popconfirm>
</div>
</template>
@@ -864,6 +953,11 @@ onMounted(() => {
:scroll="{ y: 'calc(100vh - 480px)' }"
@change="fnTableChange"
@resizeColumn="(w:number, col:any) => (col.width = w)"
:row-selection="{
type: 'checkbox',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'imsi'">
@@ -881,7 +975,10 @@ onMounted(() => {
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.deleteText') }}</template>
<a-button type="link" @click.prevent="fnRecordDelete(record)">
<a-button
type="link"
@click.prevent="fnRecordDelete(record.imsi)"
>
<template #icon>
<DeleteOutlined />
</template>

View File

@@ -253,26 +253,11 @@ function fnModalOk() {
from.neId = queryParams.neId || '-';
from.rfsp = Number(from.rfsp) || 0;
// 根据类型选择函数
let result: any = null;
let validateArr = ['imsi', 'msisdn'];
if (modalState.isBatch) {
validateArr.push('num');
if (modalState.type === 'add') {
result = batchAddRule(from);
}
if (modalState.type === 'update') {
result = batchUpdateRule(from);
}
if (modalState.type === 'delete') {
result = batchDelRule(from);
}
} else {
if (modalState.type === 'add') {
result = addRule(from);
}
if (modalState.type === 'update') {
result = updateRule(from);
validateArr = ['num', 'imsi'];
}
}
@@ -280,8 +265,29 @@ function fnModalOk() {
.validate(validateArr)
.then(e => {
modalState.confirmLoading = true;
const hide = message.loading({ content: t('common.loading') });
// 根据类型选择函数
let result: any = null;
if (modalState.isBatch) {
if (modalState.type === 'add') {
result = batchAddRule(from);
}
if (modalState.type === 'update') {
result = batchUpdateRule(from);
}
if (modalState.type === 'delete') {
result = batchDelRule(from);
}
} else {
if (modalState.type === 'add') {
result = addRule(from);
}
if (modalState.type === 'update') {
result = updateRule(from);
}
}
result
.then((res: any) => {
if (res.code === RESULT_CODE_SUCCESS) {
@@ -469,7 +475,9 @@ function fnModalUploadImportUpload(file: File) {
return res;
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && res.data?.detail) {
if (res.code === RESULT_CODE_SUCCESS && res.data?.filePath) {
uploadImportState.msg = t('views.neUser.pcf.uploadFileOk');
} else if (res.code === RESULT_CODE_SUCCESS && res.data?.detail) {
uploadImportState.msg = res.data?.detail;
} else {
uploadImportState.msg = t('views.neUser.pcf.uploadFileErr');
@@ -773,7 +781,11 @@ onMounted(() => {
name="imsi"
v-bind="modalStateFrom.validateInfos.imsi"
>
<a-input v-model:value="modalState.from.imsi" allow-clear>
<a-input
v-model:value="modalState.from.imsi"
allow-clear
:maxlength="64"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>
@@ -821,6 +833,7 @@ onMounted(() => {
<a-input
v-model:value="modalState.from.imsi"
allow-clear
:maxlength="64"
:disabled="
!modalState.isBatch && modalState.type === 'update'
"
@@ -848,6 +861,7 @@ onMounted(() => {
<a-input
v-model:value="modalState.from.msisdn"
allow-clear
:maxlength="16"
:disabled="
!modalState.isBatch && modalState.type === 'update'
"
@@ -860,13 +874,21 @@ onMounted(() => {
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="PCC Rules" name="pccRules">
<a-input v-model:value="modalState.from.pccRules" allow-clear>
<a-input
v-model:value="modalState.from.pccRules"
allow-clear
:maxlength="64"
>
</a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="SESS Rules" name="sessRules">
<a-input v-model:value="modalState.from.sessRules" allow-clear>
<a-input
v-model:value="modalState.from.sessRules"
allow-clear
:maxlength="64"
>
</a-input>
</a-form-item>
</a-col>
@@ -875,13 +897,21 @@ onMounted(() => {
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="QOS Audio" name="qosAudio">
<a-input v-model:value="modalState.from.qosAudio" allow-clear>
<a-input
v-model:value="modalState.from.qosAudio"
allow-clear
:maxlength="64"
>
</a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="QOS Video" name="qosVideo">
<a-input v-model:value="modalState.from.qosVideo" allow-clear>
<a-input
v-model:value="modalState.from.qosVideo"
allow-clear
:maxlength="64"
>
</a-input>
</a-form-item>
</a-col>
@@ -890,13 +920,21 @@ onMounted(() => {
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="HDR Enrich" name="hdrEnrich">
<a-input v-model:value="modalState.from.hdrEnrich" allow-clear>
<a-input
v-model:value="modalState.from.hdrEnrich"
allow-clear
:maxlength="64"
>
</a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="UE Policy" name="uePolicy">
<a-input v-model:value="modalState.from.uePolicy" allow-clear>
<a-input
v-model:value="modalState.from.uePolicy"
allow-clear
:maxlength="64"
>
</a-input>
</a-form-item>
</a-col>
@@ -905,14 +943,23 @@ onMounted(() => {
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="Sar" name="sar">
<a-input v-model:value="modalState.from.sar" allow-clear>
<a-input
v-model:value="modalState.from.sar"
allow-clear
:maxlength="64"
>
</a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="RFSP" name="rfsp">
<a-input v-model:value="modalState.from.rfsp" allow-clear>
</a-input>
<a-input-number
v-model:value="modalState.from.rfsp"
style="width: 100%"
:min="0"
:max="255"
placeholder="0~255"
></a-input-number>
</a-form-item>
</a-col>
</a-row>

View File

@@ -73,6 +73,8 @@ type TabeStateType = {
seached: boolean;
/**记录数据 */
data: object[];
/**勾选记录 */
selectedRowKeys: (string | number)[];
};
/**表格状态 */
@@ -82,6 +84,7 @@ let tableState: TabeStateType = reactive({
striped: false,
seached: true,
data: [],
selectedRowKeys: [],
});
/**表格字段列 */
@@ -218,6 +221,11 @@ function fnTableChange(pagination: any, filters: any, sorter: any, extra: any) {
fnGetList(1);
}
/**表格多选 */
function fnTableSelectedRowKeys(keys: (string | number)[]) {
tableState.selectedRowKeys = keys;
}
/**对话框对象信息状态类型 */
type ModalStateType = {
/**详情框是否显示 */
@@ -711,37 +719,100 @@ function fnBatchDelModalCancel() {
/**
* UDM签约用户删除
* @param imsi 网元编号ID
* @param imsi 编号imsi
*/
function fnRecordDelete(imsi: string) {
const neID = queryParams.neId;
if (!neID) return;
let imsiMsg = imsi;
if (imsi === '0') {
imsiMsg = `${tableState.selectedRowKeys[0]}... ${t(
'views.neUser.sub.numDel'
)} ${tableState.selectedRowKeys.length}`;
imsi = tableState.selectedRowKeys.join(',');
}
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.neUser.auth.delSure', { imsi: imsi }),
content: t('views.neUser.auth.delSure', { imsi: imsiMsg }),
onOk() {
const key = 'delSub';
message.loading({ content: t('common.loading'), key });
delSub(neID, imsi).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', { msg: t('common.deleteText') }),
key,
duration: 2,
});
modalState.loadDataLoading = true;
const hide = message.loading({ content: t('common.loading') });
delSub(neID, imsi)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
const msgContent = t('common.msgSuccess', {
msg: t('common.deleteText'),
});
message.success({
content: `${msgContent} : ${imsiMsg}`,
duration: 3,
});
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
fnGetList();
} else {
message.error({
content: `${res.msg}`,
key: key,
duration: 2,
});
}
});
modalState.loadDataLoading = false;
});
},
});
}
/**
* UDM签约用户导出
*/
function fnRecordExport(type: string = 'txt') {
const selectLen = tableState.selectedRowKeys.length;
if (selectLen <= 0) return;
const rows: Record<string, any>[] = tableState.data.filter(
(row: Record<string, any>) =>
tableState.selectedRowKeys.indexOf(row.imsi) >= 0
);
let content = '';
if (type == 'txt') {
for (const row of rows) {
debugger;
const epsDat = [
row.epsFlag,
row.epsOdb,
row.hplmnOdb,
row.ard,
row.epstpl,
row.contextId,
row.apnContext,
row.staticIp,
].join(',');
content += `${row.imsi},${row.msisdn},${row.ambr},${row.nssai},${row.arfb},${row.sar},${row.rat},${row.cn},${row.smfSel},${row.smData},${epsDat}\r\n`;
}
}
if (type == 'csv') {
content = `imsi,msisdn,ambr,nssai,arfb,sar,rat,cn,smf_sel,sm_dat,eps_dat\r\n`;
for (const row of rows) {
const epsDat = [
row.epsFlag,
row.epsOdb,
row.hplmnOdb,
row.ard,
row.epstpl,
row.contextId,
row.apnContext,
row.staticIp,
].join(',');
content += `${row.imsi},${row.msisdn},${row.ambr},${row.nssai},${row.arfb},${row.sar},${row.rat},${row.cn},${row.smfSel},${row.smData},${epsDat}\r\n`;
}
}
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
saveAs(blob, `UDMSub_${Date.now()}.${type}`);
}
/**列表导出 */
function fnExportList(type: string) {
const neID = queryParams.neId;
@@ -813,6 +884,10 @@ function fnGetList(pageNum?: number) {
}
listSub(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;
tableState.data = res.rows;
}
@@ -977,6 +1052,18 @@ onMounted(() => {
</template>
{{ t('views.neUser.auth.batchAddText') }}
</a-button>
<a-button
type="default"
danger
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.loadDataLoading"
@click.prevent="fnRecordDelete('0')"
>
<template #icon><DeleteOutlined /></template>
{{ t('views.neUser.sub.checkDel') }}
</a-button>
<a-button
type="primary"
danger
@@ -988,6 +1075,7 @@ onMounted(() => {
</template>
{{ t('views.neUser.auth.batchDelText') }}
</a-button>
<a-popconfirm
:title="t('views.neUser.sub.loadDataConfirm')"
:ok-text="t('common.ok')"
@@ -1017,6 +1105,7 @@ onMounted(() => {
<a-popconfirm
:title="t('views.neUser.sub.exportConfirm')"
placement="topRight"
ok-text="TXT"
ok-type="default"
@confirm="fnExportList('txt')"
@@ -1031,6 +1120,26 @@ onMounted(() => {
{{ t('views.neUser.sub.export') }}
</a-button>
</a-popconfirm>
<a-popconfirm
:title="t('views.neUser.sub.checkExportConfirm')"
placement="topRight"
ok-text="TXT"
ok-type="default"
@confirm="fnRecordExport('txt')"
:show-cancel="false"
cancel-text="CSV"
@cancel="fnRecordExport('csv')"
:disabled="tableState.selectedRowKeys.length <= 0"
>
<a-button
type="default"
:disabled="tableState.selectedRowKeys.length <= 0"
>
<template #icon><ExportOutlined /></template>
{{ t('views.neUser.sub.checkExport') }}
</a-button>
</a-popconfirm>
</div>
</template>
@@ -1105,6 +1214,11 @@ onMounted(() => {
:scroll="{ y: 'calc(100vh - 480px)' }"
@change="fnTableChange"
@resizeColumn="(w:number, col:any) => (col.width = w)"
:row-selection="{
type: 'checkbox',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'imsi'">

View File

@@ -92,8 +92,24 @@ let queryParams: any = reactive({
particle: '15',
beginTime: '',
endTime: '',
/**排序字段 */
sortField: 'timeGroup',
/**排序方式 */
sortOrder: 'asc',
});
/**表格分页、排序、筛选变化时触发操作, 排序方式,取值为 ascend descend */
function fnTableChange(pagination: any, filters: any, sorter: any, extra: any) {
const { columnKey, order } = sorter;
if (order) {
queryParams.sortField = columnKey;
queryParams.sortOrder = order.replace('end', '');
} else {
queryParams.sortOrder = 'asc';
}
fnMakeTable(1);
}
/**图表显示数据 */
const chartsOption = reactive({
/**性能指标 */
@@ -196,6 +212,8 @@ function fnDesign() {
dataIndex: 'timeGroup',
align: 'center',
fixed: 'right',
key: 'timeGroup',
sorter: true,
});
if (!queryRangePicker.value) {
queryRangePicker.value = ['', ''];
@@ -216,7 +234,7 @@ function fnDesign() {
.filter(key => !['timeGroup', 'neName', 'startIndex'].includes(key))
.map(key => {
const title: any = findTitleByKey(key);
hideAll[title]=false;
hideAll[title] = false;
return {
name: title,
data: res.data.map((item: any) => parseInt(item[key])),
@@ -260,24 +278,14 @@ function fnDesign() {
color: '#646A73',
},
icon: 'circle',
selected:hideAll,
selected: hideAll,
},
grid: {
left: '10%',
right: '30%',
bottom: '20%',
},
yAxis: [
{ type: 'value', splitNumber: 4, axisLabel: { fontSize: 10 } },
{
type: 'value',
position: 'right',
alignTicks: true,
axisLabel: {
fontSize: 10,
},
},
],
yAxis: [{ type: 'value', splitNumber: 4, axisLabel: { fontSize: 10 } }],
};
chartsOption.perfChart = option;
@@ -293,52 +301,50 @@ function fnDesign() {
onMounted(() => {
// 获取网元网元列表
neInfoStore
.fnNelist()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
// 过滤不可用的网元
neCascaderOptions.value = neInfoStore.getNeCascaderOptions.filter(
(item: any) => {
return !['OMC'].includes(item.value);
}
);
if (neCascaderOptions.value.length === 0) {
message.warning({
content: t('common.noData'),
duration: 2,
});
return;
neInfoStore.fnNelist().then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
// 过滤不可用的网元
neCascaderOptions.value = neInfoStore.getNeCascaderOptions.filter(
(item: any) => {
return !['OMC'].includes(item.value);
}
// 默认选择AMF
const item = neCascaderOptions.value.find(s => s.value === 'UPF');
if (item && item.children) {
const info = item.children[0];
queryParams.neType = [info.neType, info.neId];
} else {
const info = neCascaderOptions.value[0].children[0];
queryParams.neType = [info.neType, info.neId];
}
const initTime: Date = new Date();
const startTime: Date = new Date(initTime);
startTime.setHours(0, 0, 0, 0); // 设置为今天的0点
const endTime: Date = new Date(initTime);
endTime.setHours(23, 59, 59, 59); // 设置为今天的12点
queryRangePicker.value = [
parseDateToStr(startTime),
parseDateToStr(endTime),
];
fnGetList();
);
if (neCascaderOptions.value.length === 0) {
message.warning({
content: t('common.noData'),
duration: 2,
});
return;
}
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
// 默认选择AMF
const item = neCascaderOptions.value.find(s => s.value === 'UPF');
if (item && item.children) {
const info = item.children[0];
queryParams.neType = [info.neType, info.neId];
} else {
const info = neCascaderOptions.value[0].children[0];
queryParams.neType = [info.neType, info.neId];
}
const initTime: Date = new Date();
const startTime: Date = new Date(initTime);
startTime.setHours(0, 0, 0, 0); // 设置为今天的0点
const endTime: Date = new Date(initTime);
endTime.setHours(23, 59, 59, 59); // 设置为今天的12点
queryRangePicker.value = [
parseDateToStr(startTime),
parseDateToStr(endTime),
];
fnGetList();
}
});
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
});
});
</script>
@@ -389,6 +395,7 @@ onMounted(() => {
v-model:value="queryParams.particle"
:placeholder="t('common.selectPlease')"
:options="[
{ label: '5M', value: '5' },
{ label: '15M', value: '15' },
{ label: '30M', value: '30' },
{ label: '60M', value: '60' },
@@ -481,6 +488,7 @@ onMounted(() => {
:scroll="{ x: tableColumnsDnd.length * 200, y: 450 }"
@resizeColumn="(w:number, col:any) => (col.width = w)"
:show-expand-column="false"
@change="fnTableChange"
>
</a-table>
</a-card>
@@ -495,9 +503,7 @@ onMounted(() => {
<template #extra>
<a-space :size="8" align="center">
<a-button type="default" size="small" @click.prevent="fnMakeTable(1)">
<template #icon>
<ClearOutlined />
</template>
<template #icon> <bars-outlined /> </template>
{{ t('views.perfManage.goldTarget.allData') }}
</a-button>
</a-space>

View File

@@ -625,7 +625,7 @@ function fnRecordRun(row: Record<string, any>) {
}
/**
* 激活任务
* 挂起任务
* @param row 网元编号ID
*/
function fnRecordStop(row: Record<string, any>) {

View File

@@ -487,7 +487,7 @@ onMounted(() => {
<a-input
v-model:value="queryParams.configName"
allow-clear
:placeholder="t('common.ipnutPlease')"
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
@@ -499,7 +499,7 @@ onMounted(() => {
<a-input
v-model:value="queryParams.configKey"
allow-clear
:placeholder="t('common.ipnutPlease')"
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
@@ -839,7 +839,7 @@ onMounted(() => {
:auto-size="{ minRows: 4, maxRows: 6 }"
:maxlength="450"
:show-count="true"
:placeholder="t('common.ipnutPlease')"
:placeholder="t('common.inputPlease')"
/>
</a-form-item>
</a-form>

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router';
import { getLocalColor, changePrimaryColor } from '@/hooks/useTheme';
import { reactive, ref, onMounted, toRaw } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { Form, message, Modal } from 'ant-design-vue/lib';
@@ -32,6 +33,18 @@ const dictId = route.params && (route.params.dictId as string);
const zh = currentLocale.value === 'zh_CN';
let color = ref<string>(getLocalColor());
/**改变主题色 */
function fnColorChange(e: Event) {
const target = e.target as HTMLInputElement;
if (target.nodeName === 'INPUT') {
changePrimaryColor(target.value ?? '#1890ff');
} else {
changePrimaryColor();
}
color.value = getLocalColor();
}
/**标签类型数据固定项 */
const tagTypeOptions = ref([
{ value: '', label: zh ? '普通文本' : 'Plain text' },
@@ -289,7 +302,8 @@ function fnModalVisibleByVive(row: Record<string, string>) {
* 对话框弹出显示为 新增或者修改
* @param dictCode 数据编号id, 不传为新增
*/
function fnModalVisibleByEdit(dictCode?: string | number) {
function fnModalVisibleByEdit(dictCode?: string | number, record?: any) {
console.log(record);
if (!dictCode) {
modalStateFrom.resetFields();
modalState.from.dictType = queryParams.dictType;
@@ -522,7 +536,7 @@ onMounted(() => {
<a-input
v-model:value="queryParams.dictLabel"
allow-clear
:placeholder="t('common.ipnutPlease')"
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
@@ -686,7 +700,7 @@ onMounted(() => {
<template #title>{{ t('common.editText') }}</template>
<a-button
type="link"
@click.prevent="fnModalVisibleByEdit(record.dictCode)"
@click.prevent="fnModalVisibleByEdit(record.dictCode, record)"
v-perms:has="['system:dict:edit']"
>
<template #icon><FormOutlined /></template>
@@ -903,8 +917,27 @@ onMounted(() => {
</a-form-item>
</a-col>
</a-row>
<!-- 首页状态取色器 -->
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-col
:lg="12"
:md="12"
:xs="24"
v-if="modalState.from.dictType === 'index_status'"
>
<a-form-item
:label="t('views.system.dict.colorSelect')"
name="tagClass"
>
<a-input
v-model:value="modalState.from.tagClass"
type="color"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24" v-else>
<a-form-item
:label="t('views.system.dictData.tagType')"
name="tagType"
@@ -926,21 +959,26 @@ onMounted(() => {
v-model:value="modalState.from.dictSort"
:min="0"
:max="65535"
:placeholder="t('common.ipnutPlease')"
:placeholder="t('common.inputPlease')"
></a-input-number>
</a-form-item>
</a-col>
</a-row>
<a-form-item
:label="t('views.system.dictData.tagClass')"
name="tagClass"
>
<a-input
v-model:value="modalState.from.tagClass"
allow-clear
:placeholder="t('common.ipnutPlease')"
></a-input>
</a-form-item>
<!-- 非首页状态取色器 -->
<a-row :gutter="16" v-if="modalState.from.dictType !== 'index_status'">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.system.dictData.tagClass')"
name="tagClass"
>
<a-input
v-model:value="modalState.from.tagClass"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
</a-row>
<a-form-item :label="t('views.system.dictData.remark')" name="remark">
<a-textarea
v-model:value="modalState.from.remark"

View File

@@ -72,7 +72,7 @@ onMounted(() => {
v-model:value="state.copyright"
allow-clear
:maxlength="40"
:placeholder="t('common.ipnutPlease')"
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>

View File

@@ -75,7 +75,7 @@ onMounted(() => {
v-model:value="state.url"
allow-clear
:maxlength="255"
:placeholder="t('common.ipnutPlease')"
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>

View File

@@ -70,7 +70,7 @@ onMounted(() => {
v-model:value="state.title"
allow-clear
:maxlength="20"
:placeholder="t('common.ipnutPlease')"
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>

View File

@@ -840,7 +840,7 @@ onMounted(() => {
v-model:value="queryParams.userName"
allow-clear
:maxlength="30"
:placeholder="t('common.ipnutPlease')"
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
@@ -853,7 +853,7 @@ onMounted(() => {
v-model:value="queryParams.phonenumber"
allow-clear
:maxlength="11"
:placeholder="t('common.ipnutPlease')"
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>