Compare commits
564
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dac294b48 | ||
|
|
20007f4732 | ||
|
|
0cb892e7f3 | ||
|
|
88d4b0cbb6 | ||
|
|
729d5518e8 | ||
|
|
cb510d9fdb | ||
|
|
f310ae4f38 | ||
|
|
f070764b69 | ||
|
|
0c96b4d130 | ||
|
|
285c9b660b | ||
|
|
2fb3467fb5 | ||
|
|
63d7d11350 | ||
|
|
c1187383b6 | ||
|
|
8d8605e0cd | ||
|
|
8af48936e5 | ||
|
|
2bd98540be | ||
|
|
e21a8dc898 | ||
|
|
630c63e23f | ||
|
|
3c1ee63359 | ||
|
|
bc3940016a | ||
|
|
6ab4fbea6f | ||
|
|
ada1f388fc | ||
|
|
c73fcbd91c | ||
|
|
b9cf34714f | ||
|
|
53200d5f41 | ||
|
|
023e317b0d | ||
|
|
543a2b7434 | ||
|
|
52d5b9b732 | ||
|
|
292488f20c | ||
|
|
b0b844f0f6 | ||
|
|
0819fbdb44 | ||
|
|
4ae9051411 | ||
|
|
a88ae826cc | ||
|
|
532546c3f5 | ||
|
|
ab0d26513c | ||
|
|
8e9498ec83 | ||
|
|
ca048f223c | ||
|
|
e9ff6493dd | ||
|
|
396fb0b124 | ||
|
|
3a854be8fe | ||
|
|
c1f34f56ac | ||
|
|
b80bae0126 | ||
|
|
6c383b58c8 | ||
|
|
06db1344c3 | ||
|
|
fac47279b5 | ||
|
|
a36a12f783 | ||
|
|
76499d98e0 | ||
|
|
94ccb45f8c | ||
|
|
79e2b4ac26 | ||
|
|
b2510c0eb3 | ||
|
|
f977b6ea53 | ||
|
|
1e9c6a51be | ||
|
|
d68a773214 | ||
|
|
f60f26ae89 | ||
|
|
acd19ebdbd | ||
|
|
d53c4c34f0 | ||
|
|
c35a5a9c33 | ||
|
|
2d9011cf6b | ||
|
|
fcf53d0995 | ||
|
|
4d755cea5d | ||
|
|
c87458bc23 | ||
|
|
a2d93ddafe | ||
|
|
cfd04ba1b6 | ||
|
|
4af02693a4 | ||
|
|
66eb27813f | ||
|
|
79920542c1 | ||
|
|
6efd9cb61a | ||
|
|
4dc6699974 | ||
|
|
65c8c6f809 | ||
|
|
ecaa0ce077 | ||
|
|
99140f0641 | ||
|
|
3bdae264e1 | ||
|
|
f3c46976ae | ||
|
|
9e0a99d160 | ||
|
|
5ac0ca41eb | ||
|
|
a7de701d4d | ||
|
|
efa8764bd1 | ||
|
|
f0561242ca | ||
|
|
bc0e463191 | ||
|
|
45bf9fe115 | ||
|
|
4c85c61f05 | ||
|
|
318f84504f | ||
|
|
6102972373 | ||
|
|
d095531952 | ||
|
|
d49dc9ebfd | ||
|
|
53fa36eace | ||
|
|
43a99e9328 | ||
|
|
d333221620 | ||
|
|
481734cfe1 | ||
|
|
1fa8601675 | ||
|
|
c1aabdbe42 | ||
|
|
9123484bf5 | ||
|
|
953a36f142 | ||
|
|
c5f62e8d76 | ||
|
|
946139facb | ||
|
|
95931b2b6e | ||
|
|
e31c85835d | ||
|
|
847bae4c77 | ||
|
|
e69f43a0c2 | ||
|
|
d96e1ad259 | ||
|
|
3829d339e7 | ||
|
|
e7e4557e96 | ||
|
|
6b1fe4a582 | ||
|
|
cd4073bec3 | ||
|
|
e67126c57a | ||
|
|
2f651b5d1f | ||
|
|
2610ab17ad | ||
|
|
525854604f | ||
|
|
d42a8701da | ||
|
|
2b680d6d20 | ||
|
|
de79760a0e | ||
|
|
a5b5269b91 | ||
|
|
8a2d0ccfa1 | ||
|
|
650b02dc30 | ||
|
|
b42d8cb370 | ||
|
|
a213be0d64 | ||
|
|
2a2b441e09 | ||
|
|
84bdb44286 | ||
|
|
b84a08d825 | ||
|
|
06b6175a76 | ||
|
|
824c38593b | ||
|
|
11b790e140 | ||
|
|
296a9ba02b | ||
|
|
69fb3df7ec | ||
|
|
1801a46396 | ||
|
|
f6644a0d97 | ||
|
|
42827796c7 | ||
|
|
eb38211e3b | ||
|
|
fd1d2a5bad | ||
|
|
57f575aa84 | ||
|
|
2d5bfb9842 | ||
|
|
68cbbe7133 | ||
|
|
c11d814312 | ||
|
|
e1548d2c98 | ||
|
|
1ec374eb26 | ||
|
|
0955a79965 | ||
|
|
c0a7f28958 | ||
|
|
374a9bde7e | ||
|
|
5f4e47e998 | ||
|
|
19a91936de | ||
|
|
639a4f0b1f | ||
|
|
d1b9d7b9ba | ||
|
|
3298203f60 | ||
|
|
8f3cce52c0 | ||
|
|
ce06a88e89 | ||
|
|
749a04972d | ||
|
|
a311f0a09b | ||
|
|
c74d311537 | ||
|
|
1492f18791 | ||
|
|
cbc81643a5 | ||
|
|
c18b557c97 | ||
|
|
b6bd9bc3d5 | ||
|
|
199607e322 | ||
|
|
ee10119d77 | ||
|
|
1b1a56e49b | ||
|
|
afdc188e17 | ||
|
|
5de7d0a73f | ||
|
|
8f73d80a42 | ||
|
|
5a85f245b0 | ||
|
|
ff600c49f6 | ||
|
|
4c7e99fdd7 | ||
|
|
a6037a9737 | ||
|
|
1d55c092b1 | ||
|
|
8bdfd7ea28 | ||
|
|
39ad75fbd3 | ||
|
|
7ef775209d | ||
|
|
a8e0f36562 | ||
|
|
be50fc9c5c | ||
|
|
375f236e32 | ||
|
|
c9eb0240d8 | ||
|
|
b67d591d0a | ||
|
|
7275a87fba | ||
|
|
c103222b65 | ||
|
|
5133d09ec2 | ||
|
|
d88cec34df | ||
|
|
eadd4709ee | ||
|
|
04cbdc6b11 | ||
|
|
233496e184 | ||
|
|
919f8ef2a5 | ||
|
|
8f8d056f45 | ||
|
|
8fcd7974e4 | ||
|
|
cb7fc418a5 | ||
|
|
e6d4a898a8 | ||
|
|
be4fc896d7 | ||
|
|
e5a5ef6f96 | ||
|
|
530662bf5d | ||
|
|
2e7514d3ca | ||
|
|
1db61a5d4e | ||
|
|
618499e777 | ||
|
|
71c0306587 | ||
|
|
a3cd8f05fb | ||
|
|
d8ca0ca5ef | ||
|
|
da08e1cc5b | ||
|
|
8e4c5d0b90 | ||
|
|
467c93f710 | ||
|
|
22ced8bdb2 | ||
|
|
2ee26a6958 | ||
|
|
c63e892544 | ||
|
|
cce3088f73 | ||
|
|
2211896768 | ||
|
|
5bb3cd814c | ||
|
|
266d816a21 | ||
|
|
90c64c029a | ||
|
|
d06e2da72e | ||
|
|
7714d506c4 | ||
|
|
0b7a198235 | ||
|
|
68362ba3e3 | ||
|
|
a9633e652f | ||
|
|
6592561bca | ||
|
|
09e0353053 | ||
|
|
c1e95fd1e9 | ||
|
|
89857e0c1b | ||
|
|
7aa37ee330 | ||
|
|
4c16888184 | ||
|
|
a61716d40d | ||
|
|
3f02cb628b | ||
|
|
22805924fb | ||
|
|
e00229e5ff | ||
|
|
939a235a87 | ||
|
|
cd49162dfc | ||
|
|
b7f2df5d1c | ||
|
|
8b24bc55b9 | ||
|
|
9f2b80718e | ||
|
|
38cb406687 | ||
|
|
c25fe63a26 | ||
|
|
b84d6bb9fc | ||
|
|
b325cde5c0 | ||
|
|
34266da44e | ||
|
|
d82d6b7b47 | ||
|
|
b1c7a068cf | ||
|
|
c155160cf7 | ||
|
|
2ae4559958 | ||
|
|
782283bba6 | ||
|
|
1b50708786 | ||
|
|
206d3755a0 | ||
|
|
6470effbda | ||
|
|
f94b1ef44a | ||
|
|
7c78eab431 | ||
|
|
814b7b0058 | ||
|
|
624f67ecca | ||
|
|
c1a9497b77 | ||
|
|
1ad2ed2d26 | ||
|
|
48f278997f | ||
|
|
9b9d6222e1 | ||
|
|
2e8a878365 | ||
|
|
bc59dc67e6 | ||
|
|
df6022b4a8 | ||
|
|
f2c67d087b | ||
|
|
7a0298a419 | ||
|
|
b40e798090 | ||
|
|
d48d5d6c95 | ||
|
|
69f3347a4d | ||
|
|
85ae7dc5ea | ||
|
|
531cd6d03d | ||
|
|
9fd8c4597a | ||
|
|
7060a3d887 | ||
|
|
c12ffa583c | ||
|
|
05aabb8c61 | ||
|
|
6b461ead7c | ||
|
|
7c54e372e5 | ||
|
|
5be95a7af6 | ||
|
|
08515541b4 | ||
|
|
3948924f61 | ||
|
|
ce1944881a | ||
|
|
f6122c0758 | ||
|
|
3d3f4c2cc6 | ||
|
|
e8e5b92a57 | ||
|
|
4ad704398d | ||
|
|
26289b7787 | ||
|
|
7ad8c1706e | ||
|
|
424a596613 | ||
|
|
6d5b9f417c | ||
|
|
7278e24dce | ||
|
|
76839ae18a | ||
|
|
c8a6aa3210 | ||
|
|
9b4bbcedc5 | ||
|
|
3b793c107c | ||
|
|
a076e6d079 | ||
|
|
fb2a7c51f9 | ||
|
|
1e2c2b5170 | ||
|
|
56833b654e | ||
|
|
f50e02e6e7 | ||
|
|
52cc24813f | ||
|
|
c8a0d4c3f7 | ||
|
|
7560f21f11 | ||
|
|
8a5f80fe47 | ||
|
|
29eb1f08b1 | ||
|
|
9981de2271 | ||
|
|
207acb3f3d | ||
|
|
873a76f48f | ||
|
|
4ca700092a | ||
|
|
ad9f8574bd | ||
|
|
75faae6d7c | ||
|
|
c88c146a5e | ||
|
|
c65cda1468 | ||
|
|
d8059341fe | ||
|
|
ad86bee5f9 | ||
|
|
f44cc44a1b | ||
|
|
ad93588796 | ||
|
|
b5f6a5d24a | ||
|
|
fa4be253f7 | ||
|
|
2db0220c98 | ||
|
|
05fd678ce2 | ||
|
|
165b28bab1 | ||
|
|
dc67cdc262 | ||
|
|
c97394a0ed | ||
|
|
6b9297d30e | ||
|
|
ace7f26b53 | ||
|
|
976f72f79a | ||
|
|
530e7fd43f | ||
|
|
b76fed7dcf | ||
|
|
60a26dd015 | ||
|
|
7bdfc257c1 | ||
|
|
e3395b47cf | ||
|
|
4c9b4de12f | ||
|
|
1cfe5e2777 | ||
|
|
0cae26a1ee | ||
|
|
ed4d556384 | ||
|
|
6d9d7362a4 | ||
|
|
5a40733f20 | ||
|
|
45ee884529 | ||
|
|
7de3f7f05d | ||
|
|
97bb4db1eb | ||
|
|
fc6dfe9894 | ||
|
|
607660124a | ||
|
|
c50b3add95 | ||
|
|
2f7f0e9e20 | ||
|
|
6875ffd113 | ||
|
|
44844bfa7f | ||
|
|
21ab066761 | ||
|
|
83bdf289fa | ||
|
|
d7aa0fc91a | ||
|
|
a6be5190ec | ||
|
|
03cd06e835 | ||
|
|
2d7fe5a73b | ||
|
|
dd5604c8b7 | ||
|
|
7bf2672981 | ||
|
|
6e616c63f0 | ||
|
|
3ded481cce | ||
|
|
1baff582d8 | ||
|
|
957868cc27 | ||
|
|
efb98ac577 | ||
|
|
ff228daae7 | ||
|
|
c93e693517 | ||
|
|
55863c1c58 | ||
|
|
8a2d6c91ba | ||
|
|
86a939f206 | ||
|
|
2b86f724b1 | ||
|
|
68fbc45b7c | ||
|
|
4629ef28ff | ||
|
|
e3a11b0ede | ||
|
|
3df9ee4f3a | ||
|
|
e64d558c27 | ||
|
|
5a2aa383fe | ||
|
|
fe9f458465 | ||
|
|
8e2e9aec67 | ||
|
|
ad48e8e2e1 | ||
|
|
6f8b1000ba | ||
|
|
f5549992c2 | ||
|
|
19771ea6dc | ||
|
|
5965737384 | ||
|
|
b3cb40fd8c | ||
|
|
0ab5141369 | ||
|
|
a23284da4c | ||
|
|
1c8cc13436 | ||
|
|
e6a439faea | ||
|
|
f8b1b1f6c3 | ||
|
|
3844e07258 | ||
|
|
1fab9b3d97 | ||
|
|
1bfacbf2e0 | ||
|
|
c01f9f3830 | ||
|
|
28f9d365fd | ||
|
|
1085fa16aa | ||
|
|
e351960229 | ||
|
|
03caa354da | ||
|
|
34754ea0b6 | ||
|
|
86e1f07f08 | ||
|
|
2add7547d6 | ||
|
|
ecb89e9f26 | ||
|
|
7a9b38dc66 | ||
|
|
979b18092d | ||
|
|
ba43a647dd | ||
|
|
3be14590c6 | ||
|
|
857f8c4313 | ||
|
|
c9c39d2d4a | ||
|
|
845084f77f | ||
|
|
becba4919e | ||
|
|
b797055df9 | ||
|
|
8101c852c8 | ||
|
|
9bbeb9fc9a | ||
|
|
19a9bd3f5c | ||
|
|
399bddc635 | ||
|
|
5a67a1a51c | ||
|
|
9a168d2ba0 | ||
|
|
e42a620aed | ||
|
|
eb902594b8 | ||
|
|
817fd97496 | ||
|
|
96aab47003 | ||
|
|
432666b1ab | ||
|
|
662583c73b | ||
|
|
f73c21e6e6 | ||
|
|
17e72f1db1 | ||
|
|
55d4a36efb | ||
|
|
d3794ba904 | ||
|
|
832f18e2ee | ||
|
|
cd2e282624 | ||
|
|
3db88f1d3a | ||
|
|
f291bb0907 | ||
|
|
69c32b2593 | ||
|
|
1a2939ab01 | ||
|
|
8bf4a2a9ce | ||
|
|
efd389f98a | ||
|
|
f1125cc042 | ||
|
|
f1a0e200dc | ||
|
|
70c025f0f5 | ||
|
|
b9f0a3923d | ||
|
|
5c942c9ef7 | ||
|
|
4294fe82c5 | ||
|
|
4e7fb90544 | ||
|
|
f050e500e9 | ||
|
|
6bc10babba | ||
|
|
4d36f9952a | ||
|
|
b03f4bb1d6 | ||
|
|
4aaedf0f77 | ||
|
|
c1ea851705 | ||
|
|
47103249ef | ||
|
|
2642f18204 | ||
|
|
e2e22eabf5 | ||
|
|
325ecc8029 | ||
|
|
dc6f4560d5 | ||
|
|
3970797d5b | ||
|
|
73776ed0f9 | ||
|
|
aca842f8c2 | ||
|
|
612fb77861 | ||
|
|
6ba695ceaf | ||
|
|
070f77d3b8 | ||
|
|
15de63212f | ||
|
|
2202540763 | ||
|
|
c674b9b3f9 | ||
|
|
43d30e7bfa | ||
|
|
cd0633d519 | ||
|
|
b8bff2a159 | ||
|
|
638f890228 | ||
|
|
4460f7201c | ||
|
|
d0bbfafedc | ||
|
|
16dc1c2e20 | ||
|
|
73ed5f5285 | ||
|
|
50cb92a95b | ||
|
|
3888fd2bca | ||
|
|
4194d8cca6 | ||
|
|
ac84a0ca0a | ||
|
|
e6faa59f32 | ||
|
|
65fab4de1b | ||
|
|
b044f01b1d | ||
|
|
f4ed68fc44 | ||
|
|
4de98923b7 | ||
|
|
1e0e3a89cf | ||
|
|
edabd9a104 | ||
|
|
c11893566a | ||
|
|
c88bef959a | ||
|
|
ffd1b02bf9 | ||
|
|
075ad76d19 | ||
|
|
fbbd0496de | ||
|
|
00e8b7acbb | ||
|
|
3d40e0856e | ||
|
|
32d7897818 | ||
|
|
fa14f50e2d | ||
|
|
4eff5d48cd | ||
|
|
5380c4fb3f | ||
|
|
e10a82ff35 | ||
|
|
f85535672e | ||
|
|
3c1c0620d1 | ||
|
|
9531230fc0 | ||
|
|
151175fb3f | ||
|
|
9faf02a1a6 | ||
|
|
c2e95f607b | ||
|
|
3a50300bf9 | ||
|
|
188a108d87 | ||
|
|
11e788cb8f | ||
|
|
74195bc88b | ||
|
|
2a622aa1d6 | ||
|
|
d15b75e8fd | ||
|
|
4ae5f64da3 | ||
|
|
16ab366136 | ||
|
|
85abd7bca4 | ||
|
|
cd66ff927d | ||
|
|
d242a87191 | ||
|
|
e1eaba0d68 | ||
|
|
5a7c161ed2 | ||
|
|
a0a4e65d5e | ||
|
|
a6751424a5 | ||
|
|
8abaff86e5 | ||
|
|
e7442bf750 | ||
|
|
dce068fcb1 | ||
|
|
249d89af88 | ||
|
|
0f3a689b04 | ||
|
|
569eea08cb | ||
|
|
3cb80bbd85 | ||
|
|
e0d590724b | ||
|
|
2cfab00e8f | ||
|
|
06170ba38f | ||
|
|
7e28df210d | ||
|
|
a69e8c89f8 | ||
|
|
f5544c66bd | ||
|
|
7339f45193 | ||
|
|
739025935a | ||
|
|
eaa0e2d70c | ||
|
|
2bffdaef3a | ||
|
|
79809b56e9 | ||
|
|
e5896c8513 | ||
|
|
104041eea5 | ||
|
|
0b576ed446 | ||
|
|
c1174fc273 | ||
|
|
90e1cd022b | ||
|
|
001e5e887b | ||
|
|
8230dbf257 | ||
|
|
49fa7bdec8 | ||
|
|
bd13b70a88 | ||
|
|
9b5b43c2bf | ||
|
|
dee4733ad0 | ||
|
|
c07c1dfde3 | ||
|
|
181da5836b | ||
|
|
7a787db45d | ||
|
|
49c98697e1 | ||
|
|
7dedbb927c | ||
|
|
c2cc81fccf | ||
|
|
92b2a4fb37 | ||
|
|
b7d02ff669 | ||
|
|
b4179b4c60 | ||
|
|
d7e08a1fff | ||
|
|
d52fdc115c | ||
|
|
e86ae12801 | ||
|
|
87b2fd965c | ||
|
|
8397847e66 | ||
|
|
84b0575ab4 | ||
|
|
f19e5e48f3 | ||
|
|
073f17b61f | ||
|
|
4cdfe6097d | ||
|
|
d55a4e3da7 | ||
|
|
dd6c38c57e | ||
|
|
f705ba3a2e | ||
|
|
8721aa3a49 | ||
|
|
d2507e4706 | ||
|
|
0937efc6e6 | ||
|
|
57445cd48f | ||
|
|
a193655353 | ||
|
|
10b150f13c | ||
|
|
c9fdd1785a | ||
|
|
a821d49eed | ||
|
|
28122bbfae | ||
|
|
3b4b085499 | ||
|
|
51b3388cdc | ||
|
|
908d9341db | ||
|
|
82a2ab158f | ||
|
|
ce893f32e0 | ||
|
|
65696dee09 | ||
|
|
625b39d901 | ||
|
|
4c20e639d8 | ||
|
|
f28dfeb9b4 | ||
|
|
a75c89c451 | ||
|
|
883816c540 | ||
|
|
24ba825365 | ||
|
|
accb02963c | ||
|
|
abe5595fa7 |
No files matched your search
+3
-3
@@ -5,13 +5,13 @@ VITE_HISTORY_HASH = false
|
|||||||
VITE_HISTORY_BASE_URL = "/"
|
VITE_HISTORY_BASE_URL = "/"
|
||||||
|
|
||||||
# 应用名称
|
# 应用名称
|
||||||
VITE_APP_NAME = "Core Network EMS"
|
VITE_APP_NAME = "Core Network OMC"
|
||||||
|
|
||||||
# 应用标识
|
# 应用标识
|
||||||
VITE_APP_CODE = "CN EMS"
|
VITE_APP_CODE = "OMC"
|
||||||
|
|
||||||
# 应用版本
|
# 应用版本
|
||||||
VITE_APP_VERSION = "2.2402.5"
|
VITE_APP_VERSION = "2.241123-fix"
|
||||||
|
|
||||||
# 接口基础URL地址-不带/后缀
|
# 接口基础URL地址-不带/后缀
|
||||||
VITE_API_BASE_URL = "/omc-api"
|
VITE_API_BASE_URL = "/omc-api"
|
||||||
+3
-3
@@ -5,13 +5,13 @@ VITE_HISTORY_HASH = true
|
|||||||
VITE_HISTORY_BASE_URL = "/"
|
VITE_HISTORY_BASE_URL = "/"
|
||||||
|
|
||||||
# 应用名称
|
# 应用名称
|
||||||
VITE_APP_NAME = "Core Network EMS"
|
VITE_APP_NAME = "Core Network OMC"
|
||||||
|
|
||||||
# 应用标识
|
# 应用标识
|
||||||
VITE_APP_CODE = "CN EMS"
|
VITE_APP_CODE = "OMC"
|
||||||
|
|
||||||
# 应用版本
|
# 应用版本
|
||||||
VITE_APP_VERSION = "2.240205.5"
|
VITE_APP_VERSION = "2.241123-fix"
|
||||||
|
|
||||||
# 接口基础URL地址-不带/后缀
|
# 接口基础URL地址-不带/后缀
|
||||||
VITE_API_BASE_URL = "/omc-api"
|
VITE_API_BASE_URL = "/omc-api"
|
||||||
+54
-7
@@ -1,5 +1,52 @@
|
|||||||
# 版本发布日志
|
# 版本发布日志
|
||||||
|
|
||||||
|
## 2.2404.1-20240402
|
||||||
|
|
||||||
|
- 新增 网元安装流程相关页面与操作相关接口联调
|
||||||
|
- 新增 终端远程连接组件添加消息监听和发送句柄
|
||||||
|
- 优化 移除vue-codemirror库改为codemirror,编辑输入支持 yaml语法
|
||||||
|
- 优化 终端-基站信息Radio Name列宽调整
|
||||||
|
- 更新 依赖版本
|
||||||
|
|
||||||
|
## 2.2403.1-20240319
|
||||||
|
|
||||||
|
- 新增 ws 工具连接状态函数
|
||||||
|
- 新增 终端主机配置页面
|
||||||
|
- 新增 终端主机命令页面
|
||||||
|
- 新增 网元信息关联主机页面,表格支持勾选删除记录并展开看网元资源 sn
|
||||||
|
- 新增 数据级缓存工具,支持表格字段排序状态缓存记录,表格字段排序支持缓存变更状态
|
||||||
|
- 新增 网元主机操作翻译提示,网元主机操作 Hooks 包
|
||||||
|
- 新增 IMS-CDR 和 AMF-UE 页面,以及翻译
|
||||||
|
- 新增 网元快速安装页面,网元安装步骤页面模块
|
||||||
|
- 新增 MML 网元操作 UPF 支持区分 telnet 发送端
|
||||||
|
- 新增 网元快速安装步骤选择安装包界面
|
||||||
|
- 新增 网元版本接口/网元软件包接口
|
||||||
|
- 新增 自定义指标界面
|
||||||
|
- 修复 帮助文档中英标题翻译文件名称指定《5G 核心网网管操作手册.pdf》
|
||||||
|
- 修复 ws 函数发送消息判断是否连接正常
|
||||||
|
- 修复 网元管理表单主键 id 重置失效导致新增操作为更新操作
|
||||||
|
- 修复 网元选择列表不进行可操作使用网元状态过滤
|
||||||
|
- 修复 移除获取网元列表,取消 status 状态过滤
|
||||||
|
- 修复 缓存信息页面标题翻译,图表 Echart 语言数据设置
|
||||||
|
- 修复 性能门限提交失败异常
|
||||||
|
- 修复 提交异常并去除对 smdata 的校验
|
||||||
|
- 优化 全局缓存网元列表,接口改换查询网元列表全部无分页
|
||||||
|
- 优化 网元默认可选常量值,删除无效变量声明
|
||||||
|
- 优化 UE-PCF 页面提示信息、网元信息页面样式、网元主机表单校验和提示翻译
|
||||||
|
- 优化 拖动框地脚 null 类型异常,不支持 footer 插槽
|
||||||
|
- 优化 切片文件上传文件名去除非法字符和空格,开发服务端口改为 33020
|
||||||
|
- 更新 包依赖版本
|
||||||
|
|
||||||
|
## 2.2402.6-20240222
|
||||||
|
|
||||||
|
- 修复 UDM 用户数据超时时间 180s
|
||||||
|
- 修复 UDM 鉴权 ki 和 opc 显示禁用输入
|
||||||
|
- 新增 MML 命令输入框 shift+回车进行换行,回车直接发送
|
||||||
|
- 修复 文档查看下载文件名称?显示问题
|
||||||
|
- 修复 看板栏目添加跳转事件
|
||||||
|
- 修复 看板用户行为字典数据翻译
|
||||||
|
- 优化 看板用户行为 CDR 短信号码显示区间样式
|
||||||
|
|
||||||
## 2.2402.5-20240205
|
## 2.2402.5-20240205
|
||||||
|
|
||||||
- 修复 拓扑架构图调整缺失的节点非网元元素,看板拓扑非网元点击排除
|
- 修复 拓扑架构图调整缺失的节点非网元元素,看板拓扑非网元点击排除
|
||||||
@@ -17,14 +64,14 @@
|
|||||||
- 优化 配置参数编辑页面记录类型多语言是否系统内置
|
- 优化 配置参数编辑页面记录类型多语言是否系统内置
|
||||||
- 优化 看板用户行为小屏幕换行显示字内容,格子高度百分比充满
|
- 优化 看板用户行为小屏幕换行显示字内容,格子高度百分比充满
|
||||||
- 优化 看板推送用户行为插入又弹出减少渲染数量
|
- 优化 看板推送用户行为插入又弹出减少渲染数量
|
||||||
- 修复 看班告警统计判断修复,超时30秒,图渲染逻辑优化
|
- 修复 看班告警统计判断修复,超时 30 秒,图渲染逻辑优化
|
||||||
- 修复 参数配置DNN List参数显示null问题
|
- 修复 参数配置 DNN List 参数显示 null 问题
|
||||||
- 优化 ws消息队列延迟处理
|
- 优化 ws 消息队列延迟处理
|
||||||
- 新增 看板用户事件CDR短信类型,短信结果200显示成功
|
- 新增 看板用户事件 CDR 短信类型,短信结果 200 显示成功
|
||||||
- 优化 看板告警统计0数字不显示
|
- 优化 看板告警统计 0 数字不显示
|
||||||
- 优化 看板用户事件CDR指定类型MOC/MTSM
|
- 优化 看板用户事件 CDR 指定类型 MOC/MTSM
|
||||||
- 新增 黄金指标项图表显示全部开关控制
|
- 新增 黄金指标项图表显示全部开关控制
|
||||||
- 新增 黄金指标项随机颜色,图表实时5s动态显示
|
- 新增 黄金指标项随机颜色,图表实时 5s 动态显示
|
||||||
|
|
||||||
## 2.2401.4-20240130
|
## 2.2401.4-20240130
|
||||||
|
|
||||||
|
|||||||
@@ -2,23 +2,9 @@
|
|||||||
|
|
||||||
## 简介
|
## 简介
|
||||||
|
|
||||||
- 系统布局使用 [@ant-design-vue/pro-layout](https://github.com/vueComponent/pro-components)
|
|
||||||
- 图标来源 [@ant-design/icons-vue](https://ant.design/components/icon)
|
- 图标来源 [@ant-design/icons-vue](https://ant.design/components/icon)
|
||||||
- 菜单图标使用自定义 iconfont `font_8d5l8fzk5b87iudi.js`图标文件
|
- 菜单图标使用自定义 iconfont `font_8d5l8fzk5b87iudi.js`图标文件
|
||||||
|
|
||||||
## 测试环境
|
|
||||||
|
|
||||||
```text
|
|
||||||
Jenkins: http://192.168.2.166:3185/
|
|
||||||
Nginx: http://192.168.2.166:3188/#/index
|
|
||||||
后端暴露端口: http://192.168.2.166:3186 \ http://192.168.2.166:3187
|
|
||||||
|
|
||||||
|
|
||||||
新网管:192.168.5.13
|
|
||||||
旧网管:192.168.5.14
|
|
||||||
登录账户:manager/manager
|
|
||||||
```
|
|
||||||
|
|
||||||
## 程序命令
|
## 程序命令
|
||||||
|
|
||||||
项目目录下 `.env.[环境]` 文件对应环境的一些配置,启动前请检查文件内是否配置正确。
|
项目目录下 `.env.[环境]` 文件对应环境的一些配置,启动前请检查文件内是否配置正确。
|
||||||
@@ -60,23 +46,5 @@ export NODE_OPTIONS=--max-old-space-size=50000
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
https://192.168.5.23/
|
https://192.168.5.23/
|
||||||
admin
|
admin / admin
|
||||||
admin
|
|
||||||
```
|
|
||||||
|
|
||||||
## k8s
|
|
||||||
|
|
||||||
master 192.168.5.27 agtuser/admin123
|
|
||||||
|
|
||||||
https://192.168.5.27:31325/#/workloads?namespace=default
|
|
||||||
|
|
||||||
```text
|
|
||||||
eyJhbGciOiJSUzI1NiIsImtpZCI6ImZFVUhIb1puLW04M1dfSUYyRU8zWlZueXBpNUh4T0hTRVlzU19jNlVGQ0kifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLW44ZzRtIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI2M2NmYjAyNS01ZmQ0LTQ0ZTgtOTdiNC0yYWRiYWIxNzc5M2MiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.R3GRygFOjngTj-mEMBAHDeBxm3lpsXZYvC6cdTxByONtLrcMXDebwNVeKtAZ1V9qh2OrjD8n9CIygjULGPdfV6S520vjMh7Oa2q68nOyW49DNWQyYD8xLo-dQ6sX07fI7X_I3H35YUWW80jJAXjJawqIGXBSMG5intlo4tLTUSXmjCfhoQvFsgeRWu0j76pDvhMAvLPcgEXfTCi9tyL3yqJBIKONcKwmMlJeaKSR3pQk3KiibqrBO0MZclRozpke6J0ulfzTemwDDyCqBZmLsRPZ2yDd5hVBIJ9bHEcK0a25NmSFFzmd8XWQPZwg3Y4IbbY-8UhByGq0p9xS-7pGCQ
|
|
||||||
```
|
|
||||||
|
|
||||||
```ssh
|
|
||||||
# https://blog.csdn.net/m0_54706625/article/details/129721121
|
|
||||||
sudo chmod 700 -/.ssh/
|
|
||||||
sudo chmod 700 /home/mask/.ssh #这个尤其容易忽视掉,我就是从这个坑里爬出来。有木有很高兴呀!
|
|
||||||
sudo chmod 600 ~/.ssh/authorized_keys
|
|
||||||
```
|
```
|
||||||
+32
-24
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ems_frontend_vue3",
|
"name": "ems_frontend_vue3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "核心网管理平台",
|
"description": "Core Network EMS",
|
||||||
"author": "TsMask",
|
"author": "TsMask",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
@@ -14,40 +14,48 @@
|
|||||||
"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.1",
|
"@codemirror/lang-javascript": "^6.2.2",
|
||||||
"@codemirror/merge": "^6.5.0",
|
"@codemirror/lang-yaml": "^6.1.1",
|
||||||
|
"@codemirror/merge": "^6.7.2",
|
||||||
"@codemirror/theme-one-dark": "^6.1.2",
|
"@codemirror/theme-one-dark": "^6.1.2",
|
||||||
"@tato30/vue-pdf": "^1.9.3",
|
"@tato30/vue-pdf": "^1.11.2",
|
||||||
"@vueuse/core": "^10.7.0",
|
"@vueuse/core": "11.2.0",
|
||||||
"ant-design-vue": "^3.2.20",
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
"antdv-pro-layout": "^3.2.6",
|
"@xterm/xterm": "^5.5.0",
|
||||||
|
"ant-design-vue": "^4.2.5",
|
||||||
|
"antdv-pro-layout": "^4.1.9",
|
||||||
|
"antdv-pro-modal": "^4.0.5",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"dayjs": "^1.11.10",
|
"crypto-js": "^4.2.0",
|
||||||
"echarts": "~5.4.2",
|
"dayjs": "^1.11.11",
|
||||||
|
"echarts": "~5.5.0",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"js-base64": "^3.7.5",
|
"grid-layout-plus": "^1.0.5",
|
||||||
|
"intl-tel-input": "^24.6.0",
|
||||||
|
"js-base64": "^3.7.7",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
|
"localforage": "^1.10.0",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"p-queue": "^8.0.1",
|
"p-queue": "~8.0.1",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "2.2.6",
|
||||||
"vue": "~3.3.13",
|
"vue": "^3.5.12",
|
||||||
"vue-codemirror": "^6.1.1",
|
"vue-i18n": "^10.0.4",
|
||||||
"vue-i18n": "~9.9.0",
|
"vue-router": "^4.4.5",
|
||||||
"vue-router": "^4.2.5",
|
|
||||||
"vue3-smooth-dnd": "^0.0.6",
|
"vue3-smooth-dnd": "^0.0.6",
|
||||||
"xlsx": "^0.18.5"
|
"xlsx": "~0.18.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@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": "^18.0.0",
|
"@types/node": "^22.7.7",
|
||||||
"@types/nprogress": "^0.2.3",
|
"@types/nprogress": "^0.2.3",
|
||||||
"@vitejs/plugin-vue": "^5.0.2",
|
"@vitejs/plugin-vue": "^5.1.4",
|
||||||
"less": "^4.2.0",
|
"less": "^4.2.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.6.3",
|
||||||
"unplugin-vue-components": "^0.26.0",
|
"unplugin-vue-components": "^0.27.4",
|
||||||
"vite": "^5.0.10",
|
"vite": "5.4.10",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "~0.5.1",
|
||||||
"vue-tsc": "^1.8.27"
|
"vue-tsc": "^2.1.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# 实训教学模块
|
||||||
|
|
||||||
|
网元固定一套,ne_id 默认使用`001`
|
||||||
|
|
||||||
|
## 静态资源
|
||||||
|
|
||||||
|
将目录下文件放置到对应目录 替换约18个文件
|
||||||
|
|
||||||
|
- i18n 对应覆盖 src\i18n
|
||||||
|
- views 对应覆盖 src\views
|
||||||
|
|
||||||
|
|
||||||
|
## 涉及文件
|
||||||
|
|
||||||
|
- src\i18n\locales\en-US.ts
|
||||||
|
- src\i18n\locales\zh-CN.ts
|
||||||
|
- src\views\monitor\topologyArchitecture\index.vue
|
||||||
|
- src\views\configManage\configParamTreeTable
|
||||||
|
- src\views\configManage\configParamApply
|
||||||
|
- src\views\ne\neInfo\index.vue
|
||||||
|
- src\plugins\auth-user.ts
|
||||||
|
- src\views\dashboard\amfUE\index.vue
|
||||||
|
- src\views\dashboard\mmeUE\index.vue
|
||||||
|
- src\views\dashboard\imsCDR\index.vue
|
||||||
|
- src\views\dashboard\smfCDR\index.vue
|
||||||
|
- src\views\dashboard\smscCDR\index.vue
|
||||||
|
- src\store\modules\user.ts
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存为示例配置 (仅管理员操作)
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function ptSaveAsDefault(neType: string, neid: string) {
|
||||||
|
return request({
|
||||||
|
url: `/pt/neConfigData/saveAsDefault`,
|
||||||
|
method: 'post',
|
||||||
|
data: { neType, neid },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置为示例配置 (仅学生/教师操作)
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function ptResetAsDefault(neType: string) {
|
||||||
|
return request({
|
||||||
|
url: `/pt/neConfigData/resetAsDefault`,
|
||||||
|
method: 'post',
|
||||||
|
data: { neType },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据比较示例
|
||||||
|
* @param params 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function ptContrastAsDefault(params: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/pt/neConfigData/contrast`,
|
||||||
|
params,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置数据导出Excel
|
||||||
|
* @param student 仅教师 student
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function ptExport(student: string | undefined) {
|
||||||
|
return request({
|
||||||
|
url: `/pt/neConfigData/export`,
|
||||||
|
method: 'get',
|
||||||
|
params: { student },
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置数据导出Excel (仅教师全量)
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function ptExportAll() {
|
||||||
|
return request({
|
||||||
|
url: `/pt/neConfigData/export-all`,
|
||||||
|
method: 'get',
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置信息
|
||||||
|
* @param params 数据 {neType,paramName}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getPtNeConfigData(params: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/pt/neConfigData`,
|
||||||
|
params,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置数据更新
|
||||||
|
* @param data 数据 {neType,paramName:"参数名",paramData:{参数},loc:"层级index仅array"}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function editPtNeConfigData(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/pt/neConfigData`,
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置新增(array)
|
||||||
|
* @param data 数据 {neType,paramName:"参数名",paramData:{参数},loc:"层级index"}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addPtNeConfigData(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/pt/neConfigData`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置删除(array)
|
||||||
|
* @param params 数据 {neType,paramName:"参数名",loc:"层级index"}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delPtNeConfigData(params: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/pt/neConfigData`,
|
||||||
|
method: 'delete',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 班级学生列表 (仅教师操作)
|
||||||
|
* @param params 数据 {userName}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getPtClassStudents(params?: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/pt/neConfigApply/students`,
|
||||||
|
params,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置应用申请列表
|
||||||
|
* @param params 数据 {neType,paramName}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getPtNeConfigApplyList(params: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/pt/neConfigApply/list`,
|
||||||
|
params,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置应用申请提交(仅学生操作)
|
||||||
|
* @param data 数据 { "neType": "MME", "status": "1" }
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function stuPtNeConfigApply(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/pt/neConfigApply`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置应用申请状态变更(仅管理员/教师操作)
|
||||||
|
* @param data 数据 { "applyId": "1", "neType": "MME", "status": "3", "backInfo": "sgw参数错误" }
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function updatePtNeConfigApply(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/pt/neConfigApply`,
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置数据变更日志信息
|
||||||
|
* @param params 数据 {neType,paramName}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getPtNeConfigDataLogList(params: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/pt/neConfigDataLog`,
|
||||||
|
params,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置数据变更日志还原到数据
|
||||||
|
* @param data 数据 { "id": "1", "value": "old" }
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function restorePtNeConfigDataLog(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/pt/neConfigDataLog/restore`,
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入用户模板数据
|
||||||
|
* @param data 表单数据对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function importData(data: FormData) {
|
||||||
|
return request({
|
||||||
|
url: '/pt/system/user/importData',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
dataType: 'form-data',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入用户模板下载
|
||||||
|
* @returns bolb
|
||||||
|
*/
|
||||||
|
export function importTemplate() {
|
||||||
|
return request({
|
||||||
|
url: '/pt/system/user/importTemplate',
|
||||||
|
method: 'get',
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户列表导出
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns bolb
|
||||||
|
*/
|
||||||
|
export function exportUser(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/pt/system/user/export',
|
||||||
|
method: 'post',
|
||||||
|
data: query,
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,66 @@
|
|||||||
|
import { ADMIN_PERMISSION, ADMIN_ROLE_KEY } from '@/constants/admin-constants';
|
||||||
|
import useUserStore from '@/store/modules/user';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否系统管理员
|
||||||
|
* @returns true | false
|
||||||
|
*/
|
||||||
|
export function isSystemAdmin(): boolean {
|
||||||
|
const userPermissions = useUserStore().permissions;
|
||||||
|
if (userPermissions.includes(ADMIN_PERMISSION)) return true;
|
||||||
|
const userRoles = useUserStore().roles;
|
||||||
|
if (userRoles.includes(ADMIN_ROLE_KEY)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只需含有其中权限
|
||||||
|
* @param role 权限字符数组
|
||||||
|
* @returns true | false
|
||||||
|
*/
|
||||||
|
export function hasPermissions(permissions: string[]): boolean {
|
||||||
|
if (!permissions || permissions.length === 0) return false;
|
||||||
|
const userPermissions = useUserStore().permissions;
|
||||||
|
if (!userPermissions || userPermissions.length === 0) return false;
|
||||||
|
if (userPermissions.includes(ADMIN_PERMISSION)) return true;
|
||||||
|
return permissions.some(p => userPermissions.some(up => up === p));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同时匹配其中权限
|
||||||
|
* @param role 权限字符数组
|
||||||
|
* @returns true | false
|
||||||
|
*/
|
||||||
|
export function matchPermissions(permissions: string[]): boolean {
|
||||||
|
if (!permissions || permissions.length === 0) return false;
|
||||||
|
const userPermissions = useUserStore().permissions;
|
||||||
|
if (!userPermissions || userPermissions.length === 0) return false;
|
||||||
|
if (userPermissions.includes(ADMIN_PERMISSION)) return true;
|
||||||
|
return permissions.every(p => userPermissions.some(up => up === p));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只需含有其中角色
|
||||||
|
* @param role 角色字符数组
|
||||||
|
* @returns true | false
|
||||||
|
*/
|
||||||
|
export function hasRoles(roles: string[]): boolean {
|
||||||
|
if (!roles || roles.length === 0) return false;
|
||||||
|
const userRoles = useUserStore().roles;
|
||||||
|
if (!userRoles || userRoles.length === 0) return false;
|
||||||
|
if (userRoles.includes(ADMIN_ROLE_KEY)) return true;
|
||||||
|
return roles.some(r => userRoles.some(ur => ur === r));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同时匹配其中角色
|
||||||
|
* @param role 角色字符数组
|
||||||
|
* @returns true | false
|
||||||
|
*/
|
||||||
|
export function matchRoles(roles: string[]): boolean {
|
||||||
|
if (!roles || roles.length === 0) return false;
|
||||||
|
const userRoles = useUserStore().roles;
|
||||||
|
if (!userRoles || userRoles.length === 0) return false;
|
||||||
|
if (userRoles.includes(ADMIN_ROLE_KEY)) return true;
|
||||||
|
return roles.every(r => userRoles.some(ur => ur === r));
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
import defaultAvatar from '@/assets/images/default_avatar.png';
|
||||||
|
import useLayoutStore from './layout';
|
||||||
|
import { login, logout, getInfo } from '@/api/login';
|
||||||
|
import { setToken, removeToken } from '@/plugins/auth-token';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { TOKEN_RESPONSE_FIELD } from '@/constants/token-constants';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { parseUrlPath } from '@/plugins/file-static-url';
|
||||||
|
|
||||||
|
/**用户信息类型 */
|
||||||
|
type UserInfo = {
|
||||||
|
/**用户ID */
|
||||||
|
userId: string;
|
||||||
|
/**登录账号 */
|
||||||
|
userName: string;
|
||||||
|
/**用户角色 字符串数组 */
|
||||||
|
roles: string[];
|
||||||
|
/**用户权限 字符串数组 */
|
||||||
|
permissions: string[];
|
||||||
|
/**用户头像 */
|
||||||
|
avatar: string;
|
||||||
|
/**用户昵称 */
|
||||||
|
nickName: string;
|
||||||
|
/**用户手机号 */
|
||||||
|
phonenumber: string;
|
||||||
|
/**用户邮箱 */
|
||||||
|
email: string;
|
||||||
|
/**用户性别 */
|
||||||
|
sex: string | undefined;
|
||||||
|
/**其他信息 */
|
||||||
|
profile: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useUserStore = defineStore('user', {
|
||||||
|
state: (): UserInfo => ({
|
||||||
|
userId: '',
|
||||||
|
userName: '',
|
||||||
|
roles: [],
|
||||||
|
permissions: [],
|
||||||
|
avatar: '',
|
||||||
|
nickName: '',
|
||||||
|
phonenumber: '',
|
||||||
|
email: '',
|
||||||
|
sex: undefined,
|
||||||
|
profile: {},
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
/**
|
||||||
|
* 获取正确头像地址
|
||||||
|
* @param state 内部属性不用传入
|
||||||
|
* @returns 头像地址url
|
||||||
|
*/
|
||||||
|
getAvatar(state) {
|
||||||
|
if (!state.avatar) {
|
||||||
|
return defaultAvatar;
|
||||||
|
}
|
||||||
|
return parseUrlPath(state.avatar);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 获取基础信息属性
|
||||||
|
* @param state 内部属性不用传入
|
||||||
|
* @returns 基础信息
|
||||||
|
*/
|
||||||
|
getBaseInfo(state) {
|
||||||
|
return {
|
||||||
|
nickName: state.nickName,
|
||||||
|
phonenumber: state.phonenumber,
|
||||||
|
email: state.email,
|
||||||
|
sex: state.sex,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
/**
|
||||||
|
* 更新基础信息属性
|
||||||
|
* @param data 变更信息
|
||||||
|
*/
|
||||||
|
setBaseInfo(data: Record<string, any>) {
|
||||||
|
this.nickName = data.nickName;
|
||||||
|
this.phonenumber = data.phonenumber;
|
||||||
|
this.email = data.email;
|
||||||
|
this.sex = data.sex;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 更新头像
|
||||||
|
* @param avatar 上传后的地址
|
||||||
|
*/
|
||||||
|
setAvatar(avatar: string) {
|
||||||
|
this.avatar = avatar;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 获取正确头像地址
|
||||||
|
* @param avatar
|
||||||
|
*/
|
||||||
|
fnAvatar(avatar: string) {
|
||||||
|
if (!avatar) {
|
||||||
|
return defaultAvatar;
|
||||||
|
}
|
||||||
|
return parseUrlPath(avatar);
|
||||||
|
},
|
||||||
|
// 登录
|
||||||
|
async fnLogin(loginBody: Record<string, string>) {
|
||||||
|
const res = await login(loginBody);
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
||||||
|
const token = res.data[TOKEN_RESPONSE_FIELD];
|
||||||
|
setToken(token);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
// 获取用户信息
|
||||||
|
async fnGetInfo() {
|
||||||
|
const res = await getInfo();
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
||||||
|
const { user, roles, permissions } = res.data;
|
||||||
|
this.userId = user.userId;
|
||||||
|
// 登录账号
|
||||||
|
this.userName = user.userName;
|
||||||
|
// 用户头像
|
||||||
|
this.avatar = user.avatar;
|
||||||
|
// 基础信息
|
||||||
|
this.nickName = user.nickName;
|
||||||
|
this.phonenumber = user.phonenumber;
|
||||||
|
this.email = user.email;
|
||||||
|
this.sex = user.sex;
|
||||||
|
|
||||||
|
// 验证返回的roles是否是一个非空数组
|
||||||
|
if (Array.isArray(roles) && roles.length > 0) {
|
||||||
|
this.roles = roles;
|
||||||
|
this.permissions = permissions;
|
||||||
|
} else {
|
||||||
|
this.roles = ['ROLE_DEFAULT'];
|
||||||
|
this.permissions = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 水印文字信息=用户昵称 手机号
|
||||||
|
let waterMarkContent = this.userName;
|
||||||
|
if (this.phonenumber) {
|
||||||
|
waterMarkContent = `${this.userName} ${this.phonenumber}`;
|
||||||
|
}
|
||||||
|
// useLayoutStore().changeWaterMark(waterMarkContent);
|
||||||
|
useLayoutStore().changeWaterMark('');
|
||||||
|
// 学生布局用不一样的
|
||||||
|
if (this.roles.includes('student')) {
|
||||||
|
useLayoutStore().changeConf('layout', 'side');
|
||||||
|
useLayoutStore().changeConf('menuTheme', 'dark');
|
||||||
|
useLayoutStore().changeConf('tabRender', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 网络错误时退出登录状态
|
||||||
|
if (res.code === 0) {
|
||||||
|
removeToken();
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
// 退出系统
|
||||||
|
async fnLogOut() {
|
||||||
|
try {
|
||||||
|
await logout();
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.roles = [];
|
||||||
|
this.permissions = [];
|
||||||
|
removeToken();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default useUserStore;
|
||||||
@@ -0,0 +1,714 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, toRaw, computed } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { ProModal } from 'antdv-pro-modal';
|
||||||
|
import { Form, message, Modal } from 'ant-design-vue/es';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import useDictStore from '@/store/modules/dict';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import {
|
||||||
|
getPtNeConfigApplyList,
|
||||||
|
stuPtNeConfigApply,
|
||||||
|
updatePtNeConfigApply,
|
||||||
|
} from '@/api/pt/neConfigApply';
|
||||||
|
import { hasRoles } from '@/plugins/auth-user';
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
const neInfoStore = useNeInfoStore();
|
||||||
|
|
||||||
|
/**字典数据 */
|
||||||
|
let dict: {
|
||||||
|
/**配置申请应用状态 */
|
||||||
|
ptConfigApplyStatus: DictType[];
|
||||||
|
} = reactive({
|
||||||
|
ptConfigApplyStatus: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元类型 */
|
||||||
|
neType: '',
|
||||||
|
/**申请人 */
|
||||||
|
createBy: '',
|
||||||
|
/**状态 */
|
||||||
|
status: undefined,
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
queryParams = Object.assign(queryParams, {
|
||||||
|
neType: '',
|
||||||
|
createBy: '',
|
||||||
|
status: undefined,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
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: 'center',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.ne.common.neType'),
|
||||||
|
dataIndex: 'neType',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '申请人',
|
||||||
|
dataIndex: 'createBy',
|
||||||
|
align: 'left',
|
||||||
|
width: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '申请时间',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
if (+opt.value <= 0) return '';
|
||||||
|
return parseDateToStr(+opt.value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
key: 'status',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '处理人',
|
||||||
|
dataIndex: 'updateBy',
|
||||||
|
align: 'left',
|
||||||
|
width: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '处理时间',
|
||||||
|
dataIndex: 'updateTime',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
if (+opt.value <= 0) return '';
|
||||||
|
return parseDateToStr(+opt.value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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: 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)[], infos: any) {
|
||||||
|
const arr = [];
|
||||||
|
for (const item of infos) {
|
||||||
|
if (item.status === '0') {
|
||||||
|
arr.push(item.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableState.selectedRowKeys = arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
getPtNeConfigApplyList(toRaw(queryParams)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
// 取消勾选
|
||||||
|
if (tableState.selectedRowKeys.length > 0) {
|
||||||
|
tableState.selectedRowKeys = [];
|
||||||
|
}
|
||||||
|
tablePagination.total = res.total;
|
||||||
|
tableState.data = res.rows;
|
||||||
|
if (
|
||||||
|
tablePagination.total <=
|
||||||
|
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||||
|
queryParams.pageNum !== 1
|
||||||
|
) {
|
||||||
|
tableState.loading = false;
|
||||||
|
fnGetList(queryParams.pageNum - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**详情框是否显示 */
|
||||||
|
openByView: boolean;
|
||||||
|
/**新增框或修改框是否显示 */
|
||||||
|
openByEdit: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**表单数据 */
|
||||||
|
from: Record<string, any>;
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
/**cron生成框是否显示 */
|
||||||
|
openByCron: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
openByView: false,
|
||||||
|
openByEdit: false,
|
||||||
|
title: '任务',
|
||||||
|
from: {
|
||||||
|
id: undefined,
|
||||||
|
createBy: '',
|
||||||
|
createTime: 0,
|
||||||
|
updateBy: '',
|
||||||
|
updateTime: 0,
|
||||||
|
neType: 'MME',
|
||||||
|
status: '0',
|
||||||
|
backInfo: '',
|
||||||
|
},
|
||||||
|
confirmLoading: false,
|
||||||
|
openByCron: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**对话框内表单属性和校验规则 */
|
||||||
|
const modalStateFrom = Form.useForm(
|
||||||
|
modalState.from,
|
||||||
|
reactive({
|
||||||
|
status: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: t('common.selectPlease'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出显示为 查看
|
||||||
|
* @param jobId 任务id
|
||||||
|
*/
|
||||||
|
function fnModalVisibleByVive(row: Record<string, any>) {
|
||||||
|
modalState.from = Object.assign(modalState.from, row);
|
||||||
|
modalState.title = '查看';
|
||||||
|
modalState.openByView = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出显示为 编辑
|
||||||
|
* @param jobId 任务id
|
||||||
|
*/
|
||||||
|
function fnModalVisibleByEdit(row: Record<string, any>) {
|
||||||
|
Object.assign(modalState.from, row);
|
||||||
|
modalState.from.status = '3';
|
||||||
|
modalState.title = '编辑状态';
|
||||||
|
modalState.openByEdit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出确认执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalOk() {
|
||||||
|
modalStateFrom
|
||||||
|
.validate()
|
||||||
|
.then(() => {
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const from = toRaw(modalState.from);
|
||||||
|
updatePtNeConfigApply({
|
||||||
|
applyId: from.id,
|
||||||
|
neType: from.neType,
|
||||||
|
status: from.status,
|
||||||
|
backInfo: from.backInfo,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
modalState.openByEdit = false;
|
||||||
|
modalStateFrom.resetFields();
|
||||||
|
fnGetList();
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出关闭执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalCancel() {
|
||||||
|
modalState.openByEdit = false;
|
||||||
|
modalState.openByView = false;
|
||||||
|
modalState.from = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**批量退回 */
|
||||||
|
function fnRecordBack(row?: Record<string, any>) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: row
|
||||||
|
? '确认要撤回配置应用申请吗?'
|
||||||
|
: '确认要批量退回学生的配置应用申请吗?',
|
||||||
|
onOk() {
|
||||||
|
let result: any;
|
||||||
|
if (row) {
|
||||||
|
result = stuPtNeConfigApply({ neType: row.neType, status: '1' });
|
||||||
|
} else {
|
||||||
|
result = updatePtNeConfigApply({
|
||||||
|
status: '3',
|
||||||
|
backId: tableState.selectedRowKeys.join(','),
|
||||||
|
backInfo: '请重新检查配置',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
result.then((res: any) => {
|
||||||
|
fnGetList();
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**应用状态 */
|
||||||
|
const applyStatus = computed(() => {
|
||||||
|
if (hasRoles(['student'])) {
|
||||||
|
return dict.ptConfigApplyStatus.filter(s => ['0', '1'].includes(s.value));
|
||||||
|
}
|
||||||
|
let data = dict.ptConfigApplyStatus;
|
||||||
|
if (modalState.openByEdit && modalState.from.id) {
|
||||||
|
data = dict.ptConfigApplyStatus.filter(s => ['2', '3'].includes(s.value));
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始字典数据
|
||||||
|
Promise.allSettled([getDict('pt_config_apply_status')]).then(resArr => {
|
||||||
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
dict.ptConfigApplyStatus = resArr[0].value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 获取网元列表
|
||||||
|
neInfoStore.fnNelist().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="t('views.configManage.license.neType')"
|
||||||
|
name="neType "
|
||||||
|
>
|
||||||
|
<a-auto-complete
|
||||||
|
v-model:value="queryParams.neType"
|
||||||
|
:options="neInfoStore.getNeSelectOtions"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('views.configManage.license.neTypePlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item label="状态" name="status">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.status"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
:options="applyStatus"
|
||||||
|
>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="fnQueryReset">
|
||||||
|
<template #icon><ClearOutlined /></template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<div class="button-container">
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
danger
|
||||||
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
|
@click.prevent="fnRecordBack()"
|
||||||
|
v-roles:has="['admin', 'teacher']"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
批量退回
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<div class="button-container">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.searchBarText') }}</template>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="tableState.seached"
|
||||||
|
:checked-children="t('common.switch.show')"
|
||||||
|
:un-checked-children="t('common.switch.hide')"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip placement="topRight">
|
||||||
|
<template #title>{{ t('common.sizeText') }}</template>
|
||||||
|
<a-dropdown placement="bottomRight" trigger="click">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="id"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:scroll="{ x: tableColumns.length * 120 }"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:row-selection="{
|
||||||
|
type: 'checkbox',
|
||||||
|
selectedRowKeys: tableState.selectedRowKeys,
|
||||||
|
onChange: fnTableSelectedRowKeys,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'status'">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.ptConfigApplyStatus"
|
||||||
|
:value="record.status"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'id'">
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.viewText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnModalVisibleByVive(record)"
|
||||||
|
>
|
||||||
|
<template #icon><ProfileOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip v-if="record.status === '0' && hasRoles(['student'])">
|
||||||
|
<template #title>撤回</template>
|
||||||
|
<a-button type="link" @click.prevent="fnRecordBack(record)">
|
||||||
|
<template #icon><RollbackOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip
|
||||||
|
v-if="record.status === '0' && hasRoles(['admin', 'teacher'])"
|
||||||
|
>
|
||||||
|
<template #title>{{ t('common.editText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnModalVisibleByEdit(record)"
|
||||||
|
>
|
||||||
|
<template #icon><FormOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 详情框 -->
|
||||||
|
<ProModal
|
||||||
|
:drag="true"
|
||||||
|
:width="800"
|
||||||
|
:open="modalState.openByView"
|
||||||
|
:title="modalState.title"
|
||||||
|
@cancel="fnModalCancel"
|
||||||
|
>
|
||||||
|
<a-form layout="horizontal" :label-col="{ span: 6 }" :label-wrap="true">
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.ne.common.neType')" name="neType">
|
||||||
|
{{ modalState.from.neType }}
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item label="状态" name="status">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.ptConfigApplyStatus"
|
||||||
|
:value="modalState.from.status"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item label="申请人" name="createBy">
|
||||||
|
{{ modalState.from.createBy }}
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item label="申请时间" name="createTime">
|
||||||
|
{{ parseDateToStr(+modalState.from.createTime) }}
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row v-if="modalState.from.status !== '0'">
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item label="处理人" name="updateBy">
|
||||||
|
{{ modalState.from.updateBy }}
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item label="处理时间" name="updateTime">
|
||||||
|
{{ parseDateToStr(+modalState.from.updateTime) }}
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
v-if="modalState.from.status === '3'"
|
||||||
|
label="退回说明"
|
||||||
|
name="backInfo"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
{{ modalState.from.backInfo }}
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<template #footer>
|
||||||
|
<a-button key="cancel" @click="fnModalCancel">
|
||||||
|
{{ t('common.close') }}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</ProModal>
|
||||||
|
|
||||||
|
<!-- 新增框或修改框 -->
|
||||||
|
<ProModal
|
||||||
|
:drag="true"
|
||||||
|
:width="800"
|
||||||
|
:open="modalState.openByEdit"
|
||||||
|
:title="modalState.title"
|
||||||
|
:confirm-loading="modalState.confirmLoading"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
@ok="fnModalOk"
|
||||||
|
@cancel="fnModalCancel"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
name="modalStateFrom"
|
||||||
|
layout="horizontal"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.ne.common.neType')" name="neType">
|
||||||
|
{{ modalState.from.neType }}
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item label="申请人" name="createBy">
|
||||||
|
{{ modalState.from.createBy }}
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item label="申请时间" name="createTime">
|
||||||
|
{{ parseDateToStr(+modalState.from.createTime) }}
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
label="状态"
|
||||||
|
name="status"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
v-bind="modalStateFrom.validateInfos.status"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="modalState.from.status"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
:options="applyStatus"
|
||||||
|
>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
v-if="modalState.from.status === '3'"
|
||||||
|
label="退回说明"
|
||||||
|
name="backInfo"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-textarea
|
||||||
|
v-model:value="modalState.from.backInfo"
|
||||||
|
:auto-size="{ minRows: 2, maxRows: 6 }"
|
||||||
|
:maxlength="400"
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</ProModal>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+327
@@ -0,0 +1,327 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, toRaw, watch } from 'vue';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import CodemirrorEditeDiff from '@/components/CodemirrorEditeDiff/index.vue';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import {
|
||||||
|
getPtNeConfigDataLogList,
|
||||||
|
restorePtNeConfigDataLog,
|
||||||
|
} from '@/api/pt/neConfigDataLog';
|
||||||
|
import useDictStore from '@/store/modules/dict';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const emit = defineEmits(['ok', 'cancel', 'update:open']);
|
||||||
|
const props = defineProps({
|
||||||
|
open: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
/**网元类型 */
|
||||||
|
neType: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
/**参数名 */
|
||||||
|
paramName: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
/**学生用户账号 */
|
||||||
|
student: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
|
||||||
|
/**字典数据 */
|
||||||
|
let dict: {
|
||||||
|
/**业务类型 */
|
||||||
|
sysBusinessType: DictType[];
|
||||||
|
} = reactive({
|
||||||
|
sysBusinessType: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type StateType = {
|
||||||
|
/**新增框或修改框是否显示 */
|
||||||
|
openByList: boolean;
|
||||||
|
/**差异比较框是否显示 */
|
||||||
|
openByDiff: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**加载状态 */
|
||||||
|
loading: boolean;
|
||||||
|
/**数据 */
|
||||||
|
data: Record<string, any>[];
|
||||||
|
/**差异数据 */
|
||||||
|
dataDiff: Record<string, any>;
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let state: StateType = reactive({
|
||||||
|
openByList: false,
|
||||||
|
openByDiff: false,
|
||||||
|
title: '操作参数名称-学生账号',
|
||||||
|
loading: false,
|
||||||
|
data: [],
|
||||||
|
dataDiff: {},
|
||||||
|
confirmLoading: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
function onClose() {
|
||||||
|
state.loading = false;
|
||||||
|
state.openByList = false;
|
||||||
|
state.openByDiff = false;
|
||||||
|
state.data = [];
|
||||||
|
state.dataDiff = {};
|
||||||
|
emit('cancel');
|
||||||
|
emit('update:open', false);
|
||||||
|
queryParams = {
|
||||||
|
neType: '',
|
||||||
|
paramName: '',
|
||||||
|
student: '',
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元类型 */
|
||||||
|
neType: '',
|
||||||
|
/**可用属性值 */
|
||||||
|
paramName: '',
|
||||||
|
/**学生账号 */
|
||||||
|
student: '',
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (state.loading) return;
|
||||||
|
state.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
if (pageNum === 1) state.data = [];
|
||||||
|
}
|
||||||
|
getPtNeConfigDataLogList(toRaw(queryParams)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
|
// tablePagination.total = res.total;
|
||||||
|
state.data = state.data.concat(res.rows);
|
||||||
|
// 去首个做标题
|
||||||
|
if (queryParams.pageNum === 1 && state.data.length > 0) {
|
||||||
|
const item = state.data[0];
|
||||||
|
state.title = `${item.paramDisplay} - ${item.createBy}`;
|
||||||
|
}
|
||||||
|
if (state.data.length <= res.total && res.rows.length > 0) {
|
||||||
|
queryParams.pageNum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**差异比较框打开 */
|
||||||
|
function fnMergeCellOpen(row: Record<string, any>) {
|
||||||
|
state.dataDiff = row;
|
||||||
|
state.dataDiff.paramJsonOld = JSON.stringify(
|
||||||
|
JSON.parse(state.dataDiff.paramJsonOld),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
state.dataDiff.paramJsonNew = JSON.stringify(
|
||||||
|
JSON.parse(state.dataDiff.paramJsonNew),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
state.openByDiff = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**差异比较框关闭 */
|
||||||
|
function fnMergeCellClose() {
|
||||||
|
state.openByDiff = false;
|
||||||
|
state.dataDiff = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**差异比较还原 */
|
||||||
|
function fnMergeCellRestore(value: 'old' | 'new') {
|
||||||
|
if (state.confirmLoading) return;
|
||||||
|
const id = state.dataDiff.id;
|
||||||
|
restorePtNeConfigDataLog({ id, value })
|
||||||
|
.then((res: any) => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnMergeCellClose();
|
||||||
|
fnGetList(1);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
state.confirmLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**监听是否显示,初始数据 */
|
||||||
|
watch(
|
||||||
|
() => props.open,
|
||||||
|
val => {
|
||||||
|
if (val) {
|
||||||
|
if (props.neType && props.paramName) {
|
||||||
|
state.title = '';
|
||||||
|
state.openByList = true;
|
||||||
|
// 根据条件查询数据
|
||||||
|
queryParams.neType = props.neType;
|
||||||
|
queryParams.paramName = props.paramName;
|
||||||
|
if (props.student) {
|
||||||
|
queryParams.student = props.student;
|
||||||
|
}
|
||||||
|
fnGetList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始字典数据
|
||||||
|
Promise.allSettled([getDict('sys_oper_type')]).then(resArr => {
|
||||||
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
dict.sysBusinessType = resArr[0].value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-drawer
|
||||||
|
:width="500"
|
||||||
|
:title="state.title"
|
||||||
|
placement="right"
|
||||||
|
:open="state.openByList"
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<a-list
|
||||||
|
class="demo-loadmore-list"
|
||||||
|
item-layout="horizontal"
|
||||||
|
:data-source="state.data"
|
||||||
|
>
|
||||||
|
<template #loadMore>
|
||||||
|
<div
|
||||||
|
:style="{
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: '12px',
|
||||||
|
height: '32px',
|
||||||
|
lineHeight: '32px',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<a-button @click="fnGetList()" :loading="state.loading">
|
||||||
|
{{ t('views.configManage.configParamForm.ptDiffLoad') }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #renderItem="{ item }">
|
||||||
|
<a-list-item>
|
||||||
|
<template #actions>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>
|
||||||
|
{{ t('views.configManage.configParamForm.ptDiffMerge') }}
|
||||||
|
</template>
|
||||||
|
<a-button type="primary" @click.prevent="fnMergeCellOpen(item)">
|
||||||
|
<template #icon><MergeCellsOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<a-list-item-meta>
|
||||||
|
<template #title>
|
||||||
|
<DictTag
|
||||||
|
:options="dict.sysBusinessType"
|
||||||
|
:value="item.operaType"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
{{ parseDateToStr(item.createTime) }}
|
||||||
|
</template>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
</template>
|
||||||
|
</a-list>
|
||||||
|
</a-drawer>
|
||||||
|
|
||||||
|
<a-modal
|
||||||
|
:width="800"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
:mask-closable="false"
|
||||||
|
v-model:open="state.openByDiff"
|
||||||
|
:footer="null"
|
||||||
|
:body-style="{ padding: 0, maxHeight: '650px', 'overflow-y': 'auto' }"
|
||||||
|
@ok="fnMergeCellClose()"
|
||||||
|
@cancel="fnMergeCellClose()"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<DictTag
|
||||||
|
:options="dict.sysBusinessType"
|
||||||
|
:value="state.dataDiff.operaType"
|
||||||
|
/>
|
||||||
|
{{ parseDateToStr(state.dataDiff.createTime) }}
|
||||||
|
</template>
|
||||||
|
<div class="diffBack">
|
||||||
|
<div>
|
||||||
|
<a-button
|
||||||
|
type="text"
|
||||||
|
:loading="state.confirmLoading"
|
||||||
|
@click.prevent="fnMergeCellRestore('old')"
|
||||||
|
>
|
||||||
|
<template #icon><MergeCellsOutlined /></template>
|
||||||
|
{{ t('views.configManage.configParamForm.ptDiffRest') }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a-button
|
||||||
|
type="text"
|
||||||
|
:loading="state.confirmLoading"
|
||||||
|
@click.prevent="fnMergeCellRestore('new')"
|
||||||
|
>
|
||||||
|
<template #icon><MergeCellsOutlined /></template>
|
||||||
|
{{ t('views.configManage.configParamForm.ptDiffRest') }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<CodemirrorEditeDiff
|
||||||
|
:old-area="state.dataDiff.paramJsonOld"
|
||||||
|
:new-area="state.dataDiff.paramJsonNew"
|
||||||
|
></CodemirrorEditeDiff>
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.diffBack {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
& > div:first-child {
|
||||||
|
flex: 1;
|
||||||
|
background: #fa9;
|
||||||
|
}
|
||||||
|
& > div:last-child {
|
||||||
|
flex: 1;
|
||||||
|
background: #8f8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,407 @@
|
|||||||
|
import {
|
||||||
|
addPtNeConfigData,
|
||||||
|
delPtNeConfigData,
|
||||||
|
editPtNeConfigData,
|
||||||
|
} from '@/api/pt/neConfig';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { Modal, message } from 'ant-design-vue/es';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { reactive, watch } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数配置array类型
|
||||||
|
* @param param 父级传入 { t, treeState, fnActiveConfigNode, ruleVerification, modalState, fnModalCancel}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function useConfigArray({
|
||||||
|
t,
|
||||||
|
treeState,
|
||||||
|
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 (treeState.neType === '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 (treeState.neType === '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 hide = message.loading(t('common.loading'), 0);
|
||||||
|
editPtNeConfigData({
|
||||||
|
neType: treeState.neType,
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc: loc,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.updateItem', {
|
||||||
|
num: modalState.title,
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.updateItemErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.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() {
|
||||||
|
delPtNeConfigData({
|
||||||
|
neType: treeState.neType,
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
loc: loc,
|
||||||
|
}).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.delItemOk', {
|
||||||
|
num: title,
|
||||||
|
}),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
arrayEditClose();
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表新增单行 */
|
||||||
|
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 (treeState.neType === '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 (treeState.neType === '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 hide = message.loading(t('common.loading'), 0);
|
||||||
|
addPtNeConfigData({
|
||||||
|
neType: treeState.neType,
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc: `${from['index']['value']}`,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.addItemOk', {
|
||||||
|
num: modalState.title,
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.addItemErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.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 (treeState.neType === '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,
|
||||||
|
};
|
||||||
|
}
|
||||||
+348
@@ -0,0 +1,348 @@
|
|||||||
|
import {
|
||||||
|
addPtNeConfigData,
|
||||||
|
delPtNeConfigData,
|
||||||
|
editPtNeConfigData,
|
||||||
|
} from '@/api/pt/neConfig';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { Modal, message } from 'ant-design-vue/es';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { nextTick, reactive } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数配置array类型的嵌套array
|
||||||
|
* @param param 父级传入 { t, treeState, fnActiveConfigNode, ruleVerification, modalState, arrayState, arrayInitEdit, arrayInitAdd, arrayEditClose}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function useConfigArrayChild({
|
||||||
|
t,
|
||||||
|
treeState,
|
||||||
|
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 hide = message.loading(t('common.loading'), 0);
|
||||||
|
editPtNeConfigData({
|
||||||
|
neType: treeState.neType,
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.updateItem', {
|
||||||
|
num: modalState.title,
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.updateItemErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.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() {
|
||||||
|
delPtNeConfigData({
|
||||||
|
neType: treeState.neType,
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
loc,
|
||||||
|
}).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.delItemOk', {
|
||||||
|
num: title,
|
||||||
|
}),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
arrayEditClose();
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表嵌套行新增单行 */
|
||||||
|
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 hide = message.loading(t('common.loading'), 0);
|
||||||
|
addPtNeConfigData({
|
||||||
|
neType: treeState.neType,
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.addItemOk', {
|
||||||
|
num: modalState.title,
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.addItemErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
arrayEditClose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
arrayChildState,
|
||||||
|
arrayChildExpand,
|
||||||
|
arrayChildEdit,
|
||||||
|
arrayChildEditOk,
|
||||||
|
arrayChildDelete,
|
||||||
|
arrayChildAdd,
|
||||||
|
arrayChildAddOk,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
import { editPtNeConfigData } from '@/api/pt/neConfig';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { message } from 'ant-design-vue/es';
|
||||||
|
import { reactive, toRaw } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list类型参数处理
|
||||||
|
* @param param 父级传入 {t, treeState, ruleVerification}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function useConfigList({ t, treeState, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送
|
||||||
|
listState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
editPtNeConfigData({
|
||||||
|
neType: treeState.neType,
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: {
|
||||||
|
[from['name']]: from['value'],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.updateValue', {
|
||||||
|
num: from['display'],
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
// 改变表格数据
|
||||||
|
const item = listState.data.find(
|
||||||
|
(item: Record<string, any>) => from['name'] === item['name']
|
||||||
|
);
|
||||||
|
if (item) {
|
||||||
|
Object.assign(item, listState.editRecord);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.updateValueErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.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,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
import { ptSaveAsDefault, ptResetAsDefault } from '@/api/pt/neConfig';
|
||||||
|
import {
|
||||||
|
getPtClassStudents,
|
||||||
|
stuPtNeConfigApply,
|
||||||
|
updatePtNeConfigApply,
|
||||||
|
} from '@/api/pt/neConfigApply';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { Modal } from 'ant-design-vue/es';
|
||||||
|
import { message } from 'ant-design-vue/lib';
|
||||||
|
import { computed, reactive } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实训教学函数
|
||||||
|
* @param param 父级传入 {t,fnActiveConfigNode}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function usePtOptions({ t, fnActiveConfigNode }: any) {
|
||||||
|
const ptConfigState = reactive({
|
||||||
|
saveLoading: false,
|
||||||
|
restLoading: false,
|
||||||
|
applyLoading: false,
|
||||||
|
});
|
||||||
|
/**(管理员)保存网元下所有配置为示例配置 */
|
||||||
|
function ptConfigSave(neType: string) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.configManage.configParamForm.ptLoadTip'),
|
||||||
|
onOk() {
|
||||||
|
ptConfigState.saveLoading = true;
|
||||||
|
ptSaveAsDefault(neType, '001')
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
ptConfigState.saveLoading = false;
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**重置网元下所有配置 */
|
||||||
|
function ptConfigReset(neType: string) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.configManage.configParamForm.ptResetTip'),
|
||||||
|
onOk() {
|
||||||
|
ptConfigState.restLoading = true;
|
||||||
|
ptResetAsDefault(neType)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
ptConfigState.restLoading = false;
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**配置下方应用(学生)申请撤回和(管理/教师)应用退回 */
|
||||||
|
function ptConfigApply(
|
||||||
|
neType: string,
|
||||||
|
status: '0' | '1' | '2' | '3',
|
||||||
|
student?: string
|
||||||
|
) {
|
||||||
|
let result: any;
|
||||||
|
if (status === '2' || status === '3') {
|
||||||
|
let from: {
|
||||||
|
neType: string;
|
||||||
|
status: string;
|
||||||
|
student?: string;
|
||||||
|
backInfo?: string;
|
||||||
|
} = {
|
||||||
|
neType,
|
||||||
|
status: '2',
|
||||||
|
};
|
||||||
|
if (student) {
|
||||||
|
if (status === '2') {
|
||||||
|
from = { neType, status, student };
|
||||||
|
}
|
||||||
|
if (status === '3') {
|
||||||
|
from = { neType, status, student, backInfo: '请重新检查配置' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = updatePtNeConfigApply(from);
|
||||||
|
}
|
||||||
|
if (status === '0' || status === '1') {
|
||||||
|
result = stuPtNeConfigApply({ neType, status });
|
||||||
|
}
|
||||||
|
if (!result) return;
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.configManage.configParamForm.ptApplyStuTip', {
|
||||||
|
ne: neType,
|
||||||
|
}),
|
||||||
|
onOk() {
|
||||||
|
ptConfigState.applyLoading = true;
|
||||||
|
result
|
||||||
|
.then((res: any) => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
// 教师修改学生时改变状态
|
||||||
|
if (student) {
|
||||||
|
const item = classState.studentOptionsDef.find(
|
||||||
|
s => s.value === classState.student
|
||||||
|
);
|
||||||
|
if (item) {
|
||||||
|
item.applyStatus = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
ptConfigState.applyLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const classState = reactive<{
|
||||||
|
/**学生账号 */
|
||||||
|
student: string | undefined;
|
||||||
|
/**学生可选择列表 */
|
||||||
|
studentOptions: {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
applyId: string;
|
||||||
|
applyStatus: string;
|
||||||
|
}[];
|
||||||
|
studentOptionsDef: {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
applyId: string;
|
||||||
|
applyStatus: string;
|
||||||
|
}[];
|
||||||
|
}>({
|
||||||
|
student: undefined,
|
||||||
|
studentOptions: [],
|
||||||
|
studentOptionsDef: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**学生选择搜索 */
|
||||||
|
function studentChange(v: any) {
|
||||||
|
if (!v) {
|
||||||
|
Object.assign(classState.studentOptions, classState.studentOptionsDef);
|
||||||
|
}
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeout: any;
|
||||||
|
|
||||||
|
/**学生选择搜索 */
|
||||||
|
function studentSearch(neType: string, val: string) {
|
||||||
|
if (timeout) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = null;
|
||||||
|
}
|
||||||
|
if (!val) {
|
||||||
|
Object.assign(classState.studentOptions, classState.studentOptionsDef);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timeout = setTimeout(() => classStudents(neType, val), 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**班级学生列表 */
|
||||||
|
function classStudents(neType: string, val?: string) {
|
||||||
|
getPtClassStudents({ neType, userName: val }).then(res => {
|
||||||
|
classState.studentOptions = [];
|
||||||
|
if (!Array.isArray(res.data) || res.data.length <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const v of res.data) {
|
||||||
|
classState.studentOptions.push({
|
||||||
|
value: v.userName,
|
||||||
|
label: v.userName,
|
||||||
|
applyId: v.applyId,
|
||||||
|
applyStatus: v.applyStatus,
|
||||||
|
});
|
||||||
|
// 设为最新状态
|
||||||
|
const item = classState.studentOptionsDef.find(
|
||||||
|
s => s.value === v.userName
|
||||||
|
);
|
||||||
|
if (item) {
|
||||||
|
item.applyStatus = v.applyStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!val) {
|
||||||
|
Object.assign(classState.studentOptionsDef, classState.studentOptions);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 学生状态
|
||||||
|
const studentStatus = computed(() => {
|
||||||
|
const item = classState.studentOptionsDef.find(
|
||||||
|
s => s.value === classState.student
|
||||||
|
);
|
||||||
|
if (item) return item.applyStatus;
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
ptConfigState,
|
||||||
|
ptConfigSave,
|
||||||
|
ptConfigReset,
|
||||||
|
ptConfigApply,
|
||||||
|
classState,
|
||||||
|
classStudents,
|
||||||
|
studentStatus,
|
||||||
|
studentSearch,
|
||||||
|
studentChange,
|
||||||
|
};
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,442 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
|
import { message } from 'ant-design-vue/es';
|
||||||
|
import { reactive, ref, onMounted, onBeforeUnmount, markRaw } from 'vue';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { TooltipComponent } from 'echarts/components';
|
||||||
|
import { GaugeChart } from 'echarts/charts';
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
|
import * as echarts from 'echarts/core';
|
||||||
|
import { TitleComponent, LegendComponent } from 'echarts/components';
|
||||||
|
import { PieChart } from 'echarts/charts';
|
||||||
|
import { LabelLayout } from 'echarts/features';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import useAppStore from '@/store/modules/app';
|
||||||
|
import useDictStore from '@/store/modules/dict';
|
||||||
|
import { listAllNeInfo } from '@/api/ne/neInfo';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
echarts.use([
|
||||||
|
TooltipComponent,
|
||||||
|
GaugeChart,
|
||||||
|
TitleComponent,
|
||||||
|
LegendComponent,
|
||||||
|
PieChart,
|
||||||
|
CanvasRenderer,
|
||||||
|
LabelLayout,
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**图DOM节点实例对象 */
|
||||||
|
const statusBar = ref<HTMLElement | undefined>(undefined);
|
||||||
|
|
||||||
|
/**图实例对象 */
|
||||||
|
const statusBarChart = ref<any>(null);
|
||||||
|
|
||||||
|
/**网元状态字典数据 */
|
||||||
|
let indexColor = ref<DictType[]>([
|
||||||
|
{ label: 'Normal', value: 'normal', tagType: '', tagClass: '#91cc75' },
|
||||||
|
{
|
||||||
|
label: 'Abnormal',
|
||||||
|
value: 'abnormal',
|
||||||
|
tagType: '',
|
||||||
|
tagClass: '#ee6666',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
//customRender(){} ----单元格处理
|
||||||
|
let tableColumns: ColumnsType = [
|
||||||
|
{
|
||||||
|
title: t('views.index.object'),
|
||||||
|
dataIndex: 'neName',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.index.realNeStatus'),
|
||||||
|
dataIndex: 'serverState',
|
||||||
|
align: 'left',
|
||||||
|
key: 'status',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.index.reloadTime'),
|
||||||
|
dataIndex: 'serverState',
|
||||||
|
align: 'left',
|
||||||
|
customRender(opt) {
|
||||||
|
if (opt.value?.refreshTime) return parseDateToStr(opt.value?.refreshTime);
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.index.version'),
|
||||||
|
dataIndex: 'serverState',
|
||||||
|
align: 'left',
|
||||||
|
customRender(opt) {
|
||||||
|
return opt.value?.version || '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.index.serialNum'),
|
||||||
|
dataIndex: 'serverState',
|
||||||
|
align: 'left',
|
||||||
|
customRender(opt) {
|
||||||
|
return opt.value?.sn || '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.index.expiryDate'),
|
||||||
|
dataIndex: 'serverState',
|
||||||
|
align: 'left',
|
||||||
|
customRender(opt) {
|
||||||
|
return opt.value?.expire || '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.index.ipAddress'),
|
||||||
|
dataIndex: 'serverState',
|
||||||
|
align: 'left',
|
||||||
|
customRender(opt) {
|
||||||
|
return opt.value?.neIP || '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**记录数据 */
|
||||||
|
data: object[];
|
||||||
|
/**勾选记录 */
|
||||||
|
selectedRowKeys: (string | number)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
data: [],
|
||||||
|
selectedRowKeys: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let nfInfo: any = reactive({
|
||||||
|
obj: 'OMC',
|
||||||
|
version: appStore.version,
|
||||||
|
status: t('views.index.normal'),
|
||||||
|
outTimeDate: '',
|
||||||
|
serialNum: appStore.serialNum,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type nfStateType = {
|
||||||
|
/**主机名 */
|
||||||
|
hostName: string;
|
||||||
|
/**操作系统信息 */
|
||||||
|
osInfo: string;
|
||||||
|
/**IP地址 */
|
||||||
|
ipAddress: string;
|
||||||
|
/**版本 */
|
||||||
|
version: string;
|
||||||
|
/**CPU利用率 */
|
||||||
|
cpuUse: string;
|
||||||
|
/**内存使用 */
|
||||||
|
memoryUse: string;
|
||||||
|
/**用户容量 */
|
||||||
|
capability: number;
|
||||||
|
/**序列号 */
|
||||||
|
serialNum: string;
|
||||||
|
/**许可证到期日期 */
|
||||||
|
/* selectedRowKeys: (string | number)[];*/
|
||||||
|
expiryDate: string;
|
||||||
|
};
|
||||||
|
/**网元详细信息 */
|
||||||
|
let pronInfo: nfStateType = reactive({
|
||||||
|
hostName: '5gc',
|
||||||
|
osInfo: 'Linux 5gc 4.15.0-112-generic 2020 x86_64 GNU/Linux',
|
||||||
|
ipAddress: '-',
|
||||||
|
version: '-',
|
||||||
|
cpuUse: '-',
|
||||||
|
memoryUse: '-',
|
||||||
|
capability: 0,
|
||||||
|
serialNum: '-',
|
||||||
|
expiryDate: '-',
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询网元状态列表 */
|
||||||
|
function fnGetList(one: boolean) {
|
||||||
|
if (tableState.loading) return;
|
||||||
|
one && (tableState.loading = true);
|
||||||
|
listAllNeInfo({ bandStatus: true }).then(res => {
|
||||||
|
tableState.data = res.data;
|
||||||
|
tableState.loading = false;
|
||||||
|
var rightNum = 0;
|
||||||
|
var errorNum = 0;
|
||||||
|
res.data.forEach((item: any) => {
|
||||||
|
if (item.serverState.online) {
|
||||||
|
rightNum++;
|
||||||
|
} else {
|
||||||
|
errorNum++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const optionData: any = {
|
||||||
|
title: {
|
||||||
|
text: '',
|
||||||
|
subtext: '',
|
||||||
|
left: 'center',
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
left: 'left',
|
||||||
|
},
|
||||||
|
color: indexColor.value.map(item => item.tagClass),
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: t('views.index.realNeStatus'),
|
||||||
|
type: 'pie',
|
||||||
|
radius: '70%',
|
||||||
|
center: ['50%', '50%'],
|
||||||
|
data: [
|
||||||
|
{ value: rightNum, name: t('views.index.normal') },
|
||||||
|
{ value: errorNum, name: t('views.index.abnormal') },
|
||||||
|
],
|
||||||
|
emphasis: {
|
||||||
|
itemStyle: {
|
||||||
|
shadowBlur: 10,
|
||||||
|
shadowOffsetX: 0,
|
||||||
|
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
label: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
fnDesign(statusBar.value, optionData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fnDesign(container: HTMLElement | undefined, option: any) {
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
if (!statusBarChart.value) {
|
||||||
|
statusBarChart.value = markRaw(echarts.init(container, 'light'));
|
||||||
|
}
|
||||||
|
option && statusBarChart.value.setOption(option);
|
||||||
|
|
||||||
|
// 创建 ResizeObserver 实例
|
||||||
|
var observer = new ResizeObserver(entries => {
|
||||||
|
if (statusBarChart.value) {
|
||||||
|
statusBarChart.value.resize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 监听元素大小变化
|
||||||
|
observer.observe(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**抽屉 网元详细信息 */
|
||||||
|
const open = ref(false);
|
||||||
|
const closeDrawer = () => {
|
||||||
|
open.value = false;
|
||||||
|
};
|
||||||
|
/**抽屉 网元详细信息 */
|
||||||
|
|
||||||
|
/**监听表格行事件*/
|
||||||
|
function rowClick(record: any, index: any) {
|
||||||
|
return {
|
||||||
|
onClick: (event: any) => {
|
||||||
|
let pronData = JSON.parse(JSON.stringify(record.serverState));
|
||||||
|
if (!pronData.online) {
|
||||||
|
message.error(t('views.index.neStatus'), 2);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
const totalMemInKB = pronData.mem?.totalMem;
|
||||||
|
const nfUsedMemInKB = pronData.mem?.nfUsedMem;
|
||||||
|
const sysMemUsageInKB = pronData.mem?.sysMemUsage;
|
||||||
|
|
||||||
|
// 将KB转换为MB
|
||||||
|
const totalMemInMB = Math.round((totalMemInKB / 1024) * 100) / 100;
|
||||||
|
const nfUsedMemInMB = Math.round((nfUsedMemInKB / 1024) * 100) / 100;
|
||||||
|
const sysMemUsageInMB =
|
||||||
|
Math.round((sysMemUsageInKB / 1024) * 100) / 100;
|
||||||
|
|
||||||
|
//渲染详细信息
|
||||||
|
pronInfo = {
|
||||||
|
hostName: pronData.hostname,
|
||||||
|
osInfo: pronData.os,
|
||||||
|
ipAddress: pronData.neIP,
|
||||||
|
version: pronData.version,
|
||||||
|
cpuUse:
|
||||||
|
pronData.neName +
|
||||||
|
':' +
|
||||||
|
pronData.cpu?.nfCpuUsage / 100 +
|
||||||
|
'%; ' +
|
||||||
|
'SYS:' +
|
||||||
|
pronData.cpu?.sysCpuUsage / 100 +
|
||||||
|
'%',
|
||||||
|
memoryUse:
|
||||||
|
'Total:' +
|
||||||
|
totalMemInMB +
|
||||||
|
'MB; ' +
|
||||||
|
pronData.name +
|
||||||
|
':' +
|
||||||
|
nfUsedMemInMB +
|
||||||
|
'MB; SYS:' +
|
||||||
|
sysMemUsageInMB +
|
||||||
|
'MB',
|
||||||
|
capability: pronData.capability,
|
||||||
|
serialNum: pronData.sn,
|
||||||
|
expiryDate: pronData.expire,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
open.value = true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let timer: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国际化翻译转换
|
||||||
|
*/
|
||||||
|
function fnLocale() {
|
||||||
|
let title = route.meta.title as string;
|
||||||
|
if (title.indexOf('router.') !== -1) {
|
||||||
|
title = t(title);
|
||||||
|
}
|
||||||
|
appStore.setTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getDict('index_status')
|
||||||
|
.then(res => {
|
||||||
|
if (res.length > 0) {
|
||||||
|
indexColor.value = res;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
fnLocale();
|
||||||
|
fnGetList(true);
|
||||||
|
timer = setInterval(() => fnGetList(false), 10000); // 每隔10秒执行一次
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 在组件卸载之前清除定时器
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clearInterval(timer);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer :breadcrumb="{}">
|
||||||
|
<div>
|
||||||
|
<a-drawer :open="open" @close="closeDrawer" :width="700">
|
||||||
|
<a-descriptions bordered :column="1" :label-style="{ width: '160px' }">
|
||||||
|
<a-descriptions-item :label="t('views.index.hostName')">{{
|
||||||
|
pronInfo.hostName
|
||||||
|
}}</a-descriptions-item>
|
||||||
|
<a-descriptions-item :label="t('views.index.osInfo')">{{
|
||||||
|
pronInfo.osInfo
|
||||||
|
}}</a-descriptions-item>
|
||||||
|
<a-descriptions-item :label="t('views.index.ipAddress')">{{
|
||||||
|
pronInfo.ipAddress
|
||||||
|
}}</a-descriptions-item>
|
||||||
|
<a-descriptions-item :label="t('views.index.version')">{{
|
||||||
|
pronInfo.version
|
||||||
|
}}</a-descriptions-item>
|
||||||
|
<a-descriptions-item :label="t('views.index.capability')">{{
|
||||||
|
pronInfo.capability
|
||||||
|
}}</a-descriptions-item>
|
||||||
|
<a-descriptions-item :label="t('views.index.cpuUse')">{{
|
||||||
|
pronInfo.cpuUse
|
||||||
|
}}</a-descriptions-item>
|
||||||
|
<a-descriptions-item :label="t('views.index.memoryUse')">{{
|
||||||
|
pronInfo.memoryUse
|
||||||
|
}}</a-descriptions-item>
|
||||||
|
<a-descriptions-item :label="t('views.index.serialNum')">{{
|
||||||
|
pronInfo.serialNum
|
||||||
|
}}</a-descriptions-item>
|
||||||
|
<a-descriptions-item :label="t('views.index.expiryDate')">{{
|
||||||
|
pronInfo.expiryDate
|
||||||
|
}}</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</a-drawer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="14" :md="16" :xs="24">
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="id"
|
||||||
|
size="small"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:pagination="false"
|
||||||
|
:scroll="{ x: true }"
|
||||||
|
:customRow="rowClick"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'status'">
|
||||||
|
<div v-if="record.serverState.online">
|
||||||
|
<a-tag color="blue">{{ t('views.index.normal') }}</a-tag>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<a-tag color="pink">{{ t('views.index.abnormal') }}</a-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="10" :md="8" :xs="24">
|
||||||
|
<a-card
|
||||||
|
:title="t('views.index.runStatus')"
|
||||||
|
style="margin-bottom: 16px"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<div style="width: 100%; min-height: 200px" ref="statusBar"></div>
|
||||||
|
</a-card>
|
||||||
|
<a-card
|
||||||
|
:title="t('views.index.mark')"
|
||||||
|
style="margin-top: 16px"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<a-descriptions
|
||||||
|
bordered
|
||||||
|
:column="1"
|
||||||
|
:label-style="{ width: '160px' }"
|
||||||
|
>
|
||||||
|
<a-descriptions-item :label="t('views.index.object')">{{
|
||||||
|
nfInfo.obj
|
||||||
|
}}</a-descriptions-item>
|
||||||
|
<template v-if="nfInfo.obj === 'OMC'">
|
||||||
|
<a-descriptions-item :label="t('views.index.versionNum')">{{
|
||||||
|
nfInfo.version
|
||||||
|
}}</a-descriptions-item>
|
||||||
|
<a-descriptions-item :label="t('views.index.systemStatus')">{{
|
||||||
|
nfInfo.status
|
||||||
|
}}</a-descriptions-item>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<a-descriptions-item :label="t('views.index.serialNum')">{{
|
||||||
|
nfInfo.serialNum
|
||||||
|
}}</a-descriptions-item>
|
||||||
|
<a-descriptions-item :label="t('views.index.expiryDate')">{{
|
||||||
|
nfInfo.outTimeDate
|
||||||
|
}}</a-descriptions-item>
|
||||||
|
</template>
|
||||||
|
</a-descriptions>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
||||||
@@ -0,0 +1,728 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, toRaw, ref, onBeforeUnmount } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { message, Modal } from 'ant-design-vue/es';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import {
|
||||||
|
RESULT_CODE_ERROR,
|
||||||
|
RESULT_CODE_SUCCESS,
|
||||||
|
} from '@/constants/result-constants';
|
||||||
|
import useDictStore from '@/store/modules/dict';
|
||||||
|
import { listAMFDataUE, delAMFDataUE, exportAMFDataUE } from '@/api/neData/amf';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import saveAs from 'file-saver';
|
||||||
|
import PQueue from 'p-queue';
|
||||||
|
import { hasRoles } from '@/plugins/auth-user';
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
const ws = new WS();
|
||||||
|
const queue = new PQueue({ concurrency: 1, autoStart: true });
|
||||||
|
|
||||||
|
/**字典数据 */
|
||||||
|
let dict: {
|
||||||
|
/**UE 事件认证代码类型 */
|
||||||
|
ueAauthCode: DictType[];
|
||||||
|
/**UE 事件类型 */
|
||||||
|
ueEventType: DictType[];
|
||||||
|
/**UE 事件CM状态 */
|
||||||
|
ueEventCmState: DictType[];
|
||||||
|
} = reactive({
|
||||||
|
ueAauthCode: [],
|
||||||
|
ueEventType: [],
|
||||||
|
ueEventCmState: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**开始结束时间 */
|
||||||
|
let queryRangePicker = ref<[string, string]>(['', '']);
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元类型 */
|
||||||
|
neType: 'AMF',
|
||||||
|
neId: '001',
|
||||||
|
eventType: '',
|
||||||
|
imsi: '',
|
||||||
|
sortField: 'timestamp',
|
||||||
|
sortOrder: 'desc',
|
||||||
|
/**开始时间 */
|
||||||
|
startTime: '',
|
||||||
|
/**结束时间 */
|
||||||
|
endTime: '',
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
eventTypes.value = [];
|
||||||
|
queryParams = Object.assign(queryParams, {
|
||||||
|
eventType: '',
|
||||||
|
imsi: '',
|
||||||
|
startTime: '',
|
||||||
|
endTime: '',
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
queryRangePicker.value = ['', ''];
|
||||||
|
tablePagination.current = 1;
|
||||||
|
tablePagination.pageSize = 20;
|
||||||
|
fnGetList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**记录类型 */
|
||||||
|
const eventTypes = ref<string[]>([]);
|
||||||
|
|
||||||
|
/**查询记录类型变更 */
|
||||||
|
function fnQueryEventTypeChange(value: any) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
queryParams.eventType = value.join(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**搜索栏 */
|
||||||
|
seached: boolean;
|
||||||
|
/**记录数据 */
|
||||||
|
data: object[];
|
||||||
|
/**勾选记录 */
|
||||||
|
selectedRowKeys: (string | number)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
size: 'middle',
|
||||||
|
seached: true,
|
||||||
|
data: [],
|
||||||
|
selectedRowKeys: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
let tableColumns: ColumnsType = [
|
||||||
|
{
|
||||||
|
title: t('common.rowId'),
|
||||||
|
dataIndex: 'id',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'IMSI',
|
||||||
|
dataIndex: 'eventJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const eventJSON = opt.value;
|
||||||
|
return eventJSON.imsi;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.ue.eventType'),
|
||||||
|
dataIndex: 'eventType',
|
||||||
|
key: 'eventType',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.ue.result'),
|
||||||
|
dataIndex: 'eventJSON',
|
||||||
|
key: 'result',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.ue.time'),
|
||||||
|
dataIndex: 'eventJSON',
|
||||||
|
key: 'time',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('common.operate'),
|
||||||
|
key: 'id',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
let tablePagination = reactive({
|
||||||
|
/**当前页数 */
|
||||||
|
current: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
/**默认的每页条数 */
|
||||||
|
defaultPageSize: 20,
|
||||||
|
/**指定每页可以显示多少条 */
|
||||||
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
|
/**只有一页时是否隐藏分页器 */
|
||||||
|
hideOnSinglePage: false,
|
||||||
|
/**是否可以快速跳转至某页 */
|
||||||
|
showQuickJumper: true,
|
||||||
|
/**是否可以改变 pageSize */
|
||||||
|
showSizeChanger: true,
|
||||||
|
/**数据总数 */
|
||||||
|
total: 0,
|
||||||
|
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
|
||||||
|
onChange: (page: number, pageSize: number) => {
|
||||||
|
tablePagination.current = page;
|
||||||
|
tablePagination.pageSize = pageSize;
|
||||||
|
queryParams.pageNum = page;
|
||||||
|
queryParams.pageSize = pageSize;
|
||||||
|
fnGetList();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格紧凑型变更操作 */
|
||||||
|
function fnTableSize({ key }: MenuInfo) {
|
||||||
|
tableState.size = key as SizeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格多选 */
|
||||||
|
function fnTableSelectedRowKeys(keys: (string | number)[]) {
|
||||||
|
tableState.selectedRowKeys = keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
/**最大ID值 */
|
||||||
|
maxId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
confirmLoading: false,
|
||||||
|
maxId: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录删除
|
||||||
|
* @param id 编号
|
||||||
|
*/
|
||||||
|
function fnRecordDelete(id: string) {
|
||||||
|
if (!id || modalState.confirmLoading) return;
|
||||||
|
let msg = id;
|
||||||
|
if (id === '0') {
|
||||||
|
msg = `${id}... ${tableState.selectedRowKeys.length}`;
|
||||||
|
id = tableState.selectedRowKeys.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.dashboard.ue.delTip', { msg }),
|
||||||
|
onOk() {
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
delAMFDataUE(id)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnGetList(1);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
if (!queryRangePicker.value) {
|
||||||
|
queryRangePicker.value = ['', ''];
|
||||||
|
}
|
||||||
|
queryParams.startTime = queryRangePicker.value[0];
|
||||||
|
queryParams.endTime = queryRangePicker.value[1];
|
||||||
|
listAMFDataUE(toRaw(queryParams)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
|
// 取消勾选
|
||||||
|
if (tableState.selectedRowKeys.length > 0) {
|
||||||
|
tableState.selectedRowKeys = [];
|
||||||
|
}
|
||||||
|
tablePagination.total = res.total;
|
||||||
|
// 遍历处理cdr字符串数据
|
||||||
|
tableState.data = res.rows.map(item => {
|
||||||
|
let eventJSON = item.eventJSON;
|
||||||
|
if (!eventJSON) {
|
||||||
|
Reflect.set(item, 'eventJSON', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
eventJSON = JSON.parse(eventJSON);
|
||||||
|
Reflect.set(item, 'eventJSON', eventJSON);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
Reflect.set(item, 'eventJSON', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 取最大值ID用作实时累加
|
||||||
|
if (res.total > 0) {
|
||||||
|
modalState.maxId = Number(res.rows[0].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**列表导出 */
|
||||||
|
function fnExportList() {
|
||||||
|
if (modalState.confirmLoading) return;
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.dashboard.ue.exportTip'),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
const querys = toRaw(queryParams);
|
||||||
|
querys.pageSize = 10000;
|
||||||
|
exportAMFDataUE(querys)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
saveAs(res.data, `amf_ue_event_export_${Date.now()}.xlsx`);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**实时数据开关 */
|
||||||
|
const realTimeData = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实时数据
|
||||||
|
*/
|
||||||
|
function fnRealTime() {
|
||||||
|
realTimeData.value = !realTimeData.value;
|
||||||
|
if (realTimeData.value) {
|
||||||
|
// 建立链接
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: '/ws',
|
||||||
|
params: {
|
||||||
|
/**订阅通道组
|
||||||
|
*
|
||||||
|
* AMF_UE会话事件(GroupID:1010)
|
||||||
|
*/
|
||||||
|
subGroupID: '1010',
|
||||||
|
},
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: wsError,
|
||||||
|
};
|
||||||
|
ws.connect(options);
|
||||||
|
} else {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsError(ev: any) {
|
||||||
|
// 接收数据后回调
|
||||||
|
console.error(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsMessage(res: Record<string, any>) {
|
||||||
|
const { code, requestId, data } = res;
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅组信息
|
||||||
|
if (!data?.groupId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ueEvent AMF_UE会话事件
|
||||||
|
if (data.groupId === '1010') {
|
||||||
|
const ueEvent = data.data;
|
||||||
|
queue.add(async () => {
|
||||||
|
modalState.maxId += 1;
|
||||||
|
tableState.data.unshift({
|
||||||
|
id: modalState.maxId,
|
||||||
|
neType: ueEvent.neType,
|
||||||
|
neName: ueEvent.neName, // 空
|
||||||
|
rmUID: ueEvent.rmUID, // 空
|
||||||
|
timestamp: ueEvent.timestamp,
|
||||||
|
eventType: ueEvent.eventType,
|
||||||
|
eventJSON: ueEvent.eventJSON,
|
||||||
|
});
|
||||||
|
tablePagination.total += 1;
|
||||||
|
if (tableState.data.length > 100) {
|
||||||
|
tableState.data.pop();
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 800));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始字典数据
|
||||||
|
Promise.allSettled([
|
||||||
|
getDict('ue_auth_code'),
|
||||||
|
getDict('ue_event_type'),
|
||||||
|
getDict('ue_event_cm_state'),
|
||||||
|
])
|
||||||
|
.then(resArr => {
|
||||||
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
dict.ueAauthCode = resArr[0].value;
|
||||||
|
}
|
||||||
|
if (resArr[1].status === 'fulfilled') {
|
||||||
|
dict.ueEventType = resArr[1].value;
|
||||||
|
}
|
||||||
|
if (resArr[2].status === 'fulfilled') {
|
||||||
|
dict.ueEventCmState = resArr[2].value;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// 获取列表数据
|
||||||
|
fnGetList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (ws.state() !== -1) {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card
|
||||||
|
v-show="tableState.seached"
|
||||||
|
:bordered="false"
|
||||||
|
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
||||||
|
>
|
||||||
|
<!-- 表格搜索栏 -->
|
||||||
|
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.ue.eventType')"
|
||||||
|
name="eventType "
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="eventTypes"
|
||||||
|
mode="multiple"
|
||||||
|
:options="dict.ueEventType"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnQueryEventTypeChange"
|
||||||
|
></a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="4" :md="12" :xs="24">
|
||||||
|
<a-form-item label="IMSI" name="imsi ">
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.imsi"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.time')"
|
||||||
|
name="queryRangePicker"
|
||||||
|
>
|
||||||
|
<a-range-picker
|
||||||
|
v-model:value="queryRangePicker"
|
||||||
|
allow-clear
|
||||||
|
bordered
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
value-format="x"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-range-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="fnQueryReset">
|
||||||
|
<template #icon><ClearOutlined /></template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-popconfirm
|
||||||
|
placement="bottomLeft"
|
||||||
|
:title="
|
||||||
|
!realTimeData
|
||||||
|
? t('views.dashboard.ue.realTimeDataStart')
|
||||||
|
: t('views.dashboard.ue.realTimeDataStop')
|
||||||
|
"
|
||||||
|
ok-text="Yes"
|
||||||
|
cancel-text="No"
|
||||||
|
@confirm="fnRealTime()"
|
||||||
|
>
|
||||||
|
<a-button type="primary" :danger="realTimeData">
|
||||||
|
<template #icon><FundOutlined /> </template>
|
||||||
|
{{
|
||||||
|
!realTimeData
|
||||||
|
? t('views.dashboard.ue.realTimeDataStart')
|
||||||
|
: t('views.dashboard.ue.realTimeDataStop')
|
||||||
|
}}
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
danger
|
||||||
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
|
:loading="modalState.confirmLoading"
|
||||||
|
@click.prevent="fnRecordDelete('0')"
|
||||||
|
v-if="!hasRoles(['student'])"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
{{ t('common.deleteText') }}
|
||||||
|
</a-button>
|
||||||
|
|
||||||
|
<a-button type="dashed" @click.prevent="fnExportList()">
|
||||||
|
<template #icon><ExportOutlined /></template>
|
||||||
|
{{ t('common.export') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.searchBarText') }}</template>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="tableState.seached"
|
||||||
|
:checked-children="t('common.switch.show')"
|
||||||
|
:un-checked-children="t('common.switch.hide')"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.sizeText') }}</template>
|
||||||
|
<a-dropdown trigger="click" placement="bottomRight">
|
||||||
|
<a-button type="text">
|
||||||
|
<template #icon><ColumnHeightOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu
|
||||||
|
:selected-keys="[tableState.size as string]"
|
||||||
|
@click="fnTableSize"
|
||||||
|
>
|
||||||
|
<a-menu-item key="default">
|
||||||
|
{{ t('common.size.default') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="middle">
|
||||||
|
{{ t('common.size.middle') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="small">
|
||||||
|
{{ t('common.size.small') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="id"
|
||||||
|
:columns="hasRoles(['student']) ? tableColumns.filter((s:any)=>s.key !== 'id'): tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: tableColumns.length * 120, y: 'calc(100vh - 480px)' }"
|
||||||
|
:row-selection="{
|
||||||
|
type: 'checkbox',
|
||||||
|
columnWidth: '48px',
|
||||||
|
selectedRowKeys: tableState.selectedRowKeys,
|
||||||
|
onChange: fnTableSelectedRowKeys,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'eventType'">
|
||||||
|
<DictTag :options="dict.ueEventType" :value="record.eventType" />
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'result'">
|
||||||
|
<span v-if="record.eventType === 'auth-result'">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.ueAauthCode"
|
||||||
|
:value="record.eventJSON.authCode"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span v-if="record.eventType === 'detach'">
|
||||||
|
<span>{{ t('views.dashboard.ue.resultOk') }}</span>
|
||||||
|
</span>
|
||||||
|
<span v-if="record.eventType === 'cm-state'">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.ueEventCmState"
|
||||||
|
:value="record.eventJSON.status"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'time'">
|
||||||
|
<span
|
||||||
|
v-if="record.eventType === 'auth-result'"
|
||||||
|
:title="record.eventJSON.authTime"
|
||||||
|
>
|
||||||
|
{{ record.eventJSON.authTime }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="record.eventType === 'detach'"
|
||||||
|
:title="record.eventJSON.detachTime"
|
||||||
|
>
|
||||||
|
{{ record.eventJSON.detachTime }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="record.eventType === 'cm-state'"
|
||||||
|
:title="record.eventJSON.changeTime"
|
||||||
|
>
|
||||||
|
{{ record.eventJSON.changeTime }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'id'">
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordDelete(record.id)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<DeleteOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template #expandedRowRender="{ record }">
|
||||||
|
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.dashboard.ue.ueInfo') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.neName') }}: </span>
|
||||||
|
<span>{{ record.neName }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.rmUid') }}: </span>
|
||||||
|
<span>{{ record.rmUID }}</span>
|
||||||
|
</div>
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.dashboard.ue.rowInfo') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.ue.time') }}: </span>
|
||||||
|
<span
|
||||||
|
v-if="record.eventType === 'auth-result'"
|
||||||
|
:title="record.eventJSON.authTime"
|
||||||
|
>
|
||||||
|
{{ record.eventJSON.authTime }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="record.eventType === 'detach'"
|
||||||
|
:title="record.eventJSON.detachTime"
|
||||||
|
>
|
||||||
|
{{ record.eventJSON.detachTime }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="record.eventType === 'cm-state'"
|
||||||
|
:title="record.eventJSON.changeTime"
|
||||||
|
>
|
||||||
|
{{ record.eventJSON.changeTime }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.ue.eventType') }}: </span>
|
||||||
|
<DictTag :options="dict.ueEventType" :value="record.eventType" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.ue.result') }}: </span>
|
||||||
|
<span v-if="record.eventType === 'auth-result'">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.ueAauthCode"
|
||||||
|
:value="record.eventJSON.authCode"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span v-if="record.eventType === 'detach'">
|
||||||
|
{{ t('views.dashboard.ue.resultOk') }}
|
||||||
|
</span>
|
||||||
|
<span v-if="record.eventType === 'cm-state'">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.ueEventCmState"
|
||||||
|
:value="record.eventJSON.status"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,839 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, toRaw, ref, onBeforeUnmount } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { message, Modal } from 'ant-design-vue/es';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import {
|
||||||
|
RESULT_CODE_ERROR,
|
||||||
|
RESULT_CODE_SUCCESS,
|
||||||
|
} from '@/constants/result-constants';
|
||||||
|
import useDictStore from '@/store/modules/dict';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import {
|
||||||
|
delIMSDataCDR,
|
||||||
|
exportIMSDataCDR,
|
||||||
|
listIMSDataCDR,
|
||||||
|
} from '@/api/neData/ims';
|
||||||
|
import { parseDateToStr, parseDuration } from '@/utils/date-utils';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import saveAs from 'file-saver';
|
||||||
|
import PQueue from 'p-queue';
|
||||||
|
import { hasRoles } from '@/plugins/auth-user';
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
const ws = new WS();
|
||||||
|
const queue = new PQueue({ concurrency: 1, autoStart: true });
|
||||||
|
|
||||||
|
/**字典数据 */
|
||||||
|
let dict: {
|
||||||
|
/**CDR SIP响应代码类别类型 */
|
||||||
|
cdrSipCode: DictType[];
|
||||||
|
/**CDR 呼叫类型 */
|
||||||
|
cdrCallType: DictType[];
|
||||||
|
} = reactive({
|
||||||
|
cdrSipCode: [],
|
||||||
|
cdrCallType: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**网元可选 */
|
||||||
|
let neOtions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
|
/**开始结束时间 */
|
||||||
|
let queryRangePicker = ref<[string, string]>(['', '']);
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元类型 */
|
||||||
|
neType: 'IMS',
|
||||||
|
neId: '001',
|
||||||
|
recordType: '',
|
||||||
|
callerParty: '',
|
||||||
|
calledParty: '',
|
||||||
|
sortField: 'timestamp',
|
||||||
|
sortOrder: 'desc',
|
||||||
|
/**开始时间 */
|
||||||
|
startTime: '',
|
||||||
|
/**结束时间 */
|
||||||
|
endTime: '',
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
recordTypes.value = [];
|
||||||
|
queryParams = Object.assign(queryParams, {
|
||||||
|
recordType: '',
|
||||||
|
callerParty: '',
|
||||||
|
calledParty: '',
|
||||||
|
startTime: '',
|
||||||
|
endTime: '',
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
queryRangePicker.value = ['', ''];
|
||||||
|
tablePagination.current = 1;
|
||||||
|
tablePagination.pageSize = 20;
|
||||||
|
fnGetList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**记录类型 */
|
||||||
|
const recordTypes = ref<string[]>([]);
|
||||||
|
|
||||||
|
/**查询记录类型变更 */
|
||||||
|
function fnQueryRecordTypeChange(value: any) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
queryParams.recordType = value.join(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**搜索栏 */
|
||||||
|
seached: boolean;
|
||||||
|
/**记录数据 */
|
||||||
|
data: object[];
|
||||||
|
/**勾选记录 */
|
||||||
|
selectedRowKeys: (string | number)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
size: 'middle',
|
||||||
|
seached: true,
|
||||||
|
data: [],
|
||||||
|
selectedRowKeys: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
let tableColumns: ColumnsType = [
|
||||||
|
{
|
||||||
|
title: t('common.rowId'),
|
||||||
|
dataIndex: 'id',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.recordType'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.recordType;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.type'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
key: 'callType',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.caller'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
key: 'callerParty',
|
||||||
|
align: 'left',
|
||||||
|
width: 120,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.callerParty;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.called'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
key: 'calledParty',
|
||||||
|
align: 'left',
|
||||||
|
width: 120,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.calledParty;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.result'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
key: 'cause',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.duration'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
key: 'callDuration',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.callType === 'sms'
|
||||||
|
? '-'
|
||||||
|
: parseDuration(cdrJSON.callDuration);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.seizureTime'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 200,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
if (typeof cdrJSON.seizureTime === 'number') {
|
||||||
|
return parseDateToStr(+cdrJSON.seizureTime * 1000);
|
||||||
|
}
|
||||||
|
return cdrJSON.seizureTime;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.releaseTime'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 200,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
if (typeof cdrJSON.releaseTime === 'number') {
|
||||||
|
return parseDateToStr(+cdrJSON.releaseTime * 1000);
|
||||||
|
}
|
||||||
|
return cdrJSON.releaseTime;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
delIMSDataCDR(id)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnGetList(1);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
if (!queryRangePicker.value) {
|
||||||
|
queryRangePicker.value = ['', ''];
|
||||||
|
}
|
||||||
|
queryParams.startTime = queryRangePicker.value[0];
|
||||||
|
queryParams.endTime = queryRangePicker.value[1];
|
||||||
|
listIMSDataCDR(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;
|
||||||
|
exportIMSDataCDR(querys)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
saveAs(res.data, `ims_cdr_event_export_${Date.now()}.xlsx`);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**实时数据开关 */
|
||||||
|
const realTimeData = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实时数据
|
||||||
|
*/
|
||||||
|
function fnRealTime() {
|
||||||
|
realTimeData.value = !realTimeData.value;
|
||||||
|
if (realTimeData.value) {
|
||||||
|
tableState.seached = false;
|
||||||
|
// 建立链接
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: '/ws',
|
||||||
|
params: {
|
||||||
|
/**订阅通道组
|
||||||
|
*
|
||||||
|
* IMS_CDR会话事件(GroupID:1005)
|
||||||
|
*/
|
||||||
|
subGroupID: `1005_${queryParams.neId}`,
|
||||||
|
},
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: wsError,
|
||||||
|
};
|
||||||
|
ws.connect(options);
|
||||||
|
} else {
|
||||||
|
ws.close();
|
||||||
|
tableState.seached = true;
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsError(ev: any) {
|
||||||
|
// 接收数据后回调
|
||||||
|
console.error(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsMessage(res: Record<string, any>) {
|
||||||
|
const { code, requestId, data } = res;
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅组信息
|
||||||
|
if (!data?.groupId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// cdrEvent CDR会话事件
|
||||||
|
if (data.groupId === `1005_${queryParams.neId}`) {
|
||||||
|
const cdrEvent = data.data;
|
||||||
|
queue.add(async () => {
|
||||||
|
modalState.maxId += 1;
|
||||||
|
tableState.data.unshift({
|
||||||
|
id: modalState.maxId,
|
||||||
|
neType: cdrEvent.neType,
|
||||||
|
neName: cdrEvent.neName,
|
||||||
|
rmUID: cdrEvent.rmUID,
|
||||||
|
timestamp: cdrEvent.timestamp,
|
||||||
|
cdrJSON: cdrEvent.CDR,
|
||||||
|
});
|
||||||
|
tablePagination.total += 1;
|
||||||
|
if (tableState.data.length > 100) {
|
||||||
|
tableState.data.pop();
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 800));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始字典数据
|
||||||
|
Promise.allSettled([getDict('cdr_sip_code'), getDict('cdr_call_type')]).then(
|
||||||
|
resArr => {
|
||||||
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
dict.cdrSipCode = resArr[0].value;
|
||||||
|
}
|
||||||
|
if (resArr[1].status === 'fulfilled') {
|
||||||
|
dict.cdrCallType = resArr[1].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// 获取网元网元列表
|
||||||
|
useNeInfoStore()
|
||||||
|
.fnNelist()
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length > 0) {
|
||||||
|
let arr: Record<string, any>[] = [];
|
||||||
|
res.data.forEach(i => {
|
||||||
|
if (i.neType === 'IMS') {
|
||||||
|
arr.push({ value: i.neId, label: i.neName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neOtions.value = arr;
|
||||||
|
if (arr.length > 0) {
|
||||||
|
queryParams.neId = arr[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// 获取列表数据
|
||||||
|
fnGetList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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="IMS" name="neId ">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.neId"
|
||||||
|
:options="neOtions"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.called')"
|
||||||
|
name="calledParty"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.calledParty"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.caller')"
|
||||||
|
name="callerParty "
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.callerParty"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="4" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="fnQueryReset">
|
||||||
|
<template #icon><ClearOutlined /></template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.recordType')"
|
||||||
|
name="recordType"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="recordTypes"
|
||||||
|
mode="multiple"
|
||||||
|
:options="['MOC', 'MTC'].map(v => ({ value: v }))"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnQueryRecordTypeChange"
|
||||||
|
></a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.time')"
|
||||||
|
name="queryRangePicker"
|
||||||
|
>
|
||||||
|
<a-range-picker
|
||||||
|
v-model:value="queryRangePicker"
|
||||||
|
allow-clear
|
||||||
|
bordered
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
value-format="x"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-range-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-popconfirm
|
||||||
|
placement="bottomLeft"
|
||||||
|
:title="
|
||||||
|
!realTimeData
|
||||||
|
? t('views.dashboard.cdr.realTimeDataStart')
|
||||||
|
: t('views.dashboard.cdr.realTimeDataStop')
|
||||||
|
"
|
||||||
|
ok-text="Yes"
|
||||||
|
cancel-text="No"
|
||||||
|
@confirm="fnRealTime()"
|
||||||
|
>
|
||||||
|
<a-button type="primary" :danger="realTimeData">
|
||||||
|
<template #icon><FundOutlined /> </template>
|
||||||
|
{{
|
||||||
|
!realTimeData
|
||||||
|
? t('views.dashboard.cdr.realTimeDataStart')
|
||||||
|
: t('views.dashboard.cdr.realTimeDataStop')
|
||||||
|
}}
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
danger
|
||||||
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
|
:loading="modalState.confirmLoading"
|
||||||
|
@click.prevent="fnRecordDelete('0')"
|
||||||
|
v-if="!hasRoles(['student'])"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
{{ t('common.deleteText') }}
|
||||||
|
</a-button>
|
||||||
|
|
||||||
|
<a-button type="dashed" @click.prevent="fnExportList()">
|
||||||
|
<template #icon><ExportOutlined /></template>
|
||||||
|
{{ t('common.export') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.searchBarText') }}</template>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="tableState.seached"
|
||||||
|
:checked-children="t('common.switch.show')"
|
||||||
|
:un-checked-children="t('common.switch.hide')"
|
||||||
|
size="small"
|
||||||
|
:disabled="realTimeData"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.sizeText') }}</template>
|
||||||
|
<a-dropdown trigger="click" placement="bottomRight">
|
||||||
|
<a-button type="text">
|
||||||
|
<template #icon><ColumnHeightOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu
|
||||||
|
:selected-keys="[tableState.size as string]"
|
||||||
|
@click="fnTableSize"
|
||||||
|
>
|
||||||
|
<a-menu-item key="default">
|
||||||
|
{{ t('common.size.default') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="middle">
|
||||||
|
{{ t('common.size.middle') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="small">
|
||||||
|
{{ t('common.size.small') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="id"
|
||||||
|
:columns="hasRoles(['student']) ? tableColumns.filter((s:any)=>s.key !== 'id'): tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: tableColumns.length * 150, y: 'calc(100vh - 480px)' }"
|
||||||
|
:row-selection="{
|
||||||
|
type: 'checkbox',
|
||||||
|
columnWidth: '48px',
|
||||||
|
selectedRowKeys: tableState.selectedRowKeys,
|
||||||
|
onChange: fnTableSelectedRowKeys,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'callType'">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.cdrCallType"
|
||||||
|
:value="record.cdrJSON.callType"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'cause'">
|
||||||
|
<span v-if="record.cdrJSON.callType !== 'sms'">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.cdrSipCode"
|
||||||
|
:value="record.cdrJSON.cause"
|
||||||
|
value-default="0"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ t('views.dashboard.cdr.resultOk') }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'id'">
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordDelete(record.id)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<DeleteOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template #expandedRowRender="{ record }">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="5" :md="12" :xs="24">
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.dashboard.cdr.cdrInfo') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.neName') }}: </span>
|
||||||
|
<span>{{ record.neName }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.rmUid') }}: </span>
|
||||||
|
<span>{{ record.rmUID }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.time') }}: </span>
|
||||||
|
<span>
|
||||||
|
{{
|
||||||
|
typeof record.cdrJSON.releaseTime === 'number'
|
||||||
|
? parseDateToStr(+record.cdrJSON.releaseTime * 1000)
|
||||||
|
: record.cdrJSON.releaseTime
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.dashboard.cdr.rowInfo') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.type') }}: </span>
|
||||||
|
<DictTag
|
||||||
|
:options="dict.cdrCallType"
|
||||||
|
:value="record.cdrJSON.callType"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.duration') }}: </span>
|
||||||
|
<span v-if="record.cdrJSON.callType !== 'sms'">
|
||||||
|
{{ parseDuration(record.cdrJSON.callDuration) }}
|
||||||
|
</span>
|
||||||
|
<span v-else> - </span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.caller') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.callerParty }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.called') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.calledParty }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.result') }}: </span>
|
||||||
|
<span v-if="record.cdrJSON.callType !== 'sms'">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.cdrSipCode"
|
||||||
|
:value="record.cdrJSON.cause"
|
||||||
|
value-default="0"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ t('views.dashboard.cdr.resultOk') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.seizureTime') }}: </span>
|
||||||
|
<span>
|
||||||
|
{{
|
||||||
|
typeof record.cdrJSON.seizureTime === 'number'
|
||||||
|
? parseDateToStr(+record.cdrJSON.seizureTime * 1000)
|
||||||
|
: record.cdrJSON.seizureTime
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.releaseTime') }}: </span>
|
||||||
|
<span>
|
||||||
|
{{
|
||||||
|
typeof record.cdrJSON.releaseTime === 'number'
|
||||||
|
? parseDateToStr(+record.cdrJSON.releaseTime * 1000)
|
||||||
|
: record.cdrJSON.releaseTime
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,742 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, toRaw, ref, onBeforeUnmount } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { message, Modal } from 'ant-design-vue/es';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import {
|
||||||
|
RESULT_CODE_ERROR,
|
||||||
|
RESULT_CODE_SUCCESS,
|
||||||
|
} from '@/constants/result-constants';
|
||||||
|
import useDictStore from '@/store/modules/dict';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import { listMMEDataUE, delMMEDataUE, exportMMEDataUE } from '@/api/neData/mme';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import saveAs from 'file-saver';
|
||||||
|
import PQueue from 'p-queue';
|
||||||
|
import { hasRoles } from '@/plugins/auth-user';
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
const ws = new WS();
|
||||||
|
const queue = new PQueue({ concurrency: 1, autoStart: true });
|
||||||
|
|
||||||
|
/**网元可选 */
|
||||||
|
let neOtions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
|
/**字典数据 */
|
||||||
|
let dict: {
|
||||||
|
/**UE 事件认证代码类型 */
|
||||||
|
ueAauthCode: DictType[];
|
||||||
|
/**UE 事件类型 */
|
||||||
|
ueEventType: DictType[];
|
||||||
|
/**UE 事件CM状态 */
|
||||||
|
ueEventCmState: DictType[];
|
||||||
|
} = reactive({
|
||||||
|
ueAauthCode: [],
|
||||||
|
ueEventType: [],
|
||||||
|
ueEventCmState: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**开始结束时间 */
|
||||||
|
let queryRangePicker = ref<[string, string]>(['', '']);
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元类型 */
|
||||||
|
neType: 'MME',
|
||||||
|
neId: '001',
|
||||||
|
eventType: '',
|
||||||
|
imsi: '',
|
||||||
|
sortField: 'timestamp',
|
||||||
|
sortOrder: 'desc',
|
||||||
|
/**开始时间 */
|
||||||
|
startTime: '',
|
||||||
|
/**结束时间 */
|
||||||
|
endTime: '',
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
eventTypes.value = [];
|
||||||
|
queryParams = Object.assign(queryParams, {
|
||||||
|
eventType: '',
|
||||||
|
imsi: '',
|
||||||
|
startTime: '',
|
||||||
|
endTime: '',
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
queryRangePicker.value = ['', ''];
|
||||||
|
tablePagination.current = 1;
|
||||||
|
tablePagination.pageSize = 20;
|
||||||
|
fnGetList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**记录类型 */
|
||||||
|
const eventTypes = ref<string[]>([]);
|
||||||
|
|
||||||
|
/**查询记录类型变更 */
|
||||||
|
function fnQueryEventTypeChange(value: any) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
queryParams.eventType = value.join(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**搜索栏 */
|
||||||
|
seached: boolean;
|
||||||
|
/**记录数据 */
|
||||||
|
data: object[];
|
||||||
|
/**勾选记录 */
|
||||||
|
selectedRowKeys: (string | number)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
size: 'middle',
|
||||||
|
seached: true,
|
||||||
|
data: [],
|
||||||
|
selectedRowKeys: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
let tableColumns: ColumnsType = [
|
||||||
|
{
|
||||||
|
title: t('common.rowId'),
|
||||||
|
dataIndex: 'id',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'IMSI',
|
||||||
|
dataIndex: 'eventJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const eventJSON = opt.value;
|
||||||
|
return eventJSON.imsi;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.ue.eventType'),
|
||||||
|
dataIndex: 'eventType',
|
||||||
|
key: 'eventType',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.ue.result'),
|
||||||
|
dataIndex: 'eventJSON',
|
||||||
|
key: 'result',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.ue.time'),
|
||||||
|
dataIndex: 'eventJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return parseDateToStr(+cdrJSON.timestamp * 1000);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('common.operate'),
|
||||||
|
key: 'id',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
let tablePagination = reactive({
|
||||||
|
/**当前页数 */
|
||||||
|
current: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
/**默认的每页条数 */
|
||||||
|
defaultPageSize: 20,
|
||||||
|
/**指定每页可以显示多少条 */
|
||||||
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
|
/**只有一页时是否隐藏分页器 */
|
||||||
|
hideOnSinglePage: false,
|
||||||
|
/**是否可以快速跳转至某页 */
|
||||||
|
showQuickJumper: true,
|
||||||
|
/**是否可以改变 pageSize */
|
||||||
|
showSizeChanger: true,
|
||||||
|
/**数据总数 */
|
||||||
|
total: 0,
|
||||||
|
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
|
||||||
|
onChange: (page: number, pageSize: number) => {
|
||||||
|
tablePagination.current = page;
|
||||||
|
tablePagination.pageSize = pageSize;
|
||||||
|
queryParams.pageNum = page;
|
||||||
|
queryParams.pageSize = pageSize;
|
||||||
|
fnGetList();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格紧凑型变更操作 */
|
||||||
|
function fnTableSize({ key }: MenuInfo) {
|
||||||
|
tableState.size = key as SizeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格多选 */
|
||||||
|
function fnTableSelectedRowKeys(keys: (string | number)[]) {
|
||||||
|
tableState.selectedRowKeys = keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
/**最大ID值 */
|
||||||
|
maxId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
confirmLoading: false,
|
||||||
|
maxId: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录删除
|
||||||
|
* @param id 编号
|
||||||
|
*/
|
||||||
|
function fnRecordDelete(id: string) {
|
||||||
|
if (!id || modalState.confirmLoading) return;
|
||||||
|
let msg = id;
|
||||||
|
if (id === '0') {
|
||||||
|
msg = `${id}... ${tableState.selectedRowKeys.length}`;
|
||||||
|
id = tableState.selectedRowKeys.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.dashboard.ue.delTip', { msg }),
|
||||||
|
onOk() {
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
delMMEDataUE(id)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnGetList(1);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
if (!queryRangePicker.value) {
|
||||||
|
queryRangePicker.value = ['', ''];
|
||||||
|
}
|
||||||
|
queryParams.startTime = queryRangePicker.value[0];
|
||||||
|
queryParams.endTime = queryRangePicker.value[1];
|
||||||
|
listMMEDataUE(toRaw(queryParams)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
|
// 取消勾选
|
||||||
|
if (tableState.selectedRowKeys.length > 0) {
|
||||||
|
tableState.selectedRowKeys = [];
|
||||||
|
}
|
||||||
|
tablePagination.total = res.total;
|
||||||
|
// 遍历处理cdr字符串数据
|
||||||
|
tableState.data = res.rows.map(item => {
|
||||||
|
let eventJSON = item.eventJSON;
|
||||||
|
if (!eventJSON) {
|
||||||
|
Reflect.set(item, 'eventJSON', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
eventJSON = JSON.parse(eventJSON);
|
||||||
|
Reflect.set(item, 'eventJSON', eventJSON);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
Reflect.set(item, 'eventJSON', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 取最大值ID用作实时累加
|
||||||
|
if (res.total > 0) {
|
||||||
|
modalState.maxId = Number(res.rows[0].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**列表导出 */
|
||||||
|
function fnExportList() {
|
||||||
|
if (modalState.confirmLoading) return;
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.dashboard.ue.exportTip'),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
const querys = toRaw(queryParams);
|
||||||
|
querys.pageSize = 10000;
|
||||||
|
exportMMEDataUE(querys)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
saveAs(res.data, `mme_ue_event_export_${Date.now()}.xlsx`);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**实时数据开关 */
|
||||||
|
const realTimeData = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实时数据
|
||||||
|
*/
|
||||||
|
function fnRealTime() {
|
||||||
|
realTimeData.value = !realTimeData.value;
|
||||||
|
if (realTimeData.value) {
|
||||||
|
tableState.seached = false;
|
||||||
|
// 建立链接
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: '/ws',
|
||||||
|
params: {
|
||||||
|
/**订阅通道组
|
||||||
|
*
|
||||||
|
* MME_UE会话事件(GroupID:1011)
|
||||||
|
*/
|
||||||
|
subGroupID: `1011_${queryParams.neId}`,
|
||||||
|
},
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: wsError,
|
||||||
|
};
|
||||||
|
ws.connect(options);
|
||||||
|
} else {
|
||||||
|
ws.close();
|
||||||
|
tableState.seached = true;
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsError(ev: any) {
|
||||||
|
// 接收数据后回调
|
||||||
|
console.error(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsMessage(res: Record<string, any>) {
|
||||||
|
const { code, requestId, data } = res;
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅组信息
|
||||||
|
if (!data?.groupId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ueEvent MME_UE会话事件
|
||||||
|
if (data.groupId === `1011_${queryParams.neId}`) {
|
||||||
|
const ueEvent = data.data;
|
||||||
|
queue.add(async () => {
|
||||||
|
modalState.maxId += 1;
|
||||||
|
tableState.data.unshift({
|
||||||
|
id: modalState.maxId,
|
||||||
|
neType: ueEvent.neType,
|
||||||
|
neName: ueEvent.neName, // 空
|
||||||
|
rmUID: ueEvent.rmUID, // 空
|
||||||
|
timestamp: ueEvent.timestamp,
|
||||||
|
eventType: ueEvent.eventType,
|
||||||
|
eventJSON: ueEvent.eventJSON,
|
||||||
|
});
|
||||||
|
tablePagination.total += 1;
|
||||||
|
if (tableState.data.length > 100) {
|
||||||
|
tableState.data.pop();
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 800));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始字典数据
|
||||||
|
Promise.allSettled([
|
||||||
|
getDict('ue_auth_code'),
|
||||||
|
getDict('ue_event_type'),
|
||||||
|
getDict('ue_event_cm_state'),
|
||||||
|
]).then(resArr => {
|
||||||
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
dict.ueAauthCode = resArr[0].value;
|
||||||
|
}
|
||||||
|
if (resArr[1].status === 'fulfilled') {
|
||||||
|
const ueEventType: any[] = JSON.parse(JSON.stringify(resArr[1]));
|
||||||
|
dict.ueEventType = ueEventType.map(item => {
|
||||||
|
if (item.value === 'cm-state') {
|
||||||
|
item.label = item.label.replace('CM', 'ECM');
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (resArr[2].status === 'fulfilled') {
|
||||||
|
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 === 'MME') {
|
||||||
|
arr.push({ value: i.neId, label: i.neName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neOtions.value = arr;
|
||||||
|
if (arr.length > 0) {
|
||||||
|
queryParams.neId = arr[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// 获取列表数据
|
||||||
|
fnGetList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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="MME" name="neId ">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.neId"
|
||||||
|
:options="neOtions"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.ue.eventType')"
|
||||||
|
name="eventType "
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="eventTypes"
|
||||||
|
mode="multiple"
|
||||||
|
:options="dict.ueEventType"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnQueryEventTypeChange"
|
||||||
|
></a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="4" :md="12" :xs="24">
|
||||||
|
<a-form-item label="IMSI" name="imsi ">
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.imsi"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.time')"
|
||||||
|
name="queryRangePicker"
|
||||||
|
>
|
||||||
|
<a-range-picker
|
||||||
|
v-model:value="queryRangePicker"
|
||||||
|
allow-clear
|
||||||
|
bordered
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
value-format="x"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-range-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="fnQueryReset">
|
||||||
|
<template #icon><ClearOutlined /></template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-popconfirm
|
||||||
|
placement="bottomLeft"
|
||||||
|
:title="
|
||||||
|
!realTimeData
|
||||||
|
? t('views.dashboard.ue.realTimeDataStart')
|
||||||
|
: t('views.dashboard.ue.realTimeDataStop')
|
||||||
|
"
|
||||||
|
ok-text="Yes"
|
||||||
|
cancel-text="No"
|
||||||
|
@confirm="fnRealTime()"
|
||||||
|
>
|
||||||
|
<a-button type="primary" :danger="realTimeData">
|
||||||
|
<template #icon><FundOutlined /> </template>
|
||||||
|
{{
|
||||||
|
!realTimeData
|
||||||
|
? t('views.dashboard.ue.realTimeDataStart')
|
||||||
|
: t('views.dashboard.ue.realTimeDataStop')
|
||||||
|
}}
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
danger
|
||||||
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
|
:loading="modalState.confirmLoading"
|
||||||
|
@click.prevent="fnRecordDelete('0')"
|
||||||
|
v-if="!hasRoles(['student'])"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
{{ t('common.deleteText') }}
|
||||||
|
</a-button>
|
||||||
|
|
||||||
|
<a-button type="dashed" @click.prevent="fnExportList()">
|
||||||
|
<template #icon><ExportOutlined /></template>
|
||||||
|
{{ t('common.export') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.searchBarText') }}</template>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="tableState.seached"
|
||||||
|
:checked-children="t('common.switch.show')"
|
||||||
|
:un-checked-children="t('common.switch.hide')"
|
||||||
|
size="small"
|
||||||
|
:disabled="realTimeData"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.sizeText') }}</template>
|
||||||
|
<a-dropdown trigger="click" placement="bottomRight">
|
||||||
|
<a-button type="text">
|
||||||
|
<template #icon><ColumnHeightOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu
|
||||||
|
:selected-keys="[tableState.size as string]"
|
||||||
|
@click="fnTableSize"
|
||||||
|
>
|
||||||
|
<a-menu-item key="default">
|
||||||
|
{{ t('common.size.default') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="middle">
|
||||||
|
{{ t('common.size.middle') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="small">
|
||||||
|
{{ t('common.size.small') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="id"
|
||||||
|
:columns="hasRoles(['student']) ? tableColumns.filter((s:any)=>s.key !== 'id'): tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: tableColumns.length * 120, y: 'calc(100vh - 480px)' }"
|
||||||
|
:row-selection="{
|
||||||
|
type: 'checkbox',
|
||||||
|
columnWidth: '48px',
|
||||||
|
selectedRowKeys: tableState.selectedRowKeys,
|
||||||
|
onChange: fnTableSelectedRowKeys,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'eventType'">
|
||||||
|
<DictTag :options="dict.ueEventType" :value="record.eventType" />
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'result'">
|
||||||
|
<span v-if="record.eventType === 'auth-result'">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.ueAauthCode"
|
||||||
|
:value="record.eventJSON.result"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span v-if="record.eventType === 'detach'">
|
||||||
|
<span>{{ t('views.dashboard.ue.resultOk') }}</span>
|
||||||
|
</span>
|
||||||
|
<span v-if="record.eventType === 'cm-state'">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.ueEventCmState"
|
||||||
|
:value="record.eventJSON.result"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'id'">
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordDelete(record.id)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<DeleteOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template #expandedRowRender="{ record }">
|
||||||
|
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.dashboard.ue.ueInfo') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.neName') }}: </span>
|
||||||
|
<span>{{ record.neName }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.rmUid') }}: </span>
|
||||||
|
<span>{{ record.rmUID }}</span>
|
||||||
|
</div>
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.dashboard.ue.rowInfo') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.ue.time') }}: </span>
|
||||||
|
{{ parseDateToStr(record.eventJSON.timestamp * 1000) }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.ue.eventType') }}: </span>
|
||||||
|
<DictTag :options="dict.ueEventType" :value="record.eventType" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.ue.result') }}: </span>
|
||||||
|
<span v-if="record.eventType === 'auth-result'">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.ueAauthCode"
|
||||||
|
:value="record.eventJSON.result"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span v-if="record.eventType === 'detach'">
|
||||||
|
{{ t('views.dashboard.ue.resultOk') }}
|
||||||
|
</span>
|
||||||
|
<span v-if="record.eventType === 'cm-state'">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.ueEventCmState"
|
||||||
|
:value="record.eventJSON.result"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,284 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import * as echarts from 'echarts/core';
|
||||||
|
import {
|
||||||
|
TitleComponent,
|
||||||
|
TitleComponentOption,
|
||||||
|
TooltipComponent,
|
||||||
|
TooltipComponentOption,
|
||||||
|
GridComponent,
|
||||||
|
GridComponentOption,
|
||||||
|
LegendComponent,
|
||||||
|
LegendComponentOption,
|
||||||
|
} from 'echarts/components';
|
||||||
|
import {
|
||||||
|
PieChart,
|
||||||
|
PieSeriesOption,
|
||||||
|
BarChart,
|
||||||
|
BarSeriesOption,
|
||||||
|
} from 'echarts/charts';
|
||||||
|
import { LabelLayout } from 'echarts/features';
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
|
|
||||||
|
import { markRaw, onMounted, ref } from 'vue';
|
||||||
|
import { origGet, top3Sel } from '@/api/faultManage/actAlarm';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
echarts.use([
|
||||||
|
TitleComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
GridComponent,
|
||||||
|
LegendComponent,
|
||||||
|
PieChart,
|
||||||
|
BarChart,
|
||||||
|
CanvasRenderer,
|
||||||
|
LabelLayout,
|
||||||
|
]);
|
||||||
|
|
||||||
|
type EChartsOption = echarts.ComposeOption<
|
||||||
|
| TitleComponentOption
|
||||||
|
| TooltipComponentOption
|
||||||
|
| GridComponentOption
|
||||||
|
| LegendComponentOption
|
||||||
|
| PieSeriesOption
|
||||||
|
| BarSeriesOption
|
||||||
|
>;
|
||||||
|
|
||||||
|
/**图DOM节点实例对象 */
|
||||||
|
const alarmTypeBar = ref<HTMLElement | undefined>(undefined);
|
||||||
|
|
||||||
|
/**图实例对象 */
|
||||||
|
const alarmTypeBarChart = ref<any>(null);
|
||||||
|
|
||||||
|
/**告警类型数据 */
|
||||||
|
const alarmTypeType = ref<any>([
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
name: t('views.index.Critical'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
name: t('views.index.Major'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
name: t('views.index.Minor'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
name: t('views.index.Warning'),
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// value: 0,
|
||||||
|
// name: t('views.index.Event'),
|
||||||
|
// },
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**告警类型Top数据 */
|
||||||
|
const alarmTypeTypeTop = ref<any>([
|
||||||
|
{ name: 'AMF', value: 0 },
|
||||||
|
{ name: 'UDM', value: 0 },
|
||||||
|
{ name: 'SMF', value: 0 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
//
|
||||||
|
function initPicture() {
|
||||||
|
Promise.allSettled([origGet(), top3Sel()])
|
||||||
|
.then(resArr => {
|
||||||
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
const res0 = resArr[0].value;
|
||||||
|
if (res0.code === RESULT_CODE_SUCCESS && Array.isArray(res0.data)) {
|
||||||
|
for (const item of res0.data) {
|
||||||
|
let index = 0;
|
||||||
|
switch (item.name) {
|
||||||
|
case 'Critical':
|
||||||
|
index = 0;
|
||||||
|
break;
|
||||||
|
case 'Major':
|
||||||
|
index = 1;
|
||||||
|
break;
|
||||||
|
case 'Minor':
|
||||||
|
index = 2;
|
||||||
|
break;
|
||||||
|
case 'Warning':
|
||||||
|
index = 3;
|
||||||
|
break;
|
||||||
|
// case 'Event':
|
||||||
|
// index = 4;
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
alarmTypeType.value[index].value = Number(item.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resArr[1].status === 'fulfilled') {
|
||||||
|
const res1 = resArr[1].value;
|
||||||
|
if (res1.code === RESULT_CODE_SUCCESS && Array.isArray(res1.data)) {
|
||||||
|
alarmTypeTypeTop.value = alarmTypeTypeTop.value
|
||||||
|
.concat(res1.data)
|
||||||
|
.sort((a: any, b: any) => {
|
||||||
|
return b.value - a.value;
|
||||||
|
})
|
||||||
|
.slice(0, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
const optionData: EChartsOption = {
|
||||||
|
title: [
|
||||||
|
{
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t('views.dashboard.overview.alarmTypeBar.topTitle'),
|
||||||
|
textStyle: {
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: '14',
|
||||||
|
fontWeight: 400,
|
||||||
|
},
|
||||||
|
top: '50%',
|
||||||
|
left: '0%',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: '{b} : {c}',
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
right: '2%',
|
||||||
|
top: '12%',
|
||||||
|
data: alarmTypeType.value.map((item: any) => item.name), //label数组
|
||||||
|
textStyle: {
|
||||||
|
color: '#A7D6F4', // 设置图例文字颜色
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grid: [
|
||||||
|
{
|
||||||
|
top: '60%',
|
||||||
|
left: '15%',
|
||||||
|
right: '25%',
|
||||||
|
bottom: '10%',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
//饼图:
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
radius: '35%',
|
||||||
|
color: ['#f5222d', '#fa8c16', '#fadb14', '#1677ff', '#13c2c2'],
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'inner',
|
||||||
|
formatter: (params: any) => {
|
||||||
|
if (!params.value) return '';
|
||||||
|
return `${params.value}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
center: ['35%', '25%'],
|
||||||
|
data: alarmTypeType.value,
|
||||||
|
zlevel: 2, // 设置zlevel为1,使得柱状图在下层显示
|
||||||
|
itemStyle: {
|
||||||
|
shadowBlur: 10,
|
||||||
|
shadowOffsetX: 0,
|
||||||
|
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
//柱状
|
||||||
|
{
|
||||||
|
type: 'bar',
|
||||||
|
barWidth: 12, // 柱子宽度
|
||||||
|
barCategoryGap: '30%', // 控制同一系列的柱间距离
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'right', // 位置
|
||||||
|
color: '#A7D6F4', //淡蓝色
|
||||||
|
fontSize: 14,
|
||||||
|
distance: 14, // label与柱子距离
|
||||||
|
formatter: '{c}',
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: [0, 20, 20, 0], // 圆角(左上、右上、右下、左下)
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||||
|
{ offset: 0, color: '#f0f5ff' },
|
||||||
|
{ offset: 0.5, color: '#adc6ff' },
|
||||||
|
{ offset: 1, color: '#2f54eb' },
|
||||||
|
]), // 渐变
|
||||||
|
},
|
||||||
|
data: alarmTypeTypeTop.value,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 柱状图设置
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
splitLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
type: 'value',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
splitLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
//y轴
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
type: 'category',
|
||||||
|
axisTick: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
inverse: true,
|
||||||
|
data: alarmTypeTypeTop.value.map((item: any) => item.name),
|
||||||
|
axisLabel: {
|
||||||
|
color: '#A7D6F4',
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
fnDesign(alarmTypeBar.value, optionData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fnDesign(container: HTMLElement | undefined, option: any) {
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
alarmTypeBarChart.value = markRaw(echarts.init(container, 'light'));
|
||||||
|
option && alarmTypeBarChart.value.setOption(option);
|
||||||
|
|
||||||
|
// 创建 ResizeObserver 实例
|
||||||
|
var observer = new ResizeObserver(entries => {
|
||||||
|
if (alarmTypeBarChart.value) {
|
||||||
|
alarmTypeBarChart.value.resize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 监听元素大小变化
|
||||||
|
observer.observe(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initPicture();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="alarmTypeBar" class="chart-container"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.chart-container {
|
||||||
|
/* 设置图表容器大小和位置 */
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,368 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref, nextTick, watch } from 'vue';
|
||||||
|
import * as echarts from 'echarts/core';
|
||||||
|
import { GridComponent, GridComponentOption } from 'echarts/components';
|
||||||
|
import {
|
||||||
|
BarChart,
|
||||||
|
BarSeriesOption,
|
||||||
|
PictorialBarChart,
|
||||||
|
PictorialBarSeriesOption,
|
||||||
|
} from 'echarts/charts';
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
|
import { graphNodeClickID, graphNodeState } from '../../hooks/useTopology';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { markRaw } from 'vue';
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
echarts.use([GridComponent, BarChart, PictorialBarChart, CanvasRenderer]);
|
||||||
|
|
||||||
|
type EChartsOption = echarts.ComposeOption<
|
||||||
|
GridComponentOption | BarSeriesOption | PictorialBarSeriesOption
|
||||||
|
>;
|
||||||
|
|
||||||
|
/**图DOM节点实例对象 */
|
||||||
|
const neResourcesDom = ref<HTMLElement | undefined>(undefined);
|
||||||
|
|
||||||
|
/**图实例对象 */
|
||||||
|
const neResourcesChart = ref<any>(null);
|
||||||
|
|
||||||
|
// 类别
|
||||||
|
const category = ref<any>([
|
||||||
|
{
|
||||||
|
name: t('views.dashboard.overview.resources.sysDisk'),
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t('views.dashboard.overview.resources.sysMem'),
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t('views.dashboard.overview.resources.sysCpu'),
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t('views.dashboard.overview.resources.neCpu'),
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 数据总数
|
||||||
|
const total = 100;
|
||||||
|
|
||||||
|
/**图数据 */
|
||||||
|
const optionData: EChartsOption = {
|
||||||
|
xAxis: {
|
||||||
|
max: total,
|
||||||
|
splitLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
top: '1%', // 设置条形图的边距
|
||||||
|
bottom: '12%',
|
||||||
|
left: '25%',
|
||||||
|
right: '25%',
|
||||||
|
},
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
inverse: false,
|
||||||
|
data: category.value,
|
||||||
|
axisLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
// 内
|
||||||
|
type: 'bar',
|
||||||
|
barWidth: 10,
|
||||||
|
legendHoverLink: false,
|
||||||
|
silent: true,
|
||||||
|
itemStyle: {
|
||||||
|
color: function (params) {
|
||||||
|
// 红色
|
||||||
|
if (params.value && +params.value >= 70) {
|
||||||
|
return new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||||
|
{ offset: 0, color: '#fff1f0' },
|
||||||
|
{ offset: 0.5, color: '#ffa39e' },
|
||||||
|
{ offset: 1, color: '#f5222d' },
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
// 蓝色
|
||||||
|
if (params.value && +params.value >= 30) {
|
||||||
|
return new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||||
|
{ offset: 0, color: '#f0f5ff' },
|
||||||
|
{ offset: 0.5, color: '#adc6ff' },
|
||||||
|
{ offset: 1, color: '#2f54eb' },
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
// 绿色
|
||||||
|
return new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||||
|
{ offset: 0, color: '#f6ffed' },
|
||||||
|
{ offset: 0.5, color: '#b7eb8f' },
|
||||||
|
{ offset: 1, color: '#52c41a' },
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'left',
|
||||||
|
formatter: '{b}: ',
|
||||||
|
fontSize: 15,
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
data: category.value,
|
||||||
|
z: 1,
|
||||||
|
animationEasing: 'elasticOut',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 分隔
|
||||||
|
type: 'pictorialBar',
|
||||||
|
itemStyle: {
|
||||||
|
color: '#0a3ca0',
|
||||||
|
},
|
||||||
|
symbolRepeat: 'fixed',
|
||||||
|
symbolMargin: 6,
|
||||||
|
symbol: 'rect',
|
||||||
|
symbolClip: true,
|
||||||
|
symbolSize: [1, 12],
|
||||||
|
symbolPosition: 'start',
|
||||||
|
symbolOffset: [0, -1],
|
||||||
|
symbolBoundingData: total,
|
||||||
|
data: category.value,
|
||||||
|
z: 2,
|
||||||
|
animationEasing: 'elasticOut',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 外边框
|
||||||
|
type: 'pictorialBar',
|
||||||
|
symbol: 'rect',
|
||||||
|
symbolBoundingData: total,
|
||||||
|
itemStyle: {
|
||||||
|
color: 'transparent',
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
formatter: params => {
|
||||||
|
var text = `{a| ${params.value}%} `;
|
||||||
|
if (params.value && +params.value >= 70) {
|
||||||
|
text = `{c| ${params.value}%} `;
|
||||||
|
} else if (params.value && +params.value >= 30) {
|
||||||
|
text = `{b| ${params.value}%} `;
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
rich: {
|
||||||
|
a: {
|
||||||
|
color: '#52c41a', // 绿
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
color: '#2f54eb', // 蓝
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
c: {
|
||||||
|
color: '#f5222d', // 红
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
f: {
|
||||||
|
color: '#ffffff', // 默认
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
position: 'right',
|
||||||
|
distance: 0, // 向右偏移位置
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
data: category.value,
|
||||||
|
z: 0,
|
||||||
|
animationEasing: 'elasticOut',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '外框',
|
||||||
|
type: 'bar',
|
||||||
|
barGap: '-120%', // 设置外框粗细
|
||||||
|
data: [total, total, total],
|
||||||
|
barWidth: 14,
|
||||||
|
itemStyle: {
|
||||||
|
color: 'transparent', // 填充色
|
||||||
|
borderColor: '#0a3ca0', // 边框色
|
||||||
|
borderWidth: 1, // 边框宽度
|
||||||
|
borderRadius: 1, //圆角半径
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
// 标签显示位置
|
||||||
|
show: false,
|
||||||
|
position: 'top', // insideTop 或者横向的 insideLeft
|
||||||
|
},
|
||||||
|
z: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**图数据渲染 */
|
||||||
|
function handleRanderChart(
|
||||||
|
container: HTMLElement | undefined,
|
||||||
|
option: EChartsOption
|
||||||
|
) {
|
||||||
|
if (!container) return;
|
||||||
|
neResourcesChart.value = markRaw(echarts.init(container, 'light'));
|
||||||
|
option && neResourcesChart.value.setOption(option);
|
||||||
|
|
||||||
|
// 创建 ResizeObserver 实例
|
||||||
|
var observer = new ResizeObserver(entries => {
|
||||||
|
if (neResourcesChart.value) {
|
||||||
|
neResourcesChart.value.resize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 监听元素大小变化
|
||||||
|
observer.observe(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fnChangeData(data: any[], itemID: string) {
|
||||||
|
let info = data.find((item: any) => item.id === itemID);
|
||||||
|
if (!info || !info.neState.online) return;
|
||||||
|
// if (!info.neState.online) {
|
||||||
|
// info = data.find((item: any) => item.id === itemID);
|
||||||
|
// graphNodeClickID.value = itemID;
|
||||||
|
// }
|
||||||
|
// console.log(info.id);
|
||||||
|
// console.log(info.neState.cpu.nfCpuUsage);
|
||||||
|
// console.log(info.neState.cpu.sysCpuUsage);
|
||||||
|
// console.log(info.neState.mem);
|
||||||
|
// console.log(info.neState.disk);
|
||||||
|
let sysCpuUsage = 0;
|
||||||
|
let nfCpuUsage = 0;
|
||||||
|
if (info.neState.cpu) {
|
||||||
|
nfCpuUsage = info.neState.cpu.nfCpuUsage;
|
||||||
|
const nfCpu = +(info.neState.cpu.nfCpuUsage / 100);
|
||||||
|
nfCpuUsage = +nfCpu.toFixed(2);
|
||||||
|
if (nfCpuUsage > 100) {
|
||||||
|
nfCpuUsage = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
sysCpuUsage = info.neState.cpu.sysCpuUsage;
|
||||||
|
let sysCpu = +(info.neState.cpu.sysCpuUsage / 100);
|
||||||
|
sysCpuUsage = +sysCpu.toFixed(2);
|
||||||
|
if (sysCpuUsage > 100) {
|
||||||
|
sysCpuUsage = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let sysMemUsage = 0;
|
||||||
|
if (info.neState.mem) {
|
||||||
|
const men = info.neState.mem.sysMemUsage;
|
||||||
|
sysMemUsage = +(men / 100).toFixed(2);
|
||||||
|
if (sysMemUsage > 100) {
|
||||||
|
sysMemUsage = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let sysDiskUsage = 0;
|
||||||
|
if (info.neState.disk && Array.isArray(info.neState.disk.partitionInfo)) {
|
||||||
|
let disks: any[] = info.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
category.value[0].value = sysDiskUsage;
|
||||||
|
category.value[1].value = sysMemUsage;
|
||||||
|
category.value[2].value = sysCpuUsage;
|
||||||
|
category.value[3].value = nfCpuUsage;
|
||||||
|
neResourcesChart.value.setOption({
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: category.value,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: category.value,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: category.value,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: category.value,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
graphNodeState,
|
||||||
|
v => {
|
||||||
|
fnChangeData(v, graphNodeClickID.value);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(graphNodeClickID, v => {
|
||||||
|
fnChangeData(graphNodeState.value, v);
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// setInterval(function () {
|
||||||
|
// var ndata = [
|
||||||
|
// {
|
||||||
|
// name: '系统内存',
|
||||||
|
// value: Math.round(Math.random() * 100),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: '系统CPU',
|
||||||
|
// value: Math.round(Math.random() * 100),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: '网元CPU',
|
||||||
|
// value: Math.round(Math.random() * 100),
|
||||||
|
// },
|
||||||
|
// ];
|
||||||
|
// neResourcesChart.value.setOption({
|
||||||
|
// series: [
|
||||||
|
// {
|
||||||
|
// data: ndata,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// data: ndata,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// data: ndata,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// });
|
||||||
|
// }, 2000);
|
||||||
|
nextTick(() => {
|
||||||
|
handleRanderChart(neResourcesDom.value, optionData);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="neResourcesDom" class="chart"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { listAllNeInfo } from '@/api/ne/neInfo';
|
||||||
|
import { message } from 'ant-design-vue/es';
|
||||||
|
import { getGraphData } from '@/api/monitor/topology';
|
||||||
|
import { Graph, GraphData, Tooltip } from '@antv/g6';
|
||||||
|
import { parseBasePath } from '@/plugins/file-static-url';
|
||||||
|
import { edgeLineAnimateState } from '@/views/monitor/topologyBuild/hooks/registerEdge';
|
||||||
|
import { nodeImageAnimateState } from '@/views/monitor/topologyBuild/hooks/registerNode';
|
||||||
|
import {
|
||||||
|
graphG6,
|
||||||
|
graphState,
|
||||||
|
graphNodeClickID,
|
||||||
|
notNeNodes,
|
||||||
|
} from '../../hooks/useTopology';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
/**图DOM节点实例对象 */
|
||||||
|
const graphG6Dom = ref<HTMLElement | undefined>(undefined);
|
||||||
|
|
||||||
|
/**图节点展示 */
|
||||||
|
const graphNodeTooltip = new Tooltip({
|
||||||
|
offsetX: 20,
|
||||||
|
offsetY: 20,
|
||||||
|
getContent(evt) {
|
||||||
|
if (!evt) return t('views.monitor.topologyBuild.graphNotInfo');
|
||||||
|
const { id, label, neState }: any = evt.item?.getModel();
|
||||||
|
if (notNeNodes.includes(id)) {
|
||||||
|
return `<div><span>${label || id}</span></div>`;
|
||||||
|
}
|
||||||
|
if (!neState) {
|
||||||
|
return `<div><span>${label || id}</span></div>`;
|
||||||
|
}
|
||||||
|
return `
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 200px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div><strong>${t('views.monitor.topology.state')}:</strong><span>
|
||||||
|
${
|
||||||
|
neState.online
|
||||||
|
? t('views.monitor.topology.normalcy')
|
||||||
|
: t('views.monitor.topology.exceptions')
|
||||||
|
}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>${t('views.monitor.topology.refreshTime')}:</strong><span>
|
||||||
|
${neState.refreshTime ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div>========================</div>
|
||||||
|
<div><strong>ID:</strong><span>${neState.neId}</span></div>
|
||||||
|
<div><strong>${t('views.monitor.topology.name')}:</strong><span>
|
||||||
|
${neState.neName ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>IP:</strong><span>${neState.neIP}</span></div>
|
||||||
|
<div><strong>${t('views.monitor.topology.version')}:</strong><span>
|
||||||
|
${neState.version ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>${t('views.monitor.topology.serialNum')}:</strong><span>
|
||||||
|
${neState.sn ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>${t('views.monitor.topology.expiryDate')}:</strong><span>
|
||||||
|
${neState.expire ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
itemTypes: ['node'],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**图绑定事件 */
|
||||||
|
function fnGraphEvent(graph: Graph) {
|
||||||
|
// 节点点击
|
||||||
|
graph.on('node:click', evt => {
|
||||||
|
// 获得鼠标当前目标节点
|
||||||
|
const node = evt.item?.getModel();
|
||||||
|
if (node && node.id && !notNeNodes.includes(node.id)) {
|
||||||
|
graphNodeClickID.value = node.id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**图数据渲染 */
|
||||||
|
function handleRanderGraph(
|
||||||
|
container: HTMLElement | undefined,
|
||||||
|
data: GraphData
|
||||||
|
) {
|
||||||
|
if (!container) return;
|
||||||
|
const { clientHeight, clientWidth } = container;
|
||||||
|
|
||||||
|
edgeLineAnimateState();
|
||||||
|
nodeImageAnimateState();
|
||||||
|
|
||||||
|
const graph = new Graph({
|
||||||
|
container: container,
|
||||||
|
width: clientWidth,
|
||||||
|
height: clientHeight - 36,
|
||||||
|
fitCenter: true,
|
||||||
|
fitView: true,
|
||||||
|
fitViewPadding: [20],
|
||||||
|
autoPaint: true,
|
||||||
|
modes: {
|
||||||
|
default: ['drag-canvas', 'zoom-canvas'],
|
||||||
|
},
|
||||||
|
groupByTypes: false,
|
||||||
|
nodeStateStyles: {
|
||||||
|
selected: {
|
||||||
|
fill: 'transparent',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [graphNodeTooltip],
|
||||||
|
animate: true, // 是否使用动画过度,默认为 false
|
||||||
|
animateCfg: {
|
||||||
|
duration: 500, // Number,一次动画的时长
|
||||||
|
easing: 'linearEasing', // String,动画函数
|
||||||
|
},
|
||||||
|
});
|
||||||
|
graph.data(data);
|
||||||
|
graph.render();
|
||||||
|
|
||||||
|
fnGraphEvent(graph);
|
||||||
|
|
||||||
|
graphG6.value = graph;
|
||||||
|
|
||||||
|
// 创建 ResizeObserver 实例
|
||||||
|
var observer = new ResizeObserver(function (entries) {
|
||||||
|
// 当元素大小发生变化时触发回调函数
|
||||||
|
entries.forEach(function (entry) {
|
||||||
|
if (!graphG6.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
graphG6.value.changeSize(
|
||||||
|
entry.contentRect.width,
|
||||||
|
entry.contentRect.height - 30
|
||||||
|
);
|
||||||
|
graphG6.value.fitCenter();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// 监听元素大小变化
|
||||||
|
observer.observe(container);
|
||||||
|
|
||||||
|
return graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图组数据渲染到画布
|
||||||
|
* @param reload 是否重载数据
|
||||||
|
*/
|
||||||
|
function fnGraphDataLoad(reload: boolean = false) {
|
||||||
|
Promise.all([
|
||||||
|
getGraphData(graphState.group),
|
||||||
|
listAllNeInfo({
|
||||||
|
bandStatus: false,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
.then(resArr => {
|
||||||
|
const graphRes = resArr[0];
|
||||||
|
const neRes = resArr[1];
|
||||||
|
if (
|
||||||
|
graphRes.code === RESULT_CODE_SUCCESS &&
|
||||||
|
Array.isArray(graphRes.data.nodes) &&
|
||||||
|
graphRes.data.nodes.length > 0 &&
|
||||||
|
neRes.code === RESULT_CODE_SUCCESS &&
|
||||||
|
Array.isArray(neRes.data) &&
|
||||||
|
neRes.data.length > 0
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
graphData: graphRes.data,
|
||||||
|
neList: neRes.data,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.monitor.topology.noData'),
|
||||||
|
duration: 5,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (!res) return;
|
||||||
|
const { combos, edges, nodes } = res.graphData;
|
||||||
|
|
||||||
|
// 节点过滤
|
||||||
|
const nf: Record<string, any>[] = nodes.filter(
|
||||||
|
(node: Record<string, any>) => {
|
||||||
|
Reflect.set(node, 'neState', { online: false });
|
||||||
|
// 图片路径处理
|
||||||
|
if (node.img) node.img = parseBasePath(node.img);
|
||||||
|
if (node.icon.show && node.icon?.img) {
|
||||||
|
node.icon.img = parseBasePath(node.icon.img);
|
||||||
|
}
|
||||||
|
// 遍历是否有网元数据
|
||||||
|
const nodeID: string = node.id;
|
||||||
|
const hasNe = res.neList.some(ne => {
|
||||||
|
Reflect.set(node, 'neInfo', ne.neType === nodeID ? ne : {});
|
||||||
|
return ne.neType === nodeID;
|
||||||
|
});
|
||||||
|
if (hasNe) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (notNeNodes.includes(nodeID)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 边过滤
|
||||||
|
const ef: Record<string, any>[] = edges.filter(
|
||||||
|
(edge: Record<string, any>) => {
|
||||||
|
const edgeSource: string = edge.source;
|
||||||
|
const edgeTarget: string = edge.target;
|
||||||
|
const hasNeS = nf.some(n => n.id === edgeSource);
|
||||||
|
const hasNeT = nf.some(n => n.id === edgeTarget);
|
||||||
|
// console.log(hasNeS, edgeSource, hasNeT, edgeTarget);
|
||||||
|
if (hasNeS && hasNeT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hasNeS && notNeNodes.includes(edgeTarget)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hasNeT && notNeNodes.includes(edgeSource)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 分组过滤
|
||||||
|
combos.forEach((combo: Record<string, any>) => {
|
||||||
|
const comboChildren: Record<string, any>[] = combo.children;
|
||||||
|
combo.children = comboChildren.filter(c => nf.some(n => n.id === c.id));
|
||||||
|
return combo;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 图数据
|
||||||
|
graphState.data = { combos, edges: ef, nodes: nf };
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
if (graphState.data.length < 0) return;
|
||||||
|
// 重载数据
|
||||||
|
if (reload) {
|
||||||
|
graphG6.value.read(graphState.data);
|
||||||
|
} else {
|
||||||
|
handleRanderGraph(graphG6Dom.value, graphState.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fnGraphDataLoad(false);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="graphG6Dom" class="chart"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref, watch } from 'vue';
|
||||||
|
import { listKPIData } from '@/api/perfManage/goldTarget';
|
||||||
|
import * as echarts from 'echarts/core';
|
||||||
|
import {
|
||||||
|
TooltipComponent,
|
||||||
|
TooltipComponentOption,
|
||||||
|
GridComponent,
|
||||||
|
GridComponentOption,
|
||||||
|
LegendComponent,
|
||||||
|
LegendComponentOption,
|
||||||
|
} from 'echarts/components';
|
||||||
|
import { LineChart, LineSeriesOption } from 'echarts/charts';
|
||||||
|
import { UniversalTransition } from 'echarts/features';
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
|
import { markRaw } from 'vue';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { upfFlowData, upfFlowParse } from '../../hooks/useUPFTotalFlow';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
echarts.use([
|
||||||
|
TooltipComponent,
|
||||||
|
GridComponent,
|
||||||
|
LegendComponent,
|
||||||
|
LineChart,
|
||||||
|
CanvasRenderer,
|
||||||
|
UniversalTransition,
|
||||||
|
]);
|
||||||
|
|
||||||
|
type EChartsOption = echarts.ComposeOption<
|
||||||
|
| TooltipComponentOption
|
||||||
|
| GridComponentOption
|
||||||
|
| LegendComponentOption
|
||||||
|
| LineSeriesOption
|
||||||
|
>;
|
||||||
|
|
||||||
|
/**图DOM节点实例对象 */
|
||||||
|
const upfFlow = ref<HTMLElement | undefined>(undefined);
|
||||||
|
|
||||||
|
/**图实例对象 */
|
||||||
|
const upfFlowChart = ref<any>(null);
|
||||||
|
|
||||||
|
function fnDesign(container: HTMLElement | undefined, option: EChartsOption) {
|
||||||
|
if (!container) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!upfFlowChart.value) {
|
||||||
|
upfFlowChart.value = markRaw(echarts.init(container, 'light'));
|
||||||
|
}
|
||||||
|
|
||||||
|
option && upfFlowChart.value.setOption(option);
|
||||||
|
|
||||||
|
// 创建 ResizeObserver 实例
|
||||||
|
var observer = new ResizeObserver(entries => {
|
||||||
|
if (upfFlowChart.value) {
|
||||||
|
upfFlowChart.value.resize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 监听元素大小变化
|
||||||
|
observer.observe(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
//渲染速率图
|
||||||
|
function handleRanderChart() {
|
||||||
|
const { lineXTime, lineYUp, lineYDown } = upfFlowData.value;
|
||||||
|
|
||||||
|
var yAxisSeries: any = [
|
||||||
|
{
|
||||||
|
name: t('views.dashboard.overview.upfFlow.up'),
|
||||||
|
type: 'line',
|
||||||
|
color: 'rgba(250, 219, 20)',
|
||||||
|
smooth: true,
|
||||||
|
areaStyle: {
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(250, 219, 20, .5)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(250, 219, 20, 0.5)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||||
|
shadowBlur: 10,
|
||||||
|
},
|
||||||
|
symbol: 'circle',
|
||||||
|
symbolSize: 5,
|
||||||
|
formatter: '{b}',
|
||||||
|
data: lineYUp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t('views.dashboard.overview.upfFlow.down'),
|
||||||
|
type: 'line',
|
||||||
|
color: 'rgba(92, 123, 217)',
|
||||||
|
smooth: true,
|
||||||
|
areaStyle: {
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(92, 123, 217, .5)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(92, 123, 217, 0.5)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||||
|
shadowBlur: 10,
|
||||||
|
},
|
||||||
|
symbol: 'circle',
|
||||||
|
symbolSize: 5,
|
||||||
|
formatter: '{b}',
|
||||||
|
data: lineYDown,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const optionData: EChartsOption = {
|
||||||
|
tooltip: {
|
||||||
|
show: true, //是否显示提示框组件
|
||||||
|
trigger: 'axis',
|
||||||
|
//formatter:'{a0}:{c0}<br>{a1}:{c1}'
|
||||||
|
formatter: function (param: any) {
|
||||||
|
var tip = '';
|
||||||
|
if (param !== null && param.length > 0) {
|
||||||
|
tip += param[0].name + '<br />';
|
||||||
|
for (var i = 0; i < param.length; i++) {
|
||||||
|
tip +=
|
||||||
|
param[i].marker +
|
||||||
|
param[i].seriesName +
|
||||||
|
': ' +
|
||||||
|
param[i].value +
|
||||||
|
'<br />';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tip;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: yAxisSeries.map((s: any) => s.name),
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: 'rgb(0,253,255,0.6)',
|
||||||
|
},
|
||||||
|
left: 'center', // 设置图例居中
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
top: '14%',
|
||||||
|
left: '4%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '12%',
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: lineXTime,
|
||||||
|
axisLabel: {
|
||||||
|
formatter: function (params: any) {
|
||||||
|
return params.split(' ')[1];
|
||||||
|
},
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: 'rgb(0,253,255,0.6)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
name: '(Mbps)',
|
||||||
|
nameTextStyle: {
|
||||||
|
fontSize: 12, // 设置文字距离x轴的距离
|
||||||
|
padding: [0, -10, 0, 0], // 设置名称在x轴方向上的偏移
|
||||||
|
},
|
||||||
|
type: 'value',
|
||||||
|
// splitNumber: 4,
|
||||||
|
min: 0,
|
||||||
|
//max: 300,
|
||||||
|
axisLabel: {
|
||||||
|
formatter: '{value}',
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: 'rgb(23,255,243,0.3)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: 'rgb(0,253,255,0.6)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: yAxisSeries,
|
||||||
|
};
|
||||||
|
fnDesign(upfFlow.value, optionData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询初始UPF数据 */
|
||||||
|
function fnGetInitData() {
|
||||||
|
// 查询5分钟前的
|
||||||
|
const nowDate = new Date().getTime();
|
||||||
|
|
||||||
|
listKPIData({
|
||||||
|
neType: 'UPF',
|
||||||
|
neId: '001',
|
||||||
|
startTime: nowDate - 5 * 60 * 1000,
|
||||||
|
endTime: nowDate,
|
||||||
|
interval: 5, // 5秒
|
||||||
|
sortField: 'timeGroup',
|
||||||
|
sortOrder: 'asc',
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
for (const item of res.data) {
|
||||||
|
upfFlowParse(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
handleRanderChart();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => upfFlowData.value,
|
||||||
|
v => {
|
||||||
|
if (upfFlowChart.value == null) return;
|
||||||
|
upfFlowChart.value.setOption({
|
||||||
|
xAxis: {
|
||||||
|
data: v.lineXTime,
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: v.lineYUp,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: v.lineYDown,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fnGetInitData();
|
||||||
|
|
||||||
|
// setInterval(() => {
|
||||||
|
// upfFlowData.value.lineXTime.push(parseDateToStr(new Date()));
|
||||||
|
// const upN3 = parseSizeFromKbs(+145452, 5);
|
||||||
|
// upfFlowData.value.lineYUp.push(upN3[0]);
|
||||||
|
// const downN6 = parseSizeFromKbs(+232343, 5);
|
||||||
|
// upfFlowData.value.lineYDown.push(downN6[0]);
|
||||||
|
|
||||||
|
// upfFlowChart.value.setOption({
|
||||||
|
// xAxis: {
|
||||||
|
// data: upfFlowData.value.lineXTime,
|
||||||
|
// },
|
||||||
|
// series: [
|
||||||
|
// {
|
||||||
|
// data: upfFlowData.value.lineYUp,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// data: upfFlowData.value.lineYDown,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// });
|
||||||
|
// }, 5000);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="upfFlow" class="chart-container"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.chart-container {
|
||||||
|
/* 设置图表容器大小和位置 */
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,370 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { parseDuration, parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { eventData, eventId } from '../../hooks/useUserActivity';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import useDictStore from '@/store/modules/dict';
|
||||||
|
import { onMounted, reactive } from 'vue';
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
|
||||||
|
/**字典数据 */
|
||||||
|
let dict: {
|
||||||
|
/**CDR SIP响应代码类别类型 */
|
||||||
|
cdrSipCode: DictType[];
|
||||||
|
/**CDR 呼叫类型 */
|
||||||
|
cdrCallType: DictType[];
|
||||||
|
/**UE 事件认证代码类型 */
|
||||||
|
ueAauthCode: DictType[];
|
||||||
|
/**UE 事件类型 */
|
||||||
|
ueEventType: DictType[];
|
||||||
|
/**UE 事件CM状态 */
|
||||||
|
ueEventCmState: DictType[];
|
||||||
|
} = reactive({
|
||||||
|
cdrSipCode: [],
|
||||||
|
cdrCallType: [],
|
||||||
|
ueAauthCode: [],
|
||||||
|
ueEventType: [],
|
||||||
|
ueEventCmState: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始字典数据
|
||||||
|
Promise.allSettled([
|
||||||
|
getDict('cdr_sip_code'),
|
||||||
|
getDict('cdr_call_type'),
|
||||||
|
getDict('ue_auth_code'),
|
||||||
|
getDict('ue_event_type'),
|
||||||
|
getDict('ue_event_cm_state'),
|
||||||
|
]).then(resArr => {
|
||||||
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
dict.cdrSipCode = resArr[0].value;
|
||||||
|
}
|
||||||
|
if (resArr[1].status === 'fulfilled') {
|
||||||
|
dict.cdrCallType = resArr[1].value;
|
||||||
|
}
|
||||||
|
if (resArr[2].status === 'fulfilled') {
|
||||||
|
dict.ueAauthCode = resArr[2].value;
|
||||||
|
}
|
||||||
|
if (resArr[3].status === 'fulfilled') {
|
||||||
|
dict.ueEventType = resArr[3].value;
|
||||||
|
}
|
||||||
|
if (resArr[4].status === 'fulfilled') {
|
||||||
|
dict.ueEventCmState = resArr[4].value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="activty">
|
||||||
|
<template v-for="item in eventData" :key="item.eId">
|
||||||
|
<!-- CDR事件IMS -->
|
||||||
|
<div
|
||||||
|
class="card-cdr"
|
||||||
|
:class="{ active: item.eId === eventId }"
|
||||||
|
v-if="item.eType === 'ims_cdr'"
|
||||||
|
>
|
||||||
|
<div class="card-cdr-item">
|
||||||
|
<div>
|
||||||
|
{{ t('views.dashboard.overview.userActivity.type') }}:
|
||||||
|
<span>
|
||||||
|
<DictTag
|
||||||
|
:options="dict.cdrCallType"
|
||||||
|
:value="item.data.callType"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
<div>
|
||||||
|
{{ t('views.dashboard.overview.userActivity.time') }}:
|
||||||
|
<span :title="parseDateToStr(item.data.releaseTime * 1000)">
|
||||||
|
{{ parseDateToStr(item.data.releaseTime * 1000) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-cdr-item">
|
||||||
|
<div>
|
||||||
|
{{ t('views.dashboard.overview.userActivity.caller') }}:
|
||||||
|
<span :title="item.data.callerParty">
|
||||||
|
{{ item.data.callerParty }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ t('views.dashboard.overview.userActivity.called') }}:
|
||||||
|
<span :title="item.data.calledParty">
|
||||||
|
{{ item.data.calledParty }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.data.callType !== 'sms'">
|
||||||
|
{{ t('views.dashboard.overview.userActivity.duration') }}:
|
||||||
|
<span>{{ parseDuration(item.data.callDuration) }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-else></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||||
|
<span v-if="item.data.callType !== 'sms'">
|
||||||
|
<DictTag
|
||||||
|
:options="dict.cdrSipCode"
|
||||||
|
:value="item.data.cause"
|
||||||
|
value-default="0"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ t('views.dashboard.overview.userActivity.resultOK') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- UE事件AMF -->
|
||||||
|
<div
|
||||||
|
class="card-ue"
|
||||||
|
:class="{ active: item.eId === eventId }"
|
||||||
|
v-if="item.eType === 'amf_ue'"
|
||||||
|
>
|
||||||
|
<div class="card-ue-item">
|
||||||
|
<div>
|
||||||
|
{{ t('views.dashboard.overview.userActivity.type') }}:
|
||||||
|
<span>
|
||||||
|
<DictTag :options="dict.ueEventType" :value="item.type" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
IMSI: <span :title="item.data.imsi">{{ item.data.imsi }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ t('views.dashboard.overview.userActivity.time') }}:
|
||||||
|
<span
|
||||||
|
v-if="item.type === 'auth-result'"
|
||||||
|
:title="item.data.authTime"
|
||||||
|
>
|
||||||
|
{{ item.data.authTime }}
|
||||||
|
</span>
|
||||||
|
<span v-if="item.type === 'detach'" :title="item.data.detachTime">
|
||||||
|
{{ item.data.detachTime }}
|
||||||
|
</span>
|
||||||
|
<span v-if="item.type === 'cm-state'" :title="item.data.changeTime">
|
||||||
|
{{ item.data.changeTime }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-ue-w33" v-if="item.type === 'auth-result'">
|
||||||
|
<div>
|
||||||
|
GNB ID: <span>{{ item.data.gNBID }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Cell ID: <span>{{ item.data.cellID }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
TAC ID: <span>{{ item.data.tacID }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="item.type === 'auth-result'">
|
||||||
|
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||||
|
<span>
|
||||||
|
<DictTag :options="dict.ueAauthCode" :value="item.data.authCode" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.type === 'detach'">
|
||||||
|
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||||
|
<span>{{ t('views.dashboard.overview.userActivity.resultOK') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-ue-w33" v-if="item.type === 'cm-state'">
|
||||||
|
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||||
|
<span>
|
||||||
|
<DictTag :options="dict.ueEventCmState" :value="item.data.status" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- UE事件MME -->
|
||||||
|
<div
|
||||||
|
class="card-ue"
|
||||||
|
:class="{ active: item.eId === eventId }"
|
||||||
|
v-if="item.eType === 'mme_ue'"
|
||||||
|
>
|
||||||
|
<div class="card-ue-item">
|
||||||
|
<div>
|
||||||
|
{{ t('views.dashboard.overview.userActivity.type') }}:
|
||||||
|
<span v-if="item.type === 'cm-state'">
|
||||||
|
{{
|
||||||
|
dict.ueEventType
|
||||||
|
.find(s => s.value === item.type)
|
||||||
|
?.label.replace('CM', 'ECM')
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
<DictTag :options="dict.ueEventType" :value="item.type" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
IMSI: <span :title="item.data.imsi">{{ item.data.imsi }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ t('views.dashboard.overview.userActivity.time') }}:
|
||||||
|
<span :title="item.data.timestamp">
|
||||||
|
{{ parseDateToStr(+item.data.timestamp * 1000) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-ue-w33" v-if="item.type === 'auth-result'">
|
||||||
|
<div>
|
||||||
|
ENB ID: <span>{{ item.data.eNBID }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Cell ID: <span>{{ item.data.cellID }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
TAC ID: <span>{{ item.data.tacID }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.type === 'auth-result'">
|
||||||
|
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||||
|
<span>
|
||||||
|
<DictTag :options="dict.ueAauthCode" :value="item.data.result" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.type === 'detach'">
|
||||||
|
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||||
|
<span>{{ t('views.dashboard.overview.userActivity.resultOK') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-ue-w33" v-if="item.type === 'cm-state'">
|
||||||
|
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||||
|
<span>
|
||||||
|
<DictTag :options="dict.ueEventCmState" :value="item.data.result" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.activty {
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
height: 94%;
|
||||||
|
color: #61a8ff;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
& .card-ue {
|
||||||
|
border: 1px #61a8ff solid;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.2rem 0.5rem;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
line-height: 1rem;
|
||||||
|
& span {
|
||||||
|
color: #68d8fe;
|
||||||
|
}
|
||||||
|
&-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
& > div {
|
||||||
|
width: 50%;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: start;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-w33 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
& > div {
|
||||||
|
width: 33%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .card-cdr {
|
||||||
|
border: 1px #61a8ff solid;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.2rem 0.5rem;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
line-height: 1rem;
|
||||||
|
& span {
|
||||||
|
color: #68d8fe;
|
||||||
|
}
|
||||||
|
&-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
& > div {
|
||||||
|
flex: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: start;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .active {
|
||||||
|
color: #faad14;
|
||||||
|
border: 1px #faad14 solid;
|
||||||
|
animation: backInRight 0.3s alternate;
|
||||||
|
& span {
|
||||||
|
color: #faad14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 兼容当行显示字内容 */
|
||||||
|
@media (max-width: 1720px) {
|
||||||
|
& .card-cdr {
|
||||||
|
&-item {
|
||||||
|
display: block;
|
||||||
|
& > div {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& .card-ue {
|
||||||
|
&-item {
|
||||||
|
display: block;
|
||||||
|
& > div {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 修改滚动条的样式 */
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 8px; /* 设置滚动条宽度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background-color: #101129; /* 设置滚动条轨道背景颜色 */
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #28293f; /* 设置滚动条滑块颜色 */
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: #68d8fe; /* 设置鼠标悬停时滚动条滑块颜色 */
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes backInRight {
|
||||||
|
0% {
|
||||||
|
opacity: 0.7;
|
||||||
|
-webkit-transform: translateX(2000px) scale(0.7);
|
||||||
|
transform: translateX(2000px) scale(0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
80% {
|
||||||
|
opacity: 0.7;
|
||||||
|
-webkit-transform: translateX(0) scale(0.7);
|
||||||
|
transform: translateX(0) scale(0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
.viewport {
|
||||||
|
/* 限定大小 */
|
||||||
|
min-width: 1024px;
|
||||||
|
max-width: 1920px;
|
||||||
|
min-height: 780px;
|
||||||
|
margin: 0 auto;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
padding: 5rem 0.833rem 0;
|
||||||
|
line-height: 1.15;
|
||||||
|
background-color: #101129;
|
||||||
|
height: 100vh;
|
||||||
|
margin-bottom: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
flex: 3;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 边框 */
|
||||||
|
.panel {
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 2px solid red;
|
||||||
|
border-image: url(../images/border.png) 51 38 21 132;
|
||||||
|
border-width: 2.125rem 1.583rem 0.875rem 5.5rem;
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 0.833rem;
|
||||||
|
}
|
||||||
|
.panel .inner {
|
||||||
|
/* 装内容 */
|
||||||
|
/* height: 60px; */
|
||||||
|
position: absolute;
|
||||||
|
top: -2.125rem;
|
||||||
|
right: -1.583rem;
|
||||||
|
bottom: -0.875rem;
|
||||||
|
left: -5.5rem;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
}
|
||||||
|
.panel h3 {
|
||||||
|
font-size: 0.833rem;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 总览标题 */
|
||||||
|
.brand {
|
||||||
|
background-image: url(../images/brand.png);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center center;
|
||||||
|
position: absolute;
|
||||||
|
top: 0.833rem;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.brand .brand-title {
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
font-weight: 600;
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.brand .brand-desc {
|
||||||
|
color: #d9d9d9;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 实时流量 */
|
||||||
|
.upfFlow {
|
||||||
|
/* min-height: 16rem; */
|
||||||
|
height: 40%;
|
||||||
|
}
|
||||||
|
.upfFlow .inner .chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 网络拓扑 */
|
||||||
|
.topology {
|
||||||
|
/* min-height: 27.8rem; */
|
||||||
|
height: 56.4%;
|
||||||
|
}
|
||||||
|
.topology .inner h3 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
.topology .inner h3 .normal {
|
||||||
|
color: #52c41a;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
.topology .inner h3 .abnormal {
|
||||||
|
color: #f5222d;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
.topology .inner .chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 概览区域 */
|
||||||
|
.skim {
|
||||||
|
/* min-height: 7.78rem; */
|
||||||
|
height: 14.4%;
|
||||||
|
}
|
||||||
|
.skim .inner .data {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
height: 90%;
|
||||||
|
}
|
||||||
|
.skim .inner .data .item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: baseline;
|
||||||
|
width: 33%;
|
||||||
|
}
|
||||||
|
.skim .inner .data .item div {
|
||||||
|
font-size: 1.467rem;
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
line-height: 2rem;
|
||||||
|
}
|
||||||
|
.skim .inner .data .item span {
|
||||||
|
color: #4c9bfd;
|
||||||
|
font-size: 0.833rem;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
line-height: 2rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: start;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.skim .inner .data .item span::before {
|
||||||
|
content: ' ';
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 0;
|
||||||
|
background-image: linear-gradient(to right, #fff, #fff0);
|
||||||
|
height: 1px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 概览区域 衍生基站信息 */
|
||||||
|
.skim.base {
|
||||||
|
height: 12%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skim.base .inner .data {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
height: 75%;
|
||||||
|
}
|
||||||
|
.skim.base .inner .data .item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: baseline;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 用户行为 */
|
||||||
|
.userActivity {
|
||||||
|
/* min-height: 35.8rem; */
|
||||||
|
height: 54.6%;
|
||||||
|
}
|
||||||
|
.userActivity .inner .chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 流量统计 */
|
||||||
|
.upfFlowTotal {
|
||||||
|
/* min-height: 7.5rem; */
|
||||||
|
height: 14.4%;
|
||||||
|
}
|
||||||
|
.upfFlowTotal .inner h3 {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.upfFlowTotal .inner h3 .filter {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.upfFlowTotal .inner h3 .filter span {
|
||||||
|
display: block;
|
||||||
|
height: 0.75rem;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0 0.75rem;
|
||||||
|
color: #1950c4;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
border-right: 0.083rem solid #00f2f1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.upfFlowTotal .inner h3 .filter span:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.upfFlowTotal .inner h3 .filter span:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
.upfFlowTotal .inner h3 .filter span.active {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.833rem;
|
||||||
|
}
|
||||||
|
.upfFlowTotal .inner .chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin-top: 0.1rem;
|
||||||
|
}
|
||||||
|
.upfFlowTotal .inner .chart .data {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
height: 60%;
|
||||||
|
}
|
||||||
|
.upfFlowTotal .inner .chart .data .item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
.upfFlowTotal .inner .chart .data .item h4 {
|
||||||
|
font-size: 1.467rem;
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.upfFlowTotal .inner .chart .data .item span {
|
||||||
|
color: #4c9bfd;
|
||||||
|
font-size: 0.867rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 资源情况 */
|
||||||
|
.resources {
|
||||||
|
/* min-height: 18rem; */
|
||||||
|
height: 34.4%;
|
||||||
|
}
|
||||||
|
.resources .inner .chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 告警统计 */
|
||||||
|
.alarmType {
|
||||||
|
/* min-height: 25rem; */
|
||||||
|
height: 46%;
|
||||||
|
}
|
||||||
|
.alarmType .inner .chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 跳转鼠标悬浮 */
|
||||||
|
.toRouter:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
.toRouter:hover > *,
|
||||||
|
.toRouter:hover > * > * {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { computed, reactive, ref } from 'vue';
|
||||||
|
|
||||||
|
/**非网元元素 */
|
||||||
|
export const notNeNodes = [
|
||||||
|
'5GC',
|
||||||
|
'DN',
|
||||||
|
'UE',
|
||||||
|
'Base',
|
||||||
|
'lan',
|
||||||
|
'lan1',
|
||||||
|
'lan2',
|
||||||
|
'lan3',
|
||||||
|
'lan4',
|
||||||
|
'lan5',
|
||||||
|
'lan6',
|
||||||
|
'lan7',
|
||||||
|
'LAN',
|
||||||
|
'NR',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**图状态 */
|
||||||
|
export const graphState = reactive<Record<string, any>>({
|
||||||
|
/**当前图组名 */
|
||||||
|
group: '5GC System Architecture',
|
||||||
|
/**图数据 */
|
||||||
|
data: {
|
||||||
|
combos: [],
|
||||||
|
edges: [],
|
||||||
|
nodes: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**图实例对象 */
|
||||||
|
export const graphG6 = ref<any>(null);
|
||||||
|
|
||||||
|
/**图点击选择 */
|
||||||
|
export const graphNodeClickID = ref<string>('UPF');
|
||||||
|
|
||||||
|
/**图节点网元信息状态 */
|
||||||
|
export const graphNodeState = computed(() =>
|
||||||
|
graphState.data.nodes.map((item: any) => ({
|
||||||
|
id: item.id,
|
||||||
|
label: item.label,
|
||||||
|
neInfo: item.neInfo,
|
||||||
|
neState: item.neState,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
/**图节点网元状态数量 */
|
||||||
|
export const graphNodeStateNum = computed(() => {
|
||||||
|
let normal = 0;
|
||||||
|
let abnormal = 0;
|
||||||
|
for (const item of graphState.data.nodes) {
|
||||||
|
const neId = item.neState.neId;
|
||||||
|
if (neId) {
|
||||||
|
if (item.neState.online) {
|
||||||
|
normal += 1;
|
||||||
|
} else {
|
||||||
|
abnormal += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [normal, abnormal];
|
||||||
|
});
|
||||||
|
|
||||||
|
/**网元状态请求标记 */
|
||||||
|
export const neStateRequestMap = ref<Map<string, boolean>>(new Map());
|
||||||
|
|
||||||
|
/**neStateParse 网元状态 数据解析 */
|
||||||
|
export function neStateParse(neType: string, data: Record<string, any>) {
|
||||||
|
const { combos, edges, nodes } = graphState.data;
|
||||||
|
const node = nodes.find((item: Record<string, any>) => item.id === neType);
|
||||||
|
|
||||||
|
// 更新网元状态
|
||||||
|
const newNeState = Object.assign(node.neState, data, {
|
||||||
|
refreshTime: parseDateToStr(data.refreshTime, 'HH:mm:ss'),
|
||||||
|
online: !!data.cpu,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 通过 ID 查询节点实例
|
||||||
|
const item = graphG6.value.findById(node.id);
|
||||||
|
if (item) {
|
||||||
|
const stateColor = newNeState.online ? '#52c41a' : '#f5222d'; // 状态颜色
|
||||||
|
// 图片类型不能填充
|
||||||
|
if (node.type.startsWith('image')) {
|
||||||
|
// 更新节点
|
||||||
|
if (node.label !== newNeState.neName) {
|
||||||
|
graphG6.value.updateItem(item, {
|
||||||
|
label: newNeState.neName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 设置状态
|
||||||
|
graphG6.value.setItemState(item, 'top-right-dot', stateColor);
|
||||||
|
} else {
|
||||||
|
// 更新节点
|
||||||
|
graphG6.value.updateItem(item, {
|
||||||
|
label: newNeState.neName,
|
||||||
|
// neState: newNeState,
|
||||||
|
style: {
|
||||||
|
fill: stateColor, // 填充色
|
||||||
|
stroke: stateColor, // 填充色
|
||||||
|
},
|
||||||
|
// labelCfg: {
|
||||||
|
// style: {
|
||||||
|
// fill: '#ffffff', // 标签文本色
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
});
|
||||||
|
// 设置状态
|
||||||
|
graphG6.value.setItemState(item, 'stroke', newNeState.online);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置边状态
|
||||||
|
for (const edge of edges) {
|
||||||
|
const edgeSource: string = edge.source;
|
||||||
|
const edgeTarget: string = edge.target;
|
||||||
|
const neS = nodes.find((n: any) => n.id === edgeSource);
|
||||||
|
const neT = nodes.find((n: any) => n.id === edgeTarget);
|
||||||
|
// console.log(neS, edgeSource, neT, edgeTarget);
|
||||||
|
|
||||||
|
if (neS && neT) {
|
||||||
|
// 通过 ID 查询节点实例
|
||||||
|
// const item = graphG6.value.findById(edge.id);
|
||||||
|
// console.log(
|
||||||
|
// `${edgeSource} - ${edgeTarget}`,
|
||||||
|
// neS.neState.online && neT.neState.online
|
||||||
|
// );
|
||||||
|
// const stateColor = neS.neState.online && neT.neState.online ? '#000000' : '#ff4d4f'; // 状态颜色
|
||||||
|
// 更新边
|
||||||
|
// graphG6.value.updateItem(item, {
|
||||||
|
// label: `${edgeSource} - ${edgeTarget}`,
|
||||||
|
// style: {
|
||||||
|
// stroke: stateColor, // 填充色
|
||||||
|
// },
|
||||||
|
// labelCfg: {
|
||||||
|
// style: {
|
||||||
|
// fill: '#ffffff', // 标签文本色
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// 设置状态
|
||||||
|
graphG6.value.setItemState(
|
||||||
|
edge.id,
|
||||||
|
'circle-move',
|
||||||
|
neS.neState.online && neT.neState.online
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (neS && notNeNodes.includes(edgeTarget)) {
|
||||||
|
graphG6.value.setItemState(edge.id, 'line-dash', neS.neState.online);
|
||||||
|
}
|
||||||
|
if (neT && notNeNodes.includes(edgeSource)) {
|
||||||
|
graphG6.value.setItemState(edge.id, 'line-dash', neT.neState.online);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求标记复位
|
||||||
|
neStateRequestMap.value.set(neType, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**属性复位 */
|
||||||
|
export function topologyReset() {
|
||||||
|
graphState.data = {
|
||||||
|
combos: [],
|
||||||
|
edges: [],
|
||||||
|
nodes: [],
|
||||||
|
};
|
||||||
|
graphG6.value = null;
|
||||||
|
graphNodeClickID.value = 'UPF';
|
||||||
|
neStateRequestMap.value = new Map();
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { parseSizeFromBits, parseSizeFromKbs } from '@/utils/parse-utils';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
type FDType = {
|
||||||
|
/**时间 */
|
||||||
|
lineXTime: string[];
|
||||||
|
/**上行 N3 */
|
||||||
|
lineYUp: number[];
|
||||||
|
/**下行 N6 */
|
||||||
|
lineYDown: number[];
|
||||||
|
/**容量 */
|
||||||
|
cap: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**UPF-流量数据 */
|
||||||
|
export const upfFlowData = ref<FDType>({
|
||||||
|
lineXTime: [],
|
||||||
|
lineYUp: [],
|
||||||
|
lineYDown: [],
|
||||||
|
cap: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**UPF-流量数据 数据解析 */
|
||||||
|
export function upfFlowParse(data: Record<string, string>) {
|
||||||
|
upfFlowData.value.lineXTime.push(parseDateToStr(+data['timeGroup']));
|
||||||
|
const upN3 = parseSizeFromKbs(+data['UPF.03'], 5);
|
||||||
|
upfFlowData.value.lineYUp.push(upN3[0]);
|
||||||
|
const downN6 = parseSizeFromKbs(+data['UPF.06'], 5);
|
||||||
|
upfFlowData.value.lineYDown.push(downN6[0]);
|
||||||
|
upfFlowData.value.cap += 1;
|
||||||
|
// 超过 25 弹出
|
||||||
|
if (upfFlowData.value.cap > 25) {
|
||||||
|
upfFlowData.value.lineXTime.shift();
|
||||||
|
upfFlowData.value.lineYUp.shift();
|
||||||
|
upfFlowData.value.lineYDown.shift();
|
||||||
|
upfFlowData.value.cap -= 1;
|
||||||
|
}
|
||||||
|
// UPF-总流量数0天 当天24小时
|
||||||
|
upfTFParse('0', {
|
||||||
|
up: upfTotalFlow.value['0'].up + +data['UPF.03'],
|
||||||
|
down: upfTotalFlow.value['0'].down + +data['UPF.06'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
type TFType = {
|
||||||
|
/**上行 N3 */
|
||||||
|
up: number;
|
||||||
|
upFrom: string;
|
||||||
|
/**下行 N6 */
|
||||||
|
down: number;
|
||||||
|
downFrom: string;
|
||||||
|
/**请求标记 */
|
||||||
|
requestFlag: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**UPF-总流量数 */
|
||||||
|
export const upfTotalFlow = ref<Record<string, TFType>>({
|
||||||
|
'0': {
|
||||||
|
up: 0,
|
||||||
|
upFrom: '0 B',
|
||||||
|
down: 0,
|
||||||
|
downFrom: '0 B',
|
||||||
|
requestFlag: false,
|
||||||
|
},
|
||||||
|
'7': {
|
||||||
|
up: 0,
|
||||||
|
upFrom: '0 B',
|
||||||
|
down: 0,
|
||||||
|
downFrom: '0 B',
|
||||||
|
requestFlag: false,
|
||||||
|
},
|
||||||
|
'30': {
|
||||||
|
up: 0,
|
||||||
|
upFrom: '0 B',
|
||||||
|
down: 0,
|
||||||
|
downFrom: '0 B',
|
||||||
|
requestFlag: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**UPF-总流量数 数据解析 */
|
||||||
|
export function upfTFParse(day: string, data: Record<string, number>) {
|
||||||
|
let { up, down } = data;
|
||||||
|
upfTotalFlow.value[day] = {
|
||||||
|
up: up,
|
||||||
|
upFrom: parseSizeFromBits(up),
|
||||||
|
down: down,
|
||||||
|
downFrom: parseSizeFromBits(down),
|
||||||
|
requestFlag: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**UPF-总流量数 选中 */
|
||||||
|
export const upfTFActive = ref<string>('0');
|
||||||
|
|
||||||
|
/**属性复位 */
|
||||||
|
export function upfTotalFlowReset() {
|
||||||
|
upfFlowData.value = {
|
||||||
|
lineXTime: [],
|
||||||
|
lineYUp: [],
|
||||||
|
lineYDown: [],
|
||||||
|
cap: 0,
|
||||||
|
};
|
||||||
|
for (const key of Object.keys(upfTotalFlow.value)) {
|
||||||
|
upfTotalFlow.value[key] = {
|
||||||
|
up: 0,
|
||||||
|
upFrom: '0 B',
|
||||||
|
down: 0,
|
||||||
|
downFrom: '0 B',
|
||||||
|
requestFlag: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
upfTFActive.value = '0';
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
/**ueEventAMFParse UE会话事件AMF 数据解析 */
|
||||||
|
function ueEventAMFParse(
|
||||||
|
item: Record<string, any>
|
||||||
|
): false | Record<string, any> {
|
||||||
|
let evData: Record<string, any> = item.eventJSON;
|
||||||
|
if (typeof evData === 'string') {
|
||||||
|
try {
|
||||||
|
evData = JSON.parse(evData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
eType: 'amf_ue',
|
||||||
|
eId: `amf_ue_${item.id}_${Date.now()}`,
|
||||||
|
eTime: +item.timestamp,
|
||||||
|
id: item.id,
|
||||||
|
type: item.eventType,
|
||||||
|
data: evData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**ueEventMMEParse UE会话事件MME 数据解析 */
|
||||||
|
function ueEventMMEParse(
|
||||||
|
item: Record<string, any>
|
||||||
|
): false | Record<string, any> {
|
||||||
|
let evData: Record<string, any> = item.eventJSON;
|
||||||
|
if (typeof evData === 'string') {
|
||||||
|
try {
|
||||||
|
evData = JSON.parse(evData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
eType: 'mme_ue',
|
||||||
|
eId: `mme_ue_${item.id}_${Date.now()}`,
|
||||||
|
eTime: +item.timestamp,
|
||||||
|
id: item.id,
|
||||||
|
type: item.eventType,
|
||||||
|
data: evData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**cdrEventIMSParse CDR会话事件IMS 数据解析 */
|
||||||
|
function cdrEventIMSParse(
|
||||||
|
item: Record<string, any>
|
||||||
|
): false | Record<string, any> {
|
||||||
|
let evData: Record<string, any> = item.cdrJSON || item.CDR;
|
||||||
|
if (typeof evData === 'string') {
|
||||||
|
try {
|
||||||
|
evData = JSON.parse(evData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 指定显示CDR类型MOC/MTSM
|
||||||
|
if (!['MOC', 'MTSM'].includes(evData.recordType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
eType: 'ims_cdr',
|
||||||
|
eId: `ims_cdr_${item.id}_${Date.now()}`,
|
||||||
|
eTime: +item.timestamp,
|
||||||
|
id: item.id,
|
||||||
|
data: evData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**eventListParse 事件列表解析 */
|
||||||
|
export function eventListParse(
|
||||||
|
type: 'ims_cdr' | 'amf_ue' | 'mme_ue',
|
||||||
|
data: any
|
||||||
|
) {
|
||||||
|
eventTotal.value += data.total;
|
||||||
|
for (const item of data.rows) {
|
||||||
|
let v: false | Record<string, any> = false;
|
||||||
|
if (type === 'ims_cdr') {
|
||||||
|
v = cdrEventIMSParse(item);
|
||||||
|
}
|
||||||
|
if (type === 'amf_ue') {
|
||||||
|
v = ueEventAMFParse(item);
|
||||||
|
}
|
||||||
|
if (type === 'mme_ue') {
|
||||||
|
v = ueEventMMEParse(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v) {
|
||||||
|
eventData.value.push(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 有数据进行排序
|
||||||
|
if (eventData.value.length > 5) {
|
||||||
|
eventData.value.sort((a, b) => b.eTime - a.eTime);
|
||||||
|
}
|
||||||
|
if (eventData.value.length > 0) {
|
||||||
|
eventId.value = eventData.value[0].eId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**eventItemParseAndPush 事件项解析并添加 */
|
||||||
|
export async function eventItemParseAndPush(
|
||||||
|
type: 'ims_cdr' | 'amf_ue' | 'mme_ue',
|
||||||
|
item: any
|
||||||
|
) {
|
||||||
|
let v: false | Record<string, any> = false;
|
||||||
|
if (type === 'ims_cdr') {
|
||||||
|
v = cdrEventIMSParse(item);
|
||||||
|
}
|
||||||
|
if (type === 'amf_ue') {
|
||||||
|
v = ueEventAMFParse(item);
|
||||||
|
}
|
||||||
|
if (type === 'mme_ue') {
|
||||||
|
v = ueEventMMEParse(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v) {
|
||||||
|
eventData.value.unshift(v);
|
||||||
|
eventTotal.value += 1;
|
||||||
|
eventId.value = v.eId;
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 800));
|
||||||
|
if (eventData.value.length > 20) {
|
||||||
|
eventData.value.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**CDR+UE事件数据 */
|
||||||
|
export const eventData = ref<Record<string, any>[]>([]);
|
||||||
|
/**CDR+UE事件总量 */
|
||||||
|
export const eventTotal = ref<number>(0);
|
||||||
|
/**CDR/UE事件推送id */
|
||||||
|
export const eventId = ref<string>('');
|
||||||
|
|
||||||
|
/**属性复位 */
|
||||||
|
export function userActivityReset() {
|
||||||
|
eventData.value = [];
|
||||||
|
eventTotal.value = 0;
|
||||||
|
eventId.value = '';
|
||||||
|
}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
import { RESULT_CODE_ERROR } from '@/constants/result-constants';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import { onBeforeUnmount, onMounted } from 'vue';
|
||||||
|
import {
|
||||||
|
eventListParse,
|
||||||
|
eventItemParseAndPush,
|
||||||
|
userActivityReset,
|
||||||
|
} from './useUserActivity';
|
||||||
|
import {
|
||||||
|
upfTotalFlow,
|
||||||
|
upfTFParse,
|
||||||
|
upfFlowParse,
|
||||||
|
upfTotalFlowReset,
|
||||||
|
} from './useUPFTotalFlow';
|
||||||
|
import { topologyReset, neStateParse } from './useTopology';
|
||||||
|
import PQueue from 'p-queue';
|
||||||
|
|
||||||
|
/**websocket连接 */
|
||||||
|
export default function useWS() {
|
||||||
|
const ws = new WS();
|
||||||
|
const queue = new PQueue({ concurrency: 1, autoStart: true });
|
||||||
|
|
||||||
|
/**发消息 */
|
||||||
|
function wsSend(data: Record<string, any>) {
|
||||||
|
ws.send(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsMessage(res: Record<string, any>) {
|
||||||
|
// console.log(res);
|
||||||
|
const { code, requestId, data } = res;
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 网元状态
|
||||||
|
if (requestId && requestId.startsWith('neState')) {
|
||||||
|
const neType = requestId.split('_')[1];
|
||||||
|
neStateParse(neType, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 普通信息
|
||||||
|
switch (requestId) {
|
||||||
|
// AMF_UE会话事件
|
||||||
|
case 'amf_1010':
|
||||||
|
if (Array.isArray(data.rows)) {
|
||||||
|
eventListParse('amf_ue', data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// MME_UE会话事件
|
||||||
|
case 'mme_1011_001':
|
||||||
|
if (Array.isArray(data.rows)) {
|
||||||
|
eventListParse('mme_ue', data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// IMS_CDR会话事件
|
||||||
|
case 'ims_1005_001':
|
||||||
|
if (Array.isArray(data.rows)) {
|
||||||
|
eventListParse('ims_cdr', data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
//UPF-总流量数
|
||||||
|
case 'upf_001_0':
|
||||||
|
upfTFParse('0', data);
|
||||||
|
break;
|
||||||
|
case 'upf_001_7':
|
||||||
|
upfTFParse('7', data);
|
||||||
|
break;
|
||||||
|
case 'upf_001_30':
|
||||||
|
upfTFParse('30', data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅组信息
|
||||||
|
if (!data?.groupId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (data.groupId) {
|
||||||
|
// kpiEvent 指标UPF
|
||||||
|
case '12_001':
|
||||||
|
if (data.data) {
|
||||||
|
upfFlowParse(data.data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// AMF_UE会话事件
|
||||||
|
case '1010':
|
||||||
|
if (data.data) {
|
||||||
|
queue.add(() => eventItemParseAndPush('amf_ue', data.data));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// MME_UE会话事件
|
||||||
|
case '1011_001':
|
||||||
|
if (data.data) {
|
||||||
|
queue.add(() => eventItemParseAndPush('mme_ue', data.data));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// IMS_CDR会话事件
|
||||||
|
case '1005_001':
|
||||||
|
if (data.data) {
|
||||||
|
queue.add(() => eventItemParseAndPush('ims_cdr', data.data));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**UPF-总流量数 发消息*/
|
||||||
|
function upfTFSend(day: '0' | '7' | '30') {
|
||||||
|
// 请求标记检查避免重复发送
|
||||||
|
if (upfTotalFlow.value[day].requestFlag) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
upfTotalFlow.value[day].requestFlag = true;
|
||||||
|
|
||||||
|
ws.send({
|
||||||
|
requestId: `upf_001_${day}`,
|
||||||
|
type: 'upf_tf',
|
||||||
|
data: {
|
||||||
|
neType: 'UPF',
|
||||||
|
neId: '001',
|
||||||
|
day: Number(day),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**userActivitySend 用户行为事件基础列表数据 发消息*/
|
||||||
|
function userActivitySend() {
|
||||||
|
// AMF_UE会话事件
|
||||||
|
ws.send({
|
||||||
|
requestId: 'amf_1010',
|
||||||
|
type: 'amf_ue',
|
||||||
|
data: {
|
||||||
|
neType: 'AMF',
|
||||||
|
neId: '001',
|
||||||
|
sortField: 'timestamp',
|
||||||
|
sortOrder: 'desc',
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// MME_UE会话事件
|
||||||
|
ws.send({
|
||||||
|
requestId: 'mme_1011_001',
|
||||||
|
type: 'mme_ue',
|
||||||
|
data: {
|
||||||
|
neType: 'MME',
|
||||||
|
neId: '001',
|
||||||
|
sortField: 'timestamp',
|
||||||
|
sortOrder: 'desc',
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// IMS_CDR会话事件
|
||||||
|
ws.send({
|
||||||
|
requestId: 'ims_1005_001',
|
||||||
|
type: 'ims_cdr',
|
||||||
|
data: {
|
||||||
|
neType: 'IMS',
|
||||||
|
neId: '001',
|
||||||
|
recordType: 'MOC',
|
||||||
|
sortField: 'timestamp',
|
||||||
|
sortOrder: 'desc',
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: '/ws',
|
||||||
|
params: {
|
||||||
|
/**订阅通道组
|
||||||
|
*
|
||||||
|
* 指标UPF (GroupID:12_neId)
|
||||||
|
* AMF_UE会话事件(GroupID:1010)
|
||||||
|
* MME_UE会话事件(GroupID:1011_neId)
|
||||||
|
* IMS_CDR会话事件(GroupID:1005_neId)
|
||||||
|
*/
|
||||||
|
subGroupID: '12_001,1010,1011_001,1005_001',
|
||||||
|
},
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: (ev: any) => {
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
ws.connect(options);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
ws.close();
|
||||||
|
userActivityReset();
|
||||||
|
upfTotalFlowReset();
|
||||||
|
topologyReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
wsSend,
|
||||||
|
userActivitySend,
|
||||||
|
upfTFSend,
|
||||||
|
};
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 237 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,493 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onBeforeUnmount, onMounted, reactive, ref } from 'vue';
|
||||||
|
import svgBase from '@/assets/svg/base.svg';
|
||||||
|
import svgUserIMS from '@/assets/svg/userIMS.svg';
|
||||||
|
import svgUserSMF from '@/assets/svg/userSMF.svg';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import Topology from './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';
|
||||||
|
import { hasRoles } from '@/plugins/auth-user';
|
||||||
|
const router = useRouter();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { wsSend, userActivitySend, upfTFSend } = useWS();
|
||||||
|
|
||||||
|
/**概览状态类型 */
|
||||||
|
type SkimStateType = {
|
||||||
|
/**UDM签约用户数量 */
|
||||||
|
udmSubNum: number;
|
||||||
|
/**SMF在线用户数 */
|
||||||
|
smfUeNum: number;
|
||||||
|
/**IMS在线用户数 */
|
||||||
|
imsUeNum: number;
|
||||||
|
/**5G基站数量 */
|
||||||
|
gnbNum: number;
|
||||||
|
/**5G在线用户数量 */
|
||||||
|
gnbUeNum: number;
|
||||||
|
/**4G基站数量 */
|
||||||
|
enbNum: number;
|
||||||
|
/**4G在线用户数量 */
|
||||||
|
enbUeNum: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**概览状态信息 */
|
||||||
|
let skimState: SkimStateType = reactive({
|
||||||
|
udmSubNum: 0,
|
||||||
|
smfUeNum: 0,
|
||||||
|
imsUeNum: 0,
|
||||||
|
gnbNum: 0,
|
||||||
|
gnbUeNum: 0,
|
||||||
|
enbNum: 0,
|
||||||
|
enbUeNum: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**总览节点 */
|
||||||
|
const viewportDom = ref<HTMLElement | null>(null);
|
||||||
|
const { isFullscreen, toggle } = useFullscreen(viewportDom);
|
||||||
|
|
||||||
|
/**10s调度器 */
|
||||||
|
const interval10s = ref<any>(null);
|
||||||
|
|
||||||
|
/**5s调度器 */
|
||||||
|
const interval5s = ref<any>(null);
|
||||||
|
|
||||||
|
/**查询网元状态 */
|
||||||
|
function fnGetNeState() {
|
||||||
|
// 获取节点状态
|
||||||
|
for (const node of graphState.data.nodes) {
|
||||||
|
if (notNeNodes.includes(node.id)) continue;
|
||||||
|
const { neType, neId } = node.neInfo;
|
||||||
|
if (!neType || !neId) continue;
|
||||||
|
// 请求标记检查避免重复发送
|
||||||
|
if (neStateRequestMap.value.get(neType)) continue;
|
||||||
|
neStateRequestMap.value.set(neType, true);
|
||||||
|
|
||||||
|
wsSend({
|
||||||
|
requestId: `neState_${neType}_${neId}`,
|
||||||
|
type: 'ne_state',
|
||||||
|
data: {
|
||||||
|
neType: neType,
|
||||||
|
neId: neId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**获取概览信息 */
|
||||||
|
async function fnGetSkim() {
|
||||||
|
const resArr = await Promise.allSettled([
|
||||||
|
listUDMSub({
|
||||||
|
neid: '001',
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 1,
|
||||||
|
}),
|
||||||
|
listUENumBySMF('001'),
|
||||||
|
listUENumByIMS('001'),
|
||||||
|
listBase5G({
|
||||||
|
neType: 'AMF',
|
||||||
|
neId: '001',
|
||||||
|
}),
|
||||||
|
listBase5G({
|
||||||
|
neType: 'MME',
|
||||||
|
neId: '001',
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
const res0 = resArr[0].value;
|
||||||
|
if (res0.code === RESULT_CODE_SUCCESS) {
|
||||||
|
skimState.udmSubNum = res0.total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resArr[1].status === 'fulfilled') {
|
||||||
|
const res1 = resArr[1].value;
|
||||||
|
if (res1.code === RESULT_CODE_SUCCESS) {
|
||||||
|
skimState.smfUeNum = res1.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resArr[2].status === 'fulfilled') {
|
||||||
|
const res2 = resArr[2].value;
|
||||||
|
if (res2.code === RESULT_CODE_SUCCESS) {
|
||||||
|
skimState.imsUeNum = res2.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resArr[3].status === 'fulfilled') {
|
||||||
|
const res3 = resArr[3].value;
|
||||||
|
if (res3.code === RESULT_CODE_SUCCESS) {
|
||||||
|
skimState.gnbNum = res3.total;
|
||||||
|
skimState.gnbUeNum = 0;
|
||||||
|
res3.rows.map((item: any) => {
|
||||||
|
skimState.gnbUeNum += item.ueNum;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resArr[4].status === 'fulfilled') {
|
||||||
|
const res4 = resArr[4].value;
|
||||||
|
if (res4.code === RESULT_CODE_SUCCESS) {
|
||||||
|
skimState.enbNum = res4.total;
|
||||||
|
skimState.enbUeNum = 0;
|
||||||
|
res4.rows.map((item: any) => {
|
||||||
|
skimState.enbUeNum += item.ueNum;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**初始数据函数 */
|
||||||
|
function loadData() {
|
||||||
|
fnGetNeState(); // 获取网元状态
|
||||||
|
userActivitySend();
|
||||||
|
upfTFSend('0');
|
||||||
|
upfTFSend('7');
|
||||||
|
upfTFSend('30');
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (hasRoles(['student'])) return;
|
||||||
|
router.push({ name, query });
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fnGetSkim().then(() => {
|
||||||
|
loadData();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clearInterval(interval10s.value);
|
||||||
|
interval10s.value = null;
|
||||||
|
clearInterval(interval5s.value);
|
||||||
|
interval5s.value = null;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="viewport" ref="viewportDom">
|
||||||
|
<div class="brand">
|
||||||
|
<div
|
||||||
|
class="brand-title"
|
||||||
|
@click="toggle"
|
||||||
|
:title="t('views.dashboard.overview.fullscreen')"
|
||||||
|
>
|
||||||
|
{{ t('views.dashboard.overview.title') }}
|
||||||
|
<FullscreenExitOutlined v-if="isFullscreen" />
|
||||||
|
<FullscreenOutlined v-else />
|
||||||
|
</div>
|
||||||
|
<div class="brand-desc">{{ appStore.appName }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column">
|
||||||
|
<!--概览-->
|
||||||
|
<div class="skim panel">
|
||||||
|
<div class="inner">
|
||||||
|
<h3>
|
||||||
|
<IdcardOutlined style="color: #68d8fe" />
|
||||||
|
{{ 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].up }}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<span>
|
||||||
|
<ArrowDownOutlined style="color: #52c41a" />
|
||||||
|
{{ t('views.dashboard.overview.upfFlowTotal.down') }}
|
||||||
|
</span>
|
||||||
|
<h4>{{ upfTotalFlow[upfTFActive].down }}</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 告警统计 -->
|
||||||
|
<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>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import url('./css/index.css');
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,838 @@
|
|||||||
|
<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 {
|
||||||
|
delSMFDataCDR,
|
||||||
|
exportSMFDataCDR,
|
||||||
|
listSMFDataCDR,
|
||||||
|
} from '@/api/neData/smf';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import PQueue from 'p-queue';
|
||||||
|
import saveAs from 'file-saver';
|
||||||
|
import { hasRoles } from '@/plugins/auth-user';
|
||||||
|
const { t } = useI18n();
|
||||||
|
const ws = new WS();
|
||||||
|
const queue = new PQueue({ concurrency: 1, autoStart: true });
|
||||||
|
|
||||||
|
/**网元可选 */
|
||||||
|
let neOtions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
|
/**开始结束时间 */
|
||||||
|
let queryRangePicker = ref<[string, string]>(['', '']);
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元类型 */
|
||||||
|
neType: 'SMF',
|
||||||
|
neId: '001',
|
||||||
|
subscriberID: '',
|
||||||
|
sortField: 'timestamp',
|
||||||
|
sortOrder: 'desc',
|
||||||
|
/**开始时间 */
|
||||||
|
startTime: '',
|
||||||
|
/**结束时间 */
|
||||||
|
endTime: '',
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
queryParams = Object.assign(queryParams, {
|
||||||
|
subscriberID: '',
|
||||||
|
startTime: '',
|
||||||
|
endTime: '',
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
queryRangePicker.value = ['', ''];
|
||||||
|
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: 'center',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.smfChargingID'), // 计费ID
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.chargingID;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.smfSubscriptionIDType'), // 订阅 ID 类型
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.subscriberIdentifier?.subscriptionIDType;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.smfSubscriptionIDData'), // 订阅 ID 数据
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.subscriberIdentifier?.subscriptionIDData;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.smfDataVolumeUplink'), // 数据量上行链路
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
const listOfMultipleUnitUsage = cdrJSON.listOfMultipleUnitUsage;
|
||||||
|
if (
|
||||||
|
!Array.isArray(listOfMultipleUnitUsage) ||
|
||||||
|
listOfMultipleUnitUsage.length < 1
|
||||||
|
) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const usedUnitContainer = listOfMultipleUnitUsage[0].usedUnitContainer;
|
||||||
|
if (!Array.isArray(usedUnitContainer) || usedUnitContainer.length < 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return usedUnitContainer[0].dataVolumeUplink;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.smfDataVolumeDownlink'), // 数据量下行链路
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 180,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
const listOfMultipleUnitUsage = cdrJSON.listOfMultipleUnitUsage;
|
||||||
|
if (
|
||||||
|
!Array.isArray(listOfMultipleUnitUsage) ||
|
||||||
|
listOfMultipleUnitUsage.length < 1
|
||||||
|
) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const usedUnitContainer = listOfMultipleUnitUsage[0].usedUnitContainer;
|
||||||
|
if (!Array.isArray(usedUnitContainer) || usedUnitContainer.length < 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return usedUnitContainer[0].dataVolumeDownlink;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.smfDataTotalVolume'), // 数据总量
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
const listOfMultipleUnitUsage = cdrJSON.listOfMultipleUnitUsage;
|
||||||
|
if (
|
||||||
|
!Array.isArray(listOfMultipleUnitUsage) ||
|
||||||
|
listOfMultipleUnitUsage.length < 1
|
||||||
|
) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const usedUnitContainer = listOfMultipleUnitUsage[0].usedUnitContainer;
|
||||||
|
if (!Array.isArray(usedUnitContainer) || usedUnitContainer.length < 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return usedUnitContainer[0].dataTotalVolume;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.smfDuration'), // 持续时间
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.duration;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.smfInvocationTime'), // 调用时间
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 200,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.invocationTimestamp;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
delSMFDataCDR(id)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnGetList(1);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
if (!queryRangePicker.value) {
|
||||||
|
queryRangePicker.value = ['', ''];
|
||||||
|
}
|
||||||
|
queryParams.startTime = queryRangePicker.value[0];
|
||||||
|
queryParams.endTime = queryRangePicker.value[1];
|
||||||
|
listSMFDataCDR(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;
|
||||||
|
exportSMFDataCDR(querys)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
saveAs(res.data, `smf_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会话事件-SMF (GroupID:1006)
|
||||||
|
*/
|
||||||
|
subGroupID: `1006_${queryParams.neId}`,
|
||||||
|
},
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: wsError,
|
||||||
|
};
|
||||||
|
ws.connect(options);
|
||||||
|
} else {
|
||||||
|
ws.close();
|
||||||
|
tableState.seached = true;
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsError(ev: any) {
|
||||||
|
// 接收数据后回调
|
||||||
|
console.error(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsMessage(res: Record<string, any>) {
|
||||||
|
const { code, requestId, data } = res;
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅组信息
|
||||||
|
if (!data?.groupId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// cdrEvent CDR会话事件
|
||||||
|
if (data.groupId === `1006_${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 === 'SMF') {
|
||||||
|
arr.push({ value: i.neId, label: i.neName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neOtions.value = arr;
|
||||||
|
if (arr.length > 0) {
|
||||||
|
queryParams.neId = arr[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// 获取列表数据
|
||||||
|
fnGetList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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="SMF" name="neId ">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.neId"
|
||||||
|
:options="neOtions"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.smfSubscriptionIDData')"
|
||||||
|
name="subscriberID"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.subscriberID"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="40"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.time')"
|
||||||
|
name="queryRangePicker"
|
||||||
|
>
|
||||||
|
<a-range-picker
|
||||||
|
v-model:value="queryRangePicker"
|
||||||
|
allow-clear
|
||||||
|
bordered
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
value-format="x"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-range-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="4" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="fnQueryReset">
|
||||||
|
<template #icon><ClearOutlined /></template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-popconfirm
|
||||||
|
placement="bottomLeft"
|
||||||
|
:title="
|
||||||
|
!realTimeData
|
||||||
|
? t('views.dashboard.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-if="!hasRoles(['student'])"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
{{ t('common.deleteText') }}
|
||||||
|
</a-button>
|
||||||
|
|
||||||
|
<a-button type="dashed" @click.prevent="fnExportList()">
|
||||||
|
<template #icon><ExportOutlined /></template>
|
||||||
|
{{ t('common.export') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.searchBarText') }}</template>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="tableState.seached"
|
||||||
|
:checked-children="t('common.switch.show')"
|
||||||
|
:un-checked-children="t('common.switch.hide')"
|
||||||
|
size="small"
|
||||||
|
:disabled="realTimeData"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.sizeText') }}</template>
|
||||||
|
<a-dropdown trigger="click" placement="bottomRight">
|
||||||
|
<a-button type="text">
|
||||||
|
<template #icon><ColumnHeightOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu
|
||||||
|
:selected-keys="[tableState.size as string]"
|
||||||
|
@click="fnTableSize"
|
||||||
|
>
|
||||||
|
<a-menu-item key="default">
|
||||||
|
{{ t('common.size.default') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="middle">
|
||||||
|
{{ t('common.size.middle') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="small">
|
||||||
|
{{ t('common.size.small') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="id"
|
||||||
|
:columns="hasRoles(['student']) ? tableColumns.filter((s:any)=>s.key !== 'id'): tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: tableColumns.length * 150, y: 'calc(100vh - 480px)' }"
|
||||||
|
:row-selection="{
|
||||||
|
type: 'checkbox',
|
||||||
|
columnWidth: '48px',
|
||||||
|
selectedRowKeys: tableState.selectedRowKeys,
|
||||||
|
onChange: fnTableSelectedRowKeys,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'id'">
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordDelete(record.id)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<DeleteOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template #expandedRowRender="{ record }">
|
||||||
|
<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.invocationTimestamp }}</span>
|
||||||
|
</div>
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.dashboard.cdr.rowInfo') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>Record Network Function ID: </span>
|
||||||
|
<span>{{ record.cdrJSON.recordingNetworkFunctionID }}</span>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
<a-divider orientation="left"> Subscriber Identifier </a-divider>
|
||||||
|
<div>
|
||||||
|
<span>Subscription ID Type: </span>
|
||||||
|
<span>
|
||||||
|
{{ record.cdrJSON.subscriberIdentifier?.subscriptionIDType }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Subscription ID Data: </span>
|
||||||
|
<span>
|
||||||
|
{{ record.cdrJSON.subscriberIdentifier?.subscriptionIDData }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
|
<a-divider orientation="left">
|
||||||
|
List Of Multiple Unit Usage
|
||||||
|
</a-divider>
|
||||||
|
<div v-for="u in record.cdrJSON.listOfMultipleUnitUsage">
|
||||||
|
<div>RatingGroup: {{ u.ratingGroup }}</div>
|
||||||
|
<div v-for="udata in u.usedUnitContainer">
|
||||||
|
<div>
|
||||||
|
<span>Data Total Volume: </span>
|
||||||
|
<span>{{ udata.dataTotalVolume }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Data Volume Downlink: </span>
|
||||||
|
<span>{{ udata.dataVolumeDownlink }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Data Volume Uplink: </span>
|
||||||
|
<span>{{ udata.dataVolumeUplink }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Time: </span>
|
||||||
|
<span>{{ udata.time }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-divider orientation="left">
|
||||||
|
PDU Session Charging Information
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>User Identifier: </span>
|
||||||
|
<span>{{
|
||||||
|
record.cdrJSON.pDUSessionChargingInformation?.userIdentifier
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>SSC Mode: </span>
|
||||||
|
<span>{{
|
||||||
|
record.cdrJSON.pDUSessionChargingInformation?.sSCMode
|
||||||
|
}}</span>
|
||||||
|
|
||||||
|
<span>RAT Type: </span>
|
||||||
|
<span>{{
|
||||||
|
record.cdrJSON.pDUSessionChargingInformation?.rATType
|
||||||
|
}}</span>
|
||||||
|
|
||||||
|
<span>DNN ID: </span>
|
||||||
|
<span>
|
||||||
|
{{ record.cdrJSON.pDUSessionChargingInformation?.dNNID }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>PDU Type: </span>
|
||||||
|
<span>
|
||||||
|
{{ record.cdrJSON.pDUSessionChargingInformation?.pDUType }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>PDU IPv4 Address: </span>
|
||||||
|
<span>
|
||||||
|
{{
|
||||||
|
record.cdrJSON.pDUSessionChargingInformation?.pDUAddress
|
||||||
|
?.pDUIPv4Address
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>PDU IPv6 Addres Swith Prefix: </span>
|
||||||
|
<span>
|
||||||
|
{{
|
||||||
|
record.cdrJSON.pDUSessionChargingInformation?.pDUAddress
|
||||||
|
?.pDUIPv6AddresswithPrefix
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Network Function IPv4: </span>
|
||||||
|
<span>
|
||||||
|
{{
|
||||||
|
record.cdrJSON.nFunctionConsumerInformation
|
||||||
|
?.networkFunctionIPv4Address
|
||||||
|
}}
|
||||||
|
</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>
|
||||||
@@ -0,0 +1,770 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, toRaw, ref, onBeforeUnmount } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { message, Modal } from 'ant-design-vue/es';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
|
import useDictStore from '@/store/modules/dict';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import {
|
||||||
|
RESULT_CODE_ERROR,
|
||||||
|
RESULT_CODE_SUCCESS,
|
||||||
|
} from '@/constants/result-constants';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import {
|
||||||
|
delSMSCDataCDR,
|
||||||
|
exportSMSCDataCDR,
|
||||||
|
listSMSCDataCDR,
|
||||||
|
} from '@/api/neData/smsc';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import saveAs from 'file-saver';
|
||||||
|
import PQueue from 'p-queue';
|
||||||
|
import { hasRoles } from '@/plugins/auth-user';
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const ws = new WS();
|
||||||
|
const queue = new PQueue({ concurrency: 1, autoStart: true });
|
||||||
|
|
||||||
|
/**字典数据 */
|
||||||
|
let dict: {
|
||||||
|
/**CDR 响应原因代码类别类型 */
|
||||||
|
cdrCauseCode: DictType[];
|
||||||
|
} = reactive({
|
||||||
|
cdrCauseCode: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**网元可选 */
|
||||||
|
let neOtions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
|
/**开始结束时间 */
|
||||||
|
let queryRangePicker = ref<[string, string]>(['', '']);
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元类型 */
|
||||||
|
neType: 'SMSC',
|
||||||
|
neId: '001',
|
||||||
|
recordType: '',
|
||||||
|
callerParty: '',
|
||||||
|
calledParty: '',
|
||||||
|
sortField: 'timestamp',
|
||||||
|
sortOrder: 'desc',
|
||||||
|
/**开始时间 */
|
||||||
|
startTime: '',
|
||||||
|
/**结束时间 */
|
||||||
|
endTime: '',
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
recordTypes.value = [];
|
||||||
|
queryParams = Object.assign(queryParams, {
|
||||||
|
recordType: '',
|
||||||
|
callerParty: '',
|
||||||
|
calledParty: '',
|
||||||
|
startTime: '',
|
||||||
|
endTime: '',
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
queryRangePicker.value = ['', ''];
|
||||||
|
tablePagination.current = 1;
|
||||||
|
tablePagination.pageSize = 20;
|
||||||
|
fnGetList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**记录类型 */
|
||||||
|
const recordTypes = ref<string[]>([]);
|
||||||
|
|
||||||
|
/**查询记录类型变更 */
|
||||||
|
function fnQueryRecordTypeChange(value: any) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
queryParams.recordType = value.join(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**搜索栏 */
|
||||||
|
seached: boolean;
|
||||||
|
/**记录数据 */
|
||||||
|
data: object[];
|
||||||
|
/**勾选记录 */
|
||||||
|
selectedRowKeys: (string | number)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
size: 'middle',
|
||||||
|
seached: true,
|
||||||
|
data: [],
|
||||||
|
selectedRowKeys: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
let tableColumns: ColumnsType = [
|
||||||
|
{
|
||||||
|
title: t('common.rowId'),
|
||||||
|
dataIndex: 'id',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.recordType'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.recordType;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.type'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.serviceType;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.caller'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
key: 'callerParty',
|
||||||
|
align: 'left',
|
||||||
|
width: 120,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.callerParty;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.called'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
key: 'calledParty',
|
||||||
|
align: 'left',
|
||||||
|
width: 120,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.calledParty;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.result'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
key: 'cause',
|
||||||
|
align: 'left',
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.time'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
if (typeof cdrJSON.updateTime === 'number') {
|
||||||
|
return parseDateToStr(+cdrJSON.updateTime * 1000);
|
||||||
|
}
|
||||||
|
return cdrJSON.updateTime;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('common.operate'),
|
||||||
|
key: 'id',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
let tablePagination = reactive({
|
||||||
|
/**当前页数 */
|
||||||
|
current: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
/**默认的每页条数 */
|
||||||
|
defaultPageSize: 20,
|
||||||
|
/**指定每页可以显示多少条 */
|
||||||
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
|
/**只有一页时是否隐藏分页器 */
|
||||||
|
hideOnSinglePage: false,
|
||||||
|
/**是否可以快速跳转至某页 */
|
||||||
|
showQuickJumper: true,
|
||||||
|
/**是否可以改变 pageSize */
|
||||||
|
showSizeChanger: true,
|
||||||
|
/**数据总数 */
|
||||||
|
total: 0,
|
||||||
|
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
|
||||||
|
onChange: (page: number, pageSize: number) => {
|
||||||
|
tablePagination.current = page;
|
||||||
|
tablePagination.pageSize = pageSize;
|
||||||
|
queryParams.pageNum = page;
|
||||||
|
queryParams.pageSize = pageSize;
|
||||||
|
fnGetList();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格紧凑型变更操作 */
|
||||||
|
function fnTableSize({ key }: MenuInfo) {
|
||||||
|
tableState.size = key as SizeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格多选 */
|
||||||
|
function fnTableSelectedRowKeys(keys: (string | number)[]) {
|
||||||
|
tableState.selectedRowKeys = keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
/**最大ID值 */
|
||||||
|
maxId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
confirmLoading: false,
|
||||||
|
maxId: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录删除
|
||||||
|
* @param id 编号
|
||||||
|
*/
|
||||||
|
function fnRecordDelete(id: string) {
|
||||||
|
if (!id || modalState.confirmLoading) return;
|
||||||
|
let msg = id;
|
||||||
|
if (id === '0') {
|
||||||
|
msg = `${id}... ${tableState.selectedRowKeys.length}`;
|
||||||
|
id = tableState.selectedRowKeys.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.dashboard.cdr.delTip', { msg }),
|
||||||
|
onOk() {
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
delSMSCDataCDR(id)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnGetList(1);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
if (!queryRangePicker.value) {
|
||||||
|
queryRangePicker.value = ['', ''];
|
||||||
|
}
|
||||||
|
queryParams.startTime = queryRangePicker.value[0];
|
||||||
|
queryParams.endTime = queryRangePicker.value[1];
|
||||||
|
listSMSCDataCDR(toRaw(queryParams)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
|
// 取消勾选
|
||||||
|
if (tableState.selectedRowKeys.length > 0) {
|
||||||
|
tableState.selectedRowKeys = [];
|
||||||
|
}
|
||||||
|
tablePagination.total = res.total;
|
||||||
|
// 遍历处理cdr字符串数据
|
||||||
|
tableState.data = res.rows.map(item => {
|
||||||
|
let cdrJSON = item.cdrJSON;
|
||||||
|
if (!cdrJSON) {
|
||||||
|
Reflect.set(item, 'cdrJSON', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
cdrJSON = JSON.parse(cdrJSON);
|
||||||
|
Reflect.set(item, 'cdrJSON', cdrJSON);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
Reflect.set(item, 'cdrJSON', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 取最大值ID用作实时累加
|
||||||
|
if (res.total > 0) {
|
||||||
|
modalState.maxId = Number(res.rows[0].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**列表导出 */
|
||||||
|
function fnExportList() {
|
||||||
|
if (modalState.confirmLoading) return;
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.dashboard.cdr.exportTip'),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
const querys = toRaw(queryParams);
|
||||||
|
querys.pageSize = 10000;
|
||||||
|
exportSMSCDataCDR(querys)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
saveAs(res.data, `smsc_cdr_event_export_${Date.now()}.xlsx`);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**实时数据开关 */
|
||||||
|
const realTimeData = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实时数据
|
||||||
|
*/
|
||||||
|
function fnRealTime() {
|
||||||
|
realTimeData.value = !realTimeData.value;
|
||||||
|
if (realTimeData.value) {
|
||||||
|
tableState.seached = false;
|
||||||
|
// 建立链接
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: '/ws',
|
||||||
|
params: {
|
||||||
|
/**订阅通道组
|
||||||
|
*
|
||||||
|
* SMSC_CDR会话事件(GroupID:1007_neId)
|
||||||
|
*/
|
||||||
|
subGroupID: `1007_${queryParams.neId}`,
|
||||||
|
},
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: wsError,
|
||||||
|
};
|
||||||
|
ws.connect(options);
|
||||||
|
} else {
|
||||||
|
ws.close();
|
||||||
|
tableState.seached = true;
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsError(ev: any) {
|
||||||
|
// 接收数据后回调
|
||||||
|
console.error(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsMessage(res: Record<string, any>) {
|
||||||
|
const { code, requestId, data } = res;
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅组信息
|
||||||
|
if (!data?.groupId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// cdrEvent CDR会话事件
|
||||||
|
if (data.groupId === `1007_${queryParams.neId}`) {
|
||||||
|
const cdrEvent = data.data;
|
||||||
|
queue.add(async () => {
|
||||||
|
modalState.maxId += 1;
|
||||||
|
tableState.data.unshift({
|
||||||
|
id: modalState.maxId,
|
||||||
|
neType: cdrEvent.neType,
|
||||||
|
neName: cdrEvent.neName,
|
||||||
|
rmUID: cdrEvent.rmUID,
|
||||||
|
timestamp: cdrEvent.timestamp,
|
||||||
|
cdrJSON: cdrEvent.CDR,
|
||||||
|
});
|
||||||
|
tablePagination.total += 1;
|
||||||
|
if (tableState.data.length > 100) {
|
||||||
|
tableState.data.pop();
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 800));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始字典数据
|
||||||
|
Promise.allSettled([getDict('cdr_cause_code')]).then(resArr => {
|
||||||
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
dict.cdrCauseCode = resArr[0].value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 获取网元网元列表
|
||||||
|
useNeInfoStore()
|
||||||
|
.fnNelist()
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length > 0) {
|
||||||
|
let arr: Record<string, any>[] = [];
|
||||||
|
res.data.forEach(i => {
|
||||||
|
if (i.neType === 'SMSC') {
|
||||||
|
arr.push({ value: i.neId, label: i.neName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neOtions.value = arr;
|
||||||
|
if (arr.length > 0) {
|
||||||
|
queryParams.neId = arr[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// 获取列表数据
|
||||||
|
fnGetList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (ws.state() !== -1) {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card
|
||||||
|
v-show="tableState.seached"
|
||||||
|
:bordered="false"
|
||||||
|
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
||||||
|
>
|
||||||
|
<!-- 表格搜索栏 -->
|
||||||
|
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item label="SMSC" name="neId ">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.neId"
|
||||||
|
:options="neOtions"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.called')"
|
||||||
|
name="calledParty"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.calledParty"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.caller')"
|
||||||
|
name="callerParty "
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.callerParty"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="4" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="fnQueryReset">
|
||||||
|
<template #icon><ClearOutlined /></template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.recordType')"
|
||||||
|
name="recordType"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="recordTypes"
|
||||||
|
mode="multiple"
|
||||||
|
:options="['MOSM', 'MTSM'].map(v => ({ value: v }))"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnQueryRecordTypeChange"
|
||||||
|
></a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.time')"
|
||||||
|
name="queryRangePicker"
|
||||||
|
>
|
||||||
|
<a-range-picker
|
||||||
|
v-model:value="queryRangePicker"
|
||||||
|
allow-clear
|
||||||
|
bordered
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
value-format="x"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-range-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-popconfirm
|
||||||
|
placement="bottomLeft"
|
||||||
|
:title="
|
||||||
|
!realTimeData
|
||||||
|
? t('views.dashboard.cdr.realTimeDataStart')
|
||||||
|
: t('views.dashboard.cdr.realTimeDataStop')
|
||||||
|
"
|
||||||
|
ok-text="Yes"
|
||||||
|
cancel-text="No"
|
||||||
|
@confirm="fnRealTime()"
|
||||||
|
>
|
||||||
|
<a-button type="primary" :danger="realTimeData">
|
||||||
|
<template #icon><FundOutlined /> </template>
|
||||||
|
{{
|
||||||
|
!realTimeData
|
||||||
|
? t('views.dashboard.cdr.realTimeDataStart')
|
||||||
|
: t('views.dashboard.cdr.realTimeDataStop')
|
||||||
|
}}
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
danger
|
||||||
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
|
:loading="modalState.confirmLoading"
|
||||||
|
@click.prevent="fnRecordDelete('0')"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
{{ t('common.deleteText') }}
|
||||||
|
</a-button>
|
||||||
|
|
||||||
|
<a-button type="dashed" @click.prevent="fnExportList()">
|
||||||
|
<template #icon><ExportOutlined /></template>
|
||||||
|
{{ t('common.export') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.searchBarText') }}</template>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="tableState.seached"
|
||||||
|
:checked-children="t('common.switch.show')"
|
||||||
|
:un-checked-children="t('common.switch.hide')"
|
||||||
|
size="small"
|
||||||
|
:disabled="realTimeData"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.sizeText') }}</template>
|
||||||
|
<a-dropdown trigger="click" placement="bottomRight">
|
||||||
|
<a-button type="text">
|
||||||
|
<template #icon><ColumnHeightOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu
|
||||||
|
:selected-keys="[tableState.size as string]"
|
||||||
|
@click="fnTableSize"
|
||||||
|
>
|
||||||
|
<a-menu-item key="default">
|
||||||
|
{{ t('common.size.default') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="middle">
|
||||||
|
{{ t('common.size.middle') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="small">
|
||||||
|
{{ t('common.size.small') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="id"
|
||||||
|
:columns="hasRoles(['student']) ? tableColumns.filter((s:any)=>s.key !== 'id'): tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: tableColumns.length * 150, y: 'calc(100vh - 480px)' }"
|
||||||
|
:row-selection="{
|
||||||
|
type: 'checkbox',
|
||||||
|
columnWidth: '48px',
|
||||||
|
selectedRowKeys: tableState.selectedRowKeys,
|
||||||
|
onChange: fnTableSelectedRowKeys,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'cause'">
|
||||||
|
<span v-if="record.cdrJSON.result === 0">
|
||||||
|
{{ t('views.dashboard.cdr.resultFail') }},
|
||||||
|
<DictTag
|
||||||
|
:options="dict.cdrCauseCode"
|
||||||
|
:value="record.cdrJSON.cause"
|
||||||
|
value-default="0"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ t('views.dashboard.cdr.resultOk') }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'id'">
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordDelete(record.id)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<DeleteOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template #expandedRowRender="{ record }">
|
||||||
|
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.dashboard.cdr.cdrInfo') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.neName') }}: </span>
|
||||||
|
<span>{{ record.neName }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.rmUid') }}: </span>
|
||||||
|
<span>{{ record.rmUID }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.time') }}: </span>
|
||||||
|
<span>
|
||||||
|
{{
|
||||||
|
typeof record.cdrJSON.updateTime === 'number'
|
||||||
|
? parseDateToStr(+record.cdrJSON.updateTime * 1000)
|
||||||
|
: record.cdrJSON.updateTime
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.dashboard.cdr.rowInfo') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.type') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.serviceType }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.caller') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.callerParty }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.called') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.calledParty }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.result') }}: </span>
|
||||||
|
<span v-if="record.cdrJSON.result === 0">
|
||||||
|
{{ t('views.dashboard.cdr.resultFail') }},
|
||||||
|
<DictTag
|
||||||
|
:options="dict.cdrCauseCode"
|
||||||
|
:value="record.cdrJSON.cause"
|
||||||
|
value-default="0"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ t('views.dashboard.cdr.resultOk') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,554 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, ref, onBeforeUnmount } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import {
|
||||||
|
RESULT_CODE_ERROR,
|
||||||
|
RESULT_CODE_SUCCESS,
|
||||||
|
} from '@/constants/result-constants';
|
||||||
|
import { listAllNeInfo } from '@/api/ne/neInfo';
|
||||||
|
import { message } from 'ant-design-vue/es';
|
||||||
|
import { getGraphData } from '@/api/monitor/topology';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { Graph, GraphData, Menu, Tooltip } from '@antv/g6';
|
||||||
|
import {
|
||||||
|
edgeCubicAnimateCircleMove,
|
||||||
|
edgeCubicAnimateLineDash,
|
||||||
|
edgeLineAnimateState,
|
||||||
|
} from '../topologyBuild/hooks/registerEdge';
|
||||||
|
import {
|
||||||
|
nodeCircleAnimateShapeR,
|
||||||
|
nodeCircleAnimateShapeStroke,
|
||||||
|
nodeImageAnimateState,
|
||||||
|
nodeRectAnimateState,
|
||||||
|
} from '../topologyBuild/hooks/registerNode';
|
||||||
|
import useNeOptions from '@/views/ne/neInfo/hooks/useNeOptions';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import { hasRoles } from '@/plugins/auth-user';
|
||||||
|
import { parseBasePath } from '@/plugins/file-static-url';
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { fnNeRestart, fnNeStop, fnNeLogFile } = useNeOptions();
|
||||||
|
const ws = new WS();
|
||||||
|
|
||||||
|
/**图DOM节点实例对象 */
|
||||||
|
const graphG6Dom = ref<HTMLElement | undefined>(undefined);
|
||||||
|
|
||||||
|
/**图状态 */
|
||||||
|
const graphState = reactive<Record<string, any>>({
|
||||||
|
/**当前图组名 */
|
||||||
|
group: '5GC System Architecture',
|
||||||
|
/**图数据 */
|
||||||
|
data: {
|
||||||
|
combos: [],
|
||||||
|
edges: [],
|
||||||
|
nodes: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**非网元元素 */
|
||||||
|
const notNeNodes = [
|
||||||
|
'5GC',
|
||||||
|
'DN',
|
||||||
|
'UE',
|
||||||
|
'Base',
|
||||||
|
'lan',
|
||||||
|
'lan1',
|
||||||
|
'lan2',
|
||||||
|
'lan3',
|
||||||
|
'lan4',
|
||||||
|
'lan5',
|
||||||
|
'lan6',
|
||||||
|
'lan7',
|
||||||
|
'LAN',
|
||||||
|
'NR',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**图实例对象 */
|
||||||
|
const graphG6 = ref<any>(null);
|
||||||
|
|
||||||
|
/**图节点右击菜单 */
|
||||||
|
const graphNodeMenu = new Menu({
|
||||||
|
offsetX: 6,
|
||||||
|
offseY: 10,
|
||||||
|
itemTypes: ['node'],
|
||||||
|
getContent(evt) {
|
||||||
|
if (!evt) return t('views.monitor.topologyBuild.graphNotInfo');
|
||||||
|
const { id, label, neState }: any = evt.item?.getModel();
|
||||||
|
if (notNeNodes.includes(id)) {
|
||||||
|
return `<div><span>${label || id}</span></div>`;
|
||||||
|
}
|
||||||
|
if (!neState) {
|
||||||
|
return `<div><span>${label || id}</span></div>`;
|
||||||
|
}
|
||||||
|
if (hasRoles(['student'])) {
|
||||||
|
return 'Student';
|
||||||
|
}
|
||||||
|
return `
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 140px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<h3 style="margin-bottom: 8px">
|
||||||
|
${t('views.monitor.topology.name')}:
|
||||||
|
${neState.neName ?? '--'}
|
||||||
|
</h3>
|
||||||
|
<div id="restart" style="cursor: pointer; margin-bottom: 4px">
|
||||||
|
> ${t('views.ne.common.restart')}
|
||||||
|
</div>
|
||||||
|
<div id="stop" style="cursor: pointer; margin-bottom: 4px;">
|
||||||
|
> ${t('views.ne.common.stop')}
|
||||||
|
</div>
|
||||||
|
<div id="log" style="cursor: pointer; margin-bottom: 4px;">
|
||||||
|
> ${t('views.ne.common.log')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
handleMenuClick(target, item) {
|
||||||
|
const { neInfo }: any = item?.getModel();
|
||||||
|
const { neName, neType, neId } = neInfo;
|
||||||
|
const targetId = target.id;
|
||||||
|
switch (targetId) {
|
||||||
|
case 'restart':
|
||||||
|
fnNeRestart({ neName, neType, neId });
|
||||||
|
break;
|
||||||
|
case 'stop':
|
||||||
|
fnNeStop({ neName, neType, neId });
|
||||||
|
break;
|
||||||
|
case 'log':
|
||||||
|
fnNeLogFile({ neType, neId });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**图节点展示 */
|
||||||
|
const graphNodeTooltip = new Tooltip({
|
||||||
|
offsetX: 10,
|
||||||
|
offsetY: 20,
|
||||||
|
getContent(evt) {
|
||||||
|
if (!evt) return t('views.monitor.topologyBuild.graphNotInfo');
|
||||||
|
const { id, label, neState }: any = evt.item?.getModel();
|
||||||
|
if (notNeNodes.includes(id)) {
|
||||||
|
return `<div><span>${label || id}</span></div>`;
|
||||||
|
}
|
||||||
|
if (!neState) {
|
||||||
|
return `<div><span>${label || id}</span></div>`;
|
||||||
|
}
|
||||||
|
let notStudentInfo = '';
|
||||||
|
if (hasRoles(['teacher', 'admin'])) {
|
||||||
|
notStudentInfo = `
|
||||||
|
<div><strong>${t('views.monitor.topology.serialNum')}:</strong><span>
|
||||||
|
${neState.sn ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>${t('views.monitor.topology.expiryDate')}:</strong><span>
|
||||||
|
${neState.expire ?? '--'}
|
||||||
|
</span></div> `;
|
||||||
|
}
|
||||||
|
return `
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 200px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div><strong>${t('views.monitor.topology.state')}:</strong><span>
|
||||||
|
${
|
||||||
|
neState.online
|
||||||
|
? t('views.monitor.topology.normalcy')
|
||||||
|
: t('views.monitor.topology.exceptions')
|
||||||
|
}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>${t('views.monitor.topology.refreshTime')}:</strong><span>
|
||||||
|
${neState.refreshTime ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div>========================</div>
|
||||||
|
<div><strong>ID:</strong><span>${neState.neId}</span></div>
|
||||||
|
<div><strong>${t('views.monitor.topology.name')}:</strong><span>
|
||||||
|
${neState.neName ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>IP:</strong><span>${neState.neIP}</span></div>
|
||||||
|
<div><strong>${t('views.monitor.topology.version')}:</strong><span>
|
||||||
|
${neState.version ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
${notStudentInfo}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
itemTypes: ['node'],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**注册自定义边或节点 */
|
||||||
|
function registerEdgeNode() {
|
||||||
|
// 边
|
||||||
|
edgeCubicAnimateLineDash();
|
||||||
|
edgeCubicAnimateCircleMove();
|
||||||
|
edgeLineAnimateState();
|
||||||
|
// 节点
|
||||||
|
nodeCircleAnimateShapeR();
|
||||||
|
nodeCircleAnimateShapeStroke();
|
||||||
|
nodeRectAnimateState();
|
||||||
|
nodeImageAnimateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**图数据渲染 */
|
||||||
|
function handleRanderGraph(
|
||||||
|
container: HTMLElement | undefined,
|
||||||
|
data: GraphData
|
||||||
|
) {
|
||||||
|
if (!container) return;
|
||||||
|
const { clientHeight, clientWidth } = container;
|
||||||
|
|
||||||
|
// 注册自定义边或节点
|
||||||
|
registerEdgeNode();
|
||||||
|
|
||||||
|
const graph = new Graph({
|
||||||
|
container: container,
|
||||||
|
width: clientWidth,
|
||||||
|
height: clientHeight,
|
||||||
|
fitCenter: true,
|
||||||
|
fitView: true,
|
||||||
|
fitViewPadding: [40],
|
||||||
|
autoPaint: true,
|
||||||
|
modes: {
|
||||||
|
default: [
|
||||||
|
'drag-combo',
|
||||||
|
'drag-canvas',
|
||||||
|
'zoom-canvas',
|
||||||
|
'collapse-expand-combo',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
groupByTypes: false,
|
||||||
|
nodeStateStyles: {
|
||||||
|
selected: {
|
||||||
|
fill: 'transparent',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [graphNodeMenu, graphNodeTooltip],
|
||||||
|
animate: true, // 是否使用动画过度,默认为 false
|
||||||
|
animateCfg: {
|
||||||
|
duration: 500, // Number,一次动画的时长
|
||||||
|
easing: 'linearEasing', // String,动画函数
|
||||||
|
},
|
||||||
|
});
|
||||||
|
graph.data(data);
|
||||||
|
graph.render();
|
||||||
|
|
||||||
|
graphG6.value = graph;
|
||||||
|
|
||||||
|
// 创建 ResizeObserver 实例
|
||||||
|
var observer = new ResizeObserver(function (entries) {
|
||||||
|
// 当元素大小发生变化时触发回调函数
|
||||||
|
entries.forEach(function (entry) {
|
||||||
|
if (!graphG6.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
graphG6.value.changeSize(
|
||||||
|
entry.contentRect.width,
|
||||||
|
entry.contentRect.height - 30
|
||||||
|
);
|
||||||
|
graphG6.value.fitCenter();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// 监听元素大小变化
|
||||||
|
observer.observe(container);
|
||||||
|
|
||||||
|
return graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图组数据渲染到画布
|
||||||
|
* @param reload 是否重载数据
|
||||||
|
*/
|
||||||
|
function fnGraphDataLoad(reload: boolean = false) {
|
||||||
|
Promise.all([
|
||||||
|
getGraphData(graphState.group),
|
||||||
|
listAllNeInfo({
|
||||||
|
bandStatus: false,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
.then(resArr => {
|
||||||
|
const graphRes = resArr[0];
|
||||||
|
const neRes = resArr[1];
|
||||||
|
if (
|
||||||
|
graphRes.code === RESULT_CODE_SUCCESS &&
|
||||||
|
Array.isArray(graphRes.data.nodes) &&
|
||||||
|
graphRes.data.nodes.length > 0 &&
|
||||||
|
neRes.code === RESULT_CODE_SUCCESS &&
|
||||||
|
Array.isArray(neRes.data) &&
|
||||||
|
neRes.data.length > 0
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
graphData: graphRes.data,
|
||||||
|
neList: neRes.data,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.monitor.topology.noData'),
|
||||||
|
duration: 5,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (!res) return;
|
||||||
|
const { combos, edges, nodes } = res.graphData;
|
||||||
|
|
||||||
|
// 节点过滤
|
||||||
|
const nf: Record<string, any>[] = nodes.filter(
|
||||||
|
(node: Record<string, any>) => {
|
||||||
|
Reflect.set(node, 'neState', { online: false });
|
||||||
|
// 图片路径处理
|
||||||
|
if (node.img) node.img = parseBasePath(node.img);
|
||||||
|
if (node.icon.show && node.icon?.img)
|
||||||
|
node.icon.img = parseBasePath(node.icon.img);
|
||||||
|
// 遍历是否有网元数据
|
||||||
|
const nodeID: string = node.id;
|
||||||
|
const hasNe = res.neList.some(ne => {
|
||||||
|
Reflect.set(node, 'neInfo', ne.neType === nodeID ? ne : {});
|
||||||
|
return ne.neType === nodeID;
|
||||||
|
});
|
||||||
|
if (hasNe) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (notNeNodes.includes(nodeID)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 边过滤
|
||||||
|
const ef: Record<string, any>[] = edges.filter(
|
||||||
|
(edge: Record<string, any>) => {
|
||||||
|
const edgeSource: string = edge.source;
|
||||||
|
const edgeTarget: string = edge.target;
|
||||||
|
const hasNeS = nf.some(n => n.id === edgeSource);
|
||||||
|
const hasNeT = nf.some(n => n.id === edgeTarget);
|
||||||
|
// console.log(hasNeS, edgeSource, hasNeT, edgeTarget);
|
||||||
|
if (hasNeS && hasNeT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hasNeS && notNeNodes.includes(edgeTarget)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (hasNeT && notNeNodes.includes(edgeSource)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 分组过滤
|
||||||
|
combos.forEach((combo: Record<string, any>) => {
|
||||||
|
const comboChildren: Record<string, any>[] = combo.children;
|
||||||
|
combo.children = comboChildren.filter(c => nf.some(n => n.id === c.id));
|
||||||
|
return combo;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 图数据
|
||||||
|
graphState.data = { combos, edges: ef, nodes: nf };
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
if (graphState.data.length < 0) return;
|
||||||
|
// 重载数据
|
||||||
|
if (reload) {
|
||||||
|
graphG6.value.read(graphState.data);
|
||||||
|
} else {
|
||||||
|
handleRanderGraph(graphG6Dom.value, graphState.data);
|
||||||
|
}
|
||||||
|
clearInterval(interval10s.value);
|
||||||
|
interval10s.value = null;
|
||||||
|
fnGetState();
|
||||||
|
interval10s.value = setInterval(async () => {
|
||||||
|
if (!interval10s.value) return;
|
||||||
|
fnGetState(); // 获取网元状态
|
||||||
|
}, 20_000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**网元状态调度器 */
|
||||||
|
const interval10s = ref<any>(null);
|
||||||
|
|
||||||
|
/**查询网元状态 */
|
||||||
|
function fnGetState() {
|
||||||
|
// 获取节点状态
|
||||||
|
for (const node of graphState.data.nodes) {
|
||||||
|
if (notNeNodes.includes(node.id)) continue;
|
||||||
|
const { neType, neId } = node.neInfo;
|
||||||
|
if (!neType || !neId) continue;
|
||||||
|
ws.send({
|
||||||
|
requestId: `${neType}_${neId}`,
|
||||||
|
type: 'ne_state',
|
||||||
|
data: {
|
||||||
|
neType: neType,
|
||||||
|
neId: neId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsError(ev: any) {
|
||||||
|
// 接收数据后回调
|
||||||
|
console.error(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsMessage(res: Record<string, any>) {
|
||||||
|
const { code, requestId, data } = res;
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!requestId) return;
|
||||||
|
const [neType, neId] = requestId.split('_');
|
||||||
|
const { combos, edges, nodes } = graphState.data;
|
||||||
|
const node = nodes.find((item: Record<string, any>) => item.id === neType);
|
||||||
|
|
||||||
|
// 更新网元状态
|
||||||
|
const newNeState = Object.assign(node.neState, data, {
|
||||||
|
refreshTime: parseDateToStr(data.refreshTime, 'HH:mm:ss'),
|
||||||
|
online: !!data.cpu,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 通过 ID 查询节点实例
|
||||||
|
const item = graphG6.value.findById(node.id);
|
||||||
|
if (item) {
|
||||||
|
const stateColor = newNeState.online ? '#52c41a' : '#f5222d'; // 状态颜色
|
||||||
|
// 图片类型不能填充
|
||||||
|
if (node.type.startsWith('image')) {
|
||||||
|
// 更新节点
|
||||||
|
if (node.label !== newNeState.neName) {
|
||||||
|
graphG6.value.updateItem(item, {
|
||||||
|
label: newNeState.neName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 设置状态
|
||||||
|
graphG6.value.setItemState(item, 'top-right-dot', stateColor);
|
||||||
|
} else {
|
||||||
|
// 更新节点
|
||||||
|
graphG6.value.updateItem(item, {
|
||||||
|
label: newNeState.neName,
|
||||||
|
// neState: newNeState,
|
||||||
|
style: {
|
||||||
|
fill: stateColor, // 填充色
|
||||||
|
stroke: stateColor, // 填充色
|
||||||
|
},
|
||||||
|
// labelCfg: {
|
||||||
|
// style: {
|
||||||
|
// fill: '#ffffff', // 标签文本色
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
});
|
||||||
|
// 设置状态
|
||||||
|
graphG6.value.setItemState(item, 'stroke', newNeState.online);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置边状态
|
||||||
|
for (const edge of edges) {
|
||||||
|
const edgeSource: string = edge.source;
|
||||||
|
const edgeTarget: string = edge.target;
|
||||||
|
const neS = nodes.find((n: any) => n.id === edgeSource);
|
||||||
|
const neT = nodes.find((n: any) => n.id === edgeTarget);
|
||||||
|
// console.log(neS, edgeSource, neT, edgeTarget);
|
||||||
|
|
||||||
|
if (neS && neT) {
|
||||||
|
// 通过 ID 查询节点实例
|
||||||
|
// const item = graphG6.value.findById(edge.id);
|
||||||
|
// console.log(
|
||||||
|
// `${edgeSource} - ${edgeTarget}`,
|
||||||
|
// neS.neState.online && neT.neState.online
|
||||||
|
// );
|
||||||
|
// const stateColor = neS.neState.online && neT.neState.online ? '#000000' : '#ff4d4f'; // 状态颜色
|
||||||
|
// 更新边
|
||||||
|
// graphG6.value.updateItem(item, {
|
||||||
|
// label: `${edgeSource} - ${edgeTarget}`,
|
||||||
|
// style: {
|
||||||
|
// stroke: stateColor, // 填充色
|
||||||
|
// },
|
||||||
|
// labelCfg: {
|
||||||
|
// style: {
|
||||||
|
// fill: '#ffffff', // 标签文本色
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// 设置状态
|
||||||
|
graphG6.value.setItemState(
|
||||||
|
edge.id,
|
||||||
|
'circle-move',
|
||||||
|
neS.neState.online && neT.neState.online
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (neS && notNeNodes.includes(edgeTarget)) {
|
||||||
|
graphG6.value.setItemState(edge.id, 'line-dash', neS.neState.online);
|
||||||
|
}
|
||||||
|
if (neT && notNeNodes.includes(edgeSource)) {
|
||||||
|
graphG6.value.setItemState(edge.id, 'line-dash', neT.neState.online);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fnGraphDataLoad(false);
|
||||||
|
|
||||||
|
// 建立链接
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: '/ws',
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: wsError,
|
||||||
|
};
|
||||||
|
ws.connect(options);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
ws.close();
|
||||||
|
clearInterval(interval10s.value);
|
||||||
|
interval10s.value = null;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card
|
||||||
|
:bordered="false"
|
||||||
|
:body-style="{ marginBottom: '24px' }"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<span>
|
||||||
|
{{ t('views.monitor.topologyBuild.graphGroup') }}:
|
||||||
|
{{ graphState.group }}
|
||||||
|
</span>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
size="small"
|
||||||
|
@click.prevent="fnGraphDataLoad(true)"
|
||||||
|
>
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
{{ t('common.reloadText') }}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div ref="graphG6Dom" class="chart"></div>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.chart {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 300px);
|
||||||
|
background-color: rgb(43, 47, 51);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,374 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, toRaw, watch } from 'vue';
|
||||||
|
import { ProModal } from 'antdv-pro-modal';
|
||||||
|
import { Form, Modal, Upload, message, notification } from 'ant-design-vue/es';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { UploadRequestOption } from 'ant-design-vue/es/vc-upload/interface';
|
||||||
|
import { FileType, UploadFile } from 'ant-design-vue/es/upload/interface';
|
||||||
|
import {
|
||||||
|
exportNeConfigBackup,
|
||||||
|
importNeConfigBackup,
|
||||||
|
listNeConfigBackup,
|
||||||
|
} from '@/api/ne/neConfigBackup';
|
||||||
|
import saveAs from 'file-saver';
|
||||||
|
import { uploadFile } from '@/api/tool/file';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
const { t } = useI18n();
|
||||||
|
const emit = defineEmits(['ok', 'cancel', 'update:open']);
|
||||||
|
const props = defineProps({
|
||||||
|
open: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
/**网元ID */
|
||||||
|
neId: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
neType: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**导入状态数据 */
|
||||||
|
const importState = reactive({
|
||||||
|
typeOption: [
|
||||||
|
{ label: t('views.ne.neInfo.backConf.server'), value: 'backup' },
|
||||||
|
{ label: t('views.ne.neInfo.backConf.local'), value: 'upload' },
|
||||||
|
],
|
||||||
|
backupData: <any[]>[],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询网元远程服务器备份文件 */
|
||||||
|
function backupSearch(name?: string) {
|
||||||
|
const { neType, neId } = modalState.from;
|
||||||
|
listNeConfigBackup({
|
||||||
|
neType,
|
||||||
|
neId,
|
||||||
|
name,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
}).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
|
importState.backupData = [];
|
||||||
|
res.rows.forEach((item: any) => {
|
||||||
|
importState.backupData.push({
|
||||||
|
label: item.name,
|
||||||
|
value: item.path,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**服务器备份文件选择切换 */
|
||||||
|
function backupChange(value: any) {
|
||||||
|
if (!value) {
|
||||||
|
backupSearch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**类型切换 */
|
||||||
|
function typeChange(value: any) {
|
||||||
|
modalState.from.path = undefined;
|
||||||
|
if (value === 'backup') {
|
||||||
|
backupSearch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**新增框或修改框是否显示 */
|
||||||
|
openByEdit: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**表单数据 */
|
||||||
|
from: {
|
||||||
|
neType: string;
|
||||||
|
neId: string;
|
||||||
|
type: 'upload' | 'backup';
|
||||||
|
path: string | undefined;
|
||||||
|
};
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
/**上传文件 */
|
||||||
|
uploadFiles: any[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
openByEdit: false,
|
||||||
|
title: '配置文件导入',
|
||||||
|
from: {
|
||||||
|
neType: '',
|
||||||
|
neId: '',
|
||||||
|
type: 'upload',
|
||||||
|
path: undefined,
|
||||||
|
},
|
||||||
|
confirmLoading: false,
|
||||||
|
uploadFiles: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**对话框内表单属性和校验规则 */
|
||||||
|
const modalStateFrom = Form.useForm(
|
||||||
|
modalState.from,
|
||||||
|
reactive({
|
||||||
|
path: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: t('views.ne.neInfo.backConf.pathPlease'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出确认执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalOk() {
|
||||||
|
if (modalState.confirmLoading) return;
|
||||||
|
const from = toRaw(modalState.from);
|
||||||
|
modalStateFrom
|
||||||
|
.validate()
|
||||||
|
.then(e => {
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
importNeConfigBackup(from)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success(t('common.operateOk'), 3);
|
||||||
|
// 返回无引用信息
|
||||||
|
emit('ok', JSON.parse(JSON.stringify(from)));
|
||||||
|
fnModalCancel();
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
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.confirmLoading = false;
|
||||||
|
modalStateFrom.resetFields();
|
||||||
|
modalState.uploadFiles = [];
|
||||||
|
emit('cancel');
|
||||||
|
emit('update:open', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表单上传前删除 */
|
||||||
|
function fnBeforeRemoveFile(file: UploadFile) {
|
||||||
|
modalState.from.path = undefined;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表单上传前检查或转换压缩 */
|
||||||
|
function fnBeforeUploadFile(file: FileType) {
|
||||||
|
if (modalState.confirmLoading) return false;
|
||||||
|
if (!file.name.endsWith('.zip')) {
|
||||||
|
const msg = `${t('components.UploadModal.onlyAllow')} .zip`;
|
||||||
|
message.error(msg, 3);
|
||||||
|
return Upload.LIST_IGNORE;
|
||||||
|
}
|
||||||
|
const isLt3M = file.size / 1024 / 1024 < 100;
|
||||||
|
if (!isLt3M) {
|
||||||
|
const msg = `${t('components.UploadModal.allowFilter')} 100MB`;
|
||||||
|
message.error(msg, 3);
|
||||||
|
return Upload.LIST_IGNORE;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表单上传文件 */
|
||||||
|
function fnUploadFile(up: UploadRequestOption) {
|
||||||
|
// 发送请求
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append('file', up.file);
|
||||||
|
formData.append('subPath', 'import');
|
||||||
|
uploadFile(formData)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
// 改为完成状态
|
||||||
|
const file = modalState.uploadFiles[0];
|
||||||
|
file.percent = 100;
|
||||||
|
file.status = 'done';
|
||||||
|
// 预置到表单
|
||||||
|
const { fileName } = res.data;
|
||||||
|
modalState.from.path = fileName;
|
||||||
|
} else {
|
||||||
|
message.error(res.msg, 3);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**监听是否显示,初始数据 */
|
||||||
|
watch(
|
||||||
|
() => props.open,
|
||||||
|
val => {
|
||||||
|
if (val) {
|
||||||
|
if (props.neType && props.neId) {
|
||||||
|
modalState.from.neType = props.neType;
|
||||||
|
modalState.from.neId = props.neId;
|
||||||
|
modalState.title = t('views.ne.neInfo.backConf.title');
|
||||||
|
modalState.openByEdit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元导出配置
|
||||||
|
* @param row 网元编号ID
|
||||||
|
*/
|
||||||
|
function fnExportConf(neType: string, neId: string) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.ne.neInfo.backConf.exportTip'),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
exportNeConfigBackup({ neType, neId })
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
notification.success({
|
||||||
|
message: t('common.tipTitle'),
|
||||||
|
description: t('views.ne.neInfo.backConf.exportMsg'),
|
||||||
|
});
|
||||||
|
saveAs(
|
||||||
|
res.data,
|
||||||
|
`${neType}_${neId}_config_backup_${Date.now()}.zip`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
message.error(`${res.msg}`, 3);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 给组件设置属性 ref="xxxBackConf"
|
||||||
|
// setup内使用 const xxxBackConf = ref();
|
||||||
|
defineExpose({
|
||||||
|
/**导出文件 */
|
||||||
|
exportConf: fnExportConf,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ProModal
|
||||||
|
:drag="true"
|
||||||
|
:width="800"
|
||||||
|
: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 }">
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.ne.common.neType')" name="neType">
|
||||||
|
{{ modalState.from.neType }}
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neInfo.backConf.importType')"
|
||||||
|
name="type"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="modalState.from.type"
|
||||||
|
default-value="server"
|
||||||
|
:options="importState.typeOption"
|
||||||
|
@change="typeChange"
|
||||||
|
>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.ne.common.neId')" name="neId">
|
||||||
|
{{ modalState.from.neId }}
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neInfo.backConf.server')"
|
||||||
|
name="fileName"
|
||||||
|
v-bind="modalStateFrom.validateInfos.path"
|
||||||
|
v-if="modalState.from.type === 'backup'"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="modalState.from.path"
|
||||||
|
:options="importState.backupData"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
:show-search="true"
|
||||||
|
:default-active-first-option="false"
|
||||||
|
:show-arrow="false"
|
||||||
|
:allow-clear="true"
|
||||||
|
:filter-option="false"
|
||||||
|
:not-found-content="null"
|
||||||
|
@search="backupSearch"
|
||||||
|
@change="backupChange"
|
||||||
|
>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neInfo.backConf.local')"
|
||||||
|
name="file"
|
||||||
|
v-bind="modalStateFrom.validateInfos.path"
|
||||||
|
v-if="modalState.from.type === 'upload'"
|
||||||
|
>
|
||||||
|
<a-upload
|
||||||
|
name="file"
|
||||||
|
v-model:file-list="modalState.uploadFiles"
|
||||||
|
accept=".zip"
|
||||||
|
list-type="text"
|
||||||
|
:max-count="1"
|
||||||
|
:show-upload-list="{
|
||||||
|
showPreviewIcon: false,
|
||||||
|
showRemoveIcon: true,
|
||||||
|
showDownloadIcon: false,
|
||||||
|
}"
|
||||||
|
:remove="fnBeforeRemoveFile"
|
||||||
|
:before-upload="fnBeforeUploadFile"
|
||||||
|
:custom-request="fnUploadFile"
|
||||||
|
:disabled="modalState.confirmLoading"
|
||||||
|
>
|
||||||
|
<a-button type="primary">
|
||||||
|
<template #icon>
|
||||||
|
<UploadOutlined />
|
||||||
|
</template>
|
||||||
|
{{ t('views.ne.neInfo.backConf.localUpload') }}
|
||||||
|
</a-button>
|
||||||
|
</a-upload>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</ProModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
||||||
@@ -0,0 +1,834 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, toRaw, watch } from 'vue';
|
||||||
|
import { ProModal } from 'antdv-pro-modal';
|
||||||
|
import { message, Form, Modal } from 'ant-design-vue/es';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { NE_TYPE_LIST } from '@/constants/ne-constants';
|
||||||
|
import { regExpIPv4, regExpIPv6 } from '@/utils/regular-utils';
|
||||||
|
import { getNeInfo, addNeInfo, updateNeInfo } from '@/api/ne/neInfo';
|
||||||
|
import { neHostAuthorizedRSA, testNeHost } from '@/api/ne/neHost';
|
||||||
|
import useDictStore from '@/store/modules/dict';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const emit = defineEmits(['ok', 'cancel', 'update:open']);
|
||||||
|
const props = defineProps({
|
||||||
|
open: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
editId: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**字典数据 */
|
||||||
|
let dict: {
|
||||||
|
/**主机类型 */
|
||||||
|
neHostType: DictType[];
|
||||||
|
/**分组 */
|
||||||
|
neHostGroupId: DictType[];
|
||||||
|
/**认证模式 */
|
||||||
|
neHostAuthMode: DictType[];
|
||||||
|
} = reactive({
|
||||||
|
neHostType: [],
|
||||||
|
neHostGroupId: [],
|
||||||
|
neHostAuthMode: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试主机连接
|
||||||
|
*/
|
||||||
|
function fnHostTest(row: Record<string, any>) {
|
||||||
|
if (modalState.confirmLoading || !row.addr || !row.port) return;
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
testNeHost(row)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: `${row.addr}:${row.port} ${t('views.ne.neHost.testOk')}`,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${row.addr}:${row.port} ${res.msg}`,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**测试主机连接-免密直连 */
|
||||||
|
function fnHostAuthorized(row: Record<string, any>) {
|
||||||
|
if (modalState.confirmLoading) return;
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.ne.neHost.authRSATip'),
|
||||||
|
onOk: () => {
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
neHostAuthorizedRSA(row).then(res => {
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success(t('common.operateOk'), 3);
|
||||||
|
} else {
|
||||||
|
message.error(t('common.operateErr'), 3);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**新增框或修改框是否显示 */
|
||||||
|
openByEdit: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**表单数据 */
|
||||||
|
from: Record<string, any>;
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
openByEdit: false,
|
||||||
|
title: '网元',
|
||||||
|
from: {
|
||||||
|
id: undefined,
|
||||||
|
neId: '001',
|
||||||
|
neType: 'AMF',
|
||||||
|
neName: '',
|
||||||
|
ip: '',
|
||||||
|
port: 33030,
|
||||||
|
pvFlag: 'PNF',
|
||||||
|
rmUid: '4400HXAMF001',
|
||||||
|
neAddress: '',
|
||||||
|
dn: '',
|
||||||
|
vendorName: '',
|
||||||
|
province: '',
|
||||||
|
remark: '',
|
||||||
|
// 主机
|
||||||
|
hosts: [
|
||||||
|
{
|
||||||
|
hostId: undefined,
|
||||||
|
hostType: 'ssh',
|
||||||
|
groupId: '1',
|
||||||
|
title: 'SSH_NE_22',
|
||||||
|
addr: '',
|
||||||
|
port: 22,
|
||||||
|
user: 'omcuser',
|
||||||
|
authMode: '2',
|
||||||
|
password: '',
|
||||||
|
privateKey: '',
|
||||||
|
passPhrase: '',
|
||||||
|
remark: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
hostId: undefined,
|
||||||
|
hostType: 'telnet',
|
||||||
|
groupId: '1',
|
||||||
|
title: 'Telnet_NE_4100',
|
||||||
|
addr: '',
|
||||||
|
port: 4100,
|
||||||
|
user: 'admin',
|
||||||
|
authMode: '0',
|
||||||
|
password: 'admin',
|
||||||
|
remark: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
confirmLoading: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**对话框内表单属性和校验规则 */
|
||||||
|
const modalStateFrom = Form.useForm(
|
||||||
|
modalState.from,
|
||||||
|
reactive({
|
||||||
|
neType: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: t('views.ne.common.neTypePlease'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
neId: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: t('views.ne.common.neIdPlease'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rmUid: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: t('views.ne.common.rmUidPlease'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
ip: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
validator: modalStateFromEqualIPV4AndIPV6,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
neName: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: t('views.ne.common.neNamePlease'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**表单验证IP地址是否有效 */
|
||||||
|
function modalStateFromEqualIPV4AndIPV6(
|
||||||
|
rule: Record<string, any>,
|
||||||
|
value: string,
|
||||||
|
callback: (error?: string) => void
|
||||||
|
) {
|
||||||
|
if (!value) {
|
||||||
|
return Promise.reject(t('views.ne.common.ipAddrPlease'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.indexOf('.') === -1 && value.indexOf(':') === -1) {
|
||||||
|
return Promise.reject(t('valid.ipPlease'));
|
||||||
|
}
|
||||||
|
if (value.indexOf('.') !== -1 && !regExpIPv4.test(value)) {
|
||||||
|
return Promise.reject(t('valid.ipv4Reg'));
|
||||||
|
}
|
||||||
|
if (value.indexOf(':') !== -1 && !regExpIPv6.test(value)) {
|
||||||
|
return Promise.reject(t('valid.ipv6Reg'));
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出显示为 新增或者修改
|
||||||
|
* @param editId 网元id, 不传为新增
|
||||||
|
*/
|
||||||
|
function fnModalVisibleByEdit(editId: string) {
|
||||||
|
if (!editId) {
|
||||||
|
modalStateFrom.resetFields();
|
||||||
|
modalState.title = t('views.ne.neInfo.addTitle');
|
||||||
|
modalState.openByEdit = true;
|
||||||
|
} else {
|
||||||
|
if (modalState.confirmLoading) return;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
getNeInfo(editId).then(res => {
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
hide();
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
Object.assign(modalState.from, res.data);
|
||||||
|
modalState.title = t('views.ne.neInfo.editTitle');
|
||||||
|
modalState.openByEdit = true;
|
||||||
|
} else {
|
||||||
|
message.error(t('common.getInfoFail'), 2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出确认执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalOk() {
|
||||||
|
modalStateFrom
|
||||||
|
.validate()
|
||||||
|
.then(e => {
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const from = toRaw(modalState.from);
|
||||||
|
const result = from.id ? updateNeInfo(from) : addNeInfo(from);
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
result
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success(t('common.operateOk'), 3);
|
||||||
|
// 返回无引用信息
|
||||||
|
emit('ok', JSON.parse(JSON.stringify(from)));
|
||||||
|
fnModalCancel();
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
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.confirmLoading = false;
|
||||||
|
modalStateFrom.resetFields();
|
||||||
|
emit('cancel');
|
||||||
|
emit('update:open', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表单修改网元类型 */
|
||||||
|
function fnNeTypeChange(v: any) {
|
||||||
|
// 网元默认只含22和4100
|
||||||
|
if (modalState.from.hosts.length === 3) {
|
||||||
|
modalState.from.hosts.pop();
|
||||||
|
}
|
||||||
|
const hostsLen = modalState.from.hosts.length;
|
||||||
|
// UPF标准版本可支持5002
|
||||||
|
if (hostsLen === 2 && v === 'UPF') {
|
||||||
|
modalState.from.hosts.push({
|
||||||
|
hostId: undefined,
|
||||||
|
hostType: 'telnet',
|
||||||
|
groupId: '1',
|
||||||
|
title: 'Telnet_NE_5002',
|
||||||
|
addr: modalState.from.ip,
|
||||||
|
port: 5002,
|
||||||
|
user: 'admin',
|
||||||
|
authMode: '0',
|
||||||
|
password: 'admin',
|
||||||
|
remark: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// UDM可支持6379
|
||||||
|
if (hostsLen === 2 && v === 'UDM') {
|
||||||
|
modalState.from.hosts.push({
|
||||||
|
hostId: undefined,
|
||||||
|
hostType: 'redis',
|
||||||
|
groupId: '1',
|
||||||
|
title: 'REDIS_NE_6379',
|
||||||
|
addr: modalState.from.ip,
|
||||||
|
port: 6379,
|
||||||
|
user: 'udmdb',
|
||||||
|
authMode: '0',
|
||||||
|
password: 'helloearth',
|
||||||
|
dbName: '0',
|
||||||
|
remark: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
modalState.from.rmUid = `4400HX${v}${modalState.from.neId}`; // 4400HX1AMF001
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表单修改网元neId */
|
||||||
|
function fnNeIdChange(e: any) {
|
||||||
|
const v = e.target.value;
|
||||||
|
if (v.length < 1) return;
|
||||||
|
modalState.from.rmUid = `4400HX${modalState.from.neType}${v}`; // 4400HX1AMF001
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表单修改网元IP */
|
||||||
|
function fnNeIPChange(e: any) {
|
||||||
|
const v = e.target.value;
|
||||||
|
if (v.length < 7) return;
|
||||||
|
for (const host of modalState.from.hosts) {
|
||||||
|
host.addr = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**监听是否显示,初始数据 */
|
||||||
|
watch(
|
||||||
|
() => props.open,
|
||||||
|
val => {
|
||||||
|
if (val) fnModalVisibleByEdit(props.editId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始字典数据
|
||||||
|
Promise.allSettled([
|
||||||
|
getDict('ne_host_type'),
|
||||||
|
getDict('ne_host_groupId'),
|
||||||
|
getDict('ne_host_authMode'),
|
||||||
|
]).then(resArr => {
|
||||||
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
dict.neHostType = resArr[0].value;
|
||||||
|
}
|
||||||
|
if (resArr[1].status === 'fulfilled') {
|
||||||
|
dict.neHostGroupId = resArr[1].value;
|
||||||
|
}
|
||||||
|
if (resArr[2].status === 'fulfilled') {
|
||||||
|
dict.neHostAuthMode = resArr[2].value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ProModal
|
||||||
|
:drag="true"
|
||||||
|
:width="800"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
:body-style="{ maxHeight: '600px', 'overflow-y': 'auto' }"
|
||||||
|
: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-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.common.neType')"
|
||||||
|
name="neType"
|
||||||
|
v-bind="modalStateFrom.validateInfos.neType"
|
||||||
|
>
|
||||||
|
<a-auto-complete
|
||||||
|
v-model:value="modalState.from.neType"
|
||||||
|
:options="NE_TYPE_LIST.map(v => ({ value: v }))"
|
||||||
|
@change="fnNeTypeChange"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="32"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title>
|
||||||
|
{{ t('views.ne.common.neTypeTip') }}
|
||||||
|
</template>
|
||||||
|
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-auto-complete>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neInfo.pvflag')"
|
||||||
|
name="pvFlag"
|
||||||
|
v-bind="modalStateFrom.validateInfos.pvFlag"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="modalState.from.pvFlag"
|
||||||
|
default-value="PNF"
|
||||||
|
>
|
||||||
|
<a-select-opt-group :label="t('views.ne.neInfo.pnf')">
|
||||||
|
<a-select-option value="PNF">PNF</a-select-option>
|
||||||
|
</a-select-opt-group>
|
||||||
|
<a-select-opt-group :label="t('views.ne.neInfo.vnf')">
|
||||||
|
<a-select-option value="VNF">VNF</a-select-option>
|
||||||
|
</a-select-opt-group>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.common.neId')"
|
||||||
|
name="neId"
|
||||||
|
v-bind="modalStateFrom.validateInfos.neId"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.neId"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="32"
|
||||||
|
@change="fnNeIdChange"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title>
|
||||||
|
{{ t('views.ne.common.neIdTip') }}
|
||||||
|
</template>
|
||||||
|
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.common.neName')"
|
||||||
|
name="neName"
|
||||||
|
v-bind="modalStateFrom.validateInfos.neName"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.neName"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="64"
|
||||||
|
>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.common.ipAddr')"
|
||||||
|
name="ip"
|
||||||
|
v-bind="modalStateFrom.validateInfos.ip"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.ip"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="128"
|
||||||
|
@change="fnNeIPChange"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title>
|
||||||
|
<div>
|
||||||
|
{{ t('views.ne.common.ipAddrTip') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.common.port')"
|
||||||
|
name="port"
|
||||||
|
v-bind="modalStateFrom.validateInfos.port"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="modalState.from.port"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="1"
|
||||||
|
:max="65535"
|
||||||
|
:maxlength="5"
|
||||||
|
placeholder="<=65535"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title>
|
||||||
|
<div>{{ t('views.ne.common.portTip') }}</div>
|
||||||
|
</template>
|
||||||
|
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.common.rmUid')"
|
||||||
|
name="rmUid"
|
||||||
|
v-bind="modalStateFrom.validateInfos.rmUid"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
:labelWrap="true"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.rmUid"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="40"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title>
|
||||||
|
<div>
|
||||||
|
{{ t('views.ne.common.rmUidTip') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.ne.neInfo.neAddress')" name="neAddress">
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.neAddress"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="64"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title>
|
||||||
|
<div>{{ t('views.ne.neInfo.neAddressTip') }}</div>
|
||||||
|
</template>
|
||||||
|
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.ne.neInfo.dn')" name="dn">
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.dn"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="255"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neInfo.vendorName')"
|
||||||
|
name="vendorName"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.vendorName"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="64"
|
||||||
|
>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.ne.neInfo.province')" name="province">
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.province"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
:maxlength="32"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
:label="t('common.remark')"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-textarea
|
||||||
|
v-model:value="modalState.from.remark"
|
||||||
|
:auto-size="{ minRows: 1, maxRows: 6 }"
|
||||||
|
:maxlength="450"
|
||||||
|
:show-count="true"
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 主机连接配置 -->
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.ne.neInfo.hostConfig') }}
|
||||||
|
</a-divider>
|
||||||
|
<a-collapse class="collapse" ghost>
|
||||||
|
<a-collapse-panel
|
||||||
|
v-for="host in modalState.from.hosts.filter(
|
||||||
|
(s:any) => !(s.hostType === 'telnet' && modalState.from.neType === 'OMC')
|
||||||
|
)"
|
||||||
|
:key="host.title"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<span v-if="host.hostType === 'redis'"> DB {{ host.port }} </span>
|
||||||
|
<span v-else>
|
||||||
|
{{ `${host.hostType.toUpperCase()} ${host.port}` }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.ne.neHost.addr')">
|
||||||
|
<a-input
|
||||||
|
v-model:value="host.addr"
|
||||||
|
allow-clear
|
||||||
|
:maxlength="128"
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neHost.port')"
|
||||||
|
name="neHost.port"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="host.port"
|
||||||
|
:min="10"
|
||||||
|
:max="65535"
|
||||||
|
:step="1"
|
||||||
|
:maxlength="5"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
v-if="host.hostType === 'telnet'"
|
||||||
|
:label="t('views.ne.neHost.user')"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="host.user"
|
||||||
|
allow-clear
|
||||||
|
:maxlength="32"
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-row v-if="host.hostType === 'ssh'">
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.ne.neHost.user')">
|
||||||
|
<a-input
|
||||||
|
v-model:value="host.user"
|
||||||
|
allow-clear
|
||||||
|
:maxlength="32"
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.ne.neHost.authMode')">
|
||||||
|
<a-select
|
||||||
|
v-model:value="host.authMode"
|
||||||
|
default-value="0"
|
||||||
|
:options="dict.neHostAuthMode"
|
||||||
|
>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
v-if="host.authMode === '0'"
|
||||||
|
:label="t('views.ne.neHost.password')"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-input-password
|
||||||
|
v-model:value="host.password"
|
||||||
|
:maxlength="128"
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
>
|
||||||
|
</a-input-password>
|
||||||
|
</a-form-item>
|
||||||
|
<template v-if="host.authMode === '1'">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neHost.privateKey')"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-textarea
|
||||||
|
v-model:value="host.privateKey"
|
||||||
|
:auto-size="{ minRows: 4, maxRows: 6 }"
|
||||||
|
:maxlength="3000"
|
||||||
|
:show-count="true"
|
||||||
|
:placeholder="t('views.ne.neHost.privateKeyPlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neHost.passPhrase')"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-input-password
|
||||||
|
v-model:value="host.passPhrase"
|
||||||
|
:maxlength="128"
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
>
|
||||||
|
</a-input-password>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
v-if="host.hostType === 'mysql'"
|
||||||
|
:label="t('views.ne.neHost.database')"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="host.dbName"
|
||||||
|
allow-clear
|
||||||
|
:maxlength="32"
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
:label="t('common.remark')"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-textarea
|
||||||
|
v-model:value="host.remark"
|
||||||
|
:auto-size="{ minRows: 1, maxRows: 6 }"
|
||||||
|
:maxlength="450"
|
||||||
|
:show-count="true"
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 测试 -->
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neHost.test')"
|
||||||
|
name="test"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
shape="round"
|
||||||
|
@click="fnHostTest(host)"
|
||||||
|
:loading="modalState.confirmLoading"
|
||||||
|
>
|
||||||
|
<template #icon><LinkOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click="fnHostAuthorized(host)"
|
||||||
|
:loading="modalState.confirmLoading"
|
||||||
|
v-if="host.hostType === 'ssh' && host.authMode !== '2'"
|
||||||
|
>
|
||||||
|
{{ t('views.ne.neHost.authRSA') }}
|
||||||
|
</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
</a-form>
|
||||||
|
</ProModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.collapse :deep(.ant-collapse-item) > .ant-collapse-header {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.collapse-header {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,322 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, toRaw, watch } from 'vue';
|
||||||
|
import { ProModal } from 'antdv-pro-modal';
|
||||||
|
import { message, Form } from 'ant-design-vue/es';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { getOAMFile, saveOAMFile } from '@/api/ne/neInfo';
|
||||||
|
const { t } = useI18n();
|
||||||
|
const emit = defineEmits(['ok', 'cancel', 'update:open']);
|
||||||
|
const props = defineProps({
|
||||||
|
open: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
/**网元ID */
|
||||||
|
neId: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
neType: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**新增框或修改框是否显示 */
|
||||||
|
openByEdit: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**是否同步 */
|
||||||
|
sync: boolean;
|
||||||
|
/**表单数据 */
|
||||||
|
from: Record<string, any>;
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
openByEdit: false,
|
||||||
|
title: 'OAM Configuration',
|
||||||
|
sync: true,
|
||||||
|
from: {
|
||||||
|
omcIP: '',
|
||||||
|
oamEnable: true,
|
||||||
|
oamPort: 33030,
|
||||||
|
snmpEnable: true,
|
||||||
|
snmpPort: 4957,
|
||||||
|
kpiEnable: true,
|
||||||
|
kpiTimer: 60,
|
||||||
|
},
|
||||||
|
confirmLoading: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**对话框内表单属性和校验规则 */
|
||||||
|
const modalStateFrom = Form.useForm(
|
||||||
|
modalState.from,
|
||||||
|
reactive({
|
||||||
|
kpiTimer: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: t('views.ne.neInfo.oam.kpiTimerPlease'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出显示为 新增或者修改
|
||||||
|
* @param neType 网元类型
|
||||||
|
* @param neId 网元ID
|
||||||
|
*/
|
||||||
|
function fnModalVisibleByTypeAndId(neType: string, neId: string) {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
getOAMFile(neType, neId)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
const data = res.data;
|
||||||
|
Object.assign(modalState.from, {
|
||||||
|
omcIP: data.oamConfig[data.oamConfig.ipType],
|
||||||
|
oamEnable: data.oamConfig.enable,
|
||||||
|
oamPort: data.oamConfig.port,
|
||||||
|
snmpEnable: data.snmpConfig.enable,
|
||||||
|
snmpPort: data.snmpConfig.port,
|
||||||
|
kpiEnable: data.kpiConfig.enable,
|
||||||
|
kpiTimer: data.kpiConfig.timer,
|
||||||
|
});
|
||||||
|
modalState.title = t('views.ne.neInfo.oam.title');
|
||||||
|
modalState.openByEdit = true;
|
||||||
|
} else {
|
||||||
|
message.error(res.msg, 3);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
hide();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出确认执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalOk() {
|
||||||
|
modalStateFrom
|
||||||
|
.validate()
|
||||||
|
.then(e => {
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
const from = toRaw(modalState.from);
|
||||||
|
saveOAMFile({
|
||||||
|
neType: props.neType,
|
||||||
|
neId: props.neId,
|
||||||
|
content: from,
|
||||||
|
sync: modalState.sync,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success(t('common.operateOk'), 3);
|
||||||
|
emit('ok');
|
||||||
|
fnModalCancel();
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
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.confirmLoading = false;
|
||||||
|
modalStateFrom.resetFields();
|
||||||
|
emit('cancel');
|
||||||
|
emit('update:open', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**监听是否显示,初始数据 */
|
||||||
|
watch(
|
||||||
|
() => props.open,
|
||||||
|
val => {
|
||||||
|
if (val) {
|
||||||
|
if (props.neType && props.neId) {
|
||||||
|
fnModalVisibleByTypeAndId(props.neType, props.neId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ProModal
|
||||||
|
:drag="true"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
:body-style="{ maxHeight: '600px', 'overflow-y': 'auto' }"
|
||||||
|
: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: 12 }"
|
||||||
|
:labelWrap="true"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neInfo.oam.sync')"
|
||||||
|
name="sync"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:labelWrap="true"
|
||||||
|
>
|
||||||
|
<a-switch
|
||||||
|
:checked-children="t('common.switch.open')"
|
||||||
|
:un-checked-children="t('common.switch.shut')"
|
||||||
|
v-model:checked="modalState.sync"
|
||||||
|
></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-collapse class="collapse" ghost>
|
||||||
|
<a-collapse-panel header="OAM">
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neInfo.oam.oamEnable')"
|
||||||
|
name="oamEnable"
|
||||||
|
>
|
||||||
|
<a-switch
|
||||||
|
:checked-children="t('common.switch.open')"
|
||||||
|
:un-checked-children="t('common.switch.shut')"
|
||||||
|
v-model:checked="modalState.from.oamEnable"
|
||||||
|
></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neInfo.oam.oamPort')"
|
||||||
|
name="oamPort"
|
||||||
|
v-bind="modalStateFrom.validateInfos.oamPort"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
:min="3000"
|
||||||
|
:max="65535"
|
||||||
|
:step="1"
|
||||||
|
:maxlength="5"
|
||||||
|
v-model:value="modalState.from.oamPort"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neInfo.oam.omcIP')"
|
||||||
|
name="omcIP"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:labelWrap="true"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.omcIP"
|
||||||
|
:maxlength="128"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel header="SNMP">
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neInfo.oam.snmpEnable')"
|
||||||
|
name="snmpEnable"
|
||||||
|
>
|
||||||
|
<a-switch
|
||||||
|
:checked-children="t('common.switch.open')"
|
||||||
|
:un-checked-children="t('common.switch.shut')"
|
||||||
|
v-model:checked="modalState.from.snmpEnable"
|
||||||
|
></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neInfo.oam.snmpPort')"
|
||||||
|
name="snmpPort"
|
||||||
|
v-bind="modalStateFrom.validateInfos.snmpPort"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
:min="3000"
|
||||||
|
:max="65535"
|
||||||
|
:step="1"
|
||||||
|
:maxlength="5"
|
||||||
|
v-model:value="modalState.from.snmpPort"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel header="KPI">
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neInfo.oam.kpiEnable')"
|
||||||
|
name="kpiEnable"
|
||||||
|
>
|
||||||
|
<a-switch
|
||||||
|
:checked-children="t('common.switch.open')"
|
||||||
|
:un-checked-children="t('common.switch.shut')"
|
||||||
|
v-model:checked="modalState.from.kpiEnable"
|
||||||
|
></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neInfo.oam.kpiTimer')"
|
||||||
|
name="kpiTimer"
|
||||||
|
v-bind="modalStateFrom.validateInfos.kpiTimer"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
:min="5"
|
||||||
|
:max="3600"
|
||||||
|
:step="1"
|
||||||
|
:maxlength="4"
|
||||||
|
v-model:value="modalState.from.kpiTimer"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
</a-form>
|
||||||
|
</ProModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.collapse :deep(.ant-collapse-item) > .ant-collapse-header {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
.collapse-header {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { Modal, message } from 'ant-design-vue/es';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { updateNeConfigReload } from '@/api/configManage/configParam';
|
||||||
|
import { serviceNeAction } from '@/api/ne/neInfo';
|
||||||
|
import useMaskStore from '@/store/modules/mask';
|
||||||
|
|
||||||
|
export default function useNeOptions() {
|
||||||
|
const router = useRouter();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const maskStore = useMaskStore();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元启动
|
||||||
|
* @param row {neName,neType,neId}
|
||||||
|
*/
|
||||||
|
function fnNeStart(row: Record<string, any>) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.ne.common.startTip'),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
serviceNeAction({
|
||||||
|
neType: row.neType,
|
||||||
|
neId: row.neId,
|
||||||
|
action: 'start',
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success(t('common.operateOk'), 3);
|
||||||
|
} else {
|
||||||
|
message.error(`${res.msg}`, 3);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元重启
|
||||||
|
* @param row {neName,neType,neId}
|
||||||
|
*/
|
||||||
|
function fnNeRestart(row: Record<string, any>) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.ne.common.restartTip'),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
serviceNeAction({
|
||||||
|
neType: row.neType,
|
||||||
|
neId: row.neId,
|
||||||
|
action: 'restart',
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
// OMC自升级
|
||||||
|
if (row.neType.toUpperCase() === 'OMC') {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
maskStore.handleMaskType('reload');
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
message.success(t('common.operateOk'), 3);
|
||||||
|
} else {
|
||||||
|
message.error(`${res.msg}`, 3);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元停止
|
||||||
|
* @param row {neName,neType,neId}
|
||||||
|
*/
|
||||||
|
function fnNeStop(row: Record<string, any>) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.ne.common.stopTip'),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
serviceNeAction({
|
||||||
|
neType: row.neType,
|
||||||
|
neId: row.neId,
|
||||||
|
action: 'stop',
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success(t('common.operateOk'), 3);
|
||||||
|
} else {
|
||||||
|
message.error(`${res.msg}`, 3);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元重新加载
|
||||||
|
* @param row {neName,neType,neId}
|
||||||
|
*/
|
||||||
|
function fnNeReload(row: Record<string, any>) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.ne.common.reloadTip'),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
updateNeConfigReload(row.neType, row.neId)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success(t('common.operateOk'), 3);
|
||||||
|
} else {
|
||||||
|
message.error(`${res.msg}`, 3);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跳转网元日志文件页面
|
||||||
|
* @param row {neType,neId}
|
||||||
|
*/
|
||||||
|
function fnNeLogFile(row: Record<string, any>) {
|
||||||
|
router.push({
|
||||||
|
name: 'NeFile_2123',
|
||||||
|
query: {
|
||||||
|
neType: row.neType,
|
||||||
|
neId: row.neId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { fnNeStart, fnNeRestart, fnNeStop, fnNeReload, fnNeLogFile };
|
||||||
|
}
|
||||||
@@ -0,0 +1,790 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, toRaw, defineAsyncComponent, ref } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { message, Modal } from 'ant-design-vue/es';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import { listNeInfo, delNeInfo, stateNeInfo } from '@/api/ne/neInfo';
|
||||||
|
import { NE_TYPE_LIST } from '@/constants/ne-constants';
|
||||||
|
import { hasRoles } from '@/plugins/auth-user';
|
||||||
|
import useDictStore from '@/store/modules/dict';
|
||||||
|
import useNeOptions from './hooks/useNeOptions';
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { fnNeStart, fnNeRestart, fnNeStop, fnNeReload, fnNeLogFile } =
|
||||||
|
useNeOptions();
|
||||||
|
// 异步加载组件
|
||||||
|
const EditModal = defineAsyncComponent(
|
||||||
|
() => import('./components/EditModal.vue')
|
||||||
|
);
|
||||||
|
const OAMModal = defineAsyncComponent(
|
||||||
|
() => import('./components/OAMModal.vue')
|
||||||
|
);
|
||||||
|
// 配置备份文件导入
|
||||||
|
const BackConfModal = defineAsyncComponent(
|
||||||
|
() => import('./components/BackConfModal.vue')
|
||||||
|
);
|
||||||
|
const backConf = ref(); // 引用句柄,取导出函数
|
||||||
|
|
||||||
|
/**字典数据 */
|
||||||
|
let dict: {
|
||||||
|
/**网元信息状态 */
|
||||||
|
neInfoStatus: DictType[];
|
||||||
|
} = reactive({
|
||||||
|
neInfoStatus: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元类型 */
|
||||||
|
neType: '',
|
||||||
|
/**带状态信息 */
|
||||||
|
bandStatus: true,
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
queryParams = Object.assign(queryParams, {
|
||||||
|
neType: '',
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
tablePagination.current = 1;
|
||||||
|
tablePagination.pageSize = 20;
|
||||||
|
fnGetList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**搜索栏 */
|
||||||
|
seached: boolean;
|
||||||
|
/**记录数据 */
|
||||||
|
data: Record<string, any>[];
|
||||||
|
/**勾选记录 */
|
||||||
|
selectedRowKeys: (string | number)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
size: 'middle',
|
||||||
|
seached: false,
|
||||||
|
data: [],
|
||||||
|
selectedRowKeys: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
let tableColumns: ColumnsType = [
|
||||||
|
{
|
||||||
|
title: t('views.ne.common.neType'),
|
||||||
|
dataIndex: 'neType',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.ne.common.neId'),
|
||||||
|
dataIndex: 'neId',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.ne.common.rmUid'),
|
||||||
|
dataIndex: 'rmUid',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.ne.common.neName'),
|
||||||
|
dataIndex: 'neName',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.ne.common.ipAddr'),
|
||||||
|
dataIndex: 'ip',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.ne.common.port'),
|
||||||
|
dataIndex: 'port',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.ne.neInfo.state'),
|
||||||
|
dataIndex: 'status',
|
||||||
|
key: 'status',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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 = {
|
||||||
|
/**配置备份框是否显示 */
|
||||||
|
openByBackConf: boolean;
|
||||||
|
/**OAM文件配置框是否显示 */
|
||||||
|
openByOAM: boolean;
|
||||||
|
/**新增框或修改框是否显示 */
|
||||||
|
openByEdit: boolean;
|
||||||
|
/**新增框或修改框ID */
|
||||||
|
editId: string;
|
||||||
|
/**OAM框网元类型ID */
|
||||||
|
neId: string;
|
||||||
|
neType: string;
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
openByBackConf: false,
|
||||||
|
openByOAM: false,
|
||||||
|
openByEdit: false,
|
||||||
|
editId: '',
|
||||||
|
neId: '',
|
||||||
|
neType: '',
|
||||||
|
confirmLoading: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出显示为 新增或者修改
|
||||||
|
* @param noticeId 网元id, 不传为新增
|
||||||
|
*/
|
||||||
|
function fnModalVisibleByEdit(row?: Record<string, any>) {
|
||||||
|
if (!row) {
|
||||||
|
modalState.editId = '';
|
||||||
|
} else {
|
||||||
|
modalState.editId = row.id;
|
||||||
|
}
|
||||||
|
modalState.openByEdit = !modalState.openByEdit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出确认执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalEditOk(from: Record<string, any>) {
|
||||||
|
// 新增时刷新列表
|
||||||
|
if (!from.id) {
|
||||||
|
fnGetList();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 编辑时局部更新信息
|
||||||
|
stateNeInfo(from.neType, from.neId)
|
||||||
|
.then(res => {
|
||||||
|
// 找到编辑更新的网元
|
||||||
|
const item = tableState.data.find(s => s.id === from.id);
|
||||||
|
if (item && res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
item.neType = from.neType;
|
||||||
|
item.neId = from.neId;
|
||||||
|
item.rmUid = from.rmUid;
|
||||||
|
item.neName = from.neName;
|
||||||
|
item.ip = from.ip;
|
||||||
|
item.port = from.port;
|
||||||
|
if (item.status !== '2') {
|
||||||
|
item.status = res.data.online ? '1' : '0';
|
||||||
|
}
|
||||||
|
Object.assign(item.serverState, res.data);
|
||||||
|
const resouresUsage = parseResouresUsage(item.serverState);
|
||||||
|
Reflect.set(item, 'resoures', resouresUsage);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
useNeInfoStore().fnRefreshNelist();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出关闭执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalEditCancel() {
|
||||||
|
modalState.editId = '';
|
||||||
|
modalState.openByEdit = false;
|
||||||
|
modalState.openByOAM = false;
|
||||||
|
modalState.openByBackConf = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录删除
|
||||||
|
* @param id 编号
|
||||||
|
*/
|
||||||
|
function fnRecordDelete(id: string) {
|
||||||
|
if (!id || modalState.confirmLoading) return;
|
||||||
|
let msg = t('views.ne.neInfo.delTip');
|
||||||
|
if (id === '0') {
|
||||||
|
msg = `${msg} ...${tableState.selectedRowKeys.length}`;
|
||||||
|
id = tableState.selectedRowKeys.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: msg,
|
||||||
|
onOk() {
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
delNeInfo(id)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success(t('common.operateOk'), 3);
|
||||||
|
// 过滤掉删除的id
|
||||||
|
tableState.data = tableState.data.filter(item => {
|
||||||
|
if (id.indexOf(',') > -1) {
|
||||||
|
return !tableState.selectedRowKeys.includes(item.id);
|
||||||
|
} else {
|
||||||
|
return item.id !== id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 刷新缓存
|
||||||
|
useNeInfoStore().fnRefreshNelist();
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录多项选择
|
||||||
|
*/
|
||||||
|
function fnRecordMore(type: string | number, row: Record<string, any>) {
|
||||||
|
switch (type) {
|
||||||
|
case 'delete':
|
||||||
|
fnRecordDelete(row.id);
|
||||||
|
break;
|
||||||
|
case 'start':
|
||||||
|
fnNeStart(row);
|
||||||
|
break;
|
||||||
|
case 'restart':
|
||||||
|
fnNeRestart(row);
|
||||||
|
break;
|
||||||
|
case 'stop':
|
||||||
|
fnNeStop(row);
|
||||||
|
break;
|
||||||
|
case 'reload':
|
||||||
|
fnNeReload(row);
|
||||||
|
break;
|
||||||
|
case 'log':
|
||||||
|
fnNeLogFile(row);
|
||||||
|
break;
|
||||||
|
case 'oam':
|
||||||
|
modalState.neId = row.neId;
|
||||||
|
modalState.neType = row.neType;
|
||||||
|
modalState.openByOAM = !modalState.openByOAM;
|
||||||
|
break;
|
||||||
|
case 'backConfExport':
|
||||||
|
backConf.value.exportConf(row.neType, row.neId);
|
||||||
|
break;
|
||||||
|
case 'backConfImport':
|
||||||
|
modalState.neId = row.neId;
|
||||||
|
modalState.neType = row.neType;
|
||||||
|
modalState.openByBackConf = !modalState.openByBackConf;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn(type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
listNeInfo(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.map(item => {
|
||||||
|
let resouresUsage = {
|
||||||
|
sysDiskUsage: 0,
|
||||||
|
sysMemUsage: 0,
|
||||||
|
sysCpuUsage: 0,
|
||||||
|
nfCpuUsage: 0,
|
||||||
|
};
|
||||||
|
const neState = item.serverState;
|
||||||
|
if (neState) {
|
||||||
|
resouresUsage = parseResouresUsage(neState);
|
||||||
|
} else {
|
||||||
|
item.serverState = { online: false };
|
||||||
|
}
|
||||||
|
Reflect.set(item, 'resoures', resouresUsage);
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**解析网元状态携带的资源利用率 */
|
||||||
|
function parseResouresUsage(neState: Record<string, any>) {
|
||||||
|
let sysCpuUsage = 0;
|
||||||
|
let nfCpuUsage = 0;
|
||||||
|
if (neState.cpu) {
|
||||||
|
nfCpuUsage = neState.cpu.nfCpuUsage;
|
||||||
|
const nfCpu = +(nfCpuUsage / 100);
|
||||||
|
nfCpuUsage = +nfCpu.toFixed(2);
|
||||||
|
if (nfCpuUsage > 100) {
|
||||||
|
nfCpuUsage = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
sysCpuUsage = neState.cpu.sysCpuUsage;
|
||||||
|
const sysCpu = +(sysCpuUsage / 100);
|
||||||
|
sysCpuUsage = +sysCpu.toFixed(2);
|
||||||
|
if (sysCpuUsage > 100) {
|
||||||
|
sysCpuUsage = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let sysMemUsage = 0;
|
||||||
|
if (neState.mem) {
|
||||||
|
const men = neState.mem.sysMemUsage;
|
||||||
|
sysMemUsage = +(men / 100).toFixed(2);
|
||||||
|
if (sysMemUsage > 100) {
|
||||||
|
sysMemUsage = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let sysDiskUsage = 0;
|
||||||
|
if (neState.disk && Array.isArray(neState.disk.partitionInfo)) {
|
||||||
|
let disks: any[] = neState.disk.partitionInfo;
|
||||||
|
disks = disks.sort((a, b) => +b.used - +a.used);
|
||||||
|
if (disks.length > 0) {
|
||||||
|
const { total, used } = disks[0];
|
||||||
|
sysDiskUsage = +((used / total) * 100).toFixed(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sysDiskUsage,
|
||||||
|
sysMemUsage,
|
||||||
|
sysCpuUsage,
|
||||||
|
nfCpuUsage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始字典数据
|
||||||
|
Promise.allSettled([getDict('ne_info_status')]).then(resArr => {
|
||||||
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
dict.neInfoStatus = resArr[0].value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 刷新缓存的网元信息
|
||||||
|
useNeInfoStore()
|
||||||
|
.fnRefreshNelist()
|
||||||
|
.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="t('views.ne.common.neType')" name="neType ">
|
||||||
|
<a-auto-complete
|
||||||
|
v-model:value="queryParams.neType"
|
||||||
|
:options="NE_TYPE_LIST.map(v => ({ value: v }))"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="fnQueryReset">
|
||||||
|
<template #icon><ClearOutlined /></template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-space :size="8" align="center" v-roles:has="['admin']">
|
||||||
|
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
|
||||||
|
<template #icon><PlusOutlined /></template>
|
||||||
|
{{ t('common.addText') }}
|
||||||
|
</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-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.searchBarText') }}</template>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="tableState.seached"
|
||||||
|
:checked-children="t('common.switch.show')"
|
||||||
|
:un-checked-children="t('common.switch.hide')"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.sizeText') }}</template>
|
||||||
|
<a-dropdown trigger="click" placement="bottomRight">
|
||||||
|
<a-button type="text">
|
||||||
|
<template #icon><ColumnHeightOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu
|
||||||
|
:selected-keys="[tableState.size as string]"
|
||||||
|
@click="fnTableSize"
|
||||||
|
>
|
||||||
|
<a-menu-item key="default">
|
||||||
|
{{ t('common.size.default') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="middle">
|
||||||
|
{{ t('common.size.middle') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="small">
|
||||||
|
{{ t('common.size.small') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="id"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: tableColumns.length * 120 }"
|
||||||
|
:row-selection="{
|
||||||
|
type: 'checkbox',
|
||||||
|
columnWidth: '48px',
|
||||||
|
selectedRowKeys: tableState.selectedRowKeys,
|
||||||
|
onChange: fnTableSelectedRowKeys,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'status'">
|
||||||
|
<DictTag :options="dict.neInfoStatus" :value="record.status" />
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'id'">
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<span v-roles:has="['admin']">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.editText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnModalVisibleByEdit(record)"
|
||||||
|
>
|
||||||
|
<template #icon><FormOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<span v-roles:has="['admin', 'teacher']">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>
|
||||||
|
{{ t('views.ne.common.restart') }}
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordMore('restart', record)"
|
||||||
|
>
|
||||||
|
<template #icon><UndoOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<a-tooltip placement="left">
|
||||||
|
<template #title>{{ t('common.moreText') }}</template>
|
||||||
|
<a-dropdown placement="bottomRight" trigger="click">
|
||||||
|
<a-button type="link">
|
||||||
|
<template #icon><EllipsisOutlined /> </template>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu @click="({ key }:any) => fnRecordMore(key, record)">
|
||||||
|
<a-menu-item key="log">
|
||||||
|
<FileTextOutlined />
|
||||||
|
{{ t('views.ne.common.log') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="start" v-if="hasRoles(['admin'])">
|
||||||
|
<ThunderboltOutlined />
|
||||||
|
{{ t('views.ne.common.start') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="stop" v-if="hasRoles(['admin'])">
|
||||||
|
<CloseSquareOutlined />
|
||||||
|
{{ t('views.ne.common.stop') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item
|
||||||
|
key="reload"
|
||||||
|
v-if="
|
||||||
|
!['OMC', 'PCF', 'IMS', 'MME'].includes(
|
||||||
|
record.neType
|
||||||
|
) && hasRoles(['admin'])
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<SyncOutlined />
|
||||||
|
{{ t('views.ne.common.reload') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="delete" v-if="hasRoles(['admin'])">
|
||||||
|
<DeleteOutlined />
|
||||||
|
{{ t('common.deleteText') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="oam" v-if="hasRoles(['admin'])">
|
||||||
|
<FileTextOutlined />
|
||||||
|
{{ t('views.ne.common.oam') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<!-- 配置备份 -->
|
||||||
|
<a-menu-item key="backConfExport">
|
||||||
|
<ExportOutlined />
|
||||||
|
{{ t('views.ne.neInfo.backConf.export') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="backConfImport">
|
||||||
|
<ImportOutlined />
|
||||||
|
{{ t('views.ne.neInfo.backConf.import') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template #expandedRowRender="{ record }">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :offset="2" :lg="8" :md="8" :xs="8">
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.ne.neInfo.info') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.neInfo.serviceState') }}:</span>
|
||||||
|
<a-tag
|
||||||
|
:color="record.serverState.online ? 'processing' : 'error'"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
record.serverState.online
|
||||||
|
? t('views.ne.common.normalcy')
|
||||||
|
: t('views.ne.common.exceptions')
|
||||||
|
}}
|
||||||
|
</a-tag>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.neVersion.version') }}:</span>
|
||||||
|
<span>{{ record.serverState.version }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.serialNum') }}:</span>
|
||||||
|
<span>{{ record.serverState.sn }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.expiryDate') }}:</span>
|
||||||
|
<span>{{ record.serverState.expire }}</span>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :offset="2" :lg="8" :md="8" :xs="8">
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.ne.neInfo.resourceInfo') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.neInfo.neCpu') }}:</span>
|
||||||
|
<a-progress
|
||||||
|
status="normal"
|
||||||
|
:stroke-color="
|
||||||
|
record.resoures.nfCpuUsage < 30
|
||||||
|
? '#52c41a'
|
||||||
|
: record.resoures.nfCpuUsage > 70
|
||||||
|
? '#ff4d4f'
|
||||||
|
: '#1890ff'
|
||||||
|
"
|
||||||
|
:percent="record.resoures.nfCpuUsage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.neInfo.sysCpu') }}:</span>
|
||||||
|
<a-progress
|
||||||
|
status="normal"
|
||||||
|
:stroke-color="
|
||||||
|
record.resoures.sysCpuUsage < 30
|
||||||
|
? '#52c41a'
|
||||||
|
: record.resoures.sysCpuUsage > 70
|
||||||
|
? '#ff4d4f'
|
||||||
|
: '#1890ff'
|
||||||
|
"
|
||||||
|
:percent="record.resoures.sysCpuUsage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.neInfo.sysMem') }}:</span>
|
||||||
|
<a-progress
|
||||||
|
status="normal"
|
||||||
|
:stroke-color="
|
||||||
|
record.resoures.sysMemUsage < 30
|
||||||
|
? '#52c41a'
|
||||||
|
: record.resoures.sysMemUsage > 70
|
||||||
|
? '#ff4d4f'
|
||||||
|
: '#1890ff'
|
||||||
|
"
|
||||||
|
:percent="record.resoures.sysMemUsage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.neInfo.sysDisk') }}:</span>
|
||||||
|
<a-progress
|
||||||
|
status="normal"
|
||||||
|
:stroke-color="
|
||||||
|
record.resoures.sysDiskUsage < 30
|
||||||
|
? '#52c41a'
|
||||||
|
: record.resoures.sysDiskUsage > 70
|
||||||
|
? '#ff4d4f'
|
||||||
|
: '#1890ff'
|
||||||
|
"
|
||||||
|
:percent="record.resoures.sysDiskUsage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 新增框或修改框 -->
|
||||||
|
<EditModal
|
||||||
|
v-model:open="modalState.openByEdit"
|
||||||
|
:edit-id="modalState.editId"
|
||||||
|
@ok="fnModalEditOk"
|
||||||
|
@cancel="fnModalEditCancel"
|
||||||
|
></EditModal>
|
||||||
|
|
||||||
|
<!-- OAM编辑框 -->
|
||||||
|
<OAMModal
|
||||||
|
v-model:open="modalState.openByOAM"
|
||||||
|
:ne-id="modalState.neId"
|
||||||
|
:ne-type="modalState.neType"
|
||||||
|
@cancel="fnModalEditCancel"
|
||||||
|
></OAMModal>
|
||||||
|
|
||||||
|
<!-- 配置文件备份框 -->
|
||||||
|
<BackConfModal
|
||||||
|
ref="backConf"
|
||||||
|
v-model:open="modalState.openByBackConf"
|
||||||
|
:ne-id="modalState.neId"
|
||||||
|
:ne-type="modalState.neType"
|
||||||
|
@cancel="fnModalEditCancel"
|
||||||
|
></BackConfModal>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -11,8 +11,9 @@
|
|||||||
*/
|
*/
|
||||||
(function () {
|
(function () {
|
||||||
// host = ip:port
|
// host = ip:port
|
||||||
const host = '192.168.8.100:3030';
|
// const host = '192.168.8.100:33030';
|
||||||
|
const host = `${window.location.hostname}:33030`;
|
||||||
|
|
||||||
// Service Address
|
// Service Address
|
||||||
sessionStorage.setItem('baseUrl', `http://${host}`);
|
sessionStorage.setItem('baseUrl', `http://${host}`);
|
||||||
// websocket Address
|
// websocket Address
|
||||||
|
|||||||
+1
-1
@@ -155,7 +155,7 @@
|
|||||||
}
|
}
|
||||||
</style>`;
|
</style>`;
|
||||||
|
|
||||||
const lang = localStorage.getItem('cache:local:i18n');
|
const lang = localStorage.getItem('cache:local:i18n') || 'en_US';
|
||||||
// 根据浏览器选择语言
|
// 根据浏览器选择语言
|
||||||
// if (!lang) {
|
// if (!lang) {
|
||||||
// let preferredLanguage = navigator.language;
|
// let preferredLanguage = navigator.language;
|
||||||
|
|||||||
File renamed without changes.
Binary file not shown.
File diff suppressed because it is too large.
Load diff
Binary file not shown.
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,166 @@
|
|||||||
|
/**
|
||||||
|
* Wraps the WiregasmLib lib functionality and manages a single DissectSession
|
||||||
|
*/
|
||||||
|
class Wiregasm {
|
||||||
|
constructor() {
|
||||||
|
this.initialized = false;
|
||||||
|
this.session = null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Initialize the wrapper and the Wiregasm module
|
||||||
|
*
|
||||||
|
* @param loader Loader function for the Emscripten module
|
||||||
|
* @param overrides Overrides
|
||||||
|
*/
|
||||||
|
async init(loader, overrides = {}, beforeInit = null) {
|
||||||
|
if (this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.lib = await loader(overrides);
|
||||||
|
this.uploadDir = this.lib.getUploadDirectory();
|
||||||
|
this.pluginsDir = this.lib.getPluginsDirectory();
|
||||||
|
if (beforeInit !== null) {
|
||||||
|
await beforeInit(this.lib);
|
||||||
|
}
|
||||||
|
this.lib.init();
|
||||||
|
this.initialized = true;
|
||||||
|
}
|
||||||
|
list_modules() {
|
||||||
|
return this.lib.listModules();
|
||||||
|
}
|
||||||
|
list_prefs(module) {
|
||||||
|
return this.lib.listPreferences(module);
|
||||||
|
}
|
||||||
|
apply_prefs() {
|
||||||
|
this.lib.applyPreferences();
|
||||||
|
}
|
||||||
|
set_pref(module, key, value) {
|
||||||
|
const ret = this.lib.setPref(module, key, value);
|
||||||
|
if (ret.code != PrefSetResult.PREFS_SET_OK) {
|
||||||
|
const message =
|
||||||
|
ret.error != '' ? ret.error : preferenceSetCodeToError(ret.code);
|
||||||
|
throw new Error(
|
||||||
|
`Failed to set preference (${module}.${key}): ${message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get_pref(module, key) {
|
||||||
|
const response = this.lib.getPref(module, key);
|
||||||
|
if (response.code != 0) {
|
||||||
|
throw new Error(`Failed to get preference (${module}.${key})`);
|
||||||
|
}
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Check the validity of a filter expression.
|
||||||
|
*
|
||||||
|
* @param filter A display filter expression
|
||||||
|
*/
|
||||||
|
test_filter(filter) {
|
||||||
|
return this.lib.checkFilter(filter);
|
||||||
|
}
|
||||||
|
complete_filter(filter) {
|
||||||
|
const out = this.lib.completeFilter(filter);
|
||||||
|
return {
|
||||||
|
err: out.err,
|
||||||
|
fields: vectorToArray(out.fields),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
reload_lua_plugins() {
|
||||||
|
this.lib.reloadLuaPlugins();
|
||||||
|
}
|
||||||
|
add_plugin(name, data, opts = {}) {
|
||||||
|
const path = this.pluginsDir + '/' + name;
|
||||||
|
this.lib.FS.writeFile(path, data, opts);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Load a packet trace file for analysis.
|
||||||
|
*
|
||||||
|
* @returns Response containing the status and summary
|
||||||
|
*/
|
||||||
|
load(name, data, opts = {}) {
|
||||||
|
if (this.session != null) {
|
||||||
|
this.session.delete();
|
||||||
|
}
|
||||||
|
const path = this.uploadDir + '/' + name;
|
||||||
|
this.lib.FS.writeFile(path, data, opts);
|
||||||
|
this.session = new this.lib.DissectSession(path);
|
||||||
|
return this.session.load();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get Packet List information for a range of packets.
|
||||||
|
*
|
||||||
|
* @param filter Output those frames that pass this filter expression
|
||||||
|
* @param skip Skip N frames
|
||||||
|
* @param limit Limit the output to N frames
|
||||||
|
*/
|
||||||
|
frames(filter, skip = 0, limit = 0) {
|
||||||
|
return this.session.getFrames(filter, skip, limit);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get full information about a frame including the protocol tree.
|
||||||
|
*
|
||||||
|
* @param number Frame number
|
||||||
|
*/
|
||||||
|
frame(num) {
|
||||||
|
return this.session.getFrame(num);
|
||||||
|
}
|
||||||
|
follow(follow, filter) {
|
||||||
|
return this.session.follow(follow, filter);
|
||||||
|
}
|
||||||
|
destroy() {
|
||||||
|
if (this.initialized) {
|
||||||
|
if (this.session !== null) {
|
||||||
|
this.session.delete();
|
||||||
|
this.session = null;
|
||||||
|
}
|
||||||
|
this.lib.destroy();
|
||||||
|
this.initialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the column headers
|
||||||
|
*/
|
||||||
|
columns() {
|
||||||
|
const vec = this.lib.getColumns();
|
||||||
|
// convert it from a vector to array
|
||||||
|
return vectorToArray(vec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a Vector to a JS array
|
||||||
|
*
|
||||||
|
* @param vec Vector
|
||||||
|
* @returns JS array of the Vector contents
|
||||||
|
*/
|
||||||
|
function vectorToArray(vec) {
|
||||||
|
return new Array(vec.size()).fill(0).map((_, id) => vec.get(id));
|
||||||
|
}
|
||||||
|
function preferenceSetCodeToError(code) {
|
||||||
|
switch (code) {
|
||||||
|
case PrefSetResult.PREFS_SET_SYNTAX_ERR:
|
||||||
|
return 'Syntax error in string';
|
||||||
|
case PrefSetResult.PREFS_SET_NO_SUCH_PREF:
|
||||||
|
return 'No such preference';
|
||||||
|
case PrefSetResult.PREFS_SET_OBSOLETE:
|
||||||
|
return 'Preference used to exist but no longer does';
|
||||||
|
default:
|
||||||
|
return 'Unknown error';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof exports === 'object' && typeof module === 'object') {
|
||||||
|
module.exports = Wiregasm;
|
||||||
|
module.exports = vectorToArray;
|
||||||
|
} else if (typeof define === 'function' && define['amd']) {
|
||||||
|
define([], function () {
|
||||||
|
return Wiregasm;
|
||||||
|
});
|
||||||
|
define([], function () {
|
||||||
|
return vectorToArray;
|
||||||
|
});
|
||||||
|
} else if (typeof exports === 'object') {
|
||||||
|
exports['loadWiregasm'] = Wiregasm;
|
||||||
|
exports['vectorToArray'] = vectorToArray;
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
// load the Wiregasm library
|
||||||
|
importScripts(
|
||||||
|
'wiregasm_new.js', // self-compilation es5
|
||||||
|
'wiregasm_load.js'
|
||||||
|
// 'https://cdn.jsdelivr.net/npm/@goodtools/wiregasm/dist/wiregasm.js'
|
||||||
|
);
|
||||||
|
|
||||||
|
const wg = new Wiregasm();
|
||||||
|
|
||||||
|
const inflateRemoteBuffer = async url => {
|
||||||
|
const res = await fetch(url);
|
||||||
|
return await res.arrayBuffer();
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchPackages = async () => {
|
||||||
|
console.log('Fetching packages');
|
||||||
|
let [wasmBuffer, dataBuffer] = await Promise.all([
|
||||||
|
await inflateRemoteBuffer(
|
||||||
|
'wiregasm.wasm'
|
||||||
|
// 'https://cdn.jsdelivr.net/npm/@goodtools/wiregasm/dist/wiregasm.wasm'
|
||||||
|
),
|
||||||
|
await inflateRemoteBuffer(
|
||||||
|
'wiregasm.data'
|
||||||
|
// 'https://cdn.jsdelivr.net/npm/@goodtools/wiregasm/dist/wiregasm.data'
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return { wasmBuffer, dataBuffer };
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load the Wiregasm Wasm data
|
||||||
|
fetchPackages()
|
||||||
|
.then(({ wasmBuffer, dataBuffer }) => {
|
||||||
|
return wg.init(loadWiregasm, {
|
||||||
|
wasmBinary: wasmBuffer,
|
||||||
|
getPreloadedPackage() {
|
||||||
|
return dataBuffer;
|
||||||
|
},
|
||||||
|
handleStatus: (type, status) => {
|
||||||
|
postMessage({ type: 'status', code: type, status: status });
|
||||||
|
},
|
||||||
|
printErr: error => {
|
||||||
|
postMessage({ type: 'error', error: error });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
postMessage({ type: 'init' });
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
postMessage({ type: 'error', error: e });
|
||||||
|
});
|
||||||
|
|
||||||
|
/**Converts a Vector to a JS array */
|
||||||
|
function replacer(key, value) {
|
||||||
|
if (value.constructor.name.startsWith('Vector')) {
|
||||||
|
return vectorToArray(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event listener to receive messages from the main script
|
||||||
|
this.onmessage = ev => {
|
||||||
|
const data = ev.data;
|
||||||
|
switch (data.type) {
|
||||||
|
case 'close':
|
||||||
|
wg.destroy();
|
||||||
|
break;
|
||||||
|
case 'columns':
|
||||||
|
const columns = wg.columns();
|
||||||
|
if (Array.isArray(columns)) {
|
||||||
|
this.postMessage({ type: 'columns', data: columns });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'select': // select a frame
|
||||||
|
const number = data.number;
|
||||||
|
const frameData = wg.frame(number);
|
||||||
|
const frameDataToJSON = JSON.parse(JSON.stringify(frameData, replacer));
|
||||||
|
this.postMessage({
|
||||||
|
type: 'selected',
|
||||||
|
data: frameDataToJSON,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'frames': // get frames list
|
||||||
|
const skip = data.skip;
|
||||||
|
const limit = data.limit;
|
||||||
|
const filter = data.filter;
|
||||||
|
const framesData = wg.frames(filter, skip, limit);
|
||||||
|
const framesDataToJSON = JSON.parse(JSON.stringify(framesData, replacer));
|
||||||
|
this.postMessage({
|
||||||
|
type: 'frames',
|
||||||
|
data: framesDataToJSON,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'process-data':
|
||||||
|
const loadData = wg.load(data.name, new Uint8Array(data.data));
|
||||||
|
this.postMessage({ type: 'processed', data: loadData });
|
||||||
|
break;
|
||||||
|
case 'process':
|
||||||
|
const f = data.file;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.addEventListener('load', event => {
|
||||||
|
// XXX: this blocks the worker thread
|
||||||
|
const loadData = wg.load(f.name, new Uint8Array(event.target.result));
|
||||||
|
postMessage({ type: 'processed', data: loadData });
|
||||||
|
});
|
||||||
|
reader.readAsArrayBuffer(f);
|
||||||
|
break;
|
||||||
|
case 'check-filter':
|
||||||
|
const filterStr = data.filter;
|
||||||
|
const checkFilterRes = wg.lib.checkFilter(filterStr);
|
||||||
|
this.postMessage({ type: 'filter', data: checkFilterRes });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.type === 'reload-quick') {
|
||||||
|
if (wg.session) {
|
||||||
|
// TODO: this is a hack, we should be able to reload the session
|
||||||
|
const name = data.name;
|
||||||
|
const res = wg.session.load();
|
||||||
|
|
||||||
|
postMessage({ type: 'processed', name: name, data: res });
|
||||||
|
}
|
||||||
|
} else if (data.type === 'module-tree') {
|
||||||
|
const res = wg.list_modules();
|
||||||
|
// send it to the correct port
|
||||||
|
event.ports[0].postMessage({
|
||||||
|
result: JSON.parse(JSON.stringify(res, replacer)),
|
||||||
|
});
|
||||||
|
} else if (data.type === 'module-prefs') {
|
||||||
|
const res = wg.list_prefs(data.name);
|
||||||
|
// send it to the correct port
|
||||||
|
event.ports[0].postMessage({
|
||||||
|
result: JSON.parse(JSON.stringify(res, replacer)),
|
||||||
|
});
|
||||||
|
} else if (data.type === 'upload-file') {
|
||||||
|
const f = data.file;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.addEventListener('load', e => {
|
||||||
|
// XXX: this blocks the worker thread
|
||||||
|
const path = '/uploads/' + f.name;
|
||||||
|
wg.lib.FS.writeFile(path, Buffer.from(e.target.result));
|
||||||
|
event.ports[0].postMessage({ result: path });
|
||||||
|
});
|
||||||
|
reader.readAsArrayBuffer(f);
|
||||||
|
} else if (data.type === 'update-pref') {
|
||||||
|
try {
|
||||||
|
console.log(`set_pref(${data.module}, ${data.key}, ${data.value})`);
|
||||||
|
wg.set_pref(data.module, data.key, data.value);
|
||||||
|
event.ports[0].postMessage({ result: 'ok' });
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
`set_pref(${data.module}, ${data.key}, ${data.value}) failed: ${e.message}`
|
||||||
|
);
|
||||||
|
event.ports[0].postMessage({ error: e.message });
|
||||||
|
}
|
||||||
|
} else if (data.type === 'apply-prefs') {
|
||||||
|
console.log(`apply_prefs()`);
|
||||||
|
wg.apply_prefs();
|
||||||
|
event.ports[0].postMessage({ result: 'ok' });
|
||||||
|
}
|
||||||
|
};
|
||||||
+54
-70
@@ -1,20 +1,48 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ConfigProvider } from 'ant-design-vue/lib';
|
import { onBeforeMount, ref, watch } from 'vue';
|
||||||
import { usePrimaryColor } from '@/hooks/useTheme';
|
import { message } from 'ant-design-vue/es';
|
||||||
import zhCN from 'ant-design-vue/lib/locale/zh_CN';
|
import zhCN from 'ant-design-vue/es/locale/zh_CN';
|
||||||
import enUS from 'ant-design-vue/lib/locale/en_US';
|
import enUS from 'ant-design-vue/es/locale/en_US';
|
||||||
|
import { usePrefersColorScheme, viewTransitionTheme } from 'antdv-pro-layout';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import 'dayjs/locale/zh-cn';
|
import advancedFormat from 'dayjs/plugin/advancedFormat';
|
||||||
import { ref, watch } from 'vue';
|
import useLayoutStore from '@/store/modules/layout';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
const { t, currentLocale } = useI18n();
|
const { t, currentLocale } = useI18n();
|
||||||
const appStore = useAppStore();
|
const { themeConfig, initPrimaryColor, changeConf } = useLayoutStore();
|
||||||
|
dayjs.extend(advancedFormat);
|
||||||
|
// dayjs.locale('zh-cn'); // 默认中文
|
||||||
|
let locale = ref(enUS); // 国际化初始中文
|
||||||
|
|
||||||
dayjs.locale('zh-cn'); // 默认中文
|
// 偏好设置
|
||||||
usePrimaryColor(); // 载入用户自定义主题色
|
const colorScheme = usePrefersColorScheme();
|
||||||
|
watch(
|
||||||
|
() => colorScheme.value,
|
||||||
|
themeMode => {
|
||||||
|
viewTransitionTheme(() => {
|
||||||
|
changeConf('theme', themeMode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
let locale = ref(zhCN); // 国际化初始中文
|
onBeforeMount(() => {
|
||||||
|
// 全局message提示
|
||||||
|
message.config({
|
||||||
|
top: '100px', // 距离顶部位置100px
|
||||||
|
duration: 3,
|
||||||
|
maxCount: 15,
|
||||||
|
});
|
||||||
|
initPrimaryColor();
|
||||||
|
|
||||||
|
// 输出应用版本号
|
||||||
|
const appStore = useAppStore();
|
||||||
|
console.info(
|
||||||
|
`%c ${t('common.desc')} %c ${appStore.appCode} - ${appStore.appVersion} `,
|
||||||
|
'color: #fadfa3; background: #030307; padding: 4px 0;',
|
||||||
|
'color: #030307; background: #fadfa3; padding: 4px 0;'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// 国际化切换语言
|
// 国际化切换语言
|
||||||
function fnChangeLocale(v: string) {
|
function fnChangeLocale(v: string) {
|
||||||
@@ -35,22 +63,15 @@ fnChangeLocale(currentLocale.value);
|
|||||||
watch(currentLocale, val => {
|
watch(currentLocale, val => {
|
||||||
fnChangeLocale(val);
|
fnChangeLocale(val);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 输出应用版本号
|
|
||||||
console.info(
|
|
||||||
`%c ${t('common.title')} %c ${appStore.appCode} - ${appStore.appVersion} `,
|
|
||||||
'color: #fadfa3; background: #030307; padding: 4px 0;',
|
|
||||||
'color: #030307; background: #fadfa3; padding: 4px 0;'
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ConfigProvider :locale="locale">
|
<a-config-provider :theme="themeConfig" :locale="locale">
|
||||||
<RouterView />
|
<RouterView />
|
||||||
</ConfigProvider>
|
</a-config-provider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style lang="css">
|
||||||
#app {
|
#app {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
@@ -61,10 +82,6 @@ body .ant-pro-basicLayout {
|
|||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-pro-sider {
|
|
||||||
z-index: 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide-left-enter-active,
|
.slide-left-enter-active,
|
||||||
.slide-left-leave-active,
|
.slide-left-leave-active,
|
||||||
.slide-right-enter-active,
|
.slide-right-enter-active,
|
||||||
@@ -87,56 +104,23 @@ body .ant-pro-basicLayout {
|
|||||||
transform: translate(-2em, 0);
|
transform: translate(-2em, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**强制改表格边距 */
|
::view-transition-old(root),
|
||||||
.ant-table.ant-table-small .ant-table-tbody > tr > td,
|
::view-transition-new(root) {
|
||||||
.ant-table.ant-table-small .ant-table-thead > tr > th {
|
animation: none;
|
||||||
padding: 6px !important;
|
mix-blend-mode: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ==== 表格头按钮区域 S === **/
|
[data-theme='dark']::view-transition-old(root) {
|
||||||
/* 默认 */
|
z-index: 1;
|
||||||
.button-container {
|
}
|
||||||
display: flex;
|
[data-theme='dark']::view-transition-new(root) {
|
||||||
flex-wrap: wrap;
|
z-index: 999;
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-container > button,
|
::view-transition-old(root) {
|
||||||
.button-container > span {
|
z-index: 999;
|
||||||
margin-right: 12px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
}
|
||||||
|
::view-transition-new(root) {
|
||||||
.button-container > button:last-child,
|
z-index: 1;
|
||||||
.button-container > span:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
}
|
||||||
/* 平板端 */
|
|
||||||
@media (max-width: 992px) {
|
|
||||||
.button-container {
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: flex-start;
|
|
||||||
align-items: left;
|
|
||||||
}
|
|
||||||
.button-container > button,
|
|
||||||
.button-container > span {
|
|
||||||
margin-right: 12px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* 手机端 */
|
|
||||||
@media (max-width: 576px) {
|
|
||||||
.button-container {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
align-items: left;
|
|
||||||
}
|
|
||||||
.button-container > button,
|
|
||||||
.button-container > span {
|
|
||||||
margin-right: 0px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/** ==== 表格头按钮区域 E === **/
|
|
||||||
</style>
|
</style>
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import {
|
|
||||||
RESULT_CODE_ERROR,
|
|
||||||
RESULT_CODE_SUCCESS,
|
|
||||||
RESULT_MSG_ERROR,
|
|
||||||
} from '@/constants/result-constants';
|
|
||||||
import { language, request } from '@/plugins/http-fetch';
|
|
||||||
import { parseObjLineToHump } from '@/utils/parse-utils';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询配置详细
|
|
||||||
* @param tag 配置ID
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function getConfigInfo(tag: string) {
|
|
||||||
// 发起请求
|
|
||||||
const result = await request({
|
|
||||||
url: `/api/rest/databaseManagement/v1/omc_db/config`,
|
|
||||||
method: 'get',
|
|
||||||
params: {
|
|
||||||
SQL: `SELECT * FROM config WHERE config_tag = '${tag}'`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// 解析数据
|
|
||||||
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
|
|
||||||
let data = result.data.data[0];
|
|
||||||
return Object.assign(result, {
|
|
||||||
data: parseObjLineToHump(data['config'][0]),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改配置
|
|
||||||
* @param data 配置对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function updateConfig(tag: string, data: Record<string, any>) {
|
|
||||||
const result = await request({
|
|
||||||
url: `/api/rest/databaseManagement/v1/omc_db/config?WHERE=config_tag='${tag}'`,
|
|
||||||
method: 'put',
|
|
||||||
data: { data },
|
|
||||||
});
|
|
||||||
// 解析数据
|
|
||||||
if (result.code === RESULT_CODE_SUCCESS && result.data.data) {
|
|
||||||
let rows = result.data.data.affectedRows;
|
|
||||||
if (rows) {
|
|
||||||
delete result.data;
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
return { code: RESULT_CODE_ERROR, msg: RESULT_MSG_ERROR[language] };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
@@ -2,437 +2,8 @@ import {
|
|||||||
RESULT_CODE_ERROR,
|
RESULT_CODE_ERROR,
|
||||||
RESULT_CODE_SUCCESS,
|
RESULT_CODE_SUCCESS,
|
||||||
RESULT_MSG_ERROR,
|
RESULT_MSG_ERROR,
|
||||||
RESULT_MSG_SUCCESS,
|
|
||||||
} from '@/constants/result-constants';
|
} from '@/constants/result-constants';
|
||||||
import { language, request } from '@/plugins/http-fetch';
|
import { language, request } from '@/plugins/http-fetch';
|
||||||
import { parseObjLineToHump } from '@/utils/parse-utils';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询配置参数标签栏
|
|
||||||
* @param neType 网元类型
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function getParamConfigTopTab(neType: string) {
|
|
||||||
// 发起请求
|
|
||||||
const result = await request({
|
|
||||||
url: `/api/rest/databaseManagement/v1/elementType/omc_db/objectType/param_config`,
|
|
||||||
method: 'get',
|
|
||||||
params: {
|
|
||||||
SQL: `SELECT top_display,top_tag,method FROM param_config WHERE ne_type = '${neType}'`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// 解析数据
|
|
||||||
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
|
|
||||||
let data = result.data.data[0];
|
|
||||||
data = data['param_config'];
|
|
||||||
if (Array.isArray(data)) {
|
|
||||||
return Object.assign(result, {
|
|
||||||
data: parseObjLineToHump(data),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Object.assign(result, {
|
|
||||||
data: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询配置参数标签栏对应信息
|
|
||||||
* @param neType 网元类型
|
|
||||||
* @param topTag
|
|
||||||
* @param neId
|
|
||||||
* @returns object { wrRule, dataArr }
|
|
||||||
*/
|
|
||||||
async function getParamConfigInfo(
|
|
||||||
neType: string,
|
|
||||||
topTag: string,
|
|
||||||
neId: string
|
|
||||||
) {
|
|
||||||
return await Promise.allSettled([
|
|
||||||
// 获取参数规则
|
|
||||||
request({
|
|
||||||
url: `/api/rest/databaseManagement/v1/elementType/omc_db/objectType/param_config`,
|
|
||||||
method: 'get',
|
|
||||||
params: {
|
|
||||||
SQL: `SELECT param_json FROM param_config WHERE ne_type = '${neType}' AND top_tag='${topTag}'`,
|
|
||||||
},
|
|
||||||
timeout: 1_000,
|
|
||||||
}),
|
|
||||||
// 获取对应信息
|
|
||||||
request({
|
|
||||||
url: `/api/rest/systemManagement/v1/elementType/${neType.toLowerCase()}/objectType/config/${topTag}`,
|
|
||||||
method: 'get',
|
|
||||||
params: {
|
|
||||||
ne_id: neId,
|
|
||||||
},
|
|
||||||
timeout: 1_000,
|
|
||||||
}),
|
|
||||||
]).then(resArr => {
|
|
||||||
let wrRule: Record<string, any> = {};
|
|
||||||
// 规则数据
|
|
||||||
if (resArr[0].status === 'fulfilled') {
|
|
||||||
const itemV = resArr[0].value;
|
|
||||||
// 解析数据
|
|
||||||
if (
|
|
||||||
itemV.code === RESULT_CODE_SUCCESS &&
|
|
||||||
Array.isArray(itemV.data?.data)
|
|
||||||
) {
|
|
||||||
let itemData = itemV.data.data;
|
|
||||||
const data = itemData[0]['param_config'];
|
|
||||||
if (Array.isArray(data)) {
|
|
||||||
const v = data[0]['param_json'];
|
|
||||||
try {
|
|
||||||
itemData = parseObjLineToHump(JSON.parse(v));
|
|
||||||
wrRule = itemData;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let dataArr: Record<string, any>[] = [];
|
|
||||||
// 对应信息
|
|
||||||
if (resArr[1].status === 'fulfilled') {
|
|
||||||
const itemV = resArr[1].value;
|
|
||||||
// 解析数据
|
|
||||||
if (
|
|
||||||
itemV.code === RESULT_CODE_SUCCESS &&
|
|
||||||
Array.isArray(itemV.data?.data)
|
|
||||||
) {
|
|
||||||
let itemData = itemV.data.data;
|
|
||||||
dataArr = parseObjLineToHump(itemData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { wrRule, dataArr };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询配置参数标签栏对应信息-表格处理
|
|
||||||
* @param neType 网元类型
|
|
||||||
* @param topTag
|
|
||||||
* @param neId
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function getParamConfigInfoTable(
|
|
||||||
neType: string,
|
|
||||||
topTag: string,
|
|
||||||
neId: string
|
|
||||||
) {
|
|
||||||
const { wrRule, dataArr } = await getParamConfigInfo(neType, topTag, neId);
|
|
||||||
|
|
||||||
// UPF参数不统一
|
|
||||||
// if (neType === 'UPF') {
|
|
||||||
// if (Reflect.has(wrRule, 'list')) {
|
|
||||||
// for (const arr of wrRule['list']) {
|
|
||||||
// arr['name'] = parseFirstLower(arr['name']);
|
|
||||||
// }
|
|
||||||
// for (const item of dataArr) {
|
|
||||||
// for (const k in item) {
|
|
||||||
// item[parseFirstLower(k)] = item[k];
|
|
||||||
// Reflect.deleteProperty(item, k);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (Reflect.has(wrRule, 'array')) {
|
|
||||||
// for (const arr of wrRule['array']) {
|
|
||||||
// if (Array.isArray(arr['array'])) {
|
|
||||||
// for (const child of arr['array']) {
|
|
||||||
// child['name'] = parseFirstLower(child['name']);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// arr['name'] = parseFirstLower(arr['name']);
|
|
||||||
// }
|
|
||||||
// for (const item of dataArr) {
|
|
||||||
// for (const k in item) {
|
|
||||||
// // 处理子列表
|
|
||||||
// if (Array.isArray(item[k])) {
|
|
||||||
// for (const child of item[k]) {
|
|
||||||
// for (const childKey in child) {
|
|
||||||
// child[parseFirstLower(childKey)] = child[childKey];
|
|
||||||
// Reflect.deleteProperty(child, childKey);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// item[parseFirstLower(k)] = item[k];
|
|
||||||
// Reflect.deleteProperty(item, k);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 拼装数据
|
|
||||||
const result = {
|
|
||||||
code: RESULT_CODE_SUCCESS,
|
|
||||||
msg: RESULT_MSG_SUCCESS,
|
|
||||||
data: {
|
|
||||||
type: 'list' as 'list' | 'array',
|
|
||||||
data: [] as any[],
|
|
||||||
dataRule: {},
|
|
||||||
columns: [] as any[],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// kv单列表
|
|
||||||
if (Reflect.has(wrRule, 'list')) {
|
|
||||||
result.data.type = 'list';
|
|
||||||
const ruleArr = Object.freeze(wrRule['list']);
|
|
||||||
|
|
||||||
// 列表项数据
|
|
||||||
let dataList = [];
|
|
||||||
for (const item of dataArr) {
|
|
||||||
for (const key of Object.keys(item)) {
|
|
||||||
// 规则为准
|
|
||||||
for (const rule of ruleArr) {
|
|
||||||
if (rule['name'] === key) {
|
|
||||||
const ruleItem = Object.assign({ optional: 'true' }, rule, {
|
|
||||||
value: item[key],
|
|
||||||
});
|
|
||||||
dataList.push(ruleItem);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.data.data = dataList;
|
|
||||||
|
|
||||||
// 列表字段
|
|
||||||
result.data.columns = [
|
|
||||||
{
|
|
||||||
title: 'Key',
|
|
||||||
dataIndex: 'display',
|
|
||||||
align: 'left',
|
|
||||||
width: '30%',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Value',
|
|
||||||
dataIndex: 'value',
|
|
||||||
align: 'left',
|
|
||||||
width: '70%',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 多列表
|
|
||||||
if (Reflect.has(wrRule, 'array')) {
|
|
||||||
result.data.type = 'array';
|
|
||||||
const ruleArr = Object.freeze(wrRule['array']);
|
|
||||||
|
|
||||||
// 列表项数据
|
|
||||||
const dataArray = [];
|
|
||||||
for (const item of dataArr) {
|
|
||||||
let record: Record<string, any> = {};
|
|
||||||
for (const key of Object.keys(item)) {
|
|
||||||
// 规则为准
|
|
||||||
for (const rule of ruleArr) {
|
|
||||||
if (rule['name'] === key) {
|
|
||||||
const ruleItem = Object.assign({ optional: 'true' }, rule, {
|
|
||||||
value: item[key],
|
|
||||||
});
|
|
||||||
record[ruleItem.name] = ruleItem;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dataArray.push(record);
|
|
||||||
}
|
|
||||||
result.data.data = dataArray;
|
|
||||||
|
|
||||||
// 无数据时,用于新增
|
|
||||||
let dataRule: Record<string, any> = {};
|
|
||||||
for (const rule of ruleArr) {
|
|
||||||
dataRule[rule.name] = rule;
|
|
||||||
}
|
|
||||||
result.data.dataRule = dataRule;
|
|
||||||
|
|
||||||
// 列表字段
|
|
||||||
const columns: Record<string, any>[] = [];
|
|
||||||
for (const rule of ruleArr) {
|
|
||||||
columns.push({
|
|
||||||
title: rule.display,
|
|
||||||
dataIndex: rule.name,
|
|
||||||
align: 'left',
|
|
||||||
width: 5,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
result.data.columns = columns;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询配置参数标签栏对应信息-表单结构处理
|
|
||||||
* @param neType 网元类型
|
|
||||||
* @param topTag
|
|
||||||
* @param neId
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function getParamConfigInfoForm(
|
|
||||||
neType: string,
|
|
||||||
topTag: string,
|
|
||||||
neId: string
|
|
||||||
) {
|
|
||||||
const { wrRule, dataArr } = await getParamConfigInfo(neType, topTag, neId);
|
|
||||||
|
|
||||||
// 拼装数据
|
|
||||||
const result = {
|
|
||||||
code: RESULT_CODE_SUCCESS,
|
|
||||||
msg: RESULT_MSG_SUCCESS,
|
|
||||||
data: {
|
|
||||||
type: 'list' as 'list' | 'array',
|
|
||||||
data: [] as Record<string, any>[],
|
|
||||||
dataRule: {},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// kv单列表
|
|
||||||
if (Reflect.has(wrRule, 'list')) {
|
|
||||||
result.data.type = 'list';
|
|
||||||
const ruleArr = Object.freeze(wrRule['list']);
|
|
||||||
|
|
||||||
// 列表项数据
|
|
||||||
const dataList = [];
|
|
||||||
for (const item of dataArr) {
|
|
||||||
for (const key in item) {
|
|
||||||
// 规则为准
|
|
||||||
for (const rule of ruleArr) {
|
|
||||||
if (rule['name'] === key) {
|
|
||||||
const ruleItem = Object.assign({ optional: 'true' }, rule, {
|
|
||||||
value: item[key],
|
|
||||||
});
|
|
||||||
dataList.push(ruleItem);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.data.data = dataList;
|
|
||||||
}
|
|
||||||
// 多列表
|
|
||||||
if (Reflect.has(wrRule, 'array')) {
|
|
||||||
result.data.type = 'array';
|
|
||||||
const ruleArr = Object.freeze(wrRule['array']);
|
|
||||||
// 列表项数据
|
|
||||||
const dataArray = [];
|
|
||||||
for (const item of dataArr) {
|
|
||||||
const index = item['index'];
|
|
||||||
let record: Record<string, any>[] = [];
|
|
||||||
for (const key in 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({ title: `Index-${index}`, key: index, record });
|
|
||||||
}
|
|
||||||
result.data.data = dataArray;
|
|
||||||
|
|
||||||
// 无数据时,用于新增
|
|
||||||
result.data.dataRule = { title: `Index-0`, key: 0, record: ruleArr };
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询配置参数标签栏对应信息子节点
|
|
||||||
* @param neType 网元类型
|
|
||||||
* @param topTag
|
|
||||||
* @param neId
|
|
||||||
* @param loc 子节点(index/字段) 1/dnnList
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export async function getParamConfigInfoChild(
|
|
||||||
neType: string,
|
|
||||||
topTag: string,
|
|
||||||
neId: string,
|
|
||||||
loc: string
|
|
||||||
) {
|
|
||||||
// 发起请求
|
|
||||||
const result = await request({
|
|
||||||
url: `/api/rest/systemManagement/v1/elementType/${neType.toLowerCase()}/objectType/config/${topTag}`,
|
|
||||||
method: 'get',
|
|
||||||
params: {
|
|
||||||
ne_id: neId,
|
|
||||||
loc: loc,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// 解析数据
|
|
||||||
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
|
|
||||||
return Object.assign(result, {
|
|
||||||
data: parseObjLineToHump(result.data.data),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改配置参数标签栏对应信息
|
|
||||||
* @param args 对象 {neType,neId,topTag,loc}
|
|
||||||
* @param data 对象 {修改的数据kv}
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function updateParamConfigInfo(
|
|
||||||
type: 'list' | 'array',
|
|
||||||
args: Record<string, any>,
|
|
||||||
data: Record<string, any>
|
|
||||||
) {
|
|
||||||
let url = `/api/rest/systemManagement/v1/elementType/${args.neType.toLowerCase()}/objectType/config/${
|
|
||||||
args.topTag
|
|
||||||
}?ne_id=${args.neId}`;
|
|
||||||
|
|
||||||
// 多列表需要loc
|
|
||||||
if (type === 'array') {
|
|
||||||
url += `&loc=${args.loc}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return request({
|
|
||||||
url,
|
|
||||||
method: 'put',
|
|
||||||
data: data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增配置参数标签栏对应信息
|
|
||||||
* @param args 对象 {neType,neId,topTag,loc}
|
|
||||||
* @param data 行记录对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function addParamConfigInfo(
|
|
||||||
args: Record<string, any>,
|
|
||||||
data: Record<string, any>
|
|
||||||
) {
|
|
||||||
return request({
|
|
||||||
url: `/api/rest/systemManagement/v1/elementType/${args.neType.toLowerCase()}/objectType/config/${
|
|
||||||
args.topTag
|
|
||||||
}?ne_id=${args.neId}&loc=${args.loc}`,
|
|
||||||
method: 'post',
|
|
||||||
data: data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除配置参数标签栏对应信息
|
|
||||||
* @param args 对象 {neType,neId,topTag,loc}
|
|
||||||
* loc 多层表的定位信息{index0}/{paraName1}/{index1}
|
|
||||||
* @param data 行记录对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function delParamConfigInfo(args: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: `/api/rest/systemManagement/v1/elementType/${args.neType.toLowerCase()}/objectType/config/${
|
|
||||||
args.topTag
|
|
||||||
}?ne_id=${args.neId}&loc=${args.loc}`,
|
|
||||||
method: 'delete',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新网元配置重新载入
|
* 更新网元配置重新载入
|
||||||
@@ -462,3 +33,53 @@ export async function updateNeConfigReload(neType: string, neId: string) {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从参数配置PCF中获取对应信息提供给PCC用户策略输入框
|
||||||
|
* @param neId
|
||||||
|
* @returns object {pccRules,sessionRules,qosTemplate,headerEnrichTemplate,serviceAreaRestriction}
|
||||||
|
*/
|
||||||
|
export async function getPCCRule(neId: any) {
|
||||||
|
const paramNameArr = [
|
||||||
|
'pccRules',
|
||||||
|
'sessionRules',
|
||||||
|
'qosTemplate',
|
||||||
|
'headerEnrichTemplate',
|
||||||
|
'serviceAreaRestriction',
|
||||||
|
];
|
||||||
|
const reqArr = [];
|
||||||
|
for (const paramName of paramNameArr) {
|
||||||
|
reqArr.push(
|
||||||
|
request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
params: { neType: 'PCF', neId, paramName },
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return await Promise.allSettled(reqArr).then(resArr => {
|
||||||
|
// 规则数据
|
||||||
|
const obj: any = {};
|
||||||
|
resArr.forEach((item, i: number) => {
|
||||||
|
if (item.status === 'fulfilled') {
|
||||||
|
const res = item.value;
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
const key = paramNameArr[i];
|
||||||
|
obj[key] = res.data.map((item: any) => {
|
||||||
|
if ('qosTemplate' === key) {
|
||||||
|
return { value: item.qosId, label: item.qosId };
|
||||||
|
}
|
||||||
|
if ('headerEnrichTemplate' === key) {
|
||||||
|
return { value: item.templateName, label: item.templateName };
|
||||||
|
}
|
||||||
|
if ('serviceAreaRestriction' === key) {
|
||||||
|
return { value: item.name, label: item.name };
|
||||||
|
}
|
||||||
|
return { value: item.ruleId, label: item.ruleId };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
|
||||||
import { request } from '@/plugins/http-fetch';
|
|
||||||
import { parseObjLineToHump } from '@/utils/parse-utils';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询软件列表
|
|
||||||
* @param query 查询参数
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function listLicense(query: Record<string, any>) {
|
|
||||||
let totalSQL = 'select count(id) as total from ne_license ';
|
|
||||||
let rowsSQL = ' select * from ne_license ';
|
|
||||||
|
|
||||||
// 查询
|
|
||||||
let querySQL = 'where 1=1';
|
|
||||||
if (query.neType) {
|
|
||||||
querySQL += ` and ne_type like '%${query.neType}%' `;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分页
|
|
||||||
const pageNum = (query.pageNum - 1) * query.pageSize;
|
|
||||||
const limtSql = ` order by created_at desc limit ${pageNum},${query.pageSize} `;
|
|
||||||
|
|
||||||
// 发起请求
|
|
||||||
const result = await request({
|
|
||||||
url: `/api/rest/databaseManagement/v1/select/omc_db/ne_license`,
|
|
||||||
method: 'get',
|
|
||||||
params: {
|
|
||||||
totalSQL: totalSQL + querySQL,
|
|
||||||
rowsSQL: rowsSQL + querySQL + limtSql,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 解析数据
|
|
||||||
if (result.code === RESULT_CODE_SUCCESS) {
|
|
||||||
const data: DataList = {
|
|
||||||
total: 0,
|
|
||||||
rows: [],
|
|
||||||
code: result.code,
|
|
||||||
msg: result.msg,
|
|
||||||
};
|
|
||||||
result.data.data.forEach((item: any) => {
|
|
||||||
const itemData = item['ne_license'];
|
|
||||||
if (Array.isArray(itemData)) {
|
|
||||||
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
|
|
||||||
data.total = itemData[0]['total'];
|
|
||||||
} else {
|
|
||||||
data.rows = itemData.map(v => parseObjLineToHump(v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上传文件
|
|
||||||
* @param data 表单数据对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function uploadLicense(data: FormData) {
|
|
||||||
return request({
|
|
||||||
url: `/api/rest/systemManagement/v1/elementType/${data.get(
|
|
||||||
'nfType'
|
|
||||||
)}/objectType/license?neId=${data.get('nfId')}`,
|
|
||||||
method: 'post',
|
|
||||||
data,
|
|
||||||
dataType: 'form-data',
|
|
||||||
timeout: 180_000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import {
|
||||||
import { request } from '@/plugins/http-fetch';
|
RESULT_CODE_ERROR,
|
||||||
|
RESULT_CODE_SUCCESS,
|
||||||
|
RESULT_MSG_ERROR,
|
||||||
|
} from '@/constants/result-constants';
|
||||||
|
import { language, request } from '@/plugins/http-fetch';
|
||||||
import { parseObjLineToHump } from '@/utils/parse-utils';
|
import { parseObjLineToHump } from '@/utils/parse-utils';
|
||||||
|
import { NE_TYPE_LIST } from '@/constants/ne-constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询网元列表
|
* 查询网元列表
|
||||||
@@ -8,27 +13,8 @@ import { parseObjLineToHump } from '@/utils/parse-utils';
|
|||||||
* @returns object
|
* @returns object
|
||||||
*/
|
*/
|
||||||
export async function listNeInfo(query: Record<string, any>) {
|
export async function listNeInfo(query: Record<string, any>) {
|
||||||
let totalSQL =
|
let totalSQL = 'select count(*) as total from ne_info where 1=1 ';
|
||||||
'select count(*) as total from ne_info where (status=0 or status=3)';
|
let rowsSQL = 'select * from ne_info where 1=1 ';
|
||||||
let rowsSQL = 'select * from ne_info where (status=0 or status=3) ';
|
|
||||||
|
|
||||||
// 系统特定顺序
|
|
||||||
const specificOrder = [
|
|
||||||
'OMC',
|
|
||||||
'MME',
|
|
||||||
'AMF',
|
|
||||||
'AUSF',
|
|
||||||
'UDM',
|
|
||||||
'SMF',
|
|
||||||
'PCF',
|
|
||||||
'UPF',
|
|
||||||
'NRF',
|
|
||||||
'NSSF',
|
|
||||||
'IMS',
|
|
||||||
'N3IWF',
|
|
||||||
'NEF',
|
|
||||||
'LMF',
|
|
||||||
];
|
|
||||||
|
|
||||||
// 查询
|
// 查询
|
||||||
let querySQL = '';
|
let querySQL = '';
|
||||||
@@ -67,8 +53,8 @@ export async function listNeInfo(query: Record<string, any>) {
|
|||||||
data.rows = itemData.map(v => parseObjLineToHump(v));
|
data.rows = itemData.map(v => parseObjLineToHump(v));
|
||||||
//通过sort进行冒泡排序
|
//通过sort进行冒泡排序
|
||||||
data.rows.sort((a: any, b: any) => {
|
data.rows.sort((a: any, b: any) => {
|
||||||
const typeA = specificOrder.indexOf(a.neType);
|
const typeA = NE_TYPE_LIST.indexOf(a.neType);
|
||||||
const typeB = specificOrder.indexOf(b.neType);
|
const typeB = NE_TYPE_LIST.indexOf(b.neType);
|
||||||
if (typeA === -1) return 1; // 如果不在特定顺序中,排到后面
|
if (typeA === -1) return 1; // 如果不在特定顺序中,排到后面
|
||||||
if (typeB === -1) return -1; // 如果不在特定顺序中,排到后面
|
if (typeB === -1) return -1; // 如果不在特定顺序中,排到后面
|
||||||
return typeA - typeB;
|
return typeA - typeB;
|
||||||
@@ -92,15 +78,18 @@ export async function getNeInfo(id: string | number) {
|
|||||||
url: `/api/rest/databaseManagement/v1/select/omc_db/ne_info`,
|
url: `/api/rest/databaseManagement/v1/select/omc_db/ne_info`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: {
|
params: {
|
||||||
SQL: `select * from ne_info where (status=0 or status=3) and id = ${id}`,
|
SQL: `select * from ne_info where id = ${id}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// 解析数据
|
// 解析数据
|
||||||
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
|
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
|
||||||
let data = result.data.data[0];
|
let neInfo = result.data.data[0]['ne_info'];
|
||||||
return Object.assign(result, {
|
if (neInfo) {
|
||||||
data: parseObjLineToHump(data['ne_info'][0]),
|
return Object.assign(result, {
|
||||||
});
|
data: parseObjLineToHump(neInfo[0]),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return { code: RESULT_CODE_ERROR, msg: RESULT_MSG_ERROR[language] };
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -148,57 +137,9 @@ export async function delNeInfo(data: Record<string, any>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取网元网元列表
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function getNelistAll() {
|
|
||||||
// 系统特定顺序
|
|
||||||
const specificOrder = [
|
|
||||||
'OMC',
|
|
||||||
'MME',
|
|
||||||
'AMF',
|
|
||||||
'AUSF',
|
|
||||||
'UDM',
|
|
||||||
'SMF',
|
|
||||||
'PCF',
|
|
||||||
'UPF',
|
|
||||||
'NRF',
|
|
||||||
'NSSF',
|
|
||||||
'IMS',
|
|
||||||
'N3IWF',
|
|
||||||
'NEF',
|
|
||||||
'LMF',
|
|
||||||
];
|
|
||||||
// 发起请求
|
|
||||||
const result = await request({
|
|
||||||
url: `/api/rest/databaseManagement/v1/elementType/omc_db/objectType/ne_info`,
|
|
||||||
method: 'get',
|
|
||||||
params: {
|
|
||||||
SQL: `SELECT ne_type,ne_name,ne_id,ip FROM ne_info WHERE status in ('0','3')`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// 解析数据
|
|
||||||
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
|
|
||||||
let data = result.data.data[0];
|
|
||||||
//通过sort进行冒泡排序
|
|
||||||
data['ne_info'].sort((a: any, b: any) => {
|
|
||||||
const typeA = specificOrder.indexOf(a.ne_type);
|
|
||||||
const typeB = specificOrder.indexOf(b.ne_type);
|
|
||||||
if (typeA === -1) return 1;
|
|
||||||
if (typeB === -1) return -1;
|
|
||||||
return typeA - typeB;
|
|
||||||
});
|
|
||||||
return Object.assign(result, {
|
|
||||||
data: parseObjLineToHump(data['ne_info']),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导出网元配置文件
|
* 导出网元配置文件
|
||||||
* @param
|
* @param data data {neType neId}
|
||||||
* @returns bolb
|
* @returns bolb
|
||||||
*/
|
*/
|
||||||
export function exportSet(data: Record<string, any>) {
|
export function exportSet(data: Record<string, any>) {
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ export async function sendNeSoftware(data: Record<string, any>) {
|
|||||||
url: `/api/rest/systemManagement/v1/${data.neType}/software/${data.version}/${data.neId}`,
|
url: `/api/rest/systemManagement/v1/${data.neType}/software/${data.version}/${data.neId}`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
timeout: 180_000,
|
timeout: 180_000,
|
||||||
|
repeatSubmit: false,
|
||||||
});
|
});
|
||||||
// 解析数据
|
// 解析数据
|
||||||
if (result.code === RESULT_CODE_SUCCESS) {
|
if (result.code === RESULT_CODE_SUCCESS) {
|
||||||
@@ -130,6 +131,7 @@ export async function runNeSoftware(data: Record<string, any>) {
|
|||||||
url: `/api/rest/systemManagement/v1/${data.neType}/software/${data.version}/${data.neId}`,
|
url: `/api/rest/systemManagement/v1/${data.neType}/software/${data.version}/${data.neId}`,
|
||||||
method: 'put',
|
method: 'put',
|
||||||
timeout: 180_000,
|
timeout: 180_000,
|
||||||
|
repeatSubmit: false,
|
||||||
});
|
});
|
||||||
// 解析数据
|
// 解析数据
|
||||||
if (result.code === RESULT_CODE_SUCCESS) {
|
if (result.code === RESULT_CODE_SUCCESS) {
|
||||||
@@ -149,6 +151,7 @@ export async function backNeSoftware(data: Record<string, any>) {
|
|||||||
url: `/api/rest/systemManagement/v1/${data.neType}/software/${data.version}/${data.neId}`,
|
url: `/api/rest/systemManagement/v1/${data.neType}/software/${data.version}/${data.neId}`,
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
timeout: 180_000,
|
timeout: 180_000,
|
||||||
|
repeatSubmit: false,
|
||||||
});
|
});
|
||||||
// 解析数据
|
// 解析数据
|
||||||
if (result.code === RESULT_CODE_SUCCESS) {
|
if (result.code === RESULT_CODE_SUCCESS) {
|
||||||
|
|||||||
@@ -241,6 +241,7 @@ export function listSync() {
|
|||||||
return request({
|
return request({
|
||||||
url: `/api/rest/faultManagement/v1/elementType/all/objectType/alarms`,
|
url: `/api/rest/faultManagement/v1/elementType/all/objectType/alarms`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
timeout: 180_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +295,7 @@ export async function exportAll(query: Record<string, any>) {
|
|||||||
* @returns bolb
|
* @returns bolb
|
||||||
*/
|
*/
|
||||||
export async function origGet() {
|
export async function origGet() {
|
||||||
let totalSQL = `select count(*) as value,orig_severity as name from alarm group by orig_severity`;
|
let totalSQL = `select count(*) as value,orig_severity as name from alarm WHERE alarm_status='1' and orig_severity!='Event' group by orig_severity`;
|
||||||
|
|
||||||
// 发起请求
|
// 发起请求
|
||||||
const result = await request({
|
const result = await request({
|
||||||
@@ -329,10 +330,10 @@ export async function origGet() {
|
|||||||
* @returns object
|
* @returns object
|
||||||
*/
|
*/
|
||||||
export async function top3Sel(filterFlag?: string) {
|
export async function top3Sel(filterFlag?: string) {
|
||||||
let filter = ` WHERE orig_severity='${filterFlag}'`;
|
let filter = ` WHERE alarm_status='1'and orig_severity='${filterFlag}'`;
|
||||||
if (!filterFlag) filter = '';
|
if (!filterFlag) filter = "WHERE alarm_status='1'";
|
||||||
|
|
||||||
let top3SQL = `select count(*) as value,ne_type as name from alarm ${filter} group by ne_type ORDER BY value desc limit 0,3 `;
|
let top3SQL = `select count(*) as value,ne_type as name from alarm ${filter} and orig_severity!='Event' group by ne_type ORDER BY value desc limit 0,3 `;
|
||||||
|
|
||||||
// 发起请求
|
// 发起请求
|
||||||
const result = await request({
|
const result = await request({
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
import { parseObjLineToHump } from '@/utils/parse-utils';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import useUserStore from '@/store/modules/user';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function listAct(query: Record<string, any>) {
|
||||||
|
let totalSQL = `select count(*) as total from alarm_event where 1=1 `;
|
||||||
|
let rowsSQL = `select * from alarm_event where 1=1 `;
|
||||||
|
// 查询
|
||||||
|
let querySQL = '';
|
||||||
|
if (query.alarmCode) {
|
||||||
|
querySQL += ` and alarm_code = '${query.alarmCode}' `;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.alarmType) {
|
||||||
|
querySQL += ` and alarm_type = '${query.alarmType}' `;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.pvFlag) {
|
||||||
|
querySQL += ` and pv_flag = '${query.pvFlag}' `;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.neId) {
|
||||||
|
querySQL += ` and ne_id like '%${query.neId}%' `;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.neName) {
|
||||||
|
querySQL += ` and ne_name like '%${query.neName}%' `;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.neType) {
|
||||||
|
querySQL += ` and ne_type like '%${query.neType}%' `;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.beginTime && query.endTime) {
|
||||||
|
querySQL += ` and event_time BETWEEN '${query.beginTime}' and ' ${query.endTime}'`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
const pageNum = (query.pageNum - 1) * query.pageSize;
|
||||||
|
const limtSql = ` order by event_time desc limit ${pageNum},${query.pageSize} `;
|
||||||
|
|
||||||
|
// 发起请求
|
||||||
|
const result = await request({
|
||||||
|
url: `/api/rest/databaseManagement/v1/select/omc_db/alarm_event`,
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
SQL: totalSQL + querySQL,
|
||||||
|
rowsSQL: rowsSQL + querySQL + limtSql,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 解析数据
|
||||||
|
if (result.code === RESULT_CODE_SUCCESS) {
|
||||||
|
const data: DataList = {
|
||||||
|
total: 0,
|
||||||
|
rows: [],
|
||||||
|
code: result.code,
|
||||||
|
msg: result.msg,
|
||||||
|
};
|
||||||
|
result.data.data.forEach((item: any) => {
|
||||||
|
const itemData = item['alarm_event'];
|
||||||
|
if (Array.isArray(itemData)) {
|
||||||
|
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
|
||||||
|
data.total = itemData[0]['total'];
|
||||||
|
} else {
|
||||||
|
data.rows = itemData.map(v => parseObjLineToHump(v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件告警导出
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns bolb
|
||||||
|
*/
|
||||||
|
export async function exportAll(query: Record<string, any>) {
|
||||||
|
let rowsSQL = `select * from alarm_event where 1=1`;
|
||||||
|
// 查询
|
||||||
|
let querySQL = '';
|
||||||
|
querySQL += query.alarm_code
|
||||||
|
? ` and alarm_code = '${query.alarm_code}' `
|
||||||
|
: '';
|
||||||
|
querySQL += query.alarm_type
|
||||||
|
? ` and alarm_type = '${query.alarm_type}' `
|
||||||
|
: '';
|
||||||
|
querySQL += query.pv_flag ? ` and pv_flag = '${query.pv_flag}' ` : '';
|
||||||
|
querySQL += query.orig_severity
|
||||||
|
? ` and orig_severity in('${query.orig_severity}' )`
|
||||||
|
: '';
|
||||||
|
querySQL += query.ne_id ? ` and ne_id like '%${query.ne_id}%' ` : '';
|
||||||
|
querySQL += query.ne_name ? ` and ne_name like '%${query.ne_name}%' ` : '';
|
||||||
|
querySQL += query.ne_type ? ` and ne_type like '%${query.ne_type}%' ` : '';
|
||||||
|
querySQL +=
|
||||||
|
query.beginTime && query.endTime
|
||||||
|
? ` and event_time BETWEEN '${query.beginTime}' and ' ${query.endTime}'`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
// 发起请求
|
||||||
|
const result = await request({
|
||||||
|
url: `/api/rest/databaseManagement/v1/select/omc_db/alarm_event`,
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
rowsSQL: rowsSQL + querySQL,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.code === RESULT_CODE_SUCCESS) {
|
||||||
|
let v = result.data.data[0];
|
||||||
|
const vArr = parseObjLineToHump(v['alarm_event']);
|
||||||
|
result.data = vArr == null ? [] : vArr;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
+6
-22
@@ -1,29 +1,14 @@
|
|||||||
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 { parseDateToStr } from '@/utils/date-utils';
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { NE_TYPE_LIST } from '@/constants/ne-constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询公告列表
|
* 查询公告列表
|
||||||
* @param query 查询参数
|
* @param query 查询参数
|
||||||
* @returns object
|
* @returns object
|
||||||
*/
|
*/
|
||||||
export async function listMain() {
|
export async function listMain() {
|
||||||
// 系统特定顺序
|
|
||||||
const specificOrder = [
|
|
||||||
'OMC',
|
|
||||||
'MME',
|
|
||||||
'AMF',
|
|
||||||
'AUSF',
|
|
||||||
'UDM',
|
|
||||||
'SMF',
|
|
||||||
'PCF',
|
|
||||||
'UPF',
|
|
||||||
'NRF',
|
|
||||||
'NSSF',
|
|
||||||
'IMS',
|
|
||||||
'N3IWF',
|
|
||||||
'NEF',
|
|
||||||
'LMF',
|
|
||||||
];
|
|
||||||
const result = await request({
|
const result = await request({
|
||||||
url: '/api/rest/systemManagement/v1/elementType/all/objectType/systemState',
|
url: '/api/rest/systemManagement/v1/elementType/all/objectType/systemState',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@@ -39,7 +24,6 @@ export async function listMain() {
|
|||||||
const serialNum = (value as any).serialNum;
|
const serialNum = (value as any).serialNum;
|
||||||
const version = (value as any).version;
|
const version = (value as any).version;
|
||||||
|
|
||||||
|
|
||||||
const errCode = systemState && systemState['errorCode'];
|
const errCode = systemState && systemState['errorCode'];
|
||||||
var time = new Date();
|
var time = new Date();
|
||||||
// console.log(key, value);
|
// console.log(key, value);
|
||||||
@@ -59,21 +43,20 @@ export async function listMain() {
|
|||||||
ipAddress,
|
ipAddress,
|
||||||
serialNum,
|
serialNum,
|
||||||
name: key.split('/').join('_'),
|
name: key.split('/').join('_'),
|
||||||
|
expiryDate: '-',
|
||||||
status: 'Abnormal',
|
status: 'Abnormal',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return mergedObj;
|
return mergedObj;
|
||||||
});
|
});
|
||||||
|
|
||||||
//通过sort进行冒泡排序
|
//通过sort进行冒泡排序
|
||||||
mergedData.sort((a: any, b: any) => {
|
mergedData.sort((a: any, b: any) => {
|
||||||
const typeA = specificOrder.indexOf(a.name.split('_')[0]);
|
const typeA = NE_TYPE_LIST.indexOf(a.name.split('_')[0]);
|
||||||
const typeB = specificOrder.indexOf(b.name.split('_')[0]);
|
const typeB = NE_TYPE_LIST.indexOf(b.name.split('_')[0]);
|
||||||
if (typeA === -1) return 1; // 如果不在特定顺序中,排到后面
|
if (typeA === -1) return 1; // 如果不在特定顺序中,排到后面
|
||||||
if (typeB === -1) return -1; // 如果不在特定顺序中,排到后面
|
if (typeB === -1) return -1; // 如果不在特定顺序中,排到后面
|
||||||
return typeA - typeB;
|
return typeA - typeB;
|
||||||
});
|
});
|
||||||
//console.log(mergedData);
|
|
||||||
|
|
||||||
return mergedData;
|
return mergedData;
|
||||||
}
|
}
|
||||||
@@ -105,5 +88,6 @@ export function getSysConf() {
|
|||||||
return request({
|
return request({
|
||||||
url: `/sys-conf`,
|
url: `/sys-conf`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
whithToken: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取下拉框数据
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getBakFile() {
|
||||||
|
return request({
|
||||||
|
url: '/lm/table/list',
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取对应类型的文件列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getBakFileList(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/lm/file/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远端文件
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function downFile(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/lm/file/${query.fileName}`,
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除远端获取文件
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delFile(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/lm/file/${query.fileName}`,
|
||||||
|
method: 'delete',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ export function login(data: Record<string, string>) {
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
data: data,
|
data: data,
|
||||||
whithToken: false,
|
whithToken: false,
|
||||||
|
crypto: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ export function register(data: Record<string, any>) {
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
data: data,
|
data: data,
|
||||||
whithToken: false,
|
whithToken: false,
|
||||||
|
crypto: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,17 +30,19 @@ export async function getMMLByNE(neType: string) {
|
|||||||
* 发送网元的mml命令
|
* 发送网元的mml命令
|
||||||
* @param neType 网元类型
|
* @param neType 网元类型
|
||||||
* @param neId 网元ID
|
* @param neId 网元ID
|
||||||
|
* @param objectType 接口类型
|
||||||
* @param cmdStr 命令串
|
* @param cmdStr 命令串
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export async function sendMMlByNE(
|
export async function sendMMlByNE(
|
||||||
neType: string,
|
neType: string,
|
||||||
neId: string,
|
neId: string,
|
||||||
|
objectType: string,
|
||||||
cmdArr: string[]
|
cmdArr: string[]
|
||||||
) {
|
) {
|
||||||
// 发起请求
|
// 发起请求
|
||||||
const result = await request({
|
const result = await request({
|
||||||
url: `/api/rest/operationManagement/v1/elementType/${neType}/objectType/mml?ne_id=${neId}`,
|
url: `/api/rest/operationManagement/v1/elementType/${neType}/objectType/${objectType}?ne_id=${neId}`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: { mml: cmdArr },
|
data: { mml: cmdArr },
|
||||||
timeout: 180_000,
|
timeout: 180_000,
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ export function getLoad(query: Record<string, any>) {
|
|||||||
url: '/monitor/load',
|
url: '/monitor/load',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
import {
|
|
||||||
RESULT_CODE_ERROR,
|
|
||||||
RESULT_CODE_SUCCESS,
|
|
||||||
RESULT_MSG_ERROR,
|
|
||||||
} from '@/constants/result-constants';
|
|
||||||
import { language, request } from '@/plugins/http-fetch';
|
|
||||||
import { parseObjLineToHump } from '@/utils/parse-utils';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询用户会话列表
|
|
||||||
* @param query 查询参数
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function listSession(query: Record<string, any>) {
|
|
||||||
let totalSQL = 'select count(*) as total from session where 1=1 ';
|
|
||||||
let rowsSQL = 'select * from session where 1=1 ';
|
|
||||||
|
|
||||||
// 查询
|
|
||||||
let querySQL = '';
|
|
||||||
if (query.accountId) {
|
|
||||||
querySQL += ` and account_id like '%${query.accountId}%' `;
|
|
||||||
}
|
|
||||||
if (query.ip) {
|
|
||||||
querySQL += ` and host like '%${query.ip}%' `;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分页
|
|
||||||
const pageNum = (query.pageNum - 1) * query.pageSize;
|
|
||||||
const limtSql = ` limit ${pageNum},${query.pageSize} `;
|
|
||||||
|
|
||||||
// 排序
|
|
||||||
let sortSql = ' order by login_time ';
|
|
||||||
if (query.sortOrder === 'desc') {
|
|
||||||
sortSql += ' desc ';
|
|
||||||
} else {
|
|
||||||
sortSql += ' asc ';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发起请求
|
|
||||||
const result = await request({
|
|
||||||
url: `/api/rest/databaseManagement/v1/omc_db/session`,
|
|
||||||
method: 'get',
|
|
||||||
params: {
|
|
||||||
totalSQL: totalSQL + querySQL,
|
|
||||||
rowsSQL: rowsSQL + querySQL + sortSql + limtSql,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 解析数据
|
|
||||||
if (result.code === RESULT_CODE_SUCCESS) {
|
|
||||||
const data: DataList = {
|
|
||||||
total: 0,
|
|
||||||
rows: [],
|
|
||||||
code: result.code,
|
|
||||||
msg: result.msg,
|
|
||||||
};
|
|
||||||
result.data.data.forEach((item: any) => {
|
|
||||||
const itemData = item['session'];
|
|
||||||
if (Array.isArray(itemData)) {
|
|
||||||
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
|
|
||||||
data.total = itemData[0]['total'];
|
|
||||||
} else {
|
|
||||||
data.rows = itemData.map(v => parseObjLineToHump(v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 强退用户会话
|
|
||||||
* @param tokenId 授权标识
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function logoutSession(id: string) {
|
|
||||||
const result = await request({
|
|
||||||
url: `/api/rest/databaseManagement/v1/omc_db/session?WHERE=id='${id}'`,
|
|
||||||
method: 'delete',
|
|
||||||
});
|
|
||||||
// 解析数据
|
|
||||||
if (result.code === RESULT_CODE_SUCCESS && result.data.data) {
|
|
||||||
let rows = result.data.data.affectedRows;
|
|
||||||
if (rows) {
|
|
||||||
delete result.data;
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
return { code: RESULT_CODE_ERROR, msg: RESULT_MSG_ERROR[language] };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
@@ -5,5 +5,6 @@ export function getSystemInfo() {
|
|||||||
return request({
|
return request({
|
||||||
url: '/monitor/system-info',
|
url: '/monitor/system-info',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { request } from '@/plugins/http-fetch';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询网元列表
|
|
||||||
* @param query 查询参数
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function listNe(query: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: '/ne/list',
|
|
||||||
method: 'get',
|
|
||||||
params: query,
|
|
||||||
timeout: 60_000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询网元状态
|
|
||||||
* @param neType 网元类型
|
|
||||||
* @param neId 网元ID
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function stateNe(neType: string, neId: string) {
|
|
||||||
return request({
|
|
||||||
url: '/ne/state',
|
|
||||||
method: 'get',
|
|
||||||
params: { neType, neId },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置可用属性值列表指定网元类型全部无分页
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getAllNeConfig(neType: string) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/list/${neType}`,
|
||||||
|
method: 'get',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置数据信息
|
||||||
|
* @param params 数据 {neType,neId,paramName}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getNeConfigData(params: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
params,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置数据更新
|
||||||
|
* @param data 数据 {neType,neId,paramName:"参数名",paramData:{参数},loc:"层级index仅array"}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function editNeConfigData(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置数据新增(array)
|
||||||
|
* @param data 数据 {neType,neId,paramName:"参数名",paramData:{参数},loc:"层级index"}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addNeConfigData(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置数据删除(array)
|
||||||
|
* @param params 数据 {neType,neId,paramName:"参数名",loc:"层级index"}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delNeConfigData(params: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
method: 'delete',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元配置文件备份记录列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listNeConfigBackup(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/config/backup/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元配置文件备份记录修改
|
||||||
|
* @param data 数据 { id, name, remark }
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function updateNeConfigBackup(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/config/backup',
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元配置文件备份记录下载
|
||||||
|
* @param id 记录ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function downNeConfigBackup(id: string) {
|
||||||
|
return await request({
|
||||||
|
url: '/ne/config/backup/download',
|
||||||
|
method: 'get',
|
||||||
|
params: { id },
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元配置文件备份记录删除
|
||||||
|
* @param id 记录ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function delNeConfigBackup(id: string) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/config/backup',
|
||||||
|
method: 'delete',
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元配置文件备份导出
|
||||||
|
* @param data 数据 { neType, neId }
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function exportNeConfigBackup(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/config/backup/export',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元配置文件备份导入
|
||||||
|
* @param data 数据 { neType, neId, type, path }
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function importNeConfigBackup(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/config/backup/import',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询网元主机列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listNeHost(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/host/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询网元主机详细
|
||||||
|
* @param hostId 网元主机ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getNeHost(hostId: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/host/${hostId}`,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增网元主机
|
||||||
|
* @param data 网元主机对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addNeHost(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/host',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改网元主机
|
||||||
|
* @param data 网元主机对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function updateNeHost(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/host',
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除网元主机
|
||||||
|
* @param hostId 网元主机ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delNeHost(hostId: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/host/${hostId}`,
|
||||||
|
method: 'delete',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试连接网元主机
|
||||||
|
* @param data 网元主机对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function testNeHost(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/host/test',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元主机SSH方式检查服务器环境
|
||||||
|
* @param data 网元主机对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function neHostCheckInfo(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/host/checkBySSH',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元主机SSH方式授权免密发送
|
||||||
|
* @param data 网元主机对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function neHostAuthorizedRSA(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/host/authorizedBySSH',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询网元主机命令列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listNeHostCmd(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/hostCmd/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询网元主机命令详细
|
||||||
|
* @param cmdId 网元主机命令ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getNeHostCmd(cmdId: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/hostCmd/${cmdId}`,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增网元主机命令
|
||||||
|
* @param data 网元主机命令对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addNeHostCmd(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/hostCmd',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改网元主机命令
|
||||||
|
* @param data 网元主机命令对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function updateNeHostCmd(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/hostCmd',
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除网元主机命令
|
||||||
|
* @param cmdId 网元主机命令ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delNeHostCmd(cmdId: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/hostCmd/${cmdId}`,
|
||||||
|
method: 'delete',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询网元列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listNeInfo(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/info/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询网元信息详细
|
||||||
|
* @param infoId 信息ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getNeInfo(infoId: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/info/${infoId}`,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元信息新增
|
||||||
|
* @param data 网元对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addNeInfo(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/info`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
crypto: true,
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元信息修改
|
||||||
|
* @param data 网元对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function updateNeInfo(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/info`,
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
crypto: true,
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元信息删除
|
||||||
|
* @param id 信息ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delNeInfo(infoIds: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/info/${infoIds}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询网元列表全部无分页
|
||||||
|
* @param query 查询参数 neType neId bandStatus bandHost
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listAllNeInfo(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/info/listAll',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询网元状态
|
||||||
|
* @param neType 网元类型
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function stateNeInfo(neType: string, neId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/info/state',
|
||||||
|
method: 'get',
|
||||||
|
params: { neType, neId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询网元信息
|
||||||
|
* @param neType 网元类型
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getNeInfoByTypeAndID(neType: string, neId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/info/byTypeAndID',
|
||||||
|
method: 'get',
|
||||||
|
params: { neType, neId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元端OAM配置文件读取
|
||||||
|
* @param neType 网元类型
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getOAMFile(neType: string, neId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/info/oamFile',
|
||||||
|
method: 'get',
|
||||||
|
params: { neType, neId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元端配置文件写入
|
||||||
|
* @param neType 网元类型
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param content 用json对象
|
||||||
|
* @param sync 同步到网元
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function saveOAMFile(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/info/oamFile`,
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元端公共配置文件读取
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getPara5GFilee() {
|
||||||
|
return request({
|
||||||
|
url: '/ne/info/para5GFile',
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元端公共配置文件写入
|
||||||
|
* @param content txt内容为字符串 其他文件格式都用json对象
|
||||||
|
* @param syncNe 同步到网元端 NeType@ NeId
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function savePara5GFile(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/info/para5GFile`,
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元服务操作
|
||||||
|
* @param data 对象 {neType,neId,action}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function serviceNeAction(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/action/service`,
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询网元授权列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listNeLicense(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/license/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询网元授权详细
|
||||||
|
* @param licenseId 信息ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getNeLicense(licenseId: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/license/${licenseId}`,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元neType和neID查询
|
||||||
|
* @param neType 网元类型
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getNeLicenseByTypeAndID(neType: string, neId: string) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/license/byTypeAndID`,
|
||||||
|
method: 'get',
|
||||||
|
params: { neType, neId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元授权激活授权申请码
|
||||||
|
* @param neType 网元类型
|
||||||
|
* @param neId 网元id
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function codeNeLicense(neType: string, neId: string) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/license/code`,
|
||||||
|
method: 'get',
|
||||||
|
params: { neType, neId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元授权激活授权文件替换
|
||||||
|
* @param data 网元对象 {"neType": "", "neId": "", "licensePath": "", "reload": true}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function changeNeLicense(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/license/change`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元授权激活状态
|
||||||
|
* @param neType 网元类型
|
||||||
|
* @param neId 网元id
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function stateNeLicense(neType: string, neId: string) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/license/state`,
|
||||||
|
method: 'get',
|
||||||
|
params: { neType, neId },
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询网元版本列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listNeSoftware(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/software/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询网元软件包详细
|
||||||
|
* @param softwareId 信息ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getNeSoftware(softwareId: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/software/${softwareId}`,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元软件包新增
|
||||||
|
* @param data 网元对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addNeSoftware(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/software`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
repeatSubmit: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元软件包修改
|
||||||
|
* @param data 网元对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function updateNeSoftware(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/software`,
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元软件包删除
|
||||||
|
* @param id 信息ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delNeSoftware(softwareIds: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/software/${softwareIds}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元软件包设为网元新版本
|
||||||
|
* @param data data { "version": "2.2404.18", "neType": "SMF", "name": "smf-r2.2404.18-ub22.deb"}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function newNeVersion(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/software/newNeVersion`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询网元版本列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listNeVersion(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/ne/version/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询网元版本详细
|
||||||
|
* @param versionId 信息ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getNeVersion(versionId: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/version/${versionId}`,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元版本操作
|
||||||
|
* @param data {neType,neId,preinput:{参数},action:"upgrade"}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function operateNeVersion(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/version/operate`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询AMF-UE会话事件列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listAMFDataUE(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/amf/ue/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AMF-UE会话删除
|
||||||
|
* @param id 信息ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delAMFDataUE(ueIds: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/amf/ue/${ueIds}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AMF-UE会话列表导出
|
||||||
|
* @param data 查询列表条件
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function exportAMFDataUE(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/amf/ue/export',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询IMS-CDR会话事件
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listIMSDataCDR(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/ims/cdr/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IMS-CDR会话删除
|
||||||
|
* @param id 信息ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delIMSDataCDR(cdrIds: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/ims/cdr/${cdrIds}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IMS-CDR会话列表导出
|
||||||
|
* @param data 查询列表条件
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function exportIMSDataCDR(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/ims/cdr/export',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询MME-UE会话事件列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listMMEDataUE(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/mme/ue/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MME-UE会话删除
|
||||||
|
* @param id 信息ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delMMEDataUE(ueIds: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/mme/ue/${ueIds}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MME-UE会话列表导出
|
||||||
|
* @param data 查询列表条件
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function exportMMEDataUE(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/mme/ue/export',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询SMF-CDR会话事件
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listSMFDataCDR(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/smf/cdr/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMF-CDR会话删除
|
||||||
|
* @param id 信息ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delSMFDataCDR(cdrIds: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/smf/cdr/${cdrIds}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMF-CDR会话列表导出
|
||||||
|
* @param data 查询列表条件
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function exportSMFDataCDR(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/smf/cdr/export',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMF-在线订阅用户列表信息
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listSMFSubscribers(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/smf/subscribers',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询SMSC-CDR会话事件
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listSMSCDataCDR(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/smsc/cdr/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMSC-CDR会话删除
|
||||||
|
* @param id 信息ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delSMSCDataCDR(cdrIds: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/smsc/cdr/${cdrIds}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMSC-CDR会话列表导出
|
||||||
|
* @param data 查询列表条件
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function exportSMSCDataCDR(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/smsc/cdr/export',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM鉴权用户重载数据
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function resetUDMAuth(neId: string) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/auth/resetData/${neId}`,
|
||||||
|
method: 'put',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM鉴权用户列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listUDMAuth(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/udm/auth/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM鉴权用户信息
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param imsi IMSI
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getUDMAuth(neId: string, imsi: string) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/auth/${neId}/${imsi}`,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM鉴权用户新增
|
||||||
|
* @param data 鉴权对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addUDMAuth(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/auth/${data.neId}`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM鉴权用户批量新增
|
||||||
|
* @param data 鉴权对象
|
||||||
|
* @param num 数量
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function batchAddUDMAuth(data: Record<string, any>, num: number) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/auth/${data.neId}/${num}`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM鉴权用户修改
|
||||||
|
* @param data 鉴权对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function updateUDMAuth(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/auth/${data.neId}`,
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM鉴权用户删除
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param imsi IMSI
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delUDMAuth(neId: string, imsi: string) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/auth/${neId}/${imsi}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM鉴权用户批量删除
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param imsi IMSI
|
||||||
|
* @param num 数量
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function batchDelUDMAuth(neId: string, imsi: string, num: number) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/auth/${neId}/${imsi}/${num}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM鉴权用户导入
|
||||||
|
* @param data 表单数据对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function importUDMAuth(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/auth/import`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM鉴权用户导出
|
||||||
|
* @param data 数据参数
|
||||||
|
* @returns bolb
|
||||||
|
*/
|
||||||
|
export function exportUDMAuth(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/udm/auth/export',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM签约用户重载数据
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function resetUDMSub(neId: string) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/sub/resetData/${neId}`,
|
||||||
|
method: 'put',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM签约用户列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listUDMSub(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/udm/sub/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM签约用户信息
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param imsi IMSI
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getUDMSub(neId: string, imsi: string) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/sub/${neId}/${imsi}`,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM签约用户新增
|
||||||
|
* @param data 签约对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addUDMSub(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/sub/${data.neId}`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM签约用户批量新增
|
||||||
|
* @param data 签约对象
|
||||||
|
* @param num 数量
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function batchAddUDMSub(data: Record<string, any>, num: number) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/sub/${data.neId}/${num}`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM签约用户修改
|
||||||
|
* @param data 签约对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function updateUDMSub(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/sub/${data.neId}`,
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM签约用户删除
|
||||||
|
* @param data 签约对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delUDMSub(neId: string, imsi: string) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/sub/${neId}/${imsi}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM签约用户批量删除
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param imsi IMSI
|
||||||
|
* @param num 数量
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function batchDelUDMSub(neId: string, imsi: string, num: number) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/sub/${neId}/${imsi}/${num}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM签约用户导出
|
||||||
|
* @param data 数据参数
|
||||||
|
* @returns bolb
|
||||||
|
*/
|
||||||
|
export function exportUDMSub(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/udm/sub/export',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDM签约用户导入
|
||||||
|
* @param data 表单数据对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function importUDMSub(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/sub/import`,
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
import { request } from '@/plugins/http-fetch';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 签约鉴权导出
|
|
||||||
* @param query 查询参数
|
|
||||||
* @returns bolb
|
|
||||||
*/
|
|
||||||
export function exportAuth(query: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: '/ne/udm/auth/export',
|
|
||||||
method: 'post',
|
|
||||||
data: query,
|
|
||||||
responseType: 'blob',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导入鉴权数据
|
|
||||||
* @param neId 网元ID
|
|
||||||
* @param data 表单数据对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function importAuthData(data: FormData) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/auth/import`,
|
|
||||||
method: 'post',
|
|
||||||
data,
|
|
||||||
dataType: 'form-data',
|
|
||||||
timeout: 180_000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询鉴权列表
|
|
||||||
* @param query 查询参数
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function listAuth(query: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: '/ne/udm/auth/list',
|
|
||||||
method: 'get',
|
|
||||||
params: query,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询重新更新加载全部
|
|
||||||
* @param neId 网元ID
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function loadAuth(neId: string) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/auth/resetData/${neId}`,
|
|
||||||
method: 'put',
|
|
||||||
timeout: 180_000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询鉴权详细
|
|
||||||
* @param neId 网元ID
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function getAuth(neId: string, imsi: string) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/auth/${neId}/${imsi}`,
|
|
||||||
method: 'get',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改鉴权
|
|
||||||
* @param data 鉴权对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function updateAuth(data: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/auth/${data.neId}`,
|
|
||||||
method: 'put',
|
|
||||||
data: data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增鉴权
|
|
||||||
* @param data 鉴权对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function addAuth(data: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/auth/${data.neId}`,
|
|
||||||
method: 'post',
|
|
||||||
data: data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量新增鉴权
|
|
||||||
* @param data 鉴权对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function batchAuth(data: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/auth/${data.neID}/${data.num}`,
|
|
||||||
method: 'post',
|
|
||||||
data: data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除鉴权
|
|
||||||
* @param data 鉴权对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function delAuth(neId: string, imsi: string) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/auth/${neId}/${imsi}`,
|
|
||||||
method: 'delete',
|
|
||||||
timeout: 180_000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量删除鉴权
|
|
||||||
* @param data 鉴权对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function batchDelAuth(data: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/auth/${data.neID}/${data.imsi}/${data.num}`,
|
|
||||||
method: 'delete',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -25,8 +25,10 @@ export async function listBase5G(query: Record<string, any>) {
|
|||||||
data.total = rows.length;
|
data.total = rows.length;
|
||||||
data.rows = rows;
|
data.rows = rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 模拟数据
|
// 模拟数据
|
||||||
// data.rows =[{"address":"192.168.1.137:38412","id":"217","name":"","ueNum":0}]
|
// data.rows = [{"address":"192.168.1.137:38412","id":"217","name":"attach-enb-100000-20","ueNum":0}]
|
||||||
|
// data.rows = [{address: "192.168.8.223", id: 257, name: "SmallCell", ueNum: 0}]
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -52,9 +52,11 @@ export async function listUENumByIMS(neId: String) {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
if (result.code === RESULT_CODE_SUCCESS) {
|
if (result.code === RESULT_CODE_SUCCESS) {
|
||||||
return Object.assign(result, {
|
let num = result.data['ueNum'] || 0;
|
||||||
data: result.data['ueNum'],
|
if (num === 0) {
|
||||||
});
|
num = result.data.data['ueNum'] || 0;
|
||||||
|
}
|
||||||
|
return Object.assign(result, { data: num });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 模拟数据
|
// 模拟数据
|
||||||
|
|||||||
+31
-14
@@ -136,14 +136,21 @@ export async function batchUpdateRule(data: Record<string, any>) {
|
|||||||
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/${data.num}?neId=${data.neId}`,
|
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/${data.num}?neId=${data.neId}`,
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data,
|
data: data,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
// 解析数据
|
// 解析数据
|
||||||
if (result.code === RESULT_CODE_SUCCESS && result.data?.status) {
|
if (result.code === RESULT_CODE_SUCCESS) {
|
||||||
return {
|
if (result.data?.status) {
|
||||||
code: RESULT_CODE_ERROR,
|
return {
|
||||||
msg: result.data?.cause,
|
code: RESULT_CODE_ERROR,
|
||||||
data: result.data,
|
msg: result.data?.cause,
|
||||||
};
|
data: result.data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (result.data?.data) {
|
||||||
|
result.data = result.data.data;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -158,6 +165,7 @@ export async function addRule(data: Record<string, any>) {
|
|||||||
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo?neId=${data.neId}`,
|
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo?neId=${data.neId}`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data,
|
data: data,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
// 解析数据
|
// 解析数据
|
||||||
if (result.code === RESULT_CODE_SUCCESS && result.data?.status) {
|
if (result.code === RESULT_CODE_SUCCESS && result.data?.status) {
|
||||||
@@ -180,14 +188,21 @@ export async function batchAddRule(data: Record<string, any>) {
|
|||||||
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/${data.num}?neId=${data.neId}`,
|
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/${data.num}?neId=${data.neId}`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data,
|
data: data,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
// 解析数据
|
// 解析数据
|
||||||
if (result.code === RESULT_CODE_SUCCESS && result.data?.status) {
|
if (result.code === RESULT_CODE_SUCCESS) {
|
||||||
return {
|
if (result.data?.status) {
|
||||||
code: RESULT_CODE_ERROR,
|
return {
|
||||||
msg: result.data?.cause,
|
code: RESULT_CODE_ERROR,
|
||||||
data: result.data,
|
msg: result.data?.cause,
|
||||||
};
|
data: result.data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (result.data?.data) {
|
||||||
|
result.data = result.data.data;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -197,10 +212,11 @@ export async function batchAddRule(data: Record<string, any>) {
|
|||||||
* @param data 规则对象
|
* @param data 规则对象
|
||||||
* @returns object
|
* @returns object
|
||||||
*/
|
*/
|
||||||
export function delRule(neId: string, data: Record<string, any>) {
|
export function delRule(neId: string, imsi: string) {
|
||||||
return request({
|
return request({
|
||||||
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo?neId=${neId}&imsi=${data.imsi}`,
|
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo?neId=${neId}&imsi=${imsi}`,
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,5 +229,6 @@ export async function batchDelRule(data: Record<string, any>) {
|
|||||||
return request({
|
return request({
|
||||||
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/${data.num}?neId=${data.neId}&imsi=${data.imsi}`,
|
url: `/api/rest/ueManagement/v1/elementType/pcf/objectType/ueInfo/batch/${data.num}?neId=${data.neId}&imsi=${data.imsi}`,
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
+41
-15
@@ -1,6 +1,5 @@
|
|||||||
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';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询列表
|
* 查询列表
|
||||||
@@ -21,39 +20,66 @@ export async function listUEInfoBySMF(query: Record<string, any>) {
|
|||||||
msg: result.msg,
|
msg: result.msg,
|
||||||
};
|
};
|
||||||
// 解析数据
|
// 解析数据
|
||||||
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
|
if (result.code === RESULT_CODE_SUCCESS && result.data) {
|
||||||
const rows = parseObjLineToHump(result.data.data);
|
if (Array.isArray(result.data.data)) {
|
||||||
data.total = rows.length;
|
const rows = result.data.data;
|
||||||
data.rows = rows;
|
data.total = rows.length;
|
||||||
|
data.rows = rows;
|
||||||
|
} else {
|
||||||
|
Object.assign(data, result.data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 模拟数据
|
// 模拟数据
|
||||||
|
// data.code = RESULT_CODE_SUCCESS;
|
||||||
|
// data.total = 2;
|
||||||
// data.rows = [
|
// data.rows = [
|
||||||
// {
|
// {
|
||||||
// imsi: 'imsi-460029004200044',
|
// imsi: 'imsi-460000100000090',
|
||||||
// msisdn: 'msisdn-12346002044',
|
// msisdn: 'msisdn-12307550090',
|
||||||
// pduSessionInfo: [
|
// pduSessionInfo: [
|
||||||
// {
|
// {
|
||||||
// activeTime: '2023-11-29 18:39:06',
|
// activeTime: '2024-06-19 14:35:26',
|
||||||
// dnn: 'ims',
|
// dnn: 'ims',
|
||||||
// ipv4: '10.10.48.97',
|
// ipv4: '10.10.48.8',
|
||||||
// ipv6: '',
|
// ipv6: '',
|
||||||
// pduSessionID: 6,
|
// pduSessionID: 6,
|
||||||
// ranN3IP: '192.168.8.223',
|
// ranN3IP: '192.168.1.137',
|
||||||
// sstSD: '1-000001',
|
// sstSD: '1-000001',
|
||||||
// tai: '46000-0001',
|
// tai: '46000-001124',
|
||||||
|
// upState: 'Active',
|
||||||
// upfN3IP: '192.168.1.161',
|
// upfN3IP: '192.168.1.161',
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// activeTime: '2023-11-29 18:39:05',
|
// activeTime: '2024-06-19 14:35:26',
|
||||||
// dnn: 'cmnet',
|
// dnn: 'cmnet',
|
||||||
// ipv4: '10.10.48.62',
|
// ipv4: '10.10.48.9',
|
||||||
|
// ipv6: '2001:4860:4860::/64',
|
||||||
|
// pduSessionID: 7,
|
||||||
|
// ranN3IP: '192.168.1.137',
|
||||||
|
// sstSD: '1-000001',
|
||||||
|
// tai: '46000-001124',
|
||||||
|
// upState: 'Active',
|
||||||
|
// upfN3IP: '192.168.1.161',
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// ratType: 'NR',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// imsi: 'imsi-460602072701180',
|
||||||
|
// msisdn: 'msisdn-123460600080',
|
||||||
|
// pduSessionInfo: [
|
||||||
|
// {
|
||||||
|
// activeTime: '2024-06-19 14:31:09',
|
||||||
|
// dnn: 'cmnet',
|
||||||
|
// ipv4: '10.10.48.4',
|
||||||
// ipv6: '',
|
// ipv6: '',
|
||||||
// pduSessionID: 5,
|
// pduSessionID: 5,
|
||||||
// ranN3IP: '192.168.8.223',
|
// ranN3IP: '192.168.8.223',
|
||||||
// sstSD: '1-000001',
|
// sstSD: '1-000001',
|
||||||
// tai: '46000-0001',
|
// tai: '46060-0001',
|
||||||
// upfN3IP: '192.168.1.163',
|
// upState: 'Active',
|
||||||
|
// upfN3IP: '192.168.1.161',
|
||||||
// },
|
// },
|
||||||
// ],
|
// ],
|
||||||
// ratType: 'EUTRAN',
|
// ratType: 'EUTRAN',
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
import { request } from '@/plugins/http-fetch';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 签约列表导出
|
|
||||||
* @param query 查询参数
|
|
||||||
* @returns bolb
|
|
||||||
*/
|
|
||||||
export function exportSub(query: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: '/ne/udm/sub/export',
|
|
||||||
method: 'post',
|
|
||||||
data: query,
|
|
||||||
responseType: 'blob',
|
|
||||||
timeout: 180_000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导入签约数据
|
|
||||||
* @param neId 网元ID
|
|
||||||
* @param data 表单数据对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function importSubData(data: FormData) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/sub/import`,
|
|
||||||
method: 'post',
|
|
||||||
data,
|
|
||||||
dataType: 'form-data',
|
|
||||||
timeout: 180_000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询重新更新加载全部
|
|
||||||
* @param neId 网元ID
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function loadSub(neId: string) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/sub/resetData/${neId}`,
|
|
||||||
method: 'put',
|
|
||||||
timeout: 180_000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询签约列表
|
|
||||||
* @param query 查询参数
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function listSub(query: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: '/ne/udm/sub/list',
|
|
||||||
method: 'get',
|
|
||||||
params: query,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询签约详细
|
|
||||||
* @param neId 网元ID
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function getSub(neId: string, imsi: string) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/sub/${neId}/${imsi}`,
|
|
||||||
method: 'get',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改签约
|
|
||||||
* @param data 签约对象
|
|
||||||
* @param neId 网元ID
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function updateSub(neId: string, data: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/sub/${neId}`,
|
|
||||||
method: 'put',
|
|
||||||
data: data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增签约
|
|
||||||
* @param data 签约对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function addSub(neID: string, data: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/sub/${neID}`,
|
|
||||||
method: 'post',
|
|
||||||
data: data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量新增新增签约
|
|
||||||
* @param data 签约对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function batchAddSub(data: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/sub/${data.neID}/${data.num}`,
|
|
||||||
method: 'post',
|
|
||||||
data: data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除签约
|
|
||||||
* @param data 签约对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function delSub(neId: string, imsi: string) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/sub/${neId}/${imsi}`,
|
|
||||||
method: 'delete',
|
|
||||||
timeout: 180_000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量删除签约
|
|
||||||
* @param data 签约对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function batchDelSub(data: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/udm/sub/${data.neID}/${data.imsi}/${data.num}`,
|
|
||||||
method: 'delete',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
import { parseObjLineToHump } from '@/utils/parse-utils';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新 查询自定义指标数据
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function listCustomData(query: Record<string, any>) {
|
||||||
|
// 发起请求
|
||||||
|
const result = await request({
|
||||||
|
url: `/pm/kpiC/report`,
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
import { parseObjLineToHump } from '@/utils/parse-utils';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询自定义指标
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
// export async function listCustom(query: Record<string, any>) {
|
||||||
|
// let totalSQL = 'select count(*) as total from pm_custom_title where 1=1 ';
|
||||||
|
// let rowsSQL = 'select * from pm_custom_title where 1=1 ';
|
||||||
|
|
||||||
|
// // 查询
|
||||||
|
// let querySQL = '';
|
||||||
|
// if (query.neType) {
|
||||||
|
// querySQL += ` and ne_type like '%${query.neType}%' `;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 排序
|
||||||
|
// let sortSql = ' order by update_time ';
|
||||||
|
// if (query.sortOrder === 'asc') {
|
||||||
|
// sortSql += ' asc ';
|
||||||
|
// } else {
|
||||||
|
// sortSql += ' desc ';
|
||||||
|
// }
|
||||||
|
// // 分页
|
||||||
|
// const pageNum = (query.pageNum - 1) * query.pageSize;
|
||||||
|
// const limtSql = ` limit ${pageNum},${query.pageSize} `;
|
||||||
|
|
||||||
|
// // 发起请求
|
||||||
|
// const result = await request({
|
||||||
|
// url: `/api/rest/databaseManagement/v1/select/omc_db/pm_custom_title`,
|
||||||
|
// method: 'get',
|
||||||
|
// params: {
|
||||||
|
// totalSQL: totalSQL + querySQL,
|
||||||
|
// rowsSQL: rowsSQL + querySQL + sortSql + limtSql,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // 解析数据
|
||||||
|
// if (result.code === RESULT_CODE_SUCCESS) {
|
||||||
|
// const data: DataList = {
|
||||||
|
// total: 0,
|
||||||
|
// rows: [],
|
||||||
|
// code: result.code,
|
||||||
|
// msg: result.msg,
|
||||||
|
// };
|
||||||
|
// result.data.data.forEach((item: any) => {
|
||||||
|
// const itemData = item['pm_custom_title'];
|
||||||
|
// if (Array.isArray(itemData)) {
|
||||||
|
// if (itemData.length === 1 && itemData[0]['total'] >= 0) {
|
||||||
|
// data.total = itemData[0]['total'];
|
||||||
|
// } else {
|
||||||
|
// data.rows = itemData.map(v => parseObjLineToHump(v));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// return data;
|
||||||
|
// }
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新 查询自定义指标
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function listCustom(query?: Record<string, any>) {
|
||||||
|
// 发起请求
|
||||||
|
const result = await request({
|
||||||
|
url: `/pm/kpiC/title/totalList`,
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询自定义指标详细
|
||||||
|
* @param id 网元ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function getCustom(id: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/pm/kpiC/title/${id}`,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增自定义指标
|
||||||
|
* @param data 网元对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addCustom(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/pm/kpiC/title`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改自定义指标
|
||||||
|
* @param data 网元对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function updateCustom(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/pm/kpiC/title/${data.id}`,
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除自定义指标
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function delCustom(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/pm/kpiC/title/${data.id}`,
|
||||||
|
method: 'delete',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
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 { parseDateToStr } from '@/utils/date-utils';
|
|
||||||
import { parseObjLineToHump } from '@/utils/parse-utils';
|
import { parseObjLineToHump } from '@/utils/parse-utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Todo 废弃
|
||||||
* 查询黄金指标数据
|
* 查询黄金指标数据
|
||||||
* @param query 查询参数
|
* @param query 查询参数
|
||||||
* @returns object
|
* @returns object
|
||||||
@@ -110,6 +110,7 @@ export async function getKPITitle(neType: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Todo 废弃
|
||||||
* 查询UPF上下行速率数据
|
* 查询UPF上下行速率数据
|
||||||
* @param query 查询参数
|
* @param query 查询参数
|
||||||
* @returns object
|
* @returns object
|
||||||
@@ -126,9 +127,7 @@ export async function listUPFData(timeArr: any) {
|
|||||||
url: `/api/rest/databaseManagement/v1/select/omc_db/gold_kpi`,
|
url: `/api/rest/databaseManagement/v1/select/omc_db/gold_kpi`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: {
|
params: {
|
||||||
SQL: `SELECT gold_kpi.*,kpi_title.en_title FROM gold_kpi LEFT JOIN kpi_title on gold_kpi.kpi_id=kpi_title.kpi_id where 1=1 and gold_kpi.kpi_id ='UPF.03' and timestamp BETWEEN '${parseDateToStr(
|
SQL: `SELECT gold_kpi.*,kpi_title.en_title FROM gold_kpi LEFT JOIN kpi_title on gold_kpi.kpi_id=kpi_title.kpi_id where 1=1 and gold_kpi.kpi_id ='UPF.03' AND timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 10 MINUTE) AND NOW()`,
|
||||||
twentyFourHoursAgo
|
|
||||||
)}' AND '${parseDateToStr(initTime)}' `,
|
|
||||||
},
|
},
|
||||||
timeout: 60_000,
|
timeout: 60_000,
|
||||||
}),
|
}),
|
||||||
@@ -137,9 +136,7 @@ export async function listUPFData(timeArr: any) {
|
|||||||
url: `/api/rest/databaseManagement/v1/select/omc_db/gold_kpi`,
|
url: `/api/rest/databaseManagement/v1/select/omc_db/gold_kpi`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: {
|
params: {
|
||||||
SQL: `SELECT gold_kpi.*,kpi_title.en_title FROM gold_kpi LEFT JOIN kpi_title on gold_kpi.kpi_id=kpi_title.kpi_id where 1=1 and gold_kpi.kpi_id ='UPF.06' and timestamp BETWEEN '${parseDateToStr(
|
SQL: `SELECT gold_kpi.*,kpi_title.en_title FROM gold_kpi LEFT JOIN kpi_title on gold_kpi.kpi_id=kpi_title.kpi_id where 1=1 and gold_kpi.kpi_id ='UPF.06' AND timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 10 MINUTE) AND NOW()`,
|
||||||
twentyFourHoursAgo
|
|
||||||
)}' AND '${parseDateToStr(initTime)}' `,
|
|
||||||
},
|
},
|
||||||
timeout: 60_000,
|
timeout: 60_000,
|
||||||
}),
|
}),
|
||||||
|
|||||||
Loaded 100 of 358 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user