115 Commits

Author SHA1 Message Date
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
lai
5124c0c04a Merge branch 'main' of http://192.168.2.166:3180/OMC/ems_frontend_vue3 2023-12-14 11:33:49 +08:00
TsMask
d56c293ef5 fix: MML必填初始值的设置 2023-12-14 11:11:02 +08:00
TsMask
e90bc539e9 fix: udm签约用户新增必填msisdn 2023-12-14 11:01:57 +08:00
TsMask
7edcf1703e fix: 静态文件地址追加随机戳避免缓存 2023-12-14 11:01:19 +08:00
lai
8ee0a4d191 Merge branch 'main' of http://192.168.2.166:3180/OMC/ems_frontend_vue3 2023-12-14 10:05:12 +08:00
TsMask
bc1f897660 feat: 新增网元日志文件页面功能 2023-12-13 21:21:42 +08:00
lai
ba47d3a303 --调整删除请求网元超时时间 2023-12-13 19:47:41 +08:00
TsMask
38e5e4a9e0 fix: 参数配置index为0新增不累加问题 2023-12-13 12:49:24 +08:00
lai
15d7d118c4 调整英文翻译,添加活动告警的可伸缩列 2023-12-13 10:40:41 +08:00
lai
69d4348df7 媒体查询调整 2023-12-13 10:40:06 +08:00
TsMask
fe81a2d924 fix: 移动端适配样式调整 2023-12-12 19:30:44 +08:00
TsMask
cd53fab1f9 fix: 文件转存接口移动 2023-12-12 19:29:49 +08:00
lai
e6d62da386 网元列表进行特制化排序-初版 2023-12-12 16:44:56 +08:00
lai
752f8ccc70 批量新增时EPSODB转换十六进制问题 2023-12-12 15:34:53 +08:00
lai
3a0c85169e 修复设置显示过滤的sql 2023-12-12 15:22:27 +08:00
lai
f845f3f4de --修正后的小屏按钮挤兑问题 2023-12-12 14:55:07 +08:00
TsMask
63e3f352b2 style: 表格头按钮响应式 2023-12-12 12:14:20 +08:00
lai
d9ec9fd1e1 修复当小屏时按钮显示不出问题 2023-12-12 11:27:27 +08:00
lai
f198d2b9a2 新增媒体查询 2023-12-12 11:25:33 +08:00
TsMask
1c3e6db4bc fix: 参数配置修复根据规则取值导致的类型问题 2023-12-12 11:09:09 +08:00
TsMask
8199e16a17 fix: 主页的状态图表颜色默认值 2023-12-11 20:15:26 +08:00
TsMask
0d736966dd feat: 国际化切换判断权限去隐藏控件 2023-12-11 20:14:40 +08:00
TsMask
fe5905ee35 chore: 控制台版本号 2023-12-11 19:22:25 +08:00
TsMask
9c27c32e26 fix: 参数配置根据规则布尔类型转换 2023-12-11 19:21:32 +08:00
TsMask
4208ccc358 style: 拓扑基础节点 2023-12-08 21:16:37 +08:00
lai
51a190771d 调整后的黄金指标,首页状态颜色自定义 2023-12-08 19:19:06 +08:00
TsMask
3376d90b96 fix: 缓存网元类型获取数据判断 2023-12-08 18:52:33 +08:00
TsMask
4105f4ef33 fix: 缓存网元类型获取级联数据判断 2023-12-08 18:38:46 +08:00
TsMask
e6a4591fb6 fix: 缓存网元类型获取级联数据判断 2023-12-08 18:20:21 +08:00
TsMask
fafe604323 fix: 类型异常导致编译失败 2023-12-08 17:26:40 +08:00
TsMask
f110d0e697 fix: 配置文件key为字符串 2023-12-08 17:17:00 +08:00
TsMask
6210b87c84 style: 补充节点,鼠标悬浮动画 2023-12-08 16:30:20 +08:00
TsMask
5a761ece4e chore: 更新依赖包 2023-12-07 20:16:04 +08:00
TsMask
3fa2cffc82 fix: 拓扑图基础 2023-12-07 20:15:18 +08:00
TsMask
e92004a69a style: 文件上传网元端失败固定提示 2023-12-07 18:27:40 +08:00
lai
a612d05429 修正 2023-12-07 17:36:10 +08:00
TsMask
c93dabc6cf fix: PCF查询无数据清空表不提示警告 2023-12-07 17:16:27 +08:00
TsMask
5056de2c1a fix: 参数配置新增根据规则类型转值 2023-12-07 17:15:50 +08:00
8725518450 Merge branch 'main' of http://192.168.2.166:3180/OMC/ems_frontend_vue3 2023-12-07 15:06:45 +08:00
596ee0bb8d fix: prot to port 2023-12-07 15:06:43 +08:00
lai
d5f593647e 修复告警确认告警机制 2023-12-07 10:25:51 +08:00
37eb798c4a Merge branch 'main' of http://192.168.2.166:3180/OMC/ems_frontend_vue3 2023-12-07 09:46:39 +08:00
6536e8baba fix: config.js default host value 2023-12-07 09:46:35 +08:00
TsMask
90a34302a8 style: 更新batch图标 2023-12-06 19:33:32 +08:00
84bbeda7fc Merge branch 'main' of http://192.168.2.166:3180/OMC/ems_frontend_vue3 2023-12-06 19:30:22 +08:00
cbefc04ac9 fix: en locales 2023-12-06 19:30:21 +08:00
TsMask
7564d6c6ab style: n3iwf改表头名称 2023-12-06 18:49:36 +08:00
lai
36ac9d0a5f ---黄金指标初版 2023-12-06 18:13:21 +08:00
TsMask
be6e91390e style: 主页状态颜色 2023-12-06 18:11:53 +08:00
TsMask
f9016492be fix: 终端管理当前列imsi排序 2023-12-06 17:30:13 +08:00
TsMask
dd8dc63eab style: 中断执行提示中断 2023-12-06 16:59:40 +08:00
TsMask
6303aecb47 style: 登录页brand图标宽度174px 2023-12-06 16:16:47 +08:00
TsMask
0216c253d8 fix: 部门名称长度限制问题 2023-12-06 16:07:45 +08:00
1076663511 fix: to 2.2312.8 2023-12-06 15:08:12 +08:00
TsMask
8495192b83 fix: 软件管理-上传软件说明非必填 2023-12-06 14:41:07 +08:00
TsMask
88840cf88e style: UDM多语言-加载数据 2023-12-06 10:49:20 +08:00
lai
97e453d880 修改黄金指标接口 2023-12-05 19:54:16 +08:00
lai
dfc4a849f6 调整展示 2023-12-05 19:11:12 +08:00
lai
20b6c5c10a 新增黄金指标统计展示 2023-12-05 18:53:02 +08:00
lai
ab2154003e 增加黄金指标相关代码 2023-12-04 19:37:40 +08:00
TsMask
b2b837623a fix: 不用Nginx就关闭gzip压缩 2023-12-04 19:19:07 +08:00
TsMask
fb45c3fc10 fix: 补充缺失代码导致的打包异常 2023-12-04 19:06:08 +08:00
TsMask
d64c0f9e5e fix: 更新dev版本号 2023-12-04 18:20:48 +08:00
TsMask
27cc02e0d6 fix: 是否i显示国际化切换的开关 2023-12-04 18:19:11 +08:00
TsMask
12643cac12 feat: 系统设置国际化切换 2023-12-04 18:18:26 +08:00
TsMask
205e07af45 fix: 挂载前根据默认语言在设置一次 2023-12-04 18:17:28 +08:00
TsMask
a1443ebb95 fix: 修改兼容旧前端可改配置文件 2023-12-04 18:17:15 +08:00
TsMask
b6d22014ce del: 删除logo图片文件 2023-12-04 18:16:27 +08:00
TsMask
9abeb7be97 fix: 根据浏览器选择语言 2023-12-04 18:15:51 +08:00
TsMask
96d6cfcfa2 feat: 图标支持根据语言上传对应图标 2023-12-04 16:54:49 +08:00
c16a1675f0 fix: uploaded time 2023-12-02 17:33:58 +08:00
TsMask
bddba89d1e fix: mml命令后面紧跟命令结果 2023-12-01 19:40:30 +08:00
TsMask
eb49b4d29a chore": 更新版本号 2023-12-01 19:29:01 +08:00
TsMask
9d0daca765 fix: 参数配置列新增使用config_rule规制默认值 2023-12-01 19:27:34 +08:00
TsMask
cc234ee081 fix: 用户管理修改角色分配权限标识 2023-12-01 19:26:34 +08:00
TsMask
e23438d910 fix: PCF用户策略控制列表查询提示 2023-12-01 19:25:56 +08:00
TsMask
4fccb85353 fix: PCF用户策略控制导入问题 2023-12-01 17:10:36 +08:00
TsMask
3a8f6964ae feat: PCF用户策略控制导入导出 2023-12-01 16:54:27 +08:00
TsMask
5f89732a61 fix: 参数配置默认idnex+1 2023-12-01 14:11:12 +08:00
86 changed files with 7704 additions and 1209 deletions

View File

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

View File

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

19
CHANGELOG.md Normal file
View File

@@ -0,0 +1,19 @@
# 版本发布日志
## 2.2312.9-20231222
- 新增 日志管理查看网元日志文件并下载功能页面
- 新增 国际化切换权限控制,系统设置里控制切换默认语言
- 新增 性能管理 KPI 指标集功能页面
- 新增 UE 管理 PCF 用户策略控制功能页面
- 修复 参数配置新增数据类型值不匹配导致的异常失败
- 修复 多处英文显示词义不明确的信息提示
- 修复 UE 管理 UDM 签约用户数据批量新增时EPSODB转换十六进制问题
- 优化 网元管理重启接口超时导致网元信息不一致问题
- 优化 参数配置新增编辑参数顺序,参数类型格式化
- 优化 主页网元状态饼图颜色选择自定义
- 优化 MML 操作 UDM 签约用户信息字段,必填项标红
- 优化 软件管理回退功能操作页面
- 优化 静态文件地址追加随机戳避免缓存
- 优化 补充移动端样式适配调整
- 主页修改增加OMC的序列号点击网元信息不再覆盖右边信息框

View File

@@ -12,6 +12,11 @@
Jenkins: http://192.168.2.166:3185/
Nginx: http://192.168.2.166:3188/#/index
后端暴露端口: http://192.168.2.166:3186 \ http://192.168.2.166:3187
新网管192.168.5.13
旧网管192.168.5.14
登录账户manager/manager
```
## 程序命令
@@ -51,7 +56,8 @@ eyJhbGciOiJSUzI1NiIsImtpZCI6ImZFVUhIb1puLW04M1dfSUYyRU8zWlZueXBpNUh4T0hTRVlzU19j
```
```ssh
# https://blog.csdn.net/m0_54706625/article/details/129721121
sudo chmod 700 -/.ssh/
sudochmod 700 /home/ubuntu #这个尤其容易忽视掉,我就是从这个坑里爬出来。有木有很高兴呀!
3sudochmod 600 -/ssh/authorized keys
sudo chmod 700 /home/mask/.ssh #这个尤其容易忽视掉,我就是从这个坑里爬出来。有木有很高兴呀!
sudo chmod 600 ~/.ssh/authorized_keys
```

View File

@@ -12,15 +12,15 @@
"preview": "vite preview"
},
"dependencies": {
"antdv-pro-layout": "^3.2.6",
"@ant-design/icons-vue": "^7.0.1",
"@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",
"dayjs": "^1.11.10",
"echarts": "^5.4.2",
@@ -29,24 +29,24 @@
"js-cookie": "^3.0.5",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"vue": "^3.3.6",
"vue": "^3.3.11",
"vue-codemirror": "^6.1.1",
"vue-i18n": "^9.5.0",
"vue-i18n": "^9.8.0",
"vue-router": "^4.2.5",
"vue3-smooth-dnd": "^0.0.6",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@types/file-saver": "^2.0.5",
"@types/js-cookie": "^3.0.3",
"@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.3.4",
"@types/nprogress": "^0.2.3",
"@vitejs/plugin-vue": "^4.5.2",
"less": "^4.2.0",
"typescript": "^5.2.2",
"unplugin-vue-components": "^0.25.2",
"vite": "^4.5.0",
"typescript": "^5.3.3",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.0.9",
"vite-plugin-compression": "^0.5.1",
"vue-tsc": "^1.8.19"
"vue-tsc": "^1.8.25"
}
}

View File

@@ -17,25 +17,11 @@
*
*/
(function () {
// host = ip:prot
const host = '192.168.2.166:3030';
const configs = {
// internationalization 国际化禁用,默认语言选择
i18nDisable: false,
i18nDisableDefault: 'en_US',
// Service Address 服务地址
baseUrl: `http://${host}`,
// websocket Address
wsUrl: `ws://${host}`,
};
if(!configs.i18nDisable){
localStorage.setItem('cache:local:i18n', configs.i18nDisableDefault);
}
for (const key in configs) {
console.log(key, `${configs[key]}`);
sessionStorage.setItem(key, `${configs[key]}`);
}
// host = ip:port
const host = '192.168.8.100:3030';
// Service Address 服务地址
sessionStorage.setItem('baseUrl', `http://${host}`);
// websocket Address
sessionStorage.setItem('wsUrl', `ws://${host}`);
})();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 227 B

View File

@@ -155,15 +155,15 @@
}
</style>`;
// 根据浏览器选择语言
const lang = localStorage.getItem('cache:local:i18n');
if (!lang) {
let preferredLanguage = navigator.language;
if (preferredLanguage.indexOf('-')) {
preferredLanguage = preferredLanguage.replace('-', '_');
}
localStorage.setItem('cache:local:i18n', preferredLanguage);
}
// 根据浏览器选择语言
// if (!lang) {
// let preferredLanguage = navigator.language;
// if (preferredLanguage.indexOf('-')) {
// preferredLanguage = preferredLanguage.replace('-', '_');
// }
// localStorage.setItem('cache:local:i18n', preferredLanguage);
// }
let loadInfo = {
title: '正在加载资源',

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

@@ -92,4 +92,51 @@ body .ant-pro-basicLayout {
.ant-table.ant-table-small .ant-table-thead > tr > th {
padding: 6px !important;
}
/** ==== 表格头按钮区域 S === **/
/* 默认 */
.button-container {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
align-items: center;
}
.button-container > button,
.button-container > span {
margin-right: 12px;
margin-bottom: 12px;
}
.button-container > button:last-child,
.button-container > span:last-child {
margin-right: 0;
}
/* 平板端 */
@media (max-width: 992px) {
.button-container {
flex-direction: row;
align-items: flex-start;
align-items: left;
}
.button-container > button,
.button-container > span {
margin-right: 12px;
margin-bottom: 12px;
}
}
/* 手机端 */
@media (max-width: 576px) {
.button-container {
flex-direction: column;
align-items: flex-start;
align-items: left;
}
.button-container > button,
.button-container > span {
margin-right: 0px;
margin-bottom: 12px;
}
}
/** ==== 表格头按钮区域 E === **/
</style>

View File

@@ -11,6 +11,24 @@ 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 ';
// 系统特定顺序
const specificOrder = [
'OMC',
'MME',
'AMF',
'AUSF',
'UDM',
'SMF',
'PCF',
'UPF',
'NRF',
'NSSF',
'IMS',
'N3IWF',
'NEF',
'LMF',
];
// 查询
let querySQL = '';
if (query.neType) {
@@ -46,6 +64,14 @@ export async function listNeInfo(query: Record<string, any>) {
data.total = itemData[0]['total'];
} else {
data.rows = itemData.map(v => parseObjLineToHump(v));
//通过sort进行冒泡排序
data.rows.sort((a: any, b: any) => {
const typeA = specificOrder.indexOf(a.neType);
const typeB = specificOrder.indexOf(b.neType);
if (typeA === -1) return 1; // 如果不在特定顺序中,排到后面
if (typeB === -1) return -1; // 如果不在特定顺序中,排到后面
return typeA - typeB;
});
}
}
});
@@ -115,6 +141,7 @@ export async function delNeInfo(data: Record<string, any>) {
return request({
url: `/api/rest/systemManagement/v1/elementType/${data.neType}/objectType/neInfo?ne_id=${data.neId}`,
method: 'delete',
timeout:60*1000,
});
}
@@ -123,6 +150,23 @@ export async function delNeInfo(data: Record<string, any>) {
* @returns object
*/
export async function getNelistAll() {
// 系统特定顺序
const specificOrder = [
'OMC',
'MME',
'AMF',
'AUSF',
'UDM',
'SMF',
'PCF',
'UPF',
'NRF',
'NSSF',
'IMS',
'N3IWF',
'NEF',
'LMF',
];
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/elementType/omc_db/objectType/ne_info`,
@@ -134,6 +178,14 @@ export async function getNelistAll() {
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
let data = result.data.data[0];
//通过sort进行冒泡排序
data['ne_info'].sort((a: any, b: any) => {
const typeA = specificOrder.indexOf(a.ne_type);
const typeB = specificOrder.indexOf(b.ne_type);
if (typeA === -1) return 1;
if (typeB === -1) return -1;
return typeA - typeB;
});
return Object.assign(result, {
data: parseObjLineToHump(data['ne_info']),
});

View File

@@ -169,7 +169,7 @@ export function showPass(data: Record<string, any>) {
let querySQL = '';
querySQL += data.alarm_code ? ` and alarm_code != ${data.alarm_code} ` : '';
querySQL += data.alarm_type
? ` and alarm_type !=\\' ${data.alarm_type} \\'`
? ` and alarm_type !=\\'${data.alarm_type}\\'`
: '';
querySQL += data.pv_flag ? ` and pv_flag !=\\'${data.pv_flag} \\'` : '';
querySQL += data.orig_severity

View File

@@ -7,6 +7,23 @@ import { parseDateToStr } from '@/utils/date-utils';
* @returns object
*/
export async function listMain() {
// 系统特定顺序
const specificOrder = [
'OMC',
'MME',
'AMF',
'AUSF',
'UDM',
'SMF',
'PCF',
'UPF',
'NRF',
'NSSF',
'IMS',
'N3IWF',
'NEF',
'LMF',
];
const result = await request({
url: '/api/rest/systemManagement/v1/elementType/all/objectType/systemState',
method: 'get',
@@ -43,8 +60,16 @@ export async function listMain() {
return mergedObj;
});
// console.log(mergedData);
// console.log(rowArr)
//通过sort进行冒泡排序
mergedData.sort((a: any, b: any) => {
const typeA = specificOrder.indexOf(a.name.split('_')[0]);
const typeB = specificOrder.indexOf(b.name.split('_')[0]);
if (typeA === -1) return 1; // 如果不在特定顺序中,排到后面
if (typeB === -1) return -1; // 如果不在特定顺序中,排到后面
return typeA - typeB;
});
//console.log(mergedData);
return mergedData;
}
@@ -77,15 +102,3 @@ export function getSysConf() {
method: 'get',
});
}
/**
* 转存帮助文档
* @returns object
*/
export function transferHelpDoc(data: Record<string, any>) {
return request({
url: `/helpDoc`,
method: 'post',
data,
});
}

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

@@ -6,15 +6,15 @@ import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
/**
* 签约规则导出
* 规则导出
* @param data 表单数据对象
* @returns bolb
*/
export function exportRule(data: Record<string, any>) {
return request({
url: '/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/file/export',
method: 'post',
data: data,
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/file/export`,
method: 'get',
params: data,
responseType: 'blob',
timeout: 180_000,
});
@@ -22,14 +22,13 @@ export function exportRule(data: Record<string, any>) {
/**
* 导入规则数据
* @param neId 网元ID
* @param data 表单数据对象
* @returns object
*/
export function importRuleData(data: Record<string, any>) {
return request({
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/file/import`,
method: 'post',
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/file/import?neId=${data.neId}&filePath=${data.filePath}&fileType=${data.fileType}`,
method: 'put',
data,
timeout: 60_000,
});
@@ -52,28 +51,38 @@ export async function listRules(query: Record<string, any>) {
code: result.code,
msg: result.msg,
};
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
const rows = parseObjLineToHump(result.data.data);
data.total = rows.length;
data.rows = rows;
if (result.code === RESULT_CODE_SUCCESS) {
if (result.data?.status) {
return {
code: RESULT_CODE_ERROR,
msg: result.data?.cause,
rows: result.data,
};
}
if (Array.isArray(result.data.data)) {
const rows = parseObjLineToHump(result.data.data);
data.total = rows.length;
data.rows = rows;
}
}
// 模拟数据
// data.rows = [
// {
// msisdn: '12307550237',
// pccRules: 'internet',
// rfsp: 0,
// sessRules: 'internet',
// },
// {
// msisdn: '12307550238',
// pccRules: 'internet|ims_sig',
// rfsp: 0,
// sessRules: 'internet|ims_sig',
// },
// ];
// {
// "hdrEnrich": "dnn",
// "imsi": "160990100000003",
// "msisdn": "86755900011",
// "pccRules": "internet|ims_sig",
// "qosAudio": "qos_audio",
// "qosVideo": "qos_video",
// "rfsp": 0,
// "sar": "def_sar",
// "sessRules": "internet|ims_sig",
// "uePolicy": "uep_001"
// }
// ]
return data;
}
@@ -124,7 +133,7 @@ export async function updateRule(data: Record<string, any>) {
*/
export async function batchUpdateRule(data: Record<string, any>) {
const result = await request({
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/${data.num}?neId=${data.neId}`,
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/${data.num}?neId=${data.neId}`,
method: 'put',
data: data,
});
@@ -168,7 +177,7 @@ export async function addRule(data: Record<string, any>) {
*/
export async function batchAddRule(data: Record<string, any>) {
const result = await request({
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/${data.num}?neId=${data.neId}`,
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/${data.num}?neId=${data.neId}`,
method: 'post',
data: data,
});
@@ -202,7 +211,7 @@ export function delRule(neId: string, data: Record<string, any>) {
*/
export async function batchDelRule(data: Record<string, any>) {
return request({
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/${data.num}?neId=${data.neId}&imsi=${data.imsi}`,
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/${data.num}?neId=${data.neId}&imsi=${data.imsi}`,
method: 'delete',
});
}

View File

@@ -8,7 +8,6 @@ import { parseObjLineToHump } from '@/utils/parse-utils';
* @returns object
*/
export async function listgoldData(query: Record<string, any>) {
console.log(query);
let totalSQL = 'select count(*) as total from gold_kpi where 1=1 ';
let rowsSQL =
'SELECT gold_kpi.*,kpi_title.en_title FROM gold_kpi LEFT JOIN kpi_title on gold_kpi.kpi_id=kpi_title.kpi_id where 1=1 ';
@@ -29,10 +28,10 @@ export async function listgoldData(query: Record<string, any>) {
let sortSql = ' order by ';
if (query.sortField) {
sortSql += ` ${query.sortField} `;
}else{
} else {
sortSql += ` start_time `;
}
if (query.sortOrder === 'asc') {
sortSql += ' asc ';
} else {
@@ -76,3 +75,41 @@ export async function listgoldData(query: Record<string, any>) {
}
return result;
}
/**
* 查询黄金指标数据
* @param query 查询参数
* @returns object
*/
export async function goldData(query: Record<string, any>) {
const result = await request({
url: `/ne/kpi/data`,
method: 'get',
params: {
neType: query.neType[0],
neId: query.neType[1],
startTime: query.beginTime,
endTime: query.endTime,
interval: query.particle,
},
timeout: 60_000,
});
return result;
}
/**
* 查询网元可用黄金指标
* @param neType 网元类型
* @returns object
*/
export async function getGoldTitleByNE(neType: string) {
// 发起请求
const result = await request({
url: `/ne/kpi/title`,
method: 'get',
params: { neType },
});
// 解析数据
return result;
}

View File

@@ -199,6 +199,18 @@ export function chunkUpload(data: FormData) {
});
}
/**
* 转存上传文件到静态资源
* @returns object
*/
export function transferStaticFile(data: Record<string, any>) {
return request({
url: `/file/transferStaticFile`,
method: 'post',
data,
});
}
/**
* 上传切片文件并发送文件到网元端
* @param neType 网元类型, UPF

29
src/api/tool/neFile.ts Normal file
View File

@@ -0,0 +1,29 @@
import { request } from '@/plugins/http-fetch';
/**
* 查询文件列表列表
* @param query 查询参数
* @returns object
*/
export function listNeFiles(query: Record<string, any>) {
return request({
url: '/ne/action/files',
method: 'get',
params: query,
});
}
/**
* 获取文件
* @param query 查询参数
* @returns object
*/
export function getNeFile(query: Record<string, any>) {
return request({
url: '/ne/action/pullFile',
method: 'get',
params: query,
responseType: 'blob',
timeout: 180_000,
});
}

View File

@@ -1,69 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="1361px" height="609px" viewBox="0 0 1361 609" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>Ant-Design-Pro</title>
<desc>mask-and-vue3 By TsMask</desc>
<defs></defs>
<g id="Ant-Design-Pro" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="mask-and-vue3" transform="translate(-79.000000, -82.000000)">
<g id="Group-21" transform="translate(77.000000, 73.000000)">
<g id="Group-18" opacity="0.8" transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
<ellipse id="Oval-11" fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479" ry="21.766008"></ellipse>
<ellipse id="Oval-3" fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913" ry="5.21330997"></ellipse>
<path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z" id="Oval-3-Copy" fill="#CFDAE6" opacity="0.45"></path>
<path d="M64.2775582,33.1704963 L119.185836,16.5654915" id="Path-12" stroke="#CFDAE6" stroke-width="1.73913043" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M42.1431708,26.5002681 L7.71190162,14.5640702" id="Path-16" stroke="#E0B4B7" stroke-width="0.702678964" opacity="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<path d="M63.9262187,33.521561 L43.6721326,69.3250951" id="Path-15" stroke="#BACAD9" stroke-width="0.702678964" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<g id="Group-17" transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)" fill="#CFDAE6">
<ellipse id="Oval-4" opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653" ry="9.12768076"></ellipse>
<path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z" id="Oval-4" transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
</g>
</g>
<g id="Group-14" transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471" ry="29.1402439"></ellipse>
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275" ry="21.5853659"></ellipse>
<ellipse id="Oval-2-Copy" stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902" ry="23.7439024"></ellipse>
<ellipse id="Oval-2" fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137" ry="10.7926829"></ellipse>
<path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z" id="Oval-2" fill="#BACAD9"></path>
<g id="Group-9" opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
<ellipse id="Oval-2-Copy-2" cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
<path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z" id="Oval-2-Copy-2" transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
</g>
<ellipse id="Oval-10" fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy-2" fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy" fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275" ry="2.15853659"></ellipse>
<path d="M28.9985381,29.9671598 L171.151018,132.876024" id="Path-11" stroke="#CFDAE6" opacity="0.8"></path>
</g>
<g id="Group-10" opacity="0.799999952" transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
<ellipse id="Oval-7" stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407" ry="11.2941176"></ellipse>
<g id="Group-12" transform="translate(34.596774, 23.111111)" fill="#BACAD9">
<ellipse id="Oval-7" opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627" ry="8.55614973"></ellipse>
<path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z" id="Oval-7"></path>
</g>
<path d="M34.6597385,24.809694 L5.71666084,4.76878945" id="Path-2" stroke="#CFDAE6" stroke-width="0.941176471"></path>
<ellipse id="Oval" stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186" ry="3.29411765"></ellipse>
<ellipse id="Oval-Copy" fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
<path d="M34.6312443,39.2922712 L5.06366663,59.785082" id="Path-10" stroke="#CFDAE6" stroke-width="0.941176471"></path>
</g>
<g id="Group-19" opacity="0.33" transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
<g id="Group-17" transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)" fill="#BACAD9">
<circle id="Oval-4" opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
<path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z" id="Oval-4" transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
</g>
<circle id="Oval-5-Copy-6" fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
<path d="M143.5,88.8126685 L155.070501,17.6038544" id="Path-17" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M17.5,37.3333333 L127.466252,97.6449735" id="Path-18" stroke="#BACAD9" stroke-width="1.16666667"></path>
<polyline id="Path-19" stroke="#CFDAE6" stroke-width="1.16666667" points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
<path d="M159.833333,99.7453842 L195.416667,89.25" id="Path-20" stroke="#E0B4B7" stroke-width="1.16666667" opacity="0.6"></path>
<path d="M205.333333,82.1372105 L238.719406,36.1666667" id="Path-24" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M266.723424,132.231988 L207.083333,90.4166667" id="Path-25" stroke="#CFDAE6" stroke-width="1.16666667"></path>
<circle id="Oval-5" fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
<circle id="Oval-5-Copy-3" fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
<circle id="Oval-5-Copy-2" fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
<circle id="Oval-5-Copy-4" fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
<circle id="Oval-5-Copy-5" fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

View File

@@ -0,0 +1,392 @@
<template>
<div>
<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 } from '@antv/g6';
const props = defineProps({
/**
* 图表主题
*
* 'dark' | 'light'
*/
theme: {
type: String,
default: 'light', // 'dark' | 'light'
},
/**宽度默认100% */
width: {
type: String,
default: '100%',
},
/**高度 */
height: {
type: String,
default: '500px',
},
});
const chartGraphG6Dom = ref<HTMLElement | undefined>(undefined);
const data = {
nodes: [
// 0 基站
{
id: '0',
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',
x: 450,
y: 450,
label: 'DM',
labelCfg: {
position: 'center',
},
style: {
fill: '#00b050',
stroke: '#00b050',
lineWidth: 1,
},
},
// 2 O&M
{
id: '2',
x: 50,
y: 450,
label: 'O&M',
},
// 100 EMS
{
id: '100',
label: 'EMS',
comboId: 'combo-ems',
x: 300,
y: 450,
},
// 190 UPF
{
id: '190',
comboId: 'combo-upf',
x: 300,
y: 350,
label: 'UPF',
labelCfg: {
position: 'center',
},
style: {
fill: '#d580ff',
stroke: '#d580ff',
lineWidth: 1,
},
},
// EP-IMS
{
id: '110',
comboId: 'combo-ims',
x: 600,
y: 350,
label: 'IMS',
labelCfg: {
position: 'center',
},
style: {
fill: '#ed7d31',
stroke: '#ed7d31',
lineWidth: 1,
},
},
// 5GC控制面
{
id: '170',
label: 'NSSF',
comboId: 'combo-5gc',
x: 300,
y: 50,
},
{
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: [
{
id: '0-5gc',
source: '0',
target: 'combo-5gc',
},
{
id: '0-upf',
source: '0',
target: 'combo-upf',
},
{
id: 'upf-1',
source: 'combo-upf',
target: '1',
},
{
id: 'ems-2',
source: 'combo-ems',
target: '2',
},
{
id: '170-120',
source: '170',
target: '120',
},
{
id: '130-120',
source: '130',
target: '120',
},
{
id: '140-120',
source: '140',
target: '120',
},
{
id: '140-180',
source: '140',
target: '180',
},
{
id: '120-180',
source: '120',
target: '180',
},
{
id: '130-180',
source: '130',
target: '180',
},
{
id: '140-150',
source: '140',
target: '150',
},
{
id: '140-110',
source: '140',
target: '110',
},
{
id: '120-150',
source: '120',
target: '150',
data: {},
},
{
id: '150-180',
source: '150',
target: '180',
data: {},
},
{
id: '150-160',
source: '150',
target: '160',
},
{
id: '160-120',
source: '160',
target: '120',
},
{
id: '160-180',
source: '160',
target: '180',
},
{
id: '160-110',
source: '160',
target: '110',
},
{
id: '150-190',
source: '150',
target: '190',
},
{
id: 'upf-ims',
source: 'combo-upf',
target: 'combo-ims',
},
{
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: [
{
id: 'combo-5gc',
data: {
text: '5GC控制面',
},
},
{
id: 'combo-upf',
data: {
keyShape: {
opacity: 0.8,
padding: [20, 20, 20, 20],
radius: 4,
lineWidth: 1,
stroke: '#d580ff',
},
},
},
{
id: 'combo-ims',
data: {},
},
{
id: 'combo-ems',
data: {},
},
],
};
let graph: any = null;
/**初始化渲染图表 */
function initChart() {
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: ['drag-canvas', 'zoom-canvas', 'drag-node'], // 允许拖拽画布、放缩画布、拖拽节点
},
// 全局节点 矩形
defaultNode: {
type: 'rect',
size: [80, 40],
style: {
fill: '#fff',
lineWidth: 1,
radius: 8,
},
labelCfg: {},
},
// 全局边 三次贝塞尔曲线
defaultEdge: {
type: 'polyline',
style: {
offset: 20, // 拐弯处距离节点最小距离
radius: 4, // 拐弯处的圆角弧度,若不设置则为直角
lineWidth: 1,
stroke: '#87e8de',
},
},
// 全局框节点 矩形
defaultCombo: {
type: 'rect', // Combo 类型
size: [40, 40],
// ... 其他配置
style: {
lineWidth: 1,
},
},
});
graph.data(data); // 加载数据
graph.render(); // 渲染
}
onMounted(() => {
nextTick(() => {
initChart();
});
});
onBeforeUnmount(() => {});
</script>
<style lang="less" scoped></style>

View File

@@ -69,7 +69,7 @@ const props = defineProps({
const seriesStyle = [
{
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
{
offset: 0,
color: 'rgba(22, 119, 255, .5)',
},
@@ -203,7 +203,7 @@ function initChart() {
},
},
grid: props.option.grid || { left: '8%', right: '8%', bottom: '20%' },
legend: {
legend: props.option.legend || {
right: 10,
itemWidth: 8,
textStyle: {

View File

@@ -1,6 +1,6 @@
import { computed } from 'vue';
import { computed, onBeforeMount } from 'vue';
import { useI18n } from 'vue-i18n';
import { localSet } from '@/utils/cache-local-utils';
import { localGet, localSet } from '@/utils/cache-local-utils';
import { CACHE_LOCAL_I18N } from '@/constants/cache-keys-constants';
export default function useLocale() {
@@ -20,7 +20,7 @@ export default function useLocale() {
};
// 可选的语言
const optionsLocale= [
const optionsLocale = [
{
value: 'zh_CN',
label: '中文',
@@ -31,6 +31,14 @@ export default function useLocale() {
},
];
// 挂载前根据默认语言在设置一次
onBeforeMount(() => {
const localI18n = localGet(CACHE_LOCAL_I18N);
if (localI18n) {
i18n.locale.value = localI18n;
}
});
return {
optionsLocale,
currentLocale,

View File

@@ -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",
}
},
@@ -376,7 +376,7 @@ export default {
fileName: 'File Name',
version: 'Version',
versionPlease: 'Version number cannot be empty',
updateTime: 'Uploaded',
updateTime: 'Uploaded Time',
description: 'Description',
deleteTip: 'Are you sure to delete the data item with software [{fileName}]?',
downloadTip: 'Are you sure to download the data item with software [{fileName}]?',
@@ -402,12 +402,13 @@ 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',
neType: 'NE Type',
fileName: 'File Name',
createTime: 'Uploaded',
createTime: 'Uploaded Time',
comment: 'File Description',
updateComment: 'License Description',
updateCommentPlease: 'Please enter a license description',
@@ -455,7 +456,7 @@ export default {
addItemOk: "Add Index as {num} Record Succeeded",
addItemErr: "Record addition failure",
requireUn: "{display} input value is of unknown type",
requireString: "The {display} parameter value does not make sense.",
requireString: "The {display} parameter value is invalid.",
requireInt: "{display} Parameter value not in reasonable range {filter}",
requireIpv4: "{display} not a legitimate IPV4 address",
requireIpv6: "{display} Not a legitimate IPV6 address.",
@@ -464,7 +465,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: {
@@ -477,7 +478,7 @@ export default {
import: 'Import',
loadDataConfirm: 'Are you sure you want to reload the data?',
loadData: 'Load Data',
loadDataTip: 'Successfully fetched updated data: {num} entries, the system is internally updating the data. You can click reset to refresh the data list after the loading is finished, please don it repeat click to get update!!!!',
loadDataTip: 'Successfully fetched load data: {num} entries, the system is internally updating the data. You can click reset to refresh the data list after the loading is finished, please don it repeat click to get update!!!!',
startIMSI: 'Start IMSI',
batchAddText: 'Batch Add',
batchDelText: 'Batch Delete',
@@ -500,8 +501,8 @@ export default {
exportConfirm: 'Are you sure to export all signed user data?',
import: 'Import',
loadDataConfirm: 'Are you sure you want to reload the data?',
loadData: 'Updated Data',
loadDataTip: 'Successfully fetched updated 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!!!!',
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',
batchAddText: 'Batch Add',
@@ -536,15 +537,17 @@ export default {
addTitle: 'Adding Policy Control Information',
updateTitle: '{imsi} Policy control information',
startIMSI: 'Start IMSI',
batchAddText: 'Batch Addition',
batchDelText: 'Batch Deletion',
batchUpdateText: 'Batch Update',
batchAddText: 'Batch Add',
batchDelText: 'Batch Delete',
batchUpdateText: 'Batch Modify',
batchNum: 'Number of batches',
imsiTip: 'IMSI=MCC+MNC+MSIN',
imsiTip1: 'MCC=Mobile Country Code, consisting of three digits.',
imsiTip2: 'MNC = Mobile Network Number, consisting of two digits',
imsiTip3: 'MSIN = Mobile Subscriber Identification Number, consisting of 10 equal digits.',
delSure:'Are you sure you want to delete the user with IMSI number: {imsi}?',
uploadFileOk: 'File Upload Successful',
uploadFileErr: 'File Upload Failed',
},
base5G: {
neType: 'AMF Object',
@@ -628,6 +631,13 @@ export default {
value:'Value',
startTime:'Start Time',
endTime:'End Time',
particle: 'Particle Ssize',
timeFrame: 'Time Range',
nullTip:'There are no statistical data within this time range',
kpiTitle:'KPI Statistics Chart',
allData:'Complete Data',
makeLine:'Statistical Chart',
time:'Time',
}
},
traceManage: {
@@ -670,7 +680,7 @@ export default {
execTimeout: 'Execution timeout',
execSuccess: 'Execution completed',
execFailed: 'Execution failed',
execBreak: 'break execution',
execBreak: 'Break Execution',
noFileName: 'Invalid file name',
execUPFStartA: 'Executed, please stop capturing packets according to the situation',
execUPFStart: 'Execution successful, please stop capturing packets according to the situation',
@@ -739,7 +749,7 @@ export default {
alarmTitle:'Title',
clearUser:'Clear User',
clearType:'Clear Type',
ackState: 'Ack Status',
ackState: 'Alarm Status',
ackUser:'Ack User',
alarmSeq:'Sequence Number',
objectName:'Object Name',
@@ -808,7 +818,21 @@ export default {
alarmInfo:'Alarm Content',
eventTime:'Generation Time',
logTime:'Record Time'
}
},
neFile: {
neType:'NE Type',
neTypePlease:'Please select NE Type',
nePath: "Directory Path",
fileMode: "File Mode",
owner: "Owner",
group: "Group",
size: "Size",
modifiedTime: "Modified Time",
fileName: "File Name",
downTip: "Confirm the download file name is [{fileName}] File?",
downTipErr: "Failed to get file",
dirCd: "Enter Dir",
},
},
monitor: {
session: {
@@ -1107,6 +1131,10 @@ export default {
sysOfficialUrlOpen: 'Open the official website.',
sysOfficialUrlInstruction: 'Official website link address',
sysOfficialUrlInstruction1: 'start, if you need to hide do not jump to fill in the',
i18n: "Internationalization Switch",
i18nOpen: "Display Switch",
i18nDefault: "Default Languages",
i18nInstruction: 'Whether to display the internationalization switch and set the system default language',
},
role:{
allScopeOptions:'All data permissions',
@@ -1276,6 +1304,7 @@ export default {
dictData:'Dictionary Data',
reload:'Refresh Cache',
mark:'Dictionary Description',
colorSelect:'Color Picker',
},
dictData: {
dictType: "Dictionary name",
@@ -1309,7 +1338,7 @@ export default {
cmdNoTip: "{num} no optional command operation",
require: "Mandatory parameter: {num}",
requireUn: "{display} input value is of unknown type",
requireString: "The {display} parameter value does not make sense.",
requireString: "The {display} parameter value is invalid.",
requireInt: "{display} Parameter value not in reasonable range {filter}",
requireIpv4: "{display} not a legitimate IPV4 address",
requireIpv6: "{display} Not a legitimate IPV6 address.",
@@ -1323,6 +1352,7 @@ export default {
cmdAwait: "Waiting for a command to be sent",
uploadFileTip: 'Are you sure you want to upload the file?',
uploadFileOk: 'File Upload Successful',
uploadFileErr: 'File Upload Failed',
omcOperate:{
noOMC: "No OMC network elements",
},

View File

@@ -170,11 +170,11 @@ export default {
settings: "个人设置",
},
tabs: {
reload: "刷新当前",
reload: "刷新当前标签",
more: "更多选项",
closeCurrent: "关闭当前",
closeOther: "关闭其他",
closeAll: "关闭全部",
closeCurrent: "关闭当前标签",
closeOther: "关闭其他标签",
closeAll: "关闭全部标签",
}
},
@@ -402,6 +402,7 @@ export default {
createTime:'创建时间',
onlyAble:'只支持上传文件格式 {fileText}',
nullData:'暂无网元列表数据',
nullVersion:'当前网元无可回退版本',
},
license: {
neTypePlease: '选择网元类型',
@@ -476,8 +477,8 @@ export default {
exportConfirm: '确认导出全部鉴权用户数据吗?',
import: '导入',
loadDataConfirm: '确认要重新加载数据吗?',
loadData: '更新数据',
loadDataTip: '成功获取更新数据:{num}条,系统内部正在进行数据更新。加载结束后可点击重置刷新数据列表,请勿重复点击获取更新!!!',
loadData: '加载数据',
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新。加载结束后可点击重置刷新数据列表,请勿重复点击获取更新!!!',
startIMSI: '起始IMSI',
batchAddText: '批量新增',
batchDelText: '批量删除',
@@ -500,8 +501,8 @@ export default {
exportConfirm: '确认导出全部签约用户数据吗?',
import: '导入',
loadDataConfirm: '确认要重新加载数据吗?',
loadData: '更新数据',
loadDataTip: '成功获取更新数据:{num}条,系统内部正在进行数据更新。加载结束后可点击重置刷新数据列表,请勿重复点击获取更新!!!',
loadData: '加载数据',
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新。加载结束后可点击重置刷新数据列表,请勿重复点击获取更新!!!',
numAdd: '放号个数',
numDel: '删除个数',
batchAddText: '批量新增',
@@ -545,6 +546,8 @@ export default {
imsiTip2: 'MNC=移动网络号,由两位数字组成',
imsiTip3: 'MSIN=移动客户识别码采用等长10位数字构成',
delSure:'确认删除IMSI编号为: {imsi} 的用户吗?',
uploadFileOk: '文件上传成功',
uploadFileErr: '文件上传失败',
},
base5G: {
neType: 'AMF网元对象',
@@ -627,7 +630,14 @@ export default {
enTitle:'黄金指标项',
value:'值',
startTime:'开始时间',
endTime:'结束时间'
endTime:'结束时间',
particle:'颗粒度',
timeFrame:'时间范围',
nullTip:'此时间范围内没有统计数据',
kpiTitle:'KPI统计图表',
allData:'完整统计数据',
makeLine:'统计图',
time:'时间',
}
},
traceManage: {
@@ -808,7 +818,21 @@ export default {
alarmInfo:'告警内容',
eventTime:'告警产生时间',
logTime:'记录时间'
}
},
neFile: {
neType:'网元',
neTypePlease:'请选择网元',
nePath: "目录路径",
fileMode: "文件权限",
owner: "所属用户",
group: "所属组",
size: "文件大小",
modifiedTime: "修改时间",
fileName: "文件名称",
downTip: "确认下载文件名为 【{fileName}】 文件?",
downTipErr: "文件获取失败",
dirCd: "进入目录",
},
},
monitor: {
session: {
@@ -1107,6 +1131,10 @@ export default {
sysOfficialUrlOpen: '打开官网',
sysOfficialUrlInstruction: '官网链接地址',
sysOfficialUrlInstruction1: '开头,如需隐藏不跳转填写',
i18n: "国际化切换",
i18nOpen: "显示切换",
i18nDefault: "默认语言",
i18nInstruction: '是否显示国际化切换,设置系统默认语言',
},
role:{
allScopeOptions:'全部数据权限',
@@ -1276,6 +1304,7 @@ export default {
dictData:'字典数据',
reload:'刷新缓存',
mark:'字典说明',
colorSelect:'取色器',
},
dictData: {
dictType: "字典名称",
@@ -1323,6 +1352,7 @@ export default {
cmdAwait: "等待发送命令",
uploadFileTip: '确认要上传文件吗?',
uploadFileOk: '文件上传成功',
uploadFileErr: '文件上传失败',
omcOperate:{
noOMC: "暂无OMC网元",
},

View File

@@ -23,7 +23,8 @@ import { getServerTime } from '@/api';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { onMounted } from 'vue';
import { parseDateToStr } from '@/utils/date-utils';
const { t } = useI18n();
import { parseUrlPath } from '@/plugins/file-static-url';
const { t, currentLocale } = useI18n();
const routerStore = useRouterStore();
const tabsStore = useTabsStore();
const appStore = useAppStore();
@@ -121,6 +122,35 @@ function fnComponentSetName(component: any, to: any) {
// 清空导航栏标签
tabsStore.clear();
// LOGO地址
const logoUrl = computed(() => {
let url =
appStore.logoType === 'brand'
? parseUrlPath(appStore.filePathBrand)
: parseUrlPath(appStore.filePathIcon);
if (url.indexOf('{language}') === -1) {
return url;
}
// 语言参数替换
const local = currentLocale.value;
const lang = local.split('_')[0];
return url.replace('{language}', lang);
});
// 系统使用手册地址
const helpDocUrl = computed(() => {
let url = parseUrlPath(appStore.helpDoc);
if (url.indexOf('{language}') === -1) {
return url;
}
// 语言参数替换
const local = currentLocale.value;
const lang = local.split('_')[0];
return url.replace('{language}', lang);
});
/**系统使用手册跳转 */
function fnClickHelpDoc(language?: string) {
const routeData = router.resolve({ name: 'HelpDoc' });
@@ -220,7 +250,7 @@ document.addEventListener('visibilitychange', function () {
>
<img
class="logo-icon"
:src="appStore.getLOGOIcon"
:src="logoUrl"
:alt="appStore.appName"
:title="appStore.appName"
/>
@@ -231,7 +261,7 @@ document.addEventListener('visibilitychange', function () {
<template v-if="appStore.logoType === 'brand'">
<img
class="logo-brand"
:src="appStore.getLOGOBrand"
:src="logoUrl"
:alt="appStore.appName"
:title="appStore.appName"
/>
@@ -282,7 +312,7 @@ document.addEventListener('visibilitychange', function () {
<template #footerRender="{ width }">
<footer class="footer">
<div class="footer-fixed" :style="{ width }">
<div>
<div style="flex: 1;">
<span>{{ appStore.copyright }}</span>
</div>
<a-space direction="horizontal" :size="8">
@@ -296,12 +326,7 @@ document.addEventListener('visibilitychange', function () {
>
{{ t('loayouts.basic.officialUrl') }}
</a-button>
<a-button
type="link"
size="small"
@click="fnClickHelpDoc()"
v-if="appStore.getHelpDoc !== '#'"
>
<a-button type="link" size="small" @click="fnClickHelpDoc()">
{{ t('loayouts.basic.helpDoc') }}
</a-button>
</a-space>
@@ -339,7 +364,7 @@ document.addEventListener('visibilitychange', function () {
.footer {
z-index: 16;
margin: 0px;
width: auto;
width: auto;
margin-top: 52px;
&-fixed {
position: fixed;
@@ -354,6 +379,7 @@ document.addEventListener('visibilitychange', function () {
background: #fff;
box-shadow: 0 1px 4px #0015291f;
transition: background 0.3s, width 0.2s;
height: 32px;
}
& #serverTimeDom {

View File

@@ -6,7 +6,6 @@ import useI18n from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import useUserStore from '@/store/modules/user';
import useAlarmStore from '@/store/modules/alarm';
import { sessionGet } from '@/utils/cache-session-utils';
const { isFullscreen, toggle } = useFullscreen();
const { t, changeLocale, optionsLocale } = useI18n();
const userStore = useUserStore();
@@ -43,9 +42,6 @@ function fnClickHelpDoc(language?: string) {
window.open(href, '_blank');
}
// 兼容旧前端可改配置文件
const i18nDisable = sessionGet('i18nDisable') === 'false';
/**改变多语言 */
function fnChangeLocale(e: any) {
changeLocale(e.key);
@@ -66,7 +62,7 @@ function fnChangeLocale(e: any) {
</template>
</a-button>
<a-tooltip placement="bottom" v-if="appStore.getHelpDoc !== '#'">
<a-tooltip placement="bottom">
<template #title>{{ t('loayouts.rightContent.helpDoc') }}</template>
<a-button type="text" @click="fnClickHelpDoc()">
<template #icon>
@@ -88,7 +84,7 @@ function fnChangeLocale(e: any) {
<a-dropdown
placement="bottom"
:trigger="['click', 'hover']"
v-if="!i18nDisable"
v-if="appStore.i18nOpen"
>
<a-button size="small" type="default">
{{ t('i18n') }}

View File

@@ -0,0 +1,21 @@
import { sessionGet } from '@/utils/cache-session-utils';
import { validHttp } from '@/utils/regular-utils';
/**
* 解析资源文件绝对路径 http
* @param path 服务端资源文件
* @returns
*/
export function parseUrlPath(path: string) {
if (!path || path === '#') {
return '#';
}
if (validHttp(path)) {
return path;
}
// 兼容旧前端可改配置文件
const baseUrl = import.meta.env.PROD
? sessionGet('baseUrl') || import.meta.env.VITE_API_BASE_URL
: import.meta.env.VITE_API_BASE_URL;
return `${baseUrl}${path}?r=${Math.random().toFixed(2)}`;
}

View File

@@ -1,10 +1,9 @@
import { getSysConf } from '@/api';
import defaultLOGOIcon from '@/assets/logo_icon.png';
import defaultLOGOBrand from '@/assets/logo_brand.png';
import { CACHE_LOCAL_I18N } from '@/constants/cache-keys-constants';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { validHttp } from '@/utils/regular-utils';
import { parseUrlPath } from '@/plugins/file-static-url';
import { localGet, localSet } from '@/utils/cache-local-utils';
import { defineStore } from 'pinia';
import { sessionGet } from '@/utils/cache-session-utils';
/**应用参数类型 */
type AppStore = {
@@ -14,6 +13,12 @@ type AppStore = {
appCode: string;
/**应用版本 */
appVersion: string;
/**服务版本 */
version: string,
buildTime: string,
// 序列号
serialNum: string,
/**应用版权声明 */
copyright: string;
/**LOGO显示类型 */
@@ -29,6 +34,10 @@ type AppStore = {
helpDoc: string;
/**官方网址 */
officialUrl: string;
/**国际化切换 */
i18nOpen: boolean;
/**国际化默认语言 */
i18nDefault: string;
};
const useAppStore = defineStore('app', {
@@ -36,6 +45,10 @@ const useAppStore = defineStore('app', {
appName: import.meta.env.VITE_APP_NAME,
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: '',
@@ -44,85 +57,10 @@ const useAppStore = defineStore('app', {
loginBackground: '',
helpDoc: '',
officialUrl: '',
i18nOpen: true,
i18nDefault: 'en_US',
}),
getters: {
/**
* 获取正确LOGO地址-icon
* @param state 内部属性不用传入
* @returns LOGO地址url
*/
getLOGOIcon(state) {
const path = state.filePathIcon;
if (!path || path === '#') {
return defaultLOGOIcon;
}
if (validHttp(path)) {
return path;
}
// 兼容旧前端可改配置文件
const baseUrl = import.meta.env.PROD
? sessionGet('baseUrl') || import.meta.env.VITE_API_BASE_URL
: import.meta.env.VITE_API_BASE_URL;
return `${baseUrl}${path}`;
},
/**
* 获取正确LOGO地址-brand
* @param state 内部属性不用传入
* @returns LOGO地址url
*/
getLOGOBrand(state) {
const path = state.filePathBrand;
if (!path || path === '#') {
return defaultLOGOBrand;
}
if (validHttp(path)) {
return path;
}
// 兼容旧前端可改配置文件
const baseUrl = import.meta.env.PROD
? sessionGet('baseUrl') || import.meta.env.VITE_API_BASE_URL
: import.meta.env.VITE_API_BASE_URL;
return `${baseUrl}${path}`;
},
/**
* 获取正确登录背景地址
* @param state 内部属性不用传入
* @returns 背景地址url
*/
getLoginBackground(state) {
const path = state.loginBackground;
if (!path || path === '#') {
return '#';
}
if (validHttp(path)) {
return path;
}
// 兼容旧前端可改配置文件
const baseUrl = import.meta.env.PROD
? sessionGet('baseUrl') || import.meta.env.VITE_API_BASE_URL
: import.meta.env.VITE_API_BASE_URL;
return `${baseUrl}${path}`;
},
/**
* 获取正确使用手册地址
* @param state 内部属性不用传入
* @returns 手册地址url
*/
getHelpDoc(state) {
const path = state.helpDoc;
if (!path || path === '#') {
return '#';
}
if (validHttp(path)) {
return path;
}
// 兼容旧前端可改配置文件
const baseUrl = import.meta.env.PROD
? sessionGet('baseUrl') || import.meta.env.VITE_API_BASE_URL
: import.meta.env.VITE_API_BASE_URL;
return `${baseUrl}${path}`;
},
},
getters: {},
actions: {
/**设置网页标题 */
setTitle(title?: string) {
@@ -136,36 +74,43 @@ const useAppStore = defineStore('app', {
setCopyright(text: string) {
this.copyright = text;
},
/**设置LOGO */
setLOGO(type: 'brand' | 'icon', filePath: string) {
this.logoType = type;
if (type === 'brand') {
this.filePathBrand = filePath;
}
if (type === 'icon') {
this.filePathIcon = filePath;
}
},
// 获取系统配置信息
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;
this.filePathIcon = res.data.filePathIcon;
this.filePathBrand = res.data.filePathBrand;
// 修改html内容-小图当作favicon.ico
if (this.logoType) {
if (this.logoType) {
const iconDom = document.querySelector("link[rel~='icon']");
if (iconDom) {
iconDom.setAttribute('href', this.getLOGOIcon);
let url = parseUrlPath(this.filePathIcon);
// 语言参数替换
if (url.indexOf('{language}') !== -1) {
const local = localGet(CACHE_LOCAL_I18N) || 'en_US';
const lang = local.split('_')[0];
url = url.replace('{language}', lang);
}
iconDom.setAttribute('href', url);
}
}
this.registerUser = res.data.registerUser === 'true';
this.loginBackground = res.data.loginBackground;
this.helpDoc = res.data.helpDoc;
this.officialUrl = res.data.officialUrl;
this.i18nOpen = res.data.i18nOpen === 'true';
this.i18nDefault = res.data.i18nDefault;
// 切换禁用时,设置默认语言
const localI18n = localGet(CACHE_LOCAL_I18N);
if (localI18n == null || (!this.i18nOpen && this.i18nDefault)) {
localSet(CACHE_LOCAL_I18N, this.i18nDefault);
}
}
return res;
},

View File

@@ -10,7 +10,7 @@ type NeInfo = {
/**网元列表 */
neList: Record<string, any>[];
/**级联options树结构 */
neCascaderOtions: Record<string, any>[];
neCascaderOptions: Record<string, any>[];
/**选择器单级父类型 */
neSelectOtions: Record<string, any>[];
/**跟踪接口列表 */
@@ -22,7 +22,7 @@ type NeInfo = {
const useNeInfoStore = defineStore('neinfo', {
state: (): NeInfo => ({
neList: [],
neCascaderOtions: [],
neCascaderOptions: [],
neSelectOtions: [],
traceInterfaceList: [],
perMeasurementList: [],
@@ -33,8 +33,8 @@ const useNeInfoStore = defineStore('neinfo', {
* @param state 内部属性不用传入
* @returns 级联options
*/
getNeCascaderOtions(state) {
return state.neCascaderOtions;
getNeCascaderOptions(state) {
return state.neCascaderOptions;
},
/**
* 选择器单级父类型
@@ -70,7 +70,7 @@ const useNeInfoStore = defineStore('neinfo', {
'neName',
'neId'
);
this.neCascaderOtions = options;
this.neCascaderOptions = options;
// 转选择器单级父类型
this.neSelectOtions = options.map(item => item);

View File

@@ -20,7 +20,7 @@ import { regExpIPv4, regExpIPv6 } from '@/utils/regular-utils';
const { t } = useI18n();
/**网元参数 */
let neCascaderOtions = ref<Record<string, any>[]>([]);
let neCascaderOptions = ref<Record<string, any>[]>([]);
/**网元类型选择 type,id */
let neTypeSelect = ref<string[]>(['', '']);
@@ -1068,18 +1068,25 @@ onMounted(() => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
// 过滤不可用的网元
neCascaderOtions.value = useNeInfoStore().getNeCascaderOtions.filter(
neCascaderOptions.value = useNeInfoStore().getNeCascaderOptions.filter(
(item: any) => {
return !['OMC'].includes(item.value);
}
);
if (neCascaderOptions.value.length === 0) {
message.warning({
content: t('common.noData'),
duration: 2,
});
return;
}
// 默认选择AMF
const item = neCascaderOtions.value.find(s => s.value === 'AMF');
const item = neCascaderOptions.value.find(s => s.value === 'AMF');
if (item && item.children) {
const info = item.children[0];
neTypeSelect.value = [info.neType, info.neId];
} else {
const info = neCascaderOtions.value[0].children[0];
const info = neCascaderOptions.value[0]?.children[0];
neTypeSelect.value = [info.neType, info.neId];
}
fnGetParamConfigTopTab();
@@ -1110,7 +1117,7 @@ onMounted(() => {
>
<a-cascader
v-model:value="neTypeSelect"
:options="neCascaderOtions"
:options="neCascaderOptions"
:allow-clear="false"
@change="fnGetParamConfigTopTab"
/>

View File

@@ -19,7 +19,7 @@ import { SizeType } from 'ant-design-vue/lib/config-provider';
const { t } = useI18n();
/**网元参数 */
let neCascaderOtions = ref<Record<string, any>[]>([]);
let neCascaderOptions = ref<Record<string, any>[]>([]);
/**网元类型选择 type,id */
let neTypeSelect = ref<string[]>(['', '']);
@@ -989,18 +989,25 @@ onMounted(() => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
// 过滤不可用的网元
neCascaderOtions.value = useNeInfoStore().getNeCascaderOtions.filter(
neCascaderOptions.value = useNeInfoStore().getNeCascaderOptions.filter(
(item: any) => {
return !['OMC'].includes(item.value);
}
);
if (neCascaderOptions.value.length === 0) {
message.warning({
content: t('common.noData'),
duration: 2,
});
return;
}
// 默认选择AMF
const item = neCascaderOtions.value.find(s => s.value === 'AMF');
const item = neCascaderOptions.value.find(s => s.value === 'AMF');
if (item && item.children) {
const info = item.children[0];
neTypeSelect.value = [info.neType, info.neId];
} else {
const info = neCascaderOtions.value[0].children[0];
const info = neCascaderOptions.value[0].children[0];
neTypeSelect.value = [info.neType, info.neId];
}
fnGetParamConfigTopTab();
@@ -1035,7 +1042,7 @@ onMounted(() => {
>
<a-cascader
v-model:value="neTypeSelect"
:options="neCascaderOtions"
:options="neCascaderOptions"
:allow-clear="false"
@change="fnGetParamConfigTopTab"
/>

View File

@@ -16,10 +16,11 @@ import { toRaw } from 'vue';
import { regExpIPv4, regExpIPv6 } from '@/utils/regular-utils';
import { SizeType } from 'ant-design-vue/lib/config-provider';
import { DataNode } from 'ant-design-vue/lib/tree';
const neInfoStore = useNeInfoStore();
const { t } = useI18n();
/**网元参数 */
let neCascaderOtions = ref<Record<string, any>[]>([]);
let neCascaderOptions = ref<Record<string, any>[]>([]);
/**网元类型选择 type,id */
let neTypeSelect = ref<string[]>(['', '']);
@@ -1011,35 +1012,40 @@ function fnModalCancel() {
onMounted(() => {
// 获取网元网元列表
useNeInfoStore()
.fnNelist()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
// 过滤不可用的网元
neCascaderOtions.value = useNeInfoStore().getNeCascaderOtions.filter(
(item: any) => {
return !['OMC'].includes(item.value);
}
);
// 默认选择AMF
const item = neCascaderOtions.value.find(s => s.value === 'AMF');
if (item && item.children) {
const info = item.children[0];
neTypeSelect.value = [info.neType, info.neId];
} else {
const info = neCascaderOtions.value[0].children[0];
neTypeSelect.value = [info.neType, info.neId];
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);
}
fnGetParamConfigTopTab();
);
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 === 'AMF');
if (item && item.children) {
const info = item.children[0];
neTypeSelect.value = [info.neType, info.neId];
} else {
const info = neCascaderOptions.value[0].children[0];
neTypeSelect.value = [info.neType, info.neId];
}
fnGetParamConfigTopTab();
}
});
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
});
});
</script>
@@ -1058,7 +1064,7 @@ onMounted(() => {
<a-form-item name="neId ">
<a-cascader
v-model:value="neTypeSelect"
:options="neCascaderOtions"
:options="neCascaderOptions"
:allow-clear="false"
@change="fnGetParamConfigTopTab"
/>

View File

@@ -16,10 +16,11 @@ import useNeInfoStore from '@/store/modules/neinfo';
import { regExpIPv4, regExpIPv6 } from '@/utils/regular-utils';
import { SizeType } from 'ant-design-vue/lib/config-provider';
import { DataNode } from 'ant-design-vue/lib/tree';
const neInfoStore = useNeInfoStore();
const { t } = useI18n();
/**网元参数 */
let neCascaderOtions = ref<Record<string, any>[]>([]);
let neCascaderOptions = ref<Record<string, any>[]>([]);
/**网元类型选择 type,id */
let neTypeSelect = ref<string[]>(['', '']);
@@ -315,17 +316,21 @@ watch(
function arrayEdit(rowIndex: Record<string, any>) {
const item = arrayState.data.find((s: any) => s.key === rowIndex.value);
if (!item) return;
const from = arrayEditInit(item, arrayState.dataRule);
// 处理信息
const row: Record<string, any> = {};
for (const v of item.record) {
for (const v of from.record) {
if (Array.isArray(v.array)) {
continue;
}
row[v.name] = Object.assign({}, v);
}
modalState.from = row;
modalState.type = 'arrayEdit';
modalState.title = `${treeState.selectNode.topDisplay} ${item.title}`;
modalState.key = item.key;
modalState.data = item.record.filter((v: any) => !Array.isArray(v.array));
modalState.title = `${treeState.selectNode.topDisplay} ${from.title}`;
modalState.key = from.key;
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
modalState.visible = true;
// 关闭嵌套
@@ -636,17 +641,21 @@ function arrayChildExpandClose() {
function arrayChildEdit(rowIndex: Record<string, any>) {
const item = arrayChildState.data.find((s: any) => s.key === rowIndex.value);
if (!item) return;
const from = arrayEditInit(item, arrayChildState.dataRule);
// 处理信息
const row: Record<string, any> = {};
for (const v of item.record) {
for (const v of from.record) {
if (Array.isArray(v.array)) {
continue;
}
row[v.name] = Object.assign({}, v);
}
modalState.from = row;
modalState.type = 'arrayChildEdit';
modalState.title = `${arrayChildState.title} ${item.title}`;
modalState.key = item.key;
modalState.data = item.record.filter((v: any) => !Array.isArray(v.array));
modalState.title = `${arrayChildState.title} ${from.title}`;
modalState.key = from.key;
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
modalState.visible = true;
}
@@ -813,87 +822,78 @@ function arrayChildAddOk(from: Record<string, any>) {
});
}
/**多列表编辑行数据初始化 */
function arrayEditInit(data: Record<string, any>, dataRule: any) {
const dataFrom = data.record;
const ruleFrom = Object.assign({}, JSON.parse(JSON.stringify(dataRule)));
for (const row of ruleFrom.record) {
// 子嵌套的不初始
if (row.array) {
row.value = [];
continue;
}
// 查找项的值
const item = dataFrom.find((s: any) => s.name === row.name);
if (!item) {
continue;
}
// 可选的
row.optional = 'true';
// 根据规则类型转值
if (['enum', 'int'].includes(row.type)) {
row.value = Number(item.value);
} else if ('bool' === row.type) {
row.value = Boolean(item.value);
} else {
row.value = item.value;
}
}
ruleFrom.key = data.key;
ruleFrom.title = data.title;
return ruleFrom;
}
/**多列表新增行数据初始化 */
function arrayAddInit(data: any[], dataRule: any) {
const len = data.length;
let lastItme: Record<string, any> = {};
let newIndex = 0;
// 无数据时生成临时记录
if (len === 0) {
lastItme = dataRule;
} else {
lastItme = data[len - 1];
// 有数据时取得最后的index
let dataLastIndex = 0;
if (data.length !== 0) {
const lastFrom = Object.assign({}, JSON.parse(JSON.stringify(data.at(-1))));
if (lastFrom.record.length > 0) {
dataLastIndex = parseInt(lastFrom.key);
dataLastIndex += 1;
}
}
const from = Object.assign({}, JSON.parse(JSON.stringify(lastItme)));
for (const row of from.record) {
const value = row.value;
const ruleFrom = Object.assign({}, JSON.parse(JSON.stringify(dataRule)));
for (const row of ruleFrom.record) {
// 子嵌套的不初始
if (row.array) {
row.value = [];
continue;
}
// 可选的
row.optional = 'true';
// index值
if (row.name === 'index') {
if (value !== '') {
newIndex = parseInt(value) + 1;
}
let newIndex = dataLastIndex !== 0 ? dataLastIndex : parseInt(row.value);
if (isNaN(newIndex)) {
newIndex = 0;
}
row.value = newIndex;
from.key = newIndex;
from.title = `Index-${newIndex}`;
ruleFrom.key = newIndex;
ruleFrom.title = `Index-${newIndex}`;
continue;
}
// 子嵌套的不初始
if (row.array) {
row.value = null;
continue;
// 根据规则类型转值
if (['enum', 'int'].includes(row.type)) {
row.value = Number(row.value);
}
const type = row.type;
const filter = row.filter;
switch (type) {
case 'int':
if (filter && filter.indexOf('~') !== -1) {
const filterArr = filter.split('~');
const minInt = parseInt(filterArr[0]);
row.value = minInt;
} else {
row.value = 0;
}
break;
case 'enum':
if (filter && filter.indexOf('{') === 0) {
let filterJson: Record<string, any> = {};
try {
filterJson = JSON.parse(filter); //string---json
} catch (error) {
console.error(error);
}
row.value = Object.keys(filterJson)[0];
} else {
row.value = '0';
}
break;
case 'bool':
if (filter && filter.indexOf('{') === 0) {
let filterJson: Record<string, any> = {};
try {
filterJson = JSON.parse(filter); //string---json
} catch (error) {
console.error(error);
}
row.value = Object.keys(filterJson)[0];
} else {
row.value = false;
}
break;
case 'ipv4':
case 'ipv6':
case 'regex':
default:
row.value = '';
break;
if ('bool' === row.type) {
row.value = Boolean(row.value);
}
}
return from;
return ruleFrom;
}
/**规则校验 */
@@ -908,7 +908,7 @@ function ruleVerification(row: Record<string, any>): (string | boolean)[] {
if (row.array) {
return result;
}
// 可选的同时没有值不检查
if (row.optional === 'true' && !value) {
return result;
@@ -1093,42 +1093,47 @@ function fnModalCancel() {
onMounted(() => {
// 获取网元网元列表
useNeInfoStore()
.fnNelist()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
// 过滤不可用的网元
neCascaderOtions.value = useNeInfoStore().getNeCascaderOtions.filter(
(item: any) => {
return !['OMC'].includes(item.value);
}
);
// 默认选择AMF
const item = neCascaderOtions.value.find(s => s.value === 'UPF');
if (item && item.children) {
const info = item.children[0];
neTypeSelect.value = [info.neType, info.neId];
} else {
const info = neCascaderOtions.value[0].children[0];
neTypeSelect.value = [info.neType, info.neId];
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);
}
fnGetParamConfigTopTab();
);
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];
neTypeSelect.value = [info.neType, info.neId];
} else {
const info = neCascaderOptions.value[0].children[0];
neTypeSelect.value = [info.neType, info.neId];
}
fnGetParamConfigTopTab();
}
});
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
});
});
</script>
<template>
<PageContainer>
<a-row :gutter="16">
<a-col :span="6">
<a-col :lg="6" :md="6" :xs="24" style="margin-bottom: 24px">
<!-- 网元类型 -->
<a-card
size="small"
@@ -1140,7 +1145,7 @@ onMounted(() => {
<a-form-item name="neId ">
<a-cascader
v-model:value="neTypeSelect"
:options="neCascaderOtions"
:options="neCascaderOptions"
:allow-clear="false"
@change="fnGetParamConfigTopTab"
/>
@@ -1156,7 +1161,7 @@ onMounted(() => {
</a-form>
</a-card>
</a-col>
<a-col :span="18">
<a-col :lg="18" :md="18" :xs="24">
<a-card
size="small"
:bordered="false"
@@ -1309,19 +1314,20 @@ onMounted(() => {
v-model:expanded-row-keys="arrayState.arrayChildExpandKeys"
>
<!-- 多列新增操作 -->
<template #title v-if="treeState.selectNode.method !== 'get'">
<template #title>
<a-space :size="16" align="center">
<a-button
type="primary"
@click.prevent="arrayAdd()"
size="small"
v-if="treeState.selectNode.method !== 'get'"
>
<template #icon> <PlusOutlined /> </template>
{{ t('common.addText') }}
</a-button>
<TableColumnsDnd
type="ghost"
:columns="[...arrayState.columns]"
:columns="treeState.selectNode.method === 'get' ? [...arrayState.columns.filter((s:any)=>s.key !== 'index')] : arrayState.columns"
v-model:columns-dnd="arrayState.columnsDnd"
></TableColumnsDnd>
</a-space>
@@ -1366,7 +1372,7 @@ onMounted(() => {
</a-button>
<!--特殊字段拓展显示-->
<span v-if="text.name === 'dnnList'">
({{ text.value.map((s: any) => s.dnn).join() }})
({{ text.value?.map((s: any) => s.dnn).join() }})
</span>
</template>

View File

@@ -11,6 +11,7 @@ import useI18n from '@/hooks/useI18n';
import useNeInfoStore from '@/store/modules/neinfo';
import { FileType } from 'ant-design-vue/lib/upload/interface';
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
const neInfoStore = useNeInfoStore();
const { t } = useI18n();
/**查询参数 */
@@ -129,7 +130,7 @@ function fnTableSize({ key }: MenuInfo) {
function fnGetList(pageNum?: number) {
if (tableState.loading) return;
tableState.loading = true;
if(pageNum){
if (pageNum) {
queryParams.pageNum = pageNum;
}
listLicense(toRaw(queryParams)).then(res => {
@@ -267,7 +268,10 @@ function fnBeforeUploadFile(file: FileType) {
const fileName = file.name;
const suff = fileName.substring(fileName.lastIndexOf('.'));
if (!['.ini'].includes(suff)) {
message.error(t('views.configManage.softwareManage.onlyAble',{fileText:'(.ini)'}), 3);
message.error(
t('views.configManage.softwareManage.onlyAble', { fileText: '(.ini)' }),
3
);
return false;
}
@@ -287,19 +291,17 @@ function fnUploadFile(up: UploadRequestOption) {
onMounted(() => {
// 获取网元网元列表
useNeInfoStore()
.fnNelist()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
// 获取列表数据
fnGetList();
} else {
message.warning({
content: t('views.configManage.softwareManage.nullData'),
duration: 2,
});
}
});
neInfoStore.fnNelist().then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
// 获取列表数据
fnGetList();
} else {
message.warning({
content: t('views.configManage.softwareManage.nullData'),
duration: 2,
});
}
});
});
</script>
@@ -320,7 +322,7 @@ onMounted(() => {
>
<a-auto-complete
v-model:value="queryParams.neType"
:options="useNeInfoStore().getNeSelectOtions"
:options="neInfoStore.getNeSelectOtions"
allow-clear
:placeholder="t('views.configManage.license.neTypePlease')"
/>
@@ -425,10 +427,7 @@ onMounted(() => {
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form
name="modalStateFrom"
layout="horizontal"
>
<a-form name="modalStateFrom" layout="horizontal">
<a-form-item
:label="t('views.configManage.license.neType')"
name="neType"
@@ -436,7 +435,7 @@ onMounted(() => {
>
<a-cascader
v-model:value="modalState.from.neType"
:options="useNeInfoStore().getNeCascaderOtions"
:options="useNeInfoStore().getNeCascaderOptions"
:allow-clear="false"
:placeholder="t('views.configManage.license.neTypePlease')"
/>

View File

@@ -453,26 +453,31 @@ 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) {
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,
});
}
})
.finally(() => {
tableState.loading = false;
});
},
});
}
@@ -902,7 +907,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') }}

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,
@@ -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,
@@ -448,7 +468,7 @@ const modalStateFrom = Form.useForm(
],
comment: [
{
required: true,
required: false,
message: t('views.configManage.softwareManage.updateCommentPlease'),
},
],
@@ -512,8 +532,10 @@ function fnModalOk() {
*/
function fnModalCancel() {
modalState.visibleByEdit = false;
fileModalState.visibleByBack = false;
modalState.visibleByHistory = false;
modalStateFrom.resetFields();
fileModalStateBackFrom.resetFields();
}
/**
@@ -523,16 +545,111 @@ 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;
const fileName = file.name;
const suff = fileName.substring(fileName.lastIndexOf('.'));
if (!['.deb', '.rpm'].includes(suff)) {
message.error(t('views.configManage.softwareManage.onlyAble',{fileText:'(.deb、.rpm)'}), 3);
message.error(
t('views.configManage.softwareManage.onlyAble', {
fileText: '(.deb、.rpm)',
}),
3
);
return false;
}
// 根据给定的软件名取版本号 ims-r2.2312.x-ub22.deb
const nameArr = fileName.split('.');
if (nameArr.length > 3) {
modalState.from.version = nameArr[1];
}
return true;
}
@@ -552,7 +669,10 @@ function fnBeforeUploadCms(file: FileType) {
const fileName = file.name;
const suff = fileName.substring(fileName.lastIndexOf('.'));
if (!['.cms'].includes(suff)) {
message.error(t('views.configManage.softwareManage.onlyAble',{fileText:'(.cms)'}), 3);
message.error(
t('views.configManage.softwareManage.onlyAble', { fileText: '(.cms)' }),
3
);
return false;
}
return true;
@@ -645,6 +765,10 @@ onMounted(() => {
<template #icon><HistoryOutlined /></template>
{{ t('views.configManage.softwareManage.historyBtn') }}
</a-button>
<a-button type="dashed" @click.prevent="fnModalVisibleByBack()">
<template #icon> <UndoOutlined /></template>
{{ t('views.configManage.softwareManage.backBtn') }}
</a-button>
</a-space>
</template>
@@ -750,10 +874,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>
@@ -867,7 +987,7 @@ onMounted(() => {
@cancel="fnModalCancel"
/>
<!-- 文件框 下发激活回退 -->
<!-- 文件框 下发激活 -->
<a-modal
width="600px"
:keyboard="false"
@@ -898,6 +1018,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

@@ -18,11 +18,12 @@ import {
import useI18n from '@/hooks/useI18n';
import useDictStore from '@/store/modules/dict';
import saveAs from 'file-saver';
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
import { writeSheet } from '@/utils/execl-utils';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { readLoalXlsx } from '@/utils/execl-utils';
const { getDict } = useDictStore();
const { t,currentLocale } = useI18n();
const { t, currentLocale } = useI18n();
/**字典数据 */
let dict: {
@@ -41,6 +42,9 @@ let dict: {
activeAlarmSeverity: [],
});
/**表格字段列排序 */
let tableColumnsDnd = ref<ColumnsType>([]);
/**记录开始结束时间 */
let queryRangePicker = ref<[string, string]>(['', '']);
@@ -233,9 +237,6 @@ let tableColumns: ColumnsType = [
},
];
/**帮助文档表格字段列 */
let alarmTableColumns: ColumnsType = [
{
@@ -246,19 +247,19 @@ let alarmTableColumns: ColumnsType = [
},
{
title: t('views.faultManage.activeAlarm.locationInfo'),
dataIndex: 'alarmInfo',
dataIndex: 'alarmInfo',
align: 'center',
width: 5,
},
{
title: t('views.faultManage.activeAlarm.addInfo'),
dataIndex:'helpInfo',
dataIndex: 'helpInfo',
align: 'center',
width: 8,
},
{
title: t('views.faultManage.activeAlarm.alarmType'),
dataIndex: 'alarmType',
dataIndex: 'alarmType',
align: 'center',
width: 5,
},
@@ -294,7 +295,7 @@ let alarmTableColumns: ColumnsType = [
},
{
title: t('views.faultManage.activeAlarm.objectNf'),
dataIndex:'objNf',
dataIndex: 'objNf',
align: 'center',
width: 2,
},
@@ -416,14 +417,12 @@ function fnModalVisibleByVive(row: Record<string, any>) {
modalState.visibleByView = true;
}
/** 告警帮助文档详细信息 */
function fnModalVisibleBy(code: string) {
modalState.helpShowView = false;
const lang=currentLocale.value.split('_')[0];
modalState.title=t('views.faultManage.activeAlarm.helpFile');
readLoalXlsx(lang,code)
const lang = currentLocale.value.split('_')[0];
modalState.title = t('views.faultManage.activeAlarm.helpFile');
readLoalXlsx(lang, code)
.then(res => {
alarmTableState.data = res;
modalState.helpShowView = true;
@@ -438,7 +437,7 @@ function fnModalVisibleBy(code: string) {
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,
@@ -661,7 +660,7 @@ function fnModalCancel() {
modalState.visibleByEdit = false;
modalState.visibleByView = false;
modalState.visibleByShowSet = false;
modalState.helpShowView=false;
modalState.helpShowView = false;
}
/**查询列表, pageNum初始页数 */
@@ -850,7 +849,7 @@ onMounted(() => {
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<div class="button-container">
<a-button
type="primary"
@click.prevent="fnCancelConfirm()"
@@ -890,12 +889,12 @@ onMounted(() => {
<template #icon> <export-outlined /> </template>
{{ t('views.faultManage.activeAlarm.exportAll') }}
</a-button>
</a-space>
</div>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<div class="button-container">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
@@ -935,14 +934,18 @@ onMounted(() => {
</template>
</a-dropdown>
</a-tooltip>
</a-space>
<TableColumnsDnd
:columns="tableColumns"
v-model:columns-dnd="tableColumnsDnd"
></TableColumnsDnd>
</div>
</template>
<!-- 表格列表 -->
<a-table
class="table"
row-key="id"
:columns="tableColumns"
:columns="tableColumnsDnd"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
@@ -1006,7 +1009,6 @@ onMounted(() => {
</a-table>
</a-card>
<!-- 帮助文档 -->
<a-modal
width="100%"
@@ -1027,7 +1029,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>
@@ -1573,6 +1575,9 @@ onMounted(() => {
.table :deep(.ant-pagination) {
padding: 0 24px;
}
</style>
<style lang="less">
.full-modal {
.ant-modal {
max-width: 100%;

View File

@@ -340,7 +340,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,

View File

@@ -14,6 +14,8 @@ import { PieChart } from 'echarts/charts';
import { LabelLayout } from 'echarts/features';
import { useRoute } from 'vue-router';
import useAppStore from '@/store/modules/app';
import useDictStore from '@/store/modules/dict';
const { getDict } = useDictStore();
const appStore = useAppStore();
const route = useRoute();
const { t } = useI18n();
@@ -28,6 +30,18 @@ echarts.use([
LabelLayout,
]);
/**字典数据 */
/**用户性别字典 */
let indexColor = ref<DictType[]>([
{ label: 'normal', value: 'normal', elTagType: '', elTagClass: '#91cc75' },
{
label: 'abnormal',
value: 'abnormal',
elTagType: '',
elTagClass: '#ee6666',
},
]);
/**表格字段列 */
//customRender(){} ----单元格处理
let tableColumns: ColumnsType = [
@@ -92,10 +106,10 @@ let tableState: TabeStateType = reactive({
/**表格状态 */
let nfInfo: any = reactive({
obj: 'OMC',
version: '2.2311.8',
version: appStore.version,
status: t('views.index.normal'),
number: '',
outTimeDate: '',
serialNum: appStore.serialNum,
});
/**表格状态类型 */
@@ -141,15 +155,15 @@ let pronInfo: nfStateType = reactive({
});
/**查询网元状态列表 */
function fnGetList() {
function fnGetList(one: boolean) {
if (tableState.loading) return;
tableState.loading = true;
one && (tableState.loading = true);
listMain().then(res => {
tableState.data = res;
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++;
@@ -157,9 +171,16 @@ function fnGetList() {
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: {
@@ -174,6 +195,10 @@ function fnGetList() {
orient: 'vertical',
left: 'left',
},
color: [
'' + indexColor.value[0]['elTagClass'],
'' + indexColor.value[1]['elTagClass'],
],
series: [
{
name: t('views.index.realNeStatus'),
@@ -205,125 +230,6 @@ function fnGetList() {
});
}
/**點擊網元名 */
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 = () => {
@@ -392,9 +298,15 @@ function fnLocale() {
}
onMounted(() => {
getDict('index_status').then(res => {
if (res.length > 0) {
indexColor.value = res;
}
});
//console.log(indexColor)
fnLocale();
fnGetList();
timer = setInterval(fnGetList, 10000); // 每隔10秒执行一次
fnGetList(true);
timer = setInterval(() => fnGetList(false), 10000); // 每隔10秒执行一次
});
// 在组件卸载之前清除定时器
@@ -462,9 +374,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>
@@ -490,6 +400,9 @@ onBeforeUnmount(() => {
nfInfo.obj
}}</a-descriptions-item>
<template v-if="nfInfo.obj === 'OMC'">
<a-descriptions-item :label="t('views.index.serialNum')">{{
nfInfo.serialNum
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.versionNum')">{{
nfInfo.version
}}</a-descriptions-item>
@@ -499,7 +412,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

@@ -0,0 +1,347 @@
<script setup lang="ts">
import { reactive, ref, onMounted, toRaw } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { SizeType } from 'ant-design-vue/lib/config-provider';
import { ColumnsType } from 'ant-design-vue/lib/table';
import { parseDateToStr } from '@/utils/date-utils';
import { getNeFile, listNeFiles } from '@/api/tool/neFile';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useNeInfoStore from '@/store/modules/neinfo';
import useI18n from '@/hooks/useI18n';
import { Modal, message } from 'ant-design-vue/lib';
import saveAs from 'file-saver';
const neInfoStore = useNeInfoStore();
const { t } = useI18n();
/**网元参数 */
let neType = ref([]);
/**下载触发等待 */
let loading = ref(false);
/**访问路径 */
let nePathArr = ref<string[]>([]);
/**查询参数 */
let queryParams = reactive({
/**网元类型 */
neType: '',
neId: '',
/**读取路径 */
path: '',
/**前缀过滤 */
search: '',
/**当前页数 */
pageNum: 1,
/**每页条数 */
pageSize: 20,
});
/**表格状态类型 */
type TabeStateType = {
/**加载等待 */
loading: boolean;
/**紧凑型 */
size: SizeType;
/**记录数据 */
data: object[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
size: 'small',
data: [],
});
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('views.logManage.neFile.fileMode'),
dataIndex: 'fileMode',
align: 'center',
width: 150,
},
{
title: t('views.logManage.neFile.owner'),
dataIndex: 'owner',
align: 'left',
width: 100,
},
{
title: t('views.logManage.neFile.group'),
dataIndex: 'group',
align: 'left',
width: 100,
},
{
title: t('views.logManage.neFile.size'),
dataIndex: 'size',
align: 'left',
width: 100,
},
{
title: t('views.logManage.neFile.modifiedTime'),
dataIndex: 'modifiedTime',
align: 'left',
customRender(opt) {
if (!opt.value) return '';
return parseDateToStr(opt.value * 1000);
},
width: 150,
},
{
title: t('views.logManage.neFile.fileName'),
dataIndex: 'fileName',
align: 'left',
},
{
title: t('common.operate'),
key: 'fileName',
align: 'center',
width: 100,
},
];
/**表格分页器参数 */
let tablePagination = reactive({
/**当前页数 */
current: 1,
/**每页条数 */
pageSize: 20,
/**默认的每页条数 */
defaultPageSize: 20,
/**指定每页可以显示多少条 */
pageSizeOptions: ['10', '20', '50', '100'],
/**只有一页时是否隐藏分页器 */
hideOnSinglePage: false,
/**是否可以快速跳转至某页 */
showQuickJumper: true,
/**是否可以改变 pageSize */
showSizeChanger: true,
/**数据总数 */
total: 0,
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
onChange: (page: number, pageSize: number) => {
tablePagination.current = page;
tablePagination.pageSize = pageSize;
queryParams.pageNum = page;
queryParams.pageSize = pageSize;
fnGetList();
},
});
/**信息文件下载 */
function fnDownloadFile(row: Record<string, any>) {
if (loading.value) return;
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.logManage.neFile.downTip', { fileName: row.fileName }),
onOk() {
loading.value = true;
const hide = message.loading(t('common.loading'), 0);
getNeFile({
neType: queryParams.neType,
neId: queryParams.neId,
path: queryParams.path,
fileName: row.fileName,
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', {
msg: t('common.downloadText'),
}),
duration: 2,
});
saveAs(res.data, `${row.fileName}`);
} else {
message.error({
content: t('views.logManage.neFile.downTipErr'),
duration: 2,
});
}
})
.finally(() => {
hide();
loading.value = false;
});
},
});
}
/**进入目录 */
function fnDirCD(dir: string, index?: number) {
if (index === undefined) {
nePathArr.value.push(dir);
queryParams.search = '';
fnGetList(1);
return;
}
if (index === 0) {
const neType = queryParams.neType;
if (neType === 'IMS') {
nePathArr.value = ['/var/log/ims'];
queryParams.search = '';
} else {
nePathArr.value = ['/var/log'];
queryParams.search = neType.toLowerCase();
}
fnGetList(1);
} else {
nePathArr.value = nePathArr.value.slice(0, index + 1);
queryParams.search = '';
fnGetList(1);
}
}
/**网元类型选择对应修改 */
function fnNeChange(keys: any, _: any) {
// 不是同类型时需要重新加载
if (Array.isArray(keys) && queryParams.neType !== keys[0]) {
const neType = keys[0];
queryParams.neType = neType;
queryParams.neId = keys[1];
if (neType === 'IMS') {
nePathArr.value = ['/var/log/ims'];
queryParams.search = '';
} else {
nePathArr.value = ['/var/log'];
queryParams.search = neType.toLowerCase();
}
fnGetList(1);
}
}
/**查询备份信息列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (queryParams.neId === '') {
message.warning({
content: t('views.logManage.neFile.neTypePlease'),
duration: 2,
});
return;
}
if (tableState.loading) return;
tableState.loading = true;
if (pageNum) {
queryParams.pageNum = pageNum;
}
queryParams.path = nePathArr.value.join('/');
listNeFiles(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
tablePagination.total = res.total;
tableState.data = res.rows;
}
tableState.loading = false;
});
}
onMounted(() => {
// 获取网元网元列表
neInfoStore.fnNelist().then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length === 0) {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
}
});
});
</script>
<template>
<PageContainer>
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="8" :md="12" :xs="24">
<a-form-item
:label="t('views.logManage.neFile.neType')"
name="neType"
style="margin-bottom: 0"
>
<a-cascader
v-model:value="neType"
:options="neInfoStore.getNeCascaderOptions"
@change="fnNeChange"
:allow-clear="false"
:placeholder="t('views.logManage.neFile.neTypePlease')"
:disabled="loading || tableState.loading"
/>
</a-form-item>
</a-col>
<a-col :lg="16" :md="18" :xs="24" v-if="nePathArr.length > 0">
<a-form-item
:label="t('views.logManage.neFile.nePath')"
name="configName"
style="margin-bottom: 0"
>
<a-breadcrumb>
<a-breadcrumb-item
v-for="(path, index) in nePathArr"
:key="path"
@click="fnDirCD(path, index)"
>
{{ path }}
</a-breadcrumb-item>
</a-breadcrumb>
</a-form-item>
</a-col>
</a-row>
</a-form>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
<template #icon><ReloadOutlined /></template>
</a-button>
</a-tooltip>
</a-space>
</template>
<!-- 表格列表 -->
<a-table
class="table"
row-key="fileName"
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: 800 }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'fileName'">
<a-space :size="8" align="center">
<a-button
type="link"
@click.prevent="fnDownloadFile(record)"
v-if="record.fileType === 'file'"
>
<template #icon><DownloadOutlined /></template>
{{ t('common.downloadText') }}
</a-button>
<a-button
type="link"
@click.prevent="fnDirCD(record.fileName)"
v-if="record.fileType === 'dir'"
>
<template #icon><FolderOutlined /></template>
{{ t('views.logManage.neFile.dirCd') }}
</a-button>
</a-space>
</template>
</template>
</a-table>
</a-card>
</PageContainer>
</template>
<style lang="less" scoped></style>

View File

@@ -8,8 +8,8 @@ import { useRouter, useRoute } from 'vue-router';
import useI18n from '@/hooks/useI18n';
import { toRaw } from 'vue';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { sessionGet } from '@/utils/cache-session-utils';
const { t, changeLocale, optionsLocale } = useI18n();
import { parseUrlPath } from '@/plugins/file-static-url';
const { t, changeLocale, optionsLocale, currentLocale } = useI18n();
const appStore = useAppStore();
const router = useRouter();
const route = useRoute();
@@ -87,9 +87,25 @@ function fnGetCaptcha() {
});
}
// LOGO地址
const logoUrl = computed(() => {
let url =
appStore.logoType === 'brand'
? parseUrlPath(appStore.filePathBrand)
: parseUrlPath(appStore.filePathIcon);
if (url.indexOf('{language}') === -1) {
return url;
}
// 语言参数替换
const local = currentLocale.value;
const lang = local.split('_')[0];
return url.replace('{language}', lang);
});
// 判断是否有背景地址
const calcBG = computed(() => {
const bgURL = appStore.getLoginBackground;
const bgURL = parseUrlPath(appStore.loginBackground);
if (bgURL && bgURL !== '#') {
return {
backgroundImage: `url(${bgURL})`,
@@ -116,9 +132,6 @@ onMounted(() => {
fnGetCaptcha();
});
// 兼容旧前端可改配置文件
const i18nDisable = sessionGet('i18nDisable') === 'false';
/**改变多语言 */
function fnChangeLocale(e: any) {
changeLocale(e.key);
@@ -137,19 +150,11 @@ function fnChangeLocale(e: any) {
<header class="header">
<template v-if="appStore.logoType === 'icon'">
<img
:src="appStore.getLOGOIcon"
class="logo-icon"
:alt="appStore.appName"
/>
<img :src="logoUrl" class="logo-icon" :alt="appStore.appName" />
<span class="title">{{ appStore.appName }}</span>
</template>
<template v-if="appStore.logoType === 'brand'">
<img
:src="appStore.getLOGOBrand"
class="logo-brand"
:alt="appStore.appName"
/>
<img :src="logoUrl" class="logo-brand" :alt="appStore.appName" />
</template>
</header>
@@ -274,7 +279,7 @@ function fnChangeLocale(e: any) {
justify="space-between"
align="middle"
style="margin-top: 18px"
v-if="!i18nDisable"
v-if="appStore.i18nOpen"
>
<a-col :offset="18" :span="6">
<a-dropdown :trigger="['click', 'hover']">
@@ -374,6 +379,7 @@ function fnChangeLocale(e: any) {
}
.logo-brand {
height: 48px;
width: 174px;
vertical-align: top;
border-style: none;
border-radius: 2px;

View File

@@ -10,6 +10,7 @@ import useI18n from '@/hooks/useI18n';
import { getMMLByNE, sendMMlByNE } from '@/api/mmlManage/neOperate';
import { uploadFileToNE } from '@/api/tool/file';
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
const neInfoStore = useNeInfoStore();
const { t } = useI18n();
/**网元参数 */
@@ -63,9 +64,30 @@ let state: StateType = reactive({
function fnTreeSelect(_: any, info: any) {
state.mmlSelect = info.node.dataRef;
state.from = {};
// 遍历判断是否有初始value
if (Array.isArray(state.mmlSelect.param)) {
for (const param of state.mmlSelect.param) {
if (typeof param.value !== 'undefined' && param.value != '') {
const valueType = param.type;
if (['enum', 'int'].includes(valueType)) {
state.from[param.name] = Number(param.value);
} else if (valueType === 'bool') {
state.from[param.name] = Boolean(param.value);
} else {
state.from[param.name] = param.value;
}
}
}
}
state.autoCompleteValue =
`${state.mmlSelect.operation} ${state.mmlSelect.object}`.trim();
// state.mmlCmdLog = '';
// 回到顶部
window.scrollTo({
top: 0,
behavior: 'smooth', // 平滑滚动到顶部,如果不需要平滑效果可以将此行代码删除
});
}
/**清空控制台日志 */
@@ -151,16 +173,17 @@ function fnSendMML() {
}
// 发送
state.mmlCmdLog += `${cmdArr.join('\n')}\n`;
state.from.sendLoading = true;
const [neType, neId] = state.neType;
sendMMlByNE(neType, neId, cmdArr).then(res => {
state.from.sendLoading = false;
if (res.code === RESULT_CODE_SUCCESS) {
let resultArr = res.data;
for (const str of resultArr) {
for (let i = 0; i < resultArr.length; i++) {
const str = resultArr[i];
const logStr = str.replace(/(\r\n|\n)/g, '\n');
state.mmlCmdLog += `${logStr}\n`;
const cmdStr = cmdArr[i];
state.mmlCmdLog += `${cmdStr}\n${logStr}\n`;
}
} else {
state.mmlCmdLog += `${res.msg}\n`;
@@ -184,7 +207,7 @@ function fnUpload(up: UploadRequestOption, name: string) {
message.success(t('views.mmlManage.uploadFileOk'), 3);
state.from[name] = res.data;
} else {
message.error(res.msg, 3);
message.error(t('views.mmlManage.uploadFileErr'), 3);
}
})
.finally(() => {
@@ -454,42 +477,47 @@ function fnAutoCompleteChange(value: any, _: any) {
onMounted(() => {
// 获取网元网元列表
useNeInfoStore()
.fnNelist()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
// 过滤不可用的网元
neCascaderOptions.value = useNeInfoStore().getNeCascaderOtions.filter(
(item: any) => {
return !['OMC'].includes(item.value);
}
);
// 默认选择AMF
const item = neCascaderOptions.value.find(s => s.value === 'AMF');
if (item && item.children) {
const info = item.children[0];
state.neType = [info.neType, info.neId];
} else {
const info = neCascaderOptions.value[0].children[0];
state.neType = [info.neType, info.neId];
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);
}
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 === 'AMF');
if (item && item.children) {
const info = item.children[0];
state.neType = [info.neType, info.neId];
} else {
const info = neCascaderOptions.value[0].children[0];
state.neType = [info.neType, info.neId];
}
fnGetList();
}
});
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
});
});
</script>
<template>
<PageContainer>
<a-row :gutter="16">
<a-col :span="6">
<a-col :lg="6" :md="6" :xs="24" style="margin-bottom: 24px">
<!-- 命令导航 -->
<a-card
size="small"
@@ -517,7 +545,7 @@ onMounted(() => {
</a-form>
</a-card>
</a-col>
<a-col :span="18">
<a-col :lg="18" :md="18" :xs="24">
<!-- 命令参数输入 -->
<a-card size="small" :bordered="false">
<template #title>
@@ -557,7 +585,7 @@ onMounted(() => {
</template>
<a-form
layout="vertical"
layout="horizontal"
autocomplete="off"
:validate-on-rule-change="false"
:validateTrigger="[]"
@@ -575,8 +603,15 @@ onMounted(() => {
<a-textarea :placeholder="t('common.ipnutPlease')" auto-size />
</a-auto-complete>
</a-form-item>
</a-form>
<template v-if="state.mmlSelect.operation && state.mmlSelect.param">
<template v-if="state.mmlSelect.operation && state.mmlSelect.param">
<a-form
layout="vertical"
autocomplete="off"
:validate-on-rule-change="false"
:validateTrigger="[]"
>
<a-divider orientation="left">
{{ t('views.mmlManage.cmdParamPanel') }}
</a-divider>
@@ -652,8 +687,8 @@ onMounted(() => {
</a-form-item>
</a-col>
</a-row>
</template>
</a-form>
</a-form>
</template>
</a-card>
<!-- 命令展示 -->

View File

@@ -60,9 +60,30 @@ let state: StateType = reactive({
function fnTreeSelect(_: any, info: any) {
state.mmlSelect = info.node.dataRef;
state.from = {};
// 遍历判断是否有初始value
if (Array.isArray(state.mmlSelect.param)) {
for (const param of state.mmlSelect.param) {
if (typeof param.value !== 'undefined' && param.value != '') {
const valueType = param.type;
if (['enum', 'int'].includes(valueType)) {
state.from[param.name] = Number(param.value);
} else if (valueType === 'bool') {
state.from[param.name] = Boolean(param.value);
} else {
state.from[param.name] = param.value;
}
}
}
}
state.autoCompleteValue =
`${state.mmlSelect.operation} ${state.mmlSelect.object}`.trim();
// state.mmlCmdLog = '';
// 回到顶部
window.scrollTo({
top: 0,
behavior: 'smooth', // 平滑滚动到顶部,如果不需要平滑效果可以将此行代码删除
});
}
/**清空控制台日志 */
@@ -148,15 +169,16 @@ function fnSendMML() {
}
// 发送
state.mmlCmdLog += `${cmdArr.join('\n')}\n`;
state.from.sendLoading = true;
sendMMlByOMC(state.neId, cmdArr).then(res => {
state.from.sendLoading = false;
if (res.code === RESULT_CODE_SUCCESS) {
let resultArr = res.data;
for (const str of resultArr) {
for (let i = 0; i < resultArr.length; i++) {
const str = resultArr[i];
const logStr = str.replace(/(\r\n|\n)/g, '\n');
state.mmlCmdLog += `${logStr}\n`;
const cmdStr = cmdArr[i];
state.mmlCmdLog += `${cmdStr}\n${logStr}\n`;
}
} else {
state.mmlCmdLog += `${res.msg}\n`;
@@ -179,7 +201,7 @@ function fnUpload(up: UploadRequestOption, name: string) {
message.success(t('views.mmlManage.uploadFileOk'), 3);
state.from[name] = res.data;
} else {
message.error(res.msg, 3);
message.error(t('views.mmlManage.uploadFileErr'), 3);
}
})
.finally(() => {
@@ -465,7 +487,7 @@ onMounted(() => {
<template>
<PageContainer>
<a-row :gutter="16">
<a-col :span="6">
<a-col :lg="6" :md="6" :xs="24" style="margin-bottom: 24px">
<!-- 命令导航 -->
<a-card
size="small"
@@ -491,7 +513,7 @@ onMounted(() => {
</a-form>
</a-card>
</a-col>
<a-col :span="18">
<a-col :lg="18" :md="18" :xs="24">
<!-- 命令参数输入 -->
<a-card size="small" :bordered="false">
<template #title>
@@ -531,7 +553,7 @@ onMounted(() => {
</template>
<a-form
layout="vertical"
layout="horizontal"
autocomplete="off"
:validate-on-rule-change="false"
:validateTrigger="[]"
@@ -549,8 +571,15 @@ onMounted(() => {
<a-textarea :placeholder="t('common.ipnutPlease')" auto-size />
</a-auto-complete>
</a-form-item>
</a-form>
<template v-if="state.mmlSelect.operation && state.mmlSelect.param">
<template v-if="state.mmlSelect.operation && state.mmlSelect.param">
<a-form
layout="vertical"
autocomplete="off"
:validate-on-rule-change="false"
:validateTrigger="[]"
>
<a-divider orientation="left">
{{ t('views.mmlManage.cmdParamPanel') }}
</a-divider>
@@ -626,8 +655,8 @@ onMounted(() => {
</a-form-item>
</a-col>
</a-row>
</template>
</a-form>
</a-form>
</template>
</a-card>
<!-- 命令展示 -->

View File

@@ -60,9 +60,30 @@ let state: StateType = reactive({
function fnTreeSelect(_: any, info: any) {
state.mmlSelect = info.node.dataRef;
state.from = {};
// 遍历判断是否有初始value
if (Array.isArray(state.mmlSelect.param)) {
for (const param of state.mmlSelect.param) {
if (typeof param.value !== 'undefined' && param.value != '') {
const valueType = param.type;
if (['enum', 'int'].includes(valueType)) {
state.from[param.name] = Number(param.value);
} else if (valueType === 'bool') {
state.from[param.name] = Boolean(param.value);
} else {
state.from[param.name] = param.value;
}
}
}
}
state.autoCompleteValue =
`${state.mmlSelect.operation} ${state.mmlSelect.object}`.trim();
// state.mmlCmdLog = '';
// 回到顶部
window.scrollTo({
top: 0,
behavior: 'smooth', // 平滑滚动到顶部,如果不需要平滑效果可以将此行代码删除
});
}
/**清空控制台日志 */
@@ -148,15 +169,16 @@ function fnSendMML() {
}
// 发送
state.mmlCmdLog += `${cmdArr.join('\n')}\n`;
state.from.sendLoading = true;
sendMMlByUDM(state.neId, cmdArr).then(res => {
state.from.sendLoading = false;
if (res.code === RESULT_CODE_SUCCESS) {
let resultArr = res.data;
for (const str of resultArr) {
for (let i = 0; i < resultArr.length; i++) {
const str = resultArr[i];
const logStr = str.replace(/(\r\n|\n)/g, '\n');
state.mmlCmdLog += `${logStr}\n`;
const cmdStr = cmdArr[i];
state.mmlCmdLog += `${cmdStr}\n${logStr}\n`;
}
} else {
state.mmlCmdLog += `${res.msg}\n`;
@@ -179,7 +201,7 @@ function fnUpload(up: UploadRequestOption, name: string) {
message.success(t('views.mmlManage.uploadFileOk'), 3);
state.from[name] = res.data;
} else {
message.error(res.msg, 3);
message.error(t('views.mmlManage.uploadFileErr'), 3);
}
})
.finally(() => {
@@ -445,6 +467,11 @@ onMounted(() => {
state.neId = arr[0].value;
// 获取列表数据
fnGetList();
} else {
message.warning({
content: t('views.mmlManage.udmOpesrate.noUDM'),
duration: 5,
});
}
} else {
message.warning({
@@ -465,7 +492,7 @@ onMounted(() => {
<template>
<PageContainer>
<a-row :gutter="16">
<a-col :span="6">
<a-col :lg="6" :md="6" :xs="24" style="margin-bottom: 24px">
<!-- 命令导航 -->
<a-card
size="small"
@@ -491,7 +518,7 @@ onMounted(() => {
</a-form>
</a-card>
</a-col>
<a-col :span="18">
<a-col :lg="18" :md="18" :xs="24">
<!-- 命令参数输入 -->
<a-card size="small" :bordered="false">
<template #title>
@@ -531,7 +558,7 @@ onMounted(() => {
</template>
<a-form
layout="vertical"
layout="horizontal"
autocomplete="off"
:validate-on-rule-change="false"
:validateTrigger="[]"
@@ -549,8 +576,15 @@ onMounted(() => {
<a-textarea :placeholder="t('common.ipnutPlease')" auto-size />
</a-auto-complete>
</a-form-item>
</a-form>
<template v-if="state.mmlSelect.operation && state.mmlSelect.param">
<template v-if="state.mmlSelect.operation && state.mmlSelect.param">
<a-form
layout="vertical"
autocomplete="off"
:validate-on-rule-change="false"
:validateTrigger="[]"
>
<a-divider orientation="left">
{{ t('views.mmlManage.cmdParamPanel') }}
</a-divider>
@@ -626,8 +660,8 @@ onMounted(() => {
</a-form-item>
</a-col>
</a-row>
</template>
</a-form>
</a-form>
</template>
</a-card>
<!-- 命令展示 -->

View File

@@ -645,7 +645,7 @@ onMounted(() => {
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<div class="button-container">
<a-button
type="primary"
@click.prevent="fnModalVisibleByEdit()"
@@ -689,12 +689,12 @@ onMounted(() => {
<template #icon><SyncOutlined /></template>
{{ t('views.monitor.job.resetQueue') }}
</a-button>
</a-space>
</div>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<div class="button-container">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
@@ -743,7 +743,7 @@ onMounted(() => {
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</div>
</template>
<!-- 表格列表 -->
@@ -866,7 +866,8 @@ onMounted(() => {
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.monitor.job.status')" name="status">
{{
dict.sysJobStatus.find(s => s.value === modalState.from.status)?.label
dict.sysJobStatus.find(s => s.value === modalState.from.status)
?.label
}}
</a-form-item>
</a-col>

View File

@@ -491,7 +491,7 @@ onMounted(() => {
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<div class="button-container">
<a-button type="default" @click.prevent="fnClose()">
<template #icon><CloseOutlined /></template>
{{ t('common.close') }}
@@ -523,12 +523,12 @@ onMounted(() => {
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
</a-space>
</div>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<div class="button-container">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
@@ -577,7 +577,7 @@ onMounted(() => {
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</div>
</template>
<!-- 表格列表 -->

View File

@@ -118,7 +118,7 @@ let tablePagination = {
showSizeChanger: true,
/**数据总数 */
total: 0,
showTotal: (total: number) => `总共 ${total}`,
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
onChange: (page: number, pageSize: number) => {
tablePagination.current = page;
tablePagination.pageSize = pageSize;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,656 @@
<script setup lang="ts">
import { reactive, onMounted, ref } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
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 { parseDateToStr } from '@/utils/date-utils';
import { Graph, Util } from '@antv/g6';
const { t } = useI18n();
/**图DOM节点实例对象 */
const graphG6Dom = ref<HTMLElement | undefined>(undefined);
/**图实例对象 */
const graphG6 = ref<any>(null);
/**图数据 */
const graphG6Data = reactive<Record<string, any>>({
nodes: [
// 0 基站
{
id: '0',
x: 50,
y: 150,
size: 48,
type: 'circle',
label: '基站',
labelCfg: {
position: 'bottom',
offset: 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 DN
{
id: '1',
x: 450,
y: 450,
label: 'DN',
labelCfg: {
position: 'center',
},
style: {
fill: '#00b050',
stroke: '#00b050',
lineWidth: 1,
},
},
// 2 O&M
{
id: '2',
x: 50,
y: 450,
label: 'O&M',
},
// 100 EMS
{
id: '100',
label: 'EMS',
comboId: 'combo-ems',
x: 300,
y: 450,
},
// 190 UPF
{
id: '190',
comboId: 'combo-upf',
x: 300,
y: 350,
label: 'UPF',
labelCfg: {
position: 'center',
},
style: {
fill: '#d580ff',
stroke: '#d580ff',
lineWidth: 1,
},
},
// EP-IMS
{
id: '110',
comboId: 'combo-ims',
x: 600,
y: 350,
label: 'IMS',
labelCfg: {
position: 'center',
},
style: {
fill: '#ed7d31',
stroke: '#ed7d31',
lineWidth: 1,
},
},
// 5GC控制面
{
id: '170',
label: 'NSSF',
comboId: 'combo-5gc',
x: 300,
y: 50,
},
{
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: [
{
id: '0-5gc',
source: '0',
target: 'combo-5gc',
},
{
id: '0-upf',
source: '0',
target: 'combo-upf',
},
{
id: 'upf-1',
source: 'combo-upf',
target: '1',
},
{
id: 'ems-2',
source: 'combo-ems',
target: '2',
},
{
id: '170-120',
source: '170',
target: '120',
},
{
id: '130-120',
source: '130',
target: '120',
},
{
id: '140-120',
source: '140',
target: '120',
},
{
id: '140-180',
source: '140',
target: '180',
},
{
id: '120-180',
source: '120',
target: '180',
},
{
id: '130-180',
source: '130',
target: '180',
},
{
id: '140-150',
source: '140',
target: '150',
},
{
id: '140-110',
source: '140',
target: '110',
},
{
id: '120-150',
source: '120',
target: '150',
data: {},
},
{
id: '150-180',
source: '150',
target: '180',
data: {},
},
{
id: '150-160',
source: '150',
target: '160',
},
{
id: '160-120',
source: '160',
target: '120',
},
{
id: '160-180',
source: '160',
target: '180',
},
{
id: '160-110',
source: '160',
target: '110',
},
{
id: '150-190',
source: '150',
target: '190',
},
{
id: 'upf-ims',
source: 'combo-upf',
target: 'combo-ims',
},
{
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: [
{
id: 'combo-5gc',
label: 'combo 5GC控制面',
},
{
id: 'combo-upf',
label: 'combo upf',
},
{
id: 'combo-ims',
label: 'combo ims',
},
{
id: 'combo-ems',
label: 'Combo ems',
},
],
});
/**图绑定事件 */
function graphEvent(graph: Graph) {
// 鼠标进入节点事件
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', (ev: any) => {
// 获得鼠标当前目标节点
const node = ev.item;
// 获取该节点的所有相关边
const edges = node.getEdges();
// 遍历相关边,将所有相关边提前,再将相关边的两个端点提前,以保证相关边的端点在边的上方常规效果
edges.forEach((edge: any) => {
edge.toFront();
edge.getSource().toFront();
edge.getTarget().toFront();
});
});
graph.on('node:mouseleave', (ev: any) => {
// 获得图上所有边实例
const edges = graph.getEdges();
// 遍历边,将所有边的层级放置在后方,以恢复原样
edges.forEach(edge => {
edge.toBack();
});
});
// 使用内置交互 create-edge创建边之后触发
graph.on('aftercreateedge', e => {
console.log(JSON.parse(JSON.stringify(graph.save())));
const edges = graph.save().edges as any;
// Util.processParallelEdges(edges);
graph.getEdges().forEach((edge, i) => {
graph.updateItem(edge, {
curveOffset: edges[i].curveOffset,
curvePosition: edges[i].curvePosition,
});
});
});
}
/**查询全部网元数据列表 */
function fnRanderGraph() {
if (!graphG6Dom.value) return;
const { clientHeight, clientWidth } = graphG6Dom.value;
const graph = new Graph({
container: graphG6Dom.value,
width: clientWidth,
height: clientHeight,
animate: true,
fitCenter: true,
modes: {
// default: [
// // 允许拖拽画布、放缩画布、拖拽节点
// 'drag-canvas',
// 'zoom-canvas',
// 'drag-node',
// ],
default: [
{
type: 'click-select',
selectEdge: true,
},
'drag-combo',
{
type: 'drag-node',
onlyChangeComboSize: true,
},
'drag-canvas',
'zoom-canvas',
'collapse-expand-combo',
{
type: 'create-edge',
trigger: 'click', // 'click' by default. options: 'drag', 'click'
},
],
edit: [
{
type: 'create-edge',
trigger: 'click', // 'click' by default. options: 'drag', 'click'
},
],
},
groupByTypes: false,
// layout: {
// type: 'dagre',
// sortByCombo: false,
// ranksep: 10,
// nodesep: 10,
// },
// 全局节点 矩形
defaultNode: {
type: 'rect',
size: [80, 40],
style: {
radius: 8,
},
},
// 全局边 三次贝塞尔曲线
defaultEdge: {
type: 'polyline',
style: {
offset: 20, // 拐弯处距离节点最小距离
radius: 4, // 拐弯处的圆角弧度,若不设置则为直角
lineWidth: 1,
},
},
// 全局框节点 矩形
defaultCombo: {
type: 'rect', // Combo 类型
size: [40, 40],
style: {
fillOpacity: 0.1,
},
},
});
graph.data(graphG6Data);
graph.render();
graphEvent(graph);
graphG6.value = graph;
}
/**查询网元状态 */
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 node = graphG6.value.findById(ne.neName);
console.log('查询网元状态', node);
graphG6.value.setItemState(node, 'neState', true);
}
}
}
/**查询全部网元数据列表 */
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: '2024-03-31',
refreshTime: '10:31:47',
sn: '13770707',
version: '2.2312.8',
};
nodes.push({
id: item.neName,
label: item.neName,
info: item,
labelCfg: {
position: 'bottom',
offset: 8,
style: {
fill: '#fff',
fontSize: 14,
},
},
size: 20,
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: 20,
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;
return true;
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
return false;
}
})
.then(hasNeList => {
if (!hasNeList) return;
if (refresh) {
graphG6.value.destroy();
}
fnGetState();
});
}
onMounted(() => {
// 获取网元列表
// fnGetList();
fnRanderGraph();
});
</script>
<template>
<PageContainer>
<a-card
:bordered="false"
:body-style="{ marginBottom: '24px' }"
size="small"
>
<!-- 插槽-卡片左侧侧 -->
<template #title>
<div class="button-container" style="margin-bottom: -12px">
<a-select size="small" value="view">
<a-select-option value="view" key="view"> 查看 </a-select-option>
<a-select-option value="edit" key="edit"> 编辑 </a-select-option>
</a-select>
<a-button type="primary">
<template #icon>
<PlusOutlined />
</template>
{{ t('common.addText') }}
</a-button>
<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>
<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-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
<template #icon><ReloadOutlined /></template>
</a-button>
</a-tooltip>
</template>
<div ref="graphG6Dom" class="chart"></div>
</a-card>
</PageContainer>
</template>
<style lang="less" scoped>
.chart {
width: 100%;
height: calc(100vh - 300px);
background-color: rgb(43, 47, 51);
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,334 @@
<script setup lang="ts">
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();
/**图DOM节点实例对象 */
const graphG6Dom = ref<HTMLElement | undefined>(undefined);
/**图实例对象 */
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: '2024-03-31',
refreshTime: '10:31:47',
sn: '13770707',
version: '2.2312.8',
};
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' }"
size="small"
>
<!-- 插槽-卡片左侧侧 -->
<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-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>
<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>
<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

@@ -728,7 +728,7 @@ onMounted(() => {
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<div class="button-container">
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
<template #icon>
<PlusOutlined />
@@ -790,12 +790,12 @@ onMounted(() => {
{{ t('views.neUser.auth.export') }}
</a-button>
</a-popconfirm>
</a-space>
</div>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<div class="button-container">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
@@ -848,7 +848,7 @@ onMounted(() => {
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</div>
</template>
<!-- 表格列表 -->

View File

@@ -72,12 +72,14 @@ let tableColumns: ColumnsType = [
{
title: 'IMSI',
dataIndex: 'imsi',
sorter: (a: any, b: any) => Number(a.imsi) - Number(b.imsi),
align: 'center',
width: 150,
},
{
title: 'MSISDN',
dataIndex: 'msisdn',
sorter: (a: any, b: any) => Number(a.msisdn) - Number(b.msisdn),
align: 'center',
width: 150,
},

View File

@@ -61,20 +61,21 @@ let tableColumns: ColumnsType = [
{
title: 'IMSI',
dataIndex: 'imsi',
sorter: (a: any, b: any) => Number(a.imsi) - Number(b.imsi),
align: 'center',
width: 150,
},
{
title: 'ActiveTime',
title: 'Active Time',
dataIndex: 'activeTime',
align: 'center',
width: 150,
},
{
title: 'RegState',
title: 'Registration State ',
dataIndex: 'regState',
align: 'center',
width: 100,
width: 150,
},
{
title: 'Network Access Identity',

View File

@@ -82,6 +82,7 @@ let tableColumns: ColumnsType = [
{
title: 'MSISDN',
dataIndex: 'msisdn',
sorter: (a: any, b: any) => Number(a.msisdn) - Number(b.msisdn),
align: 'center',
width: 150,
},
@@ -382,7 +383,7 @@ function fnExportList(type: string) {
message.loading({ content: t('common.loading'), key });
exportRule({
neId: neID,
type: type,
fileType: type,
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
@@ -408,6 +409,8 @@ function fnGetList() {
listRules(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
tableState.data = res.rows;
} else {
tableState.data = [];
}
tableState.loading = false;
});
@@ -458,15 +461,21 @@ function fnModalUploadImportUpload(file: File) {
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
return importRuleData({
type: 'txt',
neId: neID,
filePath: res.data,
fileType: 'txt',
});
}
return res;
})
.then(res => {
uploadImportState.msg = res.msg;
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');
}
})
.finally(() => {
hide();
@@ -560,7 +569,7 @@ onMounted(() => {
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<div class="button-container">
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
<template #icon>
<PlusOutlined />
@@ -584,7 +593,7 @@ onMounted(() => {
@click.prevent="fnModalVisibleByBatch('update')"
>
<template #icon>
<DeleteOutlined />
<FormOutlined />
</template>
{{ t('views.neUser.pcf.batchUpdateText') }}
</a-button>
@@ -618,12 +627,12 @@ onMounted(() => {
{{ t('views.neUser.pcf.export') }}
</a-button>
</a-popconfirm>
</a-space>
</div>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<div class="button-container">
<a-tooltip>
<template #title>
{{
@@ -684,7 +693,7 @@ onMounted(() => {
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</div>
</template>
<!-- 表格列表 -->

View File

@@ -454,6 +454,7 @@ const modalStateFrom = Form.useForm(
modalState.from,
reactive({
imsi: [{ required: true, message: 'IMSI' + t('common.unableNull') }],
msisdn: [{ required: true, message: 'MSISDN' + t('common.unableNull') }],
staticIp: [
{ required: true, message: 'static ip' + t('common.unableNull') },
],
@@ -473,7 +474,7 @@ const modalStateFrom = Form.useForm(
function fnModalOk() {
const from = Object.assign({}, toRaw(modalState.from));
let validateNames = ['imsi', 'staticIp'];
let validateNames = ['imsi', 'msisdn', 'staticIp'];
if (from.id === '') {
validateNames.push('smData');
@@ -573,7 +574,7 @@ function fnBatchModalOk() {
modalState.confirmLoading = true;
let ardArr = [0, 0, 0, 0, 0, 0, 0, 0];
let hplmnArr = [0, 0, 0, 0, 0, 0, 0, 0];
let odbArr = [0, 0, 0, 0, 0, 0, 0, 0];
let odbArr = [0, 0, 0, 0, 0, 0, 0, 0, 0];
from.ard.forEach((item: any) => {
ardArr[item] = 1;
@@ -962,7 +963,7 @@ onMounted(() => {
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<div class="button-container">
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
<template #icon>
<PlusOutlined />
@@ -1030,12 +1031,12 @@ onMounted(() => {
{{ t('views.neUser.sub.export') }}
</a-button>
</a-popconfirm>
</a-space>
</div>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<div class="button-container">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
@@ -1088,7 +1089,7 @@ onMounted(() => {
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</div>
</template>
<!-- 表格列表 -->

View File

@@ -73,6 +73,7 @@ let tableColumns: ColumnsType = [
title: 'IMSI',
dataIndex: 'imsi',
align: 'center',
sorter: (a: any, b: any) => Number(a.imsi) - Number(b.imsi),
customRender(opt) {
const idx = opt.value.lastIndexOf('-');
if (idx != -1) {
@@ -81,11 +82,13 @@ let tableColumns: ColumnsType = [
return opt.value;
},
width: 150,
},
{
title: 'MSISDN',
dataIndex: 'msisdn',
align: 'center',
sorter: (a: any, b: any) => Number(a.msisdn) - Number(b.msisdn),
customRender(opt) {
const idx = opt.value.lastIndexOf('-');
if (idx != -1) {

View File

@@ -1,63 +1,33 @@
<script setup lang="ts">
import { useRoute } from 'vue-router';
import { reactive, ref, onMounted, toRaw } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { Modal } from 'ant-design-vue/lib';
import { SizeType } from 'ant-design-vue/lib/config-provider';
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
import { message, Form } from 'ant-design-vue/lib';
import { ColumnsType } from 'ant-design-vue/lib/table';
import { parseDateToStr } from '@/utils/date-utils';
import { SizeType } from 'ant-design-vue/lib/config-provider';
import ChartLine from '@/components/ChartLine/index.vue';
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { listgoldData } from '@/api/perfManage/goldTarget';
import useNeInfoStore from '@/store/modules/neinfo';
import useDictStore from '@/store/modules/dict';
import useI18n from '@/hooks/useI18n';
const { getDict } = useDictStore();
const { t } = useI18n();
const route = useRoute();
import { getGoldTitleByNE, goldData } from '@/api/perfManage/goldTarget';
import { parseDateToStr } from '@/utils/date-utils';
const neInfoStore = useNeInfoStore();
const { t, currentLocale } = useI18n();
/**路由标题 */
let title = ref<string>((route.meta.title as string) ?? '标题');
/**网元参数 */
let neCascaderOptions = ref<Record<string, any>[]>([]);
/**记录开始结束时间 */
let queryRangePicker = ref<[string, string]>(['', '']);
/**查询参数 */
let queryParams = reactive({
/**网元类型 */
neType: '',
/**记录时间 */
beginTime: '',
endTime: '',
/**排序字段 */
sortField: 'value',
/**排序方式 */
sortOrder: 'asc',
/**当前页数 */
pageNum: 1,
/**每页条数 */
pageSize: 20,
});
/**查询参数重置 */
function fnQueryReset() {
queryParams = Object.assign(queryParams, {
neType: '',
beginTime: '',
endTime: '',
sortField: 'value',
sortOrder: 'asc',
pageNum: 1,
pageSize: 20,
});
queryRangePicker.value = ['', ''];
tablePagination.current = 1;
tablePagination.pageSize = 20;
fnGetList();
}
/**表格字段列排序 */
let tableColumnsDnd = ref<ColumnsType>([]);
/**表格状态类型 */
type TabeStateType = {
/**表格列 */
tableColumns: object[];
/**加载等待 */
loading: boolean;
/**紧凑型 */
@@ -70,53 +40,13 @@ type TabeStateType = {
/**表格状态 */
let tableState: TabeStateType = reactive({
tableColumns: [],
loading: false,
size: 'middle',
seached: true,
data: [],
});
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('views.perfManage.goldTarget.type'),
dataIndex: 'neType',
align: 'center',
},
{
title: t('views.perfManage.goldTarget.enTitle'),
dataIndex: 'enTitle',
align: 'center',
},
{
title: t('views.perfManage.goldTarget.value'),
dataIndex: 'value',
key: 'value',
align: 'center',
sorter: true,
},
{
title: t('views.perfManage.goldTarget.startTime'),
dataIndex: 'startTime',
key: 'start_time',
align: 'center',
customRender(opt) {
if (!opt.value) return '';
return parseDateToStr(opt.value);
},
sorter: true,
},
{
title: t('views.perfManage.goldTarget.endTime'),
dataIndex: 'endTime',
align: 'center',
customRender(opt) {
if (!opt.value) return '';
return parseDateToStr(opt.value);
},
},
];
/**表格分页器参数 */
let tablePagination = reactive({
/**当前页数 */
@@ -150,45 +80,265 @@ function fnTableSize({ key }: MenuInfo) {
tableState.size = key as SizeType;
}
/**查询黄金指标列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (tableState.loading) return;
tableState.loading = true;
if (pageNum) {
queryParams.pageNum = pageNum;
/**查询数 */
let queryParams: any = reactive({
/**卡片切换Flag */
cardFlag: 0, //0-显示统计图 1-显示统计表
/**告警设备类型 */
neType: '',
/**告警网元标识 */
neId: '',
/**颗粒度 */
particle: '15',
beginTime: '',
endTime: '',
});
/**图表显示数据 */
const chartsOption = reactive({
/**性能指标 */
perfChart: {},
});
/**对象信息状态类型 */
type StateType = {
/**网元类型 */
neType: string[];
/**制表网元类型 */
designNeType: string;
/**黄金指标集 tree */
designTreeData: any[];
/**表单数据 */
from: Record<string, any>;
};
/**对象信息状态 */
let state: StateType = reactive({
neType: [],
designNeType: '',
designTreeData: [],
from: {
uploadLoading: false,
sendLoading: false,
},
});
/**网元类型选择对应修改 */
function fnNeChange(keys: any, _: any) {
// 不是同类型时需要重新加载
if (state.designNeType !== keys[0]) {
state.designTreeData = [];
queryParams.cardFlag = 0;
fnGetList();
}
}
/**查询可选命令列表 */
function fnGetList() {
const neType = queryParams.neType[0];
state.designNeType = neType;
var language = currentLocale.value.split('_')[0];
if (language === 'zh') language = 'cn';
getGoldTitleByNE(neType).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
// 构建树结构
const treeArr: Record<string, any>[] = [];
for (const item of res.data) {
const id = item['id'];
const kpiDisplay = item[`${language}Title`];
const kpiValue = item[`kpiId`];
treeArr.push({
key: kpiValue,
title: kpiDisplay,
});
}
state.designTreeData = treeArr;
} else {
message.warning({
content: t('common.getInfoFail'),
duration: 2,
});
}
});
fnDesign();
}
/**根据 key 查找对应的 title */
function findTitleByKey(key: string): string | undefined {
const item = state.designTreeData.find(item => item.key === key);
return item ? item.title : undefined;
}
/**筛选条件进行制图 */
function fnMakeTable(flag: any) {
queryParams.cardFlag = flag;
fnDesign();
}
/**筛选条件进行制图 */
function fnDesign() {
//当前界面是表格界面
const columnsArr = state.designTreeData.map(item => {
return {
title: item.title,
dataIndex: item.key,
align: 'center',
};
});
tableState.tableColumns = columnsArr;
tableState.tableColumns.unshift({
title: t('views.perfManage.perfData.neName'),
dataIndex: 'neName',
align: 'center',
});
tableState.tableColumns.push({
title: t('views.perfManage.goldTarget.time'),
dataIndex: 'timeGroup',
align: 'center',
fixed: 'right',
});
if (!queryRangePicker.value) {
queryRangePicker.value = ['', ''];
}
queryParams.beginTime = queryRangePicker.value[0];
queryParams.endTime = queryRangePicker.value[1];
listgoldData(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
tablePagination.total = res.total;
tableState.data = res.rows;
}
tableState.loading = false;
});
}
const neType = queryParams.neType[0];
let goldXDate: any = [];
let goldYData: any = [];
let hideAll: any = {};
goldData(queryParams).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
if (res.data.length > 0) {
tableState.data = res.data;
tablePagination.total = res.data.length;
goldXDate = res.data.map((item: any) => item.timeGroup);
goldYData = Object.keys(res.data[0])
.filter(key => !['timeGroup', 'neName', 'startIndex'].includes(key))
.map(key => {
const title: any = findTitleByKey(key);
hideAll[title]=false;
return {
name: title,
data: res.data.map((item: any) => parseInt(item[key])),
};
});
} else {
tableState.data = [];
tablePagination.total = 0;
state.designTreeData.forEach((item: any) => {
goldYData.push({ name: item.title, data: [] });
});
message.warning({
content: t('views.perfManage.goldTarget.nullTip'),
duration: 2,
});
}
/**表格分页、排序、筛选变化时触发操作, 排序方式,取值为 ascend descend */
function fnTableChange(pagination: any, filters: any, sorter: any, extra: any) {
console.log(sorter);
const { columnKey, order } = sorter;
if (order) {
queryParams.sortField = columnKey;
queryParams.sortOrder = order.replace('end', '');
} else {
queryParams.sortOrder = 'asc';
}
fnGetList(1);
// 图标参数
const option = {
xDatas: goldXDate,
yDatas: goldYData,
tooltip: {
trigger: 'axis',
formatter: function (datas: any) {
let res = datas[0].name + '<br/>';
for (const item of datas) {
res += `${item.marker} ${item.seriesName}:${item.data}<br/>`;
}
return res;
},
},
legend: {
// orient: 'vertical',
// left: 'left',
type: 'scroll',
orient: 'vertical', // vertical
right: 20,
//itemWidth: 20,
itemGap: 25,
textStyle: {
color: '#646A73',
},
icon: 'circle',
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,
},
},
],
};
chartsOption.perfChart = option;
//处理表格数据
} else {
message.warning({
content: t('common.getInfoFail'),
duration: 2,
});
}
});
}
onMounted(() => {
// 获取网元网元列表
useNeInfoStore().fnNelist();
// 获取列表数据
fnGetList();
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;
}
// 默认选择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>
@@ -200,48 +350,59 @@ onMounted(() => {
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
>
<!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-form :model="queryParams" name="queryParamsFrom" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item
:label="t('views.perfManage.goldTarget.type')"
name="neType"
:label="t('views.traceManage.task.neType')"
>
<a-auto-complete
<a-cascader
v-model:value="queryParams.neType"
:options="useNeInfoStore().getNeSelectOtions"
allow-clear
:options="neCascaderOptions"
@change="fnNeChange"
:allow-clear="false"
:placeholder="t('common.selectPlease')"
/>
</a-form-item>
</a-col>
<a-col :lg="8" :md="12" :xs="24">
<a-col :lg="10" :md="12" :xs="24">
<a-form-item
:label="t('views.perfManage.goldTarget.startTime')"
name="queryRangePicker"
:label="t('views.perfManage.goldTarget.timeFrame')"
name="eventTime"
>
<a-range-picker
v-model:value="queryRangePicker"
allow-clear
bordered
show-time
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
style="width: 100%"
></a-range-picker>
:allow-clear="false"
show-time
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-col :lg="4" :md="12" :xs="24">
<a-form-item
:label="t('views.perfManage.goldTarget.particle')"
name="particle"
>
<a-select
v-model:value="queryParams.particle"
:placeholder="t('common.selectPlease')"
:options="[
{ label: '15M', value: '15' },
{ label: '30M', value: '30' },
{ label: '60M', value: '60' },
]"
/>
</a-form-item>
</a-col>
<a-col :lg="2" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList(1)">
<a-button type="primary" @click.prevent="fnDesign()">
<template #icon><SearchOutlined /></template>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
{{ t('common.reset') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
@@ -249,86 +410,112 @@ onMounted(() => {
</a-form>
</a-card>
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title> </template>
<template v-if="queryParams.cardFlag">
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-button type="primary" @click.prevent="fnMakeTable(0)">
<template #icon> <area-chart-outlined /> </template>
{{ t('views.perfManage.goldTarget.kpiTitle') }}
</a-button>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
v-model:checked="tableState.seached"
:checked-children="t('common.switch.show')"
:un-checked-children="t('common.switch.hide')"
size="small"
/>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
<template #icon><ReloadOutlined /></template>
</a-button>
</a-tooltip>
<TableColumnsDnd
:columns="tableState.tableColumns"
v-model:columns-dnd="tableColumnsDnd"
></TableColumnsDnd>
<a-tooltip>
<template #title>{{ t('common.sizeText') }}</template>
<a-dropdown trigger="click" placement="bottomRight">
<a-button type="text">
<template #icon><ColumnHeightOutlined /></template>
</a-button>
<template #overlay>
<a-menu
:selected-keys="[tableState.size as string]"
@click="fnTableSize"
>
<a-menu-item key="default">
{{ t('common.size.default') }}
</a-menu-item>
<a-menu-item key="middle">
{{ t('common.size.middle') }}
</a-menu-item>
<a-menu-item key="small">
{{ t('common.size.small') }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</template>
<!-- 表格列表 -->
<a-table
class="table"
row-key="id"
:columns="tableColumnsDnd"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: tableColumnsDnd.length * 200, y: 450 }"
@resizeColumn="(w:number, col:any) => (col.width = w)"
:show-expand-column="false"
>
</a-table>
</a-card>
</template>
<a-card :bordered="false" :body-style="{ marginBottom: '24px' }" v-else>
<!-- 插槽-卡片左侧侧 -->
<template #title>{{
t('views.perfManage.goldTarget.kpiTitle')
}}</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
v-model:checked="tableState.seached"
:checked-children="t('common.switch.show')"
:un-checked-children="t('common.switch.hide')"
size="small"
/>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
<template #icon><ReloadOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.sizeText') }}</template>
<a-dropdown trigger="click" placement="bottomRight">
<a-button type="text">
<template #icon><ColumnHeightOutlined /></template>
</a-button>
<template #overlay>
<a-menu
:selected-keys="[tableState.size as string]"
@click="fnTableSize"
>
<a-menu-item key="default">
{{ t('common.size.default') }}
</a-menu-item>
<a-menu-item key="middle">
{{ t('common.size.middle') }}
</a-menu-item>
<a-menu-item key="small">
{{ t('common.size.small') }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-tooltip>
<a-button type="default" size="small" @click.prevent="fnMakeTable(1)">
<template #icon>
<ClearOutlined />
</template>
{{ t('views.perfManage.goldTarget.allData') }}
</a-button>
</a-space>
</template>
<!-- 表格列表 -->
<a-table
class="table"
row-key="id"
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
@change="fnTableChange"
:pagination="tablePagination"
:scroll="{ x: true }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'alarmTitle'">
<a-tooltip>
<template #title>{{ record.operResult }}</template>
<div class="alarmTitleText">{{ record.alarmTitle }}</div>
</a-tooltip>
</template>
</template>
</a-table>
<div class="chart">
<ChartLine
:option="chartsOption.perfChart"
:dataZoom="false"
height="400px"
></ChartLine>
</div>
</a-card>
</PageContainer>
</template>
<style lang="less" scoped>
.table :deep(.ant-pagination) {
padding: 0 24px;
}
.alarmTitleText {
max-width: 300px;
cursor: pointer;
.chart {
width: 100%;
height: 400px;
}
</style>

View File

@@ -0,0 +1,334 @@
<script setup lang="ts">
import { useRoute } from 'vue-router';
import { reactive, ref, onMounted, toRaw } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { Modal } from 'ant-design-vue/lib';
import { SizeType } from 'ant-design-vue/lib/config-provider';
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
import { ColumnsType } from 'ant-design-vue/lib/table';
import { parseDateToStr } from '@/utils/date-utils';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { listgoldData } from '@/api/perfManage/goldTarget';
import useNeInfoStore from '@/store/modules/neinfo';
import useDictStore from '@/store/modules/dict';
import useI18n from '@/hooks/useI18n';
const { getDict } = useDictStore();
const { t } = useI18n();
const route = useRoute();
/**路由标题 */
let title = ref<string>((route.meta.title as string) ?? '标题');
/**记录开始结束时间 */
let queryRangePicker = ref<[string, string]>(['', '']);
/**查询参数 */
let queryParams = reactive({
/**网元类型 */
neType: '',
/**记录时间 */
beginTime: '',
endTime: '',
/**排序字段 */
sortField: 'value',
/**排序方式 */
sortOrder: 'asc',
/**当前页数 */
pageNum: 1,
/**每页条数 */
pageSize: 20,
});
/**查询参数重置 */
function fnQueryReset() {
queryParams = Object.assign(queryParams, {
neType: '',
beginTime: '',
endTime: '',
sortField: 'value',
sortOrder: 'asc',
pageNum: 1,
pageSize: 20,
});
queryRangePicker.value = ['', ''];
tablePagination.current = 1;
tablePagination.pageSize = 20;
fnGetList();
}
/**表格状态类型 */
type TabeStateType = {
/**加载等待 */
loading: boolean;
/**紧凑型 */
size: SizeType;
/**搜索栏 */
seached: boolean;
/**记录数据 */
data: object[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
size: 'middle',
seached: true,
data: [],
});
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('views.perfManage.goldTarget.type'),
dataIndex: 'neType',
align: 'center',
},
{
title: t('views.perfManage.goldTarget.enTitle'),
dataIndex: 'enTitle',
align: 'center',
},
{
title: t('views.perfManage.goldTarget.value'),
dataIndex: 'value',
key: 'value',
align: 'center',
sorter: true,
},
{
title: t('views.perfManage.goldTarget.startTime'),
dataIndex: 'startTime',
key: 'start_time',
align: 'center',
customRender(opt) {
if (!opt.value) return '';
return parseDateToStr(opt.value);
},
sorter: true,
},
{
title: t('views.perfManage.goldTarget.endTime'),
dataIndex: 'endTime',
align: 'center',
customRender(opt) {
if (!opt.value) return '';
return parseDateToStr(opt.value);
},
},
];
/**表格分页器参数 */
let tablePagination = reactive({
/**当前页数 */
current: 1,
/**每页条数 */
pageSize: 20,
/**默认的每页条数 */
defaultPageSize: 20,
/**指定每页可以显示多少条 */
pageSizeOptions: ['10', '20', '50', '100'],
/**只有一页时是否隐藏分页器 */
hideOnSinglePage: false,
/**是否可以快速跳转至某页 */
showQuickJumper: true,
/**是否可以改变 pageSize */
showSizeChanger: true,
/**数据总数 */
total: 0,
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
onChange: (page: number, pageSize: number) => {
tablePagination.current = page;
tablePagination.pageSize = pageSize;
queryParams.pageNum = page;
queryParams.pageSize = pageSize;
fnGetList();
},
});
/**表格紧凑型变更操作 */
function fnTableSize({ key }: MenuInfo) {
tableState.size = key as SizeType;
}
/**查询黄金指标列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (tableState.loading) return;
tableState.loading = true;
if (pageNum) {
queryParams.pageNum = pageNum;
}
if (!queryRangePicker.value) {
queryRangePicker.value = ['', ''];
}
queryParams.beginTime = queryRangePicker.value[0];
queryParams.endTime = queryRangePicker.value[1];
listgoldData(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
tablePagination.total = res.total;
tableState.data = res.rows;
}
tableState.loading = false;
});
}
/**表格分页、排序、筛选变化时触发操作, 排序方式,取值为 ascend descend */
function fnTableChange(pagination: any, filters: any, sorter: any, extra: any) {
console.log(sorter);
const { columnKey, order } = sorter;
if (order) {
queryParams.sortField = columnKey;
queryParams.sortOrder = order.replace('end', '');
} else {
queryParams.sortOrder = 'asc';
}
fnGetList(1);
}
onMounted(() => {
// 获取网元网元列表
useNeInfoStore().fnNelist();
// 获取列表数据
fnGetList();
});
</script>
<template>
<PageContainer>
<a-card
v-show="tableState.seached"
:bordered="false"
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
>
<!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item
:label="t('views.perfManage.goldTarget.type')"
name="neType"
>
<a-auto-complete
v-model:value="queryParams.neType"
:options="useNeInfoStore().getNeSelectOtions"
allow-clear
/>
</a-form-item>
</a-col>
<a-col :lg="8" :md="12" :xs="24">
<a-form-item
:label="t('views.perfManage.goldTarget.startTime')"
name="queryRangePicker"
>
<a-range-picker
v-model:value="queryRangePicker"
allow-clear
bordered
show-time
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
style="width: 100%"
></a-range-picker>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList(1)">
<template #icon><SearchOutlined /></template>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
{{ t('common.reset') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-card>
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title> </template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
v-model:checked="tableState.seached"
:checked-children="t('common.switch.show')"
:un-checked-children="t('common.switch.hide')"
size="small"
/>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
<template #icon><ReloadOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.sizeText') }}</template>
<a-dropdown trigger="click" placement="bottomRight">
<a-button type="text">
<template #icon><ColumnHeightOutlined /></template>
</a-button>
<template #overlay>
<a-menu
:selected-keys="[tableState.size as string]"
@click="fnTableSize"
>
<a-menu-item key="default">
{{ t('common.size.default') }}
</a-menu-item>
<a-menu-item key="middle">
{{ t('common.size.middle') }}
</a-menu-item>
<a-menu-item key="small">
{{ t('common.size.small') }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</template>
<!-- 表格列表 -->
<a-table
class="table"
row-key="id"
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
@change="fnTableChange"
:pagination="tablePagination"
:scroll="{ x: true }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'alarmTitle'">
<a-tooltip>
<template #title>{{ record.operResult }}</template>
<div class="alarmTitleText">{{ record.alarmTitle }}</div>
</a-tooltip>
</template>
</template>
</a-table>
</a-card>
</PageContainer>
</template>
<style lang="less" scoped>
.table :deep(.ant-pagination) {
padding: 0 24px;
}
.alarmTitleText {
max-width: 300px;
cursor: pointer;
}
</style>

View File

@@ -352,14 +352,6 @@ onMounted(() => {
@change="fnTableChange"
:scroll="{ x: true }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'alarmTitle'">
<a-tooltip>
<template #title>{{ record.operResult }}</template>
<div class="alarmTitleText">{{ record.alarmTitle }}</div>
</a-tooltip>
</template>
</template>
</a-table>
</a-card>
</PageContainer>
@@ -369,8 +361,4 @@ onMounted(() => {
.table :deep(.ant-pagination) {
padding: 0 24px;
}
.alarmTitleText {
max-width: 300px;
cursor: pointer;
}
</style>

View File

@@ -322,7 +322,7 @@ function fnModalVisibleByEdit(id?: string) {
* 进行表达规则校验
*/
function fnModalOk() {
console.log(modalState.from);
//console.log(modalState.from);
modalStateFrom
.validate()
.then(e => {

View File

@@ -19,6 +19,7 @@ import {
taskStop,
taskRun,
} from '@/api/perfManage/taskManage';
const neInfoStore = useNeInfoStore();
const { t, currentLocale } = useI18n();
const generateOptions = (start: any, end: any) => {
@@ -378,7 +379,7 @@ function fnSelectPer(s: any, option: any) {
/**性能测量数据集选择初始 */
function fnSelectPerformanceInit(neType: string) {
//console.logg(currentLocale.value); //当前语言
const performance = useNeInfoStore().perMeasurementList.filter(
const performance = neInfoStore.perMeasurementList.filter(
i => i.neType === neType
);
//进行分组选择
@@ -692,9 +693,9 @@ onMounted(() => {
Promise.allSettled([
// 获取网元网元列表
useNeInfoStore().fnNelist(),
neInfoStore.fnNelist(),
// 获取性能测量集列表
useNeInfoStore().fnNeTaskPerformance(),
neInfoStore.fnNeTaskPerformance(),
]).finally(() => {
// 获取列表数据
fnGetList();
@@ -719,7 +720,7 @@ onMounted(() => {
>
<a-auto-complete
v-model:value="queryParams.neType"
:options="useNeInfoStore().getNeSelectOtions"
:options="neInfoStore.getNeSelectOtions"
allow-clear
:placeholder="t('views.traceManage.task.neTypePlease')"
/>
@@ -887,7 +888,7 @@ onMounted(() => {
>
<a-cascader
:value="modalState.neType"
:options="useNeInfoStore().getNeCascaderOtions"
:options="neInfoStore.getNeCascaderOptions"
disabled
/>
</a-form-item>
@@ -1002,7 +1003,7 @@ onMounted(() => {
>
<a-cascader
v-model:value="modalState.neType"
:options="useNeInfoStore().getNeCascaderOtions"
:options="neInfoStore.getNeCascaderOptions"
@change="fnNeChange"
:allow-clear="false"
:placeholder="t('views.traceManage.task.neTypePlease')"

View File

@@ -1,14 +1,15 @@
<script lang="ts" setup>
import { GlobalFooter } from 'antdv-pro-layout';
import { Modal, message } from 'ant-design-vue/lib';
import { onMounted, reactive, toRaw } from 'vue';
import { computed, onMounted, reactive, toRaw } from 'vue';
import { register } from '@/api/login';
import { regExpPasswd, regExpUserName } from '@/utils/regular-utils';
import { useRouter, useRoute } from 'vue-router';
import useAppStore from '@/store/modules/app';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
const { t } = useI18n();
import { parseUrlPath } from '@/plugins/file-static-url';
const { t, currentLocale } = useI18n();
const router = useRouter();
const route = useRoute();
const appStore = useAppStore();
@@ -70,6 +71,22 @@ function fnFinish() {
});
}
// LOGO地址
const logoUrl = computed(() => {
let url =
appStore.logoType === 'brand'
? parseUrlPath(appStore.filePathBrand)
: parseUrlPath(appStore.filePathIcon);
if (url.indexOf('{language}') === -1) {
return url;
}
// 语言参数替换
const local = currentLocale.value;
const lang = local.split('_')[0];
return url.replace('{language}', lang);
});
/**
* 国际化翻译转换
*/
@@ -92,11 +109,11 @@ onMounted(() => {
<div class="header">
<a href="/" target="_self">
<template v-if="appStore.logoType === 'icon'">
<img :src="appStore.getLOGOIcon" class="logo" alt="logo" />
<img :src="logoUrl" class="logo" alt="logo" />
<span class="title">{{ appStore.appName }}</span>
</template>
<template v-if="appStore.logoType === 'brand'">
<img :src="appStore.getLOGOBrand" class="logo" alt="logo" />
<img :src="logoUrl" class="logo" alt="logo" />
</template>
</a>
</div>
@@ -205,7 +222,6 @@ onMounted(() => {
width: 100%;
min-height: 100%;
padding: 110px 0 144px;
background-image: url(../assets/background.svg);
background-repeat: no-repeat;
background-position: center 110px;
background-size: 100%;

View File

@@ -552,7 +552,7 @@ onMounted(() => {
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<div class="button-container">
<a-button
type="primary"
@click.prevent="fnModalVisibleByEdit()"
@@ -588,12 +588,12 @@ onMounted(() => {
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
</a-space>
</div>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<div class="button-container">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
@@ -642,7 +642,7 @@ onMounted(() => {
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</div>
</template>
<!-- 表格列表 -->

View File

@@ -192,7 +192,7 @@ const modalStateFrom = Form.useForm(
{
required: true,
min: 1,
max: 30,
max: 50,
message: t('views.system.dept.className') + t('common.unableNull'),
},
],
@@ -621,26 +621,34 @@ onMounted(() => {
:title="modalState.title"
@cancel="fnModalCancel"
>
<a-form layout="horizontal">
<a-form layout="horizontal" :label-col="{ span: 6 }" :labelWrap="true">
<a-form-item
:label="t('views.system.dept.highClass')"
name="parentId"
:label-col="{ span: 3 }"
:labelWrap="true"
>
<a-tree-select
:value="modalState.from.parentId"
disabled
:tree-data="modalState.treeData"
:field-names="{
children: 'children',
label: 'deptName',
value: 'deptId',
}"
tree-node-label-prop="deptName"
>
<template #suffixIcon></template>
</a-tree-select>
</a-form-item>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.system.dept.highClass')"
name="parentId"
:label="t('views.system.dept.className')"
name="deptName"
>
<a-tree-select
:value="modalState.from.parentId"
disabled
:tree-data="modalState.treeData"
:field-names="{
children: 'children',
label: 'deptName',
value: 'deptId',
}"
tree-node-label-prop="deptName"
>
<template #suffixIcon></template>
</a-tree-select>
{{ modalState.from.deptName }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
@@ -652,6 +660,7 @@ onMounted(() => {
</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.system.dept.status')" name="status">
@@ -668,21 +677,16 @@ onMounted(() => {
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-col :lg="24" :md="24" :xs="24">
<a-form-item
:label="t('views.system.dept.className')"
name="deptName"
:label="t('views.system.dept.admin')"
name="leader"
:label-col="{ span: 3 }"
:labelWrap="true"
>
{{ modalState.from.deptName }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.system.dept.admin')" name="leader">
{{ modalState.from.leader }}
</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.system.dept.phone')" name="phone">
{{ modalState.from.phone }}
@@ -713,7 +717,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-form-item
:label="t('views.system.dept.highClass')"
name="parentId"
@@ -751,18 +760,6 @@ onMounted(() => {
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.system.dept.admin')"
name="leader"
v-bind="modalStateFrom.validateInfos.leader"
>
<a-input
v-model:value="modalState.from.leader"
allow-clear
></a-input>
</a-form-item>
</a-col>
<a-col :lg="6" :md="6" :xs="24">
<a-form-item :label="t('views.system.dept.status')" name="status">
<a-select
v-model:value="modalState.from.status"
@@ -772,8 +769,11 @@ onMounted(() => {
</a-select>
</a-form-item>
</a-col>
<a-col :lg="6" :md="6" :xs="24">
<a-form-item :label="t('views.system.dept.showSort')" name="orderNum">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.system.dept.showSort')"
name="orderNum"
>
<a-input-number
v-model:value="modalState.from.orderNum"
:min="0"
@@ -785,6 +785,20 @@ onMounted(() => {
</a-row>
<a-row :gutter="16">
<a-col :span="24">
<a-form-item
:label="t('views.system.dept.admin')"
name="leader"
v-bind="modalStateFrom.validateInfos.leader"
:label-col="{ span: 3 }"
:labelWrap="true"
>
<a-input
v-model:value="modalState.from.leader"
allow-clear
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.system.dept.phone')"
@@ -800,7 +814,7 @@ onMounted(() => {
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.system.dept.email')"
:label="t('views.system.dept.email')"
name="email"
v-bind="modalStateFrom.validateInfos.email"
>

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;
@@ -561,7 +575,7 @@ onMounted(() => {
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<div class="button-container">
<a-button type="default" @click.prevent="fnClose()">
<template #icon><CloseOutlined /></template>
{{ t('common.close') }}
@@ -592,12 +606,12 @@ onMounted(() => {
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
</a-space>
</div>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<div class="button-container">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
@@ -646,7 +660,7 @@ onMounted(() => {
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</div>
</template>
<!-- 表格列表 -->
@@ -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.ipnutPlease')"
></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"
@@ -931,16 +964,21 @@ onMounted(() => {
</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.ipnutPlease')"
></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

@@ -552,7 +552,7 @@ onMounted(() => {
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<div class="button-container">
<a-button
type="primary"
@click.prevent="fnModalVisibleByEdit()"
@@ -596,12 +596,12 @@ onMounted(() => {
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
</a-space>
</div>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<div class="button-container">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
@@ -650,7 +650,7 @@ onMounted(() => {
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</div>
</template>
<!-- 表格列表 -->

View File

@@ -491,7 +491,7 @@ onMounted(() => {
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<div class="button-container">
<a-button
type="primary"
@click.prevent="fnModalVisibleByEdit()"
@@ -518,12 +518,12 @@ onMounted(() => {
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
</a-space>
</div>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<div class="button-container">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
@@ -572,7 +572,7 @@ onMounted(() => {
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</div>
</template>
<!-- 表格列表 -->

View File

@@ -394,7 +394,7 @@ onMounted(() => {
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<div class="button-container">
<a-button type="default" @click.prevent="fnClose()">
<template #icon><CloseOutlined /></template>
{{ t('common.cancel') }}
@@ -417,12 +417,12 @@ onMounted(() => {
<template #icon><UsergroupDeleteOutlined /></template>
{{ t('views.system.role.batchCancel') }}
</a-button>
</a-space>
</div>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<div class="button-container">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
@@ -471,7 +471,7 @@ onMounted(() => {
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</div>
</template>
<!-- 表格列表 -->
@@ -498,7 +498,9 @@ onMounted(() => {
<template v-if="column.key === 'userId'">
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{t('views.system.role.cancelGive')}}</template>
<template #title>{{
t('views.system.role.cancelGive')
}}</template>
<a-button
type="link"
@click.prevent="fnRecordDelete(record.userId)"

View File

@@ -815,7 +815,7 @@ onMounted(() => {
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<div class="button-container">
<a-button
type="primary"
@click.prevent="fnModalVisibleByEdit()"
@@ -842,7 +842,7 @@ onMounted(() => {
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
</a-space>
</div>
</template>
<!-- 插槽-卡片右侧 -->
@@ -1236,7 +1236,10 @@ onMounted(() => {
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.system.role.createTime')" name="createTime">
<a-form-item
:label="t('views.system.role.createTime')"
name="createTime"
>
<span v-if="+modalState.from.createTime > 0">
{{ parseDateToStr(+modalState.from.createTime) }}
</span>
@@ -1245,12 +1248,18 @@ onMounted(() => {
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.system.role.roleSort')" name="roleSort">
<a-form-item
:label="t('views.system.role.roleSort')"
name="roleSort"
>
{{ modalState.from.roleSort }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.system.role.roleStatus')" name="status">
<a-form-item
:label="t('views.system.role.roleStatus')"
name="status"
>
<DictTag
:options="dict.sysNormalDisable"
:value="modalState.from.status"
@@ -1260,17 +1269,20 @@ onMounted(() => {
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.system.role.roleName')" name="roleName">
<a-form-item
:label="t('views.system.role.roleName')"
name="roleName"
>
{{ modalState.from.roleName }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.system.role.roleKey')" name="roleKey">
<a-form-item :label="t('views.system.role.roleKey')" name="roleKey">
{{ modalState.from.roleKey }}
</a-form-item>
</a-col>
</a-row>
<a-form-item :label="t('views.system.role.roleMark')" name="remark">
<a-form-item :label="t('views.system.role.roleMark')" name="remark">
{{ modalState.from.remark }}
</a-form-item>
@@ -1293,21 +1305,21 @@ onMounted(() => {
:checked="modalState.deptTree.expandedKeys.length > 0"
@change="(e:any) => fnModalExpandedKeys(e.target.checked, 'dept')"
>
{{t('views.system.role.openSwitch')}}
{{ t('views.system.role.openSwitch') }}
</a-checkbox>
<a-checkbox
id="dept_2"
:checked="modalState.from.deptIds.length > 0"
@change="(e:any) => fnModalCheckedKeys(e.target.checked, 'dept')"
>
{{t('views.system.role.selAllSwitch')}}
{{ t('views.system.role.selAllSwitch') }}
</a-checkbox>
<a-checkbox
id="dept_1"
:checked="modalState.from.deptCheckStrictly === '1'"
@change="(e:any) => fnModalCheckStrictly(e.target.checked, 'dept')"
>
{{t('views.system.role.relationSwitch')}}
{{ t('views.system.role.relationSwitch') }}
</a-checkbox>
</a-space>
<a-tree

View File

@@ -1,13 +1,14 @@
<script lang="ts" setup>
import { Modal, message } from 'ant-design-vue/lib';
import { onMounted, reactive } from 'vue';
import useAppStore from '@/store/modules/app';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { transferHelpDoc } from '@/api/index';
import { uploadFileChunk } from '@/api/tool/file';
import { transferStaticFile, uploadFileChunk } from '@/api/tool/file';
import { FileType } from 'ant-design-vue/lib/upload/interface';
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
import { useRouter } from 'vue-router';
const appStore = useAppStore();
const router = useRouter();
const { t, currentLocale, optionsLocale } = useI18n();
@@ -79,9 +80,10 @@ function fnSave() {
// 发送请求
const hide = message.loading(t('common.loading'), 0);
state.loading = true;
transferHelpDoc({
transferStaticFile({
language: state.language,
uploadPath: state.filePath,
staticPath: appStore.helpDoc,
}).then(res => {
state.loading = false;
hide();
@@ -121,6 +123,7 @@ onMounted(() => {
v-model:value="state.language"
style="width: 100px"
size="small"
v-perms:has="['system:setting:i18n']"
>
<a-select-option
v-for="opt in optionsLocale"
@@ -166,6 +169,7 @@ onMounted(() => {
v-model:value="state.language"
style="width: 100px"
size="small"
v-perms:has="['system:setting:i18n']"
>
<a-select-option
v-for="opt in optionsLocale"

View File

@@ -0,0 +1,143 @@
<script lang="ts" setup>
import { Modal, message } from 'ant-design-vue/lib';
import { onMounted, reactive } from 'vue';
import useAppStore from '@/store/modules/app';
import useI18n from '@/hooks/useI18n';
import { changeValue } from '@/api/system/config';
import { CACHE_LOCAL_I18N } from '@/constants/cache-keys-constants';
import { localSet } from '@/utils/cache-local-utils';
const appStore = useAppStore();
const { t, optionsLocale } = useI18n();
type StateType = {
edite: boolean;
loading: boolean;
open: boolean;
default: string;
};
let state: StateType = reactive({
edite: false,
loading: false,
open: false,
default: '',
});
/**进入可编辑 */
function fnEdit(v: boolean) {
state.edite = v;
if (!v) {
state.open = appStore.i18nOpen;
state.default = appStore.i18nDefault;
}
}
/**提交保存 */
function fnSave() {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.system.setting.sysOfficialUrlTipContent'),
onOk() {
// 发送请求
const hide = message.loading(t('common.loading'), 0);
state.loading = true;
Promise.all([
changeValue({ key: 'sys.i18n.open', value: `${state.open}` }),
changeValue({ key: 'sys.i18n.default', value: `${state.default}` }),
])
.then(resArr => {
// 不判断
message.success(t('views.system.setting.saveSuccess'), 3);
appStore.i18nOpen = state.open;
appStore.i18nDefault = state.default;
localSet(CACHE_LOCAL_I18N, state.default);
fnEdit(false);
})
.finally(() => {
state.loading = false;
hide();
});
},
});
}
onMounted(() => {
state.open = appStore.i18nOpen;
state.default = appStore.i18nDefault;
});
</script>
<template>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24" style="margin-bottom: 30px">
<a-form v-if="state.edite">
<a-form-item :label="t('views.system.setting.i18nOpen')">
<a-switch
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
v-model:checked="state.open"
></a-switch>
</a-form-item>
<a-form-item :label="t('views.system.setting.i18nDefault')">
<a-select v-model:value="state.default" style="width: 100px">
<a-select-option
v-for="opt in optionsLocale"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="fnSave">
{{ t('views.system.setting.saveSubmit') }}
</a-button>
<a-button style="margin-left: 10px" @click="fnEdit(false)">
{{ t('common.cancel') }}
</a-button>
</a-form-item>
</a-form>
<template v-else>
<a-form-item :label="t('views.system.setting.i18nOpen')">
<a-switch
:disabled="true"
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
v-model:checked="state.open"
></a-switch>
</a-form-item>
<a-form-item :label="t('views.system.setting.i18nDefault')">
<a-select
v-model:value="state.default"
style="width: 100px"
:disabled="true"
>
<a-select-option
v-for="opt in optionsLocale"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</a-select-option>
</a-select>
</a-form-item>
<a-button type="dashed" @click="fnEdit(true)">
{{ t('common.editText') }}
</a-button>
</template>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-typography>
<a-typography-paragraph>
{{ t('views.system.setting.i18nInstruction') }}
</a-typography-paragraph>
</a-typography>
</a-col>
</a-row>
</template>
<style lang="less" scoped></style>

View File

@@ -9,6 +9,7 @@ import { uploadFile } from '@/api/tool/file';
import { FileType } from 'ant-design-vue/lib/upload/interface';
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
import { sessionGet } from '@/utils/cache-session-utils';
import { parseUrlPath } from '@/plugins/file-static-url';
const appStore = useAppStore();
const { t } = useI18n();
@@ -84,7 +85,7 @@ function fnEdit(v: boolean) {
state.edite = v;
if (!v) {
state.filePath = '#';
state.flag = appStore.getLoginBackground;
state.flag = parseUrlPath(appStore.loginBackground);
}
}
@@ -142,8 +143,8 @@ function fnRevert() {
}
onMounted(() => {
state.filePath = appStore.getLoginBackground;
state.flag = appStore.getLoginBackground;
state.filePath = parseUrlPath(appStore.loginBackground);
state.flag = parseUrlPath(appStore.loginBackground);
});
</script>

View File

@@ -3,20 +3,21 @@ import { Modal, message } from 'ant-design-vue/lib';
import { FileType } from 'ant-design-vue/lib/upload/interface';
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
import IconFont from '@/components/IconFont/index.vue';
import { onMounted, reactive } from 'vue';
import { onMounted, reactive, watch, computed } from 'vue';
import useAppStore from '@/store/modules/app';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { uploadFile } from '@/api/tool/file';
import { transferStaticFile, uploadFile } from '@/api/tool/file';
import { changeValue } from '@/api/system/config';
import { computed } from 'vue';
import { sessionGet } from '@/utils/cache-session-utils';
import { parseUrlPath } from '@/plugins/file-static-url';
const appStore = useAppStore();
const { t } = useI18n();
const { t, currentLocale, optionsLocale } = useI18n();
type StateType = {
edite: boolean;
loading: boolean;
language: string;
filePath: string; // 是否上传文件
flag: string; // 是否变更标记
type: 'brand' | 'icon';
@@ -27,6 +28,7 @@ type StateType = {
let state: StateType = reactive({
edite: false,
loading: false,
language: '',
filePath: '',
flag: '',
type: 'icon',
@@ -39,11 +41,14 @@ function fnBeforeUpload(file: FileType) {
if (state.loading) return false;
const isJpgOrPng = ['image/jpeg', 'image/png'].includes(file.type);
if (!isJpgOrPng) {
message.error('只支持上传图片格式jpg、png', 3);
message.error(
t('views.system.setting.uploadFormat', { format: 'jpg、png' }),
3
);
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('图片文件大小必须小于 2MB', 3);
message.error(t('views.system.setting.uploadSize', { size: 2 }), 3);
}
return isJpgOrPng && isLt2M;
}
@@ -92,8 +97,8 @@ function fnEdit(v: boolean) {
filePath: '',
flag: `${appStore.logoType}/`,
type: appStore.logoType,
icon: appStore.getLOGOIcon,
brand: appStore.getLOGOBrand,
icon: getLogoURL('icon'),
brand: getLogoURL('brand'),
});
}
}
@@ -107,12 +112,16 @@ function fnSave() {
const reqArr = [];
// 改变LOGO地址
if (state.filePath) {
let changeFilePath = 'sys.logo.filePathIcon';
let changeFilePath = appStore.filePathIcon;
if (state.type === 'brand') {
changeFilePath = 'sys.logo.filePathBrand';
changeFilePath = appStore.filePathBrand;
}
reqArr.push(
changeValue({ key: changeFilePath, value: state.filePath })
transferStaticFile({
language: state.language,
uploadPath: state.filePath,
staticPath: changeFilePath,
})
);
}
// 判断类型是否改变
@@ -128,9 +137,6 @@ function fnSave() {
hide();
if (resArr[0].code === RESULT_CODE_SUCCESS) {
message.success(t('views.system.setting.saveSuccess'), 3);
if (state.filePath) {
appStore.setLOGO(state.type, state.filePath);
}
if (state.type !== appStore.logoType) {
appStore.logoType = state.type;
}
@@ -152,13 +158,38 @@ const changeStatus = computed(() => {
return true;
});
// LOGO地址
function getLogoURL(type: 'brand' | 'icon') {
let url =
type === 'brand'
? parseUrlPath(appStore.filePathBrand)
: parseUrlPath(appStore.filePathIcon);
if (url.indexOf('{language}') === -1) {
return url;
}
// 语言参数替换
const local = state.language;
const lang = local.split('_')[0];
return url.replace('{language}', lang || 'en');
}
// 监听语言切换
watch(
() => state.language,
() => {
state.icon = getLogoURL('icon');
state.brand = getLogoURL('brand');
}
);
onMounted(() => {
Object.assign(state, {
language: currentLocale.value,
filePath: '',
flag: `${appStore.logoType}/`,
type: appStore.logoType,
icon: appStore.getLOGOIcon,
brand: appStore.getLOGOBrand,
icon: getLogoURL('icon'),
brand: getLogoURL('brand'),
});
});
</script>
@@ -166,21 +197,39 @@ onMounted(() => {
<template>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24" style="margin-bottom: 30px">
<div class="header">
<div class="header-brand" v-show="state.type === 'brand'">
<img :width="174" :height="48" :src="state.brand" />
</div>
<div class="header-icon" v-show="state.type === 'icon'">
<img :src="state.icon" />
<h1 :title="appStore.appName">
{{ appStore.appName }}
</h1>
</div>
<div class="header-menu">
<IconFont type="icon-pcduan" style="margin-right: 10px"></IconFont>
{{ t('router.index') }}
</div>
</div>
<a-form layout="vertical">
<a-form-item style="margin-bottom: 12px">
<div class="header">
<div class="header-brand" v-show="state.type === 'brand'">
<img :width="174" :height="48" :src="state.brand" />
</div>
<div class="header-icon" v-show="state.type === 'icon'">
<img :src="state.icon" />
<h1 :title="appStore.appName">
{{ appStore.appName }}
</h1>
</div>
<div class="header-menu">
<IconFont
type="icon-pcduan"
style="margin-right: 10px"
></IconFont>
{{ t('router.index') }}
</div>
</div>
</a-form-item>
<a-form-item v-perms:has="['system:setting:i18n']">
<a-select v-model:value="state.language" style="width: 100px">
<a-select-option
v-for="opt in optionsLocale"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</a-select-option>
</a-select>
</a-form-item>
</a-form>
<a-form layout="vertical" v-if="state.edite">
<a-form-item>
@@ -257,7 +306,6 @@ onMounted(() => {
height: 48px;
padding-left: 16px;
background-color: #001529;
margin-bottom: 24px;
&-brand {
width: 174px;

View File

@@ -6,6 +6,7 @@ import ChangeTitle from './components/change-title.vue';
import ChangeCopyright from './components/change-copyright.vue';
import ChangeHelpDoc from './components/change-help-doc.vue';
import ChangeOfficialUrl from './components/change-official-url.vue';
import ChangeI18n from './components/change-i18n.vue';
import useI18n from '@/hooks/useI18n';
const { t } = useI18n();
</script>
@@ -29,14 +30,20 @@ const { t } = useI18n();
{{ t('views.system.setting.sysLoginBg') }}
</a-divider>
<ChangeLogoBG></ChangeLogoBG>
<a-divider orientation="left">
<a-divider orientation="left">
{{ t('views.system.setting.sysHelpDoc') }}
</a-divider>
</a-divider>
<ChangeHelpDoc></ChangeHelpDoc>
<a-divider orientation="left">
{{ t('views.system.setting.sysOfficialUrl') }}
</a-divider>
<ChangeOfficialUrl></ChangeOfficialUrl>
<div v-perms:has="['system:setting:i18n']">
<a-divider orientation="left">
{{ t('views.system.setting.i18n') }}
</a-divider>
<ChangeI18n></ChangeI18n>
</div>
</a-card>
</PageContainer>
</template>

View File

@@ -135,7 +135,7 @@ let tableColumns: ColumnsType = [
{
title: t('views.system.user.permission'),
key: 'roles',
align: 'center',
align: 'left',
},
{
title: t('views.system.user.className'),
@@ -889,7 +889,7 @@ onMounted(() => {
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<div class="button-container">
<a-button
type="primary"
@click.prevent="fnModalVisibleByEdit()"
@@ -924,12 +924,12 @@ onMounted(() => {
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
</a-space>
</div>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<div class="button-container">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
@@ -978,7 +978,7 @@ onMounted(() => {
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</div>
</template>
<!-- 表格列表 -->
@@ -1258,7 +1258,12 @@ onMounted(() => {
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form name="modalStateFromByEdit" layout="horizontal">
<a-form
name="modalStateFromByEdit"
layout="horizontal"
:label-col="{ span: 6 }"
:labelWrap="true"
>
<a-row :gutter="16" v-if="!modalState.from.userId">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
@@ -1309,22 +1314,20 @@ onMounted(() => {
></a-input>
</a-form-item>
</a-col>
<a-col :lg="6" :md="6" :xs="24">
<a-form-item :label="t('views.system.user.sex')" name="sex">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.system.user.userWork')"
name="postIds"
>
<a-select
v-model:value="modalState.from.sex"
default-value="1"
:options="dict.sysUserSex"
>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="6" :md="6" :xs="24">
<a-form-item :label="t('views.system.user.status')" name="status">
<a-select
v-model:value="modalState.from.status"
default-value="0"
:options="dict.sysNormalDisable"
v-model:value="modalState.from.postIds"
allow-clear
mode="multiple"
show-search
option-filter-prop="postName"
option-label-prop="postName"
:options="modalState.options.posts"
:field-names="{ label: 'postName', value: 'postId' }"
>
</a-select>
</a-form-item>
@@ -1360,7 +1363,53 @@ onMounted(() => {
</a-col>
</a-row>
<a-form-item :label="t('views.system.user.fromClass')" name="deptId">
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.system.user.sex')" name="sex">
<a-select
v-model:value="modalState.from.sex"
default-value="1"
:options="dict.sysUserSex"
>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.system.user.status')" name="status">
<a-select
v-model:value="modalState.from.status"
default-value="0"
:options="dict.sysNormalDisable"
>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-form-item
:label="t('views.system.user.permission')"
name="roleIds"
:label-col="{ span: 3 }"
v-perms:has="['system:user:editRole']"
>
<a-select
v-model:value="modalState.from.roleIds"
:allow-clear="false"
mode="multiple"
show-search
option-filter-prop="roleName"
option-label-prop="roleName"
:options="modalState.options.roles"
:field-names="{ label: 'roleName', value: 'roleId' }"
>
</a-select>
</a-form-item>
<a-form-item
:label="t('views.system.user.fromClass')"
name="deptId"
:label-col="{ span: 3 }"
>
<a-tree-select
v-model:value="modalState.from.deptId"
show-search
@@ -1379,46 +1428,11 @@ onMounted(() => {
</a-tree-select>
</a-form-item>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.system.user.userWork')"
name="postIds"
>
<a-select
v-model:value="modalState.from.postIds"
allow-clear
mode="multiple"
show-search
option-filter-prop="postName"
option-label-prop="postName"
:options="modalState.options.posts"
:field-names="{ label: 'postName', value: 'postId' }"
>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.system.user.permission')"
name="roleIds"
>
<a-select
v-model:value="modalState.from.roleIds"
:allow-clear="false"
mode="multiple"
show-search
option-filter-prop="roleName"
option-label-prop="roleName"
:options="modalState.options.roles"
:field-names="{ label: 'roleName', value: 'roleId' }"
>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-form-item :label="t('views.system.user.userTip')" name="remark">
<a-form-item
:label="t('views.system.user.userTip')"
name="remark"
:label-col="{ span: 3 }"
>
<a-textarea
v-model:value="modalState.from.remark"
:auto-size="{ minRows: 4, maxRows: 6 }"

View File

@@ -6,13 +6,14 @@ import { useRoute } from 'vue-router';
import { VuePDF, usePDF } from '@tato30/vue-pdf';
import '@tato30/vue-pdf/style.css';
import saveAs from 'file-saver';
import { parseUrlPath } from '@/plugins/file-static-url';
const { t, currentLocale } = useI18n();
const appStore = useAppStore();
const route = useRoute();
// 文档地址
const docUrl = computed(() => {
let url = appStore.getHelpDoc;
let url = parseUrlPath(appStore.helpDoc);
if (url.indexOf('{language}') === -1) {
return url;
}

View File

@@ -15,6 +15,7 @@ import {
tcpdumpPcapDownload,
} from '@/api/traceManage/pcap';
import { ref } from 'vue';
const neInfoStore = useNeInfoStore();
const { t } = useI18n();
/**对话框对象信息状态类型 */
@@ -136,6 +137,7 @@ function fnStart() {
cmd: from.cmd,
})
.then(res => {
console.log(res)
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.traceManage.pcap.execSuccess'),
@@ -151,7 +153,7 @@ function fnStart() {
msg.indexOf('tcpdump: listening on any,') !== -1;
} else if (
res.code === RESULT_CODE_ERROR &&
res.msg.includes('timeout')
res.msg.includes('imeout')
) {
message.warning({
content: t('views.traceManage.pcap.execBreak'),
@@ -337,7 +339,7 @@ onUnmounted(() => {
>
<a-cascader
v-model:value="modalState.neType"
:options="useNeInfoStore().getNeCascaderOtions"
:options="neInfoStore.getNeCascaderOptions"
@change="fnNeChange"
:allow-clear="false"
:placeholder="t('views.traceManage.pcap.neTypePlease')"

View File

@@ -19,6 +19,7 @@ import {
} from '@/api/traceManage/task';
import useDictStore from '@/store/modules/dict';
import { regExpIPv4, regExpPort } from '@/utils/regular-utils';
const neInfoStore = useNeInfoStore();
const { getDict } = useDictStore();
const { t } = useI18n();
@@ -382,7 +383,7 @@ function fnSelectInterface(s: any, _: any) {
/**信令接口选择初始 */
function fnSelectInterfaceInit(neType: string) {
const interfaces = useNeInfoStore().traceInterfaceList;
const interfaces = neInfoStore.traceInterfaceList;
modalState.neTypeInterface = interfaces
.filter(i => i.neType === neType)
.map(i => {
@@ -535,9 +536,9 @@ onMounted(() => {
});
Promise.allSettled([
// 获取网元网元列表
useNeInfoStore().fnNelist(),
neInfoStore.fnNelist(),
// 获取跟踪接口列表
useNeInfoStore().fnNeTraceInterface(),
neInfoStore.fnNeTraceInterface(),
]).finally(() => {
// 获取列表数据
fnGetList();
@@ -562,7 +563,7 @@ onMounted(() => {
>
<a-auto-complete
v-model:value="queryParams.neType"
:options="useNeInfoStore().getNeSelectOtions"
:options="neInfoStore.getNeSelectOtions"
allow-clear
:placeholder="t('views.traceManage.task.neTypePlease')"
/>
@@ -748,7 +749,7 @@ onMounted(() => {
>
<a-cascader
:value="modalState.neType"
:options="useNeInfoStore().getNeCascaderOtions"
:options="neInfoStore.getNeCascaderOptions"
disabled
/>
</a-form-item>
@@ -866,7 +867,7 @@ onMounted(() => {
>
<a-cascader
v-model:value="modalState.neType"
:options="useNeInfoStore().getNeCascaderOtions"
:options="neInfoStore.getNeCascaderOptions"
@change="fnNeChange"
:allow-clear="false"
:placeholder="t('views.traceManage.task.neTypePlease')"

View File

@@ -21,7 +21,7 @@ export default defineConfig(({ mode }) => {
// https://cn.vitejs.dev/config/#server-proxy
[env.VITE_API_BASE_URL]: {
// target: 'http://192.168.2.166:3030',
target: 'http://192.168.2.114:3040',
target: 'http://192.168.5.58:3040',
changeOrigin: true,
rewrite: p => p.replace(env.VITE_API_BASE_URL, ''),
},
@@ -70,7 +70,7 @@ export default defineConfig(({ mode }) => {
verbose: false,
algorithm: 'gzip',
ext: '.gz',
disable: false, // 是否禁用
disable: true, // 是否禁用
}),
],
};