Compare commits
487
Commits
2fb3467fb5
..
psap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5637af9798 | ||
|
|
acdf580ba5 | ||
|
|
886fb7a7c0 | ||
|
|
0ac4e1b7ff | ||
|
|
8137808d44 | ||
|
|
d4b0f9f343 | ||
|
|
b7ae3c0cdb | ||
|
|
5e21c8d53d | ||
|
|
6598f7c67b | ||
|
|
c6b5d9861c | ||
|
|
b1776f66a0 | ||
|
|
03082ba11f | ||
|
|
3e67ebbe8a | ||
|
|
3372d9929b | ||
|
|
bcca03d983 | ||
|
|
7b91ffa19d | ||
|
|
772593e6da | ||
|
|
5a4f12687e | ||
|
|
f5684d867f | ||
|
|
14df112884 | ||
|
|
1a46b1948b | ||
|
|
db20ced6f3 | ||
|
|
50543a241c | ||
|
|
eb809f4f3d | ||
|
|
4cba38f0c0 | ||
|
|
34cbcb808a | ||
|
|
e53328176c | ||
|
|
c1fe8cd4c5 | ||
|
|
42c4e19f84 | ||
|
|
270f9915dc | ||
|
|
af15418695 | ||
|
|
331315ab84 | ||
|
|
43034fd082 | ||
|
|
7e790d90b8 | ||
|
|
0a19aaa8df | ||
|
|
bfa80e10f5 | ||
|
|
edd399ac5c | ||
|
|
9a47e34dfc | ||
|
|
ab1532c2f5 | ||
|
|
eed90b24ef | ||
|
|
4e63395383 | ||
|
|
956cbfc3a3 | ||
|
|
f5b843d9a8 | ||
|
|
1246308a3d | ||
|
|
e1343ce228 | ||
|
|
0cb7158f57 | ||
|
|
63c7ae2538 | ||
|
|
48ddafaec9 | ||
|
|
eeeae3dd12 | ||
|
|
f0a5da681c | ||
|
|
35c7b86865 | ||
|
|
cef90a49f9 | ||
|
|
c9a0fd7818 | ||
|
|
7e35dca9d8 | ||
|
|
860e06e7b0 | ||
|
|
b352533523 | ||
|
|
a8a5c0a31e | ||
|
|
26686f88db | ||
|
|
fb3f1daecf | ||
|
|
3680da64c1 | ||
|
|
f87fcb73b9 | ||
|
|
c11227d747 | ||
|
|
8a612e0760 | ||
|
|
29f5e41976 | ||
|
|
3ab0b47095 | ||
|
|
aa04abdbb4 | ||
|
|
db95099934 | ||
|
|
71ef748af8 | ||
|
|
91c9829d77 | ||
|
|
5304b298f6 | ||
|
|
e09369aa5a | ||
|
|
3363e36669 | ||
|
|
ea2ce56e52 | ||
|
|
dee60e0699 | ||
|
|
e62fc0c039 | ||
|
|
de16b96971 | ||
|
|
f0e34726ec | ||
|
|
2cbd2e0aa7 | ||
|
|
16913aa721 | ||
|
|
ba426d7737 | ||
|
|
8df5e278c8 | ||
|
|
ea8fb7cad2 | ||
|
|
b7b8b11860 | ||
|
|
bae61108be | ||
|
|
f60e530636 | ||
|
|
b3e9761305 | ||
|
|
fbd2867ad2 | ||
|
|
2ca23ad99a | ||
|
|
4b032d74be | ||
|
|
8bfa73a67a | ||
|
|
f188e193f3 | ||
|
|
b362855a60 | ||
|
|
4c28d6b98c | ||
|
|
2276f2281a | ||
|
|
7ccb580e91 | ||
|
|
0346dfd584 | ||
|
|
5a64afe209 | ||
|
|
fd82d710b6 | ||
|
|
fbc1535015 | ||
|
|
36de89570f | ||
|
|
22e595131c | ||
|
|
208d14d65a | ||
|
|
80b9cd83fb | ||
|
|
721ec4a5da | ||
|
|
34f558199a | ||
|
|
07eab9378a | ||
|
|
806cbbd9ed | ||
|
|
8adf2a3dd0 | ||
|
|
2164ffc9b2 | ||
|
|
7091f1ffa6 | ||
|
|
fa44f6abe0 | ||
|
|
8586d7f1ce | ||
|
|
b2d818fc30 | ||
|
|
a20d5ee99f | ||
|
|
74b55423d5 | ||
|
|
96acbc0919 | ||
|
|
322b5f18ed | ||
|
|
e36dac9b81 | ||
|
|
8214175890 | ||
|
|
9e55768312 | ||
|
|
98ed8adfe3 | ||
|
|
f5938110f4 | ||
|
|
acd8a33b4a | ||
|
|
7ab2b3b546 | ||
|
|
b490e4f5b9 | ||
|
|
fe82336937 | ||
|
|
35a7ed5b35 | ||
|
|
56def56b58 | ||
|
|
6074078f5d | ||
|
|
428adb5186 | ||
|
|
1cbce9ad03 | ||
|
|
2138896d43 | ||
|
|
c40ee9c8cc | ||
|
|
d33183ca5e | ||
|
|
9ff9529402 | ||
|
|
51a8d6d3a0 | ||
|
|
c22663505c | ||
|
|
9b589a0e69 | ||
|
|
b0b9c69ad2 | ||
|
|
5a3fa2a6ba | ||
|
|
5c0909e356 | ||
|
|
b7da976819 | ||
|
|
41e37766b7 | ||
|
|
44612081bc | ||
|
|
4929ed30bc | ||
|
|
eea4e0069d | ||
|
|
2b69b8d72b | ||
|
|
1f130098ee | ||
|
|
5a4ab62e97 | ||
|
|
139a14fd3d | ||
|
|
6e7402fd63 | ||
|
|
302ea84cde | ||
|
|
7d470fd681 | ||
|
|
b64c4c66ab | ||
|
|
15ac549532 | ||
|
|
9bff669769 | ||
|
|
8a53ac8b9f | ||
|
|
d3a18f95db | ||
|
|
11649c3fb1 | ||
|
|
09fd8bc4dc | ||
|
|
5a704146a5 | ||
|
|
e25cd91df1 | ||
|
|
71f2e596fe | ||
|
|
7e60f0dd05 | ||
|
|
a94f9414a4 | ||
|
|
7f69bc69bc | ||
|
|
4b1058cff3 | ||
|
|
a6bab3fa0b | ||
|
|
c44fae8d13 | ||
|
|
bcb214448c | ||
|
|
65db17a319 | ||
|
|
6969669027 | ||
|
|
38a698f07b | ||
|
|
9f121505d1 | ||
|
|
6add41254d | ||
|
|
85bc4aea53 | ||
|
|
9167da1bc5 | ||
|
|
69dfc2a1f5 | ||
|
|
b1a699252b | ||
|
|
a0886abd38 | ||
|
|
9383c17484 | ||
|
|
512bd6d8eb | ||
|
|
b8b66fe610 | ||
|
|
154569304c | ||
|
|
cc3432ca06 | ||
|
|
68b9c5fa5e | ||
|
|
6620ac7279 | ||
|
|
a9a094d04a | ||
|
|
5d4a04ecf2 | ||
|
|
1b28260680 | ||
|
|
83cb3f8728 | ||
|
|
7d69d3c21d | ||
|
|
ebde50f58b | ||
|
|
b8924d161f | ||
|
|
ccb52ea94f | ||
|
|
72abbe1c53 | ||
|
|
f318f61b4a | ||
|
|
45d8314e29 | ||
|
|
886a1c8667 | ||
|
|
378729720d | ||
|
|
c2a3d4b8a8 | ||
|
|
8444de8e98 | ||
|
|
874e01996a | ||
|
|
fb855fd74e | ||
|
|
78f963fbea | ||
|
|
cf0116b5c6 | ||
|
|
80b07c462f | ||
|
|
5a8ab1343f | ||
|
|
67349e24d8 | ||
|
|
f59697a2f2 | ||
|
|
8c197bee04 | ||
|
|
f866fbf153 | ||
|
|
d7b4fd3f71 | ||
|
|
29449cc597 | ||
|
|
7615bccf04 | ||
|
|
f5f27d78f1 | ||
|
|
ebc46ff7d4 | ||
|
|
2513baf48e | ||
|
|
ac2483d690 | ||
|
|
a85f87f3fc | ||
|
|
68e002776c | ||
|
|
9322f52c9a | ||
|
|
2c9807f9b8 | ||
|
|
f8f4dc0f2e | ||
|
|
49bd59c639 | ||
|
|
6bd7a28458 | ||
|
|
053517324d | ||
|
|
256802e698 | ||
|
|
851e8a461e | ||
|
|
d8d49f23c4 | ||
|
|
1511792e3b | ||
|
|
045a25f3ae | ||
|
|
b6d1ba6766 | ||
|
|
e8e0e07189 | ||
|
|
1672c6c6ba | ||
|
|
ac251c2c00 | ||
|
|
9b9c0b39fd | ||
|
|
31bca2b98f | ||
|
|
bf94591035 | ||
|
|
d523b60311 | ||
|
|
e0990a40df | ||
|
|
20c1c455c4 | ||
|
|
dab76add73 | ||
|
|
101cb70893 | ||
|
|
348b11e201 | ||
|
|
a9fdda3f5e | ||
|
|
89d22e55c7 | ||
|
|
40f2a78717 | ||
|
|
80ee1c05ff | ||
|
|
021f9f28f6 | ||
|
|
d7990a6ee5 | ||
|
|
3561a5dc39 | ||
|
|
247a009eef | ||
|
|
fcd4db8217 | ||
|
|
3e03d47520 | ||
|
|
414afea783 | ||
|
|
df7c455881 | ||
|
|
1644765ce2 | ||
|
|
db16cdb79b | ||
|
|
800547d1ef | ||
|
|
5614be7877 | ||
|
|
0644e49161 | ||
|
|
15b81eef97 | ||
|
|
5ddf83d1fd | ||
|
|
6326f46bf2 | ||
|
|
f75719ca37 | ||
|
|
21cf86baff | ||
|
|
6583bc9972 | ||
|
|
886ea37702 | ||
|
|
23116db988 | ||
|
|
8283523327 | ||
|
|
33159befc3 | ||
|
|
c567b19fb2 | ||
|
|
d8487d7cd7 | ||
|
|
347c9f1d3b | ||
|
|
a731a6408b | ||
|
|
c0ac1f6ed5 | ||
|
|
544c3697bd | ||
|
|
33a8ce97d3 | ||
|
|
6ee9d464fb | ||
|
|
df5072bae7 | ||
|
|
ae94e3bf2a | ||
|
|
2276445ff6 | ||
|
|
e12dce1f0f | ||
|
|
d0457fc285 | ||
|
|
e04fd4077e | ||
|
|
63d32f0a39 | ||
|
|
99565dd652 | ||
|
|
7e03437ab6 | ||
|
|
5f9d19ac65 | ||
|
|
f23d4117d7 | ||
|
|
e4a56d68e0 | ||
|
|
cf5d08aaab | ||
|
|
7ad566d74f | ||
|
|
c312186d91 | ||
|
|
e3f7b08c69 | ||
|
|
6e3ef7e56a | ||
|
|
0a96fee6c3 | ||
|
|
dc7d24e2bf | ||
|
|
089ae12dd1 | ||
|
|
da0d49d306 | ||
|
|
670225a655 | ||
|
|
fa35bfc340 | ||
|
|
f4a5d28a29 | ||
|
|
c735aeba6d | ||
|
|
3c058ec107 | ||
|
|
327e82e057 | ||
|
|
012fc44f08 | ||
|
|
72fd372fe0 | ||
|
|
acdadcbb6f | ||
|
|
7a49de71ea | ||
|
|
56e4419e77 | ||
|
|
3abb4dd4bd | ||
|
|
726a284ab5 | ||
|
|
a45243390b | ||
|
|
1faed9bc3d | ||
|
|
9bd700eeb7 | ||
|
|
5cc3b9c8cf | ||
|
|
208895c7d5 | ||
|
|
46578ce97b | ||
|
|
0ff5bd5e20 | ||
|
|
f08e637e69 | ||
|
|
a600e056b8 | ||
|
|
671c80972e | ||
|
|
d07230b582 | ||
|
|
35c24407ac | ||
|
|
cf33756548 | ||
|
|
1ef98298bc | ||
|
|
b1c2a95ec4 | ||
|
|
147b2fad8d | ||
|
|
b629088406 | ||
|
|
430a067280 | ||
|
|
8a71e8f773 | ||
|
|
ff556ce1ec | ||
|
|
9ed7aed4b4 | ||
|
|
9e14297488 | ||
|
|
8e70706ed5 | ||
|
|
3e0529cf87 | ||
|
|
91af2bed92 | ||
|
|
1ecefb91dc | ||
|
|
72d9895902 | ||
|
|
41fa214137 | ||
|
|
1565f25a03 | ||
|
|
c5c2926d99 | ||
|
|
55456f9220 | ||
|
|
cf1686c348 | ||
|
|
f7833bcd9f | ||
|
|
5a621053a4 | ||
|
|
2a6451ef2a | ||
|
|
f1b440c8dd | ||
|
|
a67e54ca6e | ||
|
|
53d9e63c36 | ||
|
|
b4623d19e5 | ||
|
|
700bff6e38 | ||
|
|
d77c4e43d4 | ||
|
|
6e11d2b16a | ||
|
|
405842bc0b | ||
|
|
bf8d7f2124 | ||
|
|
ba98b37306 | ||
|
|
aa8ed65fd8 | ||
|
|
936a4410b3 | ||
|
|
58ec76f9e5 | ||
|
|
c1a77c8e48 | ||
|
|
477e8e4631 | ||
|
|
4f9d65a3a7 | ||
|
|
b1799d8ccb | ||
|
|
86833e7d6b | ||
|
|
59cf57898b | ||
|
|
fb9382e3a0 | ||
|
|
dae4697cd2 | ||
|
|
1b2e892f74 | ||
|
|
c66c640f75 | ||
|
|
30849416b6 | ||
|
|
5edcee8da5 | ||
|
|
311beed2a7 | ||
|
|
eb5fdfb635 | ||
|
|
78bcde9ef2 | ||
|
|
630e2a16ad | ||
|
|
e1fe031f25 | ||
|
|
855ba7dc9e | ||
|
|
3a72e73d5d | ||
|
|
4cb13a1419 | ||
|
|
8dd84a5255 | ||
|
|
c0e62f48b7 | ||
|
|
b992225e28 | ||
|
|
2f04562a34 | ||
|
|
d3a452cfd8 | ||
|
|
d81b8cdf38 | ||
|
|
39a417368a | ||
|
|
adfce5d2f7 | ||
|
|
977286d6b3 | ||
|
|
c33000045a | ||
|
|
b995ac378a | ||
|
|
6bea64f345 | ||
|
|
94886e255e | ||
|
|
45f66afe52 | ||
|
|
b9105c1e77 | ||
|
|
909d306942 | ||
|
|
f7273457e9 | ||
|
|
2e5ad2f65d | ||
|
|
776e9c5837 | ||
|
|
0d4979d3d9 | ||
|
|
686c7dd273 | ||
|
|
d41b308c6d | ||
|
|
84dac247d2 | ||
|
|
f8439bb40a | ||
|
|
d268d920e7 | ||
|
|
f730ef1e3a | ||
|
|
af1ce32063 | ||
|
|
678ff2d09d | ||
|
|
48f674b6ef | ||
|
|
02f0820a69 | ||
|
|
ca8605fd6e | ||
|
|
6d5e96421b | ||
|
|
bcc29007bf | ||
|
|
bdf904078d | ||
|
|
e37cfa5066 | ||
|
|
f1bff23bbc | ||
|
|
53106ddb5c | ||
|
|
3a04882fe5 | ||
|
|
19202a5e81 | ||
|
|
7b311ff673 | ||
|
|
9dba98e0ee | ||
|
|
71338670f0 | ||
|
|
7dcdfabce2 | ||
|
|
ddfe1723c9 | ||
|
|
57b5f76db7 | ||
|
|
9ac3524877 | ||
|
|
ca82a0a74b | ||
|
|
23007c3bf2 | ||
|
|
5d69d7612a | ||
|
|
ddd8930af4 | ||
|
|
757f2ec20a | ||
|
|
30caa79424 | ||
|
|
e3f83a0b98 | ||
|
|
147a3ed77b | ||
|
|
5d35d950b3 | ||
|
|
6874508d3f | ||
|
|
33f468209a | ||
|
|
e8ef2816df | ||
|
|
e38d7bbffa | ||
|
|
2f1265c47a | ||
|
|
66b6b60505 | ||
|
|
249d14320d | ||
|
|
313b90ad31 | ||
|
|
2ebc90e974 | ||
|
|
640257dd55 | ||
|
|
c1a3ce8068 | ||
|
|
0080e9c26e | ||
|
|
2ccafe622d | ||
|
|
d7a515ed9a | ||
|
|
cd82b71b77 | ||
|
|
9d6a7dcd9c | ||
|
|
46c2affcc8 | ||
|
|
3d00a80588 | ||
|
|
a3c1fe154f | ||
|
|
07dce5a27e | ||
|
|
255cf026a6 | ||
|
|
840ea56c42 | ||
|
|
09917cc9c9 | ||
|
|
4c9fe192f2 | ||
|
|
32ec55d44e | ||
|
|
527cf89d1a | ||
|
|
ac7b57c0ae | ||
|
|
8be1a8968e | ||
|
|
999ccf64ad | ||
|
|
03352f3aa8 | ||
|
|
61a58fc661 | ||
|
|
4268fa3198 | ||
|
|
f6b62c6c7e | ||
|
|
b4cbc1c190 | ||
|
|
1871f6f656 | ||
|
|
409f9836a6 | ||
|
|
b3f40ee683 | ||
|
|
aa07b51663 | ||
|
|
19b77ed005 | ||
|
|
06503fd079 | ||
|
|
2321dacd2a | ||
|
|
a8b4e91b95 | ||
|
|
a5075bef43 | ||
|
|
f4ffbc1c86 | ||
|
|
6cafa284c7 | ||
|
|
049c0e7a0f | ||
|
|
377ffc6e10 | ||
|
|
858431e86e | ||
|
|
70fca5ca41 | ||
|
|
e972d14a9a |
No files matched your search
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
# 历史路径-哈希带井号标识
|
# 历史路径-哈希带井号标识
|
||||||
VITE_HISTORY_HASH = false
|
VITE_HISTORY_HASH = true
|
||||||
|
|
||||||
# 历史路径-前缀URL如:/h5
|
# 历史路径-前缀URL如:/h5
|
||||||
VITE_HISTORY_BASE_URL = "/"
|
VITE_HISTORY_BASE_URL = "/"
|
||||||
@@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network OMC"
|
|||||||
VITE_APP_CODE = "OMC"
|
VITE_APP_CODE = "OMC"
|
||||||
|
|
||||||
# 应用版本
|
# 应用版本
|
||||||
VITE_APP_VERSION = "2.241115"
|
VITE_APP_VERSION = "2.250412"
|
||||||
|
|
||||||
# 接口基础URL地址-不带/后缀
|
# 接口基础URL地址-不带/后缀
|
||||||
VITE_API_BASE_URL = "/omc-api"
|
VITE_API_BASE_URL = "/omc-api"
|
||||||
+1
-1
@@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network OMC"
|
|||||||
VITE_APP_CODE = "OMC"
|
VITE_APP_CODE = "OMC"
|
||||||
|
|
||||||
# 应用版本
|
# 应用版本
|
||||||
VITE_APP_VERSION = "2.241115"
|
VITE_APP_VERSION = "2.250412"
|
||||||
|
|
||||||
# 接口基础URL地址-不带/后缀
|
# 接口基础URL地址-不带/后缀
|
||||||
VITE_API_BASE_URL = "/omc-api"
|
VITE_API_BASE_URL = "/omc-api"
|
||||||
+22
-22
@@ -13,34 +13,34 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons-vue": "^7.0.1",
|
"@ant-design/icons-vue": "^7.0.1",
|
||||||
"@antv/g6": "~4.8.24",
|
"@antv/g6": "4.8.24",
|
||||||
"@codemirror/lang-javascript": "^6.2.2",
|
"@codemirror/lang-javascript": "^6.2.2",
|
||||||
"@codemirror/lang-yaml": "^6.1.1",
|
"@codemirror/lang-yaml": "^6.1.2",
|
||||||
"@codemirror/merge": "^6.7.2",
|
"@codemirror/merge": "^6.8.0",
|
||||||
"@codemirror/theme-one-dark": "^6.1.2",
|
"@codemirror/theme-one-dark": "^6.1.2",
|
||||||
"@tato30/vue-pdf": "^1.11.2",
|
"@tato30/vue-pdf": "^1.11.3",
|
||||||
"@vueuse/core": "11.2.0",
|
"@vueuse/core": "^12.5.0",
|
||||||
"@xterm/addon-fit": "^0.10.0",
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
"@xterm/xterm": "^5.5.0",
|
"@xterm/xterm": "^5.5.0",
|
||||||
"ant-design-vue": "^4.2.5",
|
"ant-design-vue": "^4.2.6",
|
||||||
"antdv-pro-layout": "^4.1.9",
|
"antdv-pro-layout": "^4.2.0",
|
||||||
"antdv-pro-modal": "^4.0.5",
|
"antdv-pro-modal": "^4.0.6",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"dayjs": "^1.11.11",
|
"dayjs": "^1.11.11",
|
||||||
"echarts": "~5.5.0",
|
"echarts": "~5.6.0",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"grid-layout-plus": "^1.0.5",
|
"grid-layout-plus": "^1.0.6",
|
||||||
"intl-tel-input": "^24.6.0",
|
"intl-tel-input": "~25.2.0",
|
||||||
"js-base64": "^3.7.7",
|
"js-base64": "^3.7.7",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"p-queue": "~8.0.1",
|
"p-queue": "~8.0.1",
|
||||||
"pinia": "2.2.6",
|
"pinia": "^2.3.0",
|
||||||
"vue": "^3.5.12",
|
"vue": "^3.5.13",
|
||||||
"vue-i18n": "^10.0.4",
|
"vue-i18n": "^11.1.0",
|
||||||
"vue-router": "^4.4.5",
|
"vue-router": "^4.5.0",
|
||||||
"vue3-smooth-dnd": "^0.0.6",
|
"vue3-smooth-dnd": "^0.0.6",
|
||||||
"xlsx": "~0.18.5"
|
"xlsx": "~0.18.5"
|
||||||
},
|
},
|
||||||
@@ -48,14 +48,14 @@
|
|||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/file-saver": "^2.0.7",
|
"@types/file-saver": "^2.0.7",
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
"@types/node": "^22.7.7",
|
"@types/node": "^18.0.0",
|
||||||
"@types/nprogress": "^0.2.3",
|
"@types/nprogress": "^0.2.3",
|
||||||
"@vitejs/plugin-vue": "^5.1.4",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
"less": "^4.2.0",
|
"less": "^4.2.1",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "~5.6.3",
|
||||||
"unplugin-vue-components": "^0.27.4",
|
"unplugin-vue-components": "^0.28.0",
|
||||||
"vite": "5.4.10",
|
"vite": "^6.1.0",
|
||||||
"vite-plugin-compression": "~0.5.1",
|
"vite-plugin-compression": "~0.5.1",
|
||||||
"vue-tsc": "^2.1.8"
|
"vue-tsc": "^2.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,12 +10,19 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
(function () {
|
(function () {
|
||||||
// host = ip:port
|
// baseUrl = protocol://ip:port
|
||||||
// const host = '192.168.8.100:33030';
|
// baseUrl = 'http://192.168.8.100:33030';
|
||||||
const host = `${window.location.hostname}:33030`;
|
const protocol = window.location.protocol
|
||||||
|
let wsprotocol = "ws:"
|
||||||
|
const hostname = window.location.hostname
|
||||||
|
let host = `${hostname}:33030`;
|
||||||
|
if (protocol === 'https:') {
|
||||||
|
host = `${hostname}:33443`;
|
||||||
|
wsprotocol = "wss:"
|
||||||
|
}
|
||||||
|
|
||||||
// Service Address
|
// Service Address
|
||||||
sessionStorage.setItem('baseUrl', `http://${host}`);
|
sessionStorage.setItem('baseUrl', `${protocol}//${host}`);
|
||||||
// websocket Address
|
// websocket Address
|
||||||
sessionStorage.setItem('wsUrl', `ws://${host}`);
|
sessionStorage.setItem('wsUrl', `${wsprotocol}//${host}`);
|
||||||
})();
|
})();
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,28 @@
|
|||||||
|
<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<!-- Created with Method Draw - http://github.com/duopixel/Method-Draw/ -->
|
||||||
|
|
||||||
|
<g>
|
||||||
|
<title>background</title>
|
||||||
|
<rect fill="none" id="canvas_background" height="1026" width="1026" y="-1" x="-1"/>
|
||||||
|
<g display="none" overflow="visible" y="0" x="0" height="100%" width="100%" id="canvasGrid">
|
||||||
|
<rect fill="url(#gridpattern)" stroke-width="0" y="1" x="1" height="768" width="1024"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<title>Layer 1</title>
|
||||||
|
<g stroke="null" id="svg_15">
|
||||||
|
<path stroke="null" id="svg_4" fill="#B5D6FB" d="m512.094844,961.632039c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-405.588164,-251.575028c-2.275921,-1.419037 -3.698372,-4.054392 -3.698372,-6.892467l0,-90.007504c0,-2.838074 1.422451,-5.473429 3.698372,-6.892467l405.588164,-255.426701c1.137961,-0.709519 2.465582,-1.114958 3.793202,-1.114958s2.655242,0.405439 3.793202,1.114958l405.493334,255.426701c2.275921,1.419037 3.698372,4.054392 3.698372,6.892467l0,90.007504c0,2.838074 -1.422451,5.473429 -3.698372,6.892467l-405.588164,251.575028c-1.137961,0.709519 -2.465582,1.114958 -3.698372,1.114958z"/>
|
||||||
|
<path stroke="null" id="svg_5" fill="#0276F7" d="m512.094844,356.615382l405.398504,255.426701l0,90.007504l-66.096551,40.94936l-339.301952,210.625668l-339.491613,-210.625668l-66.096551,-40.94936l0,-90.007504l405.588164,-255.426701m0,-16.014849c-2.655242,0 -5.215653,0.709519 -7.586405,2.229916l-405.588164,255.426701c-4.551843,2.838074 -7.396745,8.108784 -7.396745,13.784933l0,90.007504c0,5.676149 2.844902,10.946859 7.491575,13.886293l66.096551,41.05072l339.491613,210.625668c2.275921,1.419037 4.931163,2.128556 7.491575,2.128556s5.215653,-0.709519 7.491575,-2.128556l339.301952,-210.625668l66.096551,-40.94936c4.646673,-2.838074 7.491575,-8.108784 7.491575,-13.886293l0,-90.007504c0,-5.676149 -2.844902,-10.946859 -7.396745,-13.784933l-405.398504,-255.426701c-2.370751,-1.520397 -5.025993,-2.331275 -7.586405,-2.331275z"/>
|
||||||
|
<path stroke="null" id="svg_6" fill="#FFFFFF" d="m106.50668,612.042083l405.493334,253.298145l405.493334,-253.298145l-405.398504,-255.426701l-405.588164,255.426701z"/>
|
||||||
|
<path stroke="null" id="svg_7" fill="#D4E4FC" d="m501.473877,64.192353l-254.9032,498.487506l263.343075,161.162085l266.662127,-162.074323l-275.102002,-497.575268z"/>
|
||||||
|
<path stroke="null" id="svg_8" fill="#0276F7" d="m229.975417,602.311542c-1.232791,0 -2.465582,-0.304079 -3.698372,-1.013598c-3.603542,-2.128556 -4.931163,-6.993826 -2.844902,-10.845499l279.653845,-532.13896c1.327621,-2.533995 3.793202,-4.054392 6.543274,-4.054392c2.655242,0 5.120823,1.520397 6.543274,4.054392l284.395348,532.13896c2.086261,3.851672 0.75864,8.716943 -2.750072,10.946859c-3.603542,2.128556 -8.155385,0.810878 -10.241646,-2.939434l-277.852074,-519.874424l-273.205401,519.671704c-1.422451,2.635355 -3.982862,4.054392 -6.543274,4.054392z"/>
|
||||||
|
<path stroke="null" id="svg_9" fill="#0276F7" d="m509.913752,755.567562c-4.172523,0 -7.491575,-3.547593 -7.491575,-8.007424l0,-666.744777c0,-4.459831 3.319052,-8.007424 7.491575,-8.007424s7.491575,3.547593 7.491575,8.007424l0,666.846137c0,4.358471 -3.413882,7.906065 -7.491575,7.906065z"/>
|
||||||
|
<path stroke="null" id="svg_10" fill="#0276F7" d="m509.913752,731.849369c-1.327621,0 -2.560412,-0.405439 -3.698372,-1.013598l-263.343075,-161.162085c-3.603542,-2.229916 -4.836333,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.939434l259.549873,158.83081l262.963755,-159.844408c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-266.662127,162.074323c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598z"/>
|
||||||
|
<path stroke="null" id="svg_11" fill="#0276F7" d="m509.913752,579.708306c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-201.988026,-125.686154c-3.603542,-2.229916 -4.741503,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.838074l198.289654,123.354879l201.798366,-122.138561c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.845499l-205.496739,124.469837c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598zm-2.465582,-157.513132c-1.232791,0 -2.370751,-0.304079 -3.508712,-0.912238l-140.917468,-79.668804c-3.698372,-2.128556 -5.025993,-6.892467 -3.129392,-10.845499c1.896601,-3.953032 6.448444,-5.37207 10.146816,-3.344873l137.503586,77.742968l143.00373,-79.871524c3.698372,-2.027196 8.155385,-0.506799 10.146816,3.344873c1.896601,3.953032 0.47415,8.716943 -3.129392,10.845499l-146.512442,81.79736c-1.232791,0.608159 -2.370751,0.912238 -3.603542,0.912238zm2.465582,-148.49211c-1.232791,0 -2.465582,-0.304079 -3.508712,-0.912238l-82.312492,-47.436387c-3.603542,-2.128556 -5.025993,-6.993826 -3.034562,-10.845499c1.991431,-3.953032 6.543274,-5.27071 10.146816,-3.243514l78.708949,45.409191l78.329629,-47.537747c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-81.933171,49.666303c-1.232791,0.608159 -2.560412,0.912238 -3.793202,0.912238z"/>
|
||||||
|
<path stroke="null" id="svg_12" fill="#0276F7" d="m509.913752,579.708306l-0.28449,0l-263.248245,-9.021022c-4.172523,-0.10136 -7.396745,-3.851672 -7.207085,-8.210144c0.09483,-4.459831 4.077693,-7.703345 7.681235,-7.703345l263.343075,9.021022c4.172523,0.10136 7.396745,3.851672 7.207085,8.210144c-0.18966,4.257112 -3.508712,7.703345 -7.491575,7.703345zm0,152.141063c-1.612111,0 -3.224222,-0.608159 -4.646673,-1.723117c-3.224222,-2.736715 -3.793202,-7.804705 -1.232791,-11.250938l205.496739,-276.610899c2.560412,-3.446233 7.301915,-4.054392 10.526137,-1.317677c3.224222,2.736715 3.793202,7.804705 1.232791,11.250938l-205.496739,276.610899c-1.517281,2.027196 -3.698372,3.040794 -5.879464,3.040794z"/>
|
||||||
|
<path stroke="null" id="svg_13" fill="#0276F7" d="m509.913752,579.708306c-1.422451,0 -2.750072,-0.405439 -4.077693,-1.216318c-3.508712,-2.432635 -4.457013,-7.297906 -2.275921,-11.048218l144.14169,-239.310492c2.275921,-3.750313 6.922594,-4.763911 10.336476,-2.432635c3.508712,2.432635 4.457013,7.297906 2.275921,11.048218l-144.14169,239.310492c-1.422451,2.331275 -3.793202,3.648953 -6.258784,3.648953zm-2.465582,-157.513132c-1.043131,0 -2.086261,-0.20272 -3.129392,-0.709519c-3.793202,-1.824476 -5.405313,-6.588387 -3.698372,-10.642779l84.398753,-198.158413c1.706941,-4.054392 6.069124,-5.777509 9.957156,-3.953032c3.793202,1.824476 5.405313,6.588387 3.698372,10.642779l-84.303923,198.158413c-1.327621,2.939434 -4.077693,4.662551 -6.922594,4.662551z"/>
|
||||||
|
<path stroke="null" id="svg_14" fill="#0276F7" d="m591.846924,375.062866c-2.750072,0 -5.405313,-1.621757 -6.732934,-4.459831c-1.801771,-3.953032 -0.28449,-8.716943 3.413882,-10.642779l129.253371,-67.302908l-365.759539,-178.089172l20.862613,208.091673l133.994874,-64.262114c3.698372,-1.824476 8.155385,0 9.862326,4.054392c1.706941,4.054392 0,8.716943 -3.793202,10.541419l-143.38305,68.823305c-2.181091,1.013598 -4.646673,0.912238 -6.827764,-0.405439c-2.086261,-1.317677 -3.413882,-3.547593 -3.698372,-6.081588l-23.328195,-233.026185c-0.28449,-2.838074 0.853471,-5.676149 3.034562,-7.297906c2.181091,-1.621757 5.025993,-2.027196 7.491575,-0.810878l392.217126,190.961867c2.655242,1.317677 4.362183,4.054392 4.362183,7.196546c0,3.142154 -1.612111,5.980228 -4.172523,7.297906l-143.57271,74.600814c-1.043131,0.608159 -2.181091,0.810878 -3.224222,0.810878zm-283.921198,78.959286c-3.603542,0 -6.827764,-2.838074 -7.396745,-6.791107c-0.56898,-4.358471 2.181091,-8.412864 6.258784,-9.122382l199.617275,-31.826978c4.077693,-0.608159 7.870895,2.331275 8.534705,6.689747c0.56898,4.358471 -2.181091,8.412864 -6.258784,9.122382l-199.617275,31.826978c-0.47415,0.10136 -0.853471,0.10136 -1.137961,0.10136z"/>
|
||||||
|
</g>
|
||||||
|
<text stroke="null" font-style="italic" transform="matrix(6.577099502228161,0,0,7.449448263868419,-1073.2057632249744,-908.8606073938396) " xml:space="preserve" text-anchor="start" font-family="Arvo, sans-serif" font-size="24" id="svg_16" y="177.898525" x="178.621382" stroke-width="0" fill="#B5D6FB">4G</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.9 KiB |
@@ -0,0 +1,28 @@
|
|||||||
|
<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<!-- Created with Method Draw - http://github.com/duopixel/Method-Draw/ -->
|
||||||
|
|
||||||
|
<g>
|
||||||
|
<title>background</title>
|
||||||
|
<rect fill="none" id="canvas_background" height="1026" width="1026" y="-1" x="-1"/>
|
||||||
|
<g display="none" overflow="visible" y="0" x="0" height="100%" width="100%" id="canvasGrid">
|
||||||
|
<rect fill="url(#gridpattern)" stroke-width="0" y="1" x="1" height="768" width="1024"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<title>Layer 1</title>
|
||||||
|
<g stroke="null" id="svg_15">
|
||||||
|
<path stroke="null" id="svg_4" fill="#B5D6FB" d="m512.094844,961.632039c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-405.588164,-251.575028c-2.275921,-1.419037 -3.698372,-4.054392 -3.698372,-6.892467l0,-90.007504c0,-2.838074 1.422451,-5.473429 3.698372,-6.892467l405.588164,-255.426701c1.137961,-0.709519 2.465582,-1.114958 3.793202,-1.114958s2.655242,0.405439 3.793202,1.114958l405.493334,255.426701c2.275921,1.419037 3.698372,4.054392 3.698372,6.892467l0,90.007504c0,2.838074 -1.422451,5.473429 -3.698372,6.892467l-405.588164,251.575028c-1.137961,0.709519 -2.465582,1.114958 -3.698372,1.114958z"/>
|
||||||
|
<path stroke="null" id="svg_5" fill="#0276F7" d="m512.094844,356.615382l405.398504,255.426701l0,90.007504l-66.096551,40.94936l-339.301952,210.625668l-339.491613,-210.625668l-66.096551,-40.94936l0,-90.007504l405.588164,-255.426701m0,-16.014849c-2.655242,0 -5.215653,0.709519 -7.586405,2.229916l-405.588164,255.426701c-4.551843,2.838074 -7.396745,8.108784 -7.396745,13.784933l0,90.007504c0,5.676149 2.844902,10.946859 7.491575,13.886293l66.096551,41.05072l339.491613,210.625668c2.275921,1.419037 4.931163,2.128556 7.491575,2.128556s5.215653,-0.709519 7.491575,-2.128556l339.301952,-210.625668l66.096551,-40.94936c4.646673,-2.838074 7.491575,-8.108784 7.491575,-13.886293l0,-90.007504c0,-5.676149 -2.844902,-10.946859 -7.396745,-13.784933l-405.398504,-255.426701c-2.370751,-1.520397 -5.025993,-2.331275 -7.586405,-2.331275z"/>
|
||||||
|
<path stroke="null" id="svg_6" fill="#FFFFFF" d="m106.50668,612.042083l405.493334,253.298145l405.493334,-253.298145l-405.398504,-255.426701l-405.588164,255.426701z"/>
|
||||||
|
<path stroke="null" id="svg_7" fill="#D4E4FC" d="m501.473877,64.192353l-254.9032,498.487506l263.343075,161.162085l266.662127,-162.074323l-275.102002,-497.575268z"/>
|
||||||
|
<path stroke="null" id="svg_8" fill="#0276F7" d="m229.975417,602.311542c-1.232791,0 -2.465582,-0.304079 -3.698372,-1.013598c-3.603542,-2.128556 -4.931163,-6.993826 -2.844902,-10.845499l279.653845,-532.13896c1.327621,-2.533995 3.793202,-4.054392 6.543274,-4.054392c2.655242,0 5.120823,1.520397 6.543274,4.054392l284.395348,532.13896c2.086261,3.851672 0.75864,8.716943 -2.750072,10.946859c-3.603542,2.128556 -8.155385,0.810878 -10.241646,-2.939434l-277.852074,-519.874424l-273.205401,519.671704c-1.422451,2.635355 -3.982862,4.054392 -6.543274,4.054392z"/>
|
||||||
|
<path stroke="null" id="svg_9" fill="#0276F7" d="m509.913752,755.567562c-4.172523,0 -7.491575,-3.547593 -7.491575,-8.007424l0,-666.744777c0,-4.459831 3.319052,-8.007424 7.491575,-8.007424s7.491575,3.547593 7.491575,8.007424l0,666.846137c0,4.358471 -3.413882,7.906065 -7.491575,7.906065z"/>
|
||||||
|
<path stroke="null" id="svg_10" fill="#0276F7" d="m509.913752,731.849369c-1.327621,0 -2.560412,-0.405439 -3.698372,-1.013598l-263.343075,-161.162085c-3.603542,-2.229916 -4.836333,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.939434l259.549873,158.83081l262.963755,-159.844408c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-266.662127,162.074323c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598z"/>
|
||||||
|
<path stroke="null" id="svg_11" fill="#0276F7" d="m509.913752,579.708306c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-201.988026,-125.686154c-3.603542,-2.229916 -4.741503,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.838074l198.289654,123.354879l201.798366,-122.138561c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.845499l-205.496739,124.469837c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598zm-2.465582,-157.513132c-1.232791,0 -2.370751,-0.304079 -3.508712,-0.912238l-140.917468,-79.668804c-3.698372,-2.128556 -5.025993,-6.892467 -3.129392,-10.845499c1.896601,-3.953032 6.448444,-5.37207 10.146816,-3.344873l137.503586,77.742968l143.00373,-79.871524c3.698372,-2.027196 8.155385,-0.506799 10.146816,3.344873c1.896601,3.953032 0.47415,8.716943 -3.129392,10.845499l-146.512442,81.79736c-1.232791,0.608159 -2.370751,0.912238 -3.603542,0.912238zm2.465582,-148.49211c-1.232791,0 -2.465582,-0.304079 -3.508712,-0.912238l-82.312492,-47.436387c-3.603542,-2.128556 -5.025993,-6.993826 -3.034562,-10.845499c1.991431,-3.953032 6.543274,-5.27071 10.146816,-3.243514l78.708949,45.409191l78.329629,-47.537747c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-81.933171,49.666303c-1.232791,0.608159 -2.560412,0.912238 -3.793202,0.912238z"/>
|
||||||
|
<path stroke="null" id="svg_12" fill="#0276F7" d="m509.913752,579.708306l-0.28449,0l-263.248245,-9.021022c-4.172523,-0.10136 -7.396745,-3.851672 -7.207085,-8.210144c0.09483,-4.459831 4.077693,-7.703345 7.681235,-7.703345l263.343075,9.021022c4.172523,0.10136 7.396745,3.851672 7.207085,8.210144c-0.18966,4.257112 -3.508712,7.703345 -7.491575,7.703345zm0,152.141063c-1.612111,0 -3.224222,-0.608159 -4.646673,-1.723117c-3.224222,-2.736715 -3.793202,-7.804705 -1.232791,-11.250938l205.496739,-276.610899c2.560412,-3.446233 7.301915,-4.054392 10.526137,-1.317677c3.224222,2.736715 3.793202,7.804705 1.232791,11.250938l-205.496739,276.610899c-1.517281,2.027196 -3.698372,3.040794 -5.879464,3.040794z"/>
|
||||||
|
<path stroke="null" id="svg_13" fill="#0276F7" d="m509.913752,579.708306c-1.422451,0 -2.750072,-0.405439 -4.077693,-1.216318c-3.508712,-2.432635 -4.457013,-7.297906 -2.275921,-11.048218l144.14169,-239.310492c2.275921,-3.750313 6.922594,-4.763911 10.336476,-2.432635c3.508712,2.432635 4.457013,7.297906 2.275921,11.048218l-144.14169,239.310492c-1.422451,2.331275 -3.793202,3.648953 -6.258784,3.648953zm-2.465582,-157.513132c-1.043131,0 -2.086261,-0.20272 -3.129392,-0.709519c-3.793202,-1.824476 -5.405313,-6.588387 -3.698372,-10.642779l84.398753,-198.158413c1.706941,-4.054392 6.069124,-5.777509 9.957156,-3.953032c3.793202,1.824476 5.405313,6.588387 3.698372,10.642779l-84.303923,198.158413c-1.327621,2.939434 -4.077693,4.662551 -6.922594,4.662551z"/>
|
||||||
|
<path stroke="null" id="svg_14" fill="#0276F7" d="m591.846924,375.062866c-2.750072,0 -5.405313,-1.621757 -6.732934,-4.459831c-1.801771,-3.953032 -0.28449,-8.716943 3.413882,-10.642779l129.253371,-67.302908l-365.759539,-178.089172l20.862613,208.091673l133.994874,-64.262114c3.698372,-1.824476 8.155385,0 9.862326,4.054392c1.706941,4.054392 0,8.716943 -3.793202,10.541419l-143.38305,68.823305c-2.181091,1.013598 -4.646673,0.912238 -6.827764,-0.405439c-2.086261,-1.317677 -3.413882,-3.547593 -3.698372,-6.081588l-23.328195,-233.026185c-0.28449,-2.838074 0.853471,-5.676149 3.034562,-7.297906c2.181091,-1.621757 5.025993,-2.027196 7.491575,-0.810878l392.217126,190.961867c2.655242,1.317677 4.362183,4.054392 4.362183,7.196546c0,3.142154 -1.612111,5.980228 -4.172523,7.297906l-143.57271,74.600814c-1.043131,0.608159 -2.181091,0.810878 -3.224222,0.810878zm-283.921198,78.959286c-3.603542,0 -6.827764,-2.838074 -7.396745,-6.791107c-0.56898,-4.358471 2.181091,-8.412864 6.258784,-9.122382l199.617275,-31.826978c4.077693,-0.608159 7.870895,2.331275 8.534705,6.689747c0.56898,4.358471 -2.181091,8.412864 -6.258784,9.122382l-199.617275,31.826978c-0.47415,0.10136 -0.853471,0.10136 -1.137961,0.10136z"/>
|
||||||
|
</g>
|
||||||
|
<text stroke="null" font-style="italic" transform="matrix(6.577099502228161,0,0,7.449448263868419,-1073.2057632249744,-908.8606073938396) " xml:space="preserve" text-anchor="start" font-family="Arvo, sans-serif" font-size="24" id="svg_16" y="177.898525" x="178.621382" stroke-width="0" fill="#D4E4FC">5G</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.9 KiB |
@@ -0,0 +1,27 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询工单列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listCallBack(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/psap/v1/mf/ticket/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换进行中状态
|
||||||
|
* @param
|
||||||
|
* @returns bolb
|
||||||
|
*/
|
||||||
|
export function updateStatus(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `psap/v1/mf/ticket/${data.ticketId}/status`,
|
||||||
|
method: 'PATCH',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询定时任务调度列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listCallings(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/psap/v1/mf/callings/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CBC列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listCBC(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/psap/v1/cbc/${query.neId}/message/list`,
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CBC签约用户新增
|
||||||
|
* @param data 签约对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addCBC(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/psap/v1/cbc/${data.neId}/message`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function updateCBCStatus(data:any) {
|
||||||
|
return request({
|
||||||
|
url: `/psap/v1/cbc/${data.neId}/message/${data.id}/${data.status}`,
|
||||||
|
method: 'put',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function updateCBC(data:any) {
|
||||||
|
return request({
|
||||||
|
url: `/psap/v1/cbc/${data.neId}/message/${data.id}`,
|
||||||
|
method: 'put',
|
||||||
|
data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CBC删除
|
||||||
|
* @param data 签约对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delCBC(neId: string, id: string) {
|
||||||
|
return request({
|
||||||
|
url: `/psap/v1/cbc/${neId}/message/${id}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { request } from '@/plugins/http-fetch';
|
import { request } from '@/plugins/http-fetch';
|
||||||
import { parseObjLineToHump } from '@/utils/parse-utils';
|
import { parseObjLineToHump } from '@/utils/parse-utils';
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
import { parseDateToStr, YYYY_MM_DD_HH_MM_SS } from '@/utils/date-utils';
|
||||||
import useUserStore from '@/store/modules/user';
|
import useUserStore from '@/store/modules/user';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,7 +122,7 @@ export function updateConfirm(data: Record<string, any>) {
|
|||||||
const userName = useUserStore().userName;
|
const userName = useUserStore().userName;
|
||||||
let finalData = {
|
let finalData = {
|
||||||
alarm: {
|
alarm: {
|
||||||
ack_time: parseDateToStr(time),
|
ack_time: parseDateToStr(time, YYYY_MM_DD_HH_MM_SS),
|
||||||
ack_user: userName,
|
ack_user: userName,
|
||||||
ack_state: '1',
|
ack_state: '1',
|
||||||
},
|
},
|
||||||
@@ -145,7 +145,7 @@ export function cancelConfirm(data: (string | number)[]) {
|
|||||||
const userName = useUserStore().userName;
|
const userName = useUserStore().userName;
|
||||||
let finalData = {
|
let finalData = {
|
||||||
alarm: {
|
alarm: {
|
||||||
ack_time: parseDateToStr(time),
|
ack_time: parseDateToStr(time, YYYY_MM_DD_HH_MM_SS),
|
||||||
ack_user: '',
|
ack_user: '',
|
||||||
ack_state: '0',
|
ack_state: '0',
|
||||||
},
|
},
|
||||||
@@ -216,7 +216,7 @@ export function clearAlarm(data: Record<string, any>) {
|
|||||||
const userName = useUserStore().userName;
|
const userName = useUserStore().userName;
|
||||||
let finalData = {
|
let finalData = {
|
||||||
data: {
|
data: {
|
||||||
clear_time: parseDateToStr(time),
|
clear_time: parseDateToStr(time, YYYY_MM_DD_HH_MM_SS),
|
||||||
clear_type: '2',
|
clear_type: '2',
|
||||||
alarm_status: '0',
|
alarm_status: '0',
|
||||||
clear_user: userName,
|
clear_user: userName,
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { request } from '@/plugins/http-fetch';
|
import { request } from '@/plugins/http-fetch';
|
||||||
import { parseObjLineToHump } from '@/utils/parse-utils';
|
import { parseObjLineToHump } from '@/utils/parse-utils';
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
|
||||||
import useUserStore from '@/store/modules/user';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询列表
|
* 查询列表
|
||||||
|
|||||||
@@ -63,7 +63,11 @@ export async function getAlarmSet() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Object.keys(resultData).length === 0) {
|
if (Object.keys(resultData).length === 0) {
|
||||||
return { code: RESULT_CODE_ERROR, msg: RESULT_MSG_ERROR[language], data: {} };
|
return {
|
||||||
|
code: RESULT_CODE_ERROR,
|
||||||
|
msg: RESULT_MSG_ERROR[language],
|
||||||
|
data: {},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
code: RESULT_CODE_SUCCESS,
|
code: RESULT_CODE_SUCCESS,
|
||||||
@@ -117,7 +121,11 @@ export async function updateAlarmSet(data: Record<string, any>) {
|
|||||||
}
|
}
|
||||||
// 无变更时
|
// 无变更时
|
||||||
if (resultNum === 0) {
|
if (resultNum === 0) {
|
||||||
return { code: RESULT_CODE_ERROR, msg: RESULT_MSG_ERROR[language], data: 0 };
|
return {
|
||||||
|
code: RESULT_CODE_ERROR,
|
||||||
|
msg: RESULT_MSG_ERROR[language],
|
||||||
|
data: 0,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
code: RESULT_CODE_SUCCESS,
|
code: RESULT_CODE_SUCCESS,
|
||||||
@@ -166,11 +174,10 @@ export async function getForwardSet() {
|
|||||||
*/
|
*/
|
||||||
export async function updateForwardSet(data: Record<string, any>) {
|
export async function updateForwardSet(data: Record<string, any>) {
|
||||||
// return false;
|
// return false;
|
||||||
console.log(data)
|
let obj: any = [
|
||||||
let obj:any=[
|
{ interface: 'Email', to_user: data.emailObj },
|
||||||
{interface:"Email",to_user:data.emailObj},
|
{ interface: 'SMS', to_user: data.smsObj },
|
||||||
{interface:"SMS",to_user:data.smsObj}
|
];
|
||||||
]
|
|
||||||
const result = await request({
|
const result = await request({
|
||||||
url: `/api/rest/databaseManagement/v1/omc_db/config?WHERE=config_tag='forwardAlarm'`,
|
url: `/api/rest/databaseManagement/v1/omc_db/config?WHERE=config_tag='forwardAlarm'`,
|
||||||
method: 'put',
|
method: 'put',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { request } from '@/plugins/http-fetch';
|
import { request } from '@/plugins/http-fetch';
|
||||||
import { parseObjLineToHump } from '@/utils/parse-utils';
|
import { parseObjLineToHump } from '@/utils/parse-utils';
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
import { parseDateToStr, YYYY_MM_DD_HH_MM_SS } from '@/utils/date-utils';
|
||||||
import useUserStore from '@/store/modules/user';
|
import useUserStore from '@/store/modules/user';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,7 +79,7 @@ export function updateConfirm(data: Record<string, any>) {
|
|||||||
const userName = useUserStore().userName;
|
const userName = useUserStore().userName;
|
||||||
let finalData = {
|
let finalData = {
|
||||||
alarm: {
|
alarm: {
|
||||||
ack_time: parseDateToStr(time),
|
ack_time: parseDateToStr(time, YYYY_MM_DD_HH_MM_SS),
|
||||||
ack_user: userName,
|
ack_user: userName,
|
||||||
ack_state: '1',
|
ack_state: '1',
|
||||||
},
|
},
|
||||||
@@ -101,7 +101,7 @@ export function cancelConfirm(data: (string | number)[]) {
|
|||||||
var time = new Date();
|
var time = new Date();
|
||||||
let finalData = {
|
let finalData = {
|
||||||
alarm: {
|
alarm: {
|
||||||
ack_time: parseDateToStr(time),
|
ack_time: parseDateToStr(time, YYYY_MM_DD_HH_MM_SS),
|
||||||
ack_user: '',
|
ack_user: '',
|
||||||
ack_state: '0',
|
ack_state: '0',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { CACHE_SESSION_CRYPTO_API } from '@/constants/cache-keys-constants';
|
||||||
|
import { sessionGet } from '@/utils/cache-session-utils';
|
||||||
import { request } from '@/plugins/http-fetch';
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,3 +53,43 @@ export function delFile(query: Record<string, any>) {
|
|||||||
params: query,
|
params: query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新FTP信息
|
||||||
|
* @param data 数据
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function updateFTPInfo(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/lm/table/ftp`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取FTP信息
|
||||||
|
* @param data 数据
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getFTPInfo() {
|
||||||
|
return request({
|
||||||
|
url: `/lm/table/ftp`,
|
||||||
|
method: 'get',
|
||||||
|
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送FTP文件
|
||||||
|
* @param data 数据
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function putFTPInfo(filePath: string, fileName: string) {
|
||||||
|
return request({
|
||||||
|
url: `/lm/table/ftp`,
|
||||||
|
method: 'put',
|
||||||
|
data: { filePath, fileName },
|
||||||
|
});
|
||||||
|
}
|
||||||
+4
-2
@@ -1,3 +1,5 @@
|
|||||||
|
import { CACHE_SESSION_CRYPTO_API } from '@/constants/cache-keys-constants';
|
||||||
|
import { sessionGet } from '@/utils/cache-session-utils';
|
||||||
import { request } from '@/plugins/http-fetch';
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
// 登录方法
|
// 登录方法
|
||||||
@@ -7,7 +9,7 @@ export function login(data: Record<string, string>) {
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
data: data,
|
data: data,
|
||||||
whithToken: false,
|
whithToken: false,
|
||||||
crypto: true,
|
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +24,7 @@ export function register(data: Record<string, any>) {
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
data: data,
|
data: data,
|
||||||
whithToken: false,
|
whithToken: false,
|
||||||
crypto: true,
|
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { CACHE_SESSION_CRYPTO_API } from '@/constants/cache-keys-constants';
|
||||||
|
import { sessionGet } from '@/utils/cache-session-utils';
|
||||||
import { request } from '@/plugins/http-fetch';
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,7 +38,7 @@ export function addNeInfo(data: Record<string, any>) {
|
|||||||
url: `/ne/info`,
|
url: `/ne/info`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data,
|
data: data,
|
||||||
crypto: true,
|
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
|
||||||
timeout: 30_000,
|
timeout: 30_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -51,7 +53,7 @@ export function updateNeInfo(data: Record<string, any>) {
|
|||||||
url: `/ne/info`,
|
url: `/ne/info`,
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data,
|
data: data,
|
||||||
crypto: true,
|
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
|
||||||
timeout: 30_000,
|
timeout: 30_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export function listAMFDataUE(query: Record<string, any>) {
|
|||||||
url: '/neData/amf/ue/list',
|
url: '/neData/amf/ue/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,3 +41,90 @@ export function exportAMFDataUE(data: Record<string, any>) {
|
|||||||
timeout: 60_000,
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AMF-接入基站信息列表
|
||||||
|
* @param query 查询参数 neId=001&id=1
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listAMFNblist(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/amf/nb/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AMF-接入基站状态信息列表
|
||||||
|
* @param query 查询参数 neId=001&state=1
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listAMFNbStatelist(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/amf/nb/list-cfg',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AMF-接入基站状态信息新增
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param data 数据 { "index": 1, "name": "Gnb", "address": "192.168.8.1", "position": "Area-B" }
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addAMFNbState(neId: string, data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
neType: 'AMF',
|
||||||
|
neId: neId,
|
||||||
|
paramName: 'gnbList',
|
||||||
|
paramData: data,
|
||||||
|
loc: `${data.index}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AMF-接入基站状态信息修改
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param data 数据 { "index": 1, "name": "Gnb", "address": "192.168.8.1", "position": "Area-B" }
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function editAMFNbState(neId: string, data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
method: 'put',
|
||||||
|
data: {
|
||||||
|
neType: 'AMF',
|
||||||
|
neId: neId,
|
||||||
|
paramName: 'gnbList',
|
||||||
|
paramData: data,
|
||||||
|
loc: `${data.index}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AMF-接入基站状态信息删除
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param index 数据index
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delAMFNbState(neId: string, index: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
method: 'delete',
|
||||||
|
params: {
|
||||||
|
neType: 'AMF',
|
||||||
|
neId: neId,
|
||||||
|
paramName: 'gnbList',
|
||||||
|
loc: `${index}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ export function listIMSDataCDR(query: Record<string, any>) {
|
|||||||
url: '/neData/ims/cdr/list',
|
url: '/neData/ims/cdr/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询IMS-CDR会话事件
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listMFDataCDR(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/mf/cdr/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IMS-CDR会话删除
|
||||||
|
* @param id 信息ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delMFDataCDR(cdrIds: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/mf/cdr/${cdrIds}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IMS-CDR会话列表导出
|
||||||
|
* @param data 查询列表条件
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function exportMFDataCDR(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/mf/cdr/export',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ export function listMMEDataUE(query: Record<string, any>) {
|
|||||||
url: '/neData/mme/ue/list',
|
url: '/neData/mme/ue/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,3 +41,90 @@ export function exportMMEDataUE(data: Record<string, any>) {
|
|||||||
timeout: 60_000,
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MME-接入基站信息列表
|
||||||
|
* @param query 查询参数 neId=001&id=1
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listMMENblist(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/mme/nb/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MME-接入基站状态信息列表
|
||||||
|
* @param query 查询参数 neId=001&state=1
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listMMENbStatelist(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/mme/nb/list-cfg',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MME-接入基站状态信息新增
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param data 数据 { "index": 1, "name": "Enb", "address": "192.168.8.1", "position": "Area-B" }
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addMMENbState(neId: string, data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
neType: 'MME',
|
||||||
|
neId: neId,
|
||||||
|
paramName: 'enbList',
|
||||||
|
paramData: data,
|
||||||
|
loc: `${data.index}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MME-接入基站状态信息修改
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param data 数据 { "index": 1, "name": "Enb", "address": "192.168.8.1", "position": "Area-B" }
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function editMMENbState(neId: string, data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
method: 'put',
|
||||||
|
data: {
|
||||||
|
neType: 'MME',
|
||||||
|
neId: neId,
|
||||||
|
paramName: 'enbList',
|
||||||
|
paramData: data,
|
||||||
|
loc: `${data.index}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MME-接入基站状态信息删除
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param index 数据index
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delMMENbState(neId: string, index: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
method: 'delete',
|
||||||
|
params: {
|
||||||
|
neType: 'MME',
|
||||||
|
neId: neId,
|
||||||
|
paramName: 'enbList',
|
||||||
|
loc: `${index}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 历史记录列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listNBState(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/nb-state/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 历史记录列表导出
|
||||||
|
* @param data 查询列表条件
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function exportNBState(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/nb-state/export',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询SGWC-CDR会话事件
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listSGWCDataCDR(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/sgwc/cdr/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SGWC-CDR会话删除
|
||||||
|
* @param id 信息ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delSGWCDataCDR(cdrIds: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/sgwc/cdr/${cdrIds}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SGWC-CDR会话列表导出
|
||||||
|
* @param data 查询列表条件
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function exportSGWCDataCDR(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/sgwc/cdr/export',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
+16
-2
@@ -10,6 +10,7 @@ export function listSMFDataCDR(query: Record<string, any>) {
|
|||||||
url: '/neData/smf/cdr/list',
|
url: '/neData/smf/cdr/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,14 +42,27 @@ export function exportSMFDataCDR(data: Record<string, any>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMF-在线订阅用户数量
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listSMFSubNum(neId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/smf/sub/num',
|
||||||
|
method: 'get',
|
||||||
|
params: { neId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SMF-在线订阅用户列表信息
|
* SMF-在线订阅用户列表信息
|
||||||
* @param query 查询参数
|
* @param query 查询参数
|
||||||
* @returns object
|
* @returns object
|
||||||
*/
|
*/
|
||||||
export function listSMFSubscribers(query: Record<string, any>) {
|
export function listSMFSubList(query: Record<string, any>) {
|
||||||
return request({
|
return request({
|
||||||
url: '/neData/smf/subscribers',
|
url: '/neData/smf/sub/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export function listSMSCDataCDR(query: Record<string, any>) {
|
|||||||
url: '/neData/smsc/cdr/list',
|
url: '/neData/smsc/cdr/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { CACHE_SESSION_CRYPTO_API } from '@/constants/cache-keys-constants';
|
||||||
|
import { sessionGet } from '@/utils/cache-session-utils';
|
||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
/**
|
||||||
|
* 获取下拉框数据
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getBakFile() {
|
||||||
|
return request({
|
||||||
|
url: '/ue/table/list',
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取对应类型的文件列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getBakFileList(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ue/file/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 下载远端文件
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function downFile(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ue/file/${query.fileName}`,
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 删除远端获取文件
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delFile(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ue/file/${query.fileName}`,
|
||||||
|
method: 'delete',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
|
||||||
import { request } from '@/plugins/http-fetch';
|
import { request } from '@/plugins/http-fetch';
|
||||||
import { parseObjLineToHump } from '@/utils/parse-utils';
|
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新 查询自定义指标数据
|
* 新 查询自定义指标数据
|
||||||
@@ -14,6 +11,7 @@ export async function listCustomData(query: Record<string, any>) {
|
|||||||
url: `/pm/kpiC/report`,
|
url: `/pm/kpiC/report`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
|
||||||
import { request } from '@/plugins/http-fetch';
|
import { request } from '@/plugins/http-fetch';
|
||||||
import { parseObjLineToHump } from '@/utils/parse-utils';
|
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询自定义指标
|
* 查询自定义指标
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { request } from '@/plugins/http-fetch';
|
import { request } from '@/plugins/http-fetch';
|
||||||
import { parseObjLineToHump } from '@/utils/parse-utils';
|
import { parseObjLineToHump } from '@/utils/parse-utils';
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询任务列表
|
* 查询任务列表
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function getNeFile(query: Record<string, any>) {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
timeout: 180_000,
|
timeout: 600_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ export function getNeDirZip(data: Record<string, any>) {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
params: data,
|
params: data,
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
timeout: 60_000,
|
timeout: 600_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export function filePullTask(traceId: string) {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
params: { traceId },
|
params: { traceId },
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
timeout: 60_000,
|
timeout: 600_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,6 @@ export function filePullTaskHLR(query: Record<string, any>) {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
timeout: 60_000,
|
timeout: 600_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
|
import { message } from 'ant-design-vue/es';
|
||||||
|
import { ref, reactive, onMounted, onBeforeUnmount, nextTick } from 'vue';
|
||||||
import { FitAddon } from '@xterm/addon-fit';
|
import { FitAddon } from '@xterm/addon-fit';
|
||||||
import { Terminal } from '@xterm/xterm';
|
import { Terminal } from '@xterm/xterm';
|
||||||
import '@xterm/xterm/css/xterm.css';
|
import '@xterm/xterm/css/xterm.css';
|
||||||
@@ -31,8 +32,91 @@ const terminalDom = ref<HTMLElement | undefined>(undefined);
|
|||||||
/**终端输入实例对象 */
|
/**终端输入实例对象 */
|
||||||
const terminal = ref<any>(null);
|
const terminal = ref<any>(null);
|
||||||
|
|
||||||
/**终端输入命令 */
|
/**终端输入文字状态 */
|
||||||
const terminalCmd = ref<string>('');
|
const terminalState = reactive<{
|
||||||
|
/**输入值 */
|
||||||
|
text: string;
|
||||||
|
/**历史 */
|
||||||
|
history: {
|
||||||
|
label?: string;
|
||||||
|
value: string;
|
||||||
|
}[];
|
||||||
|
}>({
|
||||||
|
text: '',
|
||||||
|
history: [
|
||||||
|
{
|
||||||
|
value: 'info server',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'info replication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'keys ausf:*',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'quit',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**自动完成根据输入项进行筛选 */
|
||||||
|
function fnAutoCompleteFilter(input: string, option: any) {
|
||||||
|
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**自动完成按键触发 */
|
||||||
|
function fnAutoCompleteKeydown(evt: KeyboardEvent) {
|
||||||
|
if (evt.key === 'Enter') {
|
||||||
|
// 阻止默认的换行行为
|
||||||
|
evt.preventDefault();
|
||||||
|
// 按下 Shift + Enter 键时换行
|
||||||
|
if (evt.shiftKey && evt.target) {
|
||||||
|
// 插入换行符
|
||||||
|
const textarea = evt.target as HTMLInputElement;
|
||||||
|
const start = textarea.selectionStart || 0;
|
||||||
|
const end = textarea.selectionEnd || 0;
|
||||||
|
const text = textarea.value;
|
||||||
|
textarea.value = text.substring(0, start) + '\n' + text.substring(end);
|
||||||
|
terminalState.text = textarea.value;
|
||||||
|
// 更新光标位置
|
||||||
|
textarea.selectionStart = textarea.selectionEnd = start + 1;
|
||||||
|
} else {
|
||||||
|
// ws未连接
|
||||||
|
if (ws.state() !== WebSocket.OPEN) {
|
||||||
|
message.error('disconnected');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输入历史
|
||||||
|
const cmdStr = terminalState.text.trim().replace(/\n/g, '\r\n');
|
||||||
|
const hisIndex = terminalState.history.findIndex(
|
||||||
|
item => item.value === cmdStr
|
||||||
|
);
|
||||||
|
if (hisIndex === -1) {
|
||||||
|
terminalState.history.push({
|
||||||
|
value: cmdStr,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送文本
|
||||||
|
terminal.value.scrollToBottom();
|
||||||
|
terminal.value.writeln('\r\n> ' + cmdStr);
|
||||||
|
ws.send({
|
||||||
|
requestId: `redis_${props.hostId}`,
|
||||||
|
type: 'redis',
|
||||||
|
data: `${cmdStr}\r\n`,
|
||||||
|
});
|
||||||
|
terminalState.text = '';
|
||||||
|
|
||||||
|
// 退出登录
|
||||||
|
if (['q', 'quit', 'exit'].includes(cmdStr)) {
|
||||||
|
setTimeout(() => {
|
||||||
|
ws.close();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**终端输入渲染 */
|
/**终端输入渲染 */
|
||||||
function handleRanderXterm(container: HTMLElement | undefined) {
|
function handleRanderXterm(container: HTMLElement | undefined) {
|
||||||
@@ -49,47 +133,13 @@ function handleRanderXterm(container: HTMLElement | undefined) {
|
|||||||
scrollback: 1000,
|
scrollback: 1000,
|
||||||
scrollSensitivity: 15,
|
scrollSensitivity: 15,
|
||||||
tabStopWidth: 4,
|
tabStopWidth: 4,
|
||||||
disableStdin: false, // 禁止输入
|
disableStdin: true, // 禁止输入
|
||||||
});
|
});
|
||||||
// 挂载
|
// 挂载
|
||||||
xterm.open(container);
|
xterm.open(container);
|
||||||
// 自适应尺寸
|
// 自适应尺寸
|
||||||
const fitAddon = new FitAddon();
|
const fitAddon = new FitAddon();
|
||||||
xterm.loadAddon(fitAddon);
|
xterm.loadAddon(fitAddon);
|
||||||
// 终端输入字符按键监听
|
|
||||||
xterm.onKey(({ key, domEvent }) => {
|
|
||||||
// console.log(key, domEvent);
|
|
||||||
// 单键输入
|
|
||||||
switch (domEvent.key) {
|
|
||||||
case 'Enter':
|
|
||||||
const cmdStr = terminalCmd.value.trim();
|
|
||||||
// 发送文本
|
|
||||||
terminal.value.scrollToBottom();
|
|
||||||
terminal.value.writeln('\r\n');
|
|
||||||
ws.send({
|
|
||||||
requestId: `redis_${props.hostId}`,
|
|
||||||
type: 'redis',
|
|
||||||
data: `${cmdStr}\r\n`,
|
|
||||||
});
|
|
||||||
terminalCmd.value = '';
|
|
||||||
|
|
||||||
// 退出登录
|
|
||||||
if ('quit' === cmdStr) {
|
|
||||||
setTimeout(() => {
|
|
||||||
ws.close();
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'Backspace':
|
|
||||||
// 处理退格键,删除最后一个字符
|
|
||||||
xterm.write('\b \b');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
xterm.write(key);
|
|
||||||
terminalCmd.value += key;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// 创建 ResizeObserver 实例
|
// 创建 ResizeObserver 实例
|
||||||
var observer = new ResizeObserver(entries => {
|
var observer = new ResizeObserver(entries => {
|
||||||
fitAddon.fit();
|
fitAddon.fit();
|
||||||
@@ -213,7 +263,21 @@ defineExpose({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="terminal">
|
<div class="terminal">
|
||||||
<div ref="terminalDom" :id="id" class="terminal"></div>
|
<div ref="terminalDom" style="height: calc(100% - 36px)" :id="id"></div>
|
||||||
|
<a-auto-complete
|
||||||
|
v-model:value="terminalState.text"
|
||||||
|
:dropdown-match-select-width="500"
|
||||||
|
style="width: 100%"
|
||||||
|
:options="terminalState.history"
|
||||||
|
:filter-option="fnAutoCompleteFilter"
|
||||||
|
:defaultActiveFirstOption="false"
|
||||||
|
>
|
||||||
|
<a-textarea
|
||||||
|
:auto-size="{ minRows: 1, maxRows: 6 }"
|
||||||
|
placeholder="Execute command. Shift+Enter to line feed, Enter to send"
|
||||||
|
@keypress="fnAutoCompleteKeydown"
|
||||||
|
/>
|
||||||
|
</a-auto-complete>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -80,16 +80,16 @@ function fnAutoCompleteFilter(input: string, option: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**自动完成按键触发 */
|
/**自动完成按键触发 */
|
||||||
function fnAutoCompleteKeydown(evt: any) {
|
function fnAutoCompleteKeydown(evt: KeyboardEvent) {
|
||||||
if (evt.key === 'Enter') {
|
if (evt.key === 'Enter') {
|
||||||
// 阻止默认的换行行为
|
// 阻止默认的换行行为
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
// 按下 Shift + Enter 键时换行
|
// 按下 Shift + Enter 键时换行
|
||||||
if (evt.shiftKey) {
|
if (evt.shiftKey && evt.target) {
|
||||||
// 插入换行符
|
// 插入换行符
|
||||||
const textarea = evt.target;
|
const textarea = evt.target as HTMLInputElement;
|
||||||
const start = textarea.selectionStart;
|
const start = textarea.selectionStart || 0;
|
||||||
const end = textarea.selectionEnd;
|
const end = textarea.selectionEnd || 0;
|
||||||
const text = textarea.value;
|
const text = textarea.value;
|
||||||
textarea.value = text.substring(0, start) + '\n' + text.substring(end);
|
textarea.value = text.substring(0, start) + '\n' + text.substring(end);
|
||||||
terminalState.text = textarea.value;
|
terminalState.text = textarea.value;
|
||||||
@@ -121,7 +121,7 @@ function fnAutoCompleteKeydown(evt: any) {
|
|||||||
type: 'telnet',
|
type: 'telnet',
|
||||||
data: `${cmdStr}\r\n`,
|
data: `${cmdStr}\r\n`,
|
||||||
});
|
});
|
||||||
terminalState.text = ' ';
|
terminalState.text = '';
|
||||||
|
|
||||||
// 退出登录
|
// 退出登录
|
||||||
if (['q', 'quit', 'exit'].includes(cmdStr)) {
|
if (['q', 'quit', 'exit'].includes(cmdStr)) {
|
||||||
@@ -296,11 +296,12 @@ defineExpose({
|
|||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
:options="terminalState.history"
|
:options="terminalState.history"
|
||||||
:filter-option="fnAutoCompleteFilter"
|
:filter-option="fnAutoCompleteFilter"
|
||||||
@keydown="fnAutoCompleteKeydown"
|
:defaultActiveFirstOption="false"
|
||||||
>
|
>
|
||||||
<a-textarea
|
<a-textarea
|
||||||
:auto-size="{ minRows: 1, maxRows: 6 }"
|
:auto-size="{ minRows: 1, maxRows: 6 }"
|
||||||
placeholder="Execute command. Shift+Enter to line feed, Enter to send"
|
placeholder="Execute command. Shift+Enter to line feed, Enter to send"
|
||||||
|
@keypress="fnAutoCompleteKeydown"
|
||||||
/>
|
/>
|
||||||
</a-auto-complete>
|
</a-auto-complete>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
/**会话缓存-接口加密 */
|
||||||
|
export const CACHE_SESSION_CRYPTO_API = 'cache:session:cryptoApi';
|
||||||
|
|
||||||
/**会话缓存-网络请求 */
|
/**会话缓存-网络请求 */
|
||||||
export const CACHE_SESSION_FATCH = 'cache:session:fatch';
|
export const CACHE_SESSION_FATCH = 'cache:session:fatch';
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,10 @@
|
|||||||
/**网元列表,默认顺序 */
|
/**网元列表,默认顺序 */
|
||||||
export const NE_TYPE_LIST = [
|
export const NE_TYPE_LIST = [
|
||||||
'OMC',
|
'OMC',
|
||||||
'IMS',
|
|
||||||
'AMF',
|
|
||||||
'AUSF',
|
|
||||||
'UDM',
|
|
||||||
'SMF',
|
|
||||||
'PCF',
|
|
||||||
'NSSF',
|
|
||||||
'NRF',
|
|
||||||
'UPF',
|
|
||||||
'LMF',
|
|
||||||
'NEF',
|
|
||||||
'MME',
|
|
||||||
'N3IWF',
|
|
||||||
'MOCNGW',
|
|
||||||
'SMSC',
|
|
||||||
'SMSF',
|
|
||||||
'CBC',
|
'CBC',
|
||||||
'CHF',
|
'MF',
|
||||||
|
'IMS',
|
||||||
|
'SMSC',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+154
-164
@@ -222,12 +222,11 @@ export default {
|
|||||||
capability: 'Capability',
|
capability: 'Capability',
|
||||||
serialNum: 'Serial Number',
|
serialNum: 'Serial Number',
|
||||||
expiryDate: 'Expiry Date',
|
expiryDate: 'Expiry Date',
|
||||||
neStatus: 'NE status is abnormal',
|
neStatus: 'Status Abnormal',
|
||||||
runStatus:'Running Status',
|
runStatus: 'Runing Status',
|
||||||
mark:'Brief Information',
|
mark:'Information',
|
||||||
object:'Object',
|
object:'Object',
|
||||||
versionNum:'Version',
|
versionNum:'Version',
|
||||||
systemStatus:'Status',
|
|
||||||
realNeStatus:'Status',
|
realNeStatus:'Status',
|
||||||
reloadTime:'Refresh Time',
|
reloadTime:'Refresh Time',
|
||||||
Critical:'Critical',
|
Critical:'Critical',
|
||||||
@@ -351,168 +350,89 @@ export default {
|
|||||||
description: "No data yet, try refreshing",
|
description: "No data yet, try refreshing",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
configManage: {
|
agentManage:{
|
||||||
neManage: {
|
callings:{
|
||||||
addNe:'Add Network Element',
|
callerIdNumber:'Caller Number',
|
||||||
delSure:'Confirm deleting the data item with network element name {msg}',
|
calleeIdNumber:'Callee Number',
|
||||||
editNe:'Edit Network Element',
|
startTime:'Start Time',
|
||||||
exportSure:'Confirm exporting the configuration information with the network element name {msg}',
|
answeredTime:'Answered Time(s)',
|
||||||
exportTip:'Export successful, please go to backup management for download',
|
callDuration:'Call Duration',
|
||||||
getInfo:'Failed to get network element information',
|
msdData:'MSD Info',
|
||||||
neType:'NE Type',
|
|
||||||
neTypePlease: 'Select network element type',
|
|
||||||
neId:'NE ID',
|
|
||||||
neName:'NE Name',
|
|
||||||
neTypeTip:'Fill in the type of network element created, such as:SMF',
|
|
||||||
uid:'RM UID',
|
|
||||||
uidTip:'Please enter a unique resource identifier',
|
|
||||||
ip:'IP Address',
|
|
||||||
mac:'NE MAC address',
|
|
||||||
macTip:'Able to locate the physical address (MAC) of the network element',
|
|
||||||
port:'Port',
|
|
||||||
portTip:'Maximum range 0~65535',
|
|
||||||
pvflag:'PV Flag',
|
|
||||||
pnf:'Physical Network Element',
|
|
||||||
vnf:'Virtual Network Element',
|
|
||||||
province:'Region',
|
|
||||||
vendorName:'Vendor Name',
|
|
||||||
dn:'Network Identification',
|
|
||||||
reload: 'Reload',
|
|
||||||
restart: 'Restart',
|
|
||||||
totalSure:'Confirm the network element with {operator} network element name {msg}',
|
|
||||||
stop: 'Stop',
|
|
||||||
start: 'Start',
|
|
||||||
log: 'Logs',
|
|
||||||
export: 'Export',
|
|
||||||
import: 'Import',
|
|
||||||
fileForm:'File Source',
|
|
||||||
selectPlease:'Please select the source of the import file',
|
|
||||||
server:'Server File',
|
|
||||||
local:'Local File',
|
|
||||||
fileSelect:'Please select the current import file',
|
|
||||||
sync:'Synchronize to NE',
|
|
||||||
open:'Open',
|
|
||||||
close:'Close',
|
|
||||||
addFail:'Add failed',
|
|
||||||
operFail:'Operation Failed'
|
|
||||||
},
|
},
|
||||||
backupManage: {
|
callback:{
|
||||||
setBackupTask: 'Set automatic backup time',
|
callerIdNumber:'Caller Number',
|
||||||
neTypePlease: 'Query network element type',
|
calleeIdNumber:'Callee Number',
|
||||||
neType: 'NE Type',
|
|
||||||
neID: 'NE ID',
|
|
||||||
fileName: 'File Name',
|
|
||||||
createAt: 'Create at',
|
|
||||||
remark:'Remark',
|
|
||||||
edit:'Edit Backup File',
|
|
||||||
totalSure:'Confirm that {oper} records item number {id}?',
|
|
||||||
},
|
|
||||||
softwareManage: {
|
|
||||||
sendBtn: 'Distribute',
|
|
||||||
runBtn: 'Activate',
|
|
||||||
backBtn: 'Rollback',
|
|
||||||
historyBtn: 'Distribution Record',
|
|
||||||
neTypePlease: 'Select network element type',
|
|
||||||
neType: 'NE Type',
|
|
||||||
fileName: 'File Name',
|
|
||||||
version: 'Version',
|
|
||||||
versionPlease: 'Version number cannot be empty',
|
|
||||||
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}]?',
|
|
||||||
updateComment: 'Comment',
|
|
||||||
updateCommentPlease: 'Please enter the software description',
|
|
||||||
updateFile: 'Software File',
|
|
||||||
updateFilePlease: 'Please upload the updated software file',
|
|
||||||
verifyFile: 'Verify File',
|
|
||||||
selectFile: 'SELECT FILE',
|
|
||||||
sendTitle: 'Distribute software version',
|
|
||||||
sendContent: 'Are you sure to send the file with the software package [{fileName}] to the corresponding network element?',
|
|
||||||
runTitle: 'Activate software version',
|
|
||||||
runContent: 'Are you sure to activate the software version of [{fileName}] that has been issued to the corresponding network element?',
|
|
||||||
backTitle: 'Fallback software version',
|
|
||||||
backContent: 'Confirm that the software version of [{fileName}] has been issued for the corresponding network element rollback?',
|
|
||||||
neId: 'Corresponding network element',
|
|
||||||
neIdPlease: 'Please select the corresponding network element',
|
|
||||||
versions:'Version',
|
|
||||||
upVersions:'Version before upgrade',
|
|
||||||
backVersions:'Version before rollback',
|
|
||||||
status:'Status',
|
status:'Status',
|
||||||
letUpTime:'Activation time',
|
ticketId:'Ticket ID',
|
||||||
createTime:'Creation time',
|
startTime:'Created Time',
|
||||||
onlyAble:'Only upload file format {fileText} is supported',
|
updateTime:'Update Time',
|
||||||
nullVersion:'There is no rollback version for the current network element.',
|
msdData:'MSD Info',
|
||||||
},
|
agentName: 'Agent Name',
|
||||||
license: {
|
comment: 'Comment',
|
||||||
neTypePlease: 'Select network element type',
|
agentEmail: 'Agent Email',
|
||||||
neType: 'NE Type',
|
agentMobile: 'Agent Mobile',
|
||||||
serialNum: 'Serial Num',
|
title: ' Ticket List',
|
||||||
createTime: 'Time',
|
}
|
||||||
comment: 'Description',
|
|
||||||
updateComment: 'License Description',
|
|
||||||
updateCommentPlease: 'Please enter a license description',
|
|
||||||
updateFile: 'License File',
|
|
||||||
updateFilePlease: 'Please upload and update the License file',
|
|
||||||
selectFile: 'SELECT FILE',
|
|
||||||
neId: 'NE ID',
|
|
||||||
neIdPlease: 'Please select the corresponding network element',
|
|
||||||
},
|
|
||||||
configParam:{
|
|
||||||
dataNull:'No configuration item data yet',
|
|
||||||
editSuss:'Modification successful',
|
|
||||||
editFail:'Edit failed',
|
|
||||||
Unable:'Illegal operation of attribute value',
|
|
||||||
delSure:'Confirm to delete the data item with Index [{value}]?',
|
|
||||||
addSuss:'Add successfully',
|
|
||||||
addFail:'Add failed',
|
|
||||||
delArraySure:'Confirm to delete the data item with {arrayChildTitle} Index as [{value}]?',
|
|
||||||
parUnable:'The parameter value is not within the reasonable range',
|
|
||||||
ipv4Tip:'Not a legal IPV4 address',
|
|
||||||
ipv6Tip:'Not a legal IPV6 address',
|
|
||||||
enumTip:'Not a reasonable enumeration value',
|
|
||||||
boolTip:'Not a reasonable Boolean value',
|
|
||||||
default:'The input value is of unknown type',
|
|
||||||
reloadSuss:'Network element reloading completed',
|
|
||||||
reloadFail:'Network element reloading failed',
|
|
||||||
neNUll:'No network element list data yet',
|
|
||||||
reload:'Reload',
|
|
||||||
post:'Submit',
|
|
||||||
editSure:'Are you sure you want to update this attribute value? ',
|
|
||||||
arraryEdit:'Are you sure to submit the record whose updated Index is [{value}]? ',
|
|
||||||
addSure:'Are you sure to submit the new record of Index: [{value}]? '
|
|
||||||
},
|
|
||||||
configParamForm: {
|
|
||||||
treeTitle: "Navigation Configuration",
|
|
||||||
treeSelectTip: "Select configuration item information in the left configuration navigation!",
|
|
||||||
neType: 'NE Type',
|
|
||||||
neTypePleace: "Please select the network element type",
|
|
||||||
noConfigData: "No data on configuration items",
|
|
||||||
updateValue: "[ {num} ] parameter value modified successfully.",
|
|
||||||
updateValueErr: "Attribute value modification failure",
|
|
||||||
updateItem: "Modify Index to {num}.",
|
|
||||||
updateItemErr: "Record modification failure",
|
|
||||||
delItemOk: "Deleting Index as {num} succeeded",
|
|
||||||
addItemOk: "Add Index as {num} Record Succeeded",
|
|
||||||
addItemErr: "Record addition failure",
|
|
||||||
requireUn: "[ {display} ] input value is of unknown type",
|
|
||||||
requireString: "[ {display} ] parameter value is invalid.",
|
|
||||||
requireInt: "[ {display} ] parameter value not in reasonable range {filter}",
|
|
||||||
requireIpv4: "[ {display} ] not a legitimate IPV4 address",
|
|
||||||
requireIpv6: "[ {display} ] not a legitimate IPV6 address.",
|
|
||||||
requireEnum: "[ {display} ] is not a reasonable enumeration value.",
|
|
||||||
requireBool: "[ {display} ] is not a reasonable boolean value.",
|
|
||||||
editOkTip: "Confirm updating the value of this [ {num} ] attribute?",
|
|
||||||
updateItemTip: "Confirm updating the data item with Index [{num}]?",
|
|
||||||
delItemTip: "Confirm deleting the data item with Index [{num}]?",
|
|
||||||
arrayMore: "Expand",
|
|
||||||
},
|
},
|
||||||
|
cbc:{
|
||||||
|
cbe:{
|
||||||
|
neType:'CBC Object',
|
||||||
|
title: ' CBC Event',
|
||||||
|
delTip: 'Confirm deletion of the event data item numbered [{num}]?',
|
||||||
|
eventName: 'Event Name',
|
||||||
|
startTime: 'Start Time',
|
||||||
|
endTime: 'End Time',
|
||||||
|
repetitionPeriod: 'Repetition Period',
|
||||||
|
numOfBcast: 'Number of Broadcasts',
|
||||||
|
msgPWSType: 'Message Type',
|
||||||
|
messageId: 'Message ID',
|
||||||
|
displayMode: 'Display Mode',
|
||||||
|
geoScope:' Geographic Scope',
|
||||||
|
emergencyUserAlert: 'Emergency User Alert',
|
||||||
|
activatePopup: 'Activate Popup',
|
||||||
|
warningType: 'Warning Type',
|
||||||
|
language:' Language',
|
||||||
|
warningMessageText:' Broadcast Content',
|
||||||
|
status: 'Status',
|
||||||
|
warningAreaType: 'Warning Area Type',
|
||||||
|
taiListTip:'TAI List cannot be empty',
|
||||||
|
taiSonTip:'TAI List each item MCC, MNC, TAC cannot be empty',
|
||||||
|
eutraListTip:'EUTRA CellId List cannot be empty',
|
||||||
|
eutraSonTip:'EUTRA CellId List each item MCC, MNC, CellId cannot be empty',
|
||||||
|
nrTip:'NR CellId List cannot be empty',
|
||||||
|
nrSonTip:'NR CellId List each item MCC, MNC, CellId cannot be empty',
|
||||||
|
areaTip:'Area ID List cannot be empty',
|
||||||
|
areaSonTip:'Area ID List each item AreaID cannot be empty',
|
||||||
|
messageIdProfile:'Message ID Profile',
|
||||||
|
serialNumProfile:'Serial Num Profile',
|
||||||
|
warningTypeProfile:'Warning Type Profile',
|
||||||
|
warningMessageProfile:'Warning Message Profile',
|
||||||
|
etws:'Earthquake and Tsunami Warning System',
|
||||||
|
cmas:'Commercial Mobile Alert System',
|
||||||
|
createdAt:'Create Time',
|
||||||
|
eventNameHelp:'If it is CMAS, the recommended prefix is cmas_xxx. If it is ETWS, the recommended prefix is etws_xxx',
|
||||||
|
repetitionPeriodHelp:'Unit is seconds',
|
||||||
|
numOfBcastHelp:'Unit is seconds',
|
||||||
|
cellListTip:'CellId List cannot be empty',
|
||||||
|
cellListSonTip:'MCC、MNC、 Cannot be empty, and at least one EUTRA CellId/NR CellId must be filled in',
|
||||||
|
letupSure:'Do you confirm the status of the broadcast event with the modification number 【{id}】?',
|
||||||
|
tacHelp:'The TAC value is a decimal string, separated by ";" for multiple TAC values.',
|
||||||
|
cellIdHelp:'The CellId value is a hexadecimal string, separated by ";" for multiple CellId values.',
|
||||||
|
areaId:'The areaId value is a hexadecimal string.',
|
||||||
|
detail:'Detail',
|
||||||
|
}
|
||||||
},
|
},
|
||||||
dashboard: {
|
dashboard: {
|
||||||
overview:{
|
overview:{
|
||||||
title: "Core Network Dashboard",
|
title: "Core Network Dashboard",
|
||||||
fullscreen: "Click on the full-screen display",
|
fullscreen: "Click on the full-screen display",
|
||||||
toRouter: "Click to jump to the detail page",
|
toRouter: "Click to jump to the detail page",
|
||||||
|
psapTitle:'PSAP Dashboard',
|
||||||
|
onlineUser:'Online User',
|
||||||
|
totalUser:'Total User',
|
||||||
|
parallelUser:'Parallel User',
|
||||||
|
userTitle:'User Statistics',
|
||||||
|
sysTitle:'System Resources',
|
||||||
skim: {
|
skim: {
|
||||||
users: "Users",
|
users: "Users",
|
||||||
userTitle:'User Information',
|
userTitle:'User Information',
|
||||||
@@ -579,14 +499,47 @@ export default {
|
|||||||
resultFail: "Fail",
|
resultFail: "Fail",
|
||||||
delTip: "Confirm deletion of the data item numbered [{msg}]?",
|
delTip: "Confirm deletion of the data item numbered [{msg}]?",
|
||||||
exportTip: "Do you confirm to export the current query conditions of the CDR data? (Maximum 10,000 items can be exported.)",
|
exportTip: "Do you confirm to export the current query conditions of the CDR data? (Maximum 10,000 items can be exported.)",
|
||||||
smfChargingID: 'Charging ID',
|
chargingID: 'Charging ID',
|
||||||
smfSubscriptionIDData: 'Subscription ID Data',
|
smfSubscriptionIDData: 'Subscription ID Data',
|
||||||
smfSubscriptionIDType: 'Subscription ID Type',
|
smfSubscriptionIDType: 'Subscription ID Type',
|
||||||
smfDataVolumeUplink: 'Data Volume Uplink',
|
smfDataVolumeUplink: 'Data Volume Uplink',
|
||||||
smfDataVolumeDownlink: 'Data Volume Downlink',
|
smfDataVolumeDownlink: 'Data Volume Downlink',
|
||||||
smfDataTotalVolume: 'Data Total Volume',
|
smfDataTotalVolume: 'Data Total Volume',
|
||||||
smfDuration: 'Duration',
|
durationTime: 'Duration',
|
||||||
smfInvocationTime: 'Invocation Time',
|
invocationTime: 'Invocation Time',
|
||||||
|
sgwcServedIMSI: 'IMSI',
|
||||||
|
sgwcServedMSISDN: 'MSISDN',
|
||||||
|
sgwcVolumeGPRSUplink: 'GPRS Uplink',
|
||||||
|
sgwcVolumeGPRSDownlink: 'GPRS Downlink',
|
||||||
|
recordPath:'Record Path',
|
||||||
|
msd:' MSD',
|
||||||
|
automaticActivation: 'Automatic Activation',
|
||||||
|
positionCanBeTrusted: 'Position Can Be Trusted',
|
||||||
|
testCall: 'Test Call',
|
||||||
|
vehicleType:'Vehicle Type',
|
||||||
|
messageIdentifier: 'Message Identifier',
|
||||||
|
numberOfOccupants: 'Number Of Occupants',
|
||||||
|
n1latitudeDelta:'Latitude Delta',
|
||||||
|
n1longitudeDelta: 'Longitude Delta',
|
||||||
|
n2latitudeDelta:'Latitude Delta',
|
||||||
|
n2longitudeDelta: 'Longitude Delta',
|
||||||
|
timestamp: 'Timestamp',
|
||||||
|
vehicleDirection: 'Vehicle Direction',
|
||||||
|
isovds: 'Vehicle Descriptor Section',
|
||||||
|
isovisModelyear:'Vehicle Identifier Section Year',
|
||||||
|
isovisSeqPlant: 'Vehicle Identifier Section Plant & Serial',
|
||||||
|
isowmi: 'World Manufacturer Identifier',
|
||||||
|
positionLatitude: 'Position Latitude',
|
||||||
|
positionLongitude: 'Position Longitude',
|
||||||
|
dieselTankPresent: 'Diesel Tank Present',
|
||||||
|
electricEnergyStorage: 'Electric Energy Storage',
|
||||||
|
gasolineTankPresent: 'Gasoline Tank Present',
|
||||||
|
control:'Control',
|
||||||
|
recentVehicleLocationN1:'Recent Vehicle Location N1',
|
||||||
|
recentVehicleLocationN2:'Recent Vehicle Location N2',
|
||||||
|
vehicleIdentificationNumber:'Vehicle Identification Number',
|
||||||
|
vehicleLocation:'Vehicle Location',
|
||||||
|
vehiclePropulsionStorageType:'Vehicle Propulsion Storage Type',
|
||||||
},
|
},
|
||||||
ue: {
|
ue: {
|
||||||
eventType: "Event Type",
|
eventType: "Event Type",
|
||||||
@@ -761,6 +714,7 @@ export default {
|
|||||||
upgradeDone: 'Update complete, service being reloaded',
|
upgradeDone: 'Update complete, service being reloaded',
|
||||||
upgradeFail: 'The update fails, please check whether the software file exists and whether the service terminal environment is available!',
|
upgradeFail: 'The update fails, please check whether the software file exists and whether the service terminal environment is available!',
|
||||||
upgradeModal: 'Network Element Version Updates',
|
upgradeModal: 'Network Element Version Updates',
|
||||||
|
noPath: 'Package File Not Found',
|
||||||
},
|
},
|
||||||
neLicense: {
|
neLicense: {
|
||||||
status: "License Status",
|
status: "License Status",
|
||||||
@@ -784,7 +738,9 @@ export default {
|
|||||||
treeSelectTip: "Select configuration item information in the left configuration navigation!",
|
treeSelectTip: "Select configuration item information in the left configuration navigation!",
|
||||||
neType: 'NE Type',
|
neType: 'NE Type',
|
||||||
neTypePleace: "Please select the network element type",
|
neTypePleace: "Please select the network element type",
|
||||||
|
neIdSyncPleace: "Please select the synchronized network element",
|
||||||
noConfigData: "No data on configuration items",
|
noConfigData: "No data on configuration items",
|
||||||
|
noConfigdDisabled: "The configuration item is not normal",
|
||||||
updateValue: "[ {num} ] parameter value modified successfully.",
|
updateValue: "[ {num} ] parameter value modified successfully.",
|
||||||
updateValueErr: "Attribute value modification failure",
|
updateValueErr: "Attribute value modification failure",
|
||||||
updateItem: "Modify Index to {num}.",
|
updateItem: "Modify Index to {num}.",
|
||||||
@@ -845,7 +801,7 @@ export default {
|
|||||||
installSourceUpload: 'New Upload',
|
installSourceUpload: 'New Upload',
|
||||||
installSelect: 'Select Record',
|
installSelect: 'Select Record',
|
||||||
installUpload: 'Upload File',
|
installUpload: 'Upload File',
|
||||||
installText: 'Installed',
|
installText: 'Install',
|
||||||
licenseTitle: "Licenses",
|
licenseTitle: "Licenses",
|
||||||
licenseDesc: "Network element service authorization certification",
|
licenseDesc: "Network element service authorization certification",
|
||||||
licenseResultTitle: "Whether to authorize activation immediately",
|
licenseResultTitle: "Whether to authorize activation immediately",
|
||||||
@@ -858,6 +814,30 @@ export default {
|
|||||||
licenseTip2: '2. Clicking [Finish] will end the installation process.',
|
licenseTip2: '2. Clicking [Finish] will end the installation process.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
neData: {
|
||||||
|
baseStation: {
|
||||||
|
list: "List",
|
||||||
|
topology: "Topology",
|
||||||
|
nbName: "RanNodeName",
|
||||||
|
ueNum: "UE Number",
|
||||||
|
topologyTitle: "Radio State Graph",
|
||||||
|
name: "Name",
|
||||||
|
namePlease: "text content length 0~64",
|
||||||
|
position: "Position",
|
||||||
|
positionPlease: "location description. Prohibition of spaces, length of text content 0-64",
|
||||||
|
address: "IP Address",
|
||||||
|
addressPlease: "text content length 0~64",
|
||||||
|
state: "State",
|
||||||
|
online: "Online",
|
||||||
|
offline: "Offline",
|
||||||
|
time: "Change Time",
|
||||||
|
addRadio: "Add Radio Info",
|
||||||
|
editRadio: "Edit Radio Info",
|
||||||
|
history: "Status History",
|
||||||
|
exportTip: "Confirm exporting xlsx table files based on search criteria?",
|
||||||
|
importDataEmpty: "Imported data is empty",
|
||||||
|
},
|
||||||
|
},
|
||||||
neUser: {
|
neUser: {
|
||||||
auth: {
|
auth: {
|
||||||
authInfo:' Authentication Info',
|
authInfo:' Authentication Info',
|
||||||
@@ -868,6 +848,7 @@ export default {
|
|||||||
checkExport : 'Check Export',
|
checkExport : 'Check Export',
|
||||||
checkExportConfirm: 'Confirm exporting the checked authenticated user data?',
|
checkExportConfirm: 'Confirm exporting the checked authenticated user data?',
|
||||||
import: 'Import',
|
import: 'Import',
|
||||||
|
importFail: 'Failure Record',
|
||||||
loadDataConfirm: 'Are you sure you want to reload the data?',
|
loadDataConfirm: 'Are you sure you want to reload the data?',
|
||||||
loadData: 'Load Data',
|
loadData: 'Load Data',
|
||||||
loadDataTip: 'Successfully fetched loaded data: {num} items, the system is internally updating the data, it will take about {timer} seconds, please wait!!!!!.',
|
loadDataTip: 'Successfully fetched loaded data: {num} items, the system is internally updating the data, it will take about {timer} seconds, please wait!!!!!.',
|
||||||
@@ -896,6 +877,7 @@ export default {
|
|||||||
checkExport : 'Check Export',
|
checkExport : 'Check Export',
|
||||||
checkExportConfirm: 'Are you sure to export the data of the checked subscribers?',
|
checkExportConfirm: 'Are you sure to export the data of the checked subscribers?',
|
||||||
import: 'Import',
|
import: 'Import',
|
||||||
|
importFail: 'Failure Record',
|
||||||
loadDataConfirm: 'Are you sure you want to reload the data?',
|
loadDataConfirm: 'Are you sure you want to reload the data?',
|
||||||
loadData: 'Load Data',
|
loadData: 'Load Data',
|
||||||
loadDataTip: 'Successfully fetched loaded data: {num} items, the system is internally updating the data, it will take about {timer} seconds, please wait!!!!!.',
|
loadDataTip: 'Successfully fetched loaded data: {num} items, the system is internally updating the data, it will take about {timer} seconds, please wait!!!!!.',
|
||||||
@@ -1090,6 +1072,7 @@ export default {
|
|||||||
expressionModal:'Expression Modal',
|
expressionModal:'Expression Modal',
|
||||||
expressionErrorTip:'Please check the expression, the wrong indicator is {kpiId}',
|
expressionErrorTip:'Please check the expression, the wrong indicator is {kpiId}',
|
||||||
expressionNoIdTip:'Please check the expression, no valid indicator is found',
|
expressionNoIdTip:'Please check the expression, no valid indicator is found',
|
||||||
|
unitSelect:'To better display the image, the same unit needs to be selected. The current unit is:',
|
||||||
},
|
},
|
||||||
kpiKeyTarget:{
|
kpiKeyTarget:{
|
||||||
"time":"Time",
|
"time":"Time",
|
||||||
@@ -1108,8 +1091,14 @@ export default {
|
|||||||
},
|
},
|
||||||
kpiOverView:{
|
kpiOverView:{
|
||||||
"kpiName":"NE Metrics Name",
|
"kpiName":"NE Metrics Name",
|
||||||
"maxValue":"Max Value",
|
"maxValue":"Max",
|
||||||
"minValue":"Min Value",
|
"maxValueTip":"The max value of metric data within the user's filtered time range.",
|
||||||
|
"minValue":"Min",
|
||||||
|
"minValueTip":"The min value of metric data within the user's filtered time range.",
|
||||||
|
"avgValue":"Avg",
|
||||||
|
"avgValueTip":"The average value of metric data within the user's filtered time range.",
|
||||||
|
"totalValue":"Sum",
|
||||||
|
"totalValueTip":"The sum value of metric data within the user's filtered time range.",
|
||||||
"kpiChartTitle":"Overview of NE metrics",
|
"kpiChartTitle":"Overview of NE metrics",
|
||||||
"changeLine":"Change to Line Charts",
|
"changeLine":"Change to Line Charts",
|
||||||
"changeBar":"Change to Bar Charts",
|
"changeBar":"Change to Bar Charts",
|
||||||
@@ -1320,12 +1309,13 @@ export default {
|
|||||||
size: "Size",
|
size: "Size",
|
||||||
modifiedTime: "Modified Time",
|
modifiedTime: "Modified Time",
|
||||||
fileName: "File Name",
|
fileName: "File Name",
|
||||||
|
downTipZip: "Confirm downloading the directory [{fileName}] as a ZIP file?",
|
||||||
downTip: "Confirm the download file name is [{fileName}] File?",
|
downTip: "Confirm the download file name is [{fileName}] File?",
|
||||||
downTipErr: "Failed to get file",
|
downTipErr: "Failed to get file",
|
||||||
dirCd: "Enter Dir",
|
dirCd: "Enter Dir",
|
||||||
viewAs: 'View Action',
|
viewAs: 'View Action',
|
||||||
reload: "Reload",
|
reload: "Reload",
|
||||||
follow: 'Monitoring Content',
|
follow: 'Enable Instant Update',
|
||||||
tailChar: 'End Characters',
|
tailChar: 'End Characters',
|
||||||
tailLines: 'End Lines',
|
tailLines: 'End Lines',
|
||||||
},
|
},
|
||||||
|
|||||||
+154
-160
@@ -1,3 +1,7 @@
|
|||||||
|
import { language } from "@/plugins/http-fetch";
|
||||||
|
import { eventData } from "@/views/dashboard/overview/hooks/useUserActivity";
|
||||||
|
import { start, status } from "nprogress";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// 语言
|
// 语言
|
||||||
i18n: '中文',
|
i18n: '中文',
|
||||||
@@ -222,13 +226,12 @@ export default {
|
|||||||
capability: '用户容量',
|
capability: '用户容量',
|
||||||
serialNum: '序列号',
|
serialNum: '序列号',
|
||||||
expiryDate: '许可证到期日期',
|
expiryDate: '许可证到期日期',
|
||||||
neStatus:'网元状态异常',
|
neStatus:'状态异常',
|
||||||
runStatus:'运行状态',
|
runStatus:'运行状态',
|
||||||
mark:'简略信息',
|
mark:'信息',
|
||||||
object:'对象',
|
object:'对象',
|
||||||
versionNum:'版本号',
|
versionNum:'版本号',
|
||||||
systemStatus:'系统状态',
|
realNeStatus:'状态',
|
||||||
realNeStatus:'网元状态',
|
|
||||||
reloadTime:'刷新时间',
|
reloadTime:'刷新时间',
|
||||||
Critical:'严重告警',
|
Critical:'严重告警',
|
||||||
Major:'主要告警',
|
Major:'主要告警',
|
||||||
@@ -351,168 +354,89 @@ export default {
|
|||||||
description: "暂无数据,尝试刷新看看",
|
description: "暂无数据,尝试刷新看看",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
configManage: {
|
agentManage:{
|
||||||
neManage: {
|
callings:{
|
||||||
addNe:'添加网元',
|
callerIdNumber:'主叫号码',
|
||||||
delSure:'确认删除网元名称为{msg}的数据项 ',
|
calleeIdNumber:'被叫号码',
|
||||||
editNe:'修改网元',
|
startTime:'开始时间',
|
||||||
exportSure:'确认导出网元名称为 {msg} 的配置信息',
|
answeredTime:'接听时间(s)',
|
||||||
exportTip:'导出成功,请到备份管理进行下载',
|
callDuration:'通话时长',
|
||||||
getInfo:'获取网元信息失败',
|
msdData:'MSD内容',
|
||||||
neType:'网元类型',
|
|
||||||
neTypePlease: '请输入网元类型',
|
|
||||||
neId:'网元内部标识',
|
|
||||||
neName:'网元名称',
|
|
||||||
neTypeTip:'填写创建的网元类型,如:SMF',
|
|
||||||
uid:'资源唯一标识',
|
|
||||||
uidTip:'请输入资源唯一标识',
|
|
||||||
ip:'IP地址',
|
|
||||||
mac:'网元物理地址',
|
|
||||||
macTip:'能够定位网元的物理地址(MAC)',
|
|
||||||
port:'端口',
|
|
||||||
portTip:'最大范围0~65535',
|
|
||||||
pvflag:'网元虚拟化标识',
|
|
||||||
pnf:'物理网元',
|
|
||||||
vnf:'虚拟网元',
|
|
||||||
province:'网元服务省份',
|
|
||||||
vendorName:'厂商名称',
|
|
||||||
dn:'网络标识',
|
|
||||||
reload: '重载',
|
|
||||||
restart: '重启',
|
|
||||||
totalSure:'确认{oper}网元名称为 {msg} 的网元',
|
|
||||||
stop: '停止',
|
|
||||||
start: '启动',
|
|
||||||
log: '日志',
|
|
||||||
export: '导出',
|
|
||||||
import: '导入',
|
|
||||||
fileForm:'文件来源',
|
|
||||||
selectPlease:'请选择导入文件来源',
|
|
||||||
server:'服务器文件',
|
|
||||||
local:'本地文件',
|
|
||||||
fileSelect:'请选择当前导入文件',
|
|
||||||
sync:'同步到网元',
|
|
||||||
open:'开',
|
|
||||||
close:'关',
|
|
||||||
addFail:'新增失败',
|
|
||||||
operFail:'操作失败'
|
|
||||||
},
|
},
|
||||||
backupManage: {
|
callback:{
|
||||||
setBackupTask: '设置自动备份时间',
|
callerIdNumber:'主叫号码',
|
||||||
neTypePlease: '查询网元类型',
|
calleeIdNumber:'被叫号码',
|
||||||
neType: '网元类型',
|
|
||||||
neID: '网元内部标识',
|
|
||||||
fileName: '文件名',
|
|
||||||
createAt: '创建时间',
|
|
||||||
remark:'备份说明',
|
|
||||||
edit:'编辑备份文件',
|
|
||||||
totalSure:'确认{oper}记录编号为 {id} 的数据项?',
|
|
||||||
},
|
|
||||||
softwareManage: {
|
|
||||||
sendBtn: '下发',
|
|
||||||
runBtn: '激活',
|
|
||||||
backBtn: '回退',
|
|
||||||
historyBtn: '下发记录',
|
|
||||||
neTypePlease: '选择网元类型',
|
|
||||||
neType: '网元类型',
|
|
||||||
fileName: '文件名',
|
|
||||||
version: '版本号',
|
|
||||||
versionPlease: '版本号不能为空',
|
|
||||||
updateTime: '上传时间',
|
|
||||||
description: '功能描述',
|
|
||||||
deleteTip: '确认删除 【{fileName}】 的软件数据项?',
|
|
||||||
downloadTip: '确认下载 【{fileName}】 的软件数据项?',
|
|
||||||
updateComment: '软件说明',
|
|
||||||
updateCommentPlease: '请输入软件说明',
|
|
||||||
updateFile: '软件文件',
|
|
||||||
updateFilePlease: '请上传更新软件文件',
|
|
||||||
verifyFile: '校验文件',
|
|
||||||
selectFile: '选择文件',
|
|
||||||
sendTitle: '下发软件版本',
|
|
||||||
sendContent: '确认下发软件包为【{fileName}】的文件到对应网元?',
|
|
||||||
runTitle: '激活软件版本',
|
|
||||||
runContent: '确认在对应网元激活已下发【{fileName}】的软件版本?',
|
|
||||||
backTitle: '回退软件版本',
|
|
||||||
backContent: '确认在对应网元回退已下发【{fileName}】的软件版本?',
|
|
||||||
neId: '对应网元',
|
|
||||||
neIdPlease: '请选择对应网元',
|
|
||||||
versions:'版本',
|
|
||||||
upVersions:'升级前版本',
|
|
||||||
backVersions:'回退前版本',
|
|
||||||
status:'状态',
|
status:'状态',
|
||||||
letUpTime:'激活时间',
|
ticketId:'工单编号',
|
||||||
createTime:'创建时间',
|
startTime:'创建时间',
|
||||||
onlyAble:'只支持上传文件格式 {fileText}',
|
updateTime:'更新时间',
|
||||||
nullVersion:'当前网元无可回退版本',
|
msdData:'MSD内容',
|
||||||
},
|
agentName: '座席名称',
|
||||||
license: {
|
comment: '备注',
|
||||||
neTypePlease: '选择网元类型',
|
agentEmail: '座席邮箱',
|
||||||
neType: '网元类型',
|
agentMobile: '座席手机',
|
||||||
serialNum: '序列号',
|
title: '工单列表',
|
||||||
createTime: '时间',
|
}
|
||||||
comment: '说明',
|
|
||||||
updateComment: 'License说明',
|
|
||||||
updateCommentPlease: '请输入License说明',
|
|
||||||
updateFile: 'License文件',
|
|
||||||
updateFilePlease: '请上传更新License文件',
|
|
||||||
selectFile: '选择文件',
|
|
||||||
neId: '网元内部标识',
|
|
||||||
neIdPlease: '请选择对应网元',
|
|
||||||
},
|
|
||||||
configParam:{
|
|
||||||
dataNull:'暂无配置项数据',
|
|
||||||
editSuss:'修改成功',
|
|
||||||
editFail:'修改失败',
|
|
||||||
unable:'非法操作属性值',
|
|
||||||
delSure:'确认删除Index为 【{value}】 的数据项?',
|
|
||||||
addSuss:'新增成功',
|
|
||||||
addFail:'新增失败',
|
|
||||||
delArraySure:'确认删除{arrayChildTitle} Index 为 【{value}】 的数据项?',
|
|
||||||
parUnable:'参数值不在合理范围',
|
|
||||||
ipv4Tip:'不是合法的IPV4地址',
|
|
||||||
ipv6Tip:'不是合法的IPV6地址',
|
|
||||||
enumTip:'不是合理的枚举值',
|
|
||||||
boolTip:'不是合理的布尔类型的值',
|
|
||||||
default:'输入值是未知类型',
|
|
||||||
reloadSuss:'网元重新加载完成',
|
|
||||||
reloadFail:'网元重新加载失败',
|
|
||||||
neNUll:'暂无网元列表数据',
|
|
||||||
reload:'重载',
|
|
||||||
post:'提交',
|
|
||||||
editSure:'确认更新该属性值吗?',
|
|
||||||
arraryEdit:'确认提交更新 Index 为 【{value}】 的记录吗?',
|
|
||||||
addSure:'确认提交新增 Index :【{value}】 的记录吗?',
|
|
||||||
},
|
|
||||||
configParamForm: {
|
|
||||||
treeTitle: "配置导航",
|
|
||||||
treeSelectTip: "左侧配置导航中选择配置项信息!",
|
|
||||||
neType: "网元类型",
|
|
||||||
neTypePleace: "请选择网元类型",
|
|
||||||
noConfigData: "暂无配置项数据",
|
|
||||||
updateValue: "【 {num} 】 属性值修改成功",
|
|
||||||
updateValueErr: "属性值修改失败",
|
|
||||||
updateItem: "修改 Index 为 {num} 记录成功",
|
|
||||||
updateItemErr: "记录修改失败",
|
|
||||||
delItemOk: "删除 Index 为 {num} 记录成功",
|
|
||||||
addItemOk: "新增 Index 为 {num} 记录成功",
|
|
||||||
addItemErr: "记录新增失败",
|
|
||||||
requireUn: "【 {display} 】输入值是未知类型",
|
|
||||||
requireString: "【 {display} 】参数值不合理",
|
|
||||||
requireInt: "【 {display} 】参数值不在合理范围 {filter}",
|
|
||||||
requireIpv4: "【 {display} 】不是合法的IPV4地址",
|
|
||||||
requireIpv6: "【 {display} 】不是合法的IPV6地址",
|
|
||||||
requireEnum: "【 {display} 】不是合理的枚举值",
|
|
||||||
requireBool: "【 {display} 】不是合理的布尔类型的值",
|
|
||||||
editOkTip: "确认更新该【 {num} 】属性值吗?",
|
|
||||||
updateItemTip: "确认更新Index为 【{num}】 的数据项?",
|
|
||||||
delItemTip: "确认删除Index为 【{num}】 的数据项?",
|
|
||||||
arrayMore: "展开",
|
|
||||||
},
|
},
|
||||||
|
cbc:{
|
||||||
|
cbe:{
|
||||||
|
neType:'CBC网元对象',
|
||||||
|
title: '广播事件',
|
||||||
|
delTip: '确认删除编号为【{num}】的CBC事件吗?',
|
||||||
|
eventName: '事件名称',
|
||||||
|
startTime: '开始时间',
|
||||||
|
endTime: '结束时间',
|
||||||
|
repetitionPeriod: '广播周期',
|
||||||
|
numOfBcast: '广播次数',
|
||||||
|
msgPWSType: '消息类型',
|
||||||
|
messageId: '消息编号',
|
||||||
|
displayMode: '显示模式',
|
||||||
|
geoScope:'广播覆盖范围',
|
||||||
|
emergencyUserAlert: '紧急用户提示',
|
||||||
|
activatePopup: '弹窗提示',
|
||||||
|
warningType: '预警类型标识',
|
||||||
|
language:'语言',
|
||||||
|
warningMessageText:'广播内容',
|
||||||
|
status: '状态',
|
||||||
|
warningAreaType: '预警区域类型',
|
||||||
|
taiListTip:'TAI List 不能为空',
|
||||||
|
taiSonTip:'TAI List 每项的 MCC、MNC、TAC 都不能为空',
|
||||||
|
eutraListTip:'EUTRA CellId List 不能为空',
|
||||||
|
eutraSonTip:'EUTRA CellId List 每项的 MCC、MNC、CellId 都不能为空',
|
||||||
|
nrTip:'NR CellId List 不能为空',
|
||||||
|
nrSonTip:'NR CellId List 每项的 MCC、MNC、CellId 都不能为空',
|
||||||
|
areaTip:'Area ID List 不能为空',
|
||||||
|
areaSonTip:'Area ID List 每项的 AreaID 都不能为空',
|
||||||
|
messageIdProfile:'消息标识配置',
|
||||||
|
serialNumProfile:'序列号配置',
|
||||||
|
warningTypeProfile:'预警类型配置',
|
||||||
|
warningMessageProfile:'预警消息配置',
|
||||||
|
etws:'地震海啸预警',
|
||||||
|
cmas:'公共预警广播',
|
||||||
|
createdAt:'创建时间',
|
||||||
|
eventNameHelp:'如果为CMAS,推荐前缀为cmas_xxx,如果为ETWS,推荐前缀为etws_xxx',
|
||||||
|
repetitionPeriodHelp:'单位是秒',
|
||||||
|
numOfBcastHelp:'单位是秒',
|
||||||
|
cellListTip:'CellId List不能为空',
|
||||||
|
cellListSonTip:'MCC、MNC、不能为空以及EUTRA CellId/NR CellId至少填写一个',
|
||||||
|
letupSure:'确认修改编号为 【{id}】 的广播事件的状态嘛?',
|
||||||
|
tacHelp:'TAC值是十进制字符串,通过";"分隔多个TAC值',
|
||||||
|
cellIdHelp:'CellId值是十六进制字符串,通过";"分隔多个CellId值',
|
||||||
|
areaId:'areaId值是十六进制字符串',
|
||||||
|
detail:'详情',
|
||||||
|
}
|
||||||
},
|
},
|
||||||
dashboard: {
|
dashboard: {
|
||||||
overview:{
|
overview:{
|
||||||
title: "核心网系统看板",
|
title: "核心网系统看板",
|
||||||
fullscreen: "点击全屏显示",
|
fullscreen: "点击全屏显示",
|
||||||
toRouter: "点击跳转详情页面",
|
toRouter: "点击跳转详情页面",
|
||||||
|
psapTitle:'PSAP看板',
|
||||||
|
onlineUser:'在线座席数',
|
||||||
|
totalUser:'总座席数',
|
||||||
|
parallelUser:'并行通话数',
|
||||||
|
userTitle:'用户统计',
|
||||||
|
sysTitle:'系统资源',
|
||||||
skim: {
|
skim: {
|
||||||
users: "用户数",
|
users: "用户数",
|
||||||
userTitle:'用户信息',
|
userTitle:'用户信息',
|
||||||
@@ -579,14 +503,47 @@ export default {
|
|||||||
resultFail: "失败",
|
resultFail: "失败",
|
||||||
delTip: "确认删除编号为【{msg}】的数据项?",
|
delTip: "确认删除编号为【{msg}】的数据项?",
|
||||||
exportTip: "确认导出当前查询条件的话单数据吗?(导出最大支持一万条)",
|
exportTip: "确认导出当前查询条件的话单数据吗?(导出最大支持一万条)",
|
||||||
smfChargingID: '计费ID',
|
chargingID: '计费ID',
|
||||||
smfSubscriptionIDData: '订阅 ID 数据',
|
smfSubscriptionIDData: '订阅 ID 数据',
|
||||||
smfSubscriptionIDType: '订阅 ID 类型',
|
smfSubscriptionIDType: '订阅 ID 类型',
|
||||||
smfDataVolumeUplink: '数据量上行链路',
|
smfDataVolumeUplink: '数据量上行链路',
|
||||||
smfDataVolumeDownlink: '数据量下行链路',
|
smfDataVolumeDownlink: '数据量下行链路',
|
||||||
smfDataTotalVolume: '数据总量',
|
smfDataTotalVolume: '数据总量',
|
||||||
smfDuration: '持续时间',
|
durationTime: '持续时间',
|
||||||
smfInvocationTime: '调用时间',
|
invocationTime: '调用时间',
|
||||||
|
sgwcServedIMSI: 'IMSI',
|
||||||
|
sgwcServedMSISDN: 'MSISDN',
|
||||||
|
sgwcVolumeGPRSUplink: 'GPRS 上行链路',
|
||||||
|
sgwcVolumeGPRSDownlink: 'GPRS 下行链路',
|
||||||
|
recordPath:'录音文件路径',
|
||||||
|
msd:'最小数据集',
|
||||||
|
automaticActivation: '自动激活',
|
||||||
|
positionCanBeTrusted: '位置可信',
|
||||||
|
testCall: '测试呼叫',
|
||||||
|
vehicleType:'车辆类型',
|
||||||
|
messageIdentifier: '消息标识',
|
||||||
|
numberOfOccupants: '乘员数量',
|
||||||
|
n1latitudeDelta:'纬度增量',
|
||||||
|
n1longitudeDelta: '经度增量',
|
||||||
|
n2latitudeDelta:' 纬度增量',
|
||||||
|
n2longitudeDelta: '经度增量',
|
||||||
|
timestamp: '时间戳',
|
||||||
|
vehicleDirection: '车辆行驶方向',
|
||||||
|
isovds: '车辆描述段',
|
||||||
|
isovisModelyear:'车辆标识段年份',
|
||||||
|
isovisSeqPlant: '车辆标识段的工厂编码与生产序号',
|
||||||
|
isowmi: '世界制造商标识',
|
||||||
|
positionLatitude: '位置纬度',
|
||||||
|
positionLongitude: '位置经度',
|
||||||
|
dieselTankPresent: '柴油罐存在',
|
||||||
|
electricEnergyStorage: '电力储能',
|
||||||
|
gasolineTankPresent: '汽油罐存在',
|
||||||
|
control:'控制',
|
||||||
|
recentVehicleLocationN1:'最近车辆位置N1',
|
||||||
|
recentVehicleLocationN2:'最近车辆位置N2',
|
||||||
|
vehicleIdentificationNumber:'车辆识别号码',
|
||||||
|
vehicleLocation:'车辆位置',
|
||||||
|
vehiclePropulsionStorageType:'车辆推进存储类型',
|
||||||
},
|
},
|
||||||
ue: {
|
ue: {
|
||||||
eventType: "事件类型",
|
eventType: "事件类型",
|
||||||
@@ -761,6 +718,7 @@ export default {
|
|||||||
upgradeDone: '更新完成,服务正在重载',
|
upgradeDone: '更新完成,服务正在重载',
|
||||||
upgradeFail: '更新失败,请检查软件文件是否存在且服务终端环境是否可用!',
|
upgradeFail: '更新失败,请检查软件文件是否存在且服务终端环境是否可用!',
|
||||||
upgradeModal: '网元版本更新',
|
upgradeModal: '网元版本更新',
|
||||||
|
noPath: '软件包文件未发现',
|
||||||
},
|
},
|
||||||
neLicense: {
|
neLicense: {
|
||||||
status: "许可证状态",
|
status: "许可证状态",
|
||||||
@@ -784,7 +742,9 @@ export default {
|
|||||||
treeSelectTip: "左侧配置导航中选择配置项信息!",
|
treeSelectTip: "左侧配置导航中选择配置项信息!",
|
||||||
neType: "网元类型",
|
neType: "网元类型",
|
||||||
neTypePleace: "请选择网元类型",
|
neTypePleace: "请选择网元类型",
|
||||||
|
neIdSyncPleace: "请选择同步网元",
|
||||||
noConfigData: "暂无配置项数据",
|
noConfigData: "暂无配置项数据",
|
||||||
|
noConfigdDisabled: "配置项网元未正常服务",
|
||||||
updateValue: "【 {num} 】 属性值修改成功",
|
updateValue: "【 {num} 】 属性值修改成功",
|
||||||
updateValueErr: "属性值修改失败",
|
updateValueErr: "属性值修改失败",
|
||||||
updateItem: "修改 Index 为 {num} 记录成功",
|
updateItem: "修改 Index 为 {num} 记录成功",
|
||||||
@@ -858,6 +818,30 @@ export default {
|
|||||||
licenseTip2: '2. 点击【结束】将结束安装过程',
|
licenseTip2: '2. 点击【结束】将结束安装过程',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
neData: {
|
||||||
|
baseStation: {
|
||||||
|
list: "列表",
|
||||||
|
topology: "拓扑图",
|
||||||
|
nbName: "设备名称",
|
||||||
|
ueNum: "在线用户数",
|
||||||
|
topologyTitle: "基站状态关系图",
|
||||||
|
name: "基站名称",
|
||||||
|
namePlease: "文本内容长度0~64",
|
||||||
|
position: "基站位置",
|
||||||
|
positionPlease: "位置描述。禁止空格,文本内容长度0-64",
|
||||||
|
address: "IP地址",
|
||||||
|
addressPlease: "文本内容长度0~64",
|
||||||
|
state: "基站状态",
|
||||||
|
online: "在线",
|
||||||
|
offline: "离线",
|
||||||
|
time: "变更时间",
|
||||||
|
addRadio: "添加基站信息",
|
||||||
|
editRadio: "更新基站信息",
|
||||||
|
history: "历史记录",
|
||||||
|
exportTip: "确认根据搜索条件导出xlsx表格文件吗?",
|
||||||
|
importDataEmpty: "导入数据为空",
|
||||||
|
},
|
||||||
|
},
|
||||||
neUser: {
|
neUser: {
|
||||||
auth: {
|
auth: {
|
||||||
authInfo:'鉴权信息',
|
authInfo:'鉴权信息',
|
||||||
@@ -868,6 +852,7 @@ export default {
|
|||||||
checkExport : '勾选导出',
|
checkExport : '勾选导出',
|
||||||
checkExportConfirm: '确认导出已勾选的鉴权用户数据吗?',
|
checkExportConfirm: '确认导出已勾选的鉴权用户数据吗?',
|
||||||
import: '导入',
|
import: '导入',
|
||||||
|
importFail: '失败记录',
|
||||||
loadDataConfirm: '确认要重新加载数据吗?',
|
loadDataConfirm: '确认要重新加载数据吗?',
|
||||||
loadData: '加载数据',
|
loadData: '加载数据',
|
||||||
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新,大约需要{timer}秒,请稍候!!!',
|
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新,大约需要{timer}秒,请稍候!!!',
|
||||||
@@ -896,6 +881,7 @@ export default {
|
|||||||
checkExport : '勾选导出',
|
checkExport : '勾选导出',
|
||||||
checkExportConfirm: '确认导出已勾选的签约用户数据吗?',
|
checkExportConfirm: '确认导出已勾选的签约用户数据吗?',
|
||||||
import: '导入',
|
import: '导入',
|
||||||
|
importFail: '失败记录',
|
||||||
loadDataConfirm: '确认要重新加载数据吗?',
|
loadDataConfirm: '确认要重新加载数据吗?',
|
||||||
loadData: '加载数据',
|
loadData: '加载数据',
|
||||||
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新,大约需要{timer}秒,请稍候!!!',
|
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新,大约需要{timer}秒,请稍候!!!',
|
||||||
@@ -1090,6 +1076,7 @@ export default {
|
|||||||
expressionModal:'表达式模块',
|
expressionModal:'表达式模块',
|
||||||
expressionErrorTip:'请检查表达式,错误的指标为{kpiId}',
|
expressionErrorTip:'请检查表达式,错误的指标为{kpiId}',
|
||||||
expressionNoIdTip:'请检查表达式,没有找到任何有效的指标',
|
expressionNoIdTip:'请检查表达式,没有找到任何有效的指标',
|
||||||
|
unitSelect:'为更好展示图需选择相同单位,当前单位为:',
|
||||||
},
|
},
|
||||||
kpiKeyTarget:{
|
kpiKeyTarget:{
|
||||||
"time":"时间",
|
"time":"时间",
|
||||||
@@ -1109,7 +1096,13 @@ export default {
|
|||||||
kpiOverView:{
|
kpiOverView:{
|
||||||
"kpiName":"指标名",
|
"kpiName":"指标名",
|
||||||
"maxValue":"最大值",
|
"maxValue":"最大值",
|
||||||
|
"maxValueTip":"用户筛选时间范围内度量数据的最大值。",
|
||||||
"minValue":"最小值",
|
"minValue":"最小值",
|
||||||
|
"minValueTip":"用户筛选时间范围内度量数据的最小值。",
|
||||||
|
"avgValue":"平均值",
|
||||||
|
"avgValueTip":"用户筛选时间范围内度量数据的平均值。",
|
||||||
|
"totalValue":"总值",
|
||||||
|
"totalValueTip":"用户筛选时间范围内度量数据的总值。",
|
||||||
"kpiChartTitle":"网元指标概览",
|
"kpiChartTitle":"网元指标概览",
|
||||||
"changeLine":"切换为折线图",
|
"changeLine":"切换为折线图",
|
||||||
"changeBar":"切换为柱状图",
|
"changeBar":"切换为柱状图",
|
||||||
@@ -1320,6 +1313,7 @@ export default {
|
|||||||
size: "文件大小",
|
size: "文件大小",
|
||||||
modifiedTime: "修改时间",
|
modifiedTime: "修改时间",
|
||||||
fileName: "文件名称",
|
fileName: "文件名称",
|
||||||
|
downTipZip: "确认将目录 【{fileName}】 下载为ZIP文件?",
|
||||||
downTip: "确认下载文件名为 【{fileName}】 文件?",
|
downTip: "确认下载文件名为 【{fileName}】 文件?",
|
||||||
downTipErr: "文件获取失败",
|
downTipErr: "文件获取失败",
|
||||||
dirCd: "进入目录",
|
dirCd: "进入目录",
|
||||||
|
|||||||
@@ -29,12 +29,15 @@ import { MENU_PATH_INLINE } from '@/constants/menu-constants';
|
|||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
import { parseUrlPath } from '@/plugins/file-static-url';
|
import { parseUrlPath } from '@/plugins/file-static-url';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
|
||||||
const { proConfig, waterMarkContent } = useLayoutStore();
|
const { proConfig, waterMarkContent } = useLayoutStore();
|
||||||
const { t, currentLocale } = useI18n();
|
const { t, currentLocale } = useI18n();
|
||||||
const routerStore = useRouterStore();
|
const routerStore = useRouterStore();
|
||||||
const tabsStore = useTabsStore();
|
const tabsStore = useTabsStore();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const neListStore = useNeInfoStore();
|
||||||
|
|
||||||
/**菜单面板 */
|
/**菜单面板 */
|
||||||
const layoutState = reactive({
|
const layoutState = reactive({
|
||||||
@@ -65,8 +68,9 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 动态路由添加到菜单面板
|
// 动态路由添加到菜单面板
|
||||||
const rootRoute = router.getRoutes().find(r => r.name === 'Root');
|
const menuData = computed(() => {
|
||||||
if (rootRoute) {
|
const rootRoute = router.getRoutes().find(r => r.name === 'Root');
|
||||||
|
if (rootRoute) {
|
||||||
const children = routerStore.setRootRouterData(rootRoute.children);
|
const children = routerStore.setRootRouterData(rootRoute.children);
|
||||||
const buildRouterData = routerStore.buildRouterData;
|
const buildRouterData = routerStore.buildRouterData;
|
||||||
if (buildRouterData.length > 0) {
|
if (buildRouterData.length > 0) {
|
||||||
@@ -74,9 +78,13 @@ if (rootRoute) {
|
|||||||
} else {
|
} else {
|
||||||
rootRoute.children = children;
|
rootRoute.children = children;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const neTypes = neListStore.getNeSelectOtions.map(v => v.value);
|
||||||
const { menuData } = getMenuData(clearMenuItem(router.getRoutes()));
|
let routes = clearMenuItem(router.getRoutes());
|
||||||
|
routes = routerStore.clearMenuItemByNeList(routes, neTypes);
|
||||||
|
const { menuData } = getMenuData(routes);
|
||||||
|
return menuData;
|
||||||
|
});
|
||||||
|
|
||||||
/**面包屑数据对象,排除根节点和首页不显示 */
|
/**面包屑数据对象,排除根节点和首页不显示 */
|
||||||
const breadcrumb = computed(() => {
|
const breadcrumb = computed(() => {
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ function beforeRequest(options: OptionsType): OptionsType | Promise<any> {
|
|||||||
if (options.method === 'get') return options;
|
if (options.method === 'get') return options;
|
||||||
|
|
||||||
// 非get参数提交
|
// 非get参数提交
|
||||||
let body = options.data
|
let body = options.data;
|
||||||
if (body instanceof FormData) {
|
if (body instanceof FormData) {
|
||||||
options.body = body;
|
options.body = body;
|
||||||
} else if (body) {
|
} else if (body) {
|
||||||
|
|||||||
+326
-14
@@ -13,6 +13,7 @@ import { validHttp } from '@/utils/regular-utils';
|
|||||||
import useUserStore from '@/store/modules/user';
|
import useUserStore from '@/store/modules/user';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import useRouterStore from '@/store/modules/router';
|
import useRouterStore from '@/store/modules/router';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
|
||||||
// NProgress Configuration
|
// NProgress Configuration
|
||||||
NProgress.configure({ showSpinner: false });
|
NProgress.configure({ showSpinner: false });
|
||||||
@@ -144,10 +145,17 @@ const router = createRouter({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**全局路由-后置守卫 */
|
/**全局路由-后置守卫 */
|
||||||
|
// 在路由后置守卫中清理计数器
|
||||||
router.afterEach((to, from, failure) => {
|
router.afterEach((to, from, failure) => {
|
||||||
NProgress.done();
|
NProgress.done();
|
||||||
|
|
||||||
|
// 清理成功导航的重定向计数
|
||||||
|
if (!failure) {
|
||||||
|
const redirectKey = `${from.path}->${to.path}`;
|
||||||
|
redirectCount.delete(redirectKey);
|
||||||
|
}
|
||||||
|
|
||||||
const title = to.meta?.title;
|
const title = to.meta?.title;
|
||||||
// 设置标题
|
|
||||||
if (!failure && title) {
|
if (!failure && title) {
|
||||||
useAppStore().setTitle(to.meta.title);
|
useAppStore().setTitle(to.meta.title);
|
||||||
}
|
}
|
||||||
@@ -163,10 +171,27 @@ const WHITE_LIST: string[] = [
|
|||||||
'/trace-task-hlr',
|
'/trace-task-hlr',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const redirectCount = new Map<string, number>();
|
||||||
|
const MAX_REDIRECT_COUNT = 3; // 最大重定向次数
|
||||||
|
|
||||||
/**全局路由-前置守卫 */
|
/**全局路由-前置守卫 */
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
NProgress.start();
|
NProgress.start();
|
||||||
|
|
||||||
|
// 检查是否是 F5 刷新(from.name 为 null 且 to.path 不是根路径)
|
||||||
|
const isRefresh = !from.name && from.path === '/';
|
||||||
|
|
||||||
|
// 重定向计数检查
|
||||||
|
const redirectKey = `${from.path}->${to.path}`;
|
||||||
|
const currentCount = redirectCount.get(redirectKey) || 0;
|
||||||
|
|
||||||
|
if (currentCount > MAX_REDIRECT_COUNT) {
|
||||||
|
console.warn(`检测到重定向循环: ${redirectKey},强制跳转到首页`);
|
||||||
|
redirectCount.delete(redirectKey);
|
||||||
|
next({ name: 'Index', replace: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 获取系统配置信息
|
// 获取系统配置信息
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
if (!appStore.loginBackground) {
|
if (!appStore.loginBackground) {
|
||||||
@@ -182,7 +207,12 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
next({ name: 'Index' });
|
next({ name: 'Index' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = getToken();
|
let token = getToken();
|
||||||
|
|
||||||
|
// 免用户登录认证
|
||||||
|
if (!appStore.loginAuth) {
|
||||||
|
token = '== Not Login Auth ==';
|
||||||
|
}
|
||||||
|
|
||||||
// 没有token
|
// 没有token
|
||||||
if (!token) {
|
if (!token) {
|
||||||
@@ -197,39 +227,321 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
|
|
||||||
// 有Token
|
// 有Token
|
||||||
if (token) {
|
if (token) {
|
||||||
// 防止重复访问登录页面
|
|
||||||
if (to.path === '/login') {
|
if (to.path === '/login') {
|
||||||
next({ name: 'Index' });
|
next({ name: 'Index' });
|
||||||
} else {
|
} else {
|
||||||
// 判断当前用户是否有角色信息
|
|
||||||
const user = useUserStore();
|
const user = useUserStore();
|
||||||
if (user.roles && user.roles.length === 0) {
|
if (user.roles && user.roles.length === 0) {
|
||||||
try {
|
try {
|
||||||
// 获取用户信息
|
await useNeInfoStore().fnNelist();
|
||||||
await user.fnGetInfo();
|
await user.fnGetInfo();
|
||||||
// 获取路由信息
|
|
||||||
const accessRoutes = await useRouterStore().generateRoutes();
|
const accessRoutes = await useRouterStore().generateRoutes();
|
||||||
// 根据后台配置生成可访问的路由表
|
|
||||||
if (accessRoutes && accessRoutes.length !== 0) {
|
if (accessRoutes && accessRoutes.length !== 0) {
|
||||||
for (const route of accessRoutes) {
|
for (const route of accessRoutes) {
|
||||||
// 动态添加可访问路由表,http开头会异常
|
|
||||||
if (!validHttp(route.path)) {
|
if (!validHttp(route.path)) {
|
||||||
router.addRoute(route);
|
router.addRoute(route);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// 刷新替换原先路由,确保addRoutes已完成
|
// F5 刷新时,如果目标路由有效,直接跳转,不进行重定向
|
||||||
|
if (isRefresh && await isRouteAccessible(to, accessRoutes)) {
|
||||||
|
console.log(`F5 刷新,目标路由有效,直接跳转: ${to.path}`);
|
||||||
next({ ...to, replace: true });
|
next({ ...to, replace: true });
|
||||||
} catch (error: any) {
|
return;
|
||||||
console.error(`[${to.path}]: ${error.message}`);
|
}
|
||||||
await user.fnLogOut();
|
|
||||||
next({ name: 'Login' });
|
if (await isRouteAccessible(to, accessRoutes)) {
|
||||||
|
next({ ...to, replace: true });
|
||||||
|
} else {
|
||||||
|
// F5 刷新时,如果目标路由无效,优先跳转到首页
|
||||||
|
if (isRefresh) {
|
||||||
|
console.log(`F5 刷新,目标路由无效,跳转到首页: ${to.path}`);
|
||||||
|
next({ name: 'Index', replace: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const validRedirect = findValidRedirect(to, accessRoutes);
|
||||||
|
if (validRedirect && validRedirect !== to.path) {
|
||||||
|
redirectCount.set(redirectKey, currentCount + 1);
|
||||||
|
console.log(`重定向到有效路由: ${to.path} -> ${validRedirect}`);
|
||||||
|
next({ path: validRedirect, replace: true });
|
||||||
|
} else {
|
||||||
|
next({ name: 'Index', replace: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
next({ name: 'Index', replace: true });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Route guard error:', error);
|
||||||
|
next(`/login?redirect=${to.fullPath}`);
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
to.meta.neType &&
|
||||||
|
to.meta.neType.length > 0 &&
|
||||||
|
!useNeInfoStore().fnHasNe(to.meta.neType)
|
||||||
|
) {
|
||||||
|
// 找到有效的替代路由
|
||||||
|
const validRedirect = findValidAlternative(to);
|
||||||
|
if (validRedirect && validRedirect !== to.path) {
|
||||||
|
redirectCount.set(redirectKey, currentCount + 1);
|
||||||
|
console.log(`403 重定向: ${to.path} -> ${validRedirect}`);
|
||||||
|
next({ path: validRedirect, replace: true });
|
||||||
|
} else {
|
||||||
|
console.log(`无有效替代路由,跳转到权限错误页面: ${to.path}`);
|
||||||
|
next({ name: 'NotPermission' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 清除重定向计数
|
||||||
|
redirectCount.clear();
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查路由是否可访问
|
||||||
|
*/
|
||||||
|
async function isRouteAccessible(to: any, accessRoutes: any[]): Promise<boolean> {
|
||||||
|
// 检查路由是否存在于 accessRoutes 中
|
||||||
|
const routeExists = findRouteInAccessRoutes(to.path, accessRoutes);
|
||||||
|
|
||||||
|
console.log(`检查路由可访问性: ${to.path}`, routeExists ? '找到' : '未找到');
|
||||||
|
|
||||||
|
if (!routeExists) return false;
|
||||||
|
|
||||||
|
// 检查网元类型
|
||||||
|
if (to.meta?.neType && to.meta.neType.length > 0) {
|
||||||
|
const hasNe = useNeInfoStore().fnHasNe(to.meta.neType);
|
||||||
|
console.log(`网元类型检查: ${to.meta.neType}`, hasNe ? '有效' : '无效');
|
||||||
|
return hasNe;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在访问路由中查找指定路径
|
||||||
|
*/
|
||||||
|
function findRouteInAccessRoutes(targetPath: string, routes: any[], parentPath: string = ''): any {
|
||||||
|
for (const route of routes) {
|
||||||
|
// 构建完整路径
|
||||||
|
let fullPath = route.path;
|
||||||
|
|
||||||
|
// 如果不是绝对路径,需要拼接父路径
|
||||||
|
if (!fullPath.startsWith('/')) {
|
||||||
|
const cleanParentPath = parentPath.replace(/\/$/, ''); // 移除末尾斜杠
|
||||||
|
const cleanRoutePath = fullPath.replace(/^\//, ''); // 移除开头斜杠
|
||||||
|
fullPath = cleanParentPath + '/' + cleanRoutePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标准化路径,移除多余的斜杠
|
||||||
|
fullPath = fullPath.replace(/\/+/g, '/');
|
||||||
|
|
||||||
|
console.log(`匹配路径: ${targetPath} vs ${fullPath}`);
|
||||||
|
|
||||||
|
if (fullPath === targetPath) {
|
||||||
|
return route;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归查找子路由
|
||||||
|
if (route.children && route.children.length > 0) {
|
||||||
|
const found = findRouteInAccessRoutes(targetPath, route.children, fullPath);
|
||||||
|
if (found) return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找有效的重定向目标
|
||||||
|
*/
|
||||||
|
function findValidRedirect(to: any, accessRoutes: any[]): string | null {
|
||||||
|
const neStore = useNeInfoStore();
|
||||||
|
|
||||||
|
console.log(`查找重定向目标: ${to.path}`);
|
||||||
|
|
||||||
|
// 1. 查找父路由的 redirect
|
||||||
|
const parentRoute = findParentRouteWithRedirect(to.path, accessRoutes);
|
||||||
|
|
||||||
|
if (parentRoute?.redirect) {
|
||||||
|
console.log(`找到父路由重定向: ${parentRoute.path} -> ${parentRoute.redirect}`);
|
||||||
|
|
||||||
|
// 验证 redirect 目标是否有效
|
||||||
|
const redirectTarget = findRouteInAccessRoutes(parentRoute.redirect, accessRoutes);
|
||||||
|
if (redirectTarget &&
|
||||||
|
(!redirectTarget.meta?.neType || neStore.fnHasNe(redirectTarget.meta.neType))) {
|
||||||
|
return parentRoute.redirect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 查找同级的第一个有效路由
|
||||||
|
const siblingRoute = findFirstValidSibling(to.path, accessRoutes);
|
||||||
|
if (siblingRoute) {
|
||||||
|
console.log(`找到同级有效路由: ${siblingRoute}`);
|
||||||
|
return siblingRoute;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 查找根级别的第一个有效路由
|
||||||
|
const rootRoute = findFirstValidRootRoute(accessRoutes);
|
||||||
|
if (rootRoute) {
|
||||||
|
console.log(`找到根级有效路由: ${rootRoute}`);
|
||||||
|
return rootRoute;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找具有 redirect 的父路由
|
||||||
|
*/
|
||||||
|
function findParentRouteWithRedirect(targetPath: string, routes: any[], parentPath: string = ''): any {
|
||||||
|
for (const route of routes) {
|
||||||
|
let fullPath = route.path;
|
||||||
|
|
||||||
|
if (!fullPath.startsWith('/')) {
|
||||||
|
const cleanParentPath = parentPath.replace(/\/$/, '');
|
||||||
|
const cleanRoutePath = fullPath.replace(/^\//, '');
|
||||||
|
fullPath = cleanParentPath + '/' + cleanRoutePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPath = fullPath.replace(/\/+/g, '/');
|
||||||
|
|
||||||
|
// 检查是否是父路径且有 redirect
|
||||||
|
if (targetPath.startsWith(fullPath) &&
|
||||||
|
route.redirect &&
|
||||||
|
fullPath !== targetPath) {
|
||||||
|
return { ...route, path: fullPath };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归查找
|
||||||
|
if (route.children && route.children.length > 0) {
|
||||||
|
const found = findParentRouteWithRedirect(targetPath, route.children, fullPath);
|
||||||
|
if (found) return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找同级的第一个有效路由
|
||||||
|
*/
|
||||||
|
function findFirstValidSibling(targetPath: string, routes: any[], parentPath: string = ''): string | null {
|
||||||
|
const neStore = useNeInfoStore();
|
||||||
|
|
||||||
|
// 获取父路径
|
||||||
|
const parentRouteResult = findParentOfTarget(targetPath, routes, parentPath);
|
||||||
|
if (!parentRouteResult) return null;
|
||||||
|
|
||||||
|
const { parentRoute, parentFullPath } = parentRouteResult;
|
||||||
|
|
||||||
|
if (parentRoute.children) {
|
||||||
|
for (const sibling of parentRoute.children) {
|
||||||
|
let siblingFullPath = sibling.path;
|
||||||
|
|
||||||
|
if (!siblingFullPath.startsWith('/')) {
|
||||||
|
const cleanParentPath = parentFullPath.replace(/\/$/, '');
|
||||||
|
const cleanSiblingPath = siblingFullPath.replace(/^\//, '');
|
||||||
|
siblingFullPath = cleanParentPath + '/' + cleanSiblingPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
siblingFullPath = siblingFullPath.replace(/\/+/g, '/');
|
||||||
|
|
||||||
|
if (siblingFullPath !== targetPath &&
|
||||||
|
(!sibling.meta?.neType || neStore.fnHasNe(sibling.meta.neType))) {
|
||||||
|
return siblingFullPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找目标路径的父路由
|
||||||
|
*/
|
||||||
|
function findParentOfTarget(targetPath: string, routes: any[], parentPath: string = ''): { parentRoute: any, parentFullPath: string } | null {
|
||||||
|
for (const route of routes) {
|
||||||
|
let fullPath = route.path;
|
||||||
|
|
||||||
|
if (!fullPath.startsWith('/')) {
|
||||||
|
const cleanParentPath = parentPath.replace(/\/$/, '');
|
||||||
|
const cleanRoutePath = fullPath.replace(/^\//, '');
|
||||||
|
fullPath = cleanParentPath + '/' + cleanRoutePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPath = fullPath.replace(/\/+/g, '/');
|
||||||
|
|
||||||
|
// 检查子路由
|
||||||
|
if (route.children && route.children.length > 0) {
|
||||||
|
for (const child of route.children) {
|
||||||
|
let childFullPath = child.path;
|
||||||
|
|
||||||
|
if (!childFullPath.startsWith('/')) {
|
||||||
|
const cleanParentPath = fullPath.replace(/\/$/, '');
|
||||||
|
const cleanChildPath = childFullPath.replace(/^\//, '');
|
||||||
|
childFullPath = cleanParentPath + '/' + cleanChildPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
childFullPath = childFullPath.replace(/\/+/g, '/');
|
||||||
|
|
||||||
|
if (childFullPath === targetPath) {
|
||||||
|
return { parentRoute: route, parentFullPath: fullPath };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归查找
|
||||||
|
const found = findParentOfTarget(targetPath, route.children, fullPath);
|
||||||
|
if (found) return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找根级别的第一个有效路由
|
||||||
|
*/
|
||||||
|
function findFirstValidRootRoute(routes: any[]): string | null {
|
||||||
|
const neStore = useNeInfoStore();
|
||||||
|
|
||||||
|
for (const route of routes) {
|
||||||
|
if (!route.meta?.neType || neStore.fnHasNe(route.meta.neType)) {
|
||||||
|
if (route.children && route.children.length > 0) {
|
||||||
|
// 如果有子路由,返回第一个有效子路由的完整路径
|
||||||
|
for (const child of route.children) {
|
||||||
|
let childFullPath = child.path;
|
||||||
|
|
||||||
|
if (!childFullPath.startsWith('/')) {
|
||||||
|
const cleanParentPath = route.path.replace(/\/$/, '');
|
||||||
|
const cleanChildPath = childFullPath.replace(/^\//, '');
|
||||||
|
childFullPath = cleanParentPath + '/' + cleanChildPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
childFullPath = childFullPath.replace(/\/+/g, '/');
|
||||||
|
|
||||||
|
if (!child.meta?.neType || neStore.fnHasNe(child.meta.neType)) {
|
||||||
|
return childFullPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return route.path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找有效的替代路由(用于 403 情况)
|
||||||
|
*/
|
||||||
|
function findValidAlternative(to: any): string | null {
|
||||||
|
const routerStore = useRouterStore();
|
||||||
|
const buildRoutes = routerStore.buildRouterData;
|
||||||
|
|
||||||
|
return findValidRedirect(to, buildRoutes);
|
||||||
|
}
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import { getSysConf } from '@/api';
|
import { getSysConf } from '@/api';
|
||||||
import { CACHE_LOCAL_I18N } from '@/constants/cache-keys-constants';
|
import { CACHE_LOCAL_I18N, CACHE_SESSION_CRYPTO_API } from '@/constants/cache-keys-constants';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { removeToken } from '@/plugins/auth-token';
|
import { removeToken } from '@/plugins/auth-token';
|
||||||
import { parseUrlPath } from '@/plugins/file-static-url';
|
import { parseUrlPath } from '@/plugins/file-static-url';
|
||||||
import { localGet, localSet } from '@/utils/cache-local-utils';
|
import { localGet, localSet } from '@/utils/cache-local-utils';
|
||||||
|
import { sessionSet } from '@/utils/cache-session-utils';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
/**应用参数类型 */
|
/**应用参数类型 */
|
||||||
@@ -20,6 +21,10 @@ type AppStore = {
|
|||||||
buildTime: string;
|
buildTime: string;
|
||||||
/**系统引导使用 */
|
/**系统引导使用 */
|
||||||
bootloader: boolean;
|
bootloader: boolean;
|
||||||
|
// 用户登录认证
|
||||||
|
loginAuth: boolean;
|
||||||
|
// 用户接口加密
|
||||||
|
cryptoApi: boolean;
|
||||||
// 序列号
|
// 序列号
|
||||||
serialNum: string;
|
serialNum: string;
|
||||||
/**应用版权声明 */
|
/**应用版权声明 */
|
||||||
@@ -52,6 +57,8 @@ const useAppStore = defineStore('app', {
|
|||||||
version: `-`,
|
version: `-`,
|
||||||
buildTime: `-`,
|
buildTime: `-`,
|
||||||
bootloader: false,
|
bootloader: false,
|
||||||
|
loginAuth: true,
|
||||||
|
cryptoApi: true,
|
||||||
serialNum: `-`,
|
serialNum: `-`,
|
||||||
copyright: `Copyright ©2023 For ${import.meta.env.VITE_APP_NAME}`,
|
copyright: `Copyright ©2023 For ${import.meta.env.VITE_APP_NAME}`,
|
||||||
logoType: 'icon',
|
logoType: 'icon',
|
||||||
@@ -85,6 +92,9 @@ const useAppStore = defineStore('app', {
|
|||||||
if (this.bootloader) {
|
if (this.bootloader) {
|
||||||
removeToken();
|
removeToken();
|
||||||
}
|
}
|
||||||
|
this.loginAuth = res.data.loginAuth !== 'false';
|
||||||
|
this.cryptoApi = res.data.cryptoApi !== 'false';
|
||||||
|
sessionSet(CACHE_SESSION_CRYPTO_API, res.data.cryptoApi);
|
||||||
this.serialNum = res.data.serialNum;
|
this.serialNum = res.data.serialNum;
|
||||||
this.appName = res.data.title;
|
this.appName = res.data.title;
|
||||||
this.copyright = res.data.copyright;
|
this.copyright = res.data.copyright;
|
||||||
|
|||||||
@@ -109,6 +109,32 @@ const useNeInfoStore = defineStore('neinfo', {
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 含有网元
|
||||||
|
* @param metaNeType ['udm', 'ims', 'udm+ims', 'SGWC'] 支持大小写
|
||||||
|
* @returns boolean
|
||||||
|
*/
|
||||||
|
fnHasNe(metaNeType: string[]) {
|
||||||
|
if (this.neList.length > 0) {
|
||||||
|
const neTypes = this.neSelectOtions.map(item => item.value);
|
||||||
|
let match = false; // 匹配
|
||||||
|
for (const netype of metaNeType) {
|
||||||
|
if (netype.indexOf('+') > -1) {
|
||||||
|
metaNeType = netype.split('+');
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
// 同时匹配
|
||||||
|
return metaNeType.every(item => neTypes.includes(item.toUpperCase()));
|
||||||
|
}
|
||||||
|
// 有一种
|
||||||
|
return metaNeType.some(item => neTypes.includes(item.toUpperCase()));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ import type {
|
|||||||
RouteComponent,
|
RouteComponent,
|
||||||
RouteLocationRaw,
|
RouteLocationRaw,
|
||||||
RouteMeta,
|
RouteMeta,
|
||||||
|
RouteRecord,
|
||||||
RouteRecordRaw,
|
RouteRecordRaw,
|
||||||
} from 'vue-router';
|
} from 'vue-router';
|
||||||
import { getRouters } from '@/api/router';
|
import { getRouters } from '@/api/router';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
import BasicLayout from '@/layouts/BasicLayout.vue';
|
import BasicLayout from '@/layouts/BasicLayout.vue';
|
||||||
import BlankLayout from '@/layouts/BlankLayout.vue';
|
import BlankLayout from '@/layouts/BlankLayout.vue';
|
||||||
import LinkLayout from '@/layouts/LinkLayout.vue';
|
import LinkLayout from '@/layouts/LinkLayout.vue';
|
||||||
@@ -48,19 +50,95 @@ const useRouterStore = defineStore('router', {
|
|||||||
async generateRoutes() {
|
async generateRoutes() {
|
||||||
const res = await getRouters();
|
const res = await getRouters();
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
const buildRoutes = buildRouters(res.data.concat());
|
// 获取当前网元类型
|
||||||
|
const neTypes = useNeInfoStore().getNeSelectOtions.map(v => v.value);
|
||||||
|
// 先过滤
|
||||||
|
const filteredRoutes = this.clearMenuItemByNeList(res.data.concat(), neTypes);
|
||||||
|
// 再 build
|
||||||
|
const buildRoutes = buildRouters(filteredRoutes);
|
||||||
this.buildRouterData = buildRoutes;
|
this.buildRouterData = buildRoutes;
|
||||||
return buildRoutes;
|
return buildRoutes;
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 根据网元类型过滤菜单
|
||||||
|
* @param routes 经过clearMenuItem(router.getRoutes())处理
|
||||||
|
* @param neTypes 网元类型
|
||||||
|
* @returns 过滤后的菜单
|
||||||
|
*/
|
||||||
|
clearMenuItemByNeList(
|
||||||
|
routes: RouteRecord[] | RouteRecordRaw[],
|
||||||
|
neTypes: string[]
|
||||||
|
): RouteRecordRaw[] {
|
||||||
|
return routes
|
||||||
|
.map((item: RouteRecord | RouteRecordRaw) => {
|
||||||
|
const finalItem = { ...item };
|
||||||
|
// 过滤网元类型
|
||||||
|
if (
|
||||||
|
Array.isArray(finalItem.meta?.neType) &&
|
||||||
|
finalItem.meta?.neType.length > 0
|
||||||
|
) {
|
||||||
|
let metaNeType: string[] = finalItem.meta.neType;
|
||||||
|
let match = false; // 匹配
|
||||||
|
for (const netype of metaNeType) {
|
||||||
|
if (netype.indexOf('+') > -1) {
|
||||||
|
metaNeType = netype.split('+');
|
||||||
|
match = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match && !metaNeType.every(item => neTypes.includes(item))) {
|
||||||
|
// 同时匹配
|
||||||
|
return null;
|
||||||
|
} else if (!metaNeType.some(item => neTypes.includes(item))) {
|
||||||
|
// 有一种
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 有子菜单进行递归
|
||||||
|
if (finalItem.children && finalItem.children.length > 0) {
|
||||||
|
const children = this.clearMenuItemByNeList(
|
||||||
|
finalItem.children,
|
||||||
|
neTypes
|
||||||
|
);
|
||||||
|
// 如果子菜单都被过滤掉了,就不显示
|
||||||
|
if (children.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
finalItem.children = children;
|
||||||
|
|
||||||
|
// 只重定向到第一个可用的子菜单
|
||||||
|
// finalItem.redirect = finalItem.children[0].path;
|
||||||
|
// console.log(`finalItem.redirect`, finalItem.redirect);
|
||||||
|
// 如果有子菜单,且没有重定向,则设置重定向到第一个子菜单
|
||||||
|
if (children.length > 0) {
|
||||||
|
let childPath = children[0].path;
|
||||||
|
if (!childPath.startsWith('/')) {
|
||||||
|
// 确保父路径以 / 结尾,子路径不以 / 开头
|
||||||
|
const parentPath = finalItem.path.replace(/\/$/, '');
|
||||||
|
childPath = parentPath + '/' + childPath.replace(/^\//, '');
|
||||||
|
}
|
||||||
|
finalItem.redirect = childPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete finalItem.children;
|
||||||
|
return finalItem;
|
||||||
|
})
|
||||||
|
.filter(item => item) as RouteRecordRaw[];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/**异步路由类型 */
|
/**异步路由类型 */
|
||||||
type RecordRaws = {
|
type RecordRaws = {
|
||||||
path: string;
|
path: string;
|
||||||
name: string;
|
name?: string;
|
||||||
meta: RouteMeta;
|
meta: RouteMeta;
|
||||||
redirect: RouteLocationRaw;
|
redirect: RouteLocationRaw;
|
||||||
component: string;
|
component: string;
|
||||||
@@ -75,17 +153,18 @@ type RecordRaws = {
|
|||||||
* @param recordRaws 异步路由列表
|
* @param recordRaws 异步路由列表
|
||||||
* @returns 可添加的路由列表
|
* @returns 可添加的路由列表
|
||||||
*/
|
*/
|
||||||
function buildRouters(recordRaws: RecordRaws[]): RouteRecordRaw[] {
|
function buildRouters(recordRaws: RouteRecordRaw[]): RouteRecordRaw[] {
|
||||||
const routers: RouteRecordRaw[] = [];
|
const routers: RouteRecordRaw[] = [];
|
||||||
for (const item of recordRaws) {
|
for (const item of recordRaws) {
|
||||||
// 过滤旧前端菜单 是layui的菜单跳过
|
// 过滤旧前端菜单 是layui的菜单跳过
|
||||||
if (['', '/page"'].includes(item.path)) {
|
if (['', '/page"'].includes(item.path)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 路由页面组件
|
// 路由页面组件
|
||||||
let component: RouteComponent = {};
|
let component: RouteComponent = {};
|
||||||
if (item.component) {
|
if (item.component) {
|
||||||
const comp = item.component;
|
const comp = item.component as unknown as string; // 添加类型断言
|
||||||
if (comp === MENU_COMPONENT_LAYOUT_BASIC) {
|
if (comp === MENU_COMPONENT_LAYOUT_BASIC) {
|
||||||
component = BasicLayout;
|
component = BasicLayout;
|
||||||
} else if (comp === MENU_COMPONENT_LAYOUT_BLANK) {
|
} else if (comp === MENU_COMPONENT_LAYOUT_BLANK) {
|
||||||
@@ -102,6 +181,17 @@ function buildRouters(recordRaws: RecordRaws[]): RouteRecordRaw[] {
|
|||||||
let children: RouteRecordRaw[] = [];
|
let children: RouteRecordRaw[] = [];
|
||||||
if (item.children && item.children.length > 0) {
|
if (item.children && item.children.length > 0) {
|
||||||
children = buildRouters(item.children);
|
children = buildRouters(item.children);
|
||||||
|
|
||||||
|
// 如果没有 redirect 但有子菜单,设置 redirect 到第一个子菜单
|
||||||
|
if (!item.redirect && children.length > 0) {
|
||||||
|
let childPath = children[0].path;
|
||||||
|
if (!childPath?.startsWith('/')) {
|
||||||
|
childPath = item.path.replace(/\/$/, '') + '/' + (childPath || '').replace(/^\//, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改 item 的 redirect(需要类型断言)
|
||||||
|
(item as any).redirect = childPath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对元数据特殊参数进行处理
|
// 对元数据特殊参数进行处理
|
||||||
@@ -109,7 +199,9 @@ function buildRouters(recordRaws: RecordRaws[]): RouteRecordRaw[] {
|
|||||||
if (!metaIcon.startsWith('icon-')) {
|
if (!metaIcon.startsWith('icon-')) {
|
||||||
metaIcon = '';
|
metaIcon = '';
|
||||||
}
|
}
|
||||||
item.meta = Object.assign(item.meta, {
|
|
||||||
|
// 更新 meta(需要类型断言)
|
||||||
|
(item as any).meta = Object.assign(item.meta || {}, {
|
||||||
icon: metaIcon,
|
icon: metaIcon,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Vendored
+2
@@ -9,5 +9,7 @@ declare module 'vue-router' {
|
|||||||
permissions?: string[];
|
permissions?: string[];
|
||||||
/**角色 */
|
/**角色 */
|
||||||
roles?: string[];
|
roles?: string[];
|
||||||
|
/**网元类型信息 */
|
||||||
|
neType?: string[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Vendored
+2
@@ -7,8 +7,10 @@ declare module '*.vue' {
|
|||||||
export default component;
|
export default component;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "vue3-smooth-dnd": "^0.0.6"
|
||||||
declare module 'vue3-smooth-dnd';
|
declare module 'vue3-smooth-dnd';
|
||||||
|
|
||||||
|
// "intl-tel-input": "^25.2.0"
|
||||||
declare module 'intl-tel-input/intlTelInputWithUtils' {
|
declare module 'intl-tel-input/intlTelInputWithUtils' {
|
||||||
import intlTelInput from 'intl-tel-input';
|
import intlTelInput from 'intl-tel-input';
|
||||||
export default intlTelInput;
|
export default intlTelInput;
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ export const YYYYMMDDHHMMSS = 'YYYYMMDDHHmmss';
|
|||||||
/**年-月-日 时:分:秒 列如:2022-12-30 01:01:59 */
|
/**年-月-日 时:分:秒 列如:2022-12-30 01:01:59 */
|
||||||
export const YYYY_MM_DD_HH_MM_SS = 'YYYY-MM-DD HH:mm:ss';
|
export const YYYY_MM_DD_HH_MM_SS = 'YYYY-MM-DD HH:mm:ss';
|
||||||
|
|
||||||
|
/**年-月-日 时:分:秒 列如:2022-12-30T01:01:59+08:00 */
|
||||||
|
export const YYYY_MM_DD_HH_MM_SSZ = 'YYYY-MM-DD HH:mm:ssZZ';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式时间字符串
|
* 格式时间字符串
|
||||||
* @param dateStr 时间字符串
|
* @param dateStr 时间字符串
|
||||||
@@ -36,12 +39,12 @@ export function parseStrToDate(
|
|||||||
/**
|
/**
|
||||||
* 格式时间
|
* 格式时间
|
||||||
* @param date 可转的Date对象
|
* @param date 可转的Date对象
|
||||||
* @param formatStr 时间格式 默认YYYY-MM-DD HH:mm:ss
|
* @param formatStr 时间格式 默认YYYY-MM-DD HH:mm:ssZZ
|
||||||
* @returns 时间格式字符串
|
* @returns 时间格式字符串
|
||||||
*/
|
*/
|
||||||
export function parseDateToStr(
|
export function parseDateToStr(
|
||||||
date: string | number | Date,
|
date: string | number | Date,
|
||||||
formatStr: string = YYYY_MM_DD_HH_MM_SS
|
formatStr: string = YYYY_MM_DD_HH_MM_SSZ
|
||||||
): string {
|
): string {
|
||||||
return dayjs(date).format(formatStr);
|
return dayjs(date).format(formatStr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export async function readLoalXlsx(
|
|||||||
/**
|
/**
|
||||||
* 读取表格数据 工作表
|
* 读取表格数据 工作表
|
||||||
* @param fileBolb 文件对象
|
* @param fileBolb 文件对象
|
||||||
* @param index 文件保存路径
|
* @param index SheetName索引
|
||||||
* @return 表格对象列表
|
* @return 表格对象列表
|
||||||
*/
|
*/
|
||||||
export async function readSheet(
|
export async function readSheet(
|
||||||
|
|||||||
@@ -13,13 +13,41 @@ export function generateColorHEX(): string {
|
|||||||
* @returns rgb(24 144 255) / rgba(0,0,0,.85)
|
* @returns rgb(24 144 255) / rgba(0,0,0,.85)
|
||||||
*/
|
*/
|
||||||
export function generateColorRGBA(hasAlpha: boolean = false) {
|
export function generateColorRGBA(hasAlpha: boolean = false) {
|
||||||
const red = Math.floor(Math.random() * 256);
|
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
|
||||||
const green = Math.floor(Math.random() * 256);
|
|
||||||
const blue = Math.floor(Math.random() * 256);
|
let red: number;
|
||||||
|
let green: number;
|
||||||
|
let blue: number;
|
||||||
|
|
||||||
|
if (isDark) {
|
||||||
|
// 暗色模式下生成较亮的颜色
|
||||||
|
red = Math.floor(Math.random() * 156) + 100; // 100-255
|
||||||
|
green = Math.floor(Math.random() * 156) + 100; // 100-255
|
||||||
|
blue = Math.floor(Math.random() * 156) + 100; // 100-255
|
||||||
|
|
||||||
|
// 确保至少有一个通道的值较高,使颜色更明亮
|
||||||
|
const brightChannel = Math.floor(Math.random() * 3);
|
||||||
|
switch (brightChannel) {
|
||||||
|
case 0:
|
||||||
|
red = Math.min(255, red + 50);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
green = Math.min(255, green + 50);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
blue = Math.min(255, blue + 50);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 亮色模式下生成正常的颜色
|
||||||
|
red = Math.floor(Math.random() * 256); // 0-255
|
||||||
|
green = Math.floor(Math.random() * 256); // 0-255
|
||||||
|
blue = Math.floor(Math.random() * 256); // 0-255
|
||||||
|
}
|
||||||
|
|
||||||
if (hasAlpha) {
|
if (hasAlpha) {
|
||||||
const alpha = Math.floor(Math.random() * 100);
|
const alpha = Math.floor(Math.random() * 100);
|
||||||
return `rgb(${red}, ${green}, ${blue}, 0.${alpha})`;
|
return `rgba(${red}, ${green}, ${blue}, 0.${alpha})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `rgb(${red}, ${green}, ${blue})`;
|
return `rgb(${red}, ${green}, ${blue})`;
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ export function parseDataToTreeExclude(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析树结构数据转出一维id数组
|
* 解析树结构数据转出所有一维id数组
|
||||||
*
|
*
|
||||||
* @param data 数组数据
|
* @param data 数组数据
|
||||||
* @param fieldId 读取节点字段 默认 'id'
|
* @param fieldId 读取节点字段 默认 'id'
|
||||||
@@ -158,7 +158,7 @@ export function parseTreeKeys(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析树结构数据转出含子节点的一维id数组
|
* 解析树结构数据转出根节点的一维id数组
|
||||||
*
|
*
|
||||||
* @param data 数组数据
|
* @param data 数组数据
|
||||||
* @param fieldId 读取节点字段 默认 'id'
|
* @param fieldId 读取节点字段 默认 'id'
|
||||||
@@ -221,3 +221,43 @@ export function parseDataToOptions(
|
|||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析树结构数据转出子节点关联根节点的一维id数组
|
||||||
|
*
|
||||||
|
* @param data 数组数据
|
||||||
|
* @param checkedKeys 子节点数组数据
|
||||||
|
* @param fieldId 读取节点字段 默认 'id'
|
||||||
|
* @param fieldChildren 读取子节点字段 默认 'children'
|
||||||
|
* @returns 层级数组
|
||||||
|
*/
|
||||||
|
export function parseTreeNodeKeysByChecked(
|
||||||
|
data: Record<string, any>[],
|
||||||
|
checkedKeys: (string | number)[],
|
||||||
|
fieldId: string = 'id',
|
||||||
|
fieldChildren: string = 'children'
|
||||||
|
) {
|
||||||
|
// 节点id
|
||||||
|
let treeIds: (string | number)[] = [];
|
||||||
|
componet(data);
|
||||||
|
/**闭包递归函数 */
|
||||||
|
function componet(data: Record<string, any>[]) {
|
||||||
|
if (data.length <= 0) return false;
|
||||||
|
let hasKey = false;
|
||||||
|
for (const iterator of data) {
|
||||||
|
const key = iterator[fieldId];
|
||||||
|
if (checkedKeys.includes(key)) {
|
||||||
|
hasKey = true;
|
||||||
|
}
|
||||||
|
let nodes = iterator[fieldChildren];
|
||||||
|
if (Array.isArray(nodes) && nodes.length > 0) {
|
||||||
|
if (componet(nodes)) {
|
||||||
|
treeIds.push(key);
|
||||||
|
hasKey = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasKey;
|
||||||
|
}
|
||||||
|
return treeIds;
|
||||||
|
}
|
||||||
@@ -171,8 +171,8 @@ export function parseSizeFromKbs(sizeByte: number, timeInterval: number): any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字节数转换单位
|
* 位数据转换单位
|
||||||
* @param bits 字节Bit大小 64009540 = 512.08 MB
|
* @param bits 位Bit大小 64009540 = 512.08 MB
|
||||||
* @returns xx B / KB / MB / GB / TB / PB / EB / ZB / YB
|
* @returns xx B / KB / MB / GB / TB / PB / EB / ZB / YB
|
||||||
*/
|
*/
|
||||||
export function parseSizeFromBits(bits: number | string): string {
|
export function parseSizeFromBits(bits: number | string): string {
|
||||||
@@ -181,7 +181,42 @@ export function parseSizeFromBits(bits: number | string): string {
|
|||||||
bits = bits * 8;
|
bits = bits * 8;
|
||||||
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||||
const unitIndex = Math.floor(Math.log2(bits) / 10);
|
const unitIndex = Math.floor(Math.log2(bits) / 10);
|
||||||
const value = (bits / Math.pow(1000, unitIndex)).toFixed(2);
|
const value = bits / Math.pow(1000, unitIndex);
|
||||||
const unti = units[unitIndex];
|
const unti = units[unitIndex];
|
||||||
|
if (unitIndex > 0) {
|
||||||
|
return `${value.toFixed(2)} ${unti}`;
|
||||||
|
}
|
||||||
return `${value} ${unti}`;
|
return `${value} ${unti}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字节数转换单位
|
||||||
|
* @param byte 字节Byte大小 64009540 = 512.08 MB
|
||||||
|
* @param unit 指定单位 B / KB / MB / GB / TB / PB / EB / ZB / YB
|
||||||
|
* @returns xx B / KB / MB / GB / TB / PB / EB / ZB / YB
|
||||||
|
*/
|
||||||
|
export function parseSizeFromByte(
|
||||||
|
byte: number | string,
|
||||||
|
unit?: string
|
||||||
|
): string {
|
||||||
|
byte = Number(byte) || 0;
|
||||||
|
if (byte <= 0) return '0 B';
|
||||||
|
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||||
|
let unitIndex = 0;
|
||||||
|
let unitStr = 'B';
|
||||||
|
if (unit) {
|
||||||
|
const index = units.indexOf(unit);
|
||||||
|
if (index > -1) {
|
||||||
|
unitIndex = index;
|
||||||
|
unitStr = unit;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unitIndex = Math.floor(Math.log2(byte) / 10);
|
||||||
|
unitStr = units[unitIndex];
|
||||||
|
}
|
||||||
|
const value = byte / Math.pow(1000, unitIndex);
|
||||||
|
if (unitIndex > 0) {
|
||||||
|
return `${value.toFixed(2)} ${unitStr}`;
|
||||||
|
}
|
||||||
|
return `${value} ${unitStr}`;
|
||||||
|
}
|
||||||
@@ -0,0 +1,647 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, onMounted, toRaw } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
|
import { parseDateToStr, parseStrToDate } from '@/utils/date-utils';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { listCallBack, updateStatus } from '@/api/agentManage/callback';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import useDictStore from '@/store/modules/dict';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
import { ProModal } from 'antdv-pro-modal';
|
||||||
|
import { create } from 'domain';
|
||||||
|
import { commentProps } from 'ant-design-vue/es/comment';
|
||||||
|
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
let dictStatus = ref<DictType[]>([]);
|
||||||
|
|
||||||
|
/**网元参数 */
|
||||||
|
let neOtions = ref<Record<string, any>[]>([]);
|
||||||
|
/**开始结束时间 */
|
||||||
|
let queryRangePicker = ref<[string, string]>(['', '']);
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元类型 */
|
||||||
|
neId: '001',
|
||||||
|
/**记录时间 */
|
||||||
|
startTime: '',
|
||||||
|
endTime: '',
|
||||||
|
callerNumber: '',
|
||||||
|
agentName: '',
|
||||||
|
status: undefined,
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
queryParams = Object.assign(queryParams, {
|
||||||
|
neId: '001',
|
||||||
|
status: undefined,
|
||||||
|
beginTime: '',
|
||||||
|
endTime: '',
|
||||||
|
callerNumber: '',
|
||||||
|
agentName: '',
|
||||||
|
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.agentManage.callback.ticketId'),
|
||||||
|
dataIndex: 'ticketId',
|
||||||
|
align: 'center',
|
||||||
|
width: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.agentManage.callback.agentName'),
|
||||||
|
dataIndex: 'agentName',
|
||||||
|
align: 'center',
|
||||||
|
width: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.agentManage.callback.callerIdNumber'),
|
||||||
|
dataIndex: 'callerNumber',
|
||||||
|
align: 'center',
|
||||||
|
width: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.agentManage.callback.calleeIdNumber'),
|
||||||
|
dataIndex: 'calleeNumber',
|
||||||
|
align: 'center',
|
||||||
|
width: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.agentManage.callback.status'),
|
||||||
|
dataIndex: 'status',
|
||||||
|
key: 'status',
|
||||||
|
align: 'center',
|
||||||
|
width: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.agentManage.callback.startTime'),
|
||||||
|
dataIndex: 'createdAt',
|
||||||
|
align: 'center',
|
||||||
|
width: 6,
|
||||||
|
customRender(opt) {
|
||||||
|
if (!opt.value) return '';
|
||||||
|
return parseDateToStr(opt.value / 1000);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.agentManage.callback.updateTime'),
|
||||||
|
dataIndex: 'updatedAt',
|
||||||
|
align: 'center',
|
||||||
|
width: 6,
|
||||||
|
customRender(opt) {
|
||||||
|
if (!opt.value) return '';
|
||||||
|
return parseDateToStr(opt.value / 1000);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: t('views.agentManage.callback.msdData'),
|
||||||
|
// dataIndex: 'msdData',
|
||||||
|
// align: 'center',
|
||||||
|
// width: 6,
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
title: t('common.operate'),
|
||||||
|
key: 'callbackId',
|
||||||
|
align: 'left',
|
||||||
|
width: 6,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
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.startTime = queryRangePicker.value[0];
|
||||||
|
queryParams.endTime = queryRangePicker.value[1];
|
||||||
|
|
||||||
|
listCallBack(toRaw(queryParams)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
tablePagination.total = res.total;
|
||||||
|
tableState.data = res.data;
|
||||||
|
if (
|
||||||
|
tablePagination.total <=
|
||||||
|
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||||
|
queryParams.pageNum !== 1
|
||||||
|
) {
|
||||||
|
tableState.loading = false;
|
||||||
|
fnGetList(queryParams.pageNum - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**详情框是否显示 */
|
||||||
|
openByView: boolean;
|
||||||
|
/**新增框或修改框是否显示 */
|
||||||
|
openByEdit: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**表单数据 */
|
||||||
|
from: Record<string, any>;
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
/**更新加载数据按钮 loading */
|
||||||
|
loadDataLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
openByView: false,
|
||||||
|
openByEdit: false,
|
||||||
|
title: 'callback',
|
||||||
|
from: {
|
||||||
|
id: undefined,
|
||||||
|
ticketId: '',
|
||||||
|
agentName: '',
|
||||||
|
callerNumber: '',
|
||||||
|
calleeNumber: '',
|
||||||
|
agentEmail: '',
|
||||||
|
agentMobile: '',
|
||||||
|
status: '',
|
||||||
|
createdAt: '',
|
||||||
|
comment: '',
|
||||||
|
},
|
||||||
|
confirmLoading: false,
|
||||||
|
loadDataLoading: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
function fnModalVisibleByEdit(record: any) {
|
||||||
|
modalState.title = t('common.viewText') + t('views.agentManage.callback.title');
|
||||||
|
modalState.openByEdit = true;
|
||||||
|
modalState.from = Object.assign(modalState.from, record, {
|
||||||
|
createdAt: record.createdAt ? parseDateToStr(record.createdAt / 1000) : '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fnModalOk() {
|
||||||
|
const from = Object.assign({}, toRaw(modalState.from));
|
||||||
|
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
updateStatus(from)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.msgSuccess', { msg: modalState.title }),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnGetList(1);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
fnModalCancel();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fnModalCancel() {
|
||||||
|
modalState.openByEdit = false;
|
||||||
|
//modalState.openByView = false;
|
||||||
|
//modalStateFrom.resetFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getDict('callback_status').then(res => {
|
||||||
|
dictStatus.value = res;
|
||||||
|
});
|
||||||
|
// 获取网元网元列表
|
||||||
|
useNeInfoStore()
|
||||||
|
.fnNelist()
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length > 0) {
|
||||||
|
let arr: Record<string, any>[] = [];
|
||||||
|
res.data.forEach(i => {
|
||||||
|
if (i.neType === 'MF') {
|
||||||
|
arr.push({ value: i.neId, label: i.neName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neOtions.value = arr;
|
||||||
|
if (arr.length > 0) {
|
||||||
|
queryParams.neId = arr[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// 获取列表数据
|
||||||
|
fnGetList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</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="PSAP对象" name="neId ">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.neId"
|
||||||
|
:options="neOtions"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnGetList(1)"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.agentManage.callback.callerIdNumber')"
|
||||||
|
name="callerNumber"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.callerNumber"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.agentManage.callback.agentName')"
|
||||||
|
name="agentName"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.agentName"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</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-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.agentManage.callback.status')"
|
||||||
|
name="status"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.status"
|
||||||
|
:options="dictStatus"
|
||||||
|
:allow-clear="true"
|
||||||
|
>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.agentManage.callback.startTime')"
|
||||||
|
name="queryRangePicker"
|
||||||
|
>
|
||||||
|
<a-range-picker
|
||||||
|
v-model:value="queryRangePicker"
|
||||||
|
allow-clear
|
||||||
|
bordered
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
value-format="x"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-range-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title> </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-tooltip>
|
||||||
|
<template #title>{{ t('common.sizeText') }}</template>
|
||||||
|
<a-dropdown trigger="click" placement="bottomRight">
|
||||||
|
<a-button type="text">
|
||||||
|
<template #icon>
|
||||||
|
<ColumnHeightOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu
|
||||||
|
:selected-keys="[tableState.size as string]"
|
||||||
|
@click="fnTableSize"
|
||||||
|
>
|
||||||
|
<a-menu-item key="default">
|
||||||
|
{{ t('common.size.default') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="middle">
|
||||||
|
{{ t('common.size.middle') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="small">
|
||||||
|
{{ t('common.size.small') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="id"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: 1500, y: 400 }"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
|
||||||
|
<template v-if="column.key === 'status'">
|
||||||
|
<DictTag :options="dictStatus" :value="record.status" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-if="column.key === 'callbackId'">
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.viewText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnModalVisibleByEdit(record)"
|
||||||
|
>
|
||||||
|
<template #icon><InfoCircleOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<ProModal
|
||||||
|
:drag="true"
|
||||||
|
:width="800"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
style="top: 0px"
|
||||||
|
:body-style="{ maxHeight: '600px', 'overflow-y': 'auto' }"
|
||||||
|
:keyboard="false"
|
||||||
|
:mask-closable="false"
|
||||||
|
:open="modalState.openByEdit"
|
||||||
|
:title="modalState.title"
|
||||||
|
:confirm-loading="modalState.confirmLoading"
|
||||||
|
@cancel="fnModalCancel"
|
||||||
|
@ok="fnModalOk"
|
||||||
|
:okText=" t('views.faultManage.activeAlarm.confirm') "
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
name="modalStateFrom"
|
||||||
|
layout="horizontal"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:labelWrap="true"
|
||||||
|
>
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.agentManage.callback.ticketId')"
|
||||||
|
name="ticketId"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
:disabled="true"
|
||||||
|
v-model:value="modalState.from.ticketId"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.agentManage.callback.agentName')"
|
||||||
|
name="agentName"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
:disabled="true"
|
||||||
|
v-model:value="modalState.from.agentName"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.agentManage.callback.callerIdNumber')"
|
||||||
|
name="callerNumber"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
:disabled="true"
|
||||||
|
v-model:value="modalState.from.callerNumber"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.agentManage.callback.calleeIdNumber')"
|
||||||
|
name="calleeNumber"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
:disabled="true"
|
||||||
|
v-model:value="modalState.from.calleeNumber"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.agentManage.callback.agentEmail')"
|
||||||
|
name="agentEmail"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
:disabled="true"
|
||||||
|
v-model:value="modalState.from.agentEmail"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.agentManage.callback.agentMobile')"
|
||||||
|
name="agentMobile"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
:disabled="true"
|
||||||
|
v-model:value="modalState.from.agentMobile"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.agentManage.callback.status')"
|
||||||
|
name="status"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
:disabled="true"
|
||||||
|
v-model:value="modalState.from.status"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.agentManage.callback.startTime')"
|
||||||
|
name="createdAt"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
:disabled="true"
|
||||||
|
v-model:value="modalState.from.createdAt"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.agentManage.callback.comment')"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
name="comment"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
:disabled="true"
|
||||||
|
v-model:value="modalState.from.comment"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</ProModal>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alarmTitleText {
|
||||||
|
max-width: 300px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, onMounted, toRaw } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { listCallings } from '@/api/agentManage/callings';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import useDictStore from '@/store/modules/dict';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元类型 */
|
||||||
|
neId: '001',
|
||||||
|
/**记录时间 */
|
||||||
|
beginTime: '',
|
||||||
|
endTime: '',
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
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.agentManage.callings.callerIdNumber'),
|
||||||
|
dataIndex: 'callerIdNumber',
|
||||||
|
align: 'center',
|
||||||
|
width: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.agentManage.callings.calleeIdNumber'),
|
||||||
|
dataIndex: 'calleeIdNumber',
|
||||||
|
align: 'center',
|
||||||
|
width: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.agentManage.callings.startTime'),
|
||||||
|
dataIndex: 'createdTime',
|
||||||
|
align: 'center',
|
||||||
|
width: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.agentManage.callings.answeredTime'),
|
||||||
|
dataIndex: 'answeredTime',
|
||||||
|
align: 'center',
|
||||||
|
width: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.agentManage.callings.callDuration'),
|
||||||
|
dataIndex: 'callDuration',
|
||||||
|
align: 'center',
|
||||||
|
width: 5,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
listCallings(toRaw(queryParams)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
tablePagination.total = res.total;
|
||||||
|
tableState.data = res.data;
|
||||||
|
if (tablePagination.total <=(queryParams.pageNum - 1) * tablePagination.pageSize &&queryParams.pageNum !== 1) {
|
||||||
|
tableState.loading = false;
|
||||||
|
fnGetList(queryParams.pageNum - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 获取列表数据
|
||||||
|
fnGetList();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
|
||||||
|
|
||||||
|
<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.reloadText') }}</template>
|
||||||
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.sizeText') }}</template>
|
||||||
|
<a-dropdown trigger="click" placement="bottomRight">
|
||||||
|
<a-button type="text">
|
||||||
|
<template #icon><ColumnHeightOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu
|
||||||
|
:selected-keys="[tableState.size as string]"
|
||||||
|
@click="fnTableSize"
|
||||||
|
>
|
||||||
|
<a-menu-item key="default">
|
||||||
|
{{ t('common.size.default') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="middle">
|
||||||
|
{{ t('common.size.middle') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="small">
|
||||||
|
{{ t('common.size.small') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="id"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: 1500, y: 400 }"
|
||||||
|
>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
.alarmTitleText {
|
||||||
|
max-width: 300px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -2,7 +2,14 @@
|
|||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { ColumnsType } from 'ant-design-vue/es/table';
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
import { message } from 'ant-design-vue/es';
|
import { message } from 'ant-design-vue/es';
|
||||||
import { reactive, ref, onMounted, onBeforeUnmount, markRaw } from 'vue';
|
import {
|
||||||
|
reactive,
|
||||||
|
ref,
|
||||||
|
onMounted,
|
||||||
|
onBeforeUnmount,
|
||||||
|
markRaw,
|
||||||
|
useTemplateRef,
|
||||||
|
} from 'vue';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import { TooltipComponent } from 'echarts/components';
|
import { TooltipComponent } from 'echarts/components';
|
||||||
import { GaugeChart } from 'echarts/charts';
|
import { GaugeChart } from 'echarts/charts';
|
||||||
@@ -32,24 +39,12 @@ echarts.use([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
/**图DOM节点实例对象 */
|
/**图DOM节点实例对象 */
|
||||||
const statusBar = ref<HTMLElement | undefined>(undefined);
|
const statusBar = useTemplateRef<HTMLDivElement>('statusBar');
|
||||||
|
|
||||||
/**图实例对象 */
|
/**图实例对象 */
|
||||||
const statusBarChart = ref<any>(null);
|
const statusBarChart = ref<any>(null);
|
||||||
|
|
||||||
/**网元状态字典数据 */
|
|
||||||
let indexColor = ref<DictType[]>([
|
|
||||||
{ label: 'Normal', value: 'normal', tagType: '', tagClass: '#91cc75' },
|
|
||||||
{
|
|
||||||
label: 'Abnormal',
|
|
||||||
value: 'abnormal',
|
|
||||||
tagType: '',
|
|
||||||
tagClass: '#ee6666',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
/**表格字段列 */
|
/**表格字段列 */
|
||||||
//customRender(){} ----单元格处理
|
|
||||||
let tableColumns: ColumnsType = [
|
let tableColumns: ColumnsType = [
|
||||||
{
|
{
|
||||||
title: t('views.index.object'),
|
title: t('views.index.object'),
|
||||||
@@ -67,7 +62,9 @@ let tableColumns: ColumnsType = [
|
|||||||
dataIndex: 'serverState',
|
dataIndex: 'serverState',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
if (opt.value?.refreshTime) return parseDateToStr(opt.value?.refreshTime);
|
if (opt.value?.refreshTime) {
|
||||||
|
return parseDateToStr(opt.value?.refreshTime, 'HH:mm:ss');
|
||||||
|
}
|
||||||
return '-';
|
return '-';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -104,12 +101,13 @@ let tableColumns: ColumnsType = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
/**表格状态类型 */
|
/**表格状态类型 */
|
||||||
type TabeStateType = {
|
type TabeStateType = {
|
||||||
/**加载等待 */
|
/**加载等待 */
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
/**记录数据 */
|
/**记录数据 */
|
||||||
data: object[];
|
data: Record<string, any>[];
|
||||||
/**勾选记录 */
|
/**勾选记录 */
|
||||||
selectedRowKeys: (string | number)[];
|
selectedRowKeys: (string | number)[];
|
||||||
};
|
};
|
||||||
@@ -121,66 +119,63 @@ let tableState: TabeStateType = reactive({
|
|||||||
selectedRowKeys: [],
|
selectedRowKeys: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
/**表格状态 */
|
/**状态 */
|
||||||
let nfInfo: any = reactive({
|
let serverState: any = ref({});
|
||||||
obj: 'OMC',
|
|
||||||
version: appStore.version,
|
|
||||||
status: t('views.index.normal'),
|
|
||||||
outTimeDate: '',
|
|
||||||
serialNum: appStore.serialNum,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**表格状态类型 */
|
|
||||||
type nfStateType = {
|
|
||||||
/**主机名 */
|
|
||||||
hostName: string;
|
|
||||||
/**操作系统信息 */
|
|
||||||
osInfo: string;
|
|
||||||
/**IP地址 */
|
|
||||||
ipAddress: string;
|
|
||||||
/**版本 */
|
|
||||||
version: string;
|
|
||||||
/**CPU利用率 */
|
|
||||||
cpuUse: string;
|
|
||||||
/**内存使用 */
|
|
||||||
memoryUse: string;
|
|
||||||
/**用户容量 */
|
|
||||||
capability: number;
|
|
||||||
/**序列号 */
|
|
||||||
serialNum: string;
|
|
||||||
/**许可证到期日期 */
|
|
||||||
/* selectedRowKeys: (string | number)[];*/
|
|
||||||
expiryDate: string;
|
|
||||||
};
|
|
||||||
/**网元详细信息 */
|
|
||||||
let pronInfo: nfStateType = reactive({
|
|
||||||
hostName: '5gc',
|
|
||||||
osInfo: 'Linux 5gc 4.15.0-112-generic 2020 x86_64 GNU/Linux',
|
|
||||||
ipAddress: '-',
|
|
||||||
version: '-',
|
|
||||||
cpuUse: '-',
|
|
||||||
memoryUse: '-',
|
|
||||||
capability: 0,
|
|
||||||
serialNum: '-',
|
|
||||||
expiryDate: '-',
|
|
||||||
});
|
|
||||||
|
|
||||||
/**查询网元状态列表 */
|
/**查询网元状态列表 */
|
||||||
function fnGetList(one: boolean) {
|
async function fnGetList(reload: boolean = false) {
|
||||||
if (tableState.loading) return;
|
tableState.loading = !reload;
|
||||||
one && (tableState.loading = true);
|
try {
|
||||||
listAllNeInfo({ bandStatus: true }).then(res => {
|
const res = await listAllNeInfo({ bandStatus: true });
|
||||||
tableState.data = res.data;
|
tableState.data = res.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
tableState.data = [];
|
||||||
|
}
|
||||||
tableState.loading = false;
|
tableState.loading = false;
|
||||||
|
if (tableState.data.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var rightNum = 0;
|
var rightNum = 0;
|
||||||
var errorNum = 0;
|
var errorNum = 0;
|
||||||
res.data.forEach((item: any) => {
|
for (const v of tableState.data) {
|
||||||
if (item.serverState.online) {
|
if (v?.serverState?.online) {
|
||||||
rightNum++;
|
rightNum++;
|
||||||
} else {
|
} else {
|
||||||
errorNum++;
|
errorNum++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始
|
||||||
|
if (!reload) {
|
||||||
|
// 选择第一个
|
||||||
|
if (tableState.data.length > 0) {
|
||||||
|
const id = tableState.data[0].id;
|
||||||
|
fnTableSelectedRowKeys([id]);
|
||||||
|
} else {
|
||||||
|
fnTableSelectedRowKeys(tableState.selectedRowKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusBar.value) {
|
||||||
|
fnDesign(statusBar.value, rightNum, errorNum);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
statusBarChart.value.setOption({
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: [
|
||||||
|
{ value: rightNum, name: t('views.index.normal') },
|
||||||
|
{ value: errorNum, name: t('views.index.abnormal') },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fnDesign(container: HTMLElement, rightNum: number, errorNum: number) {
|
||||||
|
/// 图表数据
|
||||||
const optionData: any = {
|
const optionData: any = {
|
||||||
title: {
|
title: {
|
||||||
text: '',
|
text: '',
|
||||||
@@ -194,10 +189,10 @@ function fnGetList(one: boolean) {
|
|||||||
orient: 'vertical',
|
orient: 'vertical',
|
||||||
left: 'left',
|
left: 'left',
|
||||||
},
|
},
|
||||||
color: indexColor.value.map(item => item.tagClass),
|
color: dict.indexStatus.map(item => item.tagClass),
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: t('views.index.realNeStatus'),
|
name: t('views.index.runStatus'),
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: '70%',
|
radius: '70%',
|
||||||
center: ['50%', '50%'],
|
center: ['50%', '50%'],
|
||||||
@@ -205,30 +200,11 @@ function fnGetList(one: boolean) {
|
|||||||
{ value: rightNum, name: t('views.index.normal') },
|
{ value: rightNum, name: t('views.index.normal') },
|
||||||
{ value: errorNum, name: t('views.index.abnormal') },
|
{ value: errorNum, name: t('views.index.abnormal') },
|
||||||
],
|
],
|
||||||
emphasis: {
|
|
||||||
itemStyle: {
|
|
||||||
shadowBlur: 10,
|
|
||||||
shadowOffsetX: 0,
|
|
||||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
label: {},
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
fnDesign(statusBar.value, optionData);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function fnDesign(container: HTMLElement | undefined, option: any) {
|
|
||||||
if (!container) return;
|
|
||||||
|
|
||||||
if (!statusBarChart.value) {
|
|
||||||
statusBarChart.value = markRaw(echarts.init(container, 'light'));
|
statusBarChart.value = markRaw(echarts.init(container, 'light'));
|
||||||
}
|
statusBarChart.value.setOption(optionData);
|
||||||
option && statusBarChart.value.setOption(option);
|
|
||||||
|
|
||||||
// 创建 ResizeObserver 实例
|
// 创建 ResizeObserver 实例
|
||||||
var observer = new ResizeObserver(entries => {
|
var observer = new ResizeObserver(entries => {
|
||||||
@@ -240,66 +216,43 @@ function fnDesign(container: HTMLElement | undefined, option: any) {
|
|||||||
observer.observe(container);
|
observer.observe(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**抽屉 网元详细信息 */
|
/**表格多选 */
|
||||||
const open = ref(false);
|
function fnTableSelectedRowKeys(keys: (string | number)[]) {
|
||||||
const closeDrawer = () => {
|
if (keys.length <= 0) return;
|
||||||
open.value = false;
|
const id = keys[0];
|
||||||
};
|
const row: any = tableState.data.find((item: any) => item.id === id);
|
||||||
/**抽屉 网元详细信息 */
|
if (!row) {
|
||||||
|
|
||||||
/**监听表格行事件*/
|
|
||||||
function rowClick(record: any, index: any) {
|
|
||||||
return {
|
|
||||||
onClick: (event: any) => {
|
|
||||||
let pronData = JSON.parse(JSON.stringify(record.serverState));
|
|
||||||
if (!pronData.online) {
|
|
||||||
message.error(t('views.index.neStatus'), 2);
|
message.error(t('views.index.neStatus'), 2);
|
||||||
return false;
|
return;
|
||||||
} else {
|
}
|
||||||
const totalMemInKB = pronData.mem?.totalMem;
|
const neState = row.serverState;
|
||||||
const nfUsedMemInKB = pronData.mem?.nfUsedMem;
|
if (!neState?.online) {
|
||||||
const sysMemUsageInKB = pronData.mem?.sysMemUsage;
|
message.error(t('views.index.neStatus'), 2);
|
||||||
|
return;
|
||||||
// 将KB转换为MB
|
}
|
||||||
|
tableState.selectedRowKeys = keys;
|
||||||
|
// Mem 将KB转换为MB
|
||||||
|
const totalMemInKB = neState.mem?.totalMem;
|
||||||
|
const nfUsedMemInKB = neState.mem?.nfUsedMem;
|
||||||
|
const sysMemUsageInKB = neState.mem?.sysMemUsage;
|
||||||
const totalMemInMB = Math.round((totalMemInKB / 1024) * 100) / 100;
|
const totalMemInMB = Math.round((totalMemInKB / 1024) * 100) / 100;
|
||||||
const nfUsedMemInMB = Math.round((nfUsedMemInKB / 1024) * 100) / 100;
|
const nfUsedMemInMB = Math.round((nfUsedMemInKB / 1024) * 100) / 100;
|
||||||
const sysMemUsageInMB =
|
const sysMemUsageInMB = Math.round((sysMemUsageInKB / 1024) * 100) / 100;
|
||||||
Math.round((sysMemUsageInKB / 1024) * 100) / 100;
|
|
||||||
|
|
||||||
//渲染详细信息
|
// CPU
|
||||||
pronInfo = {
|
const nfCpu = neState.cpu?.nfCpuUsage;
|
||||||
hostName: pronData.hostname,
|
const sysCpu = neState.cpu?.sysCpuUsage;
|
||||||
osInfo: pronData.os,
|
const nfCpuP = Math.round(nfCpu) / 100;
|
||||||
ipAddress: pronData.neIP,
|
const sysCpuP = Math.round(sysCpu) / 100;
|
||||||
version: pronData.version,
|
|
||||||
cpuUse:
|
serverState.value = Object.assign(
|
||||||
pronData.neName +
|
{
|
||||||
':' +
|
cpuUse: `NE:${nfCpuP}%; SYS:${sysCpuP}%`,
|
||||||
pronData.cpu?.nfCpuUsage / 100 +
|
memoryUse: `Total: ${totalMemInMB}MB; NE: ${nfUsedMemInMB}MB; SYS: ${sysMemUsageInMB}MB`,
|
||||||
'%; ' +
|
|
||||||
'SYS:' +
|
|
||||||
pronData.cpu?.sysCpuUsage / 100 +
|
|
||||||
'%',
|
|
||||||
memoryUse:
|
|
||||||
'Total:' +
|
|
||||||
totalMemInMB +
|
|
||||||
'MB; ' +
|
|
||||||
pronData.name +
|
|
||||||
':' +
|
|
||||||
nfUsedMemInMB +
|
|
||||||
'MB; SYS:' +
|
|
||||||
sysMemUsageInMB +
|
|
||||||
'MB',
|
|
||||||
capability: pronData.capability,
|
|
||||||
serialNum: pronData.sn,
|
|
||||||
expiryDate: pronData.expire,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
open.value = true;
|
|
||||||
},
|
},
|
||||||
};
|
neState
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let timer: any;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 国际化翻译转换
|
* 国际化翻译转换
|
||||||
@@ -312,63 +265,48 @@ function fnLocale() {
|
|||||||
appStore.setTitle(title);
|
appStore.setTitle(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**字典数据 */
|
||||||
|
let dict: {
|
||||||
|
/**网元信息状态 */
|
||||||
|
neInfoStatus: DictType[];
|
||||||
|
/**主页状态 */
|
||||||
|
indexStatus: DictType[];
|
||||||
|
} = reactive({
|
||||||
|
neInfoStatus: [],
|
||||||
|
indexStatus: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
let timer: any;
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDict('index_status')
|
// 初始字典数据
|
||||||
.then(res => {
|
Promise.allSettled([getDict('ne_info_status'), getDict('index_status')])
|
||||||
if (res.length > 0) {
|
.then(resArr => {
|
||||||
indexColor.value = res;
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
dict.neInfoStatus = resArr[0].value;
|
||||||
|
}
|
||||||
|
if (resArr[1].status === 'fulfilled') {
|
||||||
|
dict.indexStatus = resArr[1].value;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(async () => {
|
||||||
fnLocale();
|
fnLocale();
|
||||||
|
await fnGetList(false);
|
||||||
|
timer = setInterval(() => {
|
||||||
|
if (!timer) return;
|
||||||
fnGetList(true);
|
fnGetList(true);
|
||||||
timer = setInterval(() => fnGetList(false), 10000); // 每隔10秒执行一次
|
}, 10_000); // 每隔10秒执行一次
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 在组件卸载之前清除定时器
|
// 在组件卸载之前清除定时器
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
clearInterval(timer);
|
clearInterval(timer);
|
||||||
|
timer = null;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PageContainer :breadcrumb="{}">
|
<PageContainer :breadcrumb="{}">
|
||||||
<div>
|
|
||||||
<a-drawer :open="open" @close="closeDrawer" :width="700">
|
|
||||||
<a-descriptions bordered :column="1" :label-style="{ width: '160px' }">
|
|
||||||
<a-descriptions-item :label="t('views.index.hostName')">{{
|
|
||||||
pronInfo.hostName
|
|
||||||
}}</a-descriptions-item>
|
|
||||||
<a-descriptions-item :label="t('views.index.osInfo')">{{
|
|
||||||
pronInfo.osInfo
|
|
||||||
}}</a-descriptions-item>
|
|
||||||
<a-descriptions-item :label="t('views.index.ipAddress')">{{
|
|
||||||
pronInfo.ipAddress
|
|
||||||
}}</a-descriptions-item>
|
|
||||||
<a-descriptions-item :label="t('views.index.version')">{{
|
|
||||||
pronInfo.version
|
|
||||||
}}</a-descriptions-item>
|
|
||||||
<a-descriptions-item :label="t('views.index.capability')">{{
|
|
||||||
pronInfo.capability
|
|
||||||
}}</a-descriptions-item>
|
|
||||||
<a-descriptions-item :label="t('views.index.cpuUse')">{{
|
|
||||||
pronInfo.cpuUse
|
|
||||||
}}</a-descriptions-item>
|
|
||||||
<a-descriptions-item :label="t('views.index.memoryUse')">{{
|
|
||||||
pronInfo.memoryUse
|
|
||||||
}}</a-descriptions-item>
|
|
||||||
<a-descriptions-item :label="t('views.index.serialNum')">{{
|
|
||||||
pronInfo.serialNum
|
|
||||||
}}</a-descriptions-item>
|
|
||||||
<a-descriptions-item :label="t('views.index.expiryDate')">{{
|
|
||||||
pronInfo.expiryDate
|
|
||||||
}}</a-descriptions-item>
|
|
||||||
</a-descriptions>
|
|
||||||
</a-drawer>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="14" :md="16" :xs="24">
|
<a-col :lg="14" :md="16" :xs="24">
|
||||||
<!-- 表格列表 -->
|
<!-- 表格列表 -->
|
||||||
@@ -377,20 +315,20 @@ onBeforeUnmount(() => {
|
|||||||
row-key="id"
|
row-key="id"
|
||||||
size="small"
|
size="small"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
:loading="tableState.loading"
|
|
||||||
:data-source="tableState.data"
|
:data-source="tableState.data"
|
||||||
|
:loading="tableState.loading"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:scroll="{ x: true }"
|
:scroll="{ x: true }"
|
||||||
:customRow="rowClick"
|
:row-selection="{
|
||||||
|
type: 'radio',
|
||||||
|
columnWidth: '48px',
|
||||||
|
selectedRowKeys: tableState.selectedRowKeys,
|
||||||
|
onChange: fnTableSelectedRowKeys,
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'status'">
|
<template v-if="column.key === 'status'">
|
||||||
<div v-if="record.serverState.online">
|
<DictTag :options="dict.neInfoStatus" :value="record.status" />
|
||||||
<a-tag color="blue">{{ t('views.index.normal') }}</a-tag>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<a-tag color="pink">{{ t('views.index.abnormal') }}</a-tag>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
@@ -404,7 +342,8 @@ onBeforeUnmount(() => {
|
|||||||
<div style="width: 100%; min-height: 200px" ref="statusBar"></div>
|
<div style="width: 100%; min-height: 200px" ref="statusBar"></div>
|
||||||
</a-card>
|
</a-card>
|
||||||
<a-card
|
<a-card
|
||||||
:title="t('views.index.mark')"
|
:loading="tableState.loading"
|
||||||
|
:title="`${t('views.index.mark')} - ${serverState.neName || 'OMC'}`"
|
||||||
style="margin-top: 16px"
|
style="margin-top: 16px"
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
@@ -413,25 +352,33 @@ onBeforeUnmount(() => {
|
|||||||
:column="1"
|
:column="1"
|
||||||
:label-style="{ width: '160px' }"
|
:label-style="{ width: '160px' }"
|
||||||
>
|
>
|
||||||
<a-descriptions-item :label="t('views.index.object')">{{
|
<a-descriptions-item :label="t('views.index.hostName')">
|
||||||
nfInfo.obj
|
{{ serverState.hostname }}
|
||||||
}}</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<template v-if="nfInfo.obj === 'OMC'">
|
<a-descriptions-item :label="t('views.index.osInfo')">
|
||||||
<a-descriptions-item :label="t('views.index.versionNum')">{{
|
{{ serverState.os }}
|
||||||
nfInfo.version
|
</a-descriptions-item>
|
||||||
}}</a-descriptions-item>
|
<a-descriptions-item :label="t('views.index.ipAddress')">
|
||||||
<a-descriptions-item :label="t('views.index.systemStatus')">{{
|
{{ serverState.neIP }}
|
||||||
nfInfo.status
|
</a-descriptions-item>
|
||||||
}}</a-descriptions-item>
|
<a-descriptions-item :label="t('views.index.version')">
|
||||||
</template>
|
{{ serverState.version }}
|
||||||
<template v-else>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item :label="t('views.index.serialNum')">{{
|
<a-descriptions-item :label="t('views.index.capability')">
|
||||||
nfInfo.serialNum
|
{{ serverState.capability }}
|
||||||
}}</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item :label="t('views.index.expiryDate')">{{
|
<a-descriptions-item :label="t('views.index.cpuUse')">
|
||||||
nfInfo.outTimeDate
|
{{ serverState.cpuUse }}
|
||||||
}}</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
</template>
|
<a-descriptions-item :label="t('views.index.memoryUse')">
|
||||||
|
{{ serverState.memoryUse }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item :label="t('views.index.serialNum')">
|
||||||
|
{{ serverState.sn }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item :label="t('views.index.expiryDate')">
|
||||||
|
{{ serverState.expire }}
|
||||||
|
</a-descriptions-item>
|
||||||
</a-descriptions>
|
</a-descriptions>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
|||||||
@@ -11,14 +11,21 @@ import {
|
|||||||
RESULT_CODE_SUCCESS,
|
RESULT_CODE_SUCCESS,
|
||||||
} from '@/constants/result-constants';
|
} from '@/constants/result-constants';
|
||||||
import useDictStore from '@/store/modules/dict';
|
import useDictStore from '@/store/modules/dict';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
import { listAMFDataUE, delAMFDataUE, exportAMFDataUE } from '@/api/neData/amf';
|
import { listAMFDataUE, delAMFDataUE, exportAMFDataUE } from '@/api/neData/amf';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import saveAs from 'file-saver';
|
import saveAs from 'file-saver';
|
||||||
import PQueue from 'p-queue';
|
import PQueue from 'p-queue';
|
||||||
|
import { useClipboard } from '@vueuse/core';
|
||||||
|
const { copy } = useClipboard({ legacy: true });
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { getDict } = useDictStore();
|
const { getDict } = useDictStore();
|
||||||
const ws = new WS();
|
const ws = new WS();
|
||||||
const queue = new PQueue({ concurrency: 1, autoStart: true });
|
const queue = new PQueue({ concurrency: 1, autoStart: true });
|
||||||
|
/**网元可选 */
|
||||||
|
let neOtions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
/**字典数据 */
|
/**字典数据 */
|
||||||
let dict: {
|
let dict: {
|
||||||
@@ -35,7 +42,10 @@ let dict: {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**开始结束时间 */
|
/**开始结束时间 */
|
||||||
let queryRangePicker = ref<[string, string]>(['', '']);
|
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([
|
||||||
|
dayjs().startOf('hour'),
|
||||||
|
dayjs().endOf('hour'),
|
||||||
|
]);
|
||||||
|
|
||||||
/**查询参数 */
|
/**查询参数 */
|
||||||
let queryParams = reactive({
|
let queryParams = reactive({
|
||||||
@@ -47,9 +57,9 @@ let queryParams = reactive({
|
|||||||
sortField: 'timestamp',
|
sortField: 'timestamp',
|
||||||
sortOrder: 'desc',
|
sortOrder: 'desc',
|
||||||
/**开始时间 */
|
/**开始时间 */
|
||||||
startTime: '',
|
startTime: undefined as undefined | number,
|
||||||
/**结束时间 */
|
/**结束时间 */
|
||||||
endTime: '',
|
endTime: undefined as undefined | number,
|
||||||
/**当前页数 */
|
/**当前页数 */
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
/**每页条数 */
|
/**每页条数 */
|
||||||
@@ -67,7 +77,7 @@ function fnQueryReset() {
|
|||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
});
|
});
|
||||||
queryRangePicker.value = ['', ''];
|
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')];
|
||||||
tablePagination.current = 1;
|
tablePagination.current = 1;
|
||||||
tablePagination.pageSize = 20;
|
tablePagination.pageSize = 20;
|
||||||
fnGetList();
|
fnGetList();
|
||||||
@@ -141,9 +151,15 @@ let tableColumns: ColumnsType = [
|
|||||||
{
|
{
|
||||||
title: t('views.dashboard.ue.time'),
|
title: t('views.dashboard.ue.time'),
|
||||||
dataIndex: 'eventJSON',
|
dataIndex: 'eventJSON',
|
||||||
key: 'time',
|
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 150,
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const record = opt.value;
|
||||||
|
if (record?.time) {
|
||||||
|
return record.time;
|
||||||
|
}
|
||||||
|
return parseDateToStr(+record.timestamp * 1000);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('common.operate'),
|
title: t('common.operate'),
|
||||||
@@ -252,11 +268,19 @@ function fnGetList(pageNum?: number) {
|
|||||||
if (pageNum) {
|
if (pageNum) {
|
||||||
queryParams.pageNum = pageNum;
|
queryParams.pageNum = pageNum;
|
||||||
}
|
}
|
||||||
if (!queryRangePicker.value) {
|
|
||||||
queryRangePicker.value = ['', ''];
|
// 时间范围
|
||||||
|
if (
|
||||||
|
Array.isArray(queryRangePicker.value) &&
|
||||||
|
queryRangePicker.value.length > 0
|
||||||
|
) {
|
||||||
|
queryParams.startTime = queryRangePicker.value[0].valueOf();
|
||||||
|
queryParams.endTime = queryRangePicker.value[1].valueOf();
|
||||||
|
} else {
|
||||||
|
queryParams.startTime = undefined;
|
||||||
|
queryParams.endTime = undefined;
|
||||||
}
|
}
|
||||||
queryParams.startTime = queryRangePicker.value[0];
|
|
||||||
queryParams.endTime = queryRangePicker.value[1];
|
|
||||||
listAMFDataUE(toRaw(queryParams)).then(res => {
|
listAMFDataUE(toRaw(queryParams)).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
// 取消勾选
|
// 取消勾选
|
||||||
@@ -324,6 +348,18 @@ function fnExportList() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制CDR
|
||||||
|
* @param jsonStr JSON字符串
|
||||||
|
*/
|
||||||
|
function fnRecordCopy(jsonStr: string) {
|
||||||
|
if (!jsonStr) return;
|
||||||
|
const text = JSON.stringify(jsonStr, null, 2);
|
||||||
|
copy(text).then(() => {
|
||||||
|
message.success(t('common.copyOk'), 3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**实时数据开关 */
|
/**实时数据开关 */
|
||||||
const realTimeData = ref<boolean>(false);
|
const realTimeData = ref<boolean>(false);
|
||||||
|
|
||||||
@@ -333,31 +369,30 @@ const realTimeData = ref<boolean>(false);
|
|||||||
function fnRealTime() {
|
function fnRealTime() {
|
||||||
realTimeData.value = !realTimeData.value;
|
realTimeData.value = !realTimeData.value;
|
||||||
if (realTimeData.value) {
|
if (realTimeData.value) {
|
||||||
|
tableState.seached = false;
|
||||||
// 建立链接
|
// 建立链接
|
||||||
const options: OptionsType = {
|
const options: OptionsType = {
|
||||||
url: '/ws',
|
url: '/ws',
|
||||||
params: {
|
params: {
|
||||||
/**订阅通道组
|
/**订阅通道组
|
||||||
*
|
*
|
||||||
* AMF_UE会话事件(GroupID:1010)
|
* AMF_UE会话事件(GroupID:1010_neId)
|
||||||
*/
|
*/
|
||||||
subGroupID: '1010',
|
subGroupID: `1010_${queryParams.neId}`,
|
||||||
},
|
},
|
||||||
onmessage: wsMessage,
|
onmessage: wsMessage,
|
||||||
onerror: wsError,
|
onerror: (ev: any) => {
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
ws.connect(options);
|
ws.connect(options);
|
||||||
} else {
|
} else {
|
||||||
ws.close();
|
ws.close();
|
||||||
|
tableState.seached = true;
|
||||||
|
fnGetList(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**接收数据后回调 */
|
|
||||||
function wsError(ev: any) {
|
|
||||||
// 接收数据后回调
|
|
||||||
console.error(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**接收数据后回调 */
|
/**接收数据后回调 */
|
||||||
function wsMessage(res: Record<string, any>) {
|
function wsMessage(res: Record<string, any>) {
|
||||||
const { code, requestId, data } = res;
|
const { code, requestId, data } = res;
|
||||||
@@ -371,7 +406,7 @@ function wsMessage(res: Record<string, any>) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ueEvent AMF_UE会话事件
|
// ueEvent AMF_UE会话事件
|
||||||
if (data.groupId === '1010') {
|
if (data.groupId === `1010_${queryParams.neId}`) {
|
||||||
const ueEvent = data.data;
|
const ueEvent = data.data;
|
||||||
queue.add(async () => {
|
queue.add(async () => {
|
||||||
modalState.maxId += 1;
|
modalState.maxId += 1;
|
||||||
@@ -399,8 +434,7 @@ onMounted(() => {
|
|||||||
getDict('ue_auth_code'),
|
getDict('ue_auth_code'),
|
||||||
getDict('ue_event_type'),
|
getDict('ue_event_type'),
|
||||||
getDict('ue_event_cm_state'),
|
getDict('ue_event_cm_state'),
|
||||||
])
|
]).then(resArr => {
|
||||||
.then(resArr => {
|
|
||||||
if (resArr[0].status === 'fulfilled') {
|
if (resArr[0].status === 'fulfilled') {
|
||||||
dict.ueAauthCode = resArr[0].value;
|
dict.ueAauthCode = resArr[0].value;
|
||||||
}
|
}
|
||||||
@@ -410,6 +444,31 @@ onMounted(() => {
|
|||||||
if (resArr[2].status === 'fulfilled') {
|
if (resArr[2].status === 'fulfilled') {
|
||||||
dict.ueEventCmState = resArr[2].value;
|
dict.ueEventCmState = resArr[2].value;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取网元网元列表
|
||||||
|
useNeInfoStore()
|
||||||
|
.fnNelist()
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length > 0) {
|
||||||
|
let arr: Record<string, any>[] = [];
|
||||||
|
res.data.forEach(i => {
|
||||||
|
if (i.neType === 'AMF') {
|
||||||
|
arr.push({ value: i.neId, label: i.neName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neOtions.value = arr;
|
||||||
|
if (arr.length > 0) {
|
||||||
|
queryParams.neId = arr[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
// 获取列表数据
|
// 获取列表数据
|
||||||
@@ -434,6 +493,16 @@ onBeforeUnmount(() => {
|
|||||||
<!-- 表格搜索栏 -->
|
<!-- 表格搜索栏 -->
|
||||||
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item label="AMF" name="neId ">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.neId"
|
||||||
|
:options="neOtions"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnGetList(1)"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.dashboard.ue.eventType')"
|
:label="t('views.dashboard.ue.eventType')"
|
||||||
@@ -448,7 +517,7 @@ onBeforeUnmount(() => {
|
|||||||
></a-select>
|
></a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="4" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item label="IMSI" name="imsi ">
|
<a-form-item label="IMSI" name="imsi ">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="queryParams.imsi"
|
v-model:value="queryParams.imsi"
|
||||||
@@ -457,6 +526,20 @@ onBeforeUnmount(() => {
|
|||||||
></a-input>
|
></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</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-col :lg="8" :md="12" :xs="24">
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.dashboard.cdr.time')"
|
:label="t('views.dashboard.cdr.time')"
|
||||||
@@ -473,20 +556,6 @@ onBeforeUnmount(() => {
|
|||||||
></a-range-picker>
|
></a-range-picker>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</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-row>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -522,6 +591,7 @@ onBeforeUnmount(() => {
|
|||||||
:disabled="tableState.selectedRowKeys.length <= 0"
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
:loading="modalState.confirmLoading"
|
:loading="modalState.confirmLoading"
|
||||||
@click.prevent="fnRecordDelete('0')"
|
@click.prevent="fnRecordDelete('0')"
|
||||||
|
v-perms:has="['cdr:ne:remove']"
|
||||||
>
|
>
|
||||||
<template #icon><DeleteOutlined /></template>
|
<template #icon><DeleteOutlined /></template>
|
||||||
{{ t('common.deleteText') }}
|
{{ t('common.deleteText') }}
|
||||||
@@ -544,6 +614,7 @@ onBeforeUnmount(() => {
|
|||||||
:checked-children="t('common.switch.show')"
|
:checked-children="t('common.switch.show')"
|
||||||
:un-checked-children="t('common.switch.hide')"
|
:un-checked-children="t('common.switch.hide')"
|
||||||
size="small"
|
size="small"
|
||||||
|
:disabled="realTimeData"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
@@ -604,7 +675,7 @@ onBeforeUnmount(() => {
|
|||||||
<span v-if="record.eventType === 'auth-result'">
|
<span v-if="record.eventType === 'auth-result'">
|
||||||
<DictTag
|
<DictTag
|
||||||
:options="dict.ueAauthCode"
|
:options="dict.ueAauthCode"
|
||||||
:value="record.eventJSON.authCode"
|
:value="record.eventJSON.result"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="record.eventType === 'detach'">
|
<span v-if="record.eventType === 'detach'">
|
||||||
@@ -613,37 +684,29 @@ onBeforeUnmount(() => {
|
|||||||
<span v-if="record.eventType === 'cm-state'">
|
<span v-if="record.eventType === 'cm-state'">
|
||||||
<DictTag
|
<DictTag
|
||||||
:options="dict.ueEventCmState"
|
:options="dict.ueEventCmState"
|
||||||
:value="record.eventJSON.status"
|
:value="record.eventJSON.result"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'time'">
|
|
||||||
<span
|
|
||||||
v-if="record.eventType === 'auth-result'"
|
|
||||||
:title="record.eventJSON.authTime"
|
|
||||||
>
|
|
||||||
{{ record.eventJSON.authTime }}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
v-if="record.eventType === 'detach'"
|
|
||||||
:title="record.eventJSON.detachTime"
|
|
||||||
>
|
|
||||||
{{ record.eventJSON.detachTime }}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
v-if="record.eventType === 'cm-state'"
|
|
||||||
:title="record.eventJSON.changeTime"
|
|
||||||
>
|
|
||||||
{{ record.eventJSON.changeTime }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<template v-if="column.key === 'id'">
|
<template v-if="column.key === 'id'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.copyText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordCopy(record.eventJSON)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<CopyOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template #title>{{ t('common.deleteText') }}</template>
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
@click.prevent="fnRecordDelete(record.id)"
|
@click.prevent="fnRecordDelete(record.id)"
|
||||||
|
v-perms:has="['cdr:ne:remove']"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
@@ -654,7 +717,8 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template #expandedRowRender="{ record }">
|
<template #expandedRowRender="{ record }">
|
||||||
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="8" :md="12" :xs="24" :offset="2">
|
||||||
<a-divider orientation="left">
|
<a-divider orientation="left">
|
||||||
{{ t('views.dashboard.ue.ueInfo') }}
|
{{ t('views.dashboard.ue.ueInfo') }}
|
||||||
</a-divider>
|
</a-divider>
|
||||||
@@ -666,40 +730,33 @@ onBeforeUnmount(() => {
|
|||||||
<span>{{ t('views.ne.common.rmUid') }}: </span>
|
<span>{{ t('views.ne.common.rmUid') }}: </span>
|
||||||
<span>{{ record.rmUID }}</span>
|
<span>{{ record.rmUID }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
<a-divider orientation="left">
|
<a-divider orientation="left">
|
||||||
{{ t('views.dashboard.ue.rowInfo') }}
|
{{ t('views.dashboard.ue.rowInfo') }}
|
||||||
</a-divider>
|
</a-divider>
|
||||||
<div>
|
<div>
|
||||||
<span>{{ t('views.dashboard.ue.time') }}: </span>
|
<span>{{ t('views.dashboard.ue.time') }}: </span>
|
||||||
<span
|
<template v-if="record.eventJSON?.time">
|
||||||
v-if="record.eventType === 'auth-result'"
|
{{ record.eventJSON.time }}
|
||||||
:title="record.eventJSON.authTime"
|
</template>
|
||||||
>
|
<template v-else>
|
||||||
{{ record.eventJSON.authTime }}
|
{{ parseDateToStr(record.eventJSON.timestamp * 1000) }}
|
||||||
</span>
|
</template>
|
||||||
<span
|
|
||||||
v-if="record.eventType === 'detach'"
|
|
||||||
:title="record.eventJSON.detachTime"
|
|
||||||
>
|
|
||||||
{{ record.eventJSON.detachTime }}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
v-if="record.eventType === 'cm-state'"
|
|
||||||
:title="record.eventJSON.changeTime"
|
|
||||||
>
|
|
||||||
{{ record.eventJSON.changeTime }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>{{ t('views.dashboard.ue.eventType') }}: </span>
|
<span>{{ t('views.dashboard.ue.eventType') }}: </span>
|
||||||
<DictTag :options="dict.ueEventType" :value="record.eventType" />
|
<DictTag
|
||||||
|
:options="dict.ueEventType"
|
||||||
|
:value="record.eventType"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>{{ t('views.dashboard.ue.result') }}: </span>
|
<span>{{ t('views.dashboard.ue.result') }}: </span>
|
||||||
<span v-if="record.eventType === 'auth-result'">
|
<span v-if="record.eventType === 'auth-result'">
|
||||||
<DictTag
|
<DictTag
|
||||||
:options="dict.ueAauthCode"
|
:options="dict.ueAauthCode"
|
||||||
:value="record.eventJSON.authCode"
|
:value="record.eventJSON.result"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="record.eventType === 'detach'">
|
<span v-if="record.eventType === 'detach'">
|
||||||
@@ -708,11 +765,12 @@ onBeforeUnmount(() => {
|
|||||||
<span v-if="record.eventType === 'cm-state'">
|
<span v-if="record.eventType === 'cm-state'">
|
||||||
<DictTag
|
<DictTag
|
||||||
:options="dict.ueEventCmState"
|
:options="dict.ueEventCmState"
|
||||||
:value="record.eventJSON.status"
|
:value="record.eventJSON.result"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</a-col>
|
||||||
|
</a-row>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ import { parseDateToStr, parseDuration } from '@/utils/date-utils';
|
|||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
import saveAs from 'file-saver';
|
import saveAs from 'file-saver';
|
||||||
import PQueue from 'p-queue';
|
import PQueue from 'p-queue';
|
||||||
|
import { useClipboard } from '@vueuse/core';
|
||||||
|
import dayjs, { type Dayjs } from 'dayjs';
|
||||||
|
const { copy } = useClipboard({ legacy: true });
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { getDict } = useDictStore();
|
const { getDict } = useDictStore();
|
||||||
const ws = new WS();
|
const ws = new WS();
|
||||||
@@ -41,7 +44,25 @@ let dict: {
|
|||||||
let neOtions = ref<Record<string, any>[]>([]);
|
let neOtions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
/**开始结束时间 */
|
/**开始结束时间 */
|
||||||
let queryRangePicker = ref<[string, string]>(['', '']);
|
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([
|
||||||
|
dayjs().startOf('hour'),
|
||||||
|
dayjs().endOf('hour'),
|
||||||
|
]);
|
||||||
|
/**时间范围 */
|
||||||
|
let rangePickerPresets = ref([
|
||||||
|
{
|
||||||
|
label: 'Now hour',
|
||||||
|
value: [dayjs().startOf('hour'), dayjs().endOf('hour')],
|
||||||
|
},
|
||||||
|
{ label: 'Today', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
|
||||||
|
{
|
||||||
|
label: 'Yesterday',
|
||||||
|
value: [
|
||||||
|
dayjs().subtract(1, 'day').startOf('day'),
|
||||||
|
dayjs().subtract(1, 'day').endOf('day'),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
/**查询参数 */
|
/**查询参数 */
|
||||||
let queryParams = reactive({
|
let queryParams = reactive({
|
||||||
@@ -54,9 +75,9 @@ let queryParams = reactive({
|
|||||||
sortField: 'timestamp',
|
sortField: 'timestamp',
|
||||||
sortOrder: 'desc',
|
sortOrder: 'desc',
|
||||||
/**开始时间 */
|
/**开始时间 */
|
||||||
startTime: '',
|
startTime: undefined as undefined | number,
|
||||||
/**结束时间 */
|
/**结束时间 */
|
||||||
endTime: '',
|
endTime: undefined as undefined | number,
|
||||||
/**当前页数 */
|
/**当前页数 */
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
/**每页条数 */
|
/**每页条数 */
|
||||||
@@ -70,12 +91,12 @@ function fnQueryReset() {
|
|||||||
recordType: '',
|
recordType: '',
|
||||||
callerParty: '',
|
callerParty: '',
|
||||||
calledParty: '',
|
calledParty: '',
|
||||||
startTime: '',
|
startTime: undefined,
|
||||||
endTime: '',
|
endTime: undefined,
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
});
|
});
|
||||||
queryRangePicker.value = ['', ''];
|
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')];
|
||||||
tablePagination.current = 1;
|
tablePagination.current = 1;
|
||||||
tablePagination.pageSize = 20;
|
tablePagination.pageSize = 20;
|
||||||
fnGetList();
|
fnGetList();
|
||||||
@@ -307,6 +328,18 @@ function fnRecordDelete(id: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制CDR
|
||||||
|
* @param jsonStr JSON字符串
|
||||||
|
*/
|
||||||
|
function fnRecordCopy(jsonStr: string) {
|
||||||
|
if (!jsonStr) return;
|
||||||
|
const text = JSON.stringify(jsonStr, null, 2);
|
||||||
|
copy(text).then(() => {
|
||||||
|
message.success(t('common.copyOk'), 3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**查询列表, pageNum初始页数 */
|
/**查询列表, pageNum初始页数 */
|
||||||
function fnGetList(pageNum?: number) {
|
function fnGetList(pageNum?: number) {
|
||||||
if (tableState.loading) return;
|
if (tableState.loading) return;
|
||||||
@@ -314,11 +347,17 @@ function fnGetList(pageNum?: number) {
|
|||||||
if (pageNum) {
|
if (pageNum) {
|
||||||
queryParams.pageNum = pageNum;
|
queryParams.pageNum = pageNum;
|
||||||
}
|
}
|
||||||
if (!queryRangePicker.value) {
|
// 时间范围
|
||||||
queryRangePicker.value = ['', ''];
|
if (
|
||||||
|
Array.isArray(queryRangePicker.value) &&
|
||||||
|
queryRangePicker.value.length > 0
|
||||||
|
) {
|
||||||
|
queryParams.startTime = queryRangePicker.value[0].valueOf();
|
||||||
|
queryParams.endTime = queryRangePicker.value[1].valueOf();
|
||||||
|
} else {
|
||||||
|
queryParams.startTime = undefined;
|
||||||
|
queryParams.endTime = undefined;
|
||||||
}
|
}
|
||||||
queryParams.startTime = queryRangePicker.value[0];
|
|
||||||
queryParams.endTime = queryRangePicker.value[1];
|
|
||||||
listIMSDataCDR(toRaw(queryParams)).then(res => {
|
listIMSDataCDR(toRaw(queryParams)).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
// 取消勾选
|
// 取消勾选
|
||||||
@@ -407,7 +446,9 @@ function fnRealTime() {
|
|||||||
subGroupID: `1005_${queryParams.neId}`,
|
subGroupID: `1005_${queryParams.neId}`,
|
||||||
},
|
},
|
||||||
onmessage: wsMessage,
|
onmessage: wsMessage,
|
||||||
onerror: wsError,
|
onerror: (ev: any) => {
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
ws.connect(options);
|
ws.connect(options);
|
||||||
} else {
|
} else {
|
||||||
@@ -417,12 +458,6 @@ function fnRealTime() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**接收数据后回调 */
|
|
||||||
function wsError(ev: any) {
|
|
||||||
// 接收数据后回调
|
|
||||||
console.error(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**接收数据后回调 */
|
/**接收数据后回调 */
|
||||||
function wsMessage(res: Record<string, any>) {
|
function wsMessage(res: Record<string, any>) {
|
||||||
const { code, requestId, data } = res;
|
const { code, requestId, data } = res;
|
||||||
@@ -522,6 +557,7 @@ onBeforeUnmount(() => {
|
|||||||
v-model:value="queryParams.neId"
|
v-model:value="queryParams.neId"
|
||||||
:options="neOtions"
|
:options="neOtions"
|
||||||
:placeholder="t('common.selectPlease')"
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnQueryReset()"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -584,12 +620,12 @@ onBeforeUnmount(() => {
|
|||||||
>
|
>
|
||||||
<a-range-picker
|
<a-range-picker
|
||||||
v-model:value="queryRangePicker"
|
v-model:value="queryRangePicker"
|
||||||
allow-clear
|
:presets="rangePickerPresets"
|
||||||
bordered
|
:bordered="true"
|
||||||
|
:allow-clear="false"
|
||||||
|
style="width: 100%"
|
||||||
:show-time="{ format: 'HH:mm:ss' }"
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
value-format="x"
|
|
||||||
style="width: 100%"
|
|
||||||
></a-range-picker>
|
></a-range-picker>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -628,6 +664,7 @@ onBeforeUnmount(() => {
|
|||||||
:disabled="tableState.selectedRowKeys.length <= 0"
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
:loading="modalState.confirmLoading"
|
:loading="modalState.confirmLoading"
|
||||||
@click.prevent="fnRecordDelete('0')"
|
@click.prevent="fnRecordDelete('0')"
|
||||||
|
v-perms:has="['cdr:ne:remove']"
|
||||||
>
|
>
|
||||||
<template #icon><DeleteOutlined /></template>
|
<template #icon><DeleteOutlined /></template>
|
||||||
{{ t('common.deleteText') }}
|
{{ t('common.deleteText') }}
|
||||||
@@ -724,11 +761,23 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'id'">
|
<template v-if="column.key === 'id'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.copyText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordCopy(record.cdrJSON)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<CopyOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template #title>{{ t('common.deleteText') }}</template>
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
@click.prevent="fnRecordDelete(record.id)"
|
@click.prevent="fnRecordDelete(record.id)"
|
||||||
|
v-perms:has="['cdr:ne:remove']"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
@@ -740,7 +789,7 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
<template #expandedRowRender="{ record }">
|
<template #expandedRowRender="{ record }">
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="5" :md="12" :xs="24">
|
<a-col :lg="8" :md="12" :xs="24" :offset="2">
|
||||||
<a-divider orientation="left">
|
<a-divider orientation="left">
|
||||||
{{ t('views.dashboard.cdr.cdrInfo') }}
|
{{ t('views.dashboard.cdr.cdrInfo') }}
|
||||||
</a-divider>
|
</a-divider>
|
||||||
@@ -763,7 +812,7 @@ onBeforeUnmount(() => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
<a-divider orientation="left">
|
<a-divider orientation="left">
|
||||||
{{ t('views.dashboard.cdr.rowInfo') }}
|
{{ t('views.dashboard.cdr.rowInfo') }}
|
||||||
</a-divider>
|
</a-divider>
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import { reactive, onMounted, toRaw, ref, onBeforeUnmount } from 'vue';
|
import { reactive, onMounted, toRaw, ref, onBeforeUnmount } from 'vue';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { message, Modal } from 'ant-design-vue/es';
|
import { message, Modal } from 'ant-design-vue/es';
|
||||||
@@ -17,6 +18,8 @@ import { parseDateToStr } from '@/utils/date-utils';
|
|||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
import saveAs from 'file-saver';
|
import saveAs from 'file-saver';
|
||||||
import PQueue from 'p-queue';
|
import PQueue from 'p-queue';
|
||||||
|
import { useClipboard } from '@vueuse/core';
|
||||||
|
const { copy } = useClipboard({ legacy: true });
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { getDict } = useDictStore();
|
const { getDict } = useDictStore();
|
||||||
const ws = new WS();
|
const ws = new WS();
|
||||||
@@ -40,7 +43,10 @@ let dict: {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**开始结束时间 */
|
/**开始结束时间 */
|
||||||
let queryRangePicker = ref<[string, string]>(['', '']);
|
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([
|
||||||
|
dayjs().startOf('hour'),
|
||||||
|
dayjs().endOf('hour'),
|
||||||
|
]);
|
||||||
|
|
||||||
/**查询参数 */
|
/**查询参数 */
|
||||||
let queryParams = reactive({
|
let queryParams = reactive({
|
||||||
@@ -52,9 +58,9 @@ let queryParams = reactive({
|
|||||||
sortField: 'timestamp',
|
sortField: 'timestamp',
|
||||||
sortOrder: 'desc',
|
sortOrder: 'desc',
|
||||||
/**开始时间 */
|
/**开始时间 */
|
||||||
startTime: '',
|
startTime: undefined as undefined | number,
|
||||||
/**结束时间 */
|
/**结束时间 */
|
||||||
endTime: '',
|
endTime: undefined as undefined | number,
|
||||||
/**当前页数 */
|
/**当前页数 */
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
/**每页条数 */
|
/**每页条数 */
|
||||||
@@ -72,7 +78,7 @@ function fnQueryReset() {
|
|||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
});
|
});
|
||||||
queryRangePicker.value = ['', ''];
|
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')];
|
||||||
tablePagination.current = 1;
|
tablePagination.current = 1;
|
||||||
tablePagination.pageSize = 20;
|
tablePagination.pageSize = 20;
|
||||||
fnGetList();
|
fnGetList();
|
||||||
@@ -149,8 +155,11 @@ let tableColumns: ColumnsType = [
|
|||||||
align: 'left',
|
align: 'left',
|
||||||
width: 150,
|
width: 150,
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
const cdrJSON = opt.value;
|
const record = opt.value;
|
||||||
return parseDateToStr(+cdrJSON.timestamp * 1000);
|
if (record?.time) {
|
||||||
|
return record.time;
|
||||||
|
}
|
||||||
|
return parseDateToStr(+record.timestamp * 1000);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -260,11 +269,19 @@ function fnGetList(pageNum?: number) {
|
|||||||
if (pageNum) {
|
if (pageNum) {
|
||||||
queryParams.pageNum = pageNum;
|
queryParams.pageNum = pageNum;
|
||||||
}
|
}
|
||||||
if (!queryRangePicker.value) {
|
|
||||||
queryRangePicker.value = ['', ''];
|
// 时间范围
|
||||||
|
if (
|
||||||
|
Array.isArray(queryRangePicker.value) &&
|
||||||
|
queryRangePicker.value.length > 0
|
||||||
|
) {
|
||||||
|
queryParams.startTime = queryRangePicker.value[0].valueOf();
|
||||||
|
queryParams.endTime = queryRangePicker.value[1].valueOf();
|
||||||
|
} else {
|
||||||
|
queryParams.startTime = undefined;
|
||||||
|
queryParams.endTime = undefined;
|
||||||
}
|
}
|
||||||
queryParams.startTime = queryRangePicker.value[0];
|
|
||||||
queryParams.endTime = queryRangePicker.value[1];
|
|
||||||
listMMEDataUE(toRaw(queryParams)).then(res => {
|
listMMEDataUE(toRaw(queryParams)).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
// 取消勾选
|
// 取消勾选
|
||||||
@@ -332,6 +349,18 @@ function fnExportList() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制CDR
|
||||||
|
* @param jsonStr JSON字符串
|
||||||
|
*/
|
||||||
|
function fnRecordCopy(jsonStr: string) {
|
||||||
|
if (!jsonStr) return;
|
||||||
|
const text = JSON.stringify(jsonStr, null, 2);
|
||||||
|
copy(text).then(() => {
|
||||||
|
message.success(t('common.copyOk'), 3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**实时数据开关 */
|
/**实时数据开关 */
|
||||||
const realTimeData = ref<boolean>(false);
|
const realTimeData = ref<boolean>(false);
|
||||||
|
|
||||||
@@ -348,12 +377,14 @@ function fnRealTime() {
|
|||||||
params: {
|
params: {
|
||||||
/**订阅通道组
|
/**订阅通道组
|
||||||
*
|
*
|
||||||
* MME_UE会话事件(GroupID:1011)
|
* MME_UE会话事件(GroupID:1011_neId)
|
||||||
*/
|
*/
|
||||||
subGroupID: `1011_${queryParams.neId}`,
|
subGroupID: `1011_${queryParams.neId}`,
|
||||||
},
|
},
|
||||||
onmessage: wsMessage,
|
onmessage: wsMessage,
|
||||||
onerror: wsError,
|
onerror: (ev: any) => {
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
ws.connect(options);
|
ws.connect(options);
|
||||||
} else {
|
} else {
|
||||||
@@ -363,12 +394,6 @@ function fnRealTime() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**接收数据后回调 */
|
|
||||||
function wsError(ev: any) {
|
|
||||||
// 接收数据后回调
|
|
||||||
console.error(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**接收数据后回调 */
|
/**接收数据后回调 */
|
||||||
function wsMessage(res: Record<string, any>) {
|
function wsMessage(res: Record<string, any>) {
|
||||||
const { code, requestId, data } = res;
|
const { code, requestId, data } = res;
|
||||||
@@ -415,7 +440,7 @@ onMounted(() => {
|
|||||||
dict.ueAauthCode = resArr[0].value;
|
dict.ueAauthCode = resArr[0].value;
|
||||||
}
|
}
|
||||||
if (resArr[1].status === 'fulfilled') {
|
if (resArr[1].status === 'fulfilled') {
|
||||||
const ueEventType: any[] = JSON.parse(JSON.stringify(resArr[1]));
|
const ueEventType: any[] = JSON.parse(JSON.stringify(resArr[1].value));
|
||||||
dict.ueEventType = ueEventType.map(item => {
|
dict.ueEventType = ueEventType.map(item => {
|
||||||
if (item.value === 'cm-state') {
|
if (item.value === 'cm-state') {
|
||||||
item.label = item.label.replace('CM', 'ECM');
|
item.label = item.label.replace('CM', 'ECM');
|
||||||
@@ -481,6 +506,7 @@ onBeforeUnmount(() => {
|
|||||||
v-model:value="queryParams.neId"
|
v-model:value="queryParams.neId"
|
||||||
:options="neOtions"
|
:options="neOtions"
|
||||||
:placeholder="t('common.selectPlease')"
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnGetList(1)"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -498,7 +524,7 @@ onBeforeUnmount(() => {
|
|||||||
></a-select>
|
></a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="4" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item label="IMSI" name="imsi ">
|
<a-form-item label="IMSI" name="imsi ">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="queryParams.imsi"
|
v-model:value="queryParams.imsi"
|
||||||
@@ -507,6 +533,20 @@ onBeforeUnmount(() => {
|
|||||||
></a-input>
|
></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</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-col :lg="8" :md="12" :xs="24">
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.dashboard.cdr.time')"
|
:label="t('views.dashboard.cdr.time')"
|
||||||
@@ -523,20 +563,6 @@ onBeforeUnmount(() => {
|
|||||||
></a-range-picker>
|
></a-range-picker>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</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-row>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -572,6 +598,7 @@ onBeforeUnmount(() => {
|
|||||||
:disabled="tableState.selectedRowKeys.length <= 0"
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
:loading="modalState.confirmLoading"
|
:loading="modalState.confirmLoading"
|
||||||
@click.prevent="fnRecordDelete('0')"
|
@click.prevent="fnRecordDelete('0')"
|
||||||
|
v-perms:has="['cdr:ne:remove']"
|
||||||
>
|
>
|
||||||
<template #icon><DeleteOutlined /></template>
|
<template #icon><DeleteOutlined /></template>
|
||||||
{{ t('common.deleteText') }}
|
{{ t('common.deleteText') }}
|
||||||
@@ -670,11 +697,23 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'id'">
|
<template v-if="column.key === 'id'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.copyText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordCopy(record.eventJSON)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<CopyOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template #title>{{ t('common.deleteText') }}</template>
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
@click.prevent="fnRecordDelete(record.id)"
|
@click.prevent="fnRecordDelete(record.id)"
|
||||||
|
v-perms:has="['cdr:ne:remove']"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
@@ -685,7 +724,8 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template #expandedRowRender="{ record }">
|
<template #expandedRowRender="{ record }">
|
||||||
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="8" :md="12" :xs="24" :offset="2">
|
||||||
<a-divider orientation="left">
|
<a-divider orientation="left">
|
||||||
{{ t('views.dashboard.ue.ueInfo') }}
|
{{ t('views.dashboard.ue.ueInfo') }}
|
||||||
</a-divider>
|
</a-divider>
|
||||||
@@ -697,16 +737,26 @@ onBeforeUnmount(() => {
|
|||||||
<span>{{ t('views.ne.common.rmUid') }}: </span>
|
<span>{{ t('views.ne.common.rmUid') }}: </span>
|
||||||
<span>{{ record.rmUID }}</span>
|
<span>{{ record.rmUID }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
<a-divider orientation="left">
|
<a-divider orientation="left">
|
||||||
{{ t('views.dashboard.ue.rowInfo') }}
|
{{ t('views.dashboard.ue.rowInfo') }}
|
||||||
</a-divider>
|
</a-divider>
|
||||||
<div>
|
<div>
|
||||||
<span>{{ t('views.dashboard.ue.time') }}: </span>
|
<span>{{ t('views.dashboard.ue.time') }}: </span>
|
||||||
|
<template v-if="record.eventJSON?.time">
|
||||||
|
{{ record.eventJSON.time }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
{{ parseDateToStr(record.eventJSON.timestamp * 1000) }}
|
{{ parseDateToStr(record.eventJSON.timestamp * 1000) }}
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>{{ t('views.dashboard.ue.eventType') }}: </span>
|
<span>{{ t('views.dashboard.ue.eventType') }}: </span>
|
||||||
<DictTag :options="dict.ueEventType" :value="record.eventType" />
|
<DictTag
|
||||||
|
:options="dict.ueEventType"
|
||||||
|
:value="record.eventType"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>{{ t('views.dashboard.ue.result') }}: </span>
|
<span>{{ t('views.dashboard.ue.result') }}: </span>
|
||||||
@@ -726,7 +776,8 @@ onBeforeUnmount(() => {
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</a-col>
|
||||||
|
</a-row>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|||||||
@@ -77,8 +77,12 @@ onMounted(() => {
|
|||||||
<div></div>
|
<div></div>
|
||||||
<div>
|
<div>
|
||||||
{{ t('views.dashboard.overview.userActivity.time') }}:
|
{{ t('views.dashboard.overview.userActivity.time') }}:
|
||||||
<span :title="parseDateToStr(item.data.releaseTime * 1000)">
|
<span :title="item.data.releaseTime">
|
||||||
{{ parseDateToStr(item.data.releaseTime * 1000) }}
|
{{
|
||||||
|
typeof item.data.releaseTime === 'number'
|
||||||
|
? parseDateToStr(+item.data.releaseTime * 1000)
|
||||||
|
: item.data.releaseTime
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -133,18 +137,12 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{{ t('views.dashboard.overview.userActivity.time') }}:
|
{{ t('views.dashboard.overview.userActivity.time') }}:
|
||||||
<span
|
<template v-if="item.data?.time">
|
||||||
v-if="item.type === 'auth-result'"
|
{{ item.data.time }}
|
||||||
:title="item.data.authTime"
|
</template>
|
||||||
>
|
<template v-else>
|
||||||
{{ item.data.authTime }}
|
{{ parseDateToStr(+item.data.timestamp * 1000) }}
|
||||||
</span>
|
</template>
|
||||||
<span v-if="item.type === 'detach'" :title="item.data.detachTime">
|
|
||||||
{{ item.data.detachTime }}
|
|
||||||
</span>
|
|
||||||
<span v-if="item.type === 'cm-state'" :title="item.data.changeTime">
|
|
||||||
{{ item.data.changeTime }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -163,7 +161,7 @@ onMounted(() => {
|
|||||||
<div v-if="item.type === 'auth-result'">
|
<div v-if="item.type === 'auth-result'">
|
||||||
{{ t('views.dashboard.overview.userActivity.result') }}:
|
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||||
<span>
|
<span>
|
||||||
<DictTag :options="dict.ueAauthCode" :value="item.data.authCode" />
|
<DictTag :options="dict.ueAauthCode" :value="item.data.result" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="item.type === 'detach'">
|
<div v-if="item.type === 'detach'">
|
||||||
@@ -173,7 +171,7 @@ onMounted(() => {
|
|||||||
<div class="card-ue-w33" v-if="item.type === 'cm-state'">
|
<div class="card-ue-w33" v-if="item.type === 'cm-state'">
|
||||||
{{ t('views.dashboard.overview.userActivity.result') }}:
|
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||||
<span>
|
<span>
|
||||||
<DictTag :options="dict.ueEventCmState" :value="item.data.status" />
|
<DictTag :options="dict.ueEventCmState" :value="item.data.result" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -198,12 +196,16 @@ onMounted(() => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
IMSI: <span :title="item.data.imsi">{{ item.data.imsi }}</span>
|
IMSI: <span :title="item.data?.imsi">{{ item.data?.imsi }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{{ t('views.dashboard.overview.userActivity.time') }}:
|
{{ t('views.dashboard.overview.userActivity.time') }}:
|
||||||
<span :title="item.data.timestamp">
|
<span :title="item.data?.timestamp">
|
||||||
{{ parseDateToStr(+item.data.timestamp * 1000) }}
|
{{
|
||||||
|
typeof item.data?.timestamp === 'number'
|
||||||
|
? parseDateToStr(+item.data?.timestamp * 1000)
|
||||||
|
: item.data?.timestamp
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,561 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
reactive,
|
||||||
|
onMounted,
|
||||||
|
onBeforeUnmount,
|
||||||
|
markRaw,
|
||||||
|
useTemplateRef, ref
|
||||||
|
} from 'vue';
|
||||||
|
import TrendChart from './TrendChart.vue';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { listNeInfo } from '@/api/ne/neInfo';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { getNeConfigData } from '@/api/ne/neConfig';
|
||||||
|
import { listCallings } from '@/api/agentManage/callings';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import useUserStore from '@/store/modules/user';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
/**10s调度器 */
|
||||||
|
const interval10s = ref<any>(null);
|
||||||
|
|
||||||
|
// 模拟数据
|
||||||
|
const activeCallsData = ref([]);
|
||||||
|
const mosData = ref([]);
|
||||||
|
const failedCallsData = ref([]);
|
||||||
|
|
||||||
|
// 新增三个卡片的模拟数据
|
||||||
|
const networkCpuData = ref([]);
|
||||||
|
const systemCpuData = ref([]);
|
||||||
|
const systemStorageData = ref([]);
|
||||||
|
const systemMemData = ref([]);
|
||||||
|
|
||||||
|
|
||||||
|
// 是否是第一次加载资源数据
|
||||||
|
const isFirstLoad = ref(true);
|
||||||
|
|
||||||
|
// 更新图表数据的函数
|
||||||
|
function updateChartData(newValue: number, dataArray: any) {
|
||||||
|
// 如果是第一次加载,用当前值填充整个数组
|
||||||
|
if (isFirstLoad.value) {
|
||||||
|
dataArray.value = Array(7).fill(newValue);
|
||||||
|
} else {
|
||||||
|
// 非第一次加载,正常更新数据(移除第一个,添加新值)
|
||||||
|
const newData = [...dataArray.value];
|
||||||
|
newData.shift();
|
||||||
|
newData.push(newValue);
|
||||||
|
dataArray.value = newData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否是第一次加载用户数据
|
||||||
|
const isFirstLoadUser = ref(true);
|
||||||
|
function updateUserChartData(newValue: number, dataArray: any) {
|
||||||
|
// 如果是第一次加载,用当前值填充整个数组
|
||||||
|
if (isFirstLoadUser.value) {
|
||||||
|
dataArray.value = Array(7).fill(newValue);
|
||||||
|
} else {
|
||||||
|
// 非第一次加载,正常更新数据(移除第一个,添加新值)
|
||||||
|
const newData = [...dataArray.value];
|
||||||
|
newData.shift();
|
||||||
|
newData.push(newValue);
|
||||||
|
dataArray.value = newData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否是第一次加载并行用户数据
|
||||||
|
const isFirstLoadFailed = ref(true);
|
||||||
|
function updateFailedChartData(newValue: number, dataArray: any) {
|
||||||
|
// 如果是第一次加载,用当前值填充整个数组
|
||||||
|
if (isFirstLoadFailed.value) {
|
||||||
|
dataArray.value = Array(7).fill(newValue);
|
||||||
|
} else {
|
||||||
|
// 非第一次加载,正常更新数据(移除第一个,添加新值)
|
||||||
|
const newData = [...dataArray.value];
|
||||||
|
newData.shift();
|
||||||
|
newData.push(newValue);
|
||||||
|
dataArray.value = newData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前资源使用率
|
||||||
|
const currentNfCpuUsage = ref(0);
|
||||||
|
const currentSysCpuUsage = ref(0);
|
||||||
|
const currentSysDiskUsage = ref(0);
|
||||||
|
const currentSysMemUsage = ref(0);
|
||||||
|
|
||||||
|
// 上一次的资源使用率
|
||||||
|
const prevNfCpuUsage = ref(0);
|
||||||
|
const prevSysCpuUsage = ref(0);
|
||||||
|
const prevSysDiskUsage = ref(0);
|
||||||
|
const prevSysMemUsage = ref(0);
|
||||||
|
|
||||||
|
// 资源变化百分比
|
||||||
|
const nfCpuChange = ref(0);
|
||||||
|
const sysCpuChange = ref(0);
|
||||||
|
const sysDiskChange = ref(0);
|
||||||
|
const sysMemChange = ref(0);
|
||||||
|
|
||||||
|
|
||||||
|
// 当前资源使用率
|
||||||
|
const activeCalls = ref(0);
|
||||||
|
const onlineCount = ref(0);
|
||||||
|
const failedCallsCount = ref(0);
|
||||||
|
|
||||||
|
// 上一次的资源使用率
|
||||||
|
const prevActiveCalls = ref(0);
|
||||||
|
const prevOnlineCount = ref(0);
|
||||||
|
const prevFailedCallsCount = ref(0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 用户数变化百分比
|
||||||
|
const activeCallsChange = ref(0);
|
||||||
|
const onlineCountChange = ref(0);
|
||||||
|
const failedCallsCountChange = ref(0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**解析网元状态携带的资源利用率 */
|
||||||
|
function parseResouresUsage(neState: Record<string, any>) {
|
||||||
|
let sysCpuUsage = 0;
|
||||||
|
let nfCpuUsage = 0;
|
||||||
|
if (neState.cpu) {
|
||||||
|
nfCpuUsage = neState.cpu.nfCpuUsage;
|
||||||
|
const nfCpu = +(nfCpuUsage / 100);
|
||||||
|
nfCpuUsage = +nfCpu.toFixed(2);
|
||||||
|
if (nfCpuUsage > 100) {
|
||||||
|
nfCpuUsage = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
sysCpuUsage = neState.cpu.sysCpuUsage;
|
||||||
|
const sysCpu = +(sysCpuUsage / 100);
|
||||||
|
sysCpuUsage = +sysCpu.toFixed(2);
|
||||||
|
if (sysCpuUsage > 100) {
|
||||||
|
sysCpuUsage = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let sysMemUsage = 0;
|
||||||
|
if (neState.mem) {
|
||||||
|
const men = neState.mem.sysMemUsage;
|
||||||
|
sysMemUsage = +(men / 100).toFixed(2);
|
||||||
|
if (sysMemUsage > 100) {
|
||||||
|
sysMemUsage = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let sysDiskUsage = 0;
|
||||||
|
if (neState.disk && Array.isArray(neState.disk.partitionInfo)) {
|
||||||
|
let disks: any[] = neState.disk.partitionInfo;
|
||||||
|
disks = disks.sort((a, b) => +b.used - +a.used);
|
||||||
|
if (disks.length > 0) {
|
||||||
|
const { total, used } = disks[0];
|
||||||
|
sysDiskUsage = +((used / total) * 100).toFixed(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
sysDiskUsage,
|
||||||
|
sysMemUsage,
|
||||||
|
sysCpuUsage,
|
||||||
|
nfCpuUsage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算变化百分比
|
||||||
|
function calculateChange(current: number, previous: number): number {
|
||||||
|
if (previous === 0) return 0;
|
||||||
|
return Number(((current - previous) * 100 / previous).toFixed(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
let tableState: any = [];
|
||||||
|
|
||||||
|
|
||||||
|
function fnGetList() {
|
||||||
|
listNeInfo({
|
||||||
|
neType: 'MF',
|
||||||
|
bandStatus: true,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows) && res.rows.length > 0) {
|
||||||
|
// 获取第一个网元的资源使用情况
|
||||||
|
const item = res.rows[0];
|
||||||
|
let resouresUsage = {
|
||||||
|
sysDiskUsage: 0,
|
||||||
|
sysMemUsage: 0,
|
||||||
|
sysCpuUsage: 0,
|
||||||
|
nfCpuUsage: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const neState = item.serverState;
|
||||||
|
if (neState) {
|
||||||
|
resouresUsage = parseResouresUsage(neState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存上一次的值
|
||||||
|
prevNfCpuUsage.value = currentNfCpuUsage.value;
|
||||||
|
prevSysCpuUsage.value = currentSysCpuUsage.value;
|
||||||
|
prevSysDiskUsage.value = currentSysDiskUsage.value;
|
||||||
|
prevSysMemUsage.value = currentSysMemUsage.value;
|
||||||
|
|
||||||
|
// 更新当前值
|
||||||
|
currentNfCpuUsage.value = resouresUsage.nfCpuUsage;
|
||||||
|
currentSysCpuUsage.value = resouresUsage.sysCpuUsage;
|
||||||
|
currentSysDiskUsage.value = resouresUsage.sysDiskUsage;
|
||||||
|
currentSysMemUsage.value = resouresUsage.sysMemUsage;
|
||||||
|
|
||||||
|
|
||||||
|
// 计算变化百分比
|
||||||
|
nfCpuChange.value = calculateChange(currentNfCpuUsage.value, prevNfCpuUsage.value);
|
||||||
|
sysCpuChange.value = calculateChange(currentSysCpuUsage.value, prevSysCpuUsage.value);
|
||||||
|
sysDiskChange.value = calculateChange(currentSysDiskUsage.value, prevSysDiskUsage.value);
|
||||||
|
sysMemChange.value = calculateChange(currentSysMemUsage.value, prevSysMemUsage.value);
|
||||||
|
|
||||||
|
// 更新图表数据
|
||||||
|
updateChartData(currentNfCpuUsage.value, networkCpuData);
|
||||||
|
updateChartData(currentSysCpuUsage.value, systemCpuData);
|
||||||
|
updateChartData(currentSysDiskUsage.value, systemStorageData);
|
||||||
|
updateChartData(currentSysMemUsage.value, systemMemData);
|
||||||
|
|
||||||
|
// 第一次加载完成后,设置标志为false
|
||||||
|
if (isFirstLoad.value) {
|
||||||
|
isFirstLoad.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取网元端的配置数据
|
||||||
|
getNeConfigData({
|
||||||
|
neType: 'MF',
|
||||||
|
neId: '001',
|
||||||
|
paramName: 'agents',
|
||||||
|
}).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
const neData = res.data;
|
||||||
|
|
||||||
|
prevActiveCalls.value = activeCalls.value;
|
||||||
|
prevOnlineCount.value = onlineCount.value;
|
||||||
|
|
||||||
|
|
||||||
|
// 更新 activeCallsData 和 mosData
|
||||||
|
activeCalls.value = neData.length; // 数组长度
|
||||||
|
onlineCount.value = neData.filter((item: any) => item.online).length; // online 为 true 的数量
|
||||||
|
|
||||||
|
activeCallsChange.value = prevActiveCalls.value ? prevActiveCalls.value - activeCalls.value : 0;
|
||||||
|
onlineCountChange.value = prevOnlineCount.value ? prevOnlineCount.value - onlineCount.value : 0;
|
||||||
|
// 更新图表数据
|
||||||
|
updateUserChartData(activeCalls.value ? activeCalls.value : 0, activeCallsData);
|
||||||
|
updateUserChartData(onlineCount.value ? onlineCount.value : 0, mosData);
|
||||||
|
|
||||||
|
// 第一次加载完成后,设置标志为false
|
||||||
|
if (isFirstLoadUser.value) {
|
||||||
|
isFirstLoadUser.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
updateUserChartData(0, activeCallsData);
|
||||||
|
updateUserChartData(0, mosData);
|
||||||
|
|
||||||
|
// 第一次加载完成后,设置标志为false
|
||||||
|
if (isFirstLoadUser.value) {
|
||||||
|
isFirstLoadUser.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
listCallings({ neId: '001' }).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
const callData: any = res.data;
|
||||||
|
prevFailedCallsCount.value = failedCallsCount.value;
|
||||||
|
|
||||||
|
|
||||||
|
failedCallsCount.value = res.total; // 数组长度
|
||||||
|
|
||||||
|
failedCallsCountChange.value = prevFailedCallsCount.value ? prevFailedCallsCount.value - failedCallsCount.value : 0;
|
||||||
|
|
||||||
|
// 更新图表数据
|
||||||
|
updateFailedChartData(failedCallsCount.value, failedCallsData);
|
||||||
|
|
||||||
|
// 第一次加载完成后,设置标志为false
|
||||||
|
if (isFirstLoadFailed.value) {
|
||||||
|
isFirstLoadFailed.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**栏目信息跳转 */
|
||||||
|
function fnToRouter(name: string, query?: any) {
|
||||||
|
if (useUserStore().roles.includes('agent')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
router.push({ name, query });
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始获取数据
|
||||||
|
fnGetList();
|
||||||
|
|
||||||
|
// 设置10秒定时器
|
||||||
|
interval10s.value = setInterval(() => {
|
||||||
|
fnGetList();
|
||||||
|
}, 10000);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 组件销毁前清除定时器
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (interval10s.value) {
|
||||||
|
clearInterval(interval10s.value);
|
||||||
|
interval10s.value = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a-card>
|
||||||
|
<div style="font-size:32px; font-weight: bold; margin-bottom: 20px; text-align: center;">
|
||||||
|
{{ t('views.dashboard.overview.psapTitle') }}</div>
|
||||||
|
|
||||||
|
<div class="row-title">{{ t('views.dashboard.overview.userTitle') }}</div>
|
||||||
|
<a-row :gutter="[48, 48]">
|
||||||
|
<a-col :xs="24" :sm="24" :lg="8">
|
||||||
|
<a-card :bordered="false" class="metric-card"
|
||||||
|
@click="fnToRouter('NeConfig_2146', { neType: 'MF', treeName: 'agents' })">
|
||||||
|
<div class="card-title">{{ t('views.dashboard.overview.totalUser') }}</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="trend-chart">
|
||||||
|
<TrendChart :data="activeCallsData" color="#4CAF50" />
|
||||||
|
</div>
|
||||||
|
<div class="metric-info">
|
||||||
|
<div class="metric-value">
|
||||||
|
{{ activeCalls }}
|
||||||
|
<a-icon :class="['trend-icon', activeCallsChange >= 0 ? 'up' : 'down']"
|
||||||
|
:type="activeCallsChange >= 0 ? 'arrow-up' : 'arrow-down'" />
|
||||||
|
</div>
|
||||||
|
<div class="metric-change">{{ activeCallsChange >= 0 ? '+' : '' }}{{ activeCallsChange }}% last 10s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :xs="24" :sm="24" :lg="8">
|
||||||
|
<a-card :bordered="false" class="metric-card"
|
||||||
|
@click="fnToRouter('NeConfig_2146', { neType: 'MF', treeName: 'agents' })">
|
||||||
|
<div class="card-title">{{ t('views.dashboard.overview.onlineUser') }}</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="trend-chart">
|
||||||
|
<TrendChart :data="mosData" color="#2196F3" />
|
||||||
|
</div>
|
||||||
|
<div class="metric-info">
|
||||||
|
<div class="metric-value">
|
||||||
|
{{ onlineCount }}
|
||||||
|
<a-icon :class="['trend-icon', onlineCountChange >= 0 ? 'up' : 'down']"
|
||||||
|
:type="onlineCountChange >= 0 ? 'arrow-up' : 'arrow-down'" />
|
||||||
|
</div>
|
||||||
|
<div class="metric-change">{{ onlineCountChange >= 0 ? '+' : '' }}{{ onlineCountChange }} last 10s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a-col :xs="24" :sm="24" :lg="8">
|
||||||
|
<a-card :bordered="false" class="metric-card" @click="fnToRouter('Callings_20001')">
|
||||||
|
<div class="card-title">{{ t('views.dashboard.overview.parallelUser') }}</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="trend-chart">
|
||||||
|
<TrendChart :data="failedCallsData" color="#F44336" />
|
||||||
|
</div>
|
||||||
|
<div class="metric-info">
|
||||||
|
<div class="metric-value">
|
||||||
|
{{ failedCallsCount }}
|
||||||
|
<a-icon :class="['trend-icon', failedCallsCountChange >= 0 ? 'up' : 'down']"
|
||||||
|
:type="failedCallsCountChange >= 0 ? 'arrow-up' : 'arrow-down'" />
|
||||||
|
</div>
|
||||||
|
<div class="metric-change">{{ failedCallsCountChange >= 0 ? '+' : '' }}{{ failedCallsCountChange }} last
|
||||||
|
10s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<div class="row-title" style="margin-top: 100px;"> {{ t('views.dashboard.overview.sysTitle') }} </div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a-row :gutter="[48, 48]">
|
||||||
|
<a-col :xs="24" :sm="24" :lg="6">
|
||||||
|
<a-card :bordered="false" class="metric-card">
|
||||||
|
<div class="card-title">{{ t('views.dashboard.overview.resources.neCpu') }}</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="trend-chart">
|
||||||
|
<TrendChart :data="networkCpuData" color="#FF9800" />
|
||||||
|
</div>
|
||||||
|
<div class="metric-info">
|
||||||
|
<div class="metric-value">
|
||||||
|
{{ currentNfCpuUsage }}%
|
||||||
|
<a-icon :class="['trend-icon', nfCpuChange >= 0 ? 'up' : 'down']"
|
||||||
|
:type="nfCpuChange >= 0 ? 'arrow-up' : 'arrow-down'" />
|
||||||
|
</div>
|
||||||
|
<div class="metric-change">{{ nfCpuChange >= 0 ? '+' : '' }}{{ nfCpuChange }}% last 10s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :xs="24" :sm="24" :lg="6">
|
||||||
|
<a-card :bordered="false" class="metric-card">
|
||||||
|
<div class="card-title">{{ t('views.dashboard.overview.resources.sysCpu') }}</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="trend-chart">
|
||||||
|
<TrendChart :data="systemCpuData" color="#9C27B0" />
|
||||||
|
</div>
|
||||||
|
<div class="metric-info">
|
||||||
|
<div class="metric-value">
|
||||||
|
{{ currentSysCpuUsage }}%
|
||||||
|
<a-icon :class="['trend-icon', sysCpuChange >= 0 ? 'up' : 'down']"
|
||||||
|
:type="sysCpuChange >= 0 ? 'arrow-up' : 'arrow-down'" />
|
||||||
|
</div>
|
||||||
|
<div class="metric-change">{{ sysCpuChange >= 0 ? '+' : '' }}{{ sysCpuChange }}% last 10s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
|
||||||
|
<a-col :xs="24" :sm="24" :lg="6">
|
||||||
|
<a-card :bordered="false" class="metric-card">
|
||||||
|
<div class="card-title">{{ t('views.dashboard.overview.resources.sysMem') }}</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="trend-chart">
|
||||||
|
<TrendChart :data="systemMemData" color="#CDDC39" />
|
||||||
|
</div>
|
||||||
|
<div class="metric-info">
|
||||||
|
<div class="metric-value">
|
||||||
|
{{ currentSysMemUsage }}%
|
||||||
|
<a-icon :class="['trend-icon', sysMemChange >= 0 ? 'up' : 'down']"
|
||||||
|
:type="sysMemChange >= 0 ? 'arrow-up' : 'arrow-down'" />
|
||||||
|
</div>
|
||||||
|
<div class="metric-change">{{ sysMemChange >= 0 ? '+' : '' }}{{ sysMemChange }}% last 10s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :xs="24" :sm="24" :lg="6">
|
||||||
|
<a-card :bordered="false" class="metric-card">
|
||||||
|
<div class="card-title">{{ t('views.dashboard.overview.resources.sysDisk') }}</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="trend-chart">
|
||||||
|
<TrendChart :data="systemStorageData" color="#00BCD4" />
|
||||||
|
</div>
|
||||||
|
<div class="metric-info">
|
||||||
|
<div class="metric-value">
|
||||||
|
{{ currentSysDiskUsage }}%
|
||||||
|
<a-icon :class="['trend-icon', sysDiskChange >= 0 ? 'up' : 'down']"
|
||||||
|
:type="sysDiskChange >= 0 ? 'arrow-up' : 'arrow-down'" />
|
||||||
|
</div>
|
||||||
|
<div class="metric-change">{{ sysDiskChange >= 0 ? '+' : '' }}{{ sysDiskChange }}% last 10s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.row-title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-left: 8px;
|
||||||
|
border-left: 3px solid #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-cards {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card {
|
||||||
|
border-radius: 8px;
|
||||||
|
height: 100%;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trend-chart {
|
||||||
|
flex: 1;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-info {
|
||||||
|
margin-left: 16px;
|
||||||
|
text-align: right;
|
||||||
|
min-width: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-value {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-change {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trend-icon {
|
||||||
|
margin-left: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.up {
|
||||||
|
color: #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.down {
|
||||||
|
color: #F44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
color: #2196F3;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<div class="trend-chart-container" ref="chartContainer"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, watch, PropType } from 'vue';
|
||||||
|
import * as echarts from 'echarts/core';
|
||||||
|
import { LineChart } from 'echarts/charts';
|
||||||
|
import { GridComponent } from 'echarts/components';
|
||||||
|
import { SVGRenderer } from 'echarts/renderers';
|
||||||
|
|
||||||
|
echarts.use([LineChart, GridComponent, SVGRenderer]);
|
||||||
|
|
||||||
|
// 定义props
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Array as PropType<number[]>,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: '#4CAF50'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const chartContainer = ref<HTMLElement | null>(null);
|
||||||
|
let chart: echarts.ECharts | null = null;
|
||||||
|
|
||||||
|
const initChart = () => {
|
||||||
|
if (!chartContainer.value) return;
|
||||||
|
|
||||||
|
chart = echarts.init(chartContainer.value);
|
||||||
|
|
||||||
|
const option = {
|
||||||
|
grid: {
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
bottom: 0
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
show: false,
|
||||||
|
data: ['40m', '', '', '', '', '', 'now']
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: props.data,
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
symbol: 'none',
|
||||||
|
lineStyle: {
|
||||||
|
color: props.color,
|
||||||
|
width: 2
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: props.color + '40' // 40% 透明度
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: props.color + '00' // 0% 透明度
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
animation: false
|
||||||
|
};
|
||||||
|
|
||||||
|
chart.setOption(option);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initChart();
|
||||||
|
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
chart?.resize();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.data, () => {
|
||||||
|
chart?.setOption({
|
||||||
|
series: [{ data: props.data }]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.trend-chart-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -16,6 +16,8 @@
|
|||||||
.column {
|
.column {
|
||||||
flex: 3;
|
flex: 3;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 边框 */
|
/* 边框 */
|
||||||
@@ -79,13 +81,14 @@
|
|||||||
.upfFlow .inner .chart {
|
.upfFlow .inner .chart {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin-top: 1rem;
|
margin-top: 0rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 网络拓扑 */
|
/* 网络拓扑 */
|
||||||
.topology {
|
.topology {
|
||||||
/* min-height: 27.8rem; */
|
/* min-height: 27.8rem; */
|
||||||
height: 56.4%;
|
height: 56.4%;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
.topology .inner h3 {
|
.topology .inner h3 {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -179,6 +182,7 @@
|
|||||||
.userActivity {
|
.userActivity {
|
||||||
/* min-height: 35.8rem; */
|
/* min-height: 35.8rem; */
|
||||||
height: 54.6%;
|
height: 54.6%;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
.userActivity .inner .chart {
|
.userActivity .inner .chart {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -259,6 +263,7 @@
|
|||||||
.alarmType {
|
.alarmType {
|
||||||
/* min-height: 25rem; */
|
/* min-height: 25rem; */
|
||||||
height: 46%;
|
height: 46%;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
.alarmType .inner .chart {
|
.alarmType .inner .chart {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -36,11 +36,6 @@ export function upfFlowParse(data: Record<string, string>) {
|
|||||||
upfFlowData.value.lineYDown.shift();
|
upfFlowData.value.lineYDown.shift();
|
||||||
upfFlowData.value.cap -= 1;
|
upfFlowData.value.cap -= 1;
|
||||||
}
|
}
|
||||||
// UPF-总流量数0天 当天24小时
|
|
||||||
upfTFParse('0', {
|
|
||||||
up: upfTotalFlow.value['0'].up + +data['UPF.03'],
|
|
||||||
down: upfTotalFlow.value['0'].down + +data['UPF.06'],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TFType = {
|
type TFType = {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { RESULT_CODE_ERROR } from '@/constants/result-constants';
|
import { RESULT_CODE_ERROR } from '@/constants/result-constants';
|
||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
import { onBeforeUnmount, onMounted } from 'vue';
|
import { onBeforeUnmount, ref } from 'vue';
|
||||||
import {
|
import {
|
||||||
eventListParse,
|
eventListParse,
|
||||||
eventItemParseAndPush,
|
eventItemParseAndPush,
|
||||||
@@ -12,9 +12,15 @@ import {
|
|||||||
upfFlowParse,
|
upfFlowParse,
|
||||||
upfTotalFlowReset,
|
upfTotalFlowReset,
|
||||||
} from './useUPFTotalFlow';
|
} from './useUPFTotalFlow';
|
||||||
import { topologyReset, neStateParse } from './useTopology';
|
import { topologyReset, neStateParse, neStateRequestMap } from './useTopology';
|
||||||
import PQueue from 'p-queue';
|
import PQueue from 'p-queue';
|
||||||
|
|
||||||
|
/**UPF-的Id */
|
||||||
|
export const upfWhoId = ref<any>('');
|
||||||
|
|
||||||
|
/**UPF-的RmUid */
|
||||||
|
export const upfWhoRmUid = ref<any>('');
|
||||||
|
|
||||||
/**websocket连接 */
|
/**websocket连接 */
|
||||||
export default function useWS() {
|
export default function useWS() {
|
||||||
const ws = new WS();
|
const ws = new WS();
|
||||||
@@ -27,13 +33,12 @@ export default function useWS() {
|
|||||||
|
|
||||||
/**接收数据后回调 */
|
/**接收数据后回调 */
|
||||||
function wsMessage(res: Record<string, any>) {
|
function wsMessage(res: Record<string, any>) {
|
||||||
// console.log(res);
|
//console.log(res);
|
||||||
const { code, requestId, data } = res;
|
const { code, requestId, data } = res;
|
||||||
if (code === RESULT_CODE_ERROR) {
|
if (code === RESULT_CODE_ERROR) {
|
||||||
console.warn(res.msg);
|
console.warn(res.msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 网元状态
|
// 网元状态
|
||||||
if (requestId && requestId.startsWith('neState')) {
|
if (requestId && requestId.startsWith('neState')) {
|
||||||
const neType = requestId.split('_')[1];
|
const neType = requestId.split('_')[1];
|
||||||
@@ -44,7 +49,7 @@ export default function useWS() {
|
|||||||
// 普通信息
|
// 普通信息
|
||||||
switch (requestId) {
|
switch (requestId) {
|
||||||
// AMF_UE会话事件
|
// AMF_UE会话事件
|
||||||
case 'amf_1010':
|
case 'amf_1010_001':
|
||||||
if (Array.isArray(data.rows)) {
|
if (Array.isArray(data.rows)) {
|
||||||
eventListParse('amf_ue', data);
|
eventListParse('amf_ue', data);
|
||||||
}
|
}
|
||||||
@@ -72,20 +77,19 @@ export default function useWS() {
|
|||||||
upfTFParse('30', data);
|
upfTFParse('30', data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 订阅组信息
|
// 订阅组信息
|
||||||
if (!data?.groupId) {
|
if (!data?.groupId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (data.groupId) {
|
switch (data.groupId) {
|
||||||
// kpiEvent 指标UPF
|
// kpiEvent 指标UPF
|
||||||
case '12_001':
|
case '10_UPF_' + upfWhoId.value:
|
||||||
if (data.data) {
|
if (data.data) {
|
||||||
upfFlowParse(data.data);
|
upfFlowParse(data.data);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// AMF_UE会话事件
|
// AMF_UE会话事件
|
||||||
case '1010':
|
case '1010_001':
|
||||||
if (data.data) {
|
if (data.data) {
|
||||||
queue.add(() => eventItemParseAndPush('amf_ue', data.data));
|
queue.add(() => eventItemParseAndPush('amf_ue', data.data));
|
||||||
}
|
}
|
||||||
@@ -128,7 +132,7 @@ export default function useWS() {
|
|||||||
function userActivitySend() {
|
function userActivitySend() {
|
||||||
// AMF_UE会话事件
|
// AMF_UE会话事件
|
||||||
ws.send({
|
ws.send({
|
||||||
requestId: 'amf_1010',
|
requestId: 'amf_1010_001',
|
||||||
type: 'amf_ue',
|
type: 'amf_ue',
|
||||||
data: {
|
data: {
|
||||||
neType: 'AMF',
|
neType: 'AMF',
|
||||||
@@ -168,18 +172,28 @@ export default function useWS() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
/**重新发送至UPF 10_UPF_neId */
|
||||||
|
function reSendUPF(neId: string) {
|
||||||
|
upfWhoId.value = neId;
|
||||||
|
//初始时时无需还原全部属性以及关闭
|
||||||
|
if (ws.state() === WebSocket.OPEN) {
|
||||||
|
ws.close();
|
||||||
|
// userActivityReset();
|
||||||
|
upfTotalFlowReset();
|
||||||
|
neStateRequestMap.value = new Map();
|
||||||
|
//topologyReset();
|
||||||
|
}
|
||||||
const options: OptionsType = {
|
const options: OptionsType = {
|
||||||
url: '/ws',
|
url: '/ws',
|
||||||
params: {
|
params: {
|
||||||
/**订阅通道组
|
/**订阅通道组
|
||||||
*
|
*
|
||||||
* 指标UPF (GroupID:12_neId)
|
* 指标UPF (GroupID:10_neType_neId)
|
||||||
* AMF_UE会话事件(GroupID:1010)
|
* AMF_UE会话事件(GroupID:1010_neId)
|
||||||
* MME_UE会话事件(GroupID:1011_neId)
|
* MME_UE会话事件(GroupID:1011_neId)
|
||||||
* IMS_CDR会话事件(GroupID:1005_neId)
|
* IMS_CDR会话事件(GroupID:1005_neId)
|
||||||
*/
|
*/
|
||||||
subGroupID: '12_001,1010,1011_001,1005_001',
|
subGroupID: '10_UPF_' + neId + ',1010_001,1011_001,1005_001',
|
||||||
},
|
},
|
||||||
onmessage: wsMessage,
|
onmessage: wsMessage,
|
||||||
onerror: (ev: any) => {
|
onerror: (ev: any) => {
|
||||||
@@ -187,18 +201,20 @@ export default function useWS() {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
ws.connect(options);
|
ws.connect(options);
|
||||||
});
|
}
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
ws.close();
|
ws.close();
|
||||||
userActivityReset();
|
userActivityReset();
|
||||||
upfTotalFlowReset();
|
upfTotalFlowReset();
|
||||||
topologyReset();
|
topologyReset();
|
||||||
|
upfWhoId.value = '';
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
wsSend,
|
wsSend,
|
||||||
userActivitySend,
|
userActivitySend,
|
||||||
upfTFSend,
|
upfTFSend,
|
||||||
|
reSendUPF,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1,491 +1,30 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onBeforeUnmount, onMounted, reactive, ref } from 'vue';
|
import { onBeforeUnmount, onMounted, reactive, ref } from 'vue';
|
||||||
import svgBase from '@/assets/svg/base.svg';
|
|
||||||
import svgUserIMS from '@/assets/svg/userIMS.svg';
|
|
||||||
import svgUserSMF from '@/assets/svg/userSMF.svg';
|
|
||||||
import useI18n from '@/hooks/useI18n';
|
|
||||||
import Topology from './components/Topology/index.vue';
|
|
||||||
import NeResources from './components/NeResources/index.vue';
|
|
||||||
import UserActivity from './components/UserActivity/index.vue';
|
|
||||||
import AlarnTypeBar from './components/AlarnTypeBar/index.vue';
|
|
||||||
import UPFFlow from './components/UPFFlow/index.vue';
|
|
||||||
import { listUDMSub } from '@/api/neData/udm_sub';
|
|
||||||
import { listUENumBySMF } from '@/api/neUser/smf';
|
|
||||||
import { listUENumByIMS } from '@/api/neUser/ims';
|
|
||||||
import { listBase5G } from '@/api/neUser/base5G';
|
|
||||||
import {
|
|
||||||
graphNodeClickID,
|
|
||||||
graphState,
|
|
||||||
notNeNodes,
|
|
||||||
graphNodeStateNum,
|
|
||||||
neStateRequestMap,
|
|
||||||
} from './hooks/useTopology';
|
|
||||||
import { upfTotalFlow, upfTFActive } from './hooks/useUPFTotalFlow';
|
|
||||||
import { useFullscreen } from '@vueuse/core';
|
|
||||||
import useWS from './hooks/useWS';
|
|
||||||
import useAppStore from '@/store/modules/app';
|
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
const router = useRouter();
|
|
||||||
const appStore = useAppStore();
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { wsSend, userActivitySend, upfTFSend } = useWS();
|
|
||||||
|
|
||||||
/**概览状态类型 */
|
|
||||||
type SkimStateType = {
|
|
||||||
/**UDM签约用户数量 */
|
|
||||||
udmSubNum: number;
|
|
||||||
/**SMF在线用户数 */
|
|
||||||
smfUeNum: number;
|
|
||||||
/**IMS在线用户数 */
|
|
||||||
imsUeNum: number;
|
|
||||||
/**5G基站数量 */
|
|
||||||
gnbNum: number;
|
|
||||||
/**5G在线用户数量 */
|
|
||||||
gnbUeNum: number;
|
|
||||||
/**4G基站数量 */
|
|
||||||
enbNum: number;
|
|
||||||
/**4G在线用户数量 */
|
|
||||||
enbUeNum: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**概览状态信息 */
|
|
||||||
let skimState: SkimStateType = reactive({
|
|
||||||
udmSubNum: 0,
|
|
||||||
smfUeNum: 0,
|
|
||||||
imsUeNum: 0,
|
|
||||||
gnbNum: 0,
|
|
||||||
gnbUeNum: 0,
|
|
||||||
enbNum: 0,
|
|
||||||
enbUeNum: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**总览节点 */
|
import DashboardCards from './components/mfOverview/DashboardCards.vue';
|
||||||
const viewportDom = ref<HTMLElement | null>(null);
|
|
||||||
const { isFullscreen, toggle } = useFullscreen(viewportDom);
|
|
||||||
|
|
||||||
/**10s调度器 */
|
|
||||||
const interval10s = ref<any>(null);
|
|
||||||
|
|
||||||
/**5s调度器 */
|
|
||||||
const interval5s = ref<any>(null);
|
|
||||||
|
|
||||||
/**查询网元状态 */
|
|
||||||
function fnGetNeState() {
|
|
||||||
// 获取节点状态
|
|
||||||
for (const node of graphState.data.nodes) {
|
|
||||||
if (notNeNodes.includes(node.id)) continue;
|
|
||||||
const { neType, neId } = node.neInfo;
|
|
||||||
if (!neType || !neId) continue;
|
|
||||||
// 请求标记检查避免重复发送
|
|
||||||
if (neStateRequestMap.value.get(neType)) continue;
|
|
||||||
neStateRequestMap.value.set(neType, true);
|
|
||||||
|
|
||||||
wsSend({
|
|
||||||
requestId: `neState_${neType}_${neId}`,
|
|
||||||
type: 'ne_state',
|
|
||||||
data: {
|
|
||||||
neType: neType,
|
|
||||||
neId: neId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**获取概览信息 */
|
|
||||||
async function fnGetSkim() {
|
|
||||||
const resArr = await Promise.allSettled([
|
|
||||||
listUDMSub({
|
|
||||||
neid: '001',
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 1,
|
|
||||||
}),
|
|
||||||
listUENumBySMF('001'),
|
|
||||||
listUENumByIMS('001'),
|
|
||||||
listBase5G({
|
|
||||||
neType: 'AMF',
|
|
||||||
neId: '001',
|
|
||||||
}),
|
|
||||||
listBase5G({
|
|
||||||
neType: 'MME',
|
|
||||||
neId: '001',
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (resArr[0].status === 'fulfilled') {
|
|
||||||
const res0 = resArr[0].value;
|
|
||||||
if (res0.code === RESULT_CODE_SUCCESS) {
|
|
||||||
skimState.udmSubNum = res0.total;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (resArr[1].status === 'fulfilled') {
|
|
||||||
const res1 = resArr[1].value;
|
|
||||||
if (res1.code === RESULT_CODE_SUCCESS) {
|
|
||||||
skimState.smfUeNum = res1.data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (resArr[2].status === 'fulfilled') {
|
|
||||||
const res2 = resArr[2].value;
|
|
||||||
if (res2.code === RESULT_CODE_SUCCESS) {
|
|
||||||
skimState.imsUeNum = res2.data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (resArr[3].status === 'fulfilled') {
|
|
||||||
const res3 = resArr[3].value;
|
|
||||||
if (res3.code === RESULT_CODE_SUCCESS) {
|
|
||||||
skimState.gnbNum = res3.total;
|
|
||||||
skimState.gnbUeNum = 0;
|
|
||||||
res3.rows.map((item: any) => {
|
|
||||||
skimState.gnbUeNum += item.ueNum;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (resArr[4].status === 'fulfilled') {
|
|
||||||
const res4 = resArr[4].value;
|
|
||||||
if (res4.code === RESULT_CODE_SUCCESS) {
|
|
||||||
skimState.enbNum = res4.total;
|
|
||||||
skimState.enbUeNum = 0;
|
|
||||||
res4.rows.map((item: any) => {
|
|
||||||
skimState.enbUeNum += item.ueNum;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**初始数据函数 */
|
|
||||||
function loadData() {
|
|
||||||
fnGetNeState(); // 获取网元状态
|
|
||||||
userActivitySend();
|
|
||||||
upfTFSend('0');
|
|
||||||
upfTFSend('7');
|
|
||||||
upfTFSend('30');
|
|
||||||
|
|
||||||
clearInterval(interval10s.value);
|
|
||||||
interval10s.value = setInterval(() => {
|
|
||||||
if (!interval10s.value) return
|
|
||||||
if (upfTFActive.value === '0') {
|
|
||||||
upfTFSend('7');
|
|
||||||
upfTFActive.value = '7';
|
|
||||||
} else if (upfTFActive.value === '7') {
|
|
||||||
upfTFSend('30');
|
|
||||||
upfTFActive.value = '30';
|
|
||||||
} else if (upfTFActive.value === '30') {
|
|
||||||
upfTFSend('0');
|
|
||||||
upfTFActive.value = '0';
|
|
||||||
}
|
|
||||||
}, 10_000);
|
|
||||||
|
|
||||||
clearInterval(interval5s.value);
|
|
||||||
interval5s.value = setInterval(() => {
|
|
||||||
if (!interval5s.value) return
|
|
||||||
fnGetSkim(); // 获取概览信息
|
|
||||||
fnGetNeState(); // 获取网元状态
|
|
||||||
}, 5_000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**栏目信息跳转 */
|
|
||||||
function fnToRouter(name: string, query?: any) {
|
|
||||||
router.push({ name, query });
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fnGetSkim().then(() => {
|
|
||||||
loadData();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
clearInterval(interval10s.value);
|
|
||||||
interval10s.value = null;
|
|
||||||
clearInterval(interval5s.value);
|
|
||||||
interval5s.value = null;
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="viewport" ref="viewportDom">
|
<div class="dashboard-page">
|
||||||
<div class="brand">
|
<DashboardCards />
|
||||||
<div
|
|
||||||
class="brand-title"
|
|
||||||
@click="toggle"
|
|
||||||
:title="t('views.dashboard.overview.fullscreen')"
|
|
||||||
>
|
|
||||||
{{ t('views.dashboard.overview.title') }}
|
|
||||||
<FullscreenExitOutlined v-if="isFullscreen" />
|
|
||||||
<FullscreenOutlined v-else />
|
|
||||||
</div>
|
|
||||||
<div class="brand-desc">{{ appStore.appName }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="column">
|
|
||||||
<!--概览-->
|
|
||||||
<div class="skim panel">
|
|
||||||
<div class="inner">
|
|
||||||
<h3>
|
|
||||||
<IdcardOutlined style="color: #68d8fe" />
|
|
||||||
{{ t('views.dashboard.overview.skim.userTitle') }}
|
|
||||||
</h3>
|
|
||||||
<div class="data">
|
|
||||||
<div
|
|
||||||
class="item toRouter"
|
|
||||||
@click="fnToRouter('Sub_2010')"
|
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<UserOutlined
|
|
||||||
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
|
|
||||||
/>
|
|
||||||
{{ skimState.udmSubNum }}
|
|
||||||
</div>
|
|
||||||
<span>
|
|
||||||
{{ t('views.dashboard.overview.skim.users') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="item toRouter"
|
|
||||||
@click="fnToRouter('Ims_2080')"
|
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
|
||||||
style="margin: 0 12px"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<img :src="svgUserIMS" style="width: 18px; margin-right: 8px" />
|
|
||||||
{{ skimState.imsUeNum }}
|
|
||||||
</div>
|
|
||||||
<span>
|
|
||||||
{{ t('views.dashboard.overview.skim.imsUeNum') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="item toRouter"
|
|
||||||
@click="fnToRouter('Ue_2081')"
|
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<img :src="svgUserSMF" style="width: 18px; margin-right: 8px" />
|
|
||||||
{{ skimState.smfUeNum }}
|
|
||||||
</div>
|
|
||||||
<span>
|
|
||||||
{{ t('views.dashboard.overview.skim.smfUeNum') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="skim panel base">
|
|
||||||
<div class="inner">
|
|
||||||
<h3>
|
|
||||||
<GlobalOutlined style="color: #68d8fe" /> 5G
|
|
||||||
{{ t('views.dashboard.overview.skim.baseTitle') }}
|
|
||||||
</h3>
|
|
||||||
<div class="data">
|
|
||||||
<div
|
|
||||||
class="item toRouter"
|
|
||||||
@click="fnToRouter('Base5G_2082', { neType: 'AMF' })"
|
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
|
||||||
>
|
|
||||||
<div style="align-items: flex-start">
|
|
||||||
<img
|
|
||||||
:src="svgBase"
|
|
||||||
style="width: 18px; margin-right: 8px; height: 2rem"
|
|
||||||
/>
|
|
||||||
{{ skimState.gnbNum }}
|
|
||||||
</div>
|
|
||||||
<span>{{ t('views.dashboard.overview.skim.gnbBase') }}</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="item toRouter"
|
|
||||||
@click="fnToRouter('Base5G_2082', { neType: 'AMF' })"
|
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
|
||||||
>
|
|
||||||
<div style="align-items: flex-start">
|
|
||||||
<UserOutlined
|
|
||||||
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
|
|
||||||
/>
|
|
||||||
{{ skimState.gnbUeNum }}
|
|
||||||
</div>
|
|
||||||
<span>{{ t('views.dashboard.overview.skim.gnbUeNum') }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="skim panel base">
|
|
||||||
<div class="inner">
|
|
||||||
<h3>
|
|
||||||
<GlobalOutlined style="color: #68d8fe" /> 4G
|
|
||||||
{{ t('views.dashboard.overview.skim.baseTitle') }}
|
|
||||||
</h3>
|
|
||||||
<div class="data">
|
|
||||||
<div
|
|
||||||
class="item toRouter"
|
|
||||||
@click="fnToRouter('Base5G_2082', { neType: 'MME' })"
|
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
|
||||||
>
|
|
||||||
<div style="align-items: flex-start">
|
|
||||||
<img
|
|
||||||
:src="svgBase"
|
|
||||||
style="width: 18px; margin-right: 8px; height: 2rem"
|
|
||||||
/>
|
|
||||||
{{ skimState.enbNum }}
|
|
||||||
</div>
|
|
||||||
<span>{{ t('views.dashboard.overview.skim.enbBase') }}</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="item toRouter"
|
|
||||||
@click="fnToRouter('Base5G_2082', { neType: 'MME' })"
|
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
|
||||||
>
|
|
||||||
<div style="align-items: flex-start">
|
|
||||||
<UserOutlined
|
|
||||||
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
|
|
||||||
/>
|
|
||||||
{{ skimState.enbUeNum }}
|
|
||||||
</div>
|
|
||||||
<span>{{ t('views.dashboard.overview.skim.enbUeNum') }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 用户行为 -->
|
|
||||||
<div class="userActivity panel">
|
|
||||||
<div class="inner">
|
|
||||||
<h3>
|
|
||||||
<WhatsAppOutlined style="color: #68d8fe" />
|
|
||||||
{{ t('views.dashboard.overview.userActivity.title') }}
|
|
||||||
</h3>
|
|
||||||
<div class="chart">
|
|
||||||
<UserActivity />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="column" style="flex: 4; margin: 1.333rem 0.833rem 0">
|
|
||||||
<!-- 实时流量 -->
|
|
||||||
<div class="upfFlow panel">
|
|
||||||
<div class="inner">
|
|
||||||
<h3
|
|
||||||
class="toRouter"
|
|
||||||
@click="fnToRouter('GoldTarget_2104')"
|
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
|
||||||
>
|
|
||||||
<AreaChartOutlined style="color: #68d8fe" />
|
|
||||||
{{ t('views.dashboard.overview.upfFlow.title') }}
|
|
||||||
</h3>
|
|
||||||
<div class="chart">
|
|
||||||
<UPFFlow />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 网络拓扑 -->
|
|
||||||
<div class="topology panel">
|
|
||||||
<div class="inner">
|
|
||||||
<h3
|
|
||||||
class="toRouter"
|
|
||||||
@click="fnToRouter('TopologyArchitecture_2128')"
|
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
<ApartmentOutlined style="color: #68d8fe" />
|
|
||||||
{{ t('views.dashboard.overview.topology.title') }}
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
{{ t('views.dashboard.overview.topology.normal') }}:
|
|
||||||
<span class="normal"> {{ graphNodeStateNum[0] }} </span>
|
|
||||||
{{ t('views.dashboard.overview.topology.abnormal') }}:
|
|
||||||
<span class="abnormal"> {{ graphNodeStateNum[1] }} </span>
|
|
||||||
</span>
|
|
||||||
</h3>
|
|
||||||
<div class="chart">
|
|
||||||
<Topology />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="column">
|
|
||||||
<!-- 流量统计 -->
|
|
||||||
<div class="upfFlowTotal panel">
|
|
||||||
<div class="inner">
|
|
||||||
<h3>
|
|
||||||
<span>
|
|
||||||
<SwapOutlined style="color: #68d8fe" />
|
|
||||||
{{ t('views.dashboard.overview.upfFlowTotal.title') }}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<!-- 筛选 -->
|
|
||||||
<div class="filter">
|
|
||||||
<span
|
|
||||||
:data-key="v"
|
|
||||||
:class="{ active: upfTFActive === v }"
|
|
||||||
v-for="v in ['0', '7', '30']"
|
|
||||||
:key="v"
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
upfTFActive = v;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
v === '0'
|
|
||||||
? '24' + t('common.units.hour')
|
|
||||||
: v + t('common.units.day')
|
|
||||||
}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</h3>
|
|
||||||
<div class="chart">
|
|
||||||
<!-- 数据 -->
|
|
||||||
<div class="data">
|
|
||||||
<div class="item">
|
|
||||||
<span>
|
|
||||||
<ArrowUpOutlined style="color: #597ef7" />
|
|
||||||
{{ t('views.dashboard.overview.upfFlowTotal.up') }}
|
|
||||||
</span>
|
|
||||||
<h4>{{ upfTotalFlow[upfTFActive].upFrom }}</h4>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<span>
|
|
||||||
<ArrowDownOutlined style="color: #52c41a" />
|
|
||||||
{{ t('views.dashboard.overview.upfFlowTotal.down') }}
|
|
||||||
</span>
|
|
||||||
<h4>{{ upfTotalFlow[upfTFActive].downFrom }}</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 告警统计 -->
|
|
||||||
<div class="alarmType panel">
|
|
||||||
<div class="inner">
|
|
||||||
<h3
|
|
||||||
class="toRouter"
|
|
||||||
@click="fnToRouter('HistoryAlarm_2097')"
|
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
|
||||||
>
|
|
||||||
<PieChartOutlined style="color: #68d8fe" />
|
|
||||||
{{ t('views.dashboard.overview.alarmTypeBar.alarmSum') }}
|
|
||||||
</h3>
|
|
||||||
<div class="chart">
|
|
||||||
<AlarnTypeBar />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 资源情况 -->
|
|
||||||
<div class="resources panel">
|
|
||||||
<div class="inner">
|
|
||||||
<h3>
|
|
||||||
<DashboardOutlined style="color: #68d8fe" />
|
|
||||||
{{ t('views.dashboard.overview.resources.title') }}:
|
|
||||||
{{ graphNodeClickID }}
|
|
||||||
</h3>
|
|
||||||
<div class="chart">
|
|
||||||
<NeResources />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
.cardClass {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
@import url('./css/index.css');
|
@import url('./css/index.css');
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,850 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, toRaw, onBeforeUnmount, ref } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { Modal, message } from 'ant-design-vue/es';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import {
|
||||||
|
RESULT_CODE_ERROR,
|
||||||
|
RESULT_CODE_SUCCESS,
|
||||||
|
} from '@/constants/result-constants';
|
||||||
|
import {
|
||||||
|
delSGWCDataCDR,
|
||||||
|
exportSGWCDataCDR,
|
||||||
|
listSGWCDataCDR,
|
||||||
|
} from '@/api/neData/sgwc';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import PQueue from 'p-queue';
|
||||||
|
import saveAs from 'file-saver';
|
||||||
|
import { useClipboard } from '@vueuse/core';
|
||||||
|
import dayjs, { type Dayjs } from 'dayjs';
|
||||||
|
const { copy } = useClipboard({ legacy: true });
|
||||||
|
const { t } = useI18n();
|
||||||
|
const ws = new WS();
|
||||||
|
const queue = new PQueue({ concurrency: 1, autoStart: true });
|
||||||
|
|
||||||
|
/**网元可选 */
|
||||||
|
let neOtions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
|
/**开始结束时间 */
|
||||||
|
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([
|
||||||
|
dayjs().startOf('hour'),
|
||||||
|
dayjs().endOf('hour'),
|
||||||
|
]);
|
||||||
|
/**时间范围 */
|
||||||
|
let rangePickerPresets = ref([
|
||||||
|
{
|
||||||
|
label: 'Now hour',
|
||||||
|
value: [dayjs().startOf('hour'), dayjs().endOf('hour')],
|
||||||
|
},
|
||||||
|
{ label: 'Today', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
|
||||||
|
{
|
||||||
|
label: 'Yesterday',
|
||||||
|
value: [
|
||||||
|
dayjs().subtract(1, 'day').startOf('day'),
|
||||||
|
dayjs().subtract(1, 'day').endOf('day'),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元类型 */
|
||||||
|
neType: 'SGWC',
|
||||||
|
neId: '001',
|
||||||
|
imsi: '',
|
||||||
|
msisdn: '',
|
||||||
|
sortField: 'timestamp',
|
||||||
|
sortOrder: 'desc',
|
||||||
|
/**开始时间 */
|
||||||
|
startTime: undefined as undefined | number,
|
||||||
|
/**结束时间 */
|
||||||
|
endTime: undefined as undefined | number,
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
queryParams = Object.assign(queryParams, {
|
||||||
|
imsi: '',
|
||||||
|
msisdn: '',
|
||||||
|
startTime: undefined,
|
||||||
|
endTime: undefined,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')];
|
||||||
|
tablePagination.current = 1;
|
||||||
|
tablePagination.pageSize = 20;
|
||||||
|
fnGetList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**搜索栏 */
|
||||||
|
seached: boolean;
|
||||||
|
/**记录数据 */
|
||||||
|
data: object[];
|
||||||
|
/**勾选记录 */
|
||||||
|
selectedRowKeys: (string | number)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
size: 'middle',
|
||||||
|
seached: true,
|
||||||
|
data: [],
|
||||||
|
selectedRowKeys: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
let tableColumns: ColumnsType = [
|
||||||
|
{
|
||||||
|
title: t('common.rowId'),
|
||||||
|
dataIndex: 'id',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.chargingID'), // 计费ID
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.chargingID;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.sgwcServedIMSI'), // IMSI
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.servedIMSI;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.sgwcServedMSISDN'), // MSISDN
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.servedMSISDN;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.sgwcVolumeGPRSUplink'), // GPRS 上行链路
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
const listOfTrafficVolumes = cdrJSON.listOfTrafficVolumes;
|
||||||
|
if (
|
||||||
|
!Array.isArray(listOfTrafficVolumes) ||
|
||||||
|
listOfTrafficVolumes.length < 1
|
||||||
|
) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let dataVolumeGPRSUplink = 0;
|
||||||
|
for (const used of listOfTrafficVolumes) {
|
||||||
|
const v = +used.dataVolumeGPRSUplink;
|
||||||
|
dataVolumeGPRSUplink += isNaN(v) ? 0 : v;
|
||||||
|
}
|
||||||
|
return dataVolumeGPRSUplink;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.sgwcVolumeGPRSDownlink'), // GPRS 下行链路
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 180,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
const listOfTrafficVolumes = cdrJSON.listOfTrafficVolumes;
|
||||||
|
if (
|
||||||
|
!Array.isArray(listOfTrafficVolumes) ||
|
||||||
|
listOfTrafficVolumes.length < 1
|
||||||
|
) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let dataVolumeGPRSDownlink = 0;
|
||||||
|
for (const used of listOfTrafficVolumes) {
|
||||||
|
const v = +used.dataVolumeGPRSDownlink;
|
||||||
|
dataVolumeGPRSDownlink += isNaN(v) ? 0 : v;
|
||||||
|
}
|
||||||
|
return dataVolumeGPRSDownlink;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.durationTime'), // 持续时间
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.duration;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.invocationTime'), // 操作时间
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 200,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.recordOpeningTime;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('common.operate'),
|
||||||
|
key: 'id',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
let tablePagination = reactive({
|
||||||
|
/**当前页数 */
|
||||||
|
current: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
/**默认的每页条数 */
|
||||||
|
defaultPageSize: 20,
|
||||||
|
/**指定每页可以显示多少条 */
|
||||||
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
|
/**只有一页时是否隐藏分页器 */
|
||||||
|
hideOnSinglePage: false,
|
||||||
|
/**是否可以快速跳转至某页 */
|
||||||
|
showQuickJumper: true,
|
||||||
|
/**是否可以改变 pageSize */
|
||||||
|
showSizeChanger: true,
|
||||||
|
/**数据总数 */
|
||||||
|
total: 0,
|
||||||
|
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
|
||||||
|
onChange: (page: number, pageSize: number) => {
|
||||||
|
tablePagination.current = page;
|
||||||
|
tablePagination.pageSize = pageSize;
|
||||||
|
queryParams.pageNum = page;
|
||||||
|
queryParams.pageSize = pageSize;
|
||||||
|
fnGetList();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格紧凑型变更操作 */
|
||||||
|
function fnTableSize({ key }: MenuInfo) {
|
||||||
|
tableState.size = key as SizeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格多选 */
|
||||||
|
function fnTableSelectedRowKeys(keys: (string | number)[]) {
|
||||||
|
tableState.selectedRowKeys = keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
/**最大ID值 */
|
||||||
|
maxId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
confirmLoading: false,
|
||||||
|
maxId: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录删除
|
||||||
|
* @param id 编号
|
||||||
|
*/
|
||||||
|
function fnRecordDelete(id: string) {
|
||||||
|
if (!id || modalState.confirmLoading) return;
|
||||||
|
let msg = id;
|
||||||
|
if (id === '0') {
|
||||||
|
msg = `${id}... ${tableState.selectedRowKeys.length}`;
|
||||||
|
id = tableState.selectedRowKeys.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.dashboard.cdr.delTip', { msg }),
|
||||||
|
onOk() {
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
delSGWCDataCDR(id)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnGetList(1);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制CDR
|
||||||
|
* @param jsonStr JSON字符串
|
||||||
|
*/
|
||||||
|
function fnRecordCopy(jsonStr: string) {
|
||||||
|
if (!jsonStr) return;
|
||||||
|
const text = JSON.stringify(jsonStr, null, 2);
|
||||||
|
copy(text).then(() => {
|
||||||
|
message.success(t('common.copyOk'), 3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
// 时间范围
|
||||||
|
if (
|
||||||
|
Array.isArray(queryRangePicker.value) &&
|
||||||
|
queryRangePicker.value.length > 0
|
||||||
|
) {
|
||||||
|
queryParams.startTime = queryRangePicker.value[0].valueOf();
|
||||||
|
queryParams.endTime = queryRangePicker.value[1].valueOf();
|
||||||
|
} else {
|
||||||
|
queryParams.startTime = undefined;
|
||||||
|
queryParams.endTime = undefined;
|
||||||
|
}
|
||||||
|
listSGWCDataCDR(toRaw(queryParams)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
|
// 取消勾选
|
||||||
|
if (tableState.selectedRowKeys.length > 0) {
|
||||||
|
tableState.selectedRowKeys = [];
|
||||||
|
}
|
||||||
|
tablePagination.total = res.total;
|
||||||
|
// 遍历处理cdr字符串数据
|
||||||
|
tableState.data = res.rows.map(item => {
|
||||||
|
let cdrJSON = item.cdrJSON;
|
||||||
|
if (!cdrJSON) {
|
||||||
|
Reflect.set(item, 'cdrJSON', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
cdrJSON = JSON.parse(cdrJSON);
|
||||||
|
Reflect.set(item, 'cdrJSON', cdrJSON);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
Reflect.set(item, 'cdrJSON', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 取最大值ID用作实时累加
|
||||||
|
if (res.total > 0) {
|
||||||
|
modalState.maxId = Number(res.rows[0].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**列表导出 */
|
||||||
|
function fnExportList() {
|
||||||
|
if (modalState.confirmLoading) return;
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.dashboard.cdr.exportTip'),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
const querys = toRaw(queryParams);
|
||||||
|
querys.pageSize = 10000;
|
||||||
|
exportSGWCDataCDR(querys)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
saveAs(res.data, `sgwc_cdr_event_export_${Date.now()}.xlsx`);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**实时数据开关 */
|
||||||
|
const realTimeData = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实时数据
|
||||||
|
*/
|
||||||
|
function fnRealTime() {
|
||||||
|
realTimeData.value = !realTimeData.value;
|
||||||
|
if (realTimeData.value) {
|
||||||
|
tableState.seached = false;
|
||||||
|
// 建立链接
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: '/ws',
|
||||||
|
params: {
|
||||||
|
/**订阅通道组
|
||||||
|
*
|
||||||
|
* CDR会话事件-SGWC (GroupID:1008)
|
||||||
|
*/
|
||||||
|
subGroupID: `1008_${queryParams.neId}`,
|
||||||
|
},
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: (ev: any) => {
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
ws.connect(options);
|
||||||
|
} else {
|
||||||
|
ws.close();
|
||||||
|
tableState.seached = true;
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsMessage(res: Record<string, any>) {
|
||||||
|
const { code, requestId, data } = res;
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅组信息
|
||||||
|
if (!data?.groupId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// cdrEvent CDR会话事件
|
||||||
|
if (data.groupId === `1008_${queryParams.neId}`) {
|
||||||
|
const cdrEvent = data.data;
|
||||||
|
queue.add(async () => {
|
||||||
|
modalState.maxId += 1;
|
||||||
|
tableState.data.unshift({
|
||||||
|
id: modalState.maxId,
|
||||||
|
neType: cdrEvent.neType,
|
||||||
|
neName: cdrEvent.neName,
|
||||||
|
rmUID: cdrEvent.rmUID,
|
||||||
|
timestamp: cdrEvent.timestamp,
|
||||||
|
cdrJSON: cdrEvent.CDR,
|
||||||
|
});
|
||||||
|
tablePagination.total += 1;
|
||||||
|
if (tableState.data.length > 100) {
|
||||||
|
tableState.data.pop();
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 800));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 获取网元网元列表
|
||||||
|
useNeInfoStore()
|
||||||
|
.fnNelist()
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length > 0) {
|
||||||
|
let arr: Record<string, any>[] = [];
|
||||||
|
res.data.forEach(i => {
|
||||||
|
if (i.neType === 'SGWC') {
|
||||||
|
arr.push({ value: i.neId, label: i.neName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neOtions.value = arr;
|
||||||
|
if (arr.length > 0) {
|
||||||
|
queryParams.neId = arr[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// 获取列表数据
|
||||||
|
fnGetList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (ws.state() !== -1) {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card
|
||||||
|
v-show="tableState.seached"
|
||||||
|
:bordered="false"
|
||||||
|
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
||||||
|
>
|
||||||
|
<!-- 表格搜索栏 -->
|
||||||
|
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item label="SGWC" name="neId ">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.neId"
|
||||||
|
:options="neOtions"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnQueryReset()"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.sgwcServedIMSI')"
|
||||||
|
name="imsi"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.imsi"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="40"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.sgwcServedMSISDN')"
|
||||||
|
name="msisdn"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.msisdn"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="40"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="4" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="fnQueryReset">
|
||||||
|
<template #icon><ClearOutlined /></template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.time')"
|
||||||
|
name="queryRangePicker"
|
||||||
|
>
|
||||||
|
<a-range-picker
|
||||||
|
v-model:value="queryRangePicker"
|
||||||
|
:presets="rangePickerPresets"
|
||||||
|
:bordered="true"
|
||||||
|
:allow-clear="false"
|
||||||
|
style="width: 100%"
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
></a-range-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-popconfirm
|
||||||
|
placement="bottomLeft"
|
||||||
|
:title="
|
||||||
|
!realTimeData
|
||||||
|
? t('views.dashboard.cdr.realTimeDataStart')
|
||||||
|
: t('views.dashboard.cdr.realTimeDataStop')
|
||||||
|
"
|
||||||
|
ok-text="Yes"
|
||||||
|
cancel-text="No"
|
||||||
|
@confirm="fnRealTime()"
|
||||||
|
>
|
||||||
|
<a-button type="primary" :danger="realTimeData">
|
||||||
|
<template #icon><FundOutlined /> </template>
|
||||||
|
{{
|
||||||
|
!realTimeData
|
||||||
|
? t('views.dashboard.cdr.realTimeDataStart')
|
||||||
|
: t('views.dashboard.cdr.realTimeDataStop')
|
||||||
|
}}
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
danger
|
||||||
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
|
:loading="modalState.confirmLoading"
|
||||||
|
@click.prevent="fnRecordDelete('0')"
|
||||||
|
v-perms:has="['cdr:ne:remove']"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
{{ t('common.deleteText') }}
|
||||||
|
</a-button>
|
||||||
|
|
||||||
|
<a-button type="dashed" @click.prevent="fnExportList()">
|
||||||
|
<template #icon><ExportOutlined /></template>
|
||||||
|
{{ t('common.export') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.searchBarText') }}</template>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="tableState.seached"
|
||||||
|
:checked-children="t('common.switch.show')"
|
||||||
|
:un-checked-children="t('common.switch.hide')"
|
||||||
|
size="small"
|
||||||
|
:disabled="realTimeData"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.sizeText') }}</template>
|
||||||
|
<a-dropdown trigger="click" placement="bottomRight">
|
||||||
|
<a-button type="text">
|
||||||
|
<template #icon><ColumnHeightOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu
|
||||||
|
:selected-keys="[tableState.size as string]"
|
||||||
|
@click="fnTableSize"
|
||||||
|
>
|
||||||
|
<a-menu-item key="default">
|
||||||
|
{{ t('common.size.default') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="middle">
|
||||||
|
{{ t('common.size.middle') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="small">
|
||||||
|
{{ t('common.size.small') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="id"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: tableColumns.length * 150, y: 'calc(100vh - 480px)' }"
|
||||||
|
:row-selection="{
|
||||||
|
type: 'checkbox',
|
||||||
|
columnWidth: '48px',
|
||||||
|
selectedRowKeys: tableState.selectedRowKeys,
|
||||||
|
onChange: fnTableSelectedRowKeys,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'id'">
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.copyText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordCopy(record.cdrJSON)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<CopyOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordDelete(record.id)"
|
||||||
|
v-perms:has="['cdr:ne:remove']"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<DeleteOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template #expandedRowRender="{ record }">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="8" :md="12" :xs="24" :offset="2">
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.dashboard.cdr.cdrInfo') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.neName') }}: </span>
|
||||||
|
<span>{{ record.neName }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.rmUid') }}: </span>
|
||||||
|
<span>{{ record.rmUID }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.time') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.recordOpeningTime }}</span>
|
||||||
|
</div>
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.dashboard.cdr.rowInfo') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>Record Type: </span>
|
||||||
|
<span>{{ record.cdrJSON.recordType }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Record Opening Time: </span>
|
||||||
|
<span>{{ record.cdrJSON.recordOpeningTime }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Charging ID: </span>
|
||||||
|
<span>{{ record.cdrJSON.chargingID }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Duration: </span>
|
||||||
|
<span>{{ record.cdrJSON.duration }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Record Access Point Name NI: </span>
|
||||||
|
<span>{{ record.cdrJSON.accessPointNameNI }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Record Cause For Rec Closing: </span>
|
||||||
|
<span>{{ record.cdrJSON.causeForRecClosing }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Record Sequence Number: </span>
|
||||||
|
<span>{{ record.cdrJSON.recordSequenceNumber }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Local Record Sequence Number: </span>
|
||||||
|
<span>{{ record.cdrJSON.localRecordSequenceNumber }}</span>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
|
<a-divider orientation="left"> Server Information </a-divider>
|
||||||
|
<div>
|
||||||
|
<span>IMSI: </span>
|
||||||
|
<span> {{ record.cdrJSON.servedIMSI }} </span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>MSISDN: </span>
|
||||||
|
<span> {{ record.cdrJSON.servedMSISDN }} </span>
|
||||||
|
</div>
|
||||||
|
<div v-if="record.cdrJSON.pGWAddressUsed">
|
||||||
|
<span>PGW Address: </span>
|
||||||
|
<span> {{ record.cdrJSON.pGWAddressUsed }} </span>
|
||||||
|
</div>
|
||||||
|
<div v-if="record.cdrJSON.GGSNAddress">
|
||||||
|
<span>GGSN Address: </span>
|
||||||
|
<span> {{ record.cdrJSON.GGSNAddress }} </span>
|
||||||
|
</div>
|
||||||
|
<div v-if="record.cdrJSON.sGWAddress">
|
||||||
|
<span>SGW Address: </span>
|
||||||
|
<span> {{ record.cdrJSON.sGWAddress }} </span>
|
||||||
|
</div>
|
||||||
|
<div v-if="record.cdrJSON.SGSNAddress">
|
||||||
|
<span>SGSN Address: </span>
|
||||||
|
<span> {{ record.cdrJSON.SGSNAddress }} </span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>RAT Type: </span>
|
||||||
|
<span> {{ record.cdrJSON.rATType }} </span>
|
||||||
|
</div>
|
||||||
|
<a-divider orientation="left"> PDPPD Information </a-divider>
|
||||||
|
<div>
|
||||||
|
<span>PDPPDN Type: </span>
|
||||||
|
<span> {{ record.cdrJSON.pdpPDNType }} </span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>PDPPDN Address: </span>
|
||||||
|
<span> {{ record.cdrJSON.servedPDPPDNAddress }} </span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Node Address: </span>
|
||||||
|
<span>
|
||||||
|
{{ record.cdrJSON.servingNodeAddress?.join(', ') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Node Type: </span>
|
||||||
|
<span>
|
||||||
|
<template v-for="item in record.cdrJSON.servingNodeType">
|
||||||
|
{{ item.servingNodeType }}
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -19,6 +19,10 @@ import {
|
|||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
import PQueue from 'p-queue';
|
import PQueue from 'p-queue';
|
||||||
import saveAs from 'file-saver';
|
import saveAs from 'file-saver';
|
||||||
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
|
import { useClipboard } from '@vueuse/core';
|
||||||
|
import { parseSizeFromByte } from '@/utils/parse-utils';
|
||||||
|
const { copy } = useClipboard({ legacy: true });
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const ws = new WS();
|
const ws = new WS();
|
||||||
const queue = new PQueue({ concurrency: 1, autoStart: true });
|
const queue = new PQueue({ concurrency: 1, autoStart: true });
|
||||||
@@ -27,7 +31,25 @@ const queue = new PQueue({ concurrency: 1, autoStart: true });
|
|||||||
let neOtions = ref<Record<string, any>[]>([]);
|
let neOtions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
/**开始结束时间 */
|
/**开始结束时间 */
|
||||||
let queryRangePicker = ref<[string, string]>(['', '']);
|
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([
|
||||||
|
dayjs().startOf('hour'),
|
||||||
|
dayjs().endOf('hour'),
|
||||||
|
]);
|
||||||
|
/**时间范围 */
|
||||||
|
let rangePickerPresets = ref([
|
||||||
|
{
|
||||||
|
label: 'Now hour',
|
||||||
|
value: [dayjs().startOf('hour'), dayjs().endOf('hour')],
|
||||||
|
},
|
||||||
|
{ label: 'Today', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
|
||||||
|
{
|
||||||
|
label: 'Yesterday',
|
||||||
|
value: [
|
||||||
|
dayjs().subtract(1, 'day').startOf('day'),
|
||||||
|
dayjs().subtract(1, 'day').endOf('day'),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
/**查询参数 */
|
/**查询参数 */
|
||||||
let queryParams = reactive({
|
let queryParams = reactive({
|
||||||
@@ -38,9 +60,9 @@ let queryParams = reactive({
|
|||||||
sortField: 'timestamp',
|
sortField: 'timestamp',
|
||||||
sortOrder: 'desc',
|
sortOrder: 'desc',
|
||||||
/**开始时间 */
|
/**开始时间 */
|
||||||
startTime: '',
|
startTime: undefined as undefined | number,
|
||||||
/**结束时间 */
|
/**结束时间 */
|
||||||
endTime: '',
|
endTime: undefined as undefined | number,
|
||||||
/**当前页数 */
|
/**当前页数 */
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
/**每页条数 */
|
/**每页条数 */
|
||||||
@@ -51,12 +73,10 @@ let queryParams = reactive({
|
|||||||
function fnQueryReset() {
|
function fnQueryReset() {
|
||||||
queryParams = Object.assign(queryParams, {
|
queryParams = Object.assign(queryParams, {
|
||||||
subscriberID: '',
|
subscriberID: '',
|
||||||
startTime: '',
|
|
||||||
endTime: '',
|
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
});
|
});
|
||||||
queryRangePicker.value = ['', ''];
|
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')];
|
||||||
tablePagination.current = 1;
|
tablePagination.current = 1;
|
||||||
tablePagination.pageSize = 20;
|
tablePagination.pageSize = 20;
|
||||||
fnGetList();
|
fnGetList();
|
||||||
@@ -86,7 +106,7 @@ let tableState: TabeStateType = reactive({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**表格字段列 */
|
/**表格字段列 */
|
||||||
let tableColumns: ColumnsType = [
|
let tableColumns = ref<ColumnsType>([
|
||||||
{
|
{
|
||||||
title: t('common.rowId'),
|
title: t('common.rowId'),
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
@@ -94,7 +114,7 @@ let tableColumns: ColumnsType = [
|
|||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.dashboard.cdr.smfChargingID'), // 计费ID
|
title: t('views.dashboard.cdr.chargingID'), // 计费ID
|
||||||
dataIndex: 'cdrJSON',
|
dataIndex: 'cdrJSON',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 100,
|
width: 100,
|
||||||
@@ -137,11 +157,15 @@ let tableColumns: ColumnsType = [
|
|||||||
) {
|
) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const usedUnitContainer = listOfMultipleUnitUsage[0].usedUnitContainer;
|
let dataVolumeUplink = 0;
|
||||||
if (!Array.isArray(usedUnitContainer) || usedUnitContainer.length < 1) {
|
for (const v of listOfMultipleUnitUsage) {
|
||||||
return 0;
|
if (Array.isArray(v.usedUnitContainer)) {
|
||||||
|
for (const used of v.usedUnitContainer) {
|
||||||
|
dataVolumeUplink += +used.dataVolumeUplink;
|
||||||
}
|
}
|
||||||
return usedUnitContainer[0].dataVolumeUplink;
|
}
|
||||||
|
}
|
||||||
|
return parseSizeFromByte(dataVolumeUplink, 'MB');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -158,11 +182,15 @@ let tableColumns: ColumnsType = [
|
|||||||
) {
|
) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const usedUnitContainer = listOfMultipleUnitUsage[0].usedUnitContainer;
|
let dataVolumeDownlink = 0;
|
||||||
if (!Array.isArray(usedUnitContainer) || usedUnitContainer.length < 1) {
|
for (const v of listOfMultipleUnitUsage) {
|
||||||
return 0;
|
if (Array.isArray(v.usedUnitContainer)) {
|
||||||
|
for (const used of v.usedUnitContainer) {
|
||||||
|
dataVolumeDownlink += +used.dataVolumeDownlink;
|
||||||
}
|
}
|
||||||
return usedUnitContainer[0].dataVolumeDownlink;
|
}
|
||||||
|
}
|
||||||
|
return parseSizeFromByte(dataVolumeDownlink, 'MB');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -179,15 +207,19 @@ let tableColumns: ColumnsType = [
|
|||||||
) {
|
) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const usedUnitContainer = listOfMultipleUnitUsage[0].usedUnitContainer;
|
let dataTotalVolume = 0;
|
||||||
if (!Array.isArray(usedUnitContainer) || usedUnitContainer.length < 1) {
|
for (const v of listOfMultipleUnitUsage) {
|
||||||
return 0;
|
if (Array.isArray(v.usedUnitContainer)) {
|
||||||
|
for (const used of v.usedUnitContainer) {
|
||||||
|
dataTotalVolume += +used.dataTotalVolume;
|
||||||
}
|
}
|
||||||
return usedUnitContainer[0].dataTotalVolume;
|
}
|
||||||
|
}
|
||||||
|
return parseSizeFromByte(dataTotalVolume, 'MB');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.dashboard.cdr.smfDuration'), // 持续时间
|
title: t('views.dashboard.cdr.durationTime'), // 持续时间
|
||||||
dataIndex: 'cdrJSON',
|
dataIndex: 'cdrJSON',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 100,
|
width: 100,
|
||||||
@@ -197,7 +229,7 @@ let tableColumns: ColumnsType = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.dashboard.cdr.smfInvocationTime'), // 调用时间
|
title: t('views.dashboard.cdr.invocationTime'), // 调用时间
|
||||||
dataIndex: 'cdrJSON',
|
dataIndex: 'cdrJSON',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 200,
|
width: 200,
|
||||||
@@ -211,7 +243,7 @@ let tableColumns: ColumnsType = [
|
|||||||
key: 'id',
|
key: 'id',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
];
|
]);
|
||||||
|
|
||||||
/**表格分页器参数 */
|
/**表格分页器参数 */
|
||||||
let tablePagination = reactive({
|
let tablePagination = reactive({
|
||||||
@@ -306,6 +338,18 @@ function fnRecordDelete(id: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制CDR
|
||||||
|
* @param jsonStr JSON字符串
|
||||||
|
*/
|
||||||
|
function fnRecordCopy(jsonStr: string) {
|
||||||
|
if (!jsonStr) return;
|
||||||
|
const text = JSON.stringify(jsonStr, null, 2);
|
||||||
|
copy(text).then(() => {
|
||||||
|
message.success(t('common.copyOk'), 3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**查询列表, pageNum初始页数 */
|
/**查询列表, pageNum初始页数 */
|
||||||
function fnGetList(pageNum?: number) {
|
function fnGetList(pageNum?: number) {
|
||||||
if (tableState.loading) return;
|
if (tableState.loading) return;
|
||||||
@@ -313,11 +357,19 @@ function fnGetList(pageNum?: number) {
|
|||||||
if (pageNum) {
|
if (pageNum) {
|
||||||
queryParams.pageNum = pageNum;
|
queryParams.pageNum = pageNum;
|
||||||
}
|
}
|
||||||
if (!queryRangePicker.value) {
|
|
||||||
queryRangePicker.value = ['', ''];
|
// 时间范围
|
||||||
|
if (
|
||||||
|
Array.isArray(queryRangePicker.value) &&
|
||||||
|
queryRangePicker.value.length > 0
|
||||||
|
) {
|
||||||
|
queryParams.startTime = queryRangePicker.value[0].valueOf();
|
||||||
|
queryParams.endTime = queryRangePicker.value[1].valueOf();
|
||||||
|
} else {
|
||||||
|
queryParams.startTime = undefined;
|
||||||
|
queryParams.endTime = undefined;
|
||||||
}
|
}
|
||||||
queryParams.startTime = queryRangePicker.value[0];
|
|
||||||
queryParams.endTime = queryRangePicker.value[1];
|
|
||||||
listSMFDataCDR(toRaw(queryParams)).then(res => {
|
listSMFDataCDR(toRaw(queryParams)).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
// 取消勾选
|
// 取消勾选
|
||||||
@@ -406,7 +458,9 @@ function fnRealTime() {
|
|||||||
subGroupID: `1006_${queryParams.neId}`,
|
subGroupID: `1006_${queryParams.neId}`,
|
||||||
},
|
},
|
||||||
onmessage: wsMessage,
|
onmessage: wsMessage,
|
||||||
onerror: wsError,
|
onerror: (ev: any) => {
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
ws.connect(options);
|
ws.connect(options);
|
||||||
} else {
|
} else {
|
||||||
@@ -416,12 +470,6 @@ function fnRealTime() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**接收数据后回调 */
|
|
||||||
function wsError(ev: any) {
|
|
||||||
// 接收数据后回调
|
|
||||||
console.error(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**接收数据后回调 */
|
/**接收数据后回调 */
|
||||||
function wsMessage(res: Record<string, any>) {
|
function wsMessage(res: Record<string, any>) {
|
||||||
const { code, requestId, data } = res;
|
const { code, requestId, data } = res;
|
||||||
@@ -510,6 +558,7 @@ onBeforeUnmount(() => {
|
|||||||
v-model:value="queryParams.neId"
|
v-model:value="queryParams.neId"
|
||||||
:options="neOtions"
|
:options="neOtions"
|
||||||
:placeholder="t('common.selectPlease')"
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnQueryReset()"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -533,12 +582,12 @@ onBeforeUnmount(() => {
|
|||||||
>
|
>
|
||||||
<a-range-picker
|
<a-range-picker
|
||||||
v-model:value="queryRangePicker"
|
v-model:value="queryRangePicker"
|
||||||
allow-clear
|
:presets="rangePickerPresets"
|
||||||
bordered
|
:bordered="true"
|
||||||
|
:allow-clear="false"
|
||||||
|
style="width: 100%"
|
||||||
:show-time="{ format: 'HH:mm:ss' }"
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
value-format="x"
|
|
||||||
style="width: 100%"
|
|
||||||
></a-range-picker>
|
></a-range-picker>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -591,6 +640,7 @@ onBeforeUnmount(() => {
|
|||||||
:disabled="tableState.selectedRowKeys.length <= 0"
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
:loading="modalState.confirmLoading"
|
:loading="modalState.confirmLoading"
|
||||||
@click.prevent="fnRecordDelete('0')"
|
@click.prevent="fnRecordDelete('0')"
|
||||||
|
v-perms:has="['cdr:ne:remove']"
|
||||||
>
|
>
|
||||||
<template #icon><DeleteOutlined /></template>
|
<template #icon><DeleteOutlined /></template>
|
||||||
{{ t('common.deleteText') }}
|
{{ t('common.deleteText') }}
|
||||||
@@ -669,11 +719,23 @@ onBeforeUnmount(() => {
|
|||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'id'">
|
<template v-if="column.key === 'id'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.copyText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordCopy(record.cdrJSON)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<CopyOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template #title>{{ t('common.deleteText') }}</template>
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
@click.prevent="fnRecordDelete(record.id)"
|
@click.prevent="fnRecordDelete(record.id)"
|
||||||
|
v-perms:has="['cdr:ne:remove']"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
@@ -742,9 +804,17 @@ onBeforeUnmount(() => {
|
|||||||
<a-divider orientation="left">
|
<a-divider orientation="left">
|
||||||
List Of Multiple Unit Usage
|
List Of Multiple Unit Usage
|
||||||
</a-divider>
|
</a-divider>
|
||||||
|
|
||||||
<div v-for="u in record.cdrJSON.listOfMultipleUnitUsage">
|
<div v-for="u in record.cdrJSON.listOfMultipleUnitUsage">
|
||||||
<div>RatingGroup: {{ u.ratingGroup }}</div>
|
<!-- <div>RatingGroup: {{ u.ratingGroup }}</div> -->
|
||||||
<div v-for="udata in u.usedUnitContainer">
|
<div
|
||||||
|
v-for="(udata, i) in u.usedUnitContainer"
|
||||||
|
style="display: flex"
|
||||||
|
>
|
||||||
|
<!-- <strong style="margin-right: 12px">
|
||||||
|
{{ i }}
|
||||||
|
</strong> -->
|
||||||
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<span>Data Total Volume: </span>
|
<span>Data Total Volume: </span>
|
||||||
<span>{{ udata.dataTotalVolume }}</span>
|
<span>{{ udata.dataTotalVolume }}</span>
|
||||||
@@ -757,9 +827,10 @@ onBeforeUnmount(() => {
|
|||||||
<span>Data Volume Uplink: </span>
|
<span>Data Volume Uplink: </span>
|
||||||
<span>{{ udata.dataVolumeUplink }}</span>
|
<span>{{ udata.dataVolumeUplink }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<!-- <div>
|
||||||
<span>Time: </span>
|
<span>Time: </span>
|
||||||
<span>{{ udata.time }}</span>
|
<span>{{ udata.time }}</span>
|
||||||
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,699 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import * as echarts from 'echarts/core';
|
||||||
|
import {
|
||||||
|
TitleComponent,
|
||||||
|
ToolboxComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
GridComponent,
|
||||||
|
LegendComponent,
|
||||||
|
DataZoomComponent,
|
||||||
|
} from 'echarts/components';
|
||||||
|
import { LineChart } from 'echarts/charts';
|
||||||
|
import { UniversalTransition } from 'echarts/features';
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
|
|
||||||
|
echarts.use([
|
||||||
|
TitleComponent,
|
||||||
|
ToolboxComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
GridComponent,
|
||||||
|
LegendComponent,
|
||||||
|
DataZoomComponent,
|
||||||
|
LineChart,
|
||||||
|
CanvasRenderer,
|
||||||
|
UniversalTransition,
|
||||||
|
]);
|
||||||
|
|
||||||
|
import { reactive, onMounted, toRaw, onBeforeUnmount, ref } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { listSMFDataCDR } from '@/api/neData/smf';
|
||||||
|
import {
|
||||||
|
RESULT_CODE_ERROR,
|
||||||
|
RESULT_CODE_SUCCESS,
|
||||||
|
} from '@/constants/result-constants';
|
||||||
|
import { parseSizeFromByte } from '@/utils/parse-utils';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
|
const { t, currentLocale } = useI18n();
|
||||||
|
const ws = new WS();
|
||||||
|
|
||||||
|
/**图DOM节点实例对象 */
|
||||||
|
const cdrChartDom = ref<HTMLElement | undefined>(undefined);
|
||||||
|
/**图实例对象 */
|
||||||
|
let cdrChart: echarts.ECharts | null = null;
|
||||||
|
/**图表配置 */
|
||||||
|
const option = {
|
||||||
|
title: {
|
||||||
|
text: 'Data Usage Report',
|
||||||
|
left: 'left',
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
animation: true,
|
||||||
|
},
|
||||||
|
formatter: (params: any) => {
|
||||||
|
const title = params[0].name;
|
||||||
|
let uplinkValue = 0;
|
||||||
|
let downlinkValue = 0;
|
||||||
|
if (params[0].seriesName === 'Uplink') {
|
||||||
|
uplinkValue = params[0].value;
|
||||||
|
} else {
|
||||||
|
downlinkValue = params[0].value;
|
||||||
|
}
|
||||||
|
if (params[1].seriesName === 'Uplink') {
|
||||||
|
uplinkValue = params[1].value;
|
||||||
|
} else {
|
||||||
|
downlinkValue = params[1].value;
|
||||||
|
}
|
||||||
|
const uplinkValueF = parseSizeFromByte(uplinkValue, 'MB');
|
||||||
|
const downlinkValueF = parseSizeFromByte(downlinkValue, 'MB');
|
||||||
|
return `
|
||||||
|
<div style="font-weight: bold;">${title}</div>
|
||||||
|
<div>Downlink: ${downlinkValueF}</div>
|
||||||
|
<div>Uplink: ${uplinkValueF}</div>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
toolbox: {
|
||||||
|
feature: {
|
||||||
|
dataZoom: {
|
||||||
|
yAxisIndex: 'none',
|
||||||
|
},
|
||||||
|
saveAsImage: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisPointer: {
|
||||||
|
link: [
|
||||||
|
{
|
||||||
|
xAxisIndex: 'all',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
show: false,
|
||||||
|
realtime: true,
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
xAxisIndex: [0, 1],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
realtime: true,
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
xAxisIndex: [0, 1],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
grid: [
|
||||||
|
{
|
||||||
|
left: '10%',
|
||||||
|
right: 50,
|
||||||
|
height: '40%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
left: '10%',
|
||||||
|
right: 50,
|
||||||
|
top: '50%',
|
||||||
|
height: '40%',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
axisLine: { onZero: true },
|
||||||
|
data: [], // x轴初始数据
|
||||||
|
axisLabel: {
|
||||||
|
show: false, // 显示标签
|
||||||
|
rotate: 15, // 设置倾斜角度(如15度)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gridIndex: 1,
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
axisLine: { onZero: true },
|
||||||
|
data: [], // x轴初始数据
|
||||||
|
axisLabel: {
|
||||||
|
show: false, // 隐藏第二个 x 轴的标签
|
||||||
|
},
|
||||||
|
position: 'top',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
name: 'Downlink (Byte)',
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gridIndex: 1,
|
||||||
|
name: 'Uplink (Byte)',
|
||||||
|
type: 'value',
|
||||||
|
inverse: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'Downlink',
|
||||||
|
type: 'line',
|
||||||
|
data: [], // y轴初始数据
|
||||||
|
symbol: 'circle', // 数据点形状
|
||||||
|
symbolSize: 6, // 数据点大小
|
||||||
|
smooth: true, // 平滑曲线
|
||||||
|
color: 'rgb(0, 190, 99)',
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: 'rgba(0, 190, 99, .5)' },
|
||||||
|
{ offset: 1, color: 'rgba(0, 190, 99, 0.5)' },
|
||||||
|
],
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
type: 'linear',
|
||||||
|
global: false,
|
||||||
|
},
|
||||||
|
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||||
|
shadowBlur: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Uplink',
|
||||||
|
type: 'line',
|
||||||
|
xAxisIndex: 1,
|
||||||
|
yAxisIndex: 1,
|
||||||
|
data: [], // y轴初始数据
|
||||||
|
symbol: 'circle', // 数据点形状
|
||||||
|
symbolSize: 6, // 数据点大小
|
||||||
|
smooth: true, // 平滑曲线
|
||||||
|
color: 'rgb(17, 178, 255)',
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: 'rgba(17, 178, 255, .5)' },
|
||||||
|
{ offset: 1, color: 'rgba(17, 178, 255, 0.5)' },
|
||||||
|
],
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
type: 'linear',
|
||||||
|
global: false,
|
||||||
|
},
|
||||||
|
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||||
|
shadowBlur: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
/**绘制图表 */
|
||||||
|
function fnRanderChart() {
|
||||||
|
const container: HTMLElement | undefined = cdrChartDom.value;
|
||||||
|
if (!container) return;
|
||||||
|
const locale = currentLocale.value.split('_')[0];
|
||||||
|
cdrChart = echarts.init(container, 'light', {
|
||||||
|
// https://github.com/apache/echarts/tree/release/src/i18n 取值langEN.ts ==> EN
|
||||||
|
locale: locale.toUpperCase(),
|
||||||
|
});
|
||||||
|
cdrChart.setOption(option);
|
||||||
|
// cdrChart.showLoading('default', {
|
||||||
|
// text: 'Please enter IMSI to query user traffic',
|
||||||
|
// fontSize: 16, // 字体大小
|
||||||
|
// });
|
||||||
|
|
||||||
|
// 创建 ResizeObserver 实例 监听图表容器大小变化,并在变化时调整图表大小
|
||||||
|
var observer = new ResizeObserver(entries => {
|
||||||
|
if (cdrChart && !cdrChart.isDisposed) {
|
||||||
|
cdrChart.resize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 监听元素大小变化
|
||||||
|
observer.observe(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**网元可选 */
|
||||||
|
let neOtions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
|
/**开始结束时间 */
|
||||||
|
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([
|
||||||
|
dayjs().startOf('hour'),
|
||||||
|
dayjs().endOf('hour'),
|
||||||
|
]);
|
||||||
|
/**时间范围 */
|
||||||
|
let rangePickerPresets = ref([
|
||||||
|
{
|
||||||
|
label: 'Now hour',
|
||||||
|
value: [dayjs().startOf('hour'), dayjs().endOf('hour')],
|
||||||
|
},
|
||||||
|
{ label: 'Today', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
|
||||||
|
{
|
||||||
|
label: 'Yesterday',
|
||||||
|
value: [
|
||||||
|
dayjs().subtract(1, 'day').startOf('day'),
|
||||||
|
dayjs().subtract(1, 'day').endOf('day'),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元类型 */
|
||||||
|
neType: 'SMF',
|
||||||
|
neId: '001',
|
||||||
|
subscriberID: '',
|
||||||
|
dnn: '',
|
||||||
|
sortField: 'timestamp',
|
||||||
|
sortOrder: 'desc',
|
||||||
|
/**开始时间 */
|
||||||
|
startTime: undefined as undefined | number,
|
||||||
|
/**结束时间 */
|
||||||
|
endTime: undefined as undefined | number,
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 1000,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
queryParams.subscriberID = '';
|
||||||
|
queryParams.dnn = '';
|
||||||
|
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')];
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = reactive({
|
||||||
|
/**表格数据 */
|
||||||
|
data: [] as any[],
|
||||||
|
/**表格总数 */
|
||||||
|
total: 0,
|
||||||
|
/**表格加载状态 */
|
||||||
|
loading: false,
|
||||||
|
/**数据总量 up,down */
|
||||||
|
dataUsage: ['0 B', '0 B'],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (state.loading) return;
|
||||||
|
state.loading = true;
|
||||||
|
if (!queryParams.subscriberID) {
|
||||||
|
message.warning('Please enter IMSI to query user traffic');
|
||||||
|
state.loading = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
if (cdrChart) {
|
||||||
|
cdrChart.showLoading('default', {
|
||||||
|
text: 'Loading...',
|
||||||
|
fontSize: 16, // 字体大小
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时间范围
|
||||||
|
if (
|
||||||
|
Array.isArray(queryRangePicker.value) &&
|
||||||
|
queryRangePicker.value.length > 0
|
||||||
|
) {
|
||||||
|
queryParams.startTime = queryRangePicker.value[0].valueOf();
|
||||||
|
queryParams.endTime = queryRangePicker.value[1].valueOf();
|
||||||
|
} else {
|
||||||
|
queryParams.startTime = undefined;
|
||||||
|
queryParams.endTime = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
listSMFDataCDR(toRaw(queryParams))
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
|
state.total = res.total;
|
||||||
|
// 遍历处理cdr字符串数据
|
||||||
|
state.data = res.rows
|
||||||
|
.map(item => {
|
||||||
|
let cdrJSON = item.cdrJSON;
|
||||||
|
if (!cdrJSON) {
|
||||||
|
Reflect.set(item, 'cdrJSON', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
cdrJSON = JSON.parse(cdrJSON);
|
||||||
|
Reflect.set(item, 'cdrJSON', cdrJSON);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
Reflect.set(item, 'cdrJSON', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
.reverse();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
state.loading = false;
|
||||||
|
fnRanderChartDataLoad();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**图表配置数据x轴 */
|
||||||
|
let dataTimeXAxisData: string[] = [];
|
||||||
|
/**图表配置数据y轴 */
|
||||||
|
let dataVolumeUplinkYSeriesData: number[] = [];
|
||||||
|
let dataVolumeDownlinkYSeriesData: number[] = [];
|
||||||
|
/**图表数据渲染 */
|
||||||
|
function fnRanderChartDataLoad() {
|
||||||
|
if (!cdrChart) return;
|
||||||
|
dataTimeXAxisData = [];
|
||||||
|
dataVolumeUplinkYSeriesData = [];
|
||||||
|
dataVolumeDownlinkYSeriesData = [];
|
||||||
|
if (state.data.length > 0) {
|
||||||
|
// 处理数据渲染图表
|
||||||
|
for (const item of state.data) {
|
||||||
|
if (!item.cdrJSON.invocationTimestamp) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 时间
|
||||||
|
const dataTime = item.cdrJSON.invocationTimestamp;
|
||||||
|
const listOfMultipleUnitUsage = item.cdrJSON.listOfMultipleUnitUsage;
|
||||||
|
if (
|
||||||
|
!Array.isArray(listOfMultipleUnitUsage) ||
|
||||||
|
listOfMultipleUnitUsage.length < 1
|
||||||
|
) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// 数据
|
||||||
|
let dataVolumeUplink = 0;
|
||||||
|
let dataVolumeDownlink = 0;
|
||||||
|
for (const v of listOfMultipleUnitUsage) {
|
||||||
|
if (Array.isArray(v.usedUnitContainer)) {
|
||||||
|
for (const used of v.usedUnitContainer) {
|
||||||
|
dataVolumeUplink += +used.dataVolumeUplink;
|
||||||
|
dataVolumeDownlink += +used.dataVolumeDownlink;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dataTimeXAxisData.push(dataTime);
|
||||||
|
dataVolumeUplinkYSeriesData.push(dataVolumeUplink);
|
||||||
|
dataVolumeDownlinkYSeriesData.push(dataVolumeDownlink);
|
||||||
|
}
|
||||||
|
// 绘制图数据
|
||||||
|
fnRanderChartDataUpdate();
|
||||||
|
} else {
|
||||||
|
message.warning('No Data');
|
||||||
|
cdrChart.hideLoading();
|
||||||
|
cdrChart.setOption({
|
||||||
|
title: {
|
||||||
|
text: `Data Volume Uplink / Downlink By IMSI ${queryParams.subscriberID}`,
|
||||||
|
},
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
data: dataTimeXAxisData,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: dataTimeXAxisData,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: dataVolumeUplinkYSeriesData,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: dataVolumeDownlinkYSeriesData,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**图表数据渲染 */
|
||||||
|
function fnRanderChartDataUpdate() {
|
||||||
|
if (cdrChart == null) return;
|
||||||
|
// 绘制图数据
|
||||||
|
cdrChart.setOption({
|
||||||
|
title: {
|
||||||
|
text: `Data Usage Report of IMSI ${queryParams.subscriberID}`,
|
||||||
|
},
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
data: dataTimeXAxisData,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: dataTimeXAxisData,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: dataVolumeUplinkYSeriesData,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: dataVolumeDownlinkYSeriesData,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
cdrChart.hideLoading();
|
||||||
|
|
||||||
|
// 累加总量
|
||||||
|
let uplinkTotal = 0;
|
||||||
|
let downlinkTotal = 0;
|
||||||
|
for (let index = 0; index < dataVolumeUplinkYSeriesData.length; index++) {
|
||||||
|
uplinkTotal += dataVolumeUplinkYSeriesData[index];
|
||||||
|
downlinkTotal += dataVolumeDownlinkYSeriesData[index];
|
||||||
|
}
|
||||||
|
state.dataUsage = [
|
||||||
|
parseSizeFromByte(uplinkTotal, 'MB'),
|
||||||
|
parseSizeFromByte(downlinkTotal, 'MB'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实时数据
|
||||||
|
*/
|
||||||
|
function fnRealTime() {
|
||||||
|
if (ws.state() === WebSocket.OPEN) {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
// 建立链接
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: '/ws',
|
||||||
|
params: {
|
||||||
|
/**订阅通道组
|
||||||
|
*
|
||||||
|
* CDR会话事件-SMF (GroupID:1006)
|
||||||
|
*/
|
||||||
|
subGroupID: `1006_${queryParams.neId}`,
|
||||||
|
},
|
||||||
|
onmessage: (res: Record<string, any>) => {
|
||||||
|
const { code, requestId, data } = res;
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅组信息
|
||||||
|
if (!data?.groupId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// cdrEvent CDR会话事件
|
||||||
|
if (data.groupId === `1006_${queryParams.neId}`) {
|
||||||
|
const cdrEvent = data.data;
|
||||||
|
// 对应结束时间内
|
||||||
|
if (queryParams.endTime) {
|
||||||
|
const endTime = Math.round(queryParams.endTime / 1000);
|
||||||
|
if (cdrEvent.timestamp > endTime) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const cdrJSON = cdrEvent.CDR;
|
||||||
|
if (!cdrJSON.invocationTimestamp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 对应IMSI
|
||||||
|
if (
|
||||||
|
cdrJSON.subscriberIdentifier.subscriptionIDData !==
|
||||||
|
queryParams.subscriberID
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时间
|
||||||
|
const dataTime = cdrJSON.invocationTimestamp;
|
||||||
|
const listOfMultipleUnitUsage = cdrJSON.listOfMultipleUnitUsage;
|
||||||
|
if (
|
||||||
|
!Array.isArray(listOfMultipleUnitUsage) ||
|
||||||
|
listOfMultipleUnitUsage.length < 1
|
||||||
|
) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// 数据
|
||||||
|
let dataVolumeUplink = 0;
|
||||||
|
let dataVolumeDownlink = 0;
|
||||||
|
for (const v of listOfMultipleUnitUsage) {
|
||||||
|
if (Array.isArray(v.usedUnitContainer)) {
|
||||||
|
for (const used of v.usedUnitContainer) {
|
||||||
|
dataVolumeUplink += +used.dataVolumeUplink;
|
||||||
|
dataVolumeDownlink += +used.dataVolumeDownlink;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 添加数据
|
||||||
|
dataTimeXAxisData.push(dataTime);
|
||||||
|
dataVolumeUplinkYSeriesData.push(dataVolumeUplink);
|
||||||
|
dataVolumeDownlinkYSeriesData.push(dataVolumeDownlink);
|
||||||
|
fnRanderChartDataUpdate();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onerror: (ev: any) => {
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
ws.connect(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 获取网元网元列表
|
||||||
|
useNeInfoStore()
|
||||||
|
.fnNelist()
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length > 0) {
|
||||||
|
let arr: Record<string, any>[] = [];
|
||||||
|
res.data.forEach((v: any) => {
|
||||||
|
if (v.neType === 'SMF') {
|
||||||
|
arr.push({ value: v.neId, label: v.neName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neOtions.value = arr;
|
||||||
|
if (arr.length > 0) {
|
||||||
|
queryParams.neId = arr[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
fnRanderChart();
|
||||||
|
fnRealTime();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
ws.close();
|
||||||
|
if (cdrChart) {
|
||||||
|
cdrChart.clear();
|
||||||
|
cdrChart.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card
|
||||||
|
:bordered="false"
|
||||||
|
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
||||||
|
>
|
||||||
|
<!-- 表格搜索栏 -->
|
||||||
|
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item label="SMF" name="neId ">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.neId"
|
||||||
|
:options="neOtions"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnRealTime()"
|
||||||
|
:disabled="state.loading"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item label="IMSI" name="subscriberID" :required="true">
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.subscriberID"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="40"
|
||||||
|
:disabled="state.loading"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item label="DNN" name="dnn">
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.dnn"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="40"
|
||||||
|
:disabled="state.loading"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="4" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
@click.prevent="fnGetList(1)"
|
||||||
|
:loading="state.loading"
|
||||||
|
>
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
@click.prevent="fnQueryReset"
|
||||||
|
:disabled="state.loading"
|
||||||
|
>
|
||||||
|
<template #icon><ClearOutlined /></template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.time')"
|
||||||
|
name="queryRangePicker"
|
||||||
|
>
|
||||||
|
<a-range-picker
|
||||||
|
v-model:value="queryRangePicker"
|
||||||
|
:presets="rangePickerPresets"
|
||||||
|
:bordered="true"
|
||||||
|
:allow-clear="false"
|
||||||
|
style="width: 100%"
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
:disabled="state.loading"
|
||||||
|
></a-range-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false">
|
||||||
|
<!-- 图数据 -->
|
||||||
|
<div ref="cdrChartDom" style="height: 600px; width: 100%"></div>
|
||||||
|
|
||||||
|
<a-descriptions title="Data Usage" bordered :column="2">
|
||||||
|
<a-descriptions-item label="Total Uplink">
|
||||||
|
{{ state.dataUsage[0] }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="Total Downlink">
|
||||||
|
{{ state.dataUsage[1] }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
||||||
@@ -21,6 +21,10 @@ import { parseDateToStr } from '@/utils/date-utils';
|
|||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
import saveAs from 'file-saver';
|
import saveAs from 'file-saver';
|
||||||
import PQueue from 'p-queue';
|
import PQueue from 'p-queue';
|
||||||
|
import { useClipboard } from '@vueuse/core';
|
||||||
|
import { hasPermissions } from '@/plugins/auth-user';
|
||||||
|
import dayjs, { type Dayjs } from 'dayjs';
|
||||||
|
const { copy } = useClipboard({ legacy: true });
|
||||||
const { getDict } = useDictStore();
|
const { getDict } = useDictStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const ws = new WS();
|
const ws = new WS();
|
||||||
@@ -38,7 +42,25 @@ let dict: {
|
|||||||
let neOtions = ref<Record<string, any>[]>([]);
|
let neOtions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
/**开始结束时间 */
|
/**开始结束时间 */
|
||||||
let queryRangePicker = ref<[string, string]>(['', '']);
|
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([
|
||||||
|
dayjs().startOf('hour'),
|
||||||
|
dayjs().endOf('hour'),
|
||||||
|
]);
|
||||||
|
/**时间范围 */
|
||||||
|
let rangePickerPresets = ref([
|
||||||
|
{
|
||||||
|
label: 'Now hour',
|
||||||
|
value: [dayjs().startOf('hour'), dayjs().endOf('hour')],
|
||||||
|
},
|
||||||
|
{ label: 'Today', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
|
||||||
|
{
|
||||||
|
label: 'Yesterday',
|
||||||
|
value: [
|
||||||
|
dayjs().subtract(1, 'day').startOf('day'),
|
||||||
|
dayjs().subtract(1, 'day').endOf('day'),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
/**查询参数 */
|
/**查询参数 */
|
||||||
let queryParams = reactive({
|
let queryParams = reactive({
|
||||||
@@ -51,9 +73,9 @@ let queryParams = reactive({
|
|||||||
sortField: 'timestamp',
|
sortField: 'timestamp',
|
||||||
sortOrder: 'desc',
|
sortOrder: 'desc',
|
||||||
/**开始时间 */
|
/**开始时间 */
|
||||||
startTime: '',
|
startTime: undefined as undefined | number,
|
||||||
/**结束时间 */
|
/**结束时间 */
|
||||||
endTime: '',
|
endTime: undefined as undefined | number,
|
||||||
/**当前页数 */
|
/**当前页数 */
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
/**每页条数 */
|
/**每页条数 */
|
||||||
@@ -67,12 +89,12 @@ function fnQueryReset() {
|
|||||||
recordType: '',
|
recordType: '',
|
||||||
callerParty: '',
|
callerParty: '',
|
||||||
calledParty: '',
|
calledParty: '',
|
||||||
startTime: '',
|
startTime: undefined,
|
||||||
endTime: '',
|
endTime: undefined,
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
});
|
});
|
||||||
queryRangePicker.value = ['', ''];
|
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')];
|
||||||
tablePagination.current = 1;
|
tablePagination.current = 1;
|
||||||
tablePagination.pageSize = 20;
|
tablePagination.pageSize = 20;
|
||||||
fnGetList();
|
fnGetList();
|
||||||
@@ -144,7 +166,7 @@ let tableColumns: ColumnsType = [
|
|||||||
dataIndex: 'cdrJSON',
|
dataIndex: 'cdrJSON',
|
||||||
key: 'callerParty',
|
key: 'callerParty',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 120,
|
width: 150,
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
const cdrJSON = opt.value;
|
const cdrJSON = opt.value;
|
||||||
return cdrJSON.callerParty;
|
return cdrJSON.callerParty;
|
||||||
@@ -155,7 +177,7 @@ let tableColumns: ColumnsType = [
|
|||||||
dataIndex: 'cdrJSON',
|
dataIndex: 'cdrJSON',
|
||||||
key: 'calledParty',
|
key: 'calledParty',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 120,
|
width: 150,
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
const cdrJSON = opt.value;
|
const cdrJSON = opt.value;
|
||||||
return cdrJSON.calledParty;
|
return cdrJSON.calledParty;
|
||||||
@@ -173,7 +195,7 @@ let tableColumns: ColumnsType = [
|
|||||||
title: t('views.dashboard.cdr.time'),
|
title: t('views.dashboard.cdr.time'),
|
||||||
dataIndex: 'cdrJSON',
|
dataIndex: 'cdrJSON',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 150,
|
width: 200,
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
const cdrJSON = opt.value;
|
const cdrJSON = opt.value;
|
||||||
if (typeof cdrJSON.updateTime === 'number') {
|
if (typeof cdrJSON.updateTime === 'number') {
|
||||||
@@ -282,6 +304,18 @@ function fnRecordDelete(id: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制CDR
|
||||||
|
* @param jsonStr JSON字符串
|
||||||
|
*/
|
||||||
|
function fnRecordCopy(jsonStr: string) {
|
||||||
|
if (!jsonStr) return;
|
||||||
|
const text = JSON.stringify(jsonStr, null, 2);
|
||||||
|
copy(text).then(() => {
|
||||||
|
message.success(t('common.copyOk'), 3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**查询列表, pageNum初始页数 */
|
/**查询列表, pageNum初始页数 */
|
||||||
function fnGetList(pageNum?: number) {
|
function fnGetList(pageNum?: number) {
|
||||||
if (tableState.loading) return;
|
if (tableState.loading) return;
|
||||||
@@ -289,11 +323,19 @@ function fnGetList(pageNum?: number) {
|
|||||||
if (pageNum) {
|
if (pageNum) {
|
||||||
queryParams.pageNum = pageNum;
|
queryParams.pageNum = pageNum;
|
||||||
}
|
}
|
||||||
if (!queryRangePicker.value) {
|
|
||||||
queryRangePicker.value = ['', ''];
|
// 时间范围
|
||||||
|
if (
|
||||||
|
Array.isArray(queryRangePicker.value) &&
|
||||||
|
queryRangePicker.value.length > 0
|
||||||
|
) {
|
||||||
|
queryParams.startTime = queryRangePicker.value[0].valueOf();
|
||||||
|
queryParams.endTime = queryRangePicker.value[1].valueOf();
|
||||||
|
} else {
|
||||||
|
queryParams.startTime = undefined;
|
||||||
|
queryParams.endTime = undefined;
|
||||||
}
|
}
|
||||||
queryParams.startTime = queryRangePicker.value[0];
|
|
||||||
queryParams.endTime = queryRangePicker.value[1];
|
|
||||||
listSMSCDataCDR(toRaw(queryParams)).then(res => {
|
listSMSCDataCDR(toRaw(queryParams)).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
// 取消勾选
|
// 取消勾选
|
||||||
@@ -382,7 +424,9 @@ function fnRealTime() {
|
|||||||
subGroupID: `1007_${queryParams.neId}`,
|
subGroupID: `1007_${queryParams.neId}`,
|
||||||
},
|
},
|
||||||
onmessage: wsMessage,
|
onmessage: wsMessage,
|
||||||
onerror: wsError,
|
onerror: (ev: any) => {
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
ws.connect(options);
|
ws.connect(options);
|
||||||
} else {
|
} else {
|
||||||
@@ -392,12 +436,6 @@ function fnRealTime() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**接收数据后回调 */
|
|
||||||
function wsError(ev: any) {
|
|
||||||
// 接收数据后回调
|
|
||||||
console.error(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**接收数据后回调 */
|
/**接收数据后回调 */
|
||||||
function wsMessage(res: Record<string, any>) {
|
function wsMessage(res: Record<string, any>) {
|
||||||
const { code, requestId, data } = res;
|
const { code, requestId, data } = res;
|
||||||
@@ -492,6 +530,7 @@ onBeforeUnmount(() => {
|
|||||||
v-model:value="queryParams.neId"
|
v-model:value="queryParams.neId"
|
||||||
:options="neOtions"
|
:options="neOtions"
|
||||||
:placeholder="t('common.selectPlease')"
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnQueryReset()"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -554,12 +593,12 @@ onBeforeUnmount(() => {
|
|||||||
>
|
>
|
||||||
<a-range-picker
|
<a-range-picker
|
||||||
v-model:value="queryRangePicker"
|
v-model:value="queryRangePicker"
|
||||||
allow-clear
|
:presets="rangePickerPresets"
|
||||||
bordered
|
:bordered="true"
|
||||||
|
:allow-clear="false"
|
||||||
|
style="width: 100%"
|
||||||
:show-time="{ format: 'HH:mm:ss' }"
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
value-format="x"
|
|
||||||
style="width: 100%"
|
|
||||||
></a-range-picker>
|
></a-range-picker>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -598,6 +637,7 @@ onBeforeUnmount(() => {
|
|||||||
:disabled="tableState.selectedRowKeys.length <= 0"
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
:loading="modalState.confirmLoading"
|
:loading="modalState.confirmLoading"
|
||||||
@click.prevent="fnRecordDelete('0')"
|
@click.prevent="fnRecordDelete('0')"
|
||||||
|
v-perms:has="['cdr:ne:remove']"
|
||||||
>
|
>
|
||||||
<template #icon><DeleteOutlined /></template>
|
<template #icon><DeleteOutlined /></template>
|
||||||
{{ t('common.deleteText') }}
|
{{ t('common.deleteText') }}
|
||||||
@@ -665,7 +705,7 @@ onBeforeUnmount(() => {
|
|||||||
:data-source="tableState.data"
|
:data-source="tableState.data"
|
||||||
:size="tableState.size"
|
:size="tableState.size"
|
||||||
:pagination="tablePagination"
|
:pagination="tablePagination"
|
||||||
:scroll="{ x: tableColumns.length * 150, y: 'calc(100vh - 480px)' }"
|
:scroll="{ x: tableColumns.length * 180, y: 'calc(100vh - 480px)' }"
|
||||||
:row-selection="{
|
:row-selection="{
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
columnWidth: '48px',
|
columnWidth: '48px',
|
||||||
@@ -689,11 +729,23 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'id'">
|
<template v-if="column.key === 'id'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.copyText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordCopy(record.cdrJSON)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<CopyOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template #title>{{ t('common.deleteText') }}</template>
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
@click.prevent="fnRecordDelete(record.id)"
|
@click.prevent="fnRecordDelete(record.id)"
|
||||||
|
v-perms:has="['cdr:ne:remove']"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
@@ -704,7 +756,8 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template #expandedRowRender="{ record }">
|
<template #expandedRowRender="{ record }">
|
||||||
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="8" :md="12" :xs="22" :offset="2">
|
||||||
<a-divider orientation="left">
|
<a-divider orientation="left">
|
||||||
{{ t('views.dashboard.cdr.cdrInfo') }}
|
{{ t('views.dashboard.cdr.cdrInfo') }}
|
||||||
</a-divider>
|
</a-divider>
|
||||||
@@ -726,6 +779,8 @@ onBeforeUnmount(() => {
|
|||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
<a-divider orientation="left">
|
<a-divider orientation="left">
|
||||||
{{ t('views.dashboard.cdr.rowInfo') }}
|
{{ t('views.dashboard.cdr.rowInfo') }}
|
||||||
</a-divider>
|
</a-divider>
|
||||||
@@ -755,7 +810,25 @@ onBeforeUnmount(() => {
|
|||||||
{{ t('views.dashboard.cdr.resultOk') }}
|
{{ t('views.dashboard.cdr.resultOk') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</a-col>
|
||||||
|
<a-col
|
||||||
|
:lg="16"
|
||||||
|
:md="16"
|
||||||
|
:xs="22"
|
||||||
|
:offset="2"
|
||||||
|
v-if="
|
||||||
|
record.cdrJSON?.smsContent &&
|
||||||
|
hasPermissions(['cdr:smsc:content'])
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<a-divider orientation="left"> Content </a-divider>
|
||||||
|
<a-typography-paragraph
|
||||||
|
copyable
|
||||||
|
:content="record.cdrJSON.smsContent"
|
||||||
|
>
|
||||||
|
</a-typography-paragraph>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|||||||
@@ -145,66 +145,54 @@ let alarmTableState: TabeStateType = reactive({
|
|||||||
let tableColumns: ColumnsType = [
|
let tableColumns: ColumnsType = [
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.origLevel'),
|
title: t('views.faultManage.activeAlarm.origLevel'),
|
||||||
align: 'center',
|
align: 'left',
|
||||||
key: 'origSeverity',
|
key: 'origSeverity',
|
||||||
dataIndex: 'origSeverity',
|
dataIndex: 'origSeverity',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.alarmTitle'),
|
title: t('views.faultManage.activeAlarm.alarmTitle'),
|
||||||
dataIndex: 'alarmTitle',
|
dataIndex: 'alarmTitle',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 5,
|
width: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.neType'),
|
title: t('views.faultManage.activeAlarm.neType'),
|
||||||
dataIndex: 'neType',
|
dataIndex: 'neType',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.eventTime'),
|
title: t('views.faultManage.activeAlarm.eventTime'),
|
||||||
dataIndex: 'eventTime',
|
dataIndex: 'eventTime',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
sorter: (a: any, b: any) => 1,
|
sorter: (a: any, b: any) => 1,
|
||||||
width: 5,
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.alarmCode'),
|
title: t('views.faultManage.activeAlarm.alarmCode'),
|
||||||
dataIndex: 'alarmCode',
|
dataIndex: 'alarmCode',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.alarmType'),
|
title: t('views.faultManage.activeAlarm.alarmType'),
|
||||||
dataIndex: 'alarmType',
|
dataIndex: 'alarmType',
|
||||||
key: 'alarmType',
|
key: 'alarmType',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 5,
|
width: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.neName'),
|
title: t('views.faultManage.activeAlarm.neName'),
|
||||||
dataIndex: 'neName',
|
dataIndex: 'neName',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.neId'),
|
title: t('views.faultManage.activeAlarm.neId'),
|
||||||
dataIndex: 'neId',
|
dataIndex: 'neId',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.faultManage.activeAlarm.pvFlag'),
|
|
||||||
dataIndex: 'pvFlag',
|
|
||||||
align: 'center',
|
|
||||||
width: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.faultManage.activeAlarm.alarmId'),
|
|
||||||
dataIndex: 'alarmId',
|
|
||||||
align: 'center',
|
|
||||||
width: 5,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -212,20 +200,20 @@ let tableColumns: ColumnsType = [
|
|||||||
dataIndex: 'ackState',
|
dataIndex: 'ackState',
|
||||||
key: 'ackState',
|
key: 'ackState',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.ackUser'),
|
title: t('views.faultManage.activeAlarm.ackUser'),
|
||||||
dataIndex: 'ackUser',
|
dataIndex: 'ackUser',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('common.operate'),
|
title: t('common.operate'),
|
||||||
key: 'alarm_id',
|
key: 'alarm_id',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -1022,12 +1010,12 @@ onMounted(() => {
|
|||||||
:data-source="tableState.data"
|
:data-source="tableState.data"
|
||||||
:size="tableState.size"
|
:size="tableState.size"
|
||||||
:row-selection="{
|
:row-selection="{
|
||||||
columnWidth: 2,
|
columnWidth: 48,
|
||||||
selectedRowKeys: state.selectedRowKeys,
|
selectedRowKeys: state.selectedRowKeys,
|
||||||
onChange: onSelectChange,
|
onChange: onSelectChange,
|
||||||
}"
|
}"
|
||||||
:pagination="tablePagination"
|
:pagination="tablePagination"
|
||||||
:scroll="{ x: 2500, y: 400 }"
|
:scroll="{ x: tableColumns.length * 150, y: 400 }"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'origSeverity'">
|
<template v-if="column.key === 'origSeverity'">
|
||||||
@@ -1213,14 +1201,6 @@ onMounted(() => {
|
|||||||
{{ modalState.from.alarmType }}
|
{{ modalState.from.alarmType }}
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.faultManage.activeAlarm.pvFlag')"
|
|
||||||
name="pvFlag"
|
|
||||||
>
|
|
||||||
{{ modalState.from.pvFlag }}
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<a-form-item
|
<a-form-item
|
||||||
@@ -1231,17 +1211,7 @@ onMounted(() => {
|
|||||||
{{ modalState.from.locationInfo }}
|
{{ modalState.from.locationInfo }}
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-row> </a-row>
|
|
||||||
|
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.faultManage.activeAlarm.province')"
|
|
||||||
name="province"
|
|
||||||
>
|
|
||||||
{{ modalState.from.province }}
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.faultManage.activeAlarm.origLevel')"
|
:label="t('views.faultManage.activeAlarm.origLevel')"
|
||||||
@@ -1290,7 +1260,7 @@ onMounted(() => {
|
|||||||
:label="t('views.faultManage.activeAlarm.ackState')"
|
:label="t('views.faultManage.activeAlarm.ackState')"
|
||||||
name="ackState"
|
name="ackState"
|
||||||
>
|
>
|
||||||
{{ modalState.from.ackState }}
|
<DictTag :options="dict.activeAckState" :value="modalState.from.ackState" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|||||||
@@ -118,61 +118,47 @@ let tableState: TabeStateType = reactive({
|
|||||||
|
|
||||||
/**表格字段列 */
|
/**表格字段列 */
|
||||||
let tableColumns: ColumnsType = [
|
let tableColumns: ColumnsType = [
|
||||||
{
|
|
||||||
title: t('views.faultManage.activeAlarm.alarmId'),
|
|
||||||
dataIndex: 'alarmId',
|
|
||||||
align: 'center',
|
|
||||||
width: 5,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.neId'),
|
title: t('views.faultManage.activeAlarm.neId'),
|
||||||
dataIndex: 'neId',
|
dataIndex: 'neId',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 5,
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.neName'),
|
title: t('views.faultManage.activeAlarm.neName'),
|
||||||
dataIndex: 'neName',
|
dataIndex: 'neName',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 5,
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.neType'),
|
title: t('views.faultManage.activeAlarm.neType'),
|
||||||
dataIndex: 'neType',
|
dataIndex: 'neType',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 5,
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.alarmCode'),
|
title: t('views.faultManage.activeAlarm.alarmCode'),
|
||||||
dataIndex: 'alarmCode',
|
dataIndex: 'alarmCode',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.alarmTitle'),
|
title: t('views.faultManage.activeAlarm.alarmTitle'),
|
||||||
dataIndex: 'alarmTitle',
|
dataIndex: 'alarmTitle',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 5,
|
width: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.eventTime'),
|
title: t('views.faultManage.activeAlarm.eventTime'),
|
||||||
dataIndex: 'eventTime',
|
dataIndex: 'eventTime',
|
||||||
align: 'center',
|
|
||||||
sorter: (a: any, b: any) => 1,
|
sorter: (a: any, b: any) => 1,
|
||||||
width: 5,
|
align: 'left',
|
||||||
},
|
width: 200,
|
||||||
{
|
|
||||||
title: t('views.faultManage.activeAlarm.pvFlag'),
|
|
||||||
dataIndex: 'pvFlag',
|
|
||||||
align: 'center',
|
|
||||||
width: 5,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('common.operate'),
|
title: t('common.operate'),
|
||||||
key: 'alarm_id',
|
key: 'alarm_id',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
fixed: 'right',
|
|
||||||
width: 5,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -316,7 +302,7 @@ function mapKeysWithReduce(data: any, titleMapping: any) {
|
|||||||
const title = titleMapping[key] || key;
|
const title = titleMapping[key] || key;
|
||||||
newItem[title] = item[key];
|
newItem[title] = item[key];
|
||||||
return newItem;
|
return newItem;
|
||||||
});
|
}, {});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -626,7 +612,7 @@ onMounted(() => {
|
|||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<TableColumnsDnd
|
<TableColumnsDnd
|
||||||
cache-id="alarmActive"
|
cache-id="alarmEvent"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
v-model:columns-dnd="tableColumnsDnd"
|
v-model:columns-dnd="tableColumnsDnd"
|
||||||
></TableColumnsDnd>
|
></TableColumnsDnd>
|
||||||
|
|||||||
@@ -122,108 +122,96 @@ let tableState: TabeStateType = reactive({
|
|||||||
|
|
||||||
/**表格字段列 */
|
/**表格字段列 */
|
||||||
let tableColumns: ColumnsType = [
|
let tableColumns: ColumnsType = [
|
||||||
{
|
|
||||||
title: t('views.faultManage.activeAlarm.alarmId'),
|
|
||||||
dataIndex: 'alarmId',
|
|
||||||
align: 'center',
|
|
||||||
width: 5,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.neId'),
|
title: t('views.faultManage.activeAlarm.neId'),
|
||||||
dataIndex: 'neId',
|
dataIndex: 'neId',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.neName'),
|
title: t('views.faultManage.activeAlarm.neName'),
|
||||||
dataIndex: 'neName',
|
dataIndex: 'neName',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.neType'),
|
title: t('views.faultManage.activeAlarm.neType'),
|
||||||
dataIndex: 'neType',
|
dataIndex: 'neType',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.origLevel'),
|
title: t('views.faultManage.activeAlarm.origLevel'),
|
||||||
align: 'left',
|
align: 'left',
|
||||||
dataIndex: 'origSeverity',
|
dataIndex: 'origSeverity',
|
||||||
key: 'origSeverity',
|
key: 'origSeverity',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.alarmCode'),
|
title: t('views.faultManage.activeAlarm.alarmCode'),
|
||||||
dataIndex: 'alarmCode',
|
dataIndex: 'alarmCode',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.alarmTitle'),
|
title: t('views.faultManage.activeAlarm.alarmTitle'),
|
||||||
dataIndex: 'alarmTitle',
|
dataIndex: 'alarmTitle',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 5,
|
width: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.eventTime'),
|
title: t('views.faultManage.activeAlarm.eventTime'),
|
||||||
dataIndex: 'eventTime',
|
dataIndex: 'eventTime',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
sorter: (a: any, b: any) => 1,
|
sorter: (a: any, b: any) => 1,
|
||||||
width: 5,
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.alarmType'),
|
title: t('views.faultManage.activeAlarm.alarmType'),
|
||||||
dataIndex: 'alarmType',
|
dataIndex: 'alarmType',
|
||||||
key: 'alarmType',
|
key: 'alarmType',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 5,
|
width: 120,
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.faultManage.activeAlarm.pvFlag'),
|
|
||||||
dataIndex: 'pvFlag',
|
|
||||||
align: 'center',
|
|
||||||
width: 5,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.clearUser'),
|
title: t('views.faultManage.activeAlarm.clearUser'),
|
||||||
dataIndex: 'clearUser',
|
dataIndex: 'clearUser',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.clearType'),
|
title: t('views.faultManage.activeAlarm.clearType'),
|
||||||
dataIndex: 'clearType',
|
dataIndex: 'clearType',
|
||||||
key: 'clearType',
|
key: 'clearType',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 5,
|
width: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.clearTime'),
|
title: t('views.faultManage.activeAlarm.clearTime'),
|
||||||
dataIndex: 'clearTime',
|
dataIndex: 'clearTime',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
sorter: (a: any, b: any) => 1,
|
sorter: (a: any, b: any) => 1,
|
||||||
width: 5,
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.ackState'),
|
title: t('views.faultManage.activeAlarm.ackState'),
|
||||||
dataIndex: 'ackState',
|
dataIndex: 'ackState',
|
||||||
key: 'ackState',
|
key: 'ackState',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.ackUser'),
|
title: t('views.faultManage.activeAlarm.ackUser'),
|
||||||
dataIndex: 'ackUser',
|
dataIndex: 'ackUser',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 5,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('common.operate'),
|
title: t('common.operate'),
|
||||||
key: 'alarm_id',
|
key: 'alarm_id',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
width: 5,
|
width: 120,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -648,7 +636,7 @@ onMounted(() => {
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
<a-row>
|
<a-row :gutter="16">
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.faultManage.activeAlarm.alarmCode')"
|
:label="t('views.faultManage.activeAlarm.alarmCode')"
|
||||||
@@ -688,22 +676,7 @@ onMounted(() => {
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
<a-row>
|
<a-row :gutter="16">
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.faultManage.activeAlarm.pvFlag')"
|
|
||||||
name="pv_flag"
|
|
||||||
>
|
|
||||||
<a-select
|
|
||||||
v-model:value="queryParams.pv_flag"
|
|
||||||
:placeholder="t('common.selectPlease')"
|
|
||||||
:options="[
|
|
||||||
{ label: 'PNF', value: 'PNF' },
|
|
||||||
{ label: 'VNF', value: 'VNF' },
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.faultManage.activeAlarm.alarmType')"
|
:label="t('views.faultManage.activeAlarm.alarmType')"
|
||||||
@@ -804,12 +777,12 @@ onMounted(() => {
|
|||||||
:data-source="tableState.data"
|
:data-source="tableState.data"
|
||||||
:size="tableState.size"
|
:size="tableState.size"
|
||||||
:row-selection="{
|
:row-selection="{
|
||||||
columnWidth: 3,
|
columnWidth: 48,
|
||||||
selectedRowKeys: state.selectedRowKeys,
|
selectedRowKeys: state.selectedRowKeys,
|
||||||
onChange: onSelectChange,
|
onChange: onSelectChange,
|
||||||
}"
|
}"
|
||||||
:pagination="tablePagination"
|
:pagination="tablePagination"
|
||||||
:scroll="{ x: tableColumns.length * 120, y: 400 }"
|
:scroll="{ x: tableColumns.length * 150, y: 400 }"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'origSeverity'">
|
<template v-if="column.key === 'origSeverity'">
|
||||||
@@ -863,6 +836,7 @@ onMounted(() => {
|
|||||||
@ok="fnModalOk"
|
@ok="fnModalOk"
|
||||||
:ok-text="t('views.faultManage.activeAlarm.confirm')"
|
:ok-text="t('views.faultManage.activeAlarm.confirm')"
|
||||||
@cancel="fnModalCancel"
|
@cancel="fnModalCancel"
|
||||||
|
:footer="null"
|
||||||
>
|
>
|
||||||
<a-form
|
<a-form
|
||||||
name="modalStateFrom"
|
name="modalStateFrom"
|
||||||
@@ -946,15 +920,6 @@ onMounted(() => {
|
|||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.faultManage.activeAlarm.pvFlag')"
|
|
||||||
name="pvFlag"
|
|
||||||
>
|
|
||||||
{{ modalState.from.pvFlag }}
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.faultManage.activeAlarm.alarmType')"
|
:label="t('views.faultManage.activeAlarm.alarmType')"
|
||||||
@@ -973,14 +938,6 @@ onMounted(() => {
|
|||||||
{{ modalState.from.locationInfo }}
|
{{ modalState.from.locationInfo }}
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.faultManage.activeAlarm.province')"
|
|
||||||
name="province"
|
|
||||||
>
|
|
||||||
{{ modalState.from.province }}
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.faultManage.activeAlarm.origLevel')"
|
:label="t('views.faultManage.activeAlarm.origLevel')"
|
||||||
@@ -1019,7 +976,10 @@ onMounted(() => {
|
|||||||
:label="t('views.faultManage.activeAlarm.clearType')"
|
:label="t('views.faultManage.activeAlarm.clearType')"
|
||||||
name="clearType"
|
name="clearType"
|
||||||
>
|
>
|
||||||
{{ modalState.from.clearType }}
|
<DictTag
|
||||||
|
:options="dict.activeClearType"
|
||||||
|
:value="modalState.from.clearType"
|
||||||
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
@@ -1046,7 +1006,10 @@ onMounted(() => {
|
|||||||
:label="t('views.faultManage.activeAlarm.ackState')"
|
:label="t('views.faultManage.activeAlarm.ackState')"
|
||||||
name="ackState"
|
name="ackState"
|
||||||
>
|
>
|
||||||
{{ modalState.from.ackState }}
|
<DictTag
|
||||||
|
:options="dict.activeAckState"
|
||||||
|
:value="modalState.from.ackState"
|
||||||
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ onMounted(() => {
|
|||||||
getConfigKey('sys.homePage').then(res => {
|
getConfigKey('sys.homePage').then(res => {
|
||||||
spinning.value = false;
|
spinning.value = false;
|
||||||
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
||||||
console.log(spinning);
|
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
const asyncComponent = findView(`${res.data}`);
|
const asyncComponent = findView(`${res.data}`);
|
||||||
currentComponent.value = defineAsyncComponent(asyncComponent);
|
currentComponent.value = defineAsyncComponent(asyncComponent);
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, ref, onMounted, toRaw } from 'vue';
|
import { reactive, ref, onMounted, toRaw } from 'vue';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { ProModal } from 'antdv-pro-modal';
|
||||||
import { SizeType } from 'ant-design-vue/es/config-provider';
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
import { ColumnsType } from 'ant-design-vue/es/table';
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
import { Modal, message } from 'ant-design-vue/es';
|
import { Form, Modal, message } from 'ant-design-vue/es';
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
import {
|
import {
|
||||||
getBakFile,
|
getBakFile,
|
||||||
getBakFileList,
|
getBakFileList,
|
||||||
downFile,
|
downFile,
|
||||||
delFile,
|
delFile,
|
||||||
|
updateFTPInfo,
|
||||||
|
getFTPInfo,
|
||||||
|
putFTPInfo,
|
||||||
} from '@/api/logManage/exportFile';
|
} from '@/api/logManage/exportFile';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import saveAs from 'file-saver';
|
import saveAs from 'file-saver';
|
||||||
|
import { regExpIPv4 } from '@/utils/regular-utils';
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
/**网元参数 */
|
/**网元参数 */
|
||||||
@@ -269,6 +274,131 @@ onMounted(() => {
|
|||||||
// fnGetList();
|
// fnGetList();
|
||||||
// });
|
// });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**新增框或修改框是否显示 */
|
||||||
|
openByEdit: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**表单数据 */
|
||||||
|
from: Record<string, any>;
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**FTP日志对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
openByEdit: false,
|
||||||
|
title: '设置远程备份配置',
|
||||||
|
from: {
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
toIp: '',
|
||||||
|
toPort: 22,
|
||||||
|
enable: false,
|
||||||
|
dir: '',
|
||||||
|
},
|
||||||
|
confirmLoading: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**FTP日志对象信息内表单属性和校验规则 */
|
||||||
|
const modalStateFrom = Form.useForm(
|
||||||
|
modalState.from,
|
||||||
|
reactive({
|
||||||
|
toIp: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
pattern: regExpIPv4,
|
||||||
|
message: 'Please enter the service login IP',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
username: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
trigger: 'blur',
|
||||||
|
message: 'Please enter the service login user name',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
dir: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
trigger: 'blur',
|
||||||
|
message: 'Please enter the service address target file directory',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出显示为 新增或者修改
|
||||||
|
* @param configId 参数编号id, 不传为新增
|
||||||
|
*/
|
||||||
|
function fnModalVisibleByEdit() {
|
||||||
|
if (modalState.confirmLoading) return;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
getFTPInfo().then(res => {
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
hide();
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
||||||
|
modalState.from = Object.assign(modalState.from, res.data);
|
||||||
|
modalState.title = 'Setting Remote Backup';
|
||||||
|
modalState.openByEdit = true;
|
||||||
|
} else {
|
||||||
|
message.error(res.msg, 3);
|
||||||
|
modalState.title = 'Setting Remote Backup';
|
||||||
|
modalState.openByEdit = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**FTP对象保存 */
|
||||||
|
function fnModalOk() {
|
||||||
|
modalStateFrom.validate().then(() => {
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const from = toRaw(modalState.from);
|
||||||
|
updateFTPInfo(from)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success(`Configuration saved successfully`, 3);
|
||||||
|
fnModalCancel();
|
||||||
|
} else {
|
||||||
|
message.warning(`Configuration save exception`, 3);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出关闭执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalCancel() {
|
||||||
|
modalState.openByEdit = false;
|
||||||
|
modalStateFrom.resetFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步文件到FTP
|
||||||
|
* @param row
|
||||||
|
*/
|
||||||
|
function fnSyncFileToFTP(row: Record<string, any>) {
|
||||||
|
putFTPInfo(row.filePath, row.fileName)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success(t('common.operateOk'), 3);
|
||||||
|
} else {
|
||||||
|
message.warning(res.msg, 3);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -305,6 +435,12 @@ onMounted(() => {
|
|||||||
<!-- 插槽-卡片右侧 -->
|
<!-- 插槽-卡片右侧 -->
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>Setting Remote Backup</template>
|
||||||
|
<a-button type="text" @click.prevent="fnModalVisibleByEdit()">
|
||||||
|
<template #icon><DeliveredProcedureOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template #title>{{ t('common.reloadText') }}</template>
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
<a-button type="text" @click.prevent="fnGetList()">
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
@@ -328,6 +464,14 @@ onMounted(() => {
|
|||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'fileName'">
|
<template v-if="column.key === 'fileName'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
:loading="downLoading"
|
||||||
|
@click.prevent="fnSyncFileToFTP(record)"
|
||||||
|
v-if="record.fileType === 'file'"
|
||||||
|
>
|
||||||
|
<template #icon><CloudServerOutlined /></template>
|
||||||
|
</a-button>
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
:loading="downLoading"
|
:loading="downLoading"
|
||||||
@@ -349,6 +493,106 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 新增框或修改框 -->
|
||||||
|
<ProModal
|
||||||
|
:drag="true"
|
||||||
|
:width="800"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
:keyboard="false"
|
||||||
|
:mask-closable="false"
|
||||||
|
:open="modalState.openByEdit"
|
||||||
|
:title="modalState.title"
|
||||||
|
:confirm-loading="modalState.confirmLoading"
|
||||||
|
@ok="fnModalOk"
|
||||||
|
@cancel="fnModalCancel"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
name="modalStateFrom"
|
||||||
|
layout="horizontal"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-form-item label="Enable" name="enable" :label-col="{ span: 3 }">
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="modalState.from.enable"
|
||||||
|
:checked-children="t('common.switch.open')"
|
||||||
|
:un-checked-children="t('common.switch.shut')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<template v-if="modalState.from.enable">
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
label="Service IP"
|
||||||
|
name="toIp"
|
||||||
|
v-bind="modalStateFrom.validateInfos.toIp"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.toIp"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
label="Service Port"
|
||||||
|
name="toPort"
|
||||||
|
v-bind="modalStateFrom.validateInfos.toPort"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="modalState.from.toPort"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
label="UserName"
|
||||||
|
name="username"
|
||||||
|
v-bind="modalStateFrom.validateInfos.username"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.username"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
label="Password"
|
||||||
|
name="password"
|
||||||
|
v-bind="modalStateFrom.validateInfos.password"
|
||||||
|
>
|
||||||
|
<a-input-password
|
||||||
|
v-model:value="modalState.from.password"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input-password>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-form-item
|
||||||
|
label="Save Dir"
|
||||||
|
name="dir"
|
||||||
|
v-bind="modalStateFrom.validateInfos.dir"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.dir"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
</a-form>
|
||||||
|
</ProModal>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ async function fnGetState() {
|
|||||||
graphG6.value.setItemState(neShape, 'neState', ne.serverState.online);
|
graphG6.value.setItemState(neShape, 'neState', ne.serverState.online);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 15_000));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**查询全部网元数据列表 */
|
/**查询全部网元数据列表 */
|
||||||
|
|||||||
@@ -92,13 +92,13 @@ const graphNodeMenu = new Menu({
|
|||||||
${neState.neName ?? '--'}
|
${neState.neName ?? '--'}
|
||||||
</h3>
|
</h3>
|
||||||
<div id="restart" style="cursor: pointer; margin-bottom: 4px">
|
<div id="restart" style="cursor: pointer; margin-bottom: 4px">
|
||||||
> ${t('views.configManage.neManage.restart')}
|
> ${t('views.ne.common.restart')}
|
||||||
</div>
|
</div>
|
||||||
<div id="stop" style="cursor: pointer; margin-bottom: 4px;">
|
<div id="stop" style="cursor: pointer; margin-bottom: 4px;">
|
||||||
> ${t('views.configManage.neManage.stop')}
|
> ${t('views.ne.common.stop')}
|
||||||
</div>
|
</div>
|
||||||
<div id="log" style="cursor: pointer; margin-bottom: 4px;">
|
<div id="log" style="cursor: pointer; margin-bottom: 4px;">
|
||||||
> ${t('views.configManage.neManage.log')}
|
> ${t('views.ne.common.log')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -130,26 +130,17 @@ export function edgeLineAnimateState() {
|
|||||||
|
|
||||||
// circle-move 圆点沿边运动
|
// circle-move 圆点沿边运动
|
||||||
if (name === 'circle-move') {
|
if (name === 'circle-move') {
|
||||||
let back1 = group.find(
|
const backArr = group.findAll((ele: any) =>
|
||||||
(ele: any) => ele.get('name') === 'circle-stroke1'
|
ele.get('name').startsWith('circle-stroke')
|
||||||
);
|
);
|
||||||
if (back1) {
|
|
||||||
back1.remove();
|
|
||||||
back1.destroy();
|
|
||||||
}
|
|
||||||
let back2 = group.find(
|
|
||||||
(ele: any) => ele.get('name') === 'circle-stroke2'
|
|
||||||
);
|
|
||||||
if (back2) {
|
|
||||||
back2.remove();
|
|
||||||
back2.destroy();
|
|
||||||
}
|
|
||||||
if (value) {
|
if (value) {
|
||||||
|
if (backArr.length > 0) return;
|
||||||
|
|
||||||
// 第一个矩形边
|
// 第一个矩形边
|
||||||
const fillColor = typeof value === 'string' ? value : '#1890ff';
|
const fillColor = typeof value === 'string' ? value : '#1890ff';
|
||||||
// 边缘路径的起始位置
|
// 边缘路径的起始位置
|
||||||
const startPoint = keyShape.getPoint(0);
|
const startPoint = keyShape.getPoint(0);
|
||||||
back1 = group.addShape('circle', {
|
const back1 = group.addShape('circle', {
|
||||||
attrs: {
|
attrs: {
|
||||||
x: startPoint.x,
|
x: startPoint.x,
|
||||||
y: startPoint.y,
|
y: startPoint.y,
|
||||||
@@ -159,6 +150,7 @@ export function edgeLineAnimateState() {
|
|||||||
// 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性
|
// 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性
|
||||||
name: 'circle-stroke1',
|
name: 'circle-stroke1',
|
||||||
});
|
});
|
||||||
|
back1.show();
|
||||||
back1.animate(
|
back1.animate(
|
||||||
(ratio: any) => {
|
(ratio: any) => {
|
||||||
// 每帧中的操作。比率范围从 0 到 1,表示动画的进度。返回修改后的配置
|
// 每帧中的操作。比率范围从 0 到 1,表示动画的进度。返回修改后的配置
|
||||||
@@ -177,7 +169,7 @@ export function edgeLineAnimateState() {
|
|||||||
);
|
);
|
||||||
// 第二个矩形边
|
// 第二个矩形边
|
||||||
const endPoint = keyShape.getPoint(1);
|
const endPoint = keyShape.getPoint(1);
|
||||||
back2 = group.addShape('circle', {
|
const back2 = group.addShape('circle', {
|
||||||
zIndex: -2,
|
zIndex: -2,
|
||||||
attrs: {
|
attrs: {
|
||||||
x: endPoint.x,
|
x: endPoint.x,
|
||||||
@@ -188,7 +180,7 @@ export function edgeLineAnimateState() {
|
|||||||
// 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性
|
// 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性
|
||||||
name: 'circle-stroke2',
|
name: 'circle-stroke2',
|
||||||
});
|
});
|
||||||
|
back2.show();
|
||||||
back2.animate(
|
back2.animate(
|
||||||
(ratio: any) => {
|
(ratio: any) => {
|
||||||
// 每帧中的操作。比率范围从 0 到 1,表示动画的进度。返回修改后的配置
|
// 每帧中的操作。比率范围从 0 到 1,表示动画的进度。返回修改后的配置
|
||||||
@@ -205,6 +197,14 @@ export function edgeLineAnimateState() {
|
|||||||
duration: 2 * 1000, // 执行一次的持续时间
|
duration: 2 * 1000, // 执行一次的持续时间
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
if (backArr.length <= 0) return;
|
||||||
|
backArr.forEach((ele: any) => {
|
||||||
|
ele.hide();
|
||||||
|
ele.remove();
|
||||||
|
ele.destroy();
|
||||||
|
});
|
||||||
|
backArr.length = 0;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -212,11 +212,7 @@ export function edgeLineAnimateState() {
|
|||||||
// line-dash 虚线运动
|
// line-dash 虚线运动
|
||||||
if (name === 'line-dash') {
|
if (name === 'line-dash') {
|
||||||
if (value) {
|
if (value) {
|
||||||
keyShape.stopAnimate();
|
if (keyShape.cfg.animating) return;
|
||||||
keyShape.attr({
|
|
||||||
lineDash: null,
|
|
||||||
lineDashOffset: null,
|
|
||||||
});
|
|
||||||
let index = 0;
|
let index = 0;
|
||||||
keyShape.animate(
|
keyShape.animate(
|
||||||
() => {
|
() => {
|
||||||
@@ -234,6 +230,12 @@ export function edgeLineAnimateState() {
|
|||||||
duration: 3000, // 执行一次的持续时间
|
duration: 3000, // 执行一次的持续时间
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
keyShape.stopAnimate();
|
||||||
|
keyShape.attr({
|
||||||
|
lineDash: null,
|
||||||
|
lineDashOffset: null,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -246,6 +248,7 @@ export function edgeLineAnimateState() {
|
|||||||
back.remove();
|
back.remove();
|
||||||
back.destroy();
|
back.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
const { path, stroke, lineWidth } = keyShape.attr();
|
const { path, stroke, lineWidth } = keyShape.attr();
|
||||||
back = group.addShape('path', {
|
back = group.addShape('path', {
|
||||||
attrs: {
|
attrs: {
|
||||||
|
|||||||
@@ -300,12 +300,23 @@ export function nodeImageAnimateState() {
|
|||||||
ele.get('name').startsWith('circle-shape')
|
ele.get('name').startsWith('circle-shape')
|
||||||
);
|
);
|
||||||
if (value) {
|
if (value) {
|
||||||
if (Array.isArray(backArr) && backArr.length === 3) return;
|
const fillColor = typeof value === 'string' ? value : '#f5222d';
|
||||||
|
// 移除
|
||||||
|
if (Array.isArray(backArr) && backArr.length >= 3) {
|
||||||
|
for (const back of backArr) {
|
||||||
|
back.stopAnimate();
|
||||||
|
back.hide();
|
||||||
|
back.remove();
|
||||||
|
back.destroy();
|
||||||
|
}
|
||||||
|
backArr.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据大小确定半径
|
||||||
const size = Array.isArray(model?.size) ? model?.size : [40, 40];
|
const size = Array.isArray(model?.size) ? model?.size : [40, 40];
|
||||||
const x = size[0] / 2;
|
const x = size[0] / 2;
|
||||||
const y = -size[1] / 2;
|
const y = -size[1] / 2;
|
||||||
const r = 3;
|
const r = 3;
|
||||||
const fillColor = typeof value === 'string' ? value : '#f5222d';
|
|
||||||
|
|
||||||
// 第一个背景圆
|
// 第一个背景圆
|
||||||
const back1 = group.addShape('circle', {
|
const back1 = group.addShape('circle', {
|
||||||
|
|||||||
@@ -0,0 +1,383 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, toRaw, watch } from 'vue';
|
||||||
|
import { ProModal } from 'antdv-pro-modal';
|
||||||
|
import { message, Modal } from 'ant-design-vue/es';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { exportNBState, listNBState } from '@/api/neData/nb-state';
|
||||||
|
import { Dayjs } from 'dayjs';
|
||||||
|
const { t } = useI18n();
|
||||||
|
const emit = defineEmits(['cancel', 'update:open']);
|
||||||
|
const props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '标题',
|
||||||
|
},
|
||||||
|
open: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
/**网元ID */
|
||||||
|
neId: {
|
||||||
|
type: String,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
neType: {
|
||||||
|
type: String,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**开始结束时间 */
|
||||||
|
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>(undefined);
|
||||||
|
/**状态字典 */
|
||||||
|
const nbStateOptions = ref<DictType[]>([
|
||||||
|
{
|
||||||
|
value: 'ON',
|
||||||
|
label: t('views.neData.baseStation.online'),
|
||||||
|
tagType: 'green',
|
||||||
|
tagClass: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'OFF',
|
||||||
|
label: t('views.neData.baseStation.offline'),
|
||||||
|
tagType: 'red',
|
||||||
|
tagClass: '',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元 */
|
||||||
|
neType: '',
|
||||||
|
neId: '',
|
||||||
|
/**排序字段 */
|
||||||
|
sortField: 'id',
|
||||||
|
sortOrder: 'desc',
|
||||||
|
/**状态 */
|
||||||
|
status: undefined as undefined | string,
|
||||||
|
/**开始时间 */
|
||||||
|
startTime: undefined as undefined | number,
|
||||||
|
/**结束时间 */
|
||||||
|
endTime: undefined as undefined | number,
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
queryParams = Object.assign(queryParams, {
|
||||||
|
status: undefined,
|
||||||
|
startTime: undefined,
|
||||||
|
endTime: undefined,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
queryRangePicker.value = undefined;
|
||||||
|
tablePagination.current = 1;
|
||||||
|
tablePagination.pageSize = 20;
|
||||||
|
fnGetList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**搜索栏 */
|
||||||
|
seached: boolean;
|
||||||
|
/**记录数据 */
|
||||||
|
data: object[];
|
||||||
|
/**勾选记录 */
|
||||||
|
selectedRowKeys: (string | number)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
size: 'middle',
|
||||||
|
seached: true,
|
||||||
|
data: [],
|
||||||
|
selectedRowKeys: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
let tableColumns = ref<ColumnsType>([
|
||||||
|
{
|
||||||
|
title: t('common.rowId'),
|
||||||
|
dataIndex: 'id',
|
||||||
|
align: 'left',
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.neData.baseStation.name'),
|
||||||
|
dataIndex: 'name',
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 120,
|
||||||
|
minWidth: 100,
|
||||||
|
maxWidth: 250,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.neData.baseStation.position'),
|
||||||
|
dataIndex: 'position',
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 150,
|
||||||
|
minWidth: 100,
|
||||||
|
maxWidth: 400,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.neData.baseStation.address'),
|
||||||
|
dataIndex: 'address',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.neData.baseStation.nbName'),
|
||||||
|
dataIndex: 'nbName',
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
minWidth: 100,
|
||||||
|
maxWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.neData.baseStation.state'),
|
||||||
|
dataIndex: 'state',
|
||||||
|
key: 'state',
|
||||||
|
align: 'left',
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.neData.baseStation.time'),
|
||||||
|
dataIndex: 'time',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
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: total }),
|
||||||
|
onChange: (page: number, pageSize: number) => {
|
||||||
|
tablePagination.current = page;
|
||||||
|
tablePagination.pageSize = pageSize;
|
||||||
|
queryParams.pageNum = page;
|
||||||
|
queryParams.pageSize = pageSize;
|
||||||
|
fnGetList();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**列表导出 */
|
||||||
|
function fnExportList() {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.neData.baseStation.exportTip'),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
const querys = toRaw(queryParams);
|
||||||
|
exportNBState(querys)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
saveAs(res.data, `nb_state_history_records_export_${Date.now()}.xlsx`);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询字典数据列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时间范围
|
||||||
|
if (
|
||||||
|
Array.isArray(queryRangePicker.value) &&
|
||||||
|
queryRangePicker.value.length > 0
|
||||||
|
) {
|
||||||
|
queryParams.startTime = queryRangePicker.value[0].valueOf();
|
||||||
|
queryParams.endTime = queryRangePicker.value[1].valueOf();
|
||||||
|
} else {
|
||||||
|
queryParams.startTime = undefined;
|
||||||
|
queryParams.endTime = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
listNBState(toRaw(queryParams)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
|
// 取消勾选
|
||||||
|
if (tableState.selectedRowKeys.length > 0) {
|
||||||
|
tableState.selectedRowKeys = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
tablePagination.total = res.total;
|
||||||
|
tableState.data = res.rows;
|
||||||
|
if (
|
||||||
|
tablePagination.total <=
|
||||||
|
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||||
|
queryParams.pageNum !== 1
|
||||||
|
) {
|
||||||
|
tableState.loading = false;
|
||||||
|
fnGetList(queryParams.pageNum - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**弹框取消按钮事件 */
|
||||||
|
function fnModalCancel() {
|
||||||
|
emit('update:open', false);
|
||||||
|
emit('cancel');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**监听是否显示,初始数据 */
|
||||||
|
watch(
|
||||||
|
() => props.open,
|
||||||
|
val => {
|
||||||
|
if (val) {
|
||||||
|
queryParams.neType = props.neType;
|
||||||
|
queryParams.neId = props.neId;
|
||||||
|
// 获取列表数据
|
||||||
|
fnGetList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ProModal
|
||||||
|
:drag="true"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
:width="1200"
|
||||||
|
:title="props.title"
|
||||||
|
:open="props.open"
|
||||||
|
:keyboard="false"
|
||||||
|
:mask-closable="false"
|
||||||
|
@cancel="fnModalCancel"
|
||||||
|
:footer="null"
|
||||||
|
>
|
||||||
|
<!-- 表格搜索栏 -->
|
||||||
|
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||||
|
<a-row :gutter="16" style="margin-left: 0; margin-right: 0">
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.neData.baseStation.state')"
|
||||||
|
name="status"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.status"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
:options="nbStateOptions"
|
||||||
|
>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="16" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.neData.baseStation.time')"
|
||||||
|
name="queryRangePicker"
|
||||||
|
>
|
||||||
|
<a-range-picker
|
||||||
|
v-model:value="queryRangePicker"
|
||||||
|
:bordered="true"
|
||||||
|
:allow-clear="true"
|
||||||
|
style="width: 100%"
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
></a-range-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="24" :md="24" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="fnQueryReset">
|
||||||
|
<template #icon><ClearOutlined /></template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="dashed" @click.prevent="fnExportList()">
|
||||||
|
<template #icon><ExportOutlined /></template>
|
||||||
|
{{ t('common.export') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="id"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: tableColumns.length * 120, y: 'calc(100vh - 480px)' }"
|
||||||
|
@resizeColumn="(w:number, col:any) => (col.width = w)"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'state'">
|
||||||
|
<DictTag
|
||||||
|
:options="nbStateOptions"
|
||||||
|
:value="record.state"
|
||||||
|
value-default="OFF"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</ProModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import ListComponent from './list.vue';
|
||||||
|
import TopologyComponent from './topology.vue';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
const { t } = useI18n();
|
||||||
|
const value = ref<string>('list');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<template #extra>
|
||||||
|
<a-radio-group v-model:value="value" button-style="solid">
|
||||||
|
<a-radio-button value="list">
|
||||||
|
{{ t('views.neData.baseStation.list') }}
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="topology">
|
||||||
|
{{ t('views.neData.baseStation.topology') }}
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div v-show="value === 'list'">
|
||||||
|
<ListComponent></ListComponent>
|
||||||
|
</div>
|
||||||
|
<div v-if="value === 'topology'">
|
||||||
|
<TopologyComponent></TopologyComponent>
|
||||||
|
</div>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -0,0 +1,958 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
reactive,
|
||||||
|
ref,
|
||||||
|
onMounted,
|
||||||
|
toRaw,
|
||||||
|
computed,
|
||||||
|
defineAsyncComponent,
|
||||||
|
} from 'vue';
|
||||||
|
import { Form, message, Modal } from 'ant-design-vue';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
|
import { ProModal } from 'antdv-pro-modal';
|
||||||
|
import UploadModal from '@/components/UploadModal/index.vue';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
const { t, currentLocale } = useI18n();
|
||||||
|
import {
|
||||||
|
addAMFNbState,
|
||||||
|
delAMFNbState,
|
||||||
|
editAMFNbState,
|
||||||
|
listAMFNbStatelist,
|
||||||
|
} from '@/api/neData/amf';
|
||||||
|
import {
|
||||||
|
addMMENbState,
|
||||||
|
delMMENbState,
|
||||||
|
editMMENbState,
|
||||||
|
listMMENbStatelist,
|
||||||
|
} from '@/api/neData/mme';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import saveAs from 'file-saver';
|
||||||
|
import { readSheet, writeSheet } from '@/utils/execl-utils';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
const route = useRoute();
|
||||||
|
// 异步加载组件
|
||||||
|
const HistoryModal = defineAsyncComponent(
|
||||||
|
() => import('./components/history.vue')
|
||||||
|
);
|
||||||
|
|
||||||
|
const nbState = ref<DictType[]>([
|
||||||
|
{
|
||||||
|
value: 'ON',
|
||||||
|
label: t('views.neData.baseStation.online'),
|
||||||
|
tagType: 'green',
|
||||||
|
tagClass: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'OFF',
|
||||||
|
label: t('views.neData.baseStation.offline'),
|
||||||
|
tagType: 'red',
|
||||||
|
tagClass: '',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**网元参数 */
|
||||||
|
let neCascaderOptions = ref<Record<string, any>[]>([]);
|
||||||
|
/**网元数据 */
|
||||||
|
let neTypeAndId = ref<string[]>([]);
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元ID */
|
||||||
|
neId: '',
|
||||||
|
/**IMSI */
|
||||||
|
state: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
queryParams = Object.assign(queryParams, {
|
||||||
|
state: undefined,
|
||||||
|
});
|
||||||
|
fnGetList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**记录数据 */
|
||||||
|
data: Record<string, any>[];
|
||||||
|
/**勾选记录 */
|
||||||
|
selectedRowKeys: (string | number)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
size: 'small',
|
||||||
|
data: [],
|
||||||
|
selectedRowKeys: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
let tableColumns = ref<ColumnsType>([
|
||||||
|
// {
|
||||||
|
// title: t('common.rowId'),
|
||||||
|
// dataIndex: 'index',
|
||||||
|
// align: 'left',
|
||||||
|
// width: 80,
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
title: t('views.neData.baseStation.name'),
|
||||||
|
dataIndex: 'name',
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 120,
|
||||||
|
minWidth: 100,
|
||||||
|
maxWidth: 250,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.neData.baseStation.position'),
|
||||||
|
dataIndex: 'position',
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 150,
|
||||||
|
minWidth: 100,
|
||||||
|
maxWidth: 400,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.neData.baseStation.address'),
|
||||||
|
dataIndex: 'address',
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
minWidth: 100,
|
||||||
|
maxWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.neData.baseStation.nbName'),
|
||||||
|
dataIndex: 'nbName',
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
minWidth: 100,
|
||||||
|
maxWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.neData.baseStation.ueNum'),
|
||||||
|
dataIndex: 'ueNum',
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 80,
|
||||||
|
minWidth: 80,
|
||||||
|
maxWidth: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.neData.baseStation.state'),
|
||||||
|
dataIndex: 'state',
|
||||||
|
key: 'state',
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 80,
|
||||||
|
minWidth: 80,
|
||||||
|
maxWidth: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.neData.baseStation.time'),
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const record = opt.value;
|
||||||
|
if (record.state === 'OFF') {
|
||||||
|
return record.offTime || '-';
|
||||||
|
}
|
||||||
|
return record.onTime || '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
let tablePagination = {
|
||||||
|
/**当前页数 */
|
||||||
|
current: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
/**默认的每页条数 */
|
||||||
|
defaultPageSize: 20,
|
||||||
|
/**指定每页可以显示多少条 */
|
||||||
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
|
/**只有一页时是否隐藏分页器 */
|
||||||
|
hideOnSinglePage: true,
|
||||||
|
/**是否可以快速跳转至某页 */
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格多选 */
|
||||||
|
function fnTableSelectedRowKeys(keys: (string | number)[]) {
|
||||||
|
tableState.selectedRowKeys = keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录删除
|
||||||
|
* @param index ID
|
||||||
|
*/
|
||||||
|
function fnRecordDelete(index: string) {
|
||||||
|
const [neType, neId] = neTypeAndId.value;
|
||||||
|
if (!neId) return;
|
||||||
|
let msg = `Delete index as:${index}`;
|
||||||
|
if (index === '0') {
|
||||||
|
msg = `Remove the index checkbox:${tableState.selectedRowKeys.length}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: msg,
|
||||||
|
onOk() {
|
||||||
|
const reqArr = [];
|
||||||
|
if (index === '0') {
|
||||||
|
if (tableState.selectedRowKeys.length <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const v of tableState.selectedRowKeys) {
|
||||||
|
if (neType === 'MME') {
|
||||||
|
reqArr.push(delMMENbState(neId, v));
|
||||||
|
}
|
||||||
|
if (neType === 'AMF') {
|
||||||
|
reqArr.push(delAMFNbState(neId, v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (neType === 'MME') {
|
||||||
|
reqArr.push(delMMENbState(neId, index));
|
||||||
|
}
|
||||||
|
if (neType === 'AMF') {
|
||||||
|
reqArr.push(delAMFNbState(neId, index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (reqArr.length <= 0) return;
|
||||||
|
Promise.all(reqArr).then(res => {
|
||||||
|
const resArr = res.filter(
|
||||||
|
(item: any) => item.code !== RESULT_CODE_SUCCESS
|
||||||
|
);
|
||||||
|
if (resArr.length <= 0) {
|
||||||
|
message.success({
|
||||||
|
content: `${t('common.operateOk')}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnGetList();
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${t('common.operateErr')}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询列表 */
|
||||||
|
function fnGetList() {
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
const [neType, neId] = neTypeAndId.value;
|
||||||
|
queryParams.neId = neId;
|
||||||
|
let req = null;
|
||||||
|
if (neType === 'MME') {
|
||||||
|
req = listMMENbStatelist(toRaw(queryParams));
|
||||||
|
}
|
||||||
|
if (neType === 'AMF') {
|
||||||
|
req = listAMFNbStatelist(toRaw(queryParams));
|
||||||
|
}
|
||||||
|
if (req === null) {
|
||||||
|
tableState.data = [];
|
||||||
|
tableState.loading = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
req.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
// 取消勾选
|
||||||
|
if (tableState.selectedRowKeys.length > 0) {
|
||||||
|
tableState.selectedRowKeys = [];
|
||||||
|
}
|
||||||
|
tableState.data = res.data.filter((item: any) => {
|
||||||
|
// 状态过滤
|
||||||
|
if (queryParams.state) {
|
||||||
|
return item.state === queryParams.state;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
tableState.data = [];
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const stateNum = computed(() => {
|
||||||
|
let onNum = 0;
|
||||||
|
let offNum = 0;
|
||||||
|
tableState.data.forEach((item: any) => {
|
||||||
|
if (item.state === 'ON') {
|
||||||
|
onNum += 1;
|
||||||
|
}
|
||||||
|
if (item.state === 'OFF') {
|
||||||
|
offNum += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return [onNum, offNum];
|
||||||
|
});
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**新增框或修改框是否显示 */
|
||||||
|
openByEdit: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**表单数据 */
|
||||||
|
from: Record<string, any>;
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
/**历史框 */
|
||||||
|
openByHistory: boolean;
|
||||||
|
/**导入框 */
|
||||||
|
openByImport: boolean;
|
||||||
|
importMsgArr: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
openByEdit: false,
|
||||||
|
title: 'NB Config List',
|
||||||
|
from: {
|
||||||
|
address: '',
|
||||||
|
index: undefined,
|
||||||
|
name: '',
|
||||||
|
nbName: undefined,
|
||||||
|
offTime: undefined,
|
||||||
|
onTime: undefined,
|
||||||
|
position: '',
|
||||||
|
state: undefined,
|
||||||
|
ueNum: undefined,
|
||||||
|
},
|
||||||
|
confirmLoading: false,
|
||||||
|
openByHistory: false,
|
||||||
|
openByImport: false,
|
||||||
|
importMsgArr: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**对话框内表单属性和校验规则 */
|
||||||
|
const modalStateFrom = Form.useForm(
|
||||||
|
modalState.from,
|
||||||
|
reactive({
|
||||||
|
address: [
|
||||||
|
{ required: true, message: t('views.neData.baseStation.addressPlease') },
|
||||||
|
],
|
||||||
|
name: [
|
||||||
|
{ required: true, message: t('views.neData.baseStation.namePlease') },
|
||||||
|
],
|
||||||
|
position: [
|
||||||
|
{ required: true, message: t('views.neData.baseStation.positionPlease') },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出显示为 新增或者修改
|
||||||
|
* @param noticeId 网元id, 不传为新增
|
||||||
|
*/
|
||||||
|
function fnModalVisibleByEdit(edit?: string | number) {
|
||||||
|
if (!edit) {
|
||||||
|
modalStateFrom.resetFields(); //重置表单
|
||||||
|
modalState.title = t('views.neData.baseStation.addRadio');
|
||||||
|
modalState.openByEdit = true;
|
||||||
|
// 获取最大index
|
||||||
|
if (tableState.data.length <= 0) {
|
||||||
|
modalState.from.index = 1;
|
||||||
|
} else {
|
||||||
|
const last = tableState.data[tableState.data.length - 1];
|
||||||
|
modalState.from.index = last.index + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 编辑
|
||||||
|
if (edit === '0') {
|
||||||
|
const row = tableState.data.find((row: any) => {
|
||||||
|
return row.index === tableState.selectedRowKeys[0];
|
||||||
|
});
|
||||||
|
modalStateFrom.resetFields(); //重置表单
|
||||||
|
Object.assign(modalState.from, row);
|
||||||
|
modalState.title = t('views.neData.baseStation.editRadio');
|
||||||
|
modalState.openByEdit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出确认执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalOk() {
|
||||||
|
const [neType, neId] = neTypeAndId.value;
|
||||||
|
if (!neId) return;
|
||||||
|
const from = JSON.parse(JSON.stringify(modalState.from));
|
||||||
|
modalStateFrom
|
||||||
|
.validate()
|
||||||
|
.then(e => {
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
let result: any = null;
|
||||||
|
if (neType === 'MME') {
|
||||||
|
result = from.state
|
||||||
|
? editMMENbState(neId, from)
|
||||||
|
: addMMENbState(neId, from);
|
||||||
|
}
|
||||||
|
if (neType === 'AMF') {
|
||||||
|
result = from.state
|
||||||
|
? editAMFNbState(neId, from)
|
||||||
|
: addAMFNbState(neId, from);
|
||||||
|
}
|
||||||
|
if (result === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result
|
||||||
|
.then((res: any) => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnModalCancel();
|
||||||
|
fnGetList();
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: t('common.operateErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出关闭执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalCancel() {
|
||||||
|
modalState.openByEdit = false;
|
||||||
|
modalState.openByHistory = false;
|
||||||
|
modalStateFrom.resetFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**导出当前列表 */
|
||||||
|
function fnExportList() {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.neData.baseStation.exportTip'),
|
||||||
|
onOk: async () => {
|
||||||
|
if (modalState.confirmLoading) return;
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
|
||||||
|
let rows: Record<string, any>[] = [];
|
||||||
|
// 勾选的网元数据
|
||||||
|
if (tableState.selectedRowKeys.length > 0) {
|
||||||
|
rows = tableState.data.filter(item =>
|
||||||
|
tableState.selectedRowKeys.includes(item.index)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
rows = tableState.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataArr: Record<string, any>[] = [];
|
||||||
|
for (const row of rows) {
|
||||||
|
let data: any = {};
|
||||||
|
data[t('views.neData.baseStation.name')] = row.name;
|
||||||
|
data[t('views.neData.baseStation.position')] = row.position;
|
||||||
|
data[t('views.neData.baseStation.address')] = row.address;
|
||||||
|
data[t('views.neData.baseStation.nbName')] = row.nbName;
|
||||||
|
data[t('views.neData.baseStation.ueNum')] = row.ueNum;
|
||||||
|
nbState.value.find(item => {
|
||||||
|
if (item.value === row.state) {
|
||||||
|
data[t('views.neData.baseStation.state')] = item.label;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
data[t('views.neData.baseStation.time')] = row.time || '-';
|
||||||
|
dataArr.push(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
writeSheet(dataArr, 'Sheet1', {
|
||||||
|
header: [
|
||||||
|
t('views.neData.baseStation.name'),
|
||||||
|
t('views.neData.baseStation.position'),
|
||||||
|
t('views.neData.baseStation.address'),
|
||||||
|
t('views.neData.baseStation.nbName'),
|
||||||
|
t('views.neData.baseStation.ueNum'),
|
||||||
|
t('views.neData.baseStation.state'),
|
||||||
|
t('views.neData.baseStation.time'),
|
||||||
|
],
|
||||||
|
}).then(fileBlob =>
|
||||||
|
saveAs(fileBlob, `nb_state_records_export_${Date.now()}.xlsx`)
|
||||||
|
);
|
||||||
|
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
tableState.selectedRowKeys = [];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框弹出历史窗口 */
|
||||||
|
function fnHistoryView() {
|
||||||
|
modalState.openByHistory = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框表格信息导入弹出窗口 */
|
||||||
|
function fnModalImportOpen() {
|
||||||
|
modalState.openByImport = true;
|
||||||
|
}
|
||||||
|
function fnModalImportClose() {
|
||||||
|
modalState.openByImport = false;
|
||||||
|
fnQueryReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框表格信息导入上传 */
|
||||||
|
function fnModalImportUpload(file: File) {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
const [neType, neId] = neTypeAndId.value;
|
||||||
|
modalState.importMsgArr = [];
|
||||||
|
|
||||||
|
// 获取最大index
|
||||||
|
let index = 0;
|
||||||
|
if (tableState.data.length <= 0) {
|
||||||
|
index = 1;
|
||||||
|
} else {
|
||||||
|
const last = tableState.data[tableState.data.length - 1];
|
||||||
|
index = last.index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = function (e: any) {
|
||||||
|
const arrayBuffer = e.target.result;
|
||||||
|
readSheet(arrayBuffer).then(async rows => {
|
||||||
|
console.log(rows);
|
||||||
|
if (rows.length <= 0) {
|
||||||
|
hide();
|
||||||
|
message.error({
|
||||||
|
content: t('views.neData.baseStation.importDataEmpty'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 开始导入
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
for (const row of rows) {
|
||||||
|
const rowId = row[t('common.rowId')];
|
||||||
|
const name = row[t('views.neData.baseStation.name')];
|
||||||
|
const position = row[t('views.neData.baseStation.position')];
|
||||||
|
const address = row[t('views.neData.baseStation.address')];
|
||||||
|
let result: any = null;
|
||||||
|
// 检查IP地址是否定义
|
||||||
|
const hasAddress = tableState.data.find(
|
||||||
|
item => item.address === address
|
||||||
|
);
|
||||||
|
if (hasAddress) {
|
||||||
|
// 定义则更新名称位置
|
||||||
|
if (neType === 'MME') {
|
||||||
|
result = await editMMENbState(
|
||||||
|
neId,
|
||||||
|
Object.assign({}, hasAddress, {
|
||||||
|
name,
|
||||||
|
position,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (neType === 'AMF') {
|
||||||
|
result = await editAMFNbState(
|
||||||
|
neId,
|
||||||
|
Object.assign({}, hasAddress, {
|
||||||
|
name,
|
||||||
|
position,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let msg = `${t('common.rowId')}: ${rowId} ${t(
|
||||||
|
'views.neData.baseStation.editRadio'
|
||||||
|
)}${t('common.operateErr')}`;
|
||||||
|
if (result.code === RESULT_CODE_SUCCESS) {
|
||||||
|
msg = `${t('common.rowId')}: ${rowId} ${t(
|
||||||
|
'views.neData.baseStation.editRadio'
|
||||||
|
)}${t('common.operateOk')}`;
|
||||||
|
}
|
||||||
|
modalState.importMsgArr.push(msg);
|
||||||
|
} else {
|
||||||
|
// 未定义则新增
|
||||||
|
const form = {
|
||||||
|
index,
|
||||||
|
name: `${name}`,
|
||||||
|
position: `${position}`,
|
||||||
|
address: `${address}`,
|
||||||
|
};
|
||||||
|
if (neType === 'MME') {
|
||||||
|
result = await addMMENbState(neId, form);
|
||||||
|
}
|
||||||
|
if (neType === 'AMF') {
|
||||||
|
result = await addAMFNbState(neId, form);
|
||||||
|
}
|
||||||
|
let msg = `${t('common.rowId')}: ${rowId} ${t(
|
||||||
|
'views.neData.baseStation.addRadio'
|
||||||
|
)}${t('common.operateErr')}`;
|
||||||
|
if (result.code === RESULT_CODE_SUCCESS) {
|
||||||
|
index += 1;
|
||||||
|
msg = `${t('common.rowId')}: ${rowId} ${t(
|
||||||
|
'views.neData.baseStation.addRadio'
|
||||||
|
)}${t('common.operateOk')}`;
|
||||||
|
}
|
||||||
|
modalState.importMsgArr.push(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
reader.onerror = function (e) {
|
||||||
|
hide();
|
||||||
|
console.error('reader file error:', e);
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框表格信息导入模板 */
|
||||||
|
async function fnModalImportTemplate() {
|
||||||
|
const baseUrl = import.meta.env.VITE_HISTORY_BASE_URL;
|
||||||
|
const xlsxUrl = `${
|
||||||
|
baseUrl.length === 1 && baseUrl.indexOf('/') === 0
|
||||||
|
? ''
|
||||||
|
: baseUrl.indexOf('/') === -1
|
||||||
|
? '/' + baseUrl
|
||||||
|
: baseUrl
|
||||||
|
}/nbStateImput`;
|
||||||
|
const lang = currentLocale.value.split('_')[0];
|
||||||
|
saveAs(
|
||||||
|
`${xlsxUrl}/${lang}.xlsx`,
|
||||||
|
`nb_state_records_import_template_${Date.now()}.xlsx`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 获取网元网元列表
|
||||||
|
useNeInfoStore()
|
||||||
|
.fnNelist()
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length > 0) {
|
||||||
|
let arr: Record<string, any>[] = [];
|
||||||
|
useNeInfoStore().neCascaderOptions.forEach(item => {
|
||||||
|
if (['AMF', 'MME'].includes(item.value)) {
|
||||||
|
arr.push(JSON.parse(JSON.stringify(item)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neCascaderOptions.value = arr;
|
||||||
|
// 无查询参数neType时 默认选择AMF
|
||||||
|
const queryNeType = (route.query.neType as string) || 'AMF';
|
||||||
|
const item = arr.find(s => s.value === queryNeType);
|
||||||
|
if (item && item.children) {
|
||||||
|
const info = item.children[0];
|
||||||
|
neTypeAndId.value = [info.neType, info.neId];
|
||||||
|
} else {
|
||||||
|
const info = neCascaderOptions.value[0].children[0];
|
||||||
|
neTypeAndId.value = [info.neType, info.neId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// 获取列表数据
|
||||||
|
fnGetList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-card
|
||||||
|
:bordered="false"
|
||||||
|
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
||||||
|
>
|
||||||
|
<!-- 表格搜索栏 -->
|
||||||
|
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.ne.common.neType')" name="neType ">
|
||||||
|
<a-cascader
|
||||||
|
v-model:value="neTypeAndId"
|
||||||
|
:options="neCascaderOptions"
|
||||||
|
:allow-clear="false"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnGetList"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="4" :md="6" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.neData.baseStation.state')"
|
||||||
|
name="state"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.state"
|
||||||
|
:options="nbState"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnGetList"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button type="primary" @click.prevent="fnGetList()">
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="fnQueryReset">
|
||||||
|
<template #icon><ClearOutlined /></template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
|
||||||
|
<template #icon>
|
||||||
|
<PlusOutlined />
|
||||||
|
</template>
|
||||||
|
{{ t('common.addText') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
:disabled="tableState.selectedRowKeys.length != 1"
|
||||||
|
:loading="modalState.confirmLoading"
|
||||||
|
@click.prevent="fnModalVisibleByEdit('0')"
|
||||||
|
>
|
||||||
|
<template #icon><FormOutlined /></template>
|
||||||
|
{{ t('common.editText') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
danger
|
||||||
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
|
:loading="modalState.confirmLoading"
|
||||||
|
@click.prevent="fnRecordDelete('0')"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
{{ t('common.deleteText') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="dashed" @click.prevent="fnModalImportOpen()">
|
||||||
|
<template #icon><ImportOutlined /></template>
|
||||||
|
{{ t('common.import') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="dashed" @click.prevent="fnExportList()">
|
||||||
|
<template #icon><ExportOutlined /></template>
|
||||||
|
{{ t('common.export') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="fnHistoryView()">
|
||||||
|
<template #icon><ContainerOutlined /></template>
|
||||||
|
{{ t('views.neData.baseStation.history') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<div>
|
||||||
|
<template
|
||||||
|
v-if="
|
||||||
|
queryParams.state === undefined || queryParams.state === 'ON'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ t('views.neData.baseStation.online') }}:
|
||||||
|
<strong style="color: green">{{ stateNum[0] }} </strong>
|
||||||
|
</template>
|
||||||
|
<template
|
||||||
|
v-if="
|
||||||
|
queryParams.state === undefined || queryParams.state === 'OFF'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
|
||||||
|
{{ t('views.neData.baseStation.offline') }}:
|
||||||
|
<strong style="color: red">
|
||||||
|
{{ stateNum[1] }}
|
||||||
|
</strong>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<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="index"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:row-selection="{
|
||||||
|
type: 'checkbox',
|
||||||
|
selectedRowKeys: tableState.selectedRowKeys,
|
||||||
|
onChange: fnTableSelectedRowKeys,
|
||||||
|
}"
|
||||||
|
:scroll="{ x: tableColumns.length * 120, y: 'calc(100vh - 480px)' }"
|
||||||
|
@resizeColumn="(w:number, col:any) => (col.width = w)"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'state'">
|
||||||
|
<DictTag
|
||||||
|
:options="nbState"
|
||||||
|
:value="record.state"
|
||||||
|
value-default="OFF"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 新增框或修改框 -->
|
||||||
|
<ProModal
|
||||||
|
:drag="true"
|
||||||
|
:width="500"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
:keyboard="false"
|
||||||
|
:mask-closable="false"
|
||||||
|
:open="modalState.openByEdit"
|
||||||
|
:title="modalState.title"
|
||||||
|
:confirm-loading="modalState.confirmLoading"
|
||||||
|
@ok="fnModalOk"
|
||||||
|
@cancel="fnModalCancel"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
name="modalStateFrom"
|
||||||
|
layout="horizontal"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:labelWrap="true"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.neData.baseStation.name')"
|
||||||
|
name="name"
|
||||||
|
v-bind="modalStateFrom.validateInfos.name"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.name"
|
||||||
|
allow-clear
|
||||||
|
:maxlength="64"
|
||||||
|
>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.neData.baseStation.position')"
|
||||||
|
name="position"
|
||||||
|
v-bind="modalStateFrom.validateInfos.position"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.position"
|
||||||
|
allow-clear
|
||||||
|
:maxlength="64"
|
||||||
|
>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="!modalState.from.state"
|
||||||
|
:label="t('views.neData.baseStation.address')"
|
||||||
|
name="address"
|
||||||
|
v-bind="modalStateFrom.validateInfos.address"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.address"
|
||||||
|
allow-clear
|
||||||
|
:maxlength="64"
|
||||||
|
>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</ProModal>
|
||||||
|
|
||||||
|
<!-- 状态历史框 -->
|
||||||
|
<HistoryModal
|
||||||
|
v-model:open="modalState.openByHistory"
|
||||||
|
:title="t('views.neData.baseStation.history')"
|
||||||
|
:ne-type="neTypeAndId[0]"
|
||||||
|
:ne-id="neTypeAndId[1]"
|
||||||
|
@cancel="fnModalCancel"
|
||||||
|
></HistoryModal>
|
||||||
|
|
||||||
|
<!-- 上传导入表格数据文件框 -->
|
||||||
|
<UploadModal
|
||||||
|
:title="t('common.import')"
|
||||||
|
@upload="fnModalImportUpload"
|
||||||
|
@close="fnModalImportClose"
|
||||||
|
v-model:open="modalState.openByImport"
|
||||||
|
:ext="['.xls', '.xlsx']"
|
||||||
|
:size="10"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<a-row justify="space-between" align="middle">
|
||||||
|
<a-col :span="12"> </a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
:title="t('views.system.user.downloadObj')"
|
||||||
|
@click.prevent="fnModalImportTemplate"
|
||||||
|
>
|
||||||
|
{{ t('views.system.user.downloadObj') }}
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-textarea
|
||||||
|
:disabled="true"
|
||||||
|
:hidden="modalState.importMsgArr.length <= 0"
|
||||||
|
:value="modalState.importMsgArr.join('\r\n')"
|
||||||
|
:auto-size="{ minRows: 2, maxRows: 8 }"
|
||||||
|
style="background-color: transparent; color: rgba(0, 0, 0, 0.85)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</UploadModal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,642 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, ref, onBeforeUnmount, useTemplateRef } from 'vue';
|
||||||
|
import { Graph, GraphData, Menu, Tooltip, Util } from '@antv/g6';
|
||||||
|
import { listAMFNbStatelist } from '@/api/neData/amf';
|
||||||
|
import { parseBasePath } from '@/plugins/file-static-url';
|
||||||
|
import { edgeLineAnimateState } from '@/views/monitor/topologyBuild/hooks/registerEdge';
|
||||||
|
import { nodeImageAnimateState } from '@/views/monitor/topologyBuild/hooks/registerNode';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { stateNeInfo } from '@/api/ne/neInfo';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { useFullscreen } from '@vueuse/core';
|
||||||
|
import { listMMENbStatelist } from '@/api/neData/mme';
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
/**图DOM节点实例对象 */
|
||||||
|
const graphG6Dom = useTemplateRef('graphG6Dom');
|
||||||
|
|
||||||
|
/**图数据 */
|
||||||
|
const graphData = reactive<Record<string, any>>({
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'omc',
|
||||||
|
label: 'OMC',
|
||||||
|
img: parseBasePath('/svg/service_db.svg'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'amf1',
|
||||||
|
label: 'amf1',
|
||||||
|
img: parseBasePath('/svg/service.svg'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'amf2',
|
||||||
|
label: 'amf2',
|
||||||
|
img: parseBasePath('/svg/service.svg'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'base1',
|
||||||
|
label: 'base1',
|
||||||
|
img: parseBasePath('/svg/base.svg'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'base2',
|
||||||
|
label: 'base2',
|
||||||
|
img: parseBasePath('/svg/base.svg'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
source: 'omc',
|
||||||
|
target: 'amf1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: 'omc',
|
||||||
|
target: 'amf2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: 'amf1',
|
||||||
|
target: 'base1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: 'amf2',
|
||||||
|
target: 'base1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: 'amf2',
|
||||||
|
target: 'base2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**图实例对象 */
|
||||||
|
const graphG6 = ref<any>(null);
|
||||||
|
|
||||||
|
/**图节点右击菜单 */
|
||||||
|
const graphNodeMenu = new Menu({
|
||||||
|
offsetX: 6,
|
||||||
|
offseY: 10,
|
||||||
|
itemTypes: ['node'],
|
||||||
|
getContent(evt) {
|
||||||
|
if (!evt) return t('views.monitor.topologyBuild.graphNotInfo');
|
||||||
|
const { id, label, nType, nInfo }: any = evt.item?.getModel();
|
||||||
|
if (['GNB', 'ENB'].includes(nType)) {
|
||||||
|
return `
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 140px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
${t('views.neData.baseStation.name')}:
|
||||||
|
${label ?? '--'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
return `
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 140px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
${t('views.monitor.topology.name')}:
|
||||||
|
${label ?? '--'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**图节点展示 */
|
||||||
|
const graphNodeTooltip = new Tooltip({
|
||||||
|
offsetX: 10,
|
||||||
|
offsetY: 20,
|
||||||
|
getContent(evt) {
|
||||||
|
if (!evt) return t('views.monitor.topologyBuild.graphNotInfo');
|
||||||
|
const { id, label, nType, nInfo }: any = evt.item?.getModel();
|
||||||
|
if (['GNB', 'ENB'].includes(nType)) {
|
||||||
|
return `
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 256px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div><strong>${t('views.neData.baseStation.state')}:</strong><span>
|
||||||
|
${
|
||||||
|
nInfo.state === 'ON'
|
||||||
|
? t('views.neData.baseStation.online')
|
||||||
|
: t('views.neData.baseStation.offline')
|
||||||
|
}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>${t('views.neData.baseStation.time')}:</strong><span>
|
||||||
|
${nInfo.state === 'ON' ? nInfo.onTime ?? '--' : nInfo.offTime ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div>==============================</div>
|
||||||
|
<div><strong>ID:</strong><span>${nInfo.index}</span></div>
|
||||||
|
<div><strong>${t('views.neData.baseStation.address')}:</strong><span>
|
||||||
|
${nInfo.address ?? '--'}</span></div>
|
||||||
|
<div><strong>${t('views.neData.baseStation.nbName')}:</strong><span>
|
||||||
|
${nInfo.nbName ?? '--'}</span></div>
|
||||||
|
<div><strong>${t('views.neData.baseStation.ueNum')}:</strong><span>
|
||||||
|
${nInfo.ueNum ?? '--'}</span></div>
|
||||||
|
<div><strong>${t('views.neData.baseStation.name')}:</strong><span>
|
||||||
|
${nInfo.name ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>${t(
|
||||||
|
'views.neData.baseStation.position'
|
||||||
|
)}:</strong><span style="word-wrap: break-word;">
|
||||||
|
${nInfo.position}
|
||||||
|
</span></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
return `
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 200px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div><strong>${t('views.monitor.topology.state')}:</strong><span>
|
||||||
|
${
|
||||||
|
nInfo.online
|
||||||
|
? t('views.monitor.topology.normalcy')
|
||||||
|
: t('views.monitor.topology.exceptions')
|
||||||
|
}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>${t('views.monitor.topology.refreshTime')}:</strong><span>
|
||||||
|
${nInfo.refreshTime ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div>========================</div>
|
||||||
|
<div><strong>ID:</strong><span>${nInfo.neId}</span></div>
|
||||||
|
<div><strong>${t('views.monitor.topology.name')}:</strong><span>
|
||||||
|
${nInfo.neName ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>IP:</strong><span>${nInfo.neIP}</span></div>
|
||||||
|
<div><strong>${t('views.monitor.topology.version')}:</strong><span>
|
||||||
|
${nInfo.version ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>${t('views.monitor.topology.serialNum')}:</strong><span>
|
||||||
|
${nInfo.sn ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>${t('views.monitor.topology.expiryDate')}:</strong><span>
|
||||||
|
${nInfo.expire ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
itemTypes: ['node'],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**注册自定义边或节点 */
|
||||||
|
function registerEdgeNode() {
|
||||||
|
// 边
|
||||||
|
edgeLineAnimateState();
|
||||||
|
// 节点
|
||||||
|
nodeImageAnimateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* format the string
|
||||||
|
* @param {string} str The origin string
|
||||||
|
* @param {number} maxWidth max width
|
||||||
|
* @param {number} fontSize font size
|
||||||
|
* @return {string} the processed result
|
||||||
|
*/
|
||||||
|
function fittingString(str: string, maxWidth: number, fontSize: number) {
|
||||||
|
let currentWidth = 0;
|
||||||
|
let res = str;
|
||||||
|
const pattern = new RegExp('[\u4E00-\u9FA5]+'); // distinguish the Chinese charactors and letters
|
||||||
|
str.split('').forEach((letter, i) => {
|
||||||
|
if (currentWidth > maxWidth) return;
|
||||||
|
if (pattern.test(letter)) {
|
||||||
|
// Chinese charactors
|
||||||
|
currentWidth += fontSize;
|
||||||
|
} else {
|
||||||
|
// get the width of single letter according to the fontSize
|
||||||
|
currentWidth += Util.getLetterWidth(letter, fontSize);
|
||||||
|
}
|
||||||
|
if (currentWidth > maxWidth) {
|
||||||
|
res = `${str.substring(0, i)}\n${str.substring(i)}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**图事件 */
|
||||||
|
function graphEvent(graph: Graph) {
|
||||||
|
graph.on('edge:mouseenter', evt => {
|
||||||
|
const { item } = evt;
|
||||||
|
if (!item) return;
|
||||||
|
graph.setItemState(item, 'circle-move', '#b5d6fb');
|
||||||
|
});
|
||||||
|
graph.on('edge:mouseleave', evt => {
|
||||||
|
const { item } = evt;
|
||||||
|
if (!item) return;
|
||||||
|
graph.setItemState(item, 'circle-move', false);
|
||||||
|
graph.setItemState(item, 'circle-move:#b5d6fb', false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**图数据渲染 */
|
||||||
|
function handleRanderGraph(container: HTMLElement | null, data: GraphData) {
|
||||||
|
if (!container) return;
|
||||||
|
const { clientHeight, clientWidth } = container;
|
||||||
|
|
||||||
|
// 注册自定义边或节点
|
||||||
|
registerEdgeNode();
|
||||||
|
|
||||||
|
const graph = new Graph({
|
||||||
|
container: container,
|
||||||
|
width: clientWidth,
|
||||||
|
height: clientHeight,
|
||||||
|
fitCenter: false,
|
||||||
|
fitView: true,
|
||||||
|
fitViewPadding: [40, 40, 40, 40],
|
||||||
|
modes: {
|
||||||
|
// default: ['drag-canvas', 'drag-node', 'zoom-canvas'],
|
||||||
|
default: [
|
||||||
|
'zoom-canvas',
|
||||||
|
'drag-canvas',
|
||||||
|
'drag-node',
|
||||||
|
{
|
||||||
|
type: 'drag-combo',
|
||||||
|
onlyChangeComboSize: true, // 不改变层级关系
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'collapse-expand-combo',
|
||||||
|
trigger: 'dblclick',
|
||||||
|
relayout: true, // 收缩展开后,不重新布局
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
groupByTypes: false,
|
||||||
|
plugins: [graphNodeMenu, graphNodeTooltip],
|
||||||
|
layout: {
|
||||||
|
type: 'dagre',
|
||||||
|
rankdir: 'TB', // 布局的方向,TB:从上到下,BT:从下到上,LR:从左到右,RL:从右到左
|
||||||
|
//align: 'UL', // 节点对齐方式 UL、UR、DL、DR
|
||||||
|
controlPoints: true,
|
||||||
|
nodesep: 20,
|
||||||
|
ranksep: 40,
|
||||||
|
},
|
||||||
|
animate: true,
|
||||||
|
defaultNode: {
|
||||||
|
type: 'image-animate-state',
|
||||||
|
labelCfg: {
|
||||||
|
offset: 8,
|
||||||
|
position: 'bottom',
|
||||||
|
style: { fill: '#ffffff', fontSize: 14, fontWeight: 500 },
|
||||||
|
},
|
||||||
|
size: 48,
|
||||||
|
img: parseBasePath('/svg/cloud.svg'),
|
||||||
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
},
|
||||||
|
defaultEdge: {
|
||||||
|
type: 'line-animate-state',
|
||||||
|
labelCfg: {
|
||||||
|
autoRotate: true,
|
||||||
|
refY: 10,
|
||||||
|
refX: 40,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
stroke: '#fafafa',
|
||||||
|
lineWidth: 1.5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultCombo: {
|
||||||
|
labelCfg: {
|
||||||
|
offset: 16,
|
||||||
|
position: 'bottom',
|
||||||
|
style: { fill: '#ffffff', fontSize: 14, fontWeight: 500 },
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
stroke: '#BDEFDB',
|
||||||
|
fill: '#BDEFDB',
|
||||||
|
opacity: 0.25,
|
||||||
|
},
|
||||||
|
collapsedSubstituteIcon: {
|
||||||
|
show: true,
|
||||||
|
img: parseBasePath('/svg/service.svg'),
|
||||||
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
graph.data(data);
|
||||||
|
graph.render();
|
||||||
|
graphEvent(graph);
|
||||||
|
|
||||||
|
// 创建 ResizeObserver 实例
|
||||||
|
const observer = new ResizeObserver(function (entries) {
|
||||||
|
// 当元素大小发生变化时触发回调函数
|
||||||
|
entries.forEach(function (entry) {
|
||||||
|
if (!graph) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
graph.changeSize(entry.contentRect.width, entry.contentRect.height);
|
||||||
|
graph.fitCenter();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// 监听元素大小变化
|
||||||
|
observer.observe(container);
|
||||||
|
|
||||||
|
return graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图组数据渲染到画布
|
||||||
|
*/
|
||||||
|
async function fnGraphDataLoad() {
|
||||||
|
// 加载基础网元
|
||||||
|
await useNeInfoStore().fnNelist();
|
||||||
|
const dataNe = await fnGraphDataBase();
|
||||||
|
Object.assign(graphData, dataNe);
|
||||||
|
graphG6.value = handleRanderGraph(graphG6Dom.value, dataNe);
|
||||||
|
// 添加基站
|
||||||
|
const dataNb = await fnGraphDataNb(dataNe);
|
||||||
|
Object.assign(graphData, dataNb);
|
||||||
|
// graphG6.value.clear();
|
||||||
|
graphG6.value.read(dataNb);
|
||||||
|
// 添加状态
|
||||||
|
interval.value = true;
|
||||||
|
repeatFn();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**图数据网元构建 */
|
||||||
|
async function fnGraphDataBase() {
|
||||||
|
const data: GraphData = {
|
||||||
|
nodes: [],
|
||||||
|
edges: [],
|
||||||
|
};
|
||||||
|
// 添加基础网元
|
||||||
|
for (const item of useNeInfoStore().getNeSelectOtions) {
|
||||||
|
if ('OMC' === item.value) {
|
||||||
|
if (item.children?.length === 0) continue;
|
||||||
|
// 是否存在OMC保证唯一
|
||||||
|
const hasOMC = data.nodes?.findIndex(v => v.neType === 'OMC');
|
||||||
|
if (hasOMC !== -1) continue;
|
||||||
|
// 根网元
|
||||||
|
const omcInfo = item.children[0];
|
||||||
|
const node = {
|
||||||
|
id: 'OMC',
|
||||||
|
label: omcInfo.neName,
|
||||||
|
img: parseBasePath('/svg/service_db.svg'),
|
||||||
|
nInfo: { online: false, neId: omcInfo.neId, neType: omcInfo.neType },
|
||||||
|
nType: 'OMC',
|
||||||
|
};
|
||||||
|
// 添加OMC节点
|
||||||
|
data.nodes?.push(node);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (['AMF', 'MME'].includes(item.value)) {
|
||||||
|
if (item.children?.length === 0) continue;
|
||||||
|
for (const child of item.children) {
|
||||||
|
const id = `${child.neType}_${child.neId}`;
|
||||||
|
const node = {
|
||||||
|
id: id,
|
||||||
|
label: child.neName,
|
||||||
|
img: parseBasePath('/svg/service.svg'),
|
||||||
|
nInfo: { online: false, neId: child.neId, neType: child.neType },
|
||||||
|
nType: item.value,
|
||||||
|
};
|
||||||
|
// 添加节点
|
||||||
|
data.nodes?.push(node);
|
||||||
|
data.edges?.push({
|
||||||
|
source: 'OMC',
|
||||||
|
target: id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
item.children.forEach((v: any) => {});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**图数据基站构建 */
|
||||||
|
async function fnGraphDataNb(data: GraphData) {
|
||||||
|
const arr = data.nodes?.filter((v: any) => ['AMF', 'MME'].includes(v.nType));
|
||||||
|
if (arr === undefined || arr.length === 0) return data;
|
||||||
|
for (const item of arr) {
|
||||||
|
if (item.nType === 'AMF') {
|
||||||
|
const neId = (item.nInfo as any).neId;
|
||||||
|
const res = await listAMFNbStatelist({ neId });
|
||||||
|
if (res.code !== RESULT_CODE_SUCCESS || !Array.isArray(res.data)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const nb of res.data) {
|
||||||
|
const id = `${item.id}_${nb.index}`;
|
||||||
|
data.nodes?.push({
|
||||||
|
id: id,
|
||||||
|
label: fittingString(`${nb.name}`, 80, 14),
|
||||||
|
img: parseBasePath('/svg/base5G.svg'),
|
||||||
|
nInfo: nb,
|
||||||
|
nType: 'GNB',
|
||||||
|
});
|
||||||
|
data.edges?.push({
|
||||||
|
source: item.id,
|
||||||
|
target: id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item.nType === 'MME') {
|
||||||
|
const neId = (item.nInfo as any).neId;
|
||||||
|
const res = await listMMENbStatelist({ neId });
|
||||||
|
if (res.code !== RESULT_CODE_SUCCESS || !Array.isArray(res.data)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const nb of res.data) {
|
||||||
|
const id = `${item.id}_${nb.index}`;
|
||||||
|
data.nodes?.push({
|
||||||
|
id: id,
|
||||||
|
label: fittingString(`${nb.name}`, 80, 14),
|
||||||
|
img: parseBasePath('/svg/base4G.svg'),
|
||||||
|
nInfo: nb,
|
||||||
|
nType: 'ENB',
|
||||||
|
});
|
||||||
|
data.edges?.push({
|
||||||
|
source: item.id,
|
||||||
|
target: id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图状态构建
|
||||||
|
* @param reload 是否重载状态
|
||||||
|
*/
|
||||||
|
async function fnGraphState(reload: boolean = false) {
|
||||||
|
// 节点状态
|
||||||
|
if (!Array.isArray(graphData.nodes)) return;
|
||||||
|
|
||||||
|
const onc = graphData.nodes.find((v: any) => v.nType === 'OMC');
|
||||||
|
if (onc) {
|
||||||
|
const { id, nInfo } = onc as any;
|
||||||
|
if (!nInfo) return;
|
||||||
|
const res = await stateNeInfo(nInfo.neType, nInfo.neId);
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
Object.assign(nInfo, res.data, {
|
||||||
|
refreshTime: parseDateToStr(res.data.refreshTime, 'HH:mm:ss'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const stateColor = nInfo.online ? '#52c41a' : '#f5222d'; // 状态颜色
|
||||||
|
graphG6.value.setItemState(id, 'top-right-dot', stateColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
graphData.nodes
|
||||||
|
.filter((v: any) => ['AMF', 'MME'].includes(v.nType))
|
||||||
|
.forEach(async (v: any) => {
|
||||||
|
const { id, nInfo } = v;
|
||||||
|
if (!nInfo) return;
|
||||||
|
const res = await stateNeInfo(nInfo.neType, nInfo.neId);
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
Object.assign(nInfo, res.data, {
|
||||||
|
refreshTime: parseDateToStr(res.data.refreshTime, 'HH:mm:ss'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const stateColor = nInfo.online ? '#52c41a' : '#f5222d'; // 状态颜色
|
||||||
|
graphG6.value.setItemState(id, 'top-right-dot', stateColor);
|
||||||
|
|
||||||
|
// 重载时更新下级基站状态
|
||||||
|
if (reload && nInfo.neType === 'AMF') {
|
||||||
|
const res = await listAMFNbStatelist({ neId: nInfo.neId });
|
||||||
|
if (res.code == RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
for (const nb of res.data) {
|
||||||
|
const nbItem = graphData.nodes.find(
|
||||||
|
(v: any) => v.id === `${id}_${nb.index}`
|
||||||
|
);
|
||||||
|
if (nbItem) {
|
||||||
|
Object.assign(nbItem.nInfo, nb);
|
||||||
|
const stateColor = nb.state === 'ON' ? '#52c41a' : '#f5222d'; // 状态颜色
|
||||||
|
graphG6.value.setItemState(
|
||||||
|
nbItem.id,
|
||||||
|
'top-right-dot',
|
||||||
|
stateColor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (reload && nInfo.neType === 'MME') {
|
||||||
|
const res = await listMMENbStatelist({ neId: nInfo.neId });
|
||||||
|
if (res.code == RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
for (const nb of res.data) {
|
||||||
|
const nbItem = graphData.nodes.find(
|
||||||
|
(v: any) => v.id === `${id}_${nb.index}`
|
||||||
|
);
|
||||||
|
if (nbItem) {
|
||||||
|
Object.assign(nbItem.nInfo, nb);
|
||||||
|
const stateColor = nb.state === 'ON' ? '#52c41a' : '#f5222d'; // 状态颜色
|
||||||
|
graphG6.value.setItemState(
|
||||||
|
nbItem.id,
|
||||||
|
'top-right-dot',
|
||||||
|
stateColor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (reload) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 15_000));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 非重载时使用初始获取的状态
|
||||||
|
graphData.nodes
|
||||||
|
.filter((v: any) => ['GNB', 'ENB'].includes(v.nType))
|
||||||
|
.forEach(async (v: any) => {
|
||||||
|
const { id, nInfo } = v;
|
||||||
|
if (!nInfo) return;
|
||||||
|
const stateColor = nInfo.state === 'ON' ? '#52c41a' : '#f5222d'; // 状态颜色
|
||||||
|
graphG6.value.setItemState(id, 'top-right-dot', stateColor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**递归调度器 */
|
||||||
|
const interval = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**递归刷新图状态 */
|
||||||
|
function repeatFn(reload: boolean = false) {
|
||||||
|
if (!interval.value || !graphG6Dom.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fnGraphState(reload)
|
||||||
|
.finally(() => {
|
||||||
|
repeatFn(true); // 递归调用自己
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewportDom = ref<HTMLElement | null>(null);
|
||||||
|
const { isFullscreen, toggle } = useFullscreen(viewportDom);
|
||||||
|
function fullscreen() {
|
||||||
|
toggle();
|
||||||
|
|
||||||
|
if (!graphG6Dom.value) return;
|
||||||
|
if (isFullscreen.value) {
|
||||||
|
graphG6Dom.value.style.height = 'calc(100vh - 300px)';
|
||||||
|
} else {
|
||||||
|
graphG6Dom.value.style.height = '100vh';
|
||||||
|
}
|
||||||
|
const { clientHeight, clientWidth } = graphG6Dom.value;
|
||||||
|
graphG6.value.changeSize(clientHeight, clientWidth);
|
||||||
|
graphG6.value.fitView(40);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fnGraphDataLoad();
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
interval.value = false;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0' }" ref="viewportDom">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
{{ t('views.neData.baseStation.topologyTitle') }}
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-button type="default" @click.prevent="fullscreen()">
|
||||||
|
<template #icon>
|
||||||
|
<FullscreenExitOutlined v-if="isFullscreen" />
|
||||||
|
<FullscreenOutlined v-else />
|
||||||
|
</template>
|
||||||
|
{{ t('loayouts.rightContent.fullscreen') }}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div ref="graphG6Dom" class="chart"></div>
|
||||||
|
</a-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.chart {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 300px);
|
||||||
|
background-color: rgb(43, 47, 51);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -134,7 +134,7 @@ export default function useConfigList({
|
|||||||
/**指定每页可以显示多少条 */
|
/**指定每页可以显示多少条 */
|
||||||
pageSizeOptions: ['10', '20', '50', '100'],
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
/**只有一页时是否隐藏分页器 */
|
/**只有一页时是否隐藏分页器 */
|
||||||
hideOnSinglePage: true,
|
hideOnSinglePage: false,
|
||||||
/**是否可以快速跳转至某页 */
|
/**是否可以快速跳转至某页 */
|
||||||
showQuickJumper: true,
|
showQuickJumper: true,
|
||||||
/**是否可以改变 pageSize */
|
/**是否可以改变 pageSize */
|
||||||
|
|||||||
+210
-297
@@ -13,6 +13,9 @@ import useConfigList from './hooks/useConfigList';
|
|||||||
import useConfigArray from './hooks/useConfigArray';
|
import useConfigArray from './hooks/useConfigArray';
|
||||||
import useConfigArrayChild from './hooks/useConfigArrayChild';
|
import useConfigArrayChild from './hooks/useConfigArrayChild';
|
||||||
import { getAllNeConfig, getNeConfigData } from '@/api/ne/neConfig';
|
import { getAllNeConfig, getNeConfigData } from '@/api/ne/neConfig';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
const neInfoStore = useNeInfoStore();
|
const neInfoStore = useNeInfoStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { ruleVerification, smfByUPFIdLoadData, smfByUPFIdOptions } = useOptions({
|
const { ruleVerification, smfByUPFIdLoadData, smfByUPFIdOptions } = useOptions({
|
||||||
@@ -76,6 +79,7 @@ function fnSelectConfigNode(_: any, info: any) {
|
|||||||
if (treeState.selectNode.paramName == key) {
|
if (treeState.selectNode.paramName == key) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fnActiveConfigNode(key);
|
fnActiveConfigNode(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,6 +172,31 @@ function fnActiveConfigNode(key: string | number) {
|
|||||||
// 列表字段
|
// 列表字段
|
||||||
const columns: Record<string, any>[] = [];
|
const columns: Record<string, any>[] = [];
|
||||||
for (const rule of arrayState.dataRule.record) {
|
for (const rule of arrayState.dataRule.record) {
|
||||||
|
if (rule.name === 'name') {
|
||||||
|
columns.push({
|
||||||
|
title: rule.display,
|
||||||
|
dataIndex: rule.name,
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 150,
|
||||||
|
minWidth: 100,
|
||||||
|
maxWidth: 350,
|
||||||
|
customFilterDropdown: true,
|
||||||
|
onFilter: (value: any, record: any) => {
|
||||||
|
const regex = new RegExp('^[0-9]{4,8}$');
|
||||||
|
const name = record.name.value?.toString().trim() || '';
|
||||||
|
const filterValue = value?.toString().trim();
|
||||||
|
return regex.test(name) && name.includes(filterValue); // 正则匹配且模糊查询
|
||||||
|
},
|
||||||
|
onFilterDropdownOpenChange: (visible: any) => {
|
||||||
|
if (visible) {
|
||||||
|
setTimeout(() => {
|
||||||
|
searchInput.value.focus();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
columns.push({
|
columns.push({
|
||||||
title: rule.display,
|
title: rule.display,
|
||||||
dataIndex: rule.name,
|
dataIndex: rule.name,
|
||||||
@@ -178,6 +207,10 @@ function fnActiveConfigNode(key: string | number) {
|
|||||||
maxWidth: 350,
|
maxWidth: 350,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
columns.push({
|
columns.push({
|
||||||
title: t('common.operate'),
|
title: t('common.operate'),
|
||||||
dataIndex: 'index',
|
dataIndex: 'index',
|
||||||
@@ -195,9 +228,7 @@ function fnActiveConfigNode(key: string | number) {
|
|||||||
}, 300);
|
}, 300);
|
||||||
} else {
|
} else {
|
||||||
message.warning({
|
message.warning({
|
||||||
content: `${param.paramDisplay} ${t(
|
content: `${param.paramDisplay} ${t('views.ne.neConfig.noConfigData')}`,
|
||||||
'views.configManage.configParamForm.noConfigData'
|
|
||||||
)}`,
|
|
||||||
duration: 3,
|
duration: 3,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -205,11 +236,11 @@ function fnActiveConfigNode(key: string | number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**查询配置可选属性值列表 */
|
/**查询配置可选属性值列表 */
|
||||||
function fnGetNeConfig() {
|
function fnGetNeConfig(routeSelect?: string) {
|
||||||
const neType = neTypeSelect.value[0];
|
const neType = neTypeSelect.value[0];
|
||||||
if (!neType) {
|
if (!neType) {
|
||||||
message.warning({
|
message.warning({
|
||||||
content: t('views.configManage.configParamForm.neTypePleace'),
|
content: t('views.ne.neConfig.neTypePleace'),
|
||||||
duration: 3,
|
duration: 3,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -242,7 +273,8 @@ function fnGetNeConfig() {
|
|||||||
const item = JSON.parse(JSON.stringify(treeState.data[0]));
|
const item = JSON.parse(JSON.stringify(treeState.data[0]));
|
||||||
treeState.selectNode = item;
|
treeState.selectNode = item;
|
||||||
treeState.selectLoading = false;
|
treeState.selectLoading = false;
|
||||||
fnActiveConfigNode(item.key);
|
// fnActiveConfigNode(((route.query.treeName as string)||item.key));
|
||||||
|
fnActiveConfigNode(routeSelect || item.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -364,6 +396,25 @@ const {
|
|||||||
arrayEditClose,
|
arrayEditClose,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
searchText: '',
|
||||||
|
searchedColumn: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const searchInput = ref();
|
||||||
|
|
||||||
|
const handleSearch = (selectedKeys: any, confirm: any, dataIndex: any) => {
|
||||||
|
confirm();
|
||||||
|
state.searchText = selectedKeys[0];
|
||||||
|
state.searchedColumn = dataIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = (clearFilters: any) => {
|
||||||
|
clearFilters({ confirm: true });
|
||||||
|
state.searchText = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 获取网元网元列表
|
// 获取网元网元列表
|
||||||
neInfoStore.fnNelist().then(res => {
|
neInfoStore.fnNelist().then(res => {
|
||||||
@@ -383,7 +434,7 @@ onMounted(() => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 默认选择AMF
|
// 默认选择AMF
|
||||||
const item = neCascaderOptions.value.find(s => s.value === 'AMF');
|
const item = neCascaderOptions.value.find(s => s.value === ((route.query.neType as string) || 'MF'));
|
||||||
if (item && item.children) {
|
if (item && item.children) {
|
||||||
const info = item.children[0];
|
const info = item.children[0];
|
||||||
neTypeSelect.value = [info.neType, info.neId];
|
neTypeSelect.value = [info.neType, info.neId];
|
||||||
@@ -391,7 +442,7 @@ onMounted(() => {
|
|||||||
const info = neCascaderOptions.value[0].children[0];
|
const info = neCascaderOptions.value[0].children[0];
|
||||||
neTypeSelect.value = [info.neType, info.neId];
|
neTypeSelect.value = [info.neType, info.neId];
|
||||||
}
|
}
|
||||||
fnGetNeConfig();
|
fnGetNeConfig((route.query.treeName as string));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
message.warning({
|
message.warning({
|
||||||
@@ -406,51 +457,30 @@ onMounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col
|
<a-col :lg="6" :md="6" :xs="24" style="margin-bottom: 24px" v-show="collapsible">
|
||||||
:lg="6"
|
|
||||||
:md="6"
|
|
||||||
:xs="24"
|
|
||||||
style="margin-bottom: 24px"
|
|
||||||
v-show="collapsible"
|
|
||||||
>
|
|
||||||
<!-- 网元类型 -->
|
<!-- 网元类型 -->
|
||||||
<a-card size="small" :bordered="false" :loading="treeState.loading">
|
<a-card size="small" :bordered="false" :loading="treeState.loading">
|
||||||
<template #title>
|
<template #title>
|
||||||
{{ t('views.configManage.configParamForm.treeTitle') }}
|
{{ t('views.ne.neConfig.treeTitle') }}
|
||||||
</template>
|
</template>
|
||||||
<a-form layout="vertical" autocomplete="off">
|
<a-form layout="vertical" autocomplete="off">
|
||||||
<a-form-item name="neId ">
|
<a-form-item name="neId ">
|
||||||
<a-cascader
|
<a-cascader v-model:value="neTypeSelect" :options="neCascaderOptions" :allow-clear="false"
|
||||||
v-model:value="neTypeSelect"
|
@change="fnGetNeConfig()" />
|
||||||
:options="neCascaderOptions"
|
|
||||||
:allow-clear="false"
|
|
||||||
@change="fnGetNeConfig"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item name="listeningPort">
|
<a-form-item name="treeStateData">
|
||||||
<a-tree
|
<a-tree :tree-data="treeState.data" :selected-keys="[treeState.selectNode.paramName]"
|
||||||
:tree-data="treeState.data"
|
@select="fnSelectConfigNode">
|
||||||
:selected-keys="[treeState.selectNode.paramName]"
|
|
||||||
@select="fnSelectConfigNode"
|
|
||||||
>
|
|
||||||
</a-tree>
|
</a-tree>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="collapsible ? 18 : 24" :md="collapsible ? 18 : 24" :xs="24">
|
<a-col :lg="collapsible ? 18 : 24" :md="collapsible ? 18 : 24" :xs="24">
|
||||||
<a-card
|
<a-card size="small" :bordered="false" :body-style="{ maxHeight: '700px', 'overflow-y': 'auto' }"
|
||||||
size="small"
|
:loading="treeState.selectLoading">
|
||||||
:bordered="false"
|
|
||||||
:body-style="{ maxHeight: '600px', 'overflow-y': 'auto' }"
|
|
||||||
:loading="treeState.selectLoading"
|
|
||||||
>
|
|
||||||
<template #title>
|
<template #title>
|
||||||
<a-button
|
<a-button type="text" size="small" @click.prevent="changeCollapsible()">
|
||||||
type="text"
|
|
||||||
size="small"
|
|
||||||
@click.prevent="changeCollapsible()"
|
|
||||||
>
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<MenuFoldOutlined v-show="collapsible" />
|
<MenuFoldOutlined v-show="collapsible" />
|
||||||
<MenuUnfoldOutlined v-show="!collapsible" />
|
<MenuUnfoldOutlined v-show="!collapsible" />
|
||||||
@@ -461,18 +491,14 @@ onMounted(() => {
|
|||||||
{{ treeState.selectNode.paramDisplay }}
|
{{ treeState.selectNode.paramDisplay }}
|
||||||
</a-typography-text>
|
</a-typography-text>
|
||||||
<a-typography-text type="danger" v-else>
|
<a-typography-text type="danger" v-else>
|
||||||
{{ t('views.configManage.configParamForm.treeSelectTip') }}
|
{{ t('views.ne.neConfig.treeSelectTip') }}
|
||||||
</a-typography-text>
|
</a-typography-text>
|
||||||
</template>
|
</template>
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<a-space :size="8" align="center" v-show="!treeState.selectLoading">
|
<a-space :size="8" align="center" v-show="!treeState.selectLoading">
|
||||||
<a-tooltip>
|
<a-tooltip placement="topRight">
|
||||||
<template #title>{{ t('common.reloadText') }}</template>
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
<a-button
|
<a-button type="default" size="small" @click.prevent="fnActiveConfigNode('#')">
|
||||||
type="default"
|
|
||||||
size="small"
|
|
||||||
@click.prevent="fnActiveConfigNode('#')"
|
|
||||||
>
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<ReloadOutlined />
|
<ReloadOutlined />
|
||||||
</template>
|
</template>
|
||||||
@@ -482,17 +508,9 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 单列表格列表 -->
|
<!-- 单列表格列表 -->
|
||||||
<a-table
|
<a-table v-if="treeState.selectNode.paramType === 'list'" class="table" row-key="name" :size="listState.size"
|
||||||
v-if="treeState.selectNode.paramType === 'list'"
|
:columns="listState.columns" :data-source="listState.data" :pagination="tablePagination" :bordered="true"
|
||||||
class="table"
|
:scroll="{ x: true, y: '500px' }">
|
||||||
row-key="name"
|
|
||||||
:size="listState.size"
|
|
||||||
:columns="listState.columns"
|
|
||||||
:data-source="listState.data"
|
|
||||||
:pagination="tablePagination"
|
|
||||||
:bordered="true"
|
|
||||||
:scroll="{ x: true, y: '500px' }"
|
|
||||||
>
|
|
||||||
<template #bodyCell="{ column, text, record }">
|
<template #bodyCell="{ column, text, record }">
|
||||||
<template v-if="column.dataIndex === 'value'">
|
<template v-if="column.dataIndex === 'value'">
|
||||||
<a-tooltip placement="topLeft">
|
<a-tooltip placement="topLeft">
|
||||||
@@ -500,69 +518,30 @@ onMounted(() => {
|
|||||||
{{ record.comment }}
|
{{ record.comment }}
|
||||||
</template>
|
</template>
|
||||||
<div class="editable-cell">
|
<div class="editable-cell">
|
||||||
<div
|
<div v-if="
|
||||||
v-if="
|
|
||||||
listState.editRecord['display'] === record['display']
|
listState.editRecord['display'] === record['display']
|
||||||
"
|
" class="editable-cell__input-wrapper">
|
||||||
class="editable-cell__input-wrapper"
|
<a-input-number v-if="record['type'] === 'int'" v-model:value="listState.editRecord['value']"
|
||||||
>
|
:disabled="listState.confirmLoading" style="width: 100%"></a-input-number>
|
||||||
<a-input-number
|
<a-switch v-else-if="record['type'] === 'bool'" v-model:checked="listState.editRecord['value']"
|
||||||
v-if="record['type'] === 'int'"
|
:checked-children="t('common.switch.open')" :un-checked-children="t('common.switch.shut')"
|
||||||
v-model:value="listState.editRecord['value']"
|
:disabled="listState.confirmLoading"></a-switch>
|
||||||
:disabled="listState.confirmLoading"
|
<a-select v-else-if="record['type'] === 'enum'" v-model:value="listState.editRecord['value']"
|
||||||
style="width: 100%"
|
:allow-clear="true" :disabled="listState.confirmLoading" style="width: 100%">
|
||||||
></a-input-number>
|
<a-select-option :value="+v" :key="+v" v-for="(k, v) in JSON.parse(record['filter'])">
|
||||||
<a-switch
|
|
||||||
v-else-if="record['type'] === 'bool'"
|
|
||||||
v-model:checked="listState.editRecord['value']"
|
|
||||||
:checked-children="t('common.switch.open')"
|
|
||||||
:un-checked-children="t('common.switch.shut')"
|
|
||||||
:disabled="listState.confirmLoading"
|
|
||||||
></a-switch>
|
|
||||||
<a-select
|
|
||||||
v-else-if="record['type'] === 'enum'"
|
|
||||||
v-model:value="listState.editRecord['value']"
|
|
||||||
:allow-clear="true"
|
|
||||||
:disabled="listState.confirmLoading"
|
|
||||||
style="width: 100%"
|
|
||||||
>
|
|
||||||
<a-select-option
|
|
||||||
:value="+v"
|
|
||||||
:key="+v"
|
|
||||||
v-for="(k, v) in JSON.parse(record['filter'])"
|
|
||||||
>
|
|
||||||
{{ k }}
|
{{ k }}
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-input
|
<a-input v-else v-model:value="listState.editRecord['value']"
|
||||||
v-else
|
:disabled="listState.confirmLoading"></a-input>
|
||||||
v-model:value="listState.editRecord['value']"
|
<a-space :size="8" align="center" direction="horizontal" style="margin-left: 18px">
|
||||||
:disabled="listState.confirmLoading"
|
|
||||||
></a-input>
|
|
||||||
<a-space
|
|
||||||
:size="8"
|
|
||||||
align="center"
|
|
||||||
direction="horizontal"
|
|
||||||
style="margin-left: 18px"
|
|
||||||
>
|
|
||||||
<a-tooltip placement="bottomRight">
|
<a-tooltip placement="bottomRight">
|
||||||
<template #title> {{ t('common.ok') }} </template>
|
<template #title> {{ t('common.ok') }} </template>
|
||||||
<a-popconfirm
|
<a-popconfirm :title="t('views.ne.neConfig.editOkTip', {
|
||||||
:title="
|
num: record['display'],
|
||||||
t(
|
})
|
||||||
'views.configManage.configParamForm.editOkTip',
|
" placement="topRight" :disabled="listState.confirmLoading" @confirm="listEditOk()">
|
||||||
{ num: record['display'] }
|
<a-button type="text" class="editable-cell__icon-edit" :disabled="listState.confirmLoading">
|
||||||
)
|
|
||||||
"
|
|
||||||
placement="topRight"
|
|
||||||
:disabled="listState.confirmLoading"
|
|
||||||
@confirm="listEditOk()"
|
|
||||||
>
|
|
||||||
<a-button
|
|
||||||
type="text"
|
|
||||||
class="editable-cell__icon-edit"
|
|
||||||
:disabled="listState.confirmLoading"
|
|
||||||
>
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<CheckOutlined />
|
<CheckOutlined />
|
||||||
</template>
|
</template>
|
||||||
@@ -571,12 +550,8 @@ onMounted(() => {
|
|||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip placement="bottomRight">
|
<a-tooltip placement="bottomRight">
|
||||||
<template #title> {{ t('common.cancel') }} </template>
|
<template #title> {{ t('common.cancel') }} </template>
|
||||||
<a-button
|
<a-button type="text" class="editable-cell__icon-edit" :disabled="listState.confirmLoading"
|
||||||
type="text"
|
@click.prevent="listEditClose()">
|
||||||
class="editable-cell__icon-edit"
|
|
||||||
:disabled="listState.confirmLoading"
|
|
||||||
@click.prevent="listEditClose()"
|
|
||||||
>
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<CloseOutlined />
|
<CloseOutlined />
|
||||||
</template>
|
</template>
|
||||||
@@ -584,24 +559,16 @@ onMounted(() => {
|
|||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div v-else class="editable-cell__text-wrapper" @dblclick="listEdit(record)">
|
||||||
v-else
|
|
||||||
class="editable-cell__text-wrapper"
|
|
||||||
@dblclick="listEdit(record)"
|
|
||||||
>
|
|
||||||
<template v-if="record['type'] === 'enum'">
|
<template v-if="record['type'] === 'enum'">
|
||||||
{{ JSON.parse(record['filter'])[text] || ' ' }}
|
{{ JSON.parse(record['filter'])[text] || ' ' }}
|
||||||
</template>
|
</template>
|
||||||
<template v-else>{{ `${text}` || ' ' }}</template>
|
<template v-else>{{ `${text}` || ' ' }}</template>
|
||||||
<EditOutlined
|
<EditOutlined class="editable-cell__icon" @click="listEdit(record)" style="margin-left: 18px"
|
||||||
class="editable-cell__icon"
|
|
||||||
@click="listEdit(record)"
|
|
||||||
style="margin-left: 18px"
|
|
||||||
v-if="
|
v-if="
|
||||||
!listState.confirmLoading &&
|
!listState.confirmLoading &&
|
||||||
!['read-only', 'read', 'ro'].includes(record.access)
|
!['read-only', 'read', 'ro'].includes(record.access)
|
||||||
"
|
" />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
@@ -610,66 +577,78 @@ onMounted(() => {
|
|||||||
</a-table>
|
</a-table>
|
||||||
|
|
||||||
<!-- array类型 -->
|
<!-- array类型 -->
|
||||||
<template v-if="treeState.selectNode.paramType === 'array'">
|
<template v-else-if="treeState.selectNode.paramType === 'array'">
|
||||||
<a-table
|
<a-table class="table" row-key="index"
|
||||||
class="table"
|
:columns="treeState.selectNode.paramPerms.includes('get') ? arrayState.columnsDnd.filter((s: any) => s.key !== 'index') : arrayState.columnsDnd"
|
||||||
row-key="index"
|
:data-source="arrayState.columnsData" :size="arrayState.size" :pagination="tablePagination"
|
||||||
:columns="treeState.selectNode.paramPerms.includes('get') ? arrayState.columnsDnd.filter((s:any)=>s.key !== 'index') : arrayState.columnsDnd"
|
:bordered="true" :scroll="{ x: arrayState.columnsDnd.length * 200, y: 480 }"
|
||||||
:data-source="arrayState.columnsData"
|
@resizeColumn="(w: number, col: any) => (col.width = w)" :show-expand-column="false"
|
||||||
:size="arrayState.size"
|
v-model:expanded-row-keys="arrayState.arrayChildExpandKeys">
|
||||||
:pagination="tablePagination"
|
|
||||||
:bordered="true"
|
|
||||||
:scroll="{ x: arrayState.columnsDnd.length * 200, y: 480 }"
|
|
||||||
@resizeColumn="(w:number, col:any) => (col.width = w)"
|
|
||||||
:show-expand-column="false"
|
|
||||||
v-model:expanded-row-keys="arrayState.arrayChildExpandKeys"
|
|
||||||
>
|
|
||||||
<!-- 多列新增操作 -->
|
<!-- 多列新增操作 -->
|
||||||
<template #title>
|
<template #title>
|
||||||
<a-space :size="16" align="center">
|
<a-space :size="16" align="center">
|
||||||
<a-button
|
<a-button type="primary" @click.prevent="arrayAdd()" size="small"
|
||||||
type="primary"
|
v-if="treeState.selectNode.paramPerms.includes('post')">
|
||||||
@click.prevent="arrayAdd()"
|
<template #icon>
|
||||||
size="small"
|
<PlusOutlined />
|
||||||
v-if="treeState.selectNode.paramPerms.includes('post')"
|
</template>
|
||||||
>
|
|
||||||
<template #icon> <PlusOutlined /> </template>
|
|
||||||
{{ t('common.addText') }}
|
{{ t('common.addText') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<TableColumnsDnd
|
<TableColumnsDnd type="ghost" :cache-id="treeState.selectNode.key"
|
||||||
type="ghost"
|
:columns="treeState.selectNode.paramPerms.includes('get') ? [...arrayState.columns.filter((s: any) => s.key !== 'index')] : arrayState.columns"
|
||||||
:cache-id="treeState.selectNode.key"
|
v-model:columns-dnd="arrayState.columnsDnd"></TableColumnsDnd>
|
||||||
:columns="treeState.selectNode.paramPerms.includes('get') ? [...arrayState.columns.filter((s:any)=>s.key !== 'index')] : arrayState.columns"
|
|
||||||
v-model:columns-dnd="arrayState.columnsDnd"
|
|
||||||
></TableColumnsDnd>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #customFilterDropdown="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }">
|
||||||
|
<div style="padding: 8px">
|
||||||
|
<a-input ref="searchInput" :placeholder="`Search ${column.dataIndex}`" :value="selectedKeys[0]"
|
||||||
|
style="width: 188px; margin-bottom: 8px; display: block"
|
||||||
|
@change="e => setSelectedKeys(e.target.value ? [e.target.value] : [])"
|
||||||
|
@pressEnter="handleSearch(selectedKeys, confirm, column.dataIndex)" />
|
||||||
|
<a-button type="primary" size="small" style="width: 90px; margin-right: 8px"
|
||||||
|
@click="handleSearch(selectedKeys, confirm, column.dataIndex)">
|
||||||
|
<template #icon>
|
||||||
|
<SearchOutlined />
|
||||||
|
</template>
|
||||||
|
Search
|
||||||
|
</a-button>
|
||||||
|
<a-button size="small" style="width: 90px" @click="handleReset(clearFilters)">
|
||||||
|
Reset
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #customFilterIcon="{ filtered }">
|
||||||
|
<search-outlined :style="{ color: filtered ? '#108ee9' : undefined }" />
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 多列数据渲染 -->
|
<!-- 多列数据渲染 -->
|
||||||
<template #bodyCell="{ column, text, record }">
|
<template #bodyCell="{ column, text, record }">
|
||||||
|
|
||||||
<template v-if="column?.key === 'index'">
|
<template v-if="column?.key === 'index'">
|
||||||
<a-space :size="16" align="center">
|
<a-space :size="16" align="center">
|
||||||
<a-tooltip
|
<a-tooltip v-if="treeState.selectNode.paramPerms.includes('put')">
|
||||||
v-if="treeState.selectNode.paramPerms.includes('put')"
|
|
||||||
>
|
|
||||||
<template #title>{{ t('common.editText') }}</template>
|
<template #title>{{ t('common.editText') }}</template>
|
||||||
<a-button type="link" @click.prevent="arrayEdit(text)">
|
<a-button type="link" @click.prevent="arrayEdit(text)">
|
||||||
<template #icon><FormOutlined /></template>
|
<template #icon>
|
||||||
|
<FormOutlined />
|
||||||
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip
|
<a-tooltip v-if="
|
||||||
v-if="
|
|
||||||
treeState.selectNode.paramPerms.includes('delete') &&
|
treeState.selectNode.paramPerms.includes('delete') &&
|
||||||
!(
|
!(
|
||||||
neTypeSelect[0] === 'IMS' &&
|
neTypeSelect[0] === 'IMS' &&
|
||||||
treeState.selectNode.paramName === 'plmn' &&
|
treeState.selectNode.paramName === 'plmn' &&
|
||||||
text['value'] === 0
|
text['value'] === 0
|
||||||
)
|
)
|
||||||
"
|
">
|
||||||
>
|
|
||||||
<template #title>{{ t('common.deleteText') }}</template>
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
<a-button type="link" @click.prevent="arrayDelete(text)">
|
<a-button type="link" @click.prevent="arrayDelete(text)">
|
||||||
<template #icon><DeleteOutlined /></template>
|
<template #icon>
|
||||||
|
<DeleteOutlined />
|
||||||
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
@@ -681,24 +660,18 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
<div class="editable-cell">
|
<div class="editable-cell">
|
||||||
<template v-if="text.array">
|
<template v-if="text.array">
|
||||||
<a-button
|
<a-button type="default" size="small" @click.prevent="
|
||||||
type="default"
|
|
||||||
size="small"
|
|
||||||
@click.prevent="
|
|
||||||
arrayChildExpand(record['index'], text)
|
arrayChildExpand(record['index'], text)
|
||||||
"
|
">
|
||||||
>
|
<template #icon>
|
||||||
<template #icon><BarsOutlined /></template>
|
<BarsOutlined />
|
||||||
{{
|
</template>
|
||||||
t('views.configManage.configParamForm.arrayMore')
|
{{ t('views.ne.neConfig.arrayMore') }}
|
||||||
}}
|
|
||||||
</a-button>
|
</a-button>
|
||||||
<!--特殊字段拓展显示-->
|
<!--特殊字段拓展显示-->
|
||||||
<span
|
<span v-if="
|
||||||
v-if="
|
|
||||||
text.name === 'dnnList' && Array.isArray(text.value)
|
text.name === 'dnnList' && Array.isArray(text.value)
|
||||||
"
|
">
|
||||||
>
|
|
||||||
({{
|
({{
|
||||||
text.value.length > 4
|
text.value.length > 4
|
||||||
? `${text.value
|
? `${text.value
|
||||||
@@ -725,37 +698,23 @@ onMounted(() => {
|
|||||||
|
|
||||||
<!-- 多列嵌套类型 -->
|
<!-- 多列嵌套类型 -->
|
||||||
<template #expandedRowRender>
|
<template #expandedRowRender>
|
||||||
<a-table
|
<a-table class="table" row-key="index" :columns="arrayChildState.columnsDnd"
|
||||||
class="table"
|
:data-source="arrayChildState.columnsData" :size="arrayChildState.size" :pagination="tablePagination"
|
||||||
row-key="index"
|
:bordered="true" :scroll="{
|
||||||
:columns="arrayChildState.columnsDnd"
|
|
||||||
:data-source="arrayChildState.columnsData"
|
|
||||||
:size="arrayChildState.size"
|
|
||||||
:pagination="tablePagination"
|
|
||||||
:bordered="true"
|
|
||||||
:scroll="{
|
|
||||||
x: arrayChildState.columnsDnd.length * 200,
|
x: arrayChildState.columnsDnd.length * 200,
|
||||||
y: 200,
|
y: 200,
|
||||||
}"
|
}" @resizeColumn="(w: number, col: any) => (col.width = w)">
|
||||||
@resizeColumn="(w:number, col:any) => (col.width = w)"
|
|
||||||
>
|
|
||||||
<template #title>
|
<template #title>
|
||||||
<a-space :size="16" align="center">
|
<a-space :size="16" align="center">
|
||||||
<a-button
|
<a-button type="primary" @click.prevent="arrayChildAdd" size="small">
|
||||||
type="primary"
|
<template #icon>
|
||||||
@click.prevent="arrayChildAdd"
|
<PlusOutlined />
|
||||||
size="small"
|
</template>
|
||||||
>
|
|
||||||
<template #icon> <PlusOutlined /> </template>
|
|
||||||
{{ t('common.addText') }} {{ arrayChildState.title }}
|
{{ t('common.addText') }} {{ arrayChildState.title }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<TableColumnsDnd
|
<TableColumnsDnd type="ghost" :cache-id="`${treeState.selectNode.key}:${arrayChildState.loc}`"
|
||||||
type="ghost"
|
:columns="[...arrayChildState.columns]" v-model:columns-dnd="arrayChildState.columnsDnd"
|
||||||
:cache-id="`${treeState.selectNode.key}:${arrayChildState.loc}`"
|
v-if="arrayChildState.loc"></TableColumnsDnd>
|
||||||
:columns="[...arrayChildState.columns]"
|
|
||||||
v-model:columns-dnd="arrayChildState.columnsDnd"
|
|
||||||
v-if="arrayChildState.loc"
|
|
||||||
></TableColumnsDnd>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
<template #bodyCell="{ column, text, record }">
|
<template #bodyCell="{ column, text, record }">
|
||||||
@@ -763,22 +722,20 @@ onMounted(() => {
|
|||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template #title>{{ t('common.editText') }}</template>
|
<template #title>{{ t('common.editText') }}</template>
|
||||||
<a-button
|
<a-button type="link" @click.prevent="arrayChildEdit(text)">
|
||||||
type="link"
|
<template #icon>
|
||||||
@click.prevent="arrayChildEdit(text)"
|
<FormOutlined />
|
||||||
>
|
</template>
|
||||||
<template #icon><FormOutlined /></template>
|
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template #title>
|
<template #title>
|
||||||
{{ t('common.deleteText') }}
|
{{ t('common.deleteText') }}
|
||||||
</template>
|
</template>
|
||||||
<a-button
|
<a-button type="link" @click.prevent="arrayChildDelete(text)">
|
||||||
type="link"
|
<template #icon>
|
||||||
@click.prevent="arrayChildDelete(text)"
|
<DeleteOutlined />
|
||||||
>
|
</template>
|
||||||
<template #icon><DeleteOutlined /></template>
|
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
@@ -791,12 +748,10 @@ onMounted(() => {
|
|||||||
<div class="editable-cell">
|
<div class="editable-cell">
|
||||||
<template v-if="text.array">
|
<template v-if="text.array">
|
||||||
<a-button type="default" size="small">
|
<a-button type="default" size="small">
|
||||||
<template #icon><BarsOutlined /></template>
|
<template #icon>
|
||||||
{{
|
<BarsOutlined />
|
||||||
t(
|
</template>
|
||||||
'views.configManage.configParamForm.arrayMore'
|
{{ t('views.ne.neConfig.arrayMore') }}
|
||||||
)
|
|
||||||
}}
|
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -816,102 +771,55 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<a-alert type="warning" show-icon message="No Data" />
|
||||||
|
</template>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<!-- 新增框或修改框 -->
|
<!-- 新增框或修改框 -->
|
||||||
<ProModal
|
<ProModal :drag="true" :width="800" :destroyOnClose="true"
|
||||||
:drag="true"
|
:body-style="{ maxHeight: '600px', 'overflow-y': 'auto' }" :keyboard="false" :mask-closable="false"
|
||||||
:width="800"
|
:open="modalState.open" :title="modalState.title" :confirm-loading="modalState.confirmLoading" @ok="fnModalOk"
|
||||||
:destroyOnClose="true"
|
@cancel="fnModalCancel">
|
||||||
:body-style="{ maxHeight: '600px', 'overflow-y': 'auto' }"
|
<a-form class="form" layout="horizontal" autocomplete="off" :validate-on-rule-change="false" :validateTrigger="[]"
|
||||||
:keyboard="false"
|
:label-col="{ span: 6 }" :labelWrap="true">
|
||||||
:mask-closable="false"
|
<a-form-item v-for="item in modalState.data" :label="item.display" :name="item.name"
|
||||||
:open="modalState.open"
|
:required="item.optional === 'false'" style="margin-bottom: 4px">
|
||||||
:title="modalState.title"
|
|
||||||
:confirm-loading="modalState.confirmLoading"
|
|
||||||
@ok="fnModalOk"
|
|
||||||
@cancel="fnModalCancel"
|
|
||||||
>
|
|
||||||
<a-form
|
|
||||||
class="form"
|
|
||||||
layout="horizontal"
|
|
||||||
autocomplete="off"
|
|
||||||
:validate-on-rule-change="false"
|
|
||||||
:validateTrigger="[]"
|
|
||||||
:label-col="{ span: 6 }"
|
|
||||||
:labelWrap="true"
|
|
||||||
>
|
|
||||||
<a-form-item
|
|
||||||
v-for="item in modalState.data"
|
|
||||||
:label="item.display"
|
|
||||||
:name="item.name"
|
|
||||||
:required="item.optional === 'false'"
|
|
||||||
style="margin-bottom: 4px"
|
|
||||||
>
|
|
||||||
<a-tooltip placement="topLeft">
|
<a-tooltip placement="topLeft">
|
||||||
<template #title v-if="item.comment">
|
<template #title v-if="item.comment">
|
||||||
{{ item.comment }}
|
{{ item.comment }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div v-if="
|
||||||
v-if="
|
|
||||||
!Array.isArray(item.array) &&
|
!Array.isArray(item.array) &&
|
||||||
modalState.from[item.name] !== undefined
|
modalState.from[item.name] !== undefined
|
||||||
"
|
">
|
||||||
>
|
|
||||||
<!-- 特殊SMF-upfid选择 -->
|
<!-- 特殊SMF-upfid选择 -->
|
||||||
<a-select
|
<a-select v-if="
|
||||||
v-if="
|
|
||||||
neTypeSelect[0] === 'SMF' &&
|
neTypeSelect[0] === 'SMF' &&
|
||||||
modalState.from[item.name]['name'] === 'upfId'
|
modalState.from[item.name]['name'] === 'upfId'
|
||||||
"
|
" v-model:value="modalState.from[item.name]['value']" :options="smfByUPFIdOptions"
|
||||||
v-model:value="modalState.from[item.name]['value']"
|
:disabled="['read-only', 'read', 'ro'].includes(item.access)" :token-separators="[',', ';']"
|
||||||
:options="smfByUPFIdOptions"
|
mode="multiple" :max-tag-count="5" :allow-clear="true" style="width: 100%">
|
||||||
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
|
|
||||||
:token-separators="[',', ';']"
|
|
||||||
mode="multiple"
|
|
||||||
:max-tag-count="5"
|
|
||||||
:allow-clear="true"
|
|
||||||
style="width: 100%"
|
|
||||||
>
|
|
||||||
</a-select>
|
</a-select>
|
||||||
<!-- 常规 -->
|
<!-- 常规 -->
|
||||||
<a-input-number
|
<a-input-number v-else-if="item['type'] === 'int'" v-model:value="modalState.from[item.name]['value']"
|
||||||
v-else-if="item['type'] === 'int'"
|
:disabled="['read-only', 'read', 'ro'].includes(item.access)" style="width: 100%"></a-input-number>
|
||||||
v-model:value="modalState.from[item.name]['value']"
|
<a-switch v-else-if="item['type'] === 'bool'" v-model:checked="modalState.from[item.name]['value']"
|
||||||
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
|
:checked-children="t('common.switch.open')" :un-checked-children="t('common.switch.shut')"
|
||||||
style="width: 100%"
|
:disabled="['read-only', 'read', 'ro'].includes(item.access)"></a-switch>
|
||||||
></a-input-number>
|
<a-select v-else-if="item['type'] === 'enum'" v-model:value="modalState.from[item.name]['value']"
|
||||||
<a-switch
|
:disabled="['read-only', 'read', 'ro'].includes(item.access)" :allow-clear="true" style="width: 100%">
|
||||||
v-else-if="item['type'] === 'bool'"
|
<a-select-option :value="+v" :key="+v" v-for="(k, v) in JSON.parse(item['filter'])">
|
||||||
v-model:checked="modalState.from[item.name]['value']"
|
|
||||||
:checked-children="t('common.switch.open')"
|
|
||||||
:un-checked-children="t('common.switch.shut')"
|
|
||||||
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
|
|
||||||
></a-switch>
|
|
||||||
<a-select
|
|
||||||
v-else-if="item['type'] === 'enum'"
|
|
||||||
v-model:value="modalState.from[item.name]['value']"
|
|
||||||
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
|
|
||||||
:allow-clear="true"
|
|
||||||
style="width: 100%"
|
|
||||||
>
|
|
||||||
<a-select-option
|
|
||||||
:value="+v"
|
|
||||||
:key="+v"
|
|
||||||
v-for="(k, v) in JSON.parse(item['filter'])"
|
|
||||||
>
|
|
||||||
{{ k }}
|
{{ k }}
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-input
|
<a-input v-else v-model:value="modalState.from[item.name]['value']"
|
||||||
v-else
|
:disabled="['read-only', 'read', 'ro'].includes(item.access)"></a-input>
|
||||||
v-model:value="modalState.from[item.name]['value']"
|
|
||||||
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
|
|
||||||
></a-input>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
{{ `${item.value || ' '}` }}
|
{{ `${item.value || ' '}` }}
|
||||||
@@ -930,19 +838,24 @@ onMounted(() => {
|
|||||||
display: none;
|
display: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__icon:hover {
|
&__icon:hover {
|
||||||
color: var(--ant-primary-color);
|
color: var(--ant-primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__icon-edit:hover {
|
&__icon-edit:hover {
|
||||||
color: var(--ant-primary-color);
|
color: var(--ant-primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__text-wrapper {
|
&__text-wrapper {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__text-wrapper:hover &__icon {
|
&__text-wrapper:hover &__icon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__input-wrapper {
|
&__input-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: start;
|
justify-content: start;
|
||||||
|
|||||||
@@ -0,0 +1,513 @@
|
|||||||
|
import {
|
||||||
|
addNeConfigData,
|
||||||
|
delNeConfigData,
|
||||||
|
editNeConfigData,
|
||||||
|
} from '@/api/ne/neConfig';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { Modal, message } from 'ant-design-vue/es';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { reactive, watch } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数配置array类型
|
||||||
|
* @param param 父级传入 { t, treeState, neTypeSelect, neIdSelect, fnActiveConfigNode, ruleVerification, modalState, fnModalCancel}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function useConfigArray({
|
||||||
|
t,
|
||||||
|
treeState,
|
||||||
|
neTypeSelect,
|
||||||
|
neIdSelect,
|
||||||
|
fnActiveConfigNode,
|
||||||
|
ruleVerification,
|
||||||
|
modalState,
|
||||||
|
fnModalCancel,
|
||||||
|
}: any) {
|
||||||
|
/**多列列表状态类型 */
|
||||||
|
type ArrayStateType = {
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**多列嵌套记录字段 */
|
||||||
|
columns: Record<string, any>[];
|
||||||
|
/**表格字段列排序 */
|
||||||
|
columnsDnd: Record<string, any>[];
|
||||||
|
/**多列记录数据 */
|
||||||
|
columnsData: Record<string, any>[];
|
||||||
|
/**多列嵌套展开key */
|
||||||
|
arrayChildExpandKeys: any[];
|
||||||
|
|
||||||
|
/**多列记录数据 */
|
||||||
|
data: Record<string, any>[];
|
||||||
|
/**多列记录规则 */
|
||||||
|
dataRule: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**多列列表状态 */
|
||||||
|
let arrayState: ArrayStateType = reactive({
|
||||||
|
size: 'small',
|
||||||
|
columns: [],
|
||||||
|
columnsDnd: [],
|
||||||
|
columnsData: [],
|
||||||
|
arrayChildExpandKeys: [],
|
||||||
|
data: [],
|
||||||
|
dataRule: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**多列表编辑 */
|
||||||
|
function arrayEdit(rowIndex: Record<string, any>) {
|
||||||
|
const item = arrayState.data.find((s: any) => s.key === rowIndex.value);
|
||||||
|
if (!item) return;
|
||||||
|
const from = arrayInitEdit(item, arrayState.dataRule);
|
||||||
|
// 处理信息
|
||||||
|
const row: Record<string, any> = {};
|
||||||
|
for (const v of from.record) {
|
||||||
|
if (Array.isArray(v.array)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
row[v.name] = Object.assign({}, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 特殊SMF-upfid选择
|
||||||
|
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(row, 'upfId')) {
|
||||||
|
const v = row.upfId.value;
|
||||||
|
if (typeof v === 'string') {
|
||||||
|
if (v === '') {
|
||||||
|
row.upfId.value = [];
|
||||||
|
} else if (v.includes(';')) {
|
||||||
|
row.upfId.value = v.split(';');
|
||||||
|
} else if (v.includes(',')) {
|
||||||
|
row.upfId.value = v.split(',');
|
||||||
|
} else {
|
||||||
|
row.upfId.value = [v];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modalState.from = row;
|
||||||
|
modalState.type = 'arrayEdit';
|
||||||
|
modalState.title = `${treeState.selectNode.paramDisplay} ${from.title}`;
|
||||||
|
modalState.key = from.key;
|
||||||
|
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
|
||||||
|
modalState.open = true;
|
||||||
|
|
||||||
|
// 关闭嵌套
|
||||||
|
arrayState.arrayChildExpandKeys = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表编辑关闭 */
|
||||||
|
function arrayEditClose() {
|
||||||
|
arrayState.arrayChildExpandKeys = [];
|
||||||
|
fnModalCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表编辑确认 */
|
||||||
|
function arrayEditOk(from: Record<string, any>) {
|
||||||
|
const loc = `${from['index']['value']}`;
|
||||||
|
|
||||||
|
// 特殊SMF-upfid选择
|
||||||
|
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(from, 'upfId')) {
|
||||||
|
const v = from.upfId.value;
|
||||||
|
if (Array.isArray(v)) {
|
||||||
|
from.upfId.value = v.join(';');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历提取属性和值
|
||||||
|
let data: Record<string, any> = {};
|
||||||
|
for (const key in from) {
|
||||||
|
// 子嵌套的不插入
|
||||||
|
if (from[key]['array']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 检查规则
|
||||||
|
const [ok, msg] = ruleVerification(from[key]);
|
||||||
|
if (!ok) {
|
||||||
|
message.warning({
|
||||||
|
content: `${msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data[key] = from[key]['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求
|
||||||
|
const reqArr = [];
|
||||||
|
if (neTypeSelect.value[1].startsWith('SYNC')) {
|
||||||
|
for (const neId of neIdSelect.value) {
|
||||||
|
reqArr.push(
|
||||||
|
editNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neId,
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc: loc,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reqArr.push(
|
||||||
|
editNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neTypeSelect.value[1],
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc: loc,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 无请求提示
|
||||||
|
if (reqArr.length === 0) {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.neIdSyncPleace'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
arrayEditClose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
Promise.allSettled(reqArr)
|
||||||
|
.then(resArr => {
|
||||||
|
const rejected = resArr.find(res => res.status === 'rejected');
|
||||||
|
if (rejected) {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.updateItemErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.updateItem', {
|
||||||
|
num: modalState.title,
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const fulfilled = resArr.find(res => res.status === 'fulfilled');
|
||||||
|
if (fulfilled) {
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
arrayEditClose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表删除单行 */
|
||||||
|
function arrayDelete(rowIndex: Record<string, any>) {
|
||||||
|
const loc = `${rowIndex.value}`;
|
||||||
|
const title = `${treeState.selectNode.paramDisplay} Index-${loc}`;
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.ne.neConfig.delItemTip', {
|
||||||
|
num: title,
|
||||||
|
}),
|
||||||
|
onOk() {
|
||||||
|
// 请求
|
||||||
|
const reqArr = [];
|
||||||
|
if (neTypeSelect.value[1].startsWith('SYNC')) {
|
||||||
|
for (const neId of neIdSelect.value) {
|
||||||
|
reqArr.push(
|
||||||
|
delNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neId,
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
loc: loc,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reqArr.push(
|
||||||
|
delNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neTypeSelect.value[1],
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
loc: loc,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 无请求提示
|
||||||
|
if (reqArr.length === 0) {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.neIdSyncPleace'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
arrayEditClose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
Promise.allSettled(reqArr)
|
||||||
|
.then(resArr => {
|
||||||
|
const rejected = resArr.find(res => res.status === 'rejected');
|
||||||
|
if (rejected) {
|
||||||
|
message.error({
|
||||||
|
content: `${rejected.reason}`,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.delItemOk', {
|
||||||
|
num: title,
|
||||||
|
}),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const fulfilled = resArr.find(res => res.status === 'fulfilled');
|
||||||
|
if (fulfilled) {
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
arrayEditClose();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表新增单行 */
|
||||||
|
function arrayAdd() {
|
||||||
|
const from = arrayInitAdd(arrayState.data, arrayState.dataRule);
|
||||||
|
|
||||||
|
// 处理信息
|
||||||
|
const row: Record<string, any> = {};
|
||||||
|
for (const v of from.record) {
|
||||||
|
if (Array.isArray(v.array)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
row[v.name] = Object.assign({}, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 特殊SMF-upfid选择
|
||||||
|
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(row, 'upfId')) {
|
||||||
|
const v = row.upfId.value;
|
||||||
|
if (typeof v === 'string') {
|
||||||
|
if (v === '') {
|
||||||
|
row.upfId.value = [];
|
||||||
|
} else if (v.includes(';')) {
|
||||||
|
row.upfId.value = v.split(';');
|
||||||
|
} else if (v.includes(',')) {
|
||||||
|
row.upfId.value = v.split(',');
|
||||||
|
} else {
|
||||||
|
row.upfId.value = [v];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modalState.from = row;
|
||||||
|
modalState.type = 'arrayAdd';
|
||||||
|
modalState.title = `${treeState.selectNode.paramDisplay} ${from.title}`;
|
||||||
|
modalState.key = from.key;
|
||||||
|
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
|
||||||
|
modalState.open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表新增单行确认 */
|
||||||
|
function arrayAddOk(from: Record<string, any>) {
|
||||||
|
// 特殊SMF-upfid选择
|
||||||
|
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(from, 'upfId')) {
|
||||||
|
const v = from.upfId.value;
|
||||||
|
if (Array.isArray(v)) {
|
||||||
|
from.upfId.value = v.join(';');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历提取属性和值
|
||||||
|
let data: Record<string, any> = {};
|
||||||
|
for (const key in from) {
|
||||||
|
// 子嵌套的不插入
|
||||||
|
if (from[key]['array']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 检查规则
|
||||||
|
const [ok, msg] = ruleVerification(from[key]);
|
||||||
|
if (!ok) {
|
||||||
|
message.warning({
|
||||||
|
content: `${msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data[key] = from[key]['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求
|
||||||
|
const reqArr = [];
|
||||||
|
if (neTypeSelect.value[1].startsWith('SYNC')) {
|
||||||
|
for (const neId of neIdSelect.value) {
|
||||||
|
reqArr.push(
|
||||||
|
addNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neId,
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc: `${from['index']['value']}`,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reqArr.push(
|
||||||
|
addNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neTypeSelect.value[1],
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc: `${from['index']['value']}`,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 无请求提示
|
||||||
|
if (reqArr.length === 0) {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.neIdSyncPleace'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
arrayEditClose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
Promise.allSettled(reqArr)
|
||||||
|
.then(resArr => {
|
||||||
|
const rejected = resArr.find(res => res.status === 'rejected');
|
||||||
|
if (rejected) {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.addItemErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.addItemOk', {
|
||||||
|
num: modalState.title,
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const fulfilled = resArr.find(res => res.status === 'fulfilled');
|
||||||
|
if (fulfilled) {
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
arrayEditClose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表编辑行数据初始化 */
|
||||||
|
function arrayInitEdit(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 arrayInitAdd(data: any[], dataRule: any) {
|
||||||
|
// 有数据时取得最后的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 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') {
|
||||||
|
let newIndex =
|
||||||
|
dataLastIndex !== 0 ? dataLastIndex : parseInt(row.value);
|
||||||
|
if (isNaN(newIndex)) {
|
||||||
|
newIndex = 0;
|
||||||
|
}
|
||||||
|
row.value = newIndex;
|
||||||
|
ruleFrom.key = newIndex;
|
||||||
|
ruleFrom.title = `Index-${newIndex}`;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 根据规则类型转值
|
||||||
|
if (['enum', 'int'].includes(row.type)) {
|
||||||
|
row.value = Number(row.value);
|
||||||
|
}
|
||||||
|
if ('bool' === row.type) {
|
||||||
|
row.value = Boolean(row.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 特殊SMF-upfid选择
|
||||||
|
if (neTypeSelect.value[0] === 'SMF' && row.name === 'upfId') {
|
||||||
|
const v = row.value;
|
||||||
|
if (typeof v === 'string') {
|
||||||
|
if (v === '') {
|
||||||
|
row.value = [];
|
||||||
|
} else if (v.includes(';')) {
|
||||||
|
row.value = v.split(';');
|
||||||
|
} else if (v.includes(',')) {
|
||||||
|
row.value = v.split(',');
|
||||||
|
} else {
|
||||||
|
row.value = [v];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ruleFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听表格字段列排序变化关闭展开
|
||||||
|
watch(
|
||||||
|
() => arrayState.columnsDnd,
|
||||||
|
() => {
|
||||||
|
arrayEditClose();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
arrayState,
|
||||||
|
arrayEdit,
|
||||||
|
arrayEditClose,
|
||||||
|
arrayEditOk,
|
||||||
|
arrayDelete,
|
||||||
|
arrayAdd,
|
||||||
|
arrayAddOk,
|
||||||
|
arrayInitEdit,
|
||||||
|
arrayInitAdd,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,454 @@
|
|||||||
|
import {
|
||||||
|
addNeConfigData,
|
||||||
|
editNeConfigData,
|
||||||
|
delNeConfigData,
|
||||||
|
} from '@/api/ne/neConfig';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { Modal, message } from 'ant-design-vue/es';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { nextTick, reactive } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数配置array类型的嵌套array
|
||||||
|
* @param param 父级传入 { t, treeState, neTypeSelect, neIdSelect, fnActiveConfigNode, ruleVerification, modalState, arrayState, arrayInitEdit, arrayInitAdd, arrayEditClose}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function useConfigArrayChild({
|
||||||
|
t,
|
||||||
|
treeState,
|
||||||
|
neTypeSelect,
|
||||||
|
neIdSelect,
|
||||||
|
fnActiveConfigNode,
|
||||||
|
ruleVerification,
|
||||||
|
modalState,
|
||||||
|
arrayState,
|
||||||
|
arrayInitEdit,
|
||||||
|
arrayInitAdd,
|
||||||
|
arrayEditClose,
|
||||||
|
}: any) {
|
||||||
|
/**多列嵌套列表状态类型 */
|
||||||
|
type ArrayChildStateType = {
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**层级index */
|
||||||
|
loc: string;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**多列嵌套记录字段 */
|
||||||
|
columns: Record<string, any>[];
|
||||||
|
/**表格字段列排序 */
|
||||||
|
columnsDnd: Record<string, any>[];
|
||||||
|
/**多列记录数据 */
|
||||||
|
columnsData: Record<string, any>[];
|
||||||
|
|
||||||
|
/**多列嵌套记录数据 */
|
||||||
|
data: Record<string, any>[];
|
||||||
|
/**多列嵌套记录规则 */
|
||||||
|
dataRule: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**多列嵌套表格状态 */
|
||||||
|
let arrayChildState: ArrayChildStateType = reactive({
|
||||||
|
title: '',
|
||||||
|
loc: '',
|
||||||
|
size: 'small',
|
||||||
|
columns: [],
|
||||||
|
columnsDnd: [],
|
||||||
|
columnsData: [],
|
||||||
|
data: [],
|
||||||
|
dataRule: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**多列表展开嵌套行 */
|
||||||
|
function arrayChildExpand(
|
||||||
|
indexRow: Record<string, any>,
|
||||||
|
row: Record<string, any>
|
||||||
|
) {
|
||||||
|
const loc = indexRow.value;
|
||||||
|
if (arrayChildState.loc === `${loc}/${row.name}`) {
|
||||||
|
arrayChildState.loc = '';
|
||||||
|
arrayState.arrayChildExpandKeys = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
arrayChildState.loc = '';
|
||||||
|
arrayState.arrayChildExpandKeys = [];
|
||||||
|
const from = Object.assign({}, JSON.parse(JSON.stringify(row)));
|
||||||
|
// 无数据时
|
||||||
|
if (!Array.isArray(from.value)) {
|
||||||
|
from.value = [];
|
||||||
|
}
|
||||||
|
const dataArr = Object.freeze(from.value);
|
||||||
|
const ruleArr = Object.freeze(from.array);
|
||||||
|
|
||||||
|
// 列表项数据
|
||||||
|
const dataArray: Record<string, any>[] = [];
|
||||||
|
for (const item of dataArr) {
|
||||||
|
const index = item['index'];
|
||||||
|
let record: Record<string, any>[] = [];
|
||||||
|
for (const key of Object.keys(item)) {
|
||||||
|
// 规则为准
|
||||||
|
for (const rule of ruleArr) {
|
||||||
|
if (rule['name'] === key) {
|
||||||
|
const ruleItem = Object.assign({ optional: 'true' }, rule, {
|
||||||
|
value: item[key],
|
||||||
|
});
|
||||||
|
record.push(ruleItem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// dataArray.push(record);
|
||||||
|
dataArray.push({ title: `Index-${index}`, key: index, record });
|
||||||
|
}
|
||||||
|
arrayChildState.data = dataArray;
|
||||||
|
|
||||||
|
// 无数据时,用于新增
|
||||||
|
arrayChildState.dataRule = {
|
||||||
|
title: `Index-0`,
|
||||||
|
key: 0,
|
||||||
|
record: ruleArr,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 列表数据
|
||||||
|
const columnsData: Record<string, any>[] = [];
|
||||||
|
for (const v of arrayChildState.data) {
|
||||||
|
const row: Record<string, any> = {};
|
||||||
|
for (const item of v.record) {
|
||||||
|
row[item.name] = item;
|
||||||
|
}
|
||||||
|
columnsData.push(row);
|
||||||
|
}
|
||||||
|
arrayChildState.columnsData = columnsData;
|
||||||
|
|
||||||
|
// 列表字段
|
||||||
|
const columns: Record<string, any>[] = [];
|
||||||
|
for (const rule of arrayChildState.dataRule.record) {
|
||||||
|
columns.push({
|
||||||
|
title: rule.display,
|
||||||
|
dataIndex: rule.name,
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 50,
|
||||||
|
minWidth: 50,
|
||||||
|
maxWidth: 250,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
columns.push({
|
||||||
|
title: t('common.operate'),
|
||||||
|
dataIndex: 'index',
|
||||||
|
key: 'index',
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 100,
|
||||||
|
});
|
||||||
|
arrayChildState.columns = columns;
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
// 设置展开key
|
||||||
|
arrayState.arrayChildExpandKeys = [indexRow];
|
||||||
|
// 层级标识
|
||||||
|
arrayChildState.loc = `${loc}/${from['name']}`;
|
||||||
|
// 设置展开列表标题
|
||||||
|
arrayChildState.title = `${from['display']}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表嵌套行编辑 */
|
||||||
|
function arrayChildEdit(rowIndex: Record<string, any>) {
|
||||||
|
const item = arrayChildState.data.find(
|
||||||
|
(s: any) => s.key === rowIndex.value
|
||||||
|
);
|
||||||
|
if (!item) return;
|
||||||
|
const from = arrayInitEdit(item, arrayChildState.dataRule);
|
||||||
|
// 处理信息
|
||||||
|
const row: Record<string, any> = {};
|
||||||
|
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} ${from.title}`;
|
||||||
|
modalState.key = from.key;
|
||||||
|
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
|
||||||
|
modalState.open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表嵌套行编辑确认 */
|
||||||
|
function arrayChildEditOk(from: Record<string, any>) {
|
||||||
|
const loc = `${arrayChildState.loc}/${from['index']['value']}`;
|
||||||
|
|
||||||
|
let data: Record<string, any> = {};
|
||||||
|
for (const key in from) {
|
||||||
|
// 子嵌套的不插入
|
||||||
|
if (from[key]['array']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 检查规则
|
||||||
|
const [ok, msg] = ruleVerification(from[key]);
|
||||||
|
if (!ok) {
|
||||||
|
message.warning({
|
||||||
|
content: `${msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data[key] = from[key]['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求
|
||||||
|
const reqArr = [];
|
||||||
|
if (neTypeSelect.value[1].startsWith('SYNC')) {
|
||||||
|
for (const neId of neIdSelect.value) {
|
||||||
|
reqArr.push(
|
||||||
|
editNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neId,
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reqArr.push(
|
||||||
|
editNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neTypeSelect.value[1],
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 无请求提示
|
||||||
|
if (reqArr.length === 0) {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.neIdSyncPleace'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
arrayEditClose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
Promise.allSettled(reqArr)
|
||||||
|
.then(resArr => {
|
||||||
|
const rejected = resArr.find(res => res.status === 'rejected');
|
||||||
|
if (rejected) {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.updateItemErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.updateItem', {
|
||||||
|
num: modalState.title,
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const fulfilled = resArr.find(res => res.status === 'fulfilled');
|
||||||
|
if (fulfilled) {
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
arrayEditClose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表嵌套行删除单行 */
|
||||||
|
function arrayChildDelete(rowIndex: Record<string, any>) {
|
||||||
|
const index = rowIndex.value;
|
||||||
|
const loc = `${arrayChildState.loc}/${index}`;
|
||||||
|
const title = `${arrayChildState.title} Index-${index}`;
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.ne.neConfig.delItemTip', {
|
||||||
|
num: title,
|
||||||
|
}),
|
||||||
|
onOk() {
|
||||||
|
// 请求
|
||||||
|
const reqArr = [];
|
||||||
|
if (neTypeSelect.value[1].startsWith('SYNC')) {
|
||||||
|
for (const neId of neIdSelect.value) {
|
||||||
|
reqArr.push(
|
||||||
|
delNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neId,
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
loc,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reqArr.push(
|
||||||
|
delNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neTypeSelect.value[1],
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
loc,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 无请求提示
|
||||||
|
if (reqArr.length === 0) {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.neIdSyncPleace'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
arrayEditClose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
Promise.allSettled(reqArr)
|
||||||
|
.then(resArr => {
|
||||||
|
const rejected = resArr.find(res => res.status === 'rejected');
|
||||||
|
if (rejected) {
|
||||||
|
message.error({
|
||||||
|
content: `${rejected.reason}`,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.delItemOk', {
|
||||||
|
num: title,
|
||||||
|
}),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const fulfilled = resArr.find(res => res.status === 'fulfilled');
|
||||||
|
if (fulfilled) {
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
arrayEditClose();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表嵌套行新增单行 */
|
||||||
|
function arrayChildAdd() {
|
||||||
|
const from = arrayInitAdd(arrayChildState.data, arrayChildState.dataRule);
|
||||||
|
// 处理信息
|
||||||
|
const row: Record<string, any> = {};
|
||||||
|
for (const v of from.record) {
|
||||||
|
if (Array.isArray(v.array)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
row[v.name] = Object.assign({}, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
modalState.from = row;
|
||||||
|
modalState.type = 'arrayChildAdd';
|
||||||
|
modalState.title = `${arrayChildState.title} ${from.title}`;
|
||||||
|
modalState.key = from.key;
|
||||||
|
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
|
||||||
|
modalState.open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表新增单行确认 */
|
||||||
|
function arrayChildAddOk(from: Record<string, any>) {
|
||||||
|
const loc = `${arrayChildState.loc}/${from['index']['value']}`;
|
||||||
|
|
||||||
|
let data: Record<string, any> = {};
|
||||||
|
for (const key in from) {
|
||||||
|
// 子嵌套的不插入
|
||||||
|
if (from[key]['array']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 检查规则
|
||||||
|
const [ok, msg] = ruleVerification(from[key]);
|
||||||
|
if (!ok) {
|
||||||
|
message.warning({
|
||||||
|
content: `${msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data[key] = from[key]['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求
|
||||||
|
const reqArr = [];
|
||||||
|
if (neTypeSelect.value[1].startsWith('SYNC')) {
|
||||||
|
for (const neId of neIdSelect.value) {
|
||||||
|
reqArr.push(
|
||||||
|
addNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neId,
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reqArr.push(
|
||||||
|
addNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neTypeSelect.value[1],
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 无请求提示
|
||||||
|
if (reqArr.length === 0) {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.neIdSyncPleace'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
arrayEditClose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
Promise.allSettled(reqArr)
|
||||||
|
.then(resArr => {
|
||||||
|
const rejected = resArr.find(res => res.status === 'rejected');
|
||||||
|
if (rejected) {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.addItemErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.addItemOk', {
|
||||||
|
num: modalState.title,
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const fulfilled = resArr.find(res => res.status === 'fulfilled');
|
||||||
|
if (fulfilled) {
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
arrayEditClose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
arrayChildState,
|
||||||
|
arrayChildExpand,
|
||||||
|
arrayChildEdit,
|
||||||
|
arrayChildEditOk,
|
||||||
|
arrayChildDelete,
|
||||||
|
arrayChildAdd,
|
||||||
|
arrayChildAddOk,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
import { editNeConfigData } from '@/api/ne/neConfig';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { message } from 'ant-design-vue/es';
|
||||||
|
import { reactive, toRaw } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list类型参数处理
|
||||||
|
* @param param 父级传入 {t, treeState, neTypeSelect, neIdSelect, ruleVerification}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function useConfigList({
|
||||||
|
t,
|
||||||
|
treeState,
|
||||||
|
neTypeSelect,
|
||||||
|
neIdSelect,
|
||||||
|
ruleVerification,
|
||||||
|
}: any) {
|
||||||
|
/**单列表状态类型 */
|
||||||
|
type ListStateType = {
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**单列记录字段 */
|
||||||
|
columns: Record<string, any>[];
|
||||||
|
/**单列记录数据 */
|
||||||
|
data: Record<string, any>[];
|
||||||
|
/**编辑行记录 */
|
||||||
|
editRecord: Record<string, any>;
|
||||||
|
/**确认提交等待 */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**单列表状态 */
|
||||||
|
let listState: ListStateType = reactive({
|
||||||
|
size: 'small',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
title: 'Key',
|
||||||
|
dataIndex: 'display',
|
||||||
|
align: 'left',
|
||||||
|
width: '30%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Value',
|
||||||
|
dataIndex: 'value',
|
||||||
|
align: 'left',
|
||||||
|
width: '70%',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
data: [],
|
||||||
|
confirmLoading: false,
|
||||||
|
editRecord: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**单列表编辑 */
|
||||||
|
function listEdit(row: Record<string, any>) {
|
||||||
|
if (
|
||||||
|
listState.confirmLoading ||
|
||||||
|
['read-only', 'read', 'ro'].includes(row.access)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listState.editRecord = Object.assign({}, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**单列表编辑关闭 */
|
||||||
|
function listEditClose() {
|
||||||
|
listState.confirmLoading = false;
|
||||||
|
listState.editRecord = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**单列表编辑确认 */
|
||||||
|
function listEditOk() {
|
||||||
|
if (listState.confirmLoading) return;
|
||||||
|
const from = toRaw(listState.editRecord);
|
||||||
|
// 检查规则
|
||||||
|
const [ok, msg] = ruleVerification(from);
|
||||||
|
if (!ok) {
|
||||||
|
message.warning({
|
||||||
|
content: `${msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求
|
||||||
|
const reqArr = [];
|
||||||
|
if (neTypeSelect.value[1].startsWith('SYNC')) {
|
||||||
|
for (const neId of neIdSelect.value) {
|
||||||
|
reqArr.push(
|
||||||
|
editNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neId,
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: {
|
||||||
|
[from['name']]: from['value'],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reqArr.push(
|
||||||
|
editNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neTypeSelect.value[1],
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: {
|
||||||
|
[from['name']]: from['value'],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 无请求提示
|
||||||
|
if (reqArr.length === 0) {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.neIdSyncPleace'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
listState.confirmLoading = false;
|
||||||
|
listState.editRecord = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
Promise.allSettled(reqArr)
|
||||||
|
.then(resArr => {
|
||||||
|
const rejected = resArr.find(res => res.status === 'rejected');
|
||||||
|
if (rejected) {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.updateValueErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.updateValue', {
|
||||||
|
num: from['display'],
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const fulfilled = resArr.find(res => res.status === 'fulfilled');
|
||||||
|
if (fulfilled) {
|
||||||
|
// 改变表格数据
|
||||||
|
const item = listState.data.find(
|
||||||
|
(item: Record<string, any>) => from['name'] === item['name']
|
||||||
|
);
|
||||||
|
if (item) {
|
||||||
|
Object.assign(item, listState.editRecord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
listState.confirmLoading = false;
|
||||||
|
listState.editRecord = {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
let tablePagination = reactive({
|
||||||
|
/**当前页数 */
|
||||||
|
current: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 10,
|
||||||
|
/**默认的每页条数 */
|
||||||
|
defaultPageSize: 10,
|
||||||
|
/**指定每页可以显示多少条 */
|
||||||
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
|
/**只有一页时是否隐藏分页器 */
|
||||||
|
hideOnSinglePage: true,
|
||||||
|
/**是否可以快速跳转至某页 */
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { tablePagination, listState, listEdit, listEditClose, listEditOk };
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
import { getNeConfigData } from '@/api/ne/neConfig';
|
||||||
|
import { regExpIPv4, regExpIPv6, validURL } from '@/utils/regular-utils';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数公共函数
|
||||||
|
* @param param 父级传入 {t}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function useOptions({ t }: any) {
|
||||||
|
/**规则校验 */
|
||||||
|
function ruleVerification(row: Record<string, any>): (string | boolean)[] {
|
||||||
|
let result = [true, ''];
|
||||||
|
const type = row.type;
|
||||||
|
const value = row.value;
|
||||||
|
const filter = row.filter;
|
||||||
|
const display = row.display;
|
||||||
|
|
||||||
|
// 子嵌套的不检查
|
||||||
|
if (row.array) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 可选的同时没有值不检查
|
||||||
|
if (row.optional === 'true' && !value) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case 'int':
|
||||||
|
// filter: "0~128"
|
||||||
|
|
||||||
|
if (filter && filter.indexOf('~') !== -1) {
|
||||||
|
const filterArr = filter.split('~');
|
||||||
|
const minInt = parseInt(filterArr[0]);
|
||||||
|
const maxInt = parseInt(filterArr[1]);
|
||||||
|
const valueInt = parseInt(value);
|
||||||
|
if (valueInt < minInt || valueInt > maxInt) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireInt', {
|
||||||
|
display,
|
||||||
|
filter,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'ipv4':
|
||||||
|
if (!regExpIPv4.test(value)) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireIpv4', { display }),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'ipv6':
|
||||||
|
if (!regExpIPv6.test(value)) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireIpv6', { display }),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'enum':
|
||||||
|
if (filter && filter.indexOf('{') === 1) {
|
||||||
|
let filterJson: Record<string, any> = {};
|
||||||
|
try {
|
||||||
|
filterJson = JSON.parse(filter); //string---json
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Object.keys(filterJson).includes(`${value}`)) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireEnum', { display }),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'bool':
|
||||||
|
// filter: '{"0":"false", "1":"true"}'
|
||||||
|
|
||||||
|
if (filter && filter.indexOf('{') === 1) {
|
||||||
|
let filterJson: Record<string, any> = {};
|
||||||
|
try {
|
||||||
|
filterJson = JSON.parse(filter); //string---json
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Object.values(filterJson).includes(`${value}`)) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireBool', { display }),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'string':
|
||||||
|
// filter: "0~128"
|
||||||
|
|
||||||
|
// 字符串长度判断
|
||||||
|
if (filter && filter.indexOf('~') !== -1) {
|
||||||
|
try {
|
||||||
|
const filterArr = filter.split('~');
|
||||||
|
let rule = new RegExp(
|
||||||
|
'^\\S{' + filterArr[0] + ',' + filterArr[1] + '}$'
|
||||||
|
);
|
||||||
|
if (!rule.test(value)) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireString', {
|
||||||
|
display,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 字符串http判断
|
||||||
|
if (value.startsWith('http')) {
|
||||||
|
try {
|
||||||
|
if (!validURL(value)) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireString', {
|
||||||
|
display,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'regex':
|
||||||
|
// filter: "^[0-9]{3}$"
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
try {
|
||||||
|
let regex = new RegExp(filter);
|
||||||
|
if (!regex.test(value)) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireString', {
|
||||||
|
display,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireUn', { display }),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**upfId可选择 */
|
||||||
|
const smfByUPFIdOptions = ref<{ value: string; label: string }[]>([]);
|
||||||
|
/**加载smf配置的upfId */
|
||||||
|
function smfByUPFIdLoadData(neId: string) {
|
||||||
|
getNeConfigData({
|
||||||
|
neType: 'SMF',
|
||||||
|
neId: neId,
|
||||||
|
paramName: 'upfConfig',
|
||||||
|
}).then(res => {
|
||||||
|
smfByUPFIdOptions.value = [];
|
||||||
|
for (const s of res.data) {
|
||||||
|
smfByUPFIdOptions.value.push({
|
||||||
|
value: s.id,
|
||||||
|
label: s.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
ruleVerification,
|
||||||
|
smfByUPFIdLoadData,
|
||||||
|
smfByUPFIdOptions,
|
||||||
|
};
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -104,16 +104,16 @@ let modalState: ModalStateType = reactive({
|
|||||||
from: {
|
from: {
|
||||||
id: undefined,
|
id: undefined,
|
||||||
neId: '001',
|
neId: '001',
|
||||||
neType: 'AMF',
|
neType: 'MF',
|
||||||
neName: '',
|
neName: '',
|
||||||
ip: '',
|
ip: '',
|
||||||
port: 33030,
|
port: 33030,
|
||||||
pvFlag: 'PNF',
|
pvFlag: 'PNF',
|
||||||
rmUid: '4400HXAMF001',
|
rmUid: '4400PSAPMF0001',
|
||||||
neAddress: '',
|
neAddress: '',
|
||||||
dn: '',
|
dn: '',
|
||||||
vendorName: '',
|
vendorName: '',
|
||||||
province: '',
|
province: 'Area',
|
||||||
remark: '',
|
remark: '',
|
||||||
// 主机
|
// 主机
|
||||||
hosts: [
|
hosts: [
|
||||||
@@ -321,14 +321,14 @@ function fnNeTypeChange(v: any) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
modalState.from.rmUid = `4400HX${v}${modalState.from.neId}`; // 4400HX1AMF001
|
modalState.from.rmUid = `4400PSAP${v}${modalState.from.neId}`; // 4400HX1MF001
|
||||||
}
|
}
|
||||||
|
|
||||||
/**表单修改网元neId */
|
/**表单修改网元neId */
|
||||||
function fnNeIdChange(e: any) {
|
function fnNeIdChange(e: any) {
|
||||||
const v = e.target.value;
|
const v = e.target.value;
|
||||||
if (v.length < 1) return;
|
if (v.length < 1) return;
|
||||||
modalState.from.rmUid = `4400HX${modalState.from.neType}${v}`; // 4400HX1AMF001
|
modalState.from.rmUid = `4400PSAP${modalState.from.neType}${v}`; // 4400HX1MF001
|
||||||
}
|
}
|
||||||
|
|
||||||
/**表单修改网元IP */
|
/**表单修改网元IP */
|
||||||
|
|||||||
@@ -78,14 +78,19 @@ function fnModalVisibleByTypeAndId(neType: string, neId: string) {
|
|||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
const data = res.data;
|
const data = res.data;
|
||||||
|
const ipType = data?.oamConfig?.ipType || 'ipv4';
|
||||||
|
let omcIP = '127.0.0.1';
|
||||||
|
if (data.oamConfig && Reflect.has(data.oamConfig, ipType)) {
|
||||||
|
omcIP = data?.oamConfig[ipType];
|
||||||
|
}
|
||||||
Object.assign(modalState.from, {
|
Object.assign(modalState.from, {
|
||||||
omcIP: data.oamConfig[data.oamConfig.ipType],
|
omcIP: omcIP,
|
||||||
oamEnable: data.oamConfig.enable,
|
oamEnable: data?.oamConfig?.enable || false,
|
||||||
oamPort: data.oamConfig.port,
|
oamPort: data?.oamConfig?.port || 33030,
|
||||||
snmpEnable: data.snmpConfig.enable,
|
snmpEnable: data?.snmpConfig?.enable || false,
|
||||||
snmpPort: data.snmpConfig.port,
|
snmpPort: data?.snmpConfig?.port || 4957,
|
||||||
kpiEnable: data.kpiConfig.enable,
|
kpiEnable: data?.kpiConfig?.enable || false,
|
||||||
kpiTimer: data.kpiConfig.timer,
|
kpiTimer: data?.kpiConfig?.timer || 60,
|
||||||
});
|
});
|
||||||
modalState.title = t('views.ne.neInfo.oam.title');
|
modalState.title = t('views.ne.neInfo.oam.title');
|
||||||
modalState.openByEdit = true;
|
modalState.openByEdit = true;
|
||||||
|
|||||||
@@ -237,8 +237,13 @@ function fnModalEditOk(from: Record<string, any>) {
|
|||||||
item.neName = from.neName;
|
item.neName = from.neName;
|
||||||
item.ip = from.ip;
|
item.ip = from.ip;
|
||||||
item.port = from.port;
|
item.port = from.port;
|
||||||
if (item.status !== '2') {
|
if (res.data.online) {
|
||||||
item.status = res.data.online ? '1' : '0';
|
item.status = '1';
|
||||||
|
if (res.data.standby) {
|
||||||
|
item.status = '3';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
item.status = '0';
|
||||||
}
|
}
|
||||||
Object.assign(item.serverState, res.data);
|
Object.assign(item.serverState, res.data);
|
||||||
const resouresUsage = parseResouresUsage(item.serverState);
|
const resouresUsage = parseResouresUsage(item.serverState);
|
||||||
@@ -357,7 +362,8 @@ function fnGetList(pageNum?: number) {
|
|||||||
if (pageNum) {
|
if (pageNum) {
|
||||||
queryParams.pageNum = pageNum;
|
queryParams.pageNum = pageNum;
|
||||||
}
|
}
|
||||||
listNeInfo(toRaw(queryParams)).then(res => {
|
listNeInfo(toRaw(queryParams))
|
||||||
|
.then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
// 取消勾选
|
// 取消勾选
|
||||||
if (tableState.selectedRowKeys.length > 0) {
|
if (tableState.selectedRowKeys.length > 0) {
|
||||||
@@ -383,6 +389,10 @@ function fnGetList(pageNum?: number) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
tableState.loading = false;
|
tableState.loading = false;
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// 刷新缓存的网元信息
|
||||||
|
useNeInfoStore().fnRefreshNelist();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,13 +451,8 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 刷新缓存的网元信息
|
|
||||||
useNeInfoStore()
|
|
||||||
.fnRefreshNelist()
|
|
||||||
.finally(() => {
|
|
||||||
// 获取列表数据
|
// 获取列表数据
|
||||||
fnGetList();
|
fnGetList();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -662,15 +667,7 @@ onMounted(() => {
|
|||||||
</a-divider>
|
</a-divider>
|
||||||
<div>
|
<div>
|
||||||
<span>{{ t('views.ne.neInfo.serviceState') }}:</span>
|
<span>{{ t('views.ne.neInfo.serviceState') }}:</span>
|
||||||
<a-tag
|
<DictTag :options="dict.neInfoStatus" :value="record.status" />
|
||||||
:color="record.serverState.online ? 'processing' : 'error'"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
record.serverState.online
|
|
||||||
? t('views.ne.common.normalcy')
|
|
||||||
: t('views.ne.common.exceptions')
|
|
||||||
}}
|
|
||||||
</a-tag>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>{{ t('views.ne.neVersion.version') }}:</span>
|
<span>{{ t('views.ne.neVersion.version') }}:</span>
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ let tableColumns = ref<TableColumnsType>([
|
|||||||
title: t('views.ne.common.serialNum'),
|
title: t('views.ne.common.serialNum'),
|
||||||
dataIndex: 'serialNum',
|
dataIndex: 'serialNum',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 100,
|
width: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.ne.common.expiryDate'),
|
title: t('views.ne.common.expiryDate'),
|
||||||
@@ -126,12 +126,12 @@ let tableColumns = ref<TableColumnsType>([
|
|||||||
{
|
{
|
||||||
title: t('common.updateTime'),
|
title: t('common.updateTime'),
|
||||||
dataIndex: 'updateTime',
|
dataIndex: 'updateTime',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
if (!opt.value) return '';
|
if (!opt.value) return '';
|
||||||
return parseDateToStr(opt.value);
|
return parseDateToStr(opt.value);
|
||||||
},
|
},
|
||||||
width: 150,
|
width: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('common.operate'),
|
title: t('common.operate'),
|
||||||
@@ -413,7 +413,7 @@ onMounted(() => {
|
|||||||
:loading="modalState.confirmLoading"
|
:loading="modalState.confirmLoading"
|
||||||
@click.prevent="fnRecordStateBatch()"
|
@click.prevent="fnRecordStateBatch()"
|
||||||
>
|
>
|
||||||
<template #icon><CloudSyncOutlined /></template>
|
<template #icon><SyncOutlined /></template>
|
||||||
{{ t('views.ne.neLicense.reloadBatch') }}
|
{{ t('views.ne.neLicense.reloadBatch') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
@@ -508,7 +508,7 @@ onMounted(() => {
|
|||||||
<a-tooltip placement="topRight">
|
<a-tooltip placement="topRight">
|
||||||
<template #title>{{ t('views.ne.neLicense.reload') }}</template>
|
<template #title>{{ t('views.ne.neLicense.reload') }}</template>
|
||||||
<a-button type="link" @click.prevent="fnRecordState(record)">
|
<a-button type="link" @click.prevent="fnRecordState(record)">
|
||||||
<template #icon><CloudSyncOutlined /> </template>
|
<template #icon><SyncOutlined /> </template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip placement="topRight">
|
<a-tooltip placement="topRight">
|
||||||
@@ -517,7 +517,7 @@ onMounted(() => {
|
|||||||
type="link"
|
type="link"
|
||||||
@click.prevent="fnModalVisibleByEdit(record.id)"
|
@click.prevent="fnModalVisibleByEdit(record.id)"
|
||||||
>
|
>
|
||||||
<template #icon><CloudUploadOutlined /> </template>
|
<template #icon><UploadOutlined /> </template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ let modalState: ModalStateType = reactive({
|
|||||||
neAddress: '',
|
neAddress: '',
|
||||||
dn: '-',
|
dn: '-',
|
||||||
vendorName: '-',
|
vendorName: '-',
|
||||||
province: '-',
|
province: 'Area',
|
||||||
// 主机
|
// 主机
|
||||||
hosts: [
|
hosts: [
|
||||||
{
|
{
|
||||||
@@ -187,7 +187,7 @@ function fnModalOk() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
from.rmUid = `4400HX${from.neType}${from.neId}`; // 4400HX1AMF001
|
from.rmUid = `4400PSAP${from.neType}${from.neId}`; // 4400HX1AMF001
|
||||||
from.neName = `${from.neType}_${from.neId}`; // AMF_001
|
from.neName = `${from.neType}_${from.neId}`; // AMF_001
|
||||||
const hide = message.loading(t('common.loading'), 0);
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
const result =
|
const result =
|
||||||
|
|||||||
@@ -50,9 +50,10 @@ let fromState = ref({
|
|||||||
ims_sip_ip: '192.168.8.110',
|
ims_sip_ip: '192.168.8.110',
|
||||||
upf_type: 'LightUPF',
|
upf_type: 'LightUPF',
|
||||||
upf_driver_type: 'vmxnet3',
|
upf_driver_type: 'vmxnet3',
|
||||||
upf_card_name: 'eth0',
|
upfn3_card_name: 'eth0',
|
||||||
upfn3_pci: '0000:00:00.0',
|
upfn3_pci: '0000:00:00.0',
|
||||||
upfn3_mac: '00:00:00:00:00:00',
|
upfn3_mac: '00:00:00:00:00:00',
|
||||||
|
upfn6_card_name: 'eth0',
|
||||||
upfn6_pci: '0000:00:00.0',
|
upfn6_pci: '0000:00:00.0',
|
||||||
upfn6_mac: '00:00:00:00:00:00',
|
upfn6_mac: '00:00:00:00:00:00',
|
||||||
},
|
},
|
||||||
@@ -243,7 +244,12 @@ watch(
|
|||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col
|
||||||
|
:lg="12"
|
||||||
|
:md="12"
|
||||||
|
:xs="24"
|
||||||
|
v-if="fromState.external.upf_type === 'LightUPF'"
|
||||||
|
>
|
||||||
<a-form-item label="UE_POOL" name="external.ue_pool">
|
<a-form-item label="UE_POOL" name="external.ue_pool">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="fromState.external.ue_pool"
|
v-model:value="fromState.external.ue_pool"
|
||||||
@@ -260,9 +266,63 @@ watch(
|
|||||||
</a-input>
|
</a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
<a-col
|
||||||
|
:lg="12"
|
||||||
|
:md="12"
|
||||||
|
:xs="24"
|
||||||
|
v-if="fromState.external.upf_type === 'StandardUPF'"
|
||||||
|
>
|
||||||
|
<a-form-item label="DRIVER_TYPE" name="external.upf_driver_type">
|
||||||
|
<a-select
|
||||||
|
v-model:value="fromState.external.upf_driver_type"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
>
|
||||||
|
<a-select-option value="vmxnet3">vmxnet3</a-select-option>
|
||||||
|
<a-select-option value="dpdk">dpdk</a-select-option>
|
||||||
|
<a-select-option value="host">host</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<a-row>
|
<a-row v-if="fromState.external.upf_type === 'LightUPF'">
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item label="N3_IP" name="external.upfn3_ip">
|
||||||
|
<a-input
|
||||||
|
v-model:value="fromState.external.upfn3_ip"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="50"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title> netwrok ip </template>
|
||||||
|
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item label="N3_GW" name="external.upfn3_gw">
|
||||||
|
<a-input
|
||||||
|
v-model:value="fromState.external.upfn3_gw"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="50"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title> geteway </template>
|
||||||
|
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-row v-if="fromState.external.upf_type === 'StandardUPF'">
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item label="N3_IP" name="external.upfn3_ip">
|
<a-form-item label="N3_IP" name="external.upfn3_ip">
|
||||||
<a-input
|
<a-input
|
||||||
@@ -294,13 +354,6 @@ watch(
|
|||||||
</template>
|
</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
|
||||||
<a-col
|
|
||||||
:lg="12"
|
|
||||||
:md="12"
|
|
||||||
:xs="24"
|
|
||||||
v-if="fromState.external.upf_type === 'StandardUPF'"
|
|
||||||
>
|
|
||||||
<a-form-item label="N3_PCI" name="external.upfn3_pci">
|
<a-form-item label="N3_PCI" name="external.upfn3_pci">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="fromState.external.upfn3_pci"
|
v-model:value="fromState.external.upfn3_pci"
|
||||||
@@ -338,10 +391,27 @@ watch(
|
|||||||
</template>
|
</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label="N3_NIC_NAME" name="external.upfn3_card_name">
|
||||||
|
<a-input
|
||||||
|
v-model:value="fromState.external.upfn3_card_name"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="50"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title>
|
||||||
|
use `ip a` show info, to inet name
|
||||||
|
<br />
|
||||||
|
or <br />
|
||||||
|
use `lshw -class network -businfo` show device name
|
||||||
|
</template>
|
||||||
|
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
|
||||||
<template v-if="fromState.external.upf_type === 'StandardUPF'">
|
|
||||||
<a-row>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item label="N6_IP" name="external.upfn6_ip">
|
<a-form-item label="N6_IP" name="external.upfn6_ip">
|
||||||
<a-input
|
<a-input
|
||||||
@@ -361,18 +431,6 @@ watch(
|
|||||||
>
|
>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="DRIVER_TYPE" name="external.upf_driver_type">
|
|
||||||
<a-select
|
|
||||||
v-model:value="fromState.external.upf_driver_type"
|
|
||||||
:placeholder="t('common.selectPlease')"
|
|
||||||
>
|
|
||||||
<a-select-option value="vmxnet3">vmxnet3</a-select-option>
|
|
||||||
<a-select-option value="dpdk">dpdk</a-select-option>
|
|
||||||
<a-select-option value="host">host</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item label="N6_PCI" name="external.upfn6_pci">
|
<a-form-item label="N6_PCI" name="external.upfn6_pci">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="fromState.external.upfn6_pci"
|
v-model:value="fromState.external.upfn6_pci"
|
||||||
@@ -391,9 +449,9 @@ watch(
|
|||||||
>
|
>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="NIC_NAME" name="external.upf_card_name">
|
<a-form-item label="N6_NIC_NAME" name="external.upfn6_card_name">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="fromState.external.upf_card_name"
|
v-model:value="fromState.external.upfn6_card_name"
|
||||||
allow-clear
|
allow-clear
|
||||||
:placeholder="t('common.inputPlease')"
|
:placeholder="t('common.inputPlease')"
|
||||||
:maxlength="50"
|
:maxlength="50"
|
||||||
@@ -406,16 +464,13 @@ watch(
|
|||||||
or <br />
|
or <br />
|
||||||
use `lshw -class network -businfo` show device name
|
use `lshw -class network -businfo` show device name
|
||||||
</template>
|
</template>
|
||||||
<InfoCircleOutlined
|
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
|
||||||
style="opacity: 0.45; color: inherit"
|
|
||||||
/>
|
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</template>
|
|
||||||
</a-col>
|
</a-col>
|
||||||
|
|
||||||
<a-col :lg="8" :md="8" :xs="24">
|
<a-col :lg="8" :md="8" :xs="24">
|
||||||
|
|||||||
@@ -166,8 +166,8 @@ function fnBeforeUploadFile(file: FileType) {
|
|||||||
const suff = fileName.substring(fileName.lastIndexOf('.'));
|
const suff = fileName.substring(fileName.lastIndexOf('.'));
|
||||||
if (!['.deb', '.rpm'].includes(suff)) {
|
if (!['.deb', '.rpm'].includes(suff)) {
|
||||||
message.error(
|
message.error(
|
||||||
t('views.configManage.softwareManage.onlyAble', {
|
t('views.ne.neSoftware.fileTypeNotEq', {
|
||||||
fileText: '(.deb、.rpm)',
|
txt: '(.deb、.rpm)',
|
||||||
}),
|
}),
|
||||||
3
|
3
|
||||||
);
|
);
|
||||||
@@ -238,8 +238,8 @@ function fnBeforeUploadFileDep(file: FileType) {
|
|||||||
const suff = fileName.substring(fileName.lastIndexOf('.'));
|
const suff = fileName.substring(fileName.lastIndexOf('.'));
|
||||||
if (!['.deb', '.rpm'].includes(suff)) {
|
if (!['.deb', '.rpm'].includes(suff)) {
|
||||||
message.error(
|
message.error(
|
||||||
t('views.configManage.softwareManage.onlyAble', {
|
t('views.ne.neSoftware.fileTypeNotEq', {
|
||||||
fileText: '(.deb、.rpm)',
|
txt: '(.deb、.rpm)',
|
||||||
}),
|
}),
|
||||||
3
|
3
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -171,8 +171,8 @@ function fnBeforeUploadFile(file: FileType) {
|
|||||||
const suff = fileName.substring(fileName.lastIndexOf('.'));
|
const suff = fileName.substring(fileName.lastIndexOf('.'));
|
||||||
if (!['.deb', '.rpm'].includes(suff)) {
|
if (!['.deb', '.rpm'].includes(suff)) {
|
||||||
message.error(
|
message.error(
|
||||||
t('views.configManage.softwareManage.onlyAble', {
|
t('views.ne.neSoftware.fileTypeNotEq', {
|
||||||
fileText: '(.deb、.rpm)',
|
txt: '(.deb、.rpm)',
|
||||||
}),
|
}),
|
||||||
3
|
3
|
||||||
);
|
);
|
||||||
@@ -286,8 +286,8 @@ function fnBeforeUploadFileDep(file: FileType) {
|
|||||||
const suff = fileName.substring(fileName.lastIndexOf('.'));
|
const suff = fileName.substring(fileName.lastIndexOf('.'));
|
||||||
if (!['.deb', '.rpm'].includes(suff)) {
|
if (!['.deb', '.rpm'].includes(suff)) {
|
||||||
message.error(
|
message.error(
|
||||||
t('views.configManage.softwareManage.onlyAble', {
|
t('views.ne.neSoftware.fileTypeNotEq', {
|
||||||
fileText: '(.deb、.rpm)',
|
txt: '(.deb、.rpm)',
|
||||||
}),
|
}),
|
||||||
3
|
3
|
||||||
);
|
);
|
||||||
|
|||||||
Loaded 100 of 123 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user