Compare commits

..
50 Commits
Author SHA1 Message Date
TsMask 2fb3467fb5 mager: 合并11.2版本 2024-11-23 10:57:55 +08:00
TsMask 63d7d11350 Merge remote-tracking branch 'origin/main' into practical-training 2024-08-03 14:11:19 +08:00
TsMask 2bd98540be Merge remote-tracking branch 'origin/main' into practical-training 2024-08-01 17:53:53 +08:00
TsMask 630c63e23f feat: 配置数据导出成表格,多语言翻译 2024-08-01 16:07:17 +08:00
TsMask ada1f388fc fix: 参数配置array展开显示异常 2024-07-30 09:32:52 +08:00
TsMask b9cf34714f feat: 网元信息允许教师备份操作权限控制 2024-07-29 15:28:22 +08:00
TsMask 53200d5f41 feat: 网元配置备份操作权限控制删除/编辑按钮 2024-07-29 15:27:50 +08:00
TsMask 52d5b9b732 style: 学生关闭右击样式 2024-07-27 16:14:59 +08:00
TsMask 292488f20c fix: 配置载入时禁用切换网元 2024-07-27 16:13:08 +08:00
TsMask b0b844f0f6 fix: 学生禁止操作删除日志CDR/Event 2024-07-27 16:11:59 +08:00
TsMask 0819fbdb44 Merge remote-tracking branch 'origin/main' into practical-training 2024-07-27 15:03:16 +08:00
TsMask a88ae826cc Merge remote-tracking branch 'origin/main' into practical-training 2024-07-27 14:16:15 +08:00
TsMask 76499d98e0 fix: 参数数据与示例对比差异显示功能 2024-07-22 18:16:48 +08:00
TsMask 94ccb45f8c style: 配置历史记录抽屉展开的title初始为空避免显示错误 2024-07-22 18:14:29 +08:00
TsMask 79e2b4ac26 feat: 添加是否系统管理员的权限判断函数 2024-07-22 11:46:23 +08:00
TsMask b2510c0eb3 fix: 班级学生列表搜索时刷新合并申请状态,可直接应用和退回操作 2024-07-22 11:45:15 +08:00
TsMask f977b6ea53 style: 添加用户水印,学生菜单布局侧边栏显示 2024-07-19 17:32:58 +08:00
TsMask 1e9c6a51be fix: 教师只能重启网元,禁止操作网元信息 2024-07-19 17:31:45 +08:00
TsMask d68a773214 feat: 历史记录抽屉查看数据对比差异 2024-07-19 16:23:01 +08:00
TsMask f60f26ae89 fix: 参数配置列表编辑切换属性回显错误 2024-07-19 16:22:15 +08:00
TsMask c35a5a9c33 fix: 参数配置教师操作学生应用和退回 2024-07-18 15:04:51 +08:00
TsMask 4d755cea5d feat: 参数应用申请操作和查看功能 2024-07-17 17:04:13 +08:00
TsMask c87458bc23 feat: 参数配置教师应用和切换学生功能 2024-07-17 17:03:32 +08:00
TsMask cfd04ba1b6 Merge remote-tracking branch 'origin/main' into practical-training 2024-07-16 10:01:32 +08:00
TsMask 4af02693a4 feat: 教师选择学生进行切换查看学生配置数据信息 2024-07-15 17:59:30 +08:00
TsMask 66eb27813f feat: 新增网元应用申请提交列表操作 2024-07-15 17:56:28 +08:00
TsMask 65c8c6f809 perf: 参数配置页面函数重构 2024-07-11 14:49:26 +08:00
TsMask ecaa0ce077 fix: 参数配置接入实训调整网元类型选择,权限按钮 2024-07-11 10:24:27 +08:00
TsMask 99140f0641 feat: 接入实训接口调试参数配置 2024-07-11 10:23:18 +08:00
TsMask f3c46976ae Merge remote-tracking branch 'origin/main' into practical-training 2024-07-09 19:09:04 +08:00
TsMask efa8764bd1 feat: 配置参数直连数据获取优化 2024-07-09 10:05:43 +08:00
TsMask bc0e463191 fix: 用户岗位和角色指定只能选一个 2024-07-08 11:25:10 +08:00
TsMask 45bf9fe115 Merge remote-tracking branch 'origin/main' into practical-training 2024-07-08 10:57:22 +08:00
TsMask 4c85c61f05 fix: 右上角告警和帮助图标仅教师和管理员看见 2024-07-06 14:22:04 +08:00
TsMask 318f84504f fix: 看板数字活动连接禁止学生角色跳转页面 2024-07-06 14:20:06 +08:00
TsMask 53fa36eace Merge remote-tracking branch 'origin/main' into practical-training 2024-07-03 15:33:26 +08:00
TsMask 9123484bf5 Merge remote-tracking branch 'origin/main' into practical-training 2024-06-28 12:04:57 +08:00
TsMask e7e4557e96 style: 个人信息隐藏手机号和邮箱的输入 2024-06-27 16:07:55 +08:00
TsMask 6b1fe4a582 Merge remote-tracking branch 'origin/main' into practical-training 2024-06-27 15:36:40 +08:00
TsMask 2610ab17ad Merge remote-tracking branch 'origin/main' into practical-training 2024-06-26 17:25:33 +08:00
TsMask b84a08d825 fix: 教师不能管理分配部门,屏蔽电话邮箱 2024-06-24 17:13:09 +08:00
TsMask 824c38593b Merge remote-tracking branch 'origin/main' into practical-training 2024-06-24 11:18:30 +08:00
TsMask 69fb3df7ec Merge remote-tracking branch 'origin/main' into practical-training 2024-06-20 14:56:37 +08:00
TsMask 42827796c7 fix: 用户管理内教师角色只能新增学生 2024-06-20 10:28:01 +08:00
TsMask eb38211e3b style: 部门改为班级 2024-06-20 10:27:28 +08:00
TsMask fd1d2a5bad Merge remote-tracking branch 'origin/main' into practical-training 2024-06-20 10:19:01 +08:00
TsMask 2d5bfb9842 Merge remote-tracking branch 'origin/main' into practical-training 2024-06-20 09:34:54 +08:00
TsMask c0a7f28958 Merge remote-tracking branch 'origin/main' into practical-training 2024-06-19 14:26:24 +08:00
TsMask 5f4e47e998 Merge remote-tracking branch 'origin/main' into practical-training 2024-06-19 12:04:40 +08:00
TsMask 19a91936de style: 默认菜单主题色dark 2024-06-19 12:03:59 +08:00
122 changed files with 4569 additions and 14463 deletions

No files matched your search

+2 -2
View File
@@ -1,5 +1,5 @@
# 历史路径-哈希带井号标识 # 历史路径-哈希带井号标识
VITE_HISTORY_HASH = true VITE_HISTORY_HASH = false
# 历史路径-前缀URL如:/h5 # 历史路径-前缀URL如:/h5
VITE_HISTORY_BASE_URL = "/" VITE_HISTORY_BASE_URL = "/"
@@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network OMC"
VITE_APP_CODE = "OMC" VITE_APP_CODE = "OMC"
# 应用版本 # 应用版本
VITE_APP_VERSION = "2.250509" VITE_APP_VERSION = "2.241115"
# 接口基础URL地址-不带/后缀 # 接口基础URL地址-不带/后缀
VITE_API_BASE_URL = "/omc-api" VITE_API_BASE_URL = "/omc-api"
+1 -1
View File
@@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network OMC"
VITE_APP_CODE = "OMC" VITE_APP_CODE = "OMC"
# 应用版本 # 应用版本
VITE_APP_VERSION = "2.250509" VITE_APP_VERSION = "2.241115"
# 接口基础URL地址-不带/后缀 # 接口基础URL地址-不带/后缀
VITE_API_BASE_URL = "/omc-api" VITE_API_BASE_URL = "/omc-api"
+39 -39
View File
@@ -12,50 +12,50 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"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.3", "@codemirror/lang-javascript": "^6.2.2",
"@codemirror/lang-yaml": "6.1.2", "@codemirror/lang-yaml": "^6.1.1",
"@codemirror/merge": "6.10.0", "@codemirror/merge": "^6.7.2",
"@codemirror/theme-one-dark": "6.1.2", "@codemirror/theme-one-dark": "^6.1.2",
"@tato30/vue-pdf": "1.11.3", "@tato30/vue-pdf": "^1.11.2",
"@vueuse/core": "13.0.0", "@vueuse/core": "11.2.0",
"@xterm/addon-fit": "0.10.0", "@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "5.5.0", "@xterm/xterm": "^5.5.0",
"ant-design-vue": "4.2.6", "ant-design-vue": "^4.2.5",
"antdv-pro-layout": "4.2.0", "antdv-pro-layout": "^4.1.9",
"antdv-pro-modal": "4.0.6", "antdv-pro-modal": "^4.0.5",
"codemirror": "6.0.1", "codemirror": "^6.0.1",
"crypto-js": "4.2.0", "crypto-js": "^4.2.0",
"dayjs": "1.11.13", "dayjs": "^1.11.11",
"echarts": "5.6.0", "echarts": "~5.5.0",
"file-saver": "2.0.5", "file-saver": "^2.0.5",
"grid-layout-plus": "1.0.6", "grid-layout-plus": "^1.0.5",
"intl-tel-input": "25.2.0", "intl-tel-input": "^24.6.0",
"js-base64": "^3.7.7", "js-base64": "^3.7.7",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"p-queue": "8.0.1", "p-queue": "~8.0.1",
"pinia": "2.3.0", "pinia": "2.2.6",
"vue": "3.5.13", "vue": "^3.5.12",
"vue-i18n": "11.1.2", "vue-i18n": "^10.0.4",
"vue-router": "4.5.0", "vue-router": "^4.4.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/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.2.3", "@vitejs/plugin-vue": "^5.1.4",
"less": "4.2.2", "less": "^4.2.0",
"typescript": "5.8.2", "typescript": "^5.6.3",
"unplugin-vue-components": "0.28.0", "unplugin-vue-components": "^0.27.4",
"vite": "6.2.2", "vite": "5.4.10",
"vite-plugin-compression": "0.5.1", "vite-plugin-compression": "~0.5.1",
"vue-tsc": "2.2.8" "vue-tsc": "^2.1.8"
} }
} }
+7 -14
View File
@@ -1,6 +1,6 @@
/** /**
* =============== Configuration File Description =============== * =============== Configuration File Description ===============
* *
* - Nginx Deployment * - Nginx Deployment
* Delete the file with the same name under the same level of loading.js, Nginx proxy address: /omc-api * Delete the file with the same name under the same level of loading.js, Nginx proxy address: /omc-api
* *
@@ -10,19 +10,12 @@
* *
*/ */
(function () { (function () {
// baseUrl = protocol://ip:port // host = ip:port
// baseUrl = 'http://192.168.8.100:33030'; // const host = '192.168.8.100:33030';
const protocol = window.location.protocol const host = `${window.location.hostname}:33030`;
let wsprotocol = "ws:"
const hostname = window.location.hostname
let host = `${hostname}:33030`;
if (protocol === 'https:') {
host = `${hostname}:33443`;
wsprotocol = "wss:"
}
// Service Address // Service Address
sessionStorage.setItem('baseUrl', `${protocol}//${host}`); sessionStorage.setItem('baseUrl', `http://${host}`);
// websocket Address // websocket Address
sessionStorage.setItem('wsUrl', `${wsprotocol}//${host}`); sessionStorage.setItem('wsUrl', `ws://${host}`);
})(); })();
Binary file not shown.
Binary file not shown.
-28
View File
@@ -1,28 +0,0 @@
<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg">
<!-- Created with Method Draw - http://github.com/duopixel/Method-Draw/ -->
<g>
<title>background</title>
<rect fill="none" id="canvas_background" height="1026" width="1026" y="-1" x="-1"/>
<g display="none" overflow="visible" y="0" x="0" height="100%" width="100%" id="canvasGrid">
<rect fill="url(#gridpattern)" stroke-width="0" y="1" x="1" height="768" width="1024"/>
</g>
</g>
<g>
<title>Layer 1</title>
<g stroke="null" id="svg_15">
<path stroke="null" id="svg_4" fill="#B5D6FB" d="m512.094844,961.632039c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-405.588164,-251.575028c-2.275921,-1.419037 -3.698372,-4.054392 -3.698372,-6.892467l0,-90.007504c0,-2.838074 1.422451,-5.473429 3.698372,-6.892467l405.588164,-255.426701c1.137961,-0.709519 2.465582,-1.114958 3.793202,-1.114958s2.655242,0.405439 3.793202,1.114958l405.493334,255.426701c2.275921,1.419037 3.698372,4.054392 3.698372,6.892467l0,90.007504c0,2.838074 -1.422451,5.473429 -3.698372,6.892467l-405.588164,251.575028c-1.137961,0.709519 -2.465582,1.114958 -3.698372,1.114958z"/>
<path stroke="null" id="svg_5" fill="#0276F7" d="m512.094844,356.615382l405.398504,255.426701l0,90.007504l-66.096551,40.94936l-339.301952,210.625668l-339.491613,-210.625668l-66.096551,-40.94936l0,-90.007504l405.588164,-255.426701m0,-16.014849c-2.655242,0 -5.215653,0.709519 -7.586405,2.229916l-405.588164,255.426701c-4.551843,2.838074 -7.396745,8.108784 -7.396745,13.784933l0,90.007504c0,5.676149 2.844902,10.946859 7.491575,13.886293l66.096551,41.05072l339.491613,210.625668c2.275921,1.419037 4.931163,2.128556 7.491575,2.128556s5.215653,-0.709519 7.491575,-2.128556l339.301952,-210.625668l66.096551,-40.94936c4.646673,-2.838074 7.491575,-8.108784 7.491575,-13.886293l0,-90.007504c0,-5.676149 -2.844902,-10.946859 -7.396745,-13.784933l-405.398504,-255.426701c-2.370751,-1.520397 -5.025993,-2.331275 -7.586405,-2.331275z"/>
<path stroke="null" id="svg_6" fill="#FFFFFF" d="m106.50668,612.042083l405.493334,253.298145l405.493334,-253.298145l-405.398504,-255.426701l-405.588164,255.426701z"/>
<path stroke="null" id="svg_7" fill="#D4E4FC" d="m501.473877,64.192353l-254.9032,498.487506l263.343075,161.162085l266.662127,-162.074323l-275.102002,-497.575268z"/>
<path stroke="null" id="svg_8" fill="#0276F7" d="m229.975417,602.311542c-1.232791,0 -2.465582,-0.304079 -3.698372,-1.013598c-3.603542,-2.128556 -4.931163,-6.993826 -2.844902,-10.845499l279.653845,-532.13896c1.327621,-2.533995 3.793202,-4.054392 6.543274,-4.054392c2.655242,0 5.120823,1.520397 6.543274,4.054392l284.395348,532.13896c2.086261,3.851672 0.75864,8.716943 -2.750072,10.946859c-3.603542,2.128556 -8.155385,0.810878 -10.241646,-2.939434l-277.852074,-519.874424l-273.205401,519.671704c-1.422451,2.635355 -3.982862,4.054392 -6.543274,4.054392z"/>
<path stroke="null" id="svg_9" fill="#0276F7" d="m509.913752,755.567562c-4.172523,0 -7.491575,-3.547593 -7.491575,-8.007424l0,-666.744777c0,-4.459831 3.319052,-8.007424 7.491575,-8.007424s7.491575,3.547593 7.491575,8.007424l0,666.846137c0,4.358471 -3.413882,7.906065 -7.491575,7.906065z"/>
<path stroke="null" id="svg_10" fill="#0276F7" d="m509.913752,731.849369c-1.327621,0 -2.560412,-0.405439 -3.698372,-1.013598l-263.343075,-161.162085c-3.603542,-2.229916 -4.836333,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.939434l259.549873,158.83081l262.963755,-159.844408c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-266.662127,162.074323c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598z"/>
<path stroke="null" id="svg_11" fill="#0276F7" d="m509.913752,579.708306c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-201.988026,-125.686154c-3.603542,-2.229916 -4.741503,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.838074l198.289654,123.354879l201.798366,-122.138561c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.845499l-205.496739,124.469837c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598zm-2.465582,-157.513132c-1.232791,0 -2.370751,-0.304079 -3.508712,-0.912238l-140.917468,-79.668804c-3.698372,-2.128556 -5.025993,-6.892467 -3.129392,-10.845499c1.896601,-3.953032 6.448444,-5.37207 10.146816,-3.344873l137.503586,77.742968l143.00373,-79.871524c3.698372,-2.027196 8.155385,-0.506799 10.146816,3.344873c1.896601,3.953032 0.47415,8.716943 -3.129392,10.845499l-146.512442,81.79736c-1.232791,0.608159 -2.370751,0.912238 -3.603542,0.912238zm2.465582,-148.49211c-1.232791,0 -2.465582,-0.304079 -3.508712,-0.912238l-82.312492,-47.436387c-3.603542,-2.128556 -5.025993,-6.993826 -3.034562,-10.845499c1.991431,-3.953032 6.543274,-5.27071 10.146816,-3.243514l78.708949,45.409191l78.329629,-47.537747c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-81.933171,49.666303c-1.232791,0.608159 -2.560412,0.912238 -3.793202,0.912238z"/>
<path stroke="null" id="svg_12" fill="#0276F7" d="m509.913752,579.708306l-0.28449,0l-263.248245,-9.021022c-4.172523,-0.10136 -7.396745,-3.851672 -7.207085,-8.210144c0.09483,-4.459831 4.077693,-7.703345 7.681235,-7.703345l263.343075,9.021022c4.172523,0.10136 7.396745,3.851672 7.207085,8.210144c-0.18966,4.257112 -3.508712,7.703345 -7.491575,7.703345zm0,152.141063c-1.612111,0 -3.224222,-0.608159 -4.646673,-1.723117c-3.224222,-2.736715 -3.793202,-7.804705 -1.232791,-11.250938l205.496739,-276.610899c2.560412,-3.446233 7.301915,-4.054392 10.526137,-1.317677c3.224222,2.736715 3.793202,7.804705 1.232791,11.250938l-205.496739,276.610899c-1.517281,2.027196 -3.698372,3.040794 -5.879464,3.040794z"/>
<path stroke="null" id="svg_13" fill="#0276F7" d="m509.913752,579.708306c-1.422451,0 -2.750072,-0.405439 -4.077693,-1.216318c-3.508712,-2.432635 -4.457013,-7.297906 -2.275921,-11.048218l144.14169,-239.310492c2.275921,-3.750313 6.922594,-4.763911 10.336476,-2.432635c3.508712,2.432635 4.457013,7.297906 2.275921,11.048218l-144.14169,239.310492c-1.422451,2.331275 -3.793202,3.648953 -6.258784,3.648953zm-2.465582,-157.513132c-1.043131,0 -2.086261,-0.20272 -3.129392,-0.709519c-3.793202,-1.824476 -5.405313,-6.588387 -3.698372,-10.642779l84.398753,-198.158413c1.706941,-4.054392 6.069124,-5.777509 9.957156,-3.953032c3.793202,1.824476 5.405313,6.588387 3.698372,10.642779l-84.303923,198.158413c-1.327621,2.939434 -4.077693,4.662551 -6.922594,4.662551z"/>
<path stroke="null" id="svg_14" fill="#0276F7" d="m591.846924,375.062866c-2.750072,0 -5.405313,-1.621757 -6.732934,-4.459831c-1.801771,-3.953032 -0.28449,-8.716943 3.413882,-10.642779l129.253371,-67.302908l-365.759539,-178.089172l20.862613,208.091673l133.994874,-64.262114c3.698372,-1.824476 8.155385,0 9.862326,4.054392c1.706941,4.054392 0,8.716943 -3.793202,10.541419l-143.38305,68.823305c-2.181091,1.013598 -4.646673,0.912238 -6.827764,-0.405439c-2.086261,-1.317677 -3.413882,-3.547593 -3.698372,-6.081588l-23.328195,-233.026185c-0.28449,-2.838074 0.853471,-5.676149 3.034562,-7.297906c2.181091,-1.621757 5.025993,-2.027196 7.491575,-0.810878l392.217126,190.961867c2.655242,1.317677 4.362183,4.054392 4.362183,7.196546c0,3.142154 -1.612111,5.980228 -4.172523,7.297906l-143.57271,74.600814c-1.043131,0.608159 -2.181091,0.810878 -3.224222,0.810878zm-283.921198,78.959286c-3.603542,0 -6.827764,-2.838074 -7.396745,-6.791107c-0.56898,-4.358471 2.181091,-8.412864 6.258784,-9.122382l199.617275,-31.826978c4.077693,-0.608159 7.870895,2.331275 8.534705,6.689747c0.56898,4.358471 -2.181091,8.412864 -6.258784,9.122382l-199.617275,31.826978c-0.47415,0.10136 -0.853471,0.10136 -1.137961,0.10136z"/>
</g>
<text stroke="null" font-style="italic" transform="matrix(6.577099502228161,0,0,7.449448263868419,-1073.2057632249744,-908.8606073938396) " xml:space="preserve" text-anchor="start" font-family="Arvo, sans-serif" font-size="24" id="svg_16" y="177.898525" x="178.621382" stroke-width="0" fill="#B5D6FB">4G</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.9 KiB

-28
View File
@@ -1,28 +0,0 @@
<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg">
<!-- Created with Method Draw - http://github.com/duopixel/Method-Draw/ -->
<g>
<title>background</title>
<rect fill="none" id="canvas_background" height="1026" width="1026" y="-1" x="-1"/>
<g display="none" overflow="visible" y="0" x="0" height="100%" width="100%" id="canvasGrid">
<rect fill="url(#gridpattern)" stroke-width="0" y="1" x="1" height="768" width="1024"/>
</g>
</g>
<g>
<title>Layer 1</title>
<g stroke="null" id="svg_15">
<path stroke="null" id="svg_4" fill="#B5D6FB" d="m512.094844,961.632039c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-405.588164,-251.575028c-2.275921,-1.419037 -3.698372,-4.054392 -3.698372,-6.892467l0,-90.007504c0,-2.838074 1.422451,-5.473429 3.698372,-6.892467l405.588164,-255.426701c1.137961,-0.709519 2.465582,-1.114958 3.793202,-1.114958s2.655242,0.405439 3.793202,1.114958l405.493334,255.426701c2.275921,1.419037 3.698372,4.054392 3.698372,6.892467l0,90.007504c0,2.838074 -1.422451,5.473429 -3.698372,6.892467l-405.588164,251.575028c-1.137961,0.709519 -2.465582,1.114958 -3.698372,1.114958z"/>
<path stroke="null" id="svg_5" fill="#0276F7" d="m512.094844,356.615382l405.398504,255.426701l0,90.007504l-66.096551,40.94936l-339.301952,210.625668l-339.491613,-210.625668l-66.096551,-40.94936l0,-90.007504l405.588164,-255.426701m0,-16.014849c-2.655242,0 -5.215653,0.709519 -7.586405,2.229916l-405.588164,255.426701c-4.551843,2.838074 -7.396745,8.108784 -7.396745,13.784933l0,90.007504c0,5.676149 2.844902,10.946859 7.491575,13.886293l66.096551,41.05072l339.491613,210.625668c2.275921,1.419037 4.931163,2.128556 7.491575,2.128556s5.215653,-0.709519 7.491575,-2.128556l339.301952,-210.625668l66.096551,-40.94936c4.646673,-2.838074 7.491575,-8.108784 7.491575,-13.886293l0,-90.007504c0,-5.676149 -2.844902,-10.946859 -7.396745,-13.784933l-405.398504,-255.426701c-2.370751,-1.520397 -5.025993,-2.331275 -7.586405,-2.331275z"/>
<path stroke="null" id="svg_6" fill="#FFFFFF" d="m106.50668,612.042083l405.493334,253.298145l405.493334,-253.298145l-405.398504,-255.426701l-405.588164,255.426701z"/>
<path stroke="null" id="svg_7" fill="#D4E4FC" d="m501.473877,64.192353l-254.9032,498.487506l263.343075,161.162085l266.662127,-162.074323l-275.102002,-497.575268z"/>
<path stroke="null" id="svg_8" fill="#0276F7" d="m229.975417,602.311542c-1.232791,0 -2.465582,-0.304079 -3.698372,-1.013598c-3.603542,-2.128556 -4.931163,-6.993826 -2.844902,-10.845499l279.653845,-532.13896c1.327621,-2.533995 3.793202,-4.054392 6.543274,-4.054392c2.655242,0 5.120823,1.520397 6.543274,4.054392l284.395348,532.13896c2.086261,3.851672 0.75864,8.716943 -2.750072,10.946859c-3.603542,2.128556 -8.155385,0.810878 -10.241646,-2.939434l-277.852074,-519.874424l-273.205401,519.671704c-1.422451,2.635355 -3.982862,4.054392 -6.543274,4.054392z"/>
<path stroke="null" id="svg_9" fill="#0276F7" d="m509.913752,755.567562c-4.172523,0 -7.491575,-3.547593 -7.491575,-8.007424l0,-666.744777c0,-4.459831 3.319052,-8.007424 7.491575,-8.007424s7.491575,3.547593 7.491575,8.007424l0,666.846137c0,4.358471 -3.413882,7.906065 -7.491575,7.906065z"/>
<path stroke="null" id="svg_10" fill="#0276F7" d="m509.913752,731.849369c-1.327621,0 -2.560412,-0.405439 -3.698372,-1.013598l-263.343075,-161.162085c-3.603542,-2.229916 -4.836333,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.939434l259.549873,158.83081l262.963755,-159.844408c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-266.662127,162.074323c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598z"/>
<path stroke="null" id="svg_11" fill="#0276F7" d="m509.913752,579.708306c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-201.988026,-125.686154c-3.603542,-2.229916 -4.741503,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.838074l198.289654,123.354879l201.798366,-122.138561c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.845499l-205.496739,124.469837c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598zm-2.465582,-157.513132c-1.232791,0 -2.370751,-0.304079 -3.508712,-0.912238l-140.917468,-79.668804c-3.698372,-2.128556 -5.025993,-6.892467 -3.129392,-10.845499c1.896601,-3.953032 6.448444,-5.37207 10.146816,-3.344873l137.503586,77.742968l143.00373,-79.871524c3.698372,-2.027196 8.155385,-0.506799 10.146816,3.344873c1.896601,3.953032 0.47415,8.716943 -3.129392,10.845499l-146.512442,81.79736c-1.232791,0.608159 -2.370751,0.912238 -3.603542,0.912238zm2.465582,-148.49211c-1.232791,0 -2.465582,-0.304079 -3.508712,-0.912238l-82.312492,-47.436387c-3.603542,-2.128556 -5.025993,-6.993826 -3.034562,-10.845499c1.991431,-3.953032 6.543274,-5.27071 10.146816,-3.243514l78.708949,45.409191l78.329629,-47.537747c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-81.933171,49.666303c-1.232791,0.608159 -2.560412,0.912238 -3.793202,0.912238z"/>
<path stroke="null" id="svg_12" fill="#0276F7" d="m509.913752,579.708306l-0.28449,0l-263.248245,-9.021022c-4.172523,-0.10136 -7.396745,-3.851672 -7.207085,-8.210144c0.09483,-4.459831 4.077693,-7.703345 7.681235,-7.703345l263.343075,9.021022c4.172523,0.10136 7.396745,3.851672 7.207085,8.210144c-0.18966,4.257112 -3.508712,7.703345 -7.491575,7.703345zm0,152.141063c-1.612111,0 -3.224222,-0.608159 -4.646673,-1.723117c-3.224222,-2.736715 -3.793202,-7.804705 -1.232791,-11.250938l205.496739,-276.610899c2.560412,-3.446233 7.301915,-4.054392 10.526137,-1.317677c3.224222,2.736715 3.793202,7.804705 1.232791,11.250938l-205.496739,276.610899c-1.517281,2.027196 -3.698372,3.040794 -5.879464,3.040794z"/>
<path stroke="null" id="svg_13" fill="#0276F7" d="m509.913752,579.708306c-1.422451,0 -2.750072,-0.405439 -4.077693,-1.216318c-3.508712,-2.432635 -4.457013,-7.297906 -2.275921,-11.048218l144.14169,-239.310492c2.275921,-3.750313 6.922594,-4.763911 10.336476,-2.432635c3.508712,2.432635 4.457013,7.297906 2.275921,11.048218l-144.14169,239.310492c-1.422451,2.331275 -3.793202,3.648953 -6.258784,3.648953zm-2.465582,-157.513132c-1.043131,0 -2.086261,-0.20272 -3.129392,-0.709519c-3.793202,-1.824476 -5.405313,-6.588387 -3.698372,-10.642779l84.398753,-198.158413c1.706941,-4.054392 6.069124,-5.777509 9.957156,-3.953032c3.793202,1.824476 5.405313,6.588387 3.698372,10.642779l-84.303923,198.158413c-1.327621,2.939434 -4.077693,4.662551 -6.922594,4.662551z"/>
<path stroke="null" id="svg_14" fill="#0276F7" d="m591.846924,375.062866c-2.750072,0 -5.405313,-1.621757 -6.732934,-4.459831c-1.801771,-3.953032 -0.28449,-8.716943 3.413882,-10.642779l129.253371,-67.302908l-365.759539,-178.089172l20.862613,208.091673l133.994874,-64.262114c3.698372,-1.824476 8.155385,0 9.862326,4.054392c1.706941,4.054392 0,8.716943 -3.793202,10.541419l-143.38305,68.823305c-2.181091,1.013598 -4.646673,0.912238 -6.827764,-0.405439c-2.086261,-1.317677 -3.413882,-3.547593 -3.698372,-6.081588l-23.328195,-233.026185c-0.28449,-2.838074 0.853471,-5.676149 3.034562,-7.297906c2.181091,-1.621757 5.025993,-2.027196 7.491575,-0.810878l392.217126,190.961867c2.655242,1.317677 4.362183,4.054392 4.362183,7.196546c0,3.142154 -1.612111,5.980228 -4.172523,7.297906l-143.57271,74.600814c-1.043131,0.608159 -2.181091,0.810878 -3.224222,0.810878zm-283.921198,78.959286c-3.603542,0 -6.827764,-2.838074 -7.396745,-6.791107c-0.56898,-4.358471 2.181091,-8.412864 6.258784,-9.122382l199.617275,-31.826978c4.077693,-0.608159 7.870895,2.331275 8.534705,6.689747c0.56898,4.358471 -2.181091,8.412864 -6.258784,9.122382l-199.617275,31.826978c-0.47415,0.10136 -0.853471,0.10136 -1.137961,0.10136z"/>
</g>
<text stroke="null" font-style="italic" transform="matrix(6.577099502228161,0,0,7.449448263868419,-1073.2057632249744,-908.8606073938396) " xml:space="preserve" text-anchor="start" font-family="Arvo, sans-serif" font-size="24" id="svg_16" y="177.898525" x="178.621382" stroke-width="0" fill="#D4E4FC">5G</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.9 KiB

+4 -4
View File
@@ -1,7 +1,7 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch'; import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils'; import { parseObjLineToHump } from '@/utils/parse-utils';
import { parseDateToStr, YYYY_MM_DD_HH_MM_SS } from '@/utils/date-utils'; import { parseDateToStr } from '@/utils/date-utils';
import useUserStore from '@/store/modules/user'; import useUserStore from '@/store/modules/user';
/** /**
@@ -122,7 +122,7 @@ export function updateConfirm(data: Record<string, any>) {
const userName = useUserStore().userName; const userName = useUserStore().userName;
let finalData = { let finalData = {
alarm: { alarm: {
ack_time: parseDateToStr(time, YYYY_MM_DD_HH_MM_SS), ack_time: parseDateToStr(time),
ack_user: userName, ack_user: userName,
ack_state: '1', ack_state: '1',
}, },
@@ -145,7 +145,7 @@ export function cancelConfirm(data: (string | number)[]) {
const userName = useUserStore().userName; const userName = useUserStore().userName;
let finalData = { let finalData = {
alarm: { alarm: {
ack_time: parseDateToStr(time, YYYY_MM_DD_HH_MM_SS), ack_time: parseDateToStr(time),
ack_user: '', ack_user: '',
ack_state: '0', ack_state: '0',
}, },
@@ -216,7 +216,7 @@ export function clearAlarm(data: Record<string, any>) {
const userName = useUserStore().userName; const userName = useUserStore().userName;
let finalData = { let finalData = {
data: { data: {
clear_time: parseDateToStr(time, YYYY_MM_DD_HH_MM_SS), clear_time: parseDateToStr(time),
clear_type: '2', clear_type: '2',
alarm_status: '0', alarm_status: '0',
clear_user: userName, clear_user: userName,
+2
View File
@@ -1,6 +1,8 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch'; import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils'; import { parseObjLineToHump } from '@/utils/parse-utils';
import { parseDateToStr } from '@/utils/date-utils';
import useUserStore from '@/store/modules/user';
/** /**
* 查询列表 * 查询列表
+7 -14
View File
@@ -63,11 +63,7 @@ export async function getAlarmSet() {
} }
} }
if (Object.keys(resultData).length === 0) { if (Object.keys(resultData).length === 0) {
return { return { code: RESULT_CODE_ERROR, msg: RESULT_MSG_ERROR[language], data: {} };
code: RESULT_CODE_ERROR,
msg: RESULT_MSG_ERROR[language],
data: {},
};
} }
return { return {
code: RESULT_CODE_SUCCESS, code: RESULT_CODE_SUCCESS,
@@ -121,11 +117,7 @@ export async function updateAlarmSet(data: Record<string, any>) {
} }
// 无变更时 // 无变更时
if (resultNum === 0) { if (resultNum === 0) {
return { return { code: RESULT_CODE_ERROR, msg: RESULT_MSG_ERROR[language], data: 0 };
code: RESULT_CODE_ERROR,
msg: RESULT_MSG_ERROR[language],
data: 0,
};
} }
return { return {
code: RESULT_CODE_SUCCESS, code: RESULT_CODE_SUCCESS,
@@ -174,10 +166,11 @@ export async function getForwardSet() {
*/ */
export async function updateForwardSet(data: Record<string, any>) { export async function updateForwardSet(data: Record<string, any>) {
// return false; // return false;
let obj: any = [ console.log(data)
{ interface: 'Email', to_user: data.emailObj }, let obj:any=[
{ interface: 'SMS', to_user: data.smsObj }, {interface:"Email",to_user:data.emailObj},
]; {interface:"SMS",to_user:data.smsObj}
]
const result = await request({ const result = await request({
url: `/api/rest/databaseManagement/v1/omc_db/config?WHERE=config_tag='forwardAlarm'`, url: `/api/rest/databaseManagement/v1/omc_db/config?WHERE=config_tag='forwardAlarm'`,
method: 'put', method: 'put',
+3 -3
View File
@@ -1,7 +1,7 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch'; import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils'; import { parseObjLineToHump } from '@/utils/parse-utils';
import { parseDateToStr, YYYY_MM_DD_HH_MM_SS } from '@/utils/date-utils'; import { parseDateToStr } from '@/utils/date-utils';
import useUserStore from '@/store/modules/user'; import useUserStore from '@/store/modules/user';
/** /**
@@ -79,7 +79,7 @@ export function updateConfirm(data: Record<string, any>) {
const userName = useUserStore().userName; const userName = useUserStore().userName;
let finalData = { let finalData = {
alarm: { alarm: {
ack_time: parseDateToStr(time, YYYY_MM_DD_HH_MM_SS), ack_time: parseDateToStr(time),
ack_user: userName, ack_user: userName,
ack_state: '1', ack_state: '1',
}, },
@@ -101,7 +101,7 @@ export function cancelConfirm(data: (string | number)[]) {
var time = new Date(); var time = new Date();
let finalData = { let finalData = {
alarm: { alarm: {
ack_time: parseDateToStr(time, YYYY_MM_DD_HH_MM_SS), ack_time: parseDateToStr(time),
ack_user: '', ack_user: '',
ack_state: '0', ack_state: '0',
}, },
-42
View File
@@ -1,5 +1,3 @@
import { CACHE_SESSION_CRYPTO_API } from '@/constants/cache-keys-constants';
import { sessionGet } from '@/utils/cache-session-utils';
import { request } from '@/plugins/http-fetch'; import { request } from '@/plugins/http-fetch';
/** /**
@@ -53,43 +51,3 @@ export function delFile(query: Record<string, any>) {
params: query, params: query,
}); });
} }
/**
* 更新FTP信息
* @param data 数据
* @returns object
*/
export function updateFTPInfo(data: Record<string, any>) {
return request({
url: `/lm/table/ftp`,
method: 'post',
data: data,
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
});
}
/**
* 获取FTP信息
* @param data 数据
* @returns object
*/
export function getFTPInfo() {
return request({
url: `/lm/table/ftp`,
method: 'get',
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
});
}
/**
* 发送FTP文件
* @param data 数据
* @returns object
*/
export function putFTPInfo(filePath: string, fileName: string) {
return request({
url: `/lm/table/ftp`,
method: 'put',
data: { filePath, fileName },
});
}
+2 -4
View File
@@ -1,5 +1,3 @@
import { CACHE_SESSION_CRYPTO_API } from '@/constants/cache-keys-constants';
import { sessionGet } from '@/utils/cache-session-utils';
import { request } from '@/plugins/http-fetch'; import { request } from '@/plugins/http-fetch';
// 登录方法 // 登录方法
@@ -9,7 +7,7 @@ export function login(data: Record<string, string>) {
method: 'post', method: 'post',
data: data, data: data,
whithToken: false, whithToken: false,
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false', crypto: true,
}); });
} }
@@ -24,7 +22,7 @@ export function register(data: Record<string, any>) {
method: 'post', method: 'post',
data: data, data: data,
whithToken: false, whithToken: false,
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false', crypto: true,
}); });
} }
+2 -4
View File
@@ -1,5 +1,3 @@
import { CACHE_SESSION_CRYPTO_API } from '@/constants/cache-keys-constants';
import { sessionGet } from '@/utils/cache-session-utils';
import { request } from '@/plugins/http-fetch'; import { request } from '@/plugins/http-fetch';
/** /**
@@ -38,7 +36,7 @@ export function addNeInfo(data: Record<string, any>) {
url: `/ne/info`, url: `/ne/info`,
method: 'post', method: 'post',
data: data, data: data,
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false', crypto: true,
timeout: 30_000, timeout: 30_000,
}); });
} }
@@ -53,7 +51,7 @@ export function updateNeInfo(data: Record<string, any>) {
url: `/ne/info`, url: `/ne/info`,
method: 'put', method: 'put',
data: data, data: data,
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false', crypto: true,
timeout: 30_000, timeout: 30_000,
}); });
} }
-88
View File
@@ -10,7 +10,6 @@ export function listAMFDataUE(query: Record<string, any>) {
url: '/neData/amf/ue/list', url: '/neData/amf/ue/list',
method: 'get', method: 'get',
params: query, params: query,
timeout: 60_000,
}); });
} }
@@ -41,90 +40,3 @@ export function exportAMFDataUE(data: Record<string, any>) {
timeout: 60_000, timeout: 60_000,
}); });
} }
/**
* AMF-接入基站信息列表
* @param query 查询参数 neId=001&id=1
* @returns object
*/
export function listAMFNblist(query: Record<string, any>) {
return request({
url: '/neData/amf/nb/list',
method: 'get',
params: query,
timeout: 60_000,
});
}
/**
* AMF-接入基站状态信息列表
* @param query 查询参数 neId=001&state=1
* @returns object
*/
export function listAMFNbStatelist(query: Record<string, any>) {
return request({
url: '/neData/amf/nb/list-cfg',
method: 'get',
params: query,
timeout: 60_000,
});
}
/**
* AMF-接入基站状态信息新增
* @param neId 网元ID
* @param data 数据 { "index": 1, "name": "Gnb", "address": "192.168.8.1", "position": "Area-B" }
* @returns object
*/
export function addAMFNbState(neId: string, data: Record<string, any>) {
return request({
url: `/ne/config/data`,
method: 'post',
data: {
neType: 'AMF',
neId: neId,
paramName: 'gnbList',
paramData: data,
loc: `${data.index}`,
},
});
}
/**
* AMF-接入基站状态信息修改
* @param neId 网元ID
* @param data 数据 { "index": 1, "name": "Gnb", "address": "192.168.8.1", "position": "Area-B" }
* @returns object
*/
export function editAMFNbState(neId: string, data: Record<string, any>) {
return request({
url: `/ne/config/data`,
method: 'put',
data: {
neType: 'AMF',
neId: neId,
paramName: 'gnbList',
paramData: data,
loc: `${data.index}`,
},
});
}
/**
* AMF-接入基站状态信息删除
* @param neId 网元ID
* @param index 数据index
* @returns object
*/
export function delAMFNbState(neId: string, index: string | number) {
return request({
url: `/ne/config/data`,
method: 'delete',
params: {
neType: 'AMF',
neId: neId,
paramName: 'gnbList',
loc: `${index}`,
},
});
}
-1
View File
@@ -10,7 +10,6 @@ export function listIMSDataCDR(query: Record<string, any>) {
url: '/neData/ims/cdr/list', url: '/neData/ims/cdr/list',
method: 'get', method: 'get',
params: query, params: query,
timeout: 60_000,
}); });
} }
-88
View File
@@ -10,7 +10,6 @@ export function listMMEDataUE(query: Record<string, any>) {
url: '/neData/mme/ue/list', url: '/neData/mme/ue/list',
method: 'get', method: 'get',
params: query, params: query,
timeout: 60_000,
}); });
} }
@@ -41,90 +40,3 @@ export function exportMMEDataUE(data: Record<string, any>) {
timeout: 60_000, timeout: 60_000,
}); });
} }
/**
* MME-接入基站信息列表
* @param query 查询参数 neId=001&id=1
* @returns object
*/
export function listMMENblist(query: Record<string, any>) {
return request({
url: '/neData/mme/nb/list',
method: 'get',
params: query,
timeout: 60_000,
});
}
/**
* MME-接入基站状态信息列表
* @param query 查询参数 neId=001&state=1
* @returns object
*/
export function listMMENbStatelist(query: Record<string, any>) {
return request({
url: '/neData/mme/nb/list-cfg',
method: 'get',
params: query,
timeout: 60_000,
});
}
/**
* MME-接入基站状态信息新增
* @param neId 网元ID
* @param data 数据 { "index": 1, "name": "Enb", "address": "192.168.8.1", "position": "Area-B" }
* @returns object
*/
export function addMMENbState(neId: string, data: Record<string, any>) {
return request({
url: `/ne/config/data`,
method: 'post',
data: {
neType: 'MME',
neId: neId,
paramName: 'enbList',
paramData: data,
loc: `${data.index}`,
},
});
}
/**
* MME-接入基站状态信息修改
* @param neId 网元ID
* @param data 数据 { "index": 1, "name": "Enb", "address": "192.168.8.1", "position": "Area-B" }
* @returns object
*/
export function editMMENbState(neId: string, data: Record<string, any>) {
return request({
url: `/ne/config/data`,
method: 'put',
data: {
neType: 'MME',
neId: neId,
paramName: 'enbList',
paramData: data,
loc: `${data.index}`,
},
});
}
/**
* MME-接入基站状态信息删除
* @param neId 网元ID
* @param index 数据index
* @returns object
*/
export function delMMENbState(neId: string, index: string | number) {
return request({
url: `/ne/config/data`,
method: 'delete',
params: {
neType: 'MME',
neId: neId,
paramName: 'enbList',
loc: `${index}`,
},
});
}
-30
View File
@@ -1,30 +0,0 @@
import { request } from '@/plugins/http-fetch';
/**
* 历史记录列表
* @param query 查询参数
* @returns object
*/
export function listNBState(query: Record<string, any>) {
return request({
url: '/neData/nb-state/list',
method: 'get',
params: query,
timeout: 60_000,
});
}
/**
* 历史记录列表导出
* @param data 查询列表条件
* @returns object
*/
export function exportNBState(data: Record<string, any>) {
return request({
url: '/neData/nb-state/export',
method: 'post',
data,
responseType: 'blob',
timeout: 60_000,
});
}
-43
View File
@@ -1,43 +0,0 @@
import { request } from '@/plugins/http-fetch';
/**
* 查询SGWC-CDR会话事件
* @param query 查询参数
* @returns object
*/
export function listSGWCDataCDR(query: Record<string, any>) {
return request({
url: '/neData/sgwc/cdr/list',
method: 'get',
params: query,
timeout: 60_000,
});
}
/**
* SGWC-CDR会话删除
* @param id 信息ID
* @returns object
*/
export function delSGWCDataCDR(cdrIds: string | number) {
return request({
url: `/neData/sgwc/cdr/${cdrIds}`,
method: 'delete',
timeout: 60_000,
});
}
/**
* SGWC-CDR会话列表导出
* @param data 查询列表条件
* @returns object
*/
export function exportSGWCDataCDR(data: Record<string, any>) {
return request({
url: '/neData/sgwc/cdr/export',
method: 'post',
data,
responseType: 'blob',
timeout: 60_000,
});
}
+2 -16
View File
@@ -10,7 +10,6 @@ export function listSMFDataCDR(query: Record<string, any>) {
url: '/neData/smf/cdr/list', url: '/neData/smf/cdr/list',
method: 'get', method: 'get',
params: query, params: query,
timeout: 60_000,
}); });
} }
@@ -42,27 +41,14 @@ export function exportSMFDataCDR(data: Record<string, any>) {
}); });
} }
/**
* SMF-在线订阅用户数量
* @param query 查询参数
* @returns object
*/
export function listSMFSubNum(neId: string) {
return request({
url: '/neData/smf/sub/num',
method: 'get',
params: { neId },
});
}
/** /**
* SMF-在线订阅用户列表信息 * SMF-在线订阅用户列表信息
* @param query 查询参数 * @param query 查询参数
* @returns object * @returns object
*/ */
export function listSMFSubList(query: Record<string, any>) { export function listSMFSubscribers(query: Record<string, any>) {
return request({ return request({
url: '/neData/smf/sub/list', url: '/neData/smf/subscribers',
method: 'get', method: 'get',
params: query, params: query,
}); });
-1
View File
@@ -10,7 +10,6 @@ export function listSMSCDataCDR(query: Record<string, any>) {
url: '/neData/smsc/cdr/list', url: '/neData/smsc/cdr/list',
method: 'get', method: 'get',
params: query, params: query,
timeout: 60_000,
}); });
} }
-51
View File
@@ -1,51 +0,0 @@
import { CACHE_SESSION_CRYPTO_API } from '@/constants/cache-keys-constants';
import { sessionGet } from '@/utils/cache-session-utils';
import { request } from '@/plugins/http-fetch';
/**
* 获取下拉框数据
* @returns object
*/
export function getBakFile() {
return request({
url: '/ue/table/list',
method: 'get',
});
}
/**
* 获取对应类型的文件列表
* @param query 查询参数
* @returns object
*/
export function getBakFileList(query: Record<string, any>) {
return request({
url: '/ue/file/list',
method: 'get',
params: query,
});
}
/**
* 下载远端文件
* @param query 查询参数
* @returns object
*/
export function downFile(query: Record<string, any>) {
return request({
url: `/ue/file/${query.fileName}`,
method: 'get',
params: query,
responseType: 'blob',
timeout: 180_000,
});
}
/**
* 删除远端获取文件
* @param query 查询参数
* @returns object
*/
export function delFile(query: Record<string, any>) {
return request({
url: `/ue/file/${query.fileName}`,
method: 'delete',
params: query,
});
}
+3 -1
View File
@@ -1,4 +1,7 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch'; import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
import { parseDateToStr } from '@/utils/date-utils';
/** /**
* 新 查询自定义指标数据 * 新 查询自定义指标数据
@@ -11,7 +14,6 @@ export async function listCustomData(query: Record<string, any>) {
url: `/pm/kpiC/report`, url: `/pm/kpiC/report`,
method: 'get', method: 'get',
params: query, params: query,
timeout: 60_000,
}); });
return result; return result;
} }
+3
View File
@@ -1,4 +1,7 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch'; import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils';
import { parseDateToStr } from '@/utils/date-utils';
/** /**
* 查询自定义指标 * 查询自定义指标
+1
View File
@@ -1,6 +1,7 @@
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { request } from '@/plugins/http-fetch'; import { request } from '@/plugins/http-fetch';
import { parseObjLineToHump } from '@/utils/parse-utils'; import { parseObjLineToHump } from '@/utils/parse-utils';
import { parseDateToStr } from '@/utils/date-utils';
/** /**
* 查询任务列表 * 查询任务列表
+2 -2
View File
@@ -24,7 +24,7 @@ export function getNeFile(query: Record<string, any>) {
method: 'get', method: 'get',
params: query, params: query,
responseType: 'blob', responseType: 'blob',
timeout: 600_000, timeout: 180_000,
}); });
} }
@@ -35,7 +35,7 @@ export function getNeDirZip(data: Record<string, any>) {
method: 'get', method: 'get',
params: data, params: data,
responseType: 'blob', responseType: 'blob',
timeout: 600_000, timeout: 60_000,
}); });
} }
+71
View File
@@ -0,0 +1,71 @@
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 listTraceData(query: Record<string, any>) {
let totalSQL = 'select count(*) as total from trace_data where 1=1 ';
let rowsSQL = 'select * from trace_data where 1=1 ';
// 查询
let querySQL = '';
if (query.imsi) {
querySQL += ` and imsi like '%${query.imsi}%' `;
}
if (query.msisdn) {
querySQL += ` and msisdn like '%${query.msisdn}%' `;
}
// 分页
const pageNum = (query.pageNum - 1) * query.pageSize;
const limtSql = ` limit ${pageNum},${query.pageSize} `;
// 发起请求
const result = await request({
url: `/api/rest/databaseManagement/v1/omc_db/trace_data`,
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['trace_data'];
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;
}
/**
* 信令数据解析HTML
* @param id 任务ID
* @returns
*/
export function getTraceRawInfo(id: Record<string, string>) {
return request({
url: `/api/rest/traceManagement/v1/decMessage/${id}`,
method: 'get',
responseType: 'text',
});
}
-15
View File
@@ -62,18 +62,3 @@ export function packetKeep(taskNo: string, duration: number = 120) {
data: { taskNo, duration }, data: { taskNo, duration },
}); });
} }
/**
* 信令跟踪文件
* @param taskNo 对象
* @returns object
*/
export function packetPCAPFile(taskNo: string) {
return request({
url: '/trace/packet/filePull',
method: 'get',
params: { taskNo },
responseType: 'blob',
timeout: 680_000,
});
}
+19 -19
View File
@@ -1,4 +1,6 @@
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';
/** /**
* 查询跟踪任务列表 * 查询跟踪任务列表
@@ -74,31 +76,29 @@ export function filePullTask(traceId: string) {
method: 'get', method: 'get',
params: { traceId }, params: { traceId },
responseType: 'blob', responseType: 'blob',
timeout: 600_000, timeout: 60_000,
}); });
} }
/** /**
* 跟踪任务数据列表 * 获取网元跟踪接口列表
* @param query 查询参数
* @returns object * @returns object
*/ */
export async function listTraceData(query: Record<string, any>) { export async function getNeTraceInterfaceAll() {
return request({ // 发起请求
url: '/trace/data/list', const result = await request({
method: 'get', url: `/api/rest/databaseManagement/v1/elementType/omc_db/objectType/ne_info`,
params: query,
});
}
/**
* 查询跟踪任务数据信息
* @param id ID
* @returns object
*/
export async function getTraceData(id: string | number) {
return request({
url: `/trace/data/${id}`,
method: 'get', method: 'get',
params: {
SQL: `SELECT ne_type,interface FROM trace_info GROUP BY ne_type,interface`,
},
}); });
// 解析数据
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
let data = result.data.data[0];
return Object.assign(result, {
data: parseObjLineToHump(data['trace_info']),
});
}
return result;
} }
+1 -1
View File
@@ -78,6 +78,6 @@ export function filePullTaskHLR(query: Record<string, any>) {
method: 'get', method: 'get',
params: query, params: query,
responseType: 'blob', responseType: 'blob',
timeout: 600_000, timeout: 60_000,
}); });
} }
+39 -103
View File
@@ -1,6 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { message } from 'ant-design-vue/es'; import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
import { ref, reactive, onMounted, onBeforeUnmount, nextTick } from 'vue';
import { FitAddon } from '@xterm/addon-fit'; import { FitAddon } from '@xterm/addon-fit';
import { Terminal } from '@xterm/xterm'; import { Terminal } from '@xterm/xterm';
import '@xterm/xterm/css/xterm.css'; import '@xterm/xterm/css/xterm.css';
@@ -32,91 +31,8 @@ const terminalDom = ref<HTMLElement | undefined>(undefined);
/**终端输入实例对象 */ /**终端输入实例对象 */
const terminal = ref<any>(null); const terminal = ref<any>(null);
/**终端输入文字状态 */ /**终端输入命令 */
const terminalState = reactive<{ const terminalCmd = ref<string>('');
/**输入值 */
text: string;
/**历史 */
history: {
label?: string;
value: string;
}[];
}>({
text: '',
history: [
{
value: 'info server',
},
{
value: 'info replication',
},
{
value: 'keys ausf:*',
},
{
value: 'quit',
},
],
});
/**自动完成根据输入项进行筛选 */
function fnAutoCompleteFilter(input: string, option: any) {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0;
}
/**自动完成按键触发 */
function fnAutoCompleteKeydown(evt: KeyboardEvent) {
if (evt.key === 'Enter') {
// 阻止默认的换行行为
evt.preventDefault();
// 按下 Shift + Enter 键时换行
if (evt.shiftKey && evt.target) {
// 插入换行符
const textarea = evt.target as HTMLInputElement;
const start = textarea.selectionStart || 0;
const end = textarea.selectionEnd || 0;
const text = textarea.value;
textarea.value = text.substring(0, start) + '\n' + text.substring(end);
terminalState.text = textarea.value;
// 更新光标位置
textarea.selectionStart = textarea.selectionEnd = start + 1;
} else {
// ws未连接
if (ws.state() !== WebSocket.OPEN) {
message.error('disconnected');
return;
}
// 输入历史
const cmdStr = terminalState.text.trim().replace(/\n/g, '\r\n');
const hisIndex = terminalState.history.findIndex(
item => item.value === cmdStr
);
if (hisIndex === -1) {
terminalState.history.push({
value: cmdStr,
});
}
// 发送文本
terminal.value.scrollToBottom();
terminal.value.writeln('\r\n> ' + cmdStr);
ws.send({
requestId: `redis_${props.hostId}`,
type: 'redis',
data: `${cmdStr}\r\n`,
});
terminalState.text = '';
// 退出登录
if (['q', 'quit', 'exit'].includes(cmdStr)) {
setTimeout(() => {
ws.close();
}, 1000);
}
}
}
}
/**终端输入渲染 */ /**终端输入渲染 */
function handleRanderXterm(container: HTMLElement | undefined) { function handleRanderXterm(container: HTMLElement | undefined) {
@@ -133,13 +49,47 @@ function handleRanderXterm(container: HTMLElement | undefined) {
scrollback: 1000, scrollback: 1000,
scrollSensitivity: 15, scrollSensitivity: 15,
tabStopWidth: 4, tabStopWidth: 4,
disableStdin: true, // 禁止输入 disableStdin: false, // 禁止输入
}); });
// 挂载 // 挂载
xterm.open(container); xterm.open(container);
// 自适应尺寸 // 自适应尺寸
const fitAddon = new FitAddon(); const fitAddon = new FitAddon();
xterm.loadAddon(fitAddon); xterm.loadAddon(fitAddon);
// 终端输入字符按键监听
xterm.onKey(({ key, domEvent }) => {
// console.log(key, domEvent);
// 单键输入
switch (domEvent.key) {
case 'Enter':
const cmdStr = terminalCmd.value.trim();
// 发送文本
terminal.value.scrollToBottom();
terminal.value.writeln('\r\n');
ws.send({
requestId: `redis_${props.hostId}`,
type: 'redis',
data: `${cmdStr}\r\n`,
});
terminalCmd.value = '';
// 退出登录
if ('quit' === cmdStr) {
setTimeout(() => {
ws.close();
}, 1000);
}
break;
case 'Backspace':
// 处理退格键,删除最后一个字符
xterm.write('\b \b');
break;
default:
xterm.write(key);
terminalCmd.value += key;
return;
}
});
// 创建 ResizeObserver 实例 // 创建 ResizeObserver 实例
var observer = new ResizeObserver(entries => { var observer = new ResizeObserver(entries => {
fitAddon.fit(); fitAddon.fit();
@@ -263,21 +213,7 @@ defineExpose({
<template> <template>
<div class="terminal"> <div class="terminal">
<div ref="terminalDom" style="height: calc(100% - 36px)" :id="id"></div> <div ref="terminalDom" :id="id" class="terminal"></div>
<a-auto-complete
v-model:value="terminalState.text"
:dropdown-match-select-width="500"
style="width: 100%"
:options="terminalState.history"
:filter-option="fnAutoCompleteFilter"
:defaultActiveFirstOption="false"
>
<a-textarea
:auto-size="{ minRows: 1, maxRows: 6 }"
placeholder="Execute command. Shift+Enter to line feed, Enter to send"
@keypress="fnAutoCompleteKeydown"
/>
</a-auto-complete>
</div> </div>
</template> </template>
+7 -8
View File
@@ -80,16 +80,16 @@ function fnAutoCompleteFilter(input: string, option: any) {
} }
/**自动完成按键触发 */ /**自动完成按键触发 */
function fnAutoCompleteKeydown(evt: KeyboardEvent) { function fnAutoCompleteKeydown(evt: any) {
if (evt.key === 'Enter') { if (evt.key === 'Enter') {
// 阻止默认的换行行为 // 阻止默认的换行行为
evt.preventDefault(); evt.preventDefault();
// 按下 Shift + Enter 键时换行 // 按下 Shift + Enter 键时换行
if (evt.shiftKey && evt.target) { if (evt.shiftKey) {
// 插入换行符 // 插入换行符
const textarea = evt.target as HTMLInputElement; const textarea = evt.target;
const start = textarea.selectionStart || 0; const start = textarea.selectionStart;
const end = textarea.selectionEnd || 0; const end = textarea.selectionEnd;
const text = textarea.value; const text = textarea.value;
textarea.value = text.substring(0, start) + '\n' + text.substring(end); textarea.value = text.substring(0, start) + '\n' + text.substring(end);
terminalState.text = textarea.value; terminalState.text = textarea.value;
@@ -121,7 +121,7 @@ function fnAutoCompleteKeydown(evt: KeyboardEvent) {
type: 'telnet', type: 'telnet',
data: `${cmdStr}\r\n`, data: `${cmdStr}\r\n`,
}); });
terminalState.text = ''; terminalState.text = ' ';
// 退出登录 // 退出登录
if (['q', 'quit', 'exit'].includes(cmdStr)) { if (['q', 'quit', 'exit'].includes(cmdStr)) {
@@ -296,12 +296,11 @@ defineExpose({
style="width: 100%" style="width: 100%"
:options="terminalState.history" :options="terminalState.history"
:filter-option="fnAutoCompleteFilter" :filter-option="fnAutoCompleteFilter"
:defaultActiveFirstOption="false" @keydown="fnAutoCompleteKeydown"
> >
<a-textarea <a-textarea
:auto-size="{ minRows: 1, maxRows: 6 }" :auto-size="{ minRows: 1, maxRows: 6 }"
placeholder="Execute command. Shift+Enter to line feed, Enter to send" placeholder="Execute command. Shift+Enter to line feed, Enter to send"
@keypress="fnAutoCompleteKeydown"
/> />
</a-auto-complete> </a-auto-complete>
</div> </div>
-3
View File
@@ -1,6 +1,3 @@
/**会话缓存-接口加密 */
export const CACHE_SESSION_CRYPTO_API = 'cache:session:cryptoApi';
/**会话缓存-网络请求 */ /**会话缓存-网络请求 */
export const CACHE_SESSION_FATCH = 'cache:session:fatch'; export const CACHE_SESSION_FATCH = 'cache:session:fatch';
-3
View File
@@ -4,7 +4,6 @@ export const NE_TYPE_LIST = [
'IMS', 'IMS',
'AMF', 'AMF',
'AUSF', 'AUSF',
'UDR',
'UDM', 'UDM',
'SMF', 'SMF',
'PCF', 'PCF',
@@ -20,8 +19,6 @@ export const NE_TYPE_LIST = [
'SMSF', 'SMSF',
'CBC', 'CBC',
'CHF', 'CHF',
'HLR',
'SGWC',
]; ];
/** /**
+199 -67
View File
@@ -16,6 +16,7 @@ export default {
errorFields: 'Please fill in the required information in {num} correctly!', errorFields: 'Please fill in the required information in {num} correctly!',
tablePaginationTotal: 'Total {total} items', tablePaginationTotal: 'Total {total} items',
noData: "No Data", noData: "No Data",
zebra:'Tabular zebra pattern',
ok: 'Ok', ok: 'Ok',
cancel: 'Cancel', cancel: 'Cancel',
close: 'Close', close: 'Close',
@@ -130,7 +131,7 @@ export default {
}, },
LockScreen: { LockScreen: {
inputPlacePwd:'Lock Screen Password', inputPlacePwd:'Lock Screen Password',
enter:'Enter', validSucc:'Validation Passed',
validError:'Validation Failure', validError:'Validation Failure',
backLogin:'Logout to Relogin', backLogin:'Logout to Relogin',
backReload:'Restarting now, please wait...', backReload:'Restarting now, please wait...',
@@ -221,11 +222,12 @@ export default {
capability: 'Capability', capability: 'Capability',
serialNum: 'Serial Number', serialNum: 'Serial Number',
expiryDate: 'Expiry Date', expiryDate: 'Expiry Date',
neStatus: 'Status Abnormal', neStatus: 'NE status is abnormal',
runStatus: 'Runing Status', runStatus:'Running Status',
mark:'Information', mark:'Brief Information',
object:'Object', object:'Object',
versionNum:'Version', versionNum:'Version',
systemStatus:'Status',
realNeStatus:'Status', realNeStatus:'Status',
reloadTime:'Refresh Time', reloadTime:'Refresh Time',
Critical:'Critical', Critical:'Critical',
@@ -349,6 +351,163 @@ export default {
description: "No data yet, try refreshing", description: "No data yet, try refreshing",
}, },
}, },
configManage: {
neManage: {
addNe:'Add Network Element',
delSure:'Confirm deleting the data item with network element name {msg}',
editNe:'Edit Network Element',
exportSure:'Confirm exporting the configuration information with the network element name {msg}',
exportTip:'Export successful, please go to backup management for download',
getInfo:'Failed to get network element information',
neType:'NE Type',
neTypePlease: 'Select network element type',
neId:'NE ID',
neName:'NE Name',
neTypeTip:'Fill in the type of network element created, such as:SMF',
uid:'RM UID',
uidTip:'Please enter a unique resource identifier',
ip:'IP Address',
mac:'NE MAC address',
macTip:'Able to locate the physical address (MAC) of the network element',
port:'Port',
portTip:'Maximum range 0~65535',
pvflag:'PV Flag',
pnf:'Physical Network Element',
vnf:'Virtual Network Element',
province:'Region',
vendorName:'Vendor Name',
dn:'Network Identification',
reload: 'Reload',
restart: 'Restart',
totalSure:'Confirm the network element with {operator} network element name {msg}',
stop: 'Stop',
start: 'Start',
log: 'Logs',
export: 'Export',
import: 'Import',
fileForm:'File Source',
selectPlease:'Please select the source of the import file',
server:'Server File',
local:'Local File',
fileSelect:'Please select the current import file',
sync:'Synchronize to NE',
open:'Open',
close:'Close',
addFail:'Add failed',
operFail:'Operation Failed'
},
backupManage: {
setBackupTask: 'Set automatic backup time',
neTypePlease: 'Query network element type',
neType: 'NE Type',
neID: 'NE ID',
fileName: 'File Name',
createAt: 'Create at',
remark:'Remark',
edit:'Edit Backup File',
totalSure:'Confirm that {oper} records item number {id}?',
},
softwareManage: {
sendBtn: 'Distribute',
runBtn: 'Activate',
backBtn: 'Rollback',
historyBtn: 'Distribution Record',
neTypePlease: 'Select network element type',
neType: 'NE Type',
fileName: 'File Name',
version: 'Version',
versionPlease: 'Version number cannot be empty',
updateTime: 'Uploaded Time',
description: 'Description',
deleteTip: 'Are you sure to delete the data item with software [{fileName}]?',
downloadTip: 'Are you sure to download the data item with software [{fileName}]?',
updateComment: 'Comment',
updateCommentPlease: 'Please enter the software description',
updateFile: 'Software File',
updateFilePlease: 'Please upload the updated software file',
verifyFile: 'Verify File',
selectFile: 'SELECT FILE',
sendTitle: 'Distribute software version',
sendContent: 'Are you sure to send the file with the software package [{fileName}] to the corresponding network element?',
runTitle: 'Activate software version',
runContent: 'Are you sure to activate the software version of [{fileName}] that has been issued to the corresponding network element?',
backTitle: 'Fallback software version',
backContent: 'Confirm that the software version of [{fileName}] has been issued for the corresponding network element rollback?',
neId: 'Corresponding network element',
neIdPlease: 'Please select the corresponding network element',
versions:'Version',
upVersions:'Version before upgrade',
backVersions:'Version before rollback',
status:'Status',
letUpTime:'Activation time',
createTime:'Creation time',
onlyAble:'Only upload file format {fileText} is supported',
nullVersion:'There is no rollback version for the current network element.',
},
license: {
neTypePlease: 'Select network element type',
neType: 'NE Type',
serialNum: 'Serial Num',
createTime: 'Time',
comment: 'Description',
updateComment: 'License Description',
updateCommentPlease: 'Please enter a license description',
updateFile: 'License File',
updateFilePlease: 'Please upload and update the License file',
selectFile: 'SELECT FILE',
neId: 'NE ID',
neIdPlease: 'Please select the corresponding network element',
},
configParam:{
dataNull:'No configuration item data yet',
editSuss:'Modification successful',
editFail:'Edit failed',
Unable:'Illegal operation of attribute value',
delSure:'Confirm to delete the data item with Index [{value}]?',
addSuss:'Add successfully',
addFail:'Add failed',
delArraySure:'Confirm to delete the data item with {arrayChildTitle} Index as [{value}]?',
parUnable:'The parameter value is not within the reasonable range',
ipv4Tip:'Not a legal IPV4 address',
ipv6Tip:'Not a legal IPV6 address',
enumTip:'Not a reasonable enumeration value',
boolTip:'Not a reasonable Boolean value',
default:'The input value is of unknown type',
reloadSuss:'Network element reloading completed',
reloadFail:'Network element reloading failed',
neNUll:'No network element list data yet',
reload:'Reload',
post:'Submit',
editSure:'Are you sure you want to update this attribute value? ',
arraryEdit:'Are you sure to submit the record whose updated Index is [{value}]? ',
addSure:'Are you sure to submit the new record of Index: [{value}]? '
},
configParamForm: {
treeTitle: "Navigation Configuration",
treeSelectTip: "Select configuration item information in the left configuration navigation!",
neType: 'NE Type',
neTypePleace: "Please select the network element type",
noConfigData: "No data on configuration items",
updateValue: "[ {num} ] parameter value modified successfully.",
updateValueErr: "Attribute value modification failure",
updateItem: "Modify Index to {num}.",
updateItemErr: "Record modification failure",
delItemOk: "Deleting Index as {num} succeeded",
addItemOk: "Add Index as {num} Record Succeeded",
addItemErr: "Record addition failure",
requireUn: "[ {display} ] input value is of unknown type",
requireString: "[ {display} ] parameter value is invalid.",
requireInt: "[ {display} ] parameter value not in reasonable range {filter}",
requireIpv4: "[ {display} ] not a legitimate IPV4 address",
requireIpv6: "[ {display} ] not a legitimate IPV6 address.",
requireEnum: "[ {display} ] is not a reasonable enumeration value.",
requireBool: "[ {display} ] is not a reasonable boolean value.",
editOkTip: "Confirm updating the value of this [ {num} ] attribute?",
updateItemTip: "Confirm updating the data item with Index [{num}]?",
delItemTip: "Confirm deleting the data item with Index [{num}]?",
arrayMore: "Expand",
},
},
dashboard: { dashboard: {
overview:{ overview:{
title: "Core Network Dashboard", title: "Core Network Dashboard",
@@ -420,18 +579,14 @@ export default {
resultFail: "Fail", resultFail: "Fail",
delTip: "Confirm deletion of the data item numbered [{msg}]?", delTip: "Confirm deletion of the data item numbered [{msg}]?",
exportTip: "Do you confirm to export the current query conditions of the CDR data? (Maximum 10,000 items can be exported.)", exportTip: "Do you confirm to export the current query conditions of the CDR data? (Maximum 10,000 items can be exported.)",
chargingID: 'Charging ID', smfChargingID: 'Charging ID',
smfSubscriptionIDData: 'Subscription ID Data', smfSubscriptionIDData: 'Subscription ID Data',
smfSubscriptionIDType: 'Subscription ID Type', smfSubscriptionIDType: 'Subscription ID Type',
smfDataVolumeUplink: 'Data Volume Uplink', smfDataVolumeUplink: 'Data Volume Uplink',
smfDataVolumeDownlink: 'Data Volume Downlink', smfDataVolumeDownlink: 'Data Volume Downlink',
smfDataTotalVolume: 'Data Total Volume', smfDataTotalVolume: 'Data Total Volume',
durationTime: 'Duration', smfDuration: 'Duration',
invocationTime: 'Invocation Time', smfInvocationTime: 'Invocation Time',
sgwcServedIMSI: 'IMSI',
sgwcServedMSISDN: 'MSISDN',
sgwcVolumeGPRSUplink: 'GPRS Uplink',
sgwcVolumeGPRSDownlink: 'GPRS Downlink',
}, },
ue: { ue: {
eventType: "Event Type", eventType: "Event Type",
@@ -503,7 +658,7 @@ export default {
delTip: 'Confirm deletion of network element information data items?', delTip: 'Confirm deletion of network element information data items?',
oam: { oam: {
title: 'OAM Configuration', title: 'OAM Configuration',
restart: 'Restart NE', sync: 'Sync to NE',
oamEnable: 'Service', oamEnable: 'Service',
oamPort: 'Port', oamPort: 'Port',
snmpEnable: 'Service', snmpEnable: 'Service',
@@ -606,7 +761,6 @@ export default {
upgradeDone: 'Update complete, service being reloaded', upgradeDone: 'Update complete, service being reloaded',
upgradeFail: 'The update fails, please check whether the software file exists and whether the service terminal environment is available!', upgradeFail: 'The update fails, please check whether the software file exists and whether the service terminal environment is available!',
upgradeModal: 'Network Element Version Updates', upgradeModal: 'Network Element Version Updates',
noPath: 'Package File Not Found',
}, },
neLicense: { neLicense: {
status: "License Status", status: "License Status",
@@ -630,9 +784,7 @@ export default {
treeSelectTip: "Select configuration item information in the left configuration navigation!", treeSelectTip: "Select configuration item information in the left configuration navigation!",
neType: 'NE Type', neType: 'NE Type',
neTypePleace: "Please select the network element type", neTypePleace: "Please select the network element type",
neIdSyncPleace: "Please select the synchronized network element",
noConfigData: "No data on configuration items", noConfigData: "No data on configuration items",
noConfigdDisabled: "The configuration item is not normal",
updateValue: "[ {num} ] parameter value modified successfully.", updateValue: "[ {num} ] parameter value modified successfully.",
updateValueErr: "Attribute value modification failure", updateValueErr: "Attribute value modification failure",
updateItem: "Modify Index to {num}.", updateItem: "Modify Index to {num}.",
@@ -693,7 +845,7 @@ export default {
installSourceUpload: 'New Upload', installSourceUpload: 'New Upload',
installSelect: 'Select Record', installSelect: 'Select Record',
installUpload: 'Upload File', installUpload: 'Upload File',
installText: 'Install', installText: 'Installed',
licenseTitle: "Licenses", licenseTitle: "Licenses",
licenseDesc: "Network element service authorization certification", licenseDesc: "Network element service authorization certification",
licenseResultTitle: "Whether to authorize activation immediately", licenseResultTitle: "Whether to authorize activation immediately",
@@ -706,30 +858,6 @@ export default {
licenseTip2: '2. Clicking [Finish] will end the installation process.', licenseTip2: '2. Clicking [Finish] will end the installation process.',
}, },
}, },
neData: {
baseStation: {
list: "List",
topology: "Topology",
nbName: "RanNodeName",
ueNum: "UE Number",
topologyTitle: "Radio State Graph",
name: "Name",
namePlease: "text content length 0~64",
position: "Position",
positionPlease: "location description. Prohibition of spaces, length of text content 0-64",
address: "IP Address",
addressPlease: "text content length 0~64",
state: "State",
online: "Online",
offline: "Offline",
time: "Change Time",
addRadio: "Add Radio Info",
editRadio: "Edit Radio Info",
history: "Status History",
exportTip: "Confirm exporting xlsx table files based on search criteria?",
importDataEmpty: "Imported data is empty",
},
},
neUser: { neUser: {
auth: { auth: {
authInfo:' Authentication Info', authInfo:' Authentication Info',
@@ -740,7 +868,6 @@ export default {
checkExport : 'Check Export', checkExport : 'Check Export',
checkExportConfirm: 'Confirm exporting the checked authenticated user data?', checkExportConfirm: 'Confirm exporting the checked authenticated user data?',
import: 'Import', import: 'Import',
importFail: 'Failure Record',
loadDataConfirm: 'Are you sure you want to reload the data?', loadDataConfirm: 'Are you sure you want to reload the data?',
loadData: 'Load Data', loadData: 'Load Data',
loadDataTip: 'Successfully fetched loaded data: {num} items, the system is internally updating the data, it will take about {timer} seconds, please wait!!!!!.', loadDataTip: 'Successfully fetched loaded data: {num} items, the system is internally updating the data, it will take about {timer} seconds, please wait!!!!!.',
@@ -769,7 +896,6 @@ export default {
checkExport : 'Check Export', checkExport : 'Check Export',
checkExportConfirm: 'Are you sure to export the data of the checked subscribers?', checkExportConfirm: 'Are you sure to export the data of the checked subscribers?',
import: 'Import', import: 'Import',
importFail: 'Failure Record',
loadDataConfirm: 'Are you sure you want to reload the data?', loadDataConfirm: 'Are you sure you want to reload the data?',
loadData: 'Load Data', loadData: 'Load Data',
loadDataTip: 'Successfully fetched loaded data: {num} items, the system is internally updating the data, it will take about {timer} seconds, please wait!!!!!.', loadDataTip: 'Successfully fetched loaded data: {num} items, the system is internally updating the data, it will take about {timer} seconds, please wait!!!!!.',
@@ -964,7 +1090,6 @@ export default {
expressionModal:'Expression Modal', expressionModal:'Expression Modal',
expressionErrorTip:'Please check the expression, the wrong indicator is {kpiId}', expressionErrorTip:'Please check the expression, the wrong indicator is {kpiId}',
expressionNoIdTip:'Please check the expression, no valid indicator is found', expressionNoIdTip:'Please check the expression, no valid indicator is found',
unitSelect:'To better display the image, the same unit needs to be selected. The current unit is:',
}, },
kpiKeyTarget:{ kpiKeyTarget:{
"time":"Time", "time":"Time",
@@ -983,14 +1108,8 @@ export default {
}, },
kpiOverView:{ kpiOverView:{
"kpiName":"NE Metrics Name", "kpiName":"NE Metrics Name",
"maxValue":"Max", "maxValue":"Max Value",
"maxValueTip":"The max value of metric data within the user's filtered time range.", "minValue":"Min Value",
"minValue":"Min",
"minValueTip":"The min value of metric data within the user's filtered time range.",
"avgValue":"Avg",
"avgValueTip":"The average value of metric data within the user's filtered time range.",
"totalValue":"Sum",
"totalValueTip":"The sum value of metric data within the user's filtered time range.",
"kpiChartTitle":"Overview of NE metrics", "kpiChartTitle":"Overview of NE metrics",
"changeLine":"Change to Line Charts", "changeLine":"Change to Line Charts",
"changeBar":"Change to Bar Charts", "changeBar":"Change to Bar Charts",
@@ -999,6 +1118,25 @@ export default {
}, },
}, },
traceManage: { traceManage: {
analysis: {
imsi: 'IMSI',
imsiPlease: 'Please enter IMSI',
msisdn: 'MSISDN',
msisdnPlease: 'Please enter MSISDN',
trackTaskId: 'Task ID',
srcIp: 'Source IP Address',
dstIp: 'Destination IP Address',
signalType: 'Signaling Type',
msgDirect: 'Message Direction',
msgType: 'Message Type',
rowTime: 'Record Time',
signalData: 'Signaling Data',
signalDetail: 'Signaling Details',
noData: 'No information content',
taskTitle: 'Task {num}',
taskDownText: 'Download HTML',
taskDownTip: 'Confirm downloading the signaling details HTML file?',
},
pcap: { pcap: {
capArgPlease: 'Please enter tcpdump -i any support parameter', capArgPlease: 'Please enter tcpdump -i any support parameter',
cmd: 'Command', cmd: 'Command',
@@ -1049,35 +1187,30 @@ export default {
imsiTip: 'Mobile communication IMSI number', imsiTip: 'Mobile communication IMSI number',
srcIp: 'Source IP Address', srcIp: 'Source IP Address',
srcIpPlease: 'Please enter the IP address', srcIpPlease: 'Please enter the IP address',
srcIpTip: 'sending IPv4 address', srcIpTip: 'Current sender IPv4 address',
dstIp: 'Destination IP Address', dstIp: 'Destination IP Address',
dstIpPlease: 'Please enter the IP address', dstIpPlease: 'Please enter the IP address',
dstIpTip: 'receiving end IPv4 address', dstIpTip: 'IPv4 address of the receiving end of the other party',
interfaces: 'Signaling Interface', interfaces: 'Signaling Interface',
interfacesPlease: 'Please enter the signaling interface', interfacesPlease: 'Please enter the signaling interface',
rangePicker: 'Task Time', signalPort: 'Signal Port',
signalPortPlease: 'Please enter the signaling port',
signalPortTip: 'Port of the side corresponding to the destination IP address or source IP address',
rangePicker: 'Start/End Time',
rangePickerPlease: 'Please select the start and end time of the task', rangePickerPlease: 'Please select the start and end time of the task',
remark: 'Remark', remark: 'Remark',
remarkPlease: 'Task description can be entered', remarkPlease: 'Task description can be entered',
addTask: 'Add Task', addTask: 'Add Task',
editTask: 'Modify Task',
viewTask: 'View Task', viewTask: 'View Task',
errorTaskInfo: 'Failed to obtain task information', errorTaskInfo: 'Failed to obtain task information',
delTaskTip: 'Are you sure to delete the data item with record ID {id} ?', delTaskTip: 'Are you sure to delete the data item with record ID {id} ?',
stopTask: 'Successful cessation of tasks {id}', stopTask: 'Successful cessation of tasks {id}',
stopTaskTip: 'Confirm stopping the task with record ID {id} ?', stopTaskTip: 'Confirm stopping the task with record ID {id} ?',
pcapView: "Track Data Analysis", pcapView: "Tracking Data Analysis",
traceFile: "Track File", traceFile: "Tracking File",
errMsg: "Error Message", errMsg: "Error Message",
imsiORmsisdn: "imsi or msisdn is null, cannot start task", imsiORmsisdn: "imsi or msisdn is null, cannot start task",
dataView: "Track Data",
protocolOrInterface: "Protocol/Interface",
msgNe: 'Network Element',
msgEvent: 'Event',
msgType: 'Type',
msgDirect: 'Direction',
msgLen: 'Length',
rowTime: 'Time',
taskInfo: 'Task information',
}, },
}, },
faultManage: { faultManage: {
@@ -1187,13 +1320,12 @@ export default {
size: "Size", size: "Size",
modifiedTime: "Modified Time", modifiedTime: "Modified Time",
fileName: "File Name", fileName: "File Name",
downTipZip: "Confirm downloading the directory [{fileName}] as a ZIP file?",
downTip: "Confirm the download file name is [{fileName}] File?", downTip: "Confirm the download file name is [{fileName}] File?",
downTipErr: "Failed to get file", downTipErr: "Failed to get file",
dirCd: "Enter Dir", dirCd: "Enter Dir",
viewAs: 'View Action', viewAs: 'View Action',
reload: "Reload", reload: "Reload",
follow: 'Enable Instant Update', follow: 'Monitoring Content',
tailChar: 'End Characters', tailChar: 'End Characters',
tailLines: 'End Lines', tailLines: 'End Lines',
}, },
+193 -61
View File
@@ -16,6 +16,7 @@ export default {
errorFields: '请正确填写 {num} 处必填信息!', errorFields: '请正确填写 {num} 处必填信息!',
tablePaginationTotal: '总共 {total} 条', tablePaginationTotal: '总共 {total} 条',
noData: "暂无数据", noData: "暂无数据",
zebra:'表格斑马纹',
ok: '确定', ok: '确定',
cancel: '取消', cancel: '取消',
close: '关闭', close: '关闭',
@@ -130,7 +131,7 @@ export default {
}, },
LockScreen: { LockScreen: {
inputPlacePwd:'请输入锁屏密码', inputPlacePwd:'请输入锁屏密码',
enter:'进入', validSucc:'校验通过',
validError:'校验失败', validError:'校验失败',
backLogin:'退出并重新登录', backLogin:'退出并重新登录',
backReload:'正在重启,请稍等...', backReload:'正在重启,请稍等...',
@@ -221,12 +222,13 @@ export default {
capability: '用户容量', capability: '用户容量',
serialNum: '序列号', serialNum: '序列号',
expiryDate: '许可证到期日期', expiryDate: '许可证到期日期',
neStatus:'状态异常', neStatus:'网元状态异常',
runStatus:'运行状态', runStatus:'运行状态',
mark:'信息', mark:'简略信息',
object:'对象', object:'对象',
versionNum:'版本号', versionNum:'版本号',
realNeStatus:'状态', systemStatus:'系统状态',
realNeStatus:'网元状态',
reloadTime:'刷新时间', reloadTime:'刷新时间',
Critical:'严重告警', Critical:'严重告警',
Major:'主要告警', Major:'主要告警',
@@ -349,6 +351,163 @@ export default {
description: "暂无数据,尝试刷新看看", description: "暂无数据,尝试刷新看看",
}, },
}, },
configManage: {
neManage: {
addNe:'添加网元',
delSure:'确认删除网元名称为{msg}的数据项 ',
editNe:'修改网元',
exportSure:'确认导出网元名称为 {msg} 的配置信息',
exportTip:'导出成功,请到备份管理进行下载',
getInfo:'获取网元信息失败',
neType:'网元类型',
neTypePlease: '请输入网元类型',
neId:'网元内部标识',
neName:'网元名称',
neTypeTip:'填写创建的网元类型,如:SMF',
uid:'资源唯一标识',
uidTip:'请输入资源唯一标识',
ip:'IP地址',
mac:'网元物理地址',
macTip:'能够定位网元的物理地址(MAC)',
port:'端口',
portTip:'最大范围0~65535',
pvflag:'网元虚拟化标识',
pnf:'物理网元',
vnf:'虚拟网元',
province:'网元服务省份',
vendorName:'厂商名称',
dn:'网络标识',
reload: '重载',
restart: '重启',
totalSure:'确认{oper}网元名称为 {msg} 的网元',
stop: '停止',
start: '启动',
log: '日志',
export: '导出',
import: '导入',
fileForm:'文件来源',
selectPlease:'请选择导入文件来源',
server:'服务器文件',
local:'本地文件',
fileSelect:'请选择当前导入文件',
sync:'同步到网元',
open:'开',
close:'关',
addFail:'新增失败',
operFail:'操作失败'
},
backupManage: {
setBackupTask: '设置自动备份时间',
neTypePlease: '查询网元类型',
neType: '网元类型',
neID: '网元内部标识',
fileName: '文件名',
createAt: '创建时间',
remark:'备份说明',
edit:'编辑备份文件',
totalSure:'确认{oper}记录编号为 {id} 的数据项?',
},
softwareManage: {
sendBtn: '下发',
runBtn: '激活',
backBtn: '回退',
historyBtn: '下发记录',
neTypePlease: '选择网元类型',
neType: '网元类型',
fileName: '文件名',
version: '版本号',
versionPlease: '版本号不能为空',
updateTime: '上传时间',
description: '功能描述',
deleteTip: '确认删除 【{fileName}】 的软件数据项?',
downloadTip: '确认下载 【{fileName}】 的软件数据项?',
updateComment: '软件说明',
updateCommentPlease: '请输入软件说明',
updateFile: '软件文件',
updateFilePlease: '请上传更新软件文件',
verifyFile: '校验文件',
selectFile: '选择文件',
sendTitle: '下发软件版本',
sendContent: '确认下发软件包为【{fileName}】的文件到对应网元?',
runTitle: '激活软件版本',
runContent: '确认在对应网元激活已下发【{fileName}】的软件版本?',
backTitle: '回退软件版本',
backContent: '确认在对应网元回退已下发【{fileName}】的软件版本?',
neId: '对应网元',
neIdPlease: '请选择对应网元',
versions:'版本',
upVersions:'升级前版本',
backVersions:'回退前版本',
status:'状态',
letUpTime:'激活时间',
createTime:'创建时间',
onlyAble:'只支持上传文件格式 {fileText}',
nullVersion:'当前网元无可回退版本',
},
license: {
neTypePlease: '选择网元类型',
neType: '网元类型',
serialNum: '序列号',
createTime: '时间',
comment: '说明',
updateComment: 'License说明',
updateCommentPlease: '请输入License说明',
updateFile: 'License文件',
updateFilePlease: '请上传更新License文件',
selectFile: '选择文件',
neId: '网元内部标识',
neIdPlease: '请选择对应网元',
},
configParam:{
dataNull:'暂无配置项数据',
editSuss:'修改成功',
editFail:'修改失败',
unable:'非法操作属性值',
delSure:'确认删除Index为 【{value}】 的数据项?',
addSuss:'新增成功',
addFail:'新增失败',
delArraySure:'确认删除{arrayChildTitle} Index 为 【{value}】 的数据项?',
parUnable:'参数值不在合理范围',
ipv4Tip:'不是合法的IPV4地址',
ipv6Tip:'不是合法的IPV6地址',
enumTip:'不是合理的枚举值',
boolTip:'不是合理的布尔类型的值',
default:'输入值是未知类型',
reloadSuss:'网元重新加载完成',
reloadFail:'网元重新加载失败',
neNUll:'暂无网元列表数据',
reload:'重载',
post:'提交',
editSure:'确认更新该属性值吗?',
arraryEdit:'确认提交更新 Index 为 【{value}】 的记录吗?',
addSure:'确认提交新增 Index :【{value}】 的记录吗?',
},
configParamForm: {
treeTitle: "配置导航",
treeSelectTip: "左侧配置导航中选择配置项信息!",
neType: "网元类型",
neTypePleace: "请选择网元类型",
noConfigData: "暂无配置项数据",
updateValue: "【 {num} 】 属性值修改成功",
updateValueErr: "属性值修改失败",
updateItem: "修改 Index 为 {num} 记录成功",
updateItemErr: "记录修改失败",
delItemOk: "删除 Index 为 {num} 记录成功",
addItemOk: "新增 Index 为 {num} 记录成功",
addItemErr: "记录新增失败",
requireUn: "【 {display} 】输入值是未知类型",
requireString: "【 {display} 】参数值不合理",
requireInt: "【 {display} 】参数值不在合理范围 {filter}",
requireIpv4: "【 {display} 】不是合法的IPV4地址",
requireIpv6: "【 {display} 】不是合法的IPV6地址",
requireEnum: "【 {display} 】不是合理的枚举值",
requireBool: "【 {display} 】不是合理的布尔类型的值",
editOkTip: "确认更新该【 {num} 】属性值吗?",
updateItemTip: "确认更新Index为 【{num}】 的数据项?",
delItemTip: "确认删除Index为 【{num}】 的数据项?",
arrayMore: "展开",
},
},
dashboard: { dashboard: {
overview:{ overview:{
title: "核心网系统看板", title: "核心网系统看板",
@@ -420,18 +579,14 @@ export default {
resultFail: "失败", resultFail: "失败",
delTip: "确认删除编号为【{msg}】的数据项?", delTip: "确认删除编号为【{msg}】的数据项?",
exportTip: "确认导出当前查询条件的话单数据吗?(导出最大支持一万条)", exportTip: "确认导出当前查询条件的话单数据吗?(导出最大支持一万条)",
chargingID: '计费ID', smfChargingID: '计费ID',
smfSubscriptionIDData: '订阅 ID 数据', smfSubscriptionIDData: '订阅 ID 数据',
smfSubscriptionIDType: '订阅 ID 类型', smfSubscriptionIDType: '订阅 ID 类型',
smfDataVolumeUplink: '数据量上行链路', smfDataVolumeUplink: '数据量上行链路',
smfDataVolumeDownlink: '数据量下行链路', smfDataVolumeDownlink: '数据量下行链路',
smfDataTotalVolume: '数据总量', smfDataTotalVolume: '数据总量',
durationTime: '持续时间', smfDuration: '持续时间',
invocationTime: '调用时间', smfInvocationTime: '调用时间',
sgwcServedIMSI: 'IMSI',
sgwcServedMSISDN: 'MSISDN',
sgwcVolumeGPRSUplink: 'GPRS 上行链路',
sgwcVolumeGPRSDownlink: 'GPRS 下行链路',
}, },
ue: { ue: {
eventType: "事件类型", eventType: "事件类型",
@@ -503,7 +658,7 @@ export default {
delTip: '确认删除网元信息数据项吗?', delTip: '确认删除网元信息数据项吗?',
oam: { oam: {
title: 'OAM配置', title: 'OAM配置',
restart: '下发后重启网元', sync: '同步到网元',
oamEnable: '服务', oamEnable: '服务',
oamPort: '端口', oamPort: '端口',
snmpEnable: '服务', snmpEnable: '服务',
@@ -606,7 +761,6 @@ export default {
upgradeDone: '更新完成,服务正在重载', upgradeDone: '更新完成,服务正在重载',
upgradeFail: '更新失败,请检查软件文件是否存在且服务终端环境是否可用!', upgradeFail: '更新失败,请检查软件文件是否存在且服务终端环境是否可用!',
upgradeModal: '网元版本更新', upgradeModal: '网元版本更新',
noPath: '软件包文件未发现',
}, },
neLicense: { neLicense: {
status: "许可证状态", status: "许可证状态",
@@ -630,9 +784,7 @@ export default {
treeSelectTip: "左侧配置导航中选择配置项信息!", treeSelectTip: "左侧配置导航中选择配置项信息!",
neType: "网元类型", neType: "网元类型",
neTypePleace: "请选择网元类型", neTypePleace: "请选择网元类型",
neIdSyncPleace: "请选择同步网元",
noConfigData: "暂无配置项数据", noConfigData: "暂无配置项数据",
noConfigdDisabled: "配置项网元未正常服务",
updateValue: "【 {num} 】 属性值修改成功", updateValue: "【 {num} 】 属性值修改成功",
updateValueErr: "属性值修改失败", updateValueErr: "属性值修改失败",
updateItem: "修改 Index 为 {num} 记录成功", updateItem: "修改 Index 为 {num} 记录成功",
@@ -706,30 +858,6 @@ export default {
licenseTip2: '2. 点击【结束】将结束安装过程', licenseTip2: '2. 点击【结束】将结束安装过程',
}, },
}, },
neData: {
baseStation: {
list: "列表",
topology: "拓扑图",
nbName: "设备名称",
ueNum: "在线用户数",
topologyTitle: "基站状态关系图",
name: "基站名称",
namePlease: "文本内容长度0~64",
position: "基站位置",
positionPlease: "位置描述。禁止空格,文本内容长度0-64",
address: "IP地址",
addressPlease: "文本内容长度0~64",
state: "基站状态",
online: "在线",
offline: "离线",
time: "变更时间",
addRadio: "添加基站信息",
editRadio: "更新基站信息",
history: "历史记录",
exportTip: "确认根据搜索条件导出xlsx表格文件吗?",
importDataEmpty: "导入数据为空",
},
},
neUser: { neUser: {
auth: { auth: {
authInfo:'鉴权信息', authInfo:'鉴权信息',
@@ -740,7 +868,6 @@ export default {
checkExport : '勾选导出', checkExport : '勾选导出',
checkExportConfirm: '确认导出已勾选的鉴权用户数据吗?', checkExportConfirm: '确认导出已勾选的鉴权用户数据吗?',
import: '导入', import: '导入',
importFail: '失败记录',
loadDataConfirm: '确认要重新加载数据吗?', loadDataConfirm: '确认要重新加载数据吗?',
loadData: '加载数据', loadData: '加载数据',
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新,大约需要{timer}秒,请稍候!!!', loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新,大约需要{timer}秒,请稍候!!!',
@@ -769,7 +896,6 @@ export default {
checkExport : '勾选导出', checkExport : '勾选导出',
checkExportConfirm: '确认导出已勾选的签约用户数据吗?', checkExportConfirm: '确认导出已勾选的签约用户数据吗?',
import: '导入', import: '导入',
importFail: '失败记录',
loadDataConfirm: '确认要重新加载数据吗?', loadDataConfirm: '确认要重新加载数据吗?',
loadData: '加载数据', loadData: '加载数据',
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新,大约需要{timer}秒,请稍候!!!', loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新,大约需要{timer}秒,请稍候!!!',
@@ -964,7 +1090,6 @@ export default {
expressionModal:'表达式模块', expressionModal:'表达式模块',
expressionErrorTip:'请检查表达式,错误的指标为{kpiId}', expressionErrorTip:'请检查表达式,错误的指标为{kpiId}',
expressionNoIdTip:'请检查表达式,没有找到任何有效的指标', expressionNoIdTip:'请检查表达式,没有找到任何有效的指标',
unitSelect:'为更好展示图需选择相同单位,当前单位为:',
}, },
kpiKeyTarget:{ kpiKeyTarget:{
"time":"时间", "time":"时间",
@@ -984,13 +1109,7 @@ export default {
kpiOverView:{ kpiOverView:{
"kpiName":"指标名", "kpiName":"指标名",
"maxValue":"最大值", "maxValue":"最大值",
"maxValueTip":"用户筛选时间范围内度量数据的最大值。",
"minValue":"最小值", "minValue":"最小值",
"minValueTip":"用户筛选时间范围内度量数据的最小值。",
"avgValue":"平均值",
"avgValueTip":"用户筛选时间范围内度量数据的平均值。",
"totalValue":"总值",
"totalValueTip":"用户筛选时间范围内度量数据的总值。",
"kpiChartTitle":"网元指标概览", "kpiChartTitle":"网元指标概览",
"changeLine":"切换为折线图", "changeLine":"切换为折线图",
"changeBar":"切换为柱状图", "changeBar":"切换为柱状图",
@@ -999,6 +1118,25 @@ export default {
}, },
}, },
traceManage: { traceManage: {
analysis: {
imsi: 'IMSI',
imsiPlease: '请输入IMSI',
msisdn: 'MSISDN',
msisdnPlease: '请输入MSISDN',
trackTaskId: '跟踪任务标记',
srcIp: '源IP地址',
dstIp: '目标IP地址',
signalType: '信令类型',
msgDirect: '消息元',
msgType: '消息类型',
rowTime: '记录时间',
signalData: '信令数据',
signalDetail: '信令详情',
noData: '无信息内容',
taskTitle: '任务 {num}',
taskDownText: '下载HTML',
taskDownTip: '确认下载信令详情HTML文件?',
},
pcap: { pcap: {
capArgPlease: '请输入tcpdump -i any支持参数', capArgPlease: '请输入tcpdump -i any支持参数',
cmd: '命令', cmd: '命令',
@@ -1049,17 +1187,21 @@ export default {
imsiTip: '移动通信IMSI编号', imsiTip: '移动通信IMSI编号',
srcIp: '源IP地址', srcIp: '源IP地址',
srcIpPlease: '请输入源IP地址', srcIpPlease: '请输入源IP地址',
srcIpTip: '发送端IPv4地址', srcIpTip: '当前发送端IPv4地址',
dstIp: '目标IP地址', dstIp: '目标IP地址',
dstIpPlease: '请输入目标IP地址', dstIpPlease: '请输入目标IP地址',
dstIpTip: '接收端IPv4地址', dstIpTip: '对方接收端IPv4地址',
interfaces: '信令接口', interfaces: '信令接口',
interfacesPlease: '请输入信令接口', interfacesPlease: '请输入信令接口',
rangePicker: '任务时间', signalPort: '信令端口',
signalPortPlease: '请输入信令端口',
signalPortTip: '目标IP地址或源IP地址对应一方的端口',
rangePicker: '开始结束时间',
rangePickerPlease: '请选择任务时间开始结束时间', rangePickerPlease: '请选择任务时间开始结束时间',
remark: '说明', remark: '说明',
remarkPlease: '可输入任务说明', remarkPlease: '可输入任务说明',
addTask: '添加任务', addTask: '添加任务',
editTask: '修改任务',
viewTask: '查看任务', viewTask: '查看任务',
errorTaskInfo: '获取任务信息失败', errorTaskInfo: '获取任务信息失败',
delTaskTip: '确认删除记录编号为 {id} 的数据项?', delTaskTip: '确认删除记录编号为 {id} 的数据项?',
@@ -1069,15 +1211,6 @@ export default {
traceFile: "跟踪文件", traceFile: "跟踪文件",
errMsg: "错误信息", errMsg: "错误信息",
imsiORmsisdn: "imsi 或 msisdn 是空值,不能开始任务", imsiORmsisdn: "imsi 或 msisdn 是空值,不能开始任务",
dataView: "跟踪数据",
protocolOrInterface: "协议/接口",
msgNe: '消息网元',
msgEvent: '消息事件',
msgType: '消息类型',
msgDirect: '消息方向',
msgLen: '消息长度',
rowTime: '消息时间',
taskInfo: '任务信息',
}, },
}, },
faultManage: { faultManage: {
@@ -1187,7 +1320,6 @@ export default {
size: "文件大小", size: "文件大小",
modifiedTime: "修改时间", modifiedTime: "修改时间",
fileName: "文件名称", fileName: "文件名称",
downTipZip: "确认将目录 【{fileName}】 下载为ZIP文件?",
downTip: "确认下载文件名为 【{fileName}】 文件?", downTip: "确认下载文件名为 【{fileName}】 文件?",
downTipErr: "文件获取失败", downTipErr: "文件获取失败",
dirCd: "进入目录", dirCd: "进入目录",
+1 -1
View File
@@ -198,7 +198,7 @@ function beforeRequest(options: OptionsType): OptionsType | Promise<any> {
if (options.method === 'get') return options; if (options.method === 'get') return options;
// 非get参数提交 // 非get参数提交
let body = options.data; let body = options.data
if (body instanceof FormData) { if (body instanceof FormData) {
options.body = body; options.body = body;
} else if (body) { } else if (body) {
+10 -8
View File
@@ -119,15 +119,17 @@ export class WS {
}; };
// 用于指定当从服务器接受到信息时的回调函数。 // 用于指定当从服务器接受到信息时的回调函数。
ws.onmessage = ev => { ws.onmessage = ev => {
if (ev.type !== 'message') return;
// 解析文本消息 // 解析文本消息
try { if (ev.type === 'message') {
const jsonData = JSON.parse(ev.data); const data = ev.data;
if (typeof options.onmessage === 'function') { try {
options.onmessage(jsonData); const jsonData = JSON.parse(data);
if (typeof options.onmessage === 'function') {
options.onmessage(jsonData);
}
} catch (error) {
console.error('websocket message formatting error', error);
} }
} catch (error) {
console.error('websocket message formatting error', error);
} }
}; };
// 用于指定连接关闭后的回调函数。 // 用于指定连接关闭后的回调函数。
@@ -219,7 +221,7 @@ export class WS {
this.heartInterval = window.setInterval(() => { this.heartInterval = window.setInterval(() => {
this.send({ this.send({
requestId: `${Date.now()}`, requestId: `${Date.now()}`,
type: 'PING', type: 'ping',
}); });
}, heartTimer); }, heartTimer);
} }
+1 -6
View File
@@ -182,12 +182,7 @@ router.beforeEach(async (to, from, next) => {
next({ name: 'Index' }); next({ name: 'Index' });
} }
let token = getToken(); const token = getToken();
// 免用户登录认证
if (!appStore.loginAuth) {
token = '== Not Login Auth ==';
}
// 没有token // 没有token
if (!token) { if (!token) {
+1 -11
View File
@@ -1,10 +1,9 @@
import { getSysConf } from '@/api'; import { getSysConf } from '@/api';
import { CACHE_LOCAL_I18N, CACHE_SESSION_CRYPTO_API } from '@/constants/cache-keys-constants'; import { CACHE_LOCAL_I18N } from '@/constants/cache-keys-constants';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { removeToken } from '@/plugins/auth-token'; import { removeToken } from '@/plugins/auth-token';
import { parseUrlPath } from '@/plugins/file-static-url'; import { parseUrlPath } from '@/plugins/file-static-url';
import { localGet, localSet } from '@/utils/cache-local-utils'; import { localGet, localSet } from '@/utils/cache-local-utils';
import { sessionSet } from '@/utils/cache-session-utils';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
/**应用参数类型 */ /**应用参数类型 */
@@ -21,10 +20,6 @@ type AppStore = {
buildTime: string; buildTime: string;
/**系统引导使用 */ /**系统引导使用 */
bootloader: boolean; bootloader: boolean;
// 用户登录认证
loginAuth: boolean;
// 用户接口加密
cryptoApi: boolean;
// 序列号 // 序列号
serialNum: string; serialNum: string;
/**应用版权声明 */ /**应用版权声明 */
@@ -57,8 +52,6 @@ const useAppStore = defineStore('app', {
version: `-`, version: `-`,
buildTime: `-`, buildTime: `-`,
bootloader: false, bootloader: false,
loginAuth: true,
cryptoApi: true,
serialNum: `-`, serialNum: `-`,
copyright: `Copyright ©2023 For ${import.meta.env.VITE_APP_NAME}`, copyright: `Copyright ©2023 For ${import.meta.env.VITE_APP_NAME}`,
logoType: 'icon', logoType: 'icon',
@@ -92,9 +85,6 @@ const useAppStore = defineStore('app', {
if (this.bootloader) { if (this.bootloader) {
removeToken(); removeToken();
} }
this.loginAuth = res.data.loginAuth !== 'false';
this.cryptoApi = res.data.cryptoApi !== 'false';
sessionSet(CACHE_SESSION_CRYPTO_API, res.data.cryptoApi);
this.serialNum = res.data.serialNum; this.serialNum = res.data.serialNum;
this.appName = res.data.title; this.appName = res.data.title;
this.copyright = res.data.copyright; this.copyright = res.data.copyright;
+2 -2
View File
@@ -20,7 +20,7 @@ type MaskStateType = {
const useMaskStore = defineStore('mask', { const useMaskStore = defineStore('mask', {
state: (): MaskStateType => ({ state: (): MaskStateType => ({
type: (localGet(CACHE_LOCAL_MASK) || 'none') as MaskStateType['type'], type: (localGet(CACHE_LOCAL_MASK) || 'none') as MaskStateType['type'],
lockPasswd: atob(localGet(CACHE_LOCAL_LOCK_PASSWD) || ''), lockPasswd: localGet(CACHE_LOCAL_LOCK_PASSWD) || '',
lockTimeout: 0, lockTimeout: 0,
}), }),
getters: {}, getters: {},
@@ -59,7 +59,7 @@ const useMaskStore = defineStore('mask', {
}, 5_000); }, 5_000);
} }
if (type === 'lock') { if (type === 'lock') {
localSet(CACHE_LOCAL_LOCK_PASSWD, btoa(this.lockPasswd)); localSet(CACHE_LOCAL_LOCK_PASSWD, this.lockPasswd);
} else { } else {
localRemove(CACHE_LOCAL_LOCK_PASSWD); localRemove(CACHE_LOCAL_LOCK_PASSWD);
} }
+24 -2
View File
@@ -2,6 +2,7 @@ import { defineStore } from 'pinia';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { listAllNeInfo } from '@/api/ne/neInfo'; import { listAllNeInfo } from '@/api/ne/neInfo';
import { parseDataToOptions } from '@/utils/parse-tree-utils'; import { parseDataToOptions } from '@/utils/parse-tree-utils';
import { getNeTraceInterfaceAll } from '@/api/trace/task';
import { getNePerformanceList } from '@/api/perfManage/taskManage'; import { getNePerformanceList } from '@/api/perfManage/taskManage';
/**网元信息类型 */ /**网元信息类型 */
@@ -12,6 +13,8 @@ type NeInfo = {
neCascaderOptions: Record<string, any>[]; neCascaderOptions: Record<string, any>[];
/**选择器单级父类型 */ /**选择器单级父类型 */
neSelectOtions: Record<string, any>[]; neSelectOtions: Record<string, any>[];
/**跟踪接口列表 */
traceInterfaceList: Record<string, any>[];
/**性能测量数据集 */ /**性能测量数据集 */
perMeasurementList: Record<string, any>[]; perMeasurementList: Record<string, any>[];
}; };
@@ -21,6 +24,7 @@ const useNeInfoStore = defineStore('neinfo', {
neList: [], neList: [],
neCascaderOptions: [], neCascaderOptions: [],
neSelectOtions: [], neSelectOtions: [],
traceInterfaceList: [],
perMeasurementList: [], perMeasurementList: [],
}), }),
getters: { getters: {
@@ -57,7 +61,7 @@ const useNeInfoStore = defineStore('neinfo', {
const res = await listAllNeInfo({ const res = await listAllNeInfo({
bandStatus: false, bandStatus: false,
}); });
if (res.code === RESULT_CODE_SUCCESS) { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
// 原始列表 // 原始列表
this.neList = JSON.parse(JSON.stringify(res.data)); this.neList = JSON.parse(JSON.stringify(res.data));
@@ -75,6 +79,24 @@ const useNeInfoStore = defineStore('neinfo', {
} }
return res; return res;
}, },
// 刷新跟踪接口列表
async fnRefreshNeTraceInterface() {
this.traceInterfaceList = [];
const res = await this.fnNeTraceInterface();
return res;
},
// 获取跟踪接口列表
async fnNeTraceInterface() {
// 有数据不请求
if (this.traceInterfaceList.length > 0) {
return { code: 1, data: this.traceInterfaceList, msg: 'success' };
}
const res = await getNeTraceInterfaceAll();
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
this.traceInterfaceList = res.data;
}
return res;
},
// 获取性能测量数据集列表 // 获取性能测量数据集列表
async fnNeTaskPerformance() { async fnNeTaskPerformance() {
// 有数据不请求 // 有数据不请求
@@ -82,7 +104,7 @@ const useNeInfoStore = defineStore('neinfo', {
return { code: 1, data: this.perMeasurementList, msg: 'success' }; return { code: 1, data: this.perMeasurementList, msg: 'success' };
} }
const res = await getNePerformanceList(); const res = await getNePerformanceList();
if (res.code === RESULT_CODE_SUCCESS) { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
this.perMeasurementList = res.data; this.perMeasurementList = res.data;
} }
return res; return res;
-2
View File
@@ -7,10 +7,8 @@ declare module '*.vue' {
export default component; export default component;
} }
// "vue3-smooth-dnd": "^0.0.6"
declare module 'vue3-smooth-dnd'; declare module 'vue3-smooth-dnd';
// "intl-tel-input": "^25.2.0"
declare module 'intl-tel-input/intlTelInputWithUtils' { declare module 'intl-tel-input/intlTelInputWithUtils' {
import intlTelInput from 'intl-tel-input'; import intlTelInput from 'intl-tel-input';
export default intlTelInput; export default intlTelInput;
+2 -5
View File
@@ -20,9 +20,6 @@ export const YYYYMMDDHHMMSS = 'YYYYMMDDHHmmss';
/**年-月-日 时:分:秒 列如:2022-12-30 01:01:59 */ /**年-月-日 时:分:秒 列如:2022-12-30 01:01:59 */
export const YYYY_MM_DD_HH_MM_SS = 'YYYY-MM-DD HH:mm:ss'; export const YYYY_MM_DD_HH_MM_SS = 'YYYY-MM-DD HH:mm:ss';
/**年-月-日 时:分:秒 列如:2022-12-30T01:01:59+08:00 */
export const YYYY_MM_DD_HH_MM_SSZ = 'YYYY-MM-DDTHH:mm:ssZZ';
/** /**
* 格式时间字符串 * 格式时间字符串
* @param dateStr 时间字符串 * @param dateStr 时间字符串
@@ -39,12 +36,12 @@ export function parseStrToDate(
/** /**
* 格式时间 * 格式时间
* @param date 可转的Date对象 * @param date 可转的Date对象
* @param formatStr 时间格式 默认YYYY-MM-DD HH:mm:ssZZ * @param formatStr 时间格式 默认YYYY-MM-DD HH:mm:ss
* @returns 时间格式字符串 * @returns 时间格式字符串
*/ */
export function parseDateToStr( export function parseDateToStr(
date: string | number | Date, date: string | number | Date,
formatStr: string = YYYY_MM_DD_HH_MM_SSZ formatStr: string = YYYY_MM_DD_HH_MM_SS
): string { ): string {
return dayjs(date).format(formatStr); return dayjs(date).format(formatStr);
} }
+1 -1
View File
@@ -42,7 +42,7 @@ export async function readLoalXlsx(
/** /**
* 读取表格数据 工作表 * 读取表格数据 工作表
* @param fileBolb 文件对象 * @param fileBolb 文件对象
* @param index SheetName索引 * @param index 文件保存路径
* @return 表格对象列表 * @return 表格对象列表
*/ */
export async function readSheet( export async function readSheet(
+4 -32
View File
@@ -13,41 +13,13 @@ export function generateColorHEX(): string {
* @returns rgb(24 144 255) / rgba(0,0,0,.85) * @returns rgb(24 144 255) / rgba(0,0,0,.85)
*/ */
export function generateColorRGBA(hasAlpha: boolean = false) { export function generateColorRGBA(hasAlpha: boolean = false) {
const isDark = document.documentElement.getAttribute('data-theme') === 'dark'; const red = Math.floor(Math.random() * 256);
const green = Math.floor(Math.random() * 256);
let red: number; const blue = Math.floor(Math.random() * 256);
let green: number;
let blue: number;
if (isDark) {
// 暗色模式下生成较亮的颜色
red = Math.floor(Math.random() * 156) + 100; // 100-255
green = Math.floor(Math.random() * 156) + 100; // 100-255
blue = Math.floor(Math.random() * 156) + 100; // 100-255
// 确保至少有一个通道的值较高,使颜色更明亮
const brightChannel = Math.floor(Math.random() * 3);
switch (brightChannel) {
case 0:
red = Math.min(255, red + 50);
break;
case 1:
green = Math.min(255, green + 50);
break;
case 2:
blue = Math.min(255, blue + 50);
break;
}
} else {
// 亮色模式下生成正常的颜色
red = Math.floor(Math.random() * 256); // 0-255
green = Math.floor(Math.random() * 256); // 0-255
blue = Math.floor(Math.random() * 256); // 0-255
}
if (hasAlpha) { if (hasAlpha) {
const alpha = Math.floor(Math.random() * 100); const alpha = Math.floor(Math.random() * 100);
return `rgba(${red}, ${green}, ${blue}, 0.${alpha})`; return `rgb(${red}, ${green}, ${blue}, 0.${alpha})`;
} }
return `rgb(${red}, ${green}, ${blue})`; return `rgb(${red}, ${green}, ${blue})`;
+2 -42
View File
@@ -126,7 +126,7 @@ export function parseDataToTreeExclude(
} }
/** /**
* 解析树结构数据转出所有一维id数组 * 解析树结构数据转出一维id数组
* *
* @param data 数组数据 * @param data 数组数据
* @param fieldId 读取节点字段 默认 'id' * @param fieldId 读取节点字段 默认 'id'
@@ -158,7 +158,7 @@ export function parseTreeKeys(
} }
/** /**
* 解析树结构数据转出根节点的一维id数组 * 解析树结构数据转出含子节点的一维id数组
* *
* @param data 数组数据 * @param data 数组数据
* @param fieldId 读取节点字段 默认 'id' * @param fieldId 读取节点字段 默认 'id'
@@ -221,43 +221,3 @@ export function parseDataToOptions(
return options; return options;
} }
/**
* 解析树结构数据转出子节点关联根节点的一维id数组
*
* @param data 数组数据
* @param checkedKeys 子节点数组数据
* @param fieldId 读取节点字段 默认 'id'
* @param fieldChildren 读取子节点字段 默认 'children'
* @returns 层级数组
*/
export function parseTreeNodeKeysByChecked(
data: Record<string, any>[],
checkedKeys: (string | number)[],
fieldId: string = 'id',
fieldChildren: string = 'children'
) {
// 节点id
let treeIds: (string | number)[] = [];
componet(data);
/**闭包递归函数 */
function componet(data: Record<string, any>[]) {
if (data.length <= 0) return false;
let hasKey = false;
for (const iterator of data) {
const key = iterator[fieldId];
if (checkedKeys.includes(key)) {
hasKey = true;
}
let nodes = iterator[fieldChildren];
if (Array.isArray(nodes) && nodes.length > 0) {
if (componet(nodes)) {
treeIds.push(key);
hasKey = true;
}
}
}
return hasKey;
}
return treeIds;
}
+3 -38
View File
@@ -171,8 +171,8 @@ export function parseSizeFromKbs(sizeByte: number, timeInterval: number): any {
} }
/** /**
* 位数据转换单位 * 字节数转换单位
* @param bits 位Bit大小 64009540 = 512.08 MB * @param bits 字节Bit大小 64009540 = 512.08 MB
* @returns xx B / KB / MB / GB / TB / PB / EB / ZB / YB * @returns xx B / KB / MB / GB / TB / PB / EB / ZB / YB
*/ */
export function parseSizeFromBits(bits: number | string): string { export function parseSizeFromBits(bits: number | string): string {
@@ -181,42 +181,7 @@ export function parseSizeFromBits(bits: number | string): string {
bits = bits * 8; bits = bits * 8;
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const unitIndex = Math.floor(Math.log2(bits) / 10); const unitIndex = Math.floor(Math.log2(bits) / 10);
const value = bits / Math.pow(1000, unitIndex); const value = (bits / Math.pow(1000, unitIndex)).toFixed(2);
const unti = units[unitIndex]; const unti = units[unitIndex];
if (unitIndex > 0) {
return `${value.toFixed(2)} ${unti}`;
}
return `${value} ${unti}`; return `${value} ${unti}`;
} }
/**
* 字节数转换单位
* @param byte 字节Byte大小 64009540 = 512.08 MB
* @param unit 指定单位 B / KB / MB / GB / TB / PB / EB / ZB / YB
* @returns xx B / KB / MB / GB / TB / PB / EB / ZB / YB
*/
export function parseSizeFromByte(
byte: number | string,
unit?: string
): string {
byte = Number(byte) || 0;
if (byte <= 0) return '0 B';
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
let unitIndex = 0;
let unitStr = 'B';
if (unit) {
const index = units.indexOf(unit);
if (index > -1) {
unitIndex = index;
unitStr = unit;
}
} else {
unitIndex = Math.floor(Math.log2(byte) / 10);
unitStr = units[unitIndex];
}
const value = byte / Math.pow(1000, unitIndex);
if (unitIndex > 0) {
return `${value.toFixed(2)} ${unitStr}`;
}
return `${value} ${unitStr}`;
}
File diff suppressed because it is too large. Load diff
+237 -184
View File
@@ -2,14 +2,7 @@
import { PageContainer } from 'antdv-pro-layout'; import { PageContainer } from 'antdv-pro-layout';
import { ColumnsType } from 'ant-design-vue/es/table'; import { ColumnsType } from 'ant-design-vue/es/table';
import { message } from 'ant-design-vue/es'; import { message } from 'ant-design-vue/es';
import { import { reactive, ref, onMounted, onBeforeUnmount, markRaw } from 'vue';
reactive,
ref,
onMounted,
onBeforeUnmount,
markRaw,
useTemplateRef,
} from 'vue';
import useI18n from '@/hooks/useI18n'; import useI18n from '@/hooks/useI18n';
import { TooltipComponent } from 'echarts/components'; import { TooltipComponent } from 'echarts/components';
import { GaugeChart } from 'echarts/charts'; import { GaugeChart } from 'echarts/charts';
@@ -39,12 +32,24 @@ echarts.use([
]); ]);
/**图DOM节点实例对象 */ /**图DOM节点实例对象 */
const statusBar = useTemplateRef<HTMLDivElement>('statusBar'); const statusBar = ref<HTMLElement | undefined>(undefined);
/**图实例对象 */ /**图实例对象 */
const statusBarChart = ref<any>(null); const statusBarChart = ref<any>(null);
/**网元状态字典数据 */
let indexColor = ref<DictType[]>([
{ label: 'Normal', value: 'normal', tagType: '', tagClass: '#91cc75' },
{
label: 'Abnormal',
value: 'abnormal',
tagType: '',
tagClass: '#ee6666',
},
]);
/**表格字段列 */ /**表格字段列 */
//customRender(){} ----单元格处理
let tableColumns: ColumnsType = [ let tableColumns: ColumnsType = [
{ {
title: t('views.index.object'), title: t('views.index.object'),
@@ -62,9 +67,7 @@ let tableColumns: ColumnsType = [
dataIndex: 'serverState', dataIndex: 'serverState',
align: 'left', align: 'left',
customRender(opt) { customRender(opt) {
if (opt.value?.refreshTime) { if (opt.value?.refreshTime) return parseDateToStr(opt.value?.refreshTime);
return parseDateToStr(opt.value?.refreshTime, 'HH:mm:ss');
}
return '-'; return '-';
}, },
}, },
@@ -101,13 +104,12 @@ let tableColumns: ColumnsType = [
}, },
}, },
]; ];
/**表格状态类型 */ /**表格状态类型 */
type TabeStateType = { type TabeStateType = {
/**加载等待 */ /**加载等待 */
loading: boolean; loading: boolean;
/**记录数据 */ /**记录数据 */
data: Record<string, any>[]; data: object[];
/**勾选记录 */ /**勾选记录 */
selectedRowKeys: (string | number)[]; selectedRowKeys: (string | number)[];
}; };
@@ -119,92 +121,114 @@ let tableState: TabeStateType = reactive({
selectedRowKeys: [], selectedRowKeys: [],
}); });
/**状态 */ /**表格状态 */
let serverState: any = ref({}); 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: '-',
});
/**查询网元状态列表 */ /**查询网元状态列表 */
async function fnGetList(reload: boolean = false) { function fnGetList(one: boolean) {
tableState.loading = !reload; if (tableState.loading) return;
try { one && (tableState.loading = true);
const res = await listAllNeInfo({ bandStatus: true }); listAllNeInfo({ bandStatus: true }).then(res => {
tableState.data = res.data; tableState.data = res.data;
} catch (error) { tableState.loading = false;
console.error(error); var rightNum = 0;
tableState.data = []; var errorNum = 0;
} res.data.forEach((item: any) => {
tableState.loading = false; if (item.serverState.online) {
if (tableState.data.length == 0) { rightNum++;
return; } else {
} errorNum++;
}
var rightNum = 0; });
var errorNum = 0; const optionData: any = {
for (const v of tableState.data) { title: {
if (v?.serverState?.online) { text: '',
rightNum++; subtext: '',
} else { left: 'center',
errorNum++; },
} tooltip: {
} trigger: 'item',
},
// 初始 legend: {
if (!reload) { orient: 'vertical',
// 选择第一个 left: 'left',
if (tableState.data.length > 0) { },
const id = tableState.data[0].id; color: indexColor.value.map(item => item.tagClass),
fnTableSelectedRowKeys([id]);
} else {
fnTableSelectedRowKeys(tableState.selectedRowKeys);
}
if (statusBar.value) {
fnDesign(statusBar.value, rightNum, errorNum);
}
} else {
statusBarChart.value.setOption({
series: [ series: [
{ {
name: t('views.index.realNeStatus'),
type: 'pie',
radius: '70%',
center: ['50%', '50%'],
data: [ data: [
{ value: rightNum, name: t('views.index.normal') }, { value: rightNum, name: t('views.index.normal') },
{ value: errorNum, name: t('views.index.abnormal') }, { value: errorNum, name: t('views.index.abnormal') },
], ],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
label: {},
}, },
], ],
}); };
}
fnDesign(statusBar.value, optionData);
});
} }
function fnDesign(container: HTMLElement, rightNum: number, errorNum: number) { function fnDesign(container: HTMLElement | undefined, option: any) {
/// 图表数据 if (!container) return;
const optionData: any = {
title: { if (!statusBarChart.value) {
text: '', statusBarChart.value = markRaw(echarts.init(container, 'light'));
subtext: '', }
left: 'center', option && statusBarChart.value.setOption(option);
},
tooltip: {
trigger: 'item',
},
legend: {
orient: 'vertical',
left: 'left',
},
color: dict.indexStatus.map(item => item.tagClass),
series: [
{
name: t('views.index.runStatus'),
type: 'pie',
radius: '70%',
center: ['50%', '50%'],
data: [
{ value: rightNum, name: t('views.index.normal') },
{ value: errorNum, name: t('views.index.abnormal') },
],
},
],
};
statusBarChart.value = markRaw(echarts.init(container, 'light'));
statusBarChart.value.setOption(optionData);
// 创建 ResizeObserver 实例 // 创建 ResizeObserver 实例
var observer = new ResizeObserver(entries => { var observer = new ResizeObserver(entries => {
@@ -216,43 +240,66 @@ function fnDesign(container: HTMLElement, rightNum: number, errorNum: number) {
observer.observe(container); observer.observe(container);
} }
/**表格多选 */ /**抽屉 网元详细信息 */
function fnTableSelectedRowKeys(keys: (string | number)[]) { const open = ref(false);
if (keys.length <= 0) return; const closeDrawer = () => {
const id = keys[0]; open.value = false;
const row: any = tableState.data.find((item: any) => item.id === id); };
if (!row) { /**抽屉 网元详细信息 */
message.error(t('views.index.neStatus'), 2);
return;
}
const neState = row.serverState;
if (!neState?.online) {
message.error(t('views.index.neStatus'), 2);
return;
}
tableState.selectedRowKeys = keys;
// Mem 将KB转换为MB
const totalMemInKB = neState.mem?.totalMem;
const nfUsedMemInKB = neState.mem?.nfUsedMem;
const sysMemUsageInKB = neState.mem?.sysMemUsage;
const totalMemInMB = Math.round((totalMemInKB / 1024) * 100) / 100;
const nfUsedMemInMB = Math.round((nfUsedMemInKB / 1024) * 100) / 100;
const sysMemUsageInMB = Math.round((sysMemUsageInKB / 1024) * 100) / 100;
// CPU /**监听表格行事件*/
const nfCpu = neState.cpu?.nfCpuUsage; function rowClick(record: any, index: any) {
const sysCpu = neState.cpu?.sysCpuUsage; return {
const nfCpuP = Math.round(nfCpu) / 100; onClick: (event: any) => {
const sysCpuP = Math.round(sysCpu) / 100; 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;
serverState.value = Object.assign( // 将KB转换为MB
{ const totalMemInMB = Math.round((totalMemInKB / 1024) * 100) / 100;
cpuUse: `NE:${nfCpuP}%; SYS:${sysCpuP}%`, const nfUsedMemInMB = Math.round((nfUsedMemInKB / 1024) * 100) / 100;
memoryUse: `Total: ${totalMemInMB}MB; NE: ${nfUsedMemInMB}MB; SYS: ${sysMemUsageInMB}MB`, 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;
}, },
neState };
);
} }
let timer: any;
/** /**
* 国际化翻译转换 * 国际化翻译转换
@@ -265,48 +312,63 @@ function fnLocale() {
appStore.setTitle(title); appStore.setTitle(title);
} }
/**字典数据 */
let dict: {
/**网元信息状态 */
neInfoStatus: DictType[];
/**主页状态 */
indexStatus: DictType[];
} = reactive({
neInfoStatus: [],
indexStatus: [],
});
let timer: any;
onMounted(() => { onMounted(() => {
// 初始字典数据 getDict('index_status')
Promise.allSettled([getDict('ne_info_status'), getDict('index_status')]) .then(res => {
.then(resArr => { if (res.length > 0) {
if (resArr[0].status === 'fulfilled') { indexColor.value = res;
dict.neInfoStatus = resArr[0].value;
}
if (resArr[1].status === 'fulfilled') {
dict.indexStatus = resArr[1].value;
} }
}) })
.finally(async () => { .finally(() => {
fnLocale(); fnLocale();
await fnGetList(false); fnGetList(true);
timer = setInterval(() => { timer = setInterval(() => fnGetList(false), 10000); // 每隔10秒执行一次
if (!timer) return;
fnGetList(true);
}, 10_000); // 每隔10秒执行一次
}); });
}); });
// 在组件卸载之前清除定时器 // 在组件卸载之前清除定时器
onBeforeUnmount(() => { onBeforeUnmount(() => {
clearInterval(timer); clearInterval(timer);
timer = null;
}); });
</script> </script>
<template> <template>
<PageContainer :breadcrumb="{}"> <PageContainer :breadcrumb="{}">
<div>
<a-drawer :open="open" @close="closeDrawer" :width="700">
<a-descriptions bordered :column="1" :label-style="{ width: '160px' }">
<a-descriptions-item :label="t('views.index.hostName')">{{
pronInfo.hostName
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.osInfo')">{{
pronInfo.osInfo
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.ipAddress')">{{
pronInfo.ipAddress
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.version')">{{
pronInfo.version
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.capability')">{{
pronInfo.capability
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.cpuUse')">{{
pronInfo.cpuUse
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.memoryUse')">{{
pronInfo.memoryUse
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.serialNum')">{{
pronInfo.serialNum
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.expiryDate')">{{
pronInfo.expiryDate
}}</a-descriptions-item>
</a-descriptions>
</a-drawer>
</div>
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="14" :md="16" :xs="24"> <a-col :lg="14" :md="16" :xs="24">
<!-- 表格列表 --> <!-- 表格列表 -->
@@ -315,20 +377,20 @@ onBeforeUnmount(() => {
row-key="id" row-key="id"
size="small" size="small"
:columns="tableColumns" :columns="tableColumns"
:data-source="tableState.data"
:loading="tableState.loading" :loading="tableState.loading"
:data-source="tableState.data"
:pagination="false" :pagination="false"
:scroll="{ x: true }" :scroll="{ x: true }"
:row-selection="{ :customRow="rowClick"
type: 'radio',
columnWidth: '48px',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'status'"> <template v-if="column.key === 'status'">
<DictTag :options="dict.neInfoStatus" :value="record.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>
</template> </template>
</a-table> </a-table>
@@ -342,8 +404,7 @@ onBeforeUnmount(() => {
<div style="width: 100%; min-height: 200px" ref="statusBar"></div> <div style="width: 100%; min-height: 200px" ref="statusBar"></div>
</a-card> </a-card>
<a-card <a-card
:loading="tableState.loading" :title="t('views.index.mark')"
:title="`${t('views.index.mark')} - ${serverState.neName || 'OMC'}`"
style="margin-top: 16px" style="margin-top: 16px"
size="small" size="small"
> >
@@ -352,33 +413,25 @@ onBeforeUnmount(() => {
:column="1" :column="1"
:label-style="{ width: '160px' }" :label-style="{ width: '160px' }"
> >
<a-descriptions-item :label="t('views.index.hostName')"> <a-descriptions-item :label="t('views.index.object')">{{
{{ serverState.hostname }} nfInfo.obj
</a-descriptions-item> }}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.osInfo')"> <template v-if="nfInfo.obj === 'OMC'">
{{ serverState.os }} <a-descriptions-item :label="t('views.index.versionNum')">{{
</a-descriptions-item> nfInfo.version
<a-descriptions-item :label="t('views.index.ipAddress')"> }}</a-descriptions-item>
{{ serverState.neIP }} <a-descriptions-item :label="t('views.index.systemStatus')">{{
</a-descriptions-item> nfInfo.status
<a-descriptions-item :label="t('views.index.version')"> }}</a-descriptions-item>
{{ serverState.version }} </template>
</a-descriptions-item> <template v-else>
<a-descriptions-item :label="t('views.index.capability')"> <a-descriptions-item :label="t('views.index.serialNum')">{{
{{ serverState.capability }} nfInfo.serialNum
</a-descriptions-item> }}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.cpuUse')"> <a-descriptions-item :label="t('views.index.expiryDate')">{{
{{ serverState.cpuUse }} nfInfo.outTimeDate
</a-descriptions-item> }}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.memoryUse')"> </template>
{{ serverState.memoryUse }}
</a-descriptions-item>
<a-descriptions-item :label="t('views.index.serialNum')">
{{ serverState.sn }}
</a-descriptions-item>
<a-descriptions-item :label="t('views.index.expiryDate')">
{{ serverState.expire }}
</a-descriptions-item>
</a-descriptions> </a-descriptions>
</a-card> </a-card>
</a-col> </a-col>
+123 -181
View File
@@ -11,21 +11,14 @@ import {
RESULT_CODE_SUCCESS, RESULT_CODE_SUCCESS,
} from '@/constants/result-constants'; } from '@/constants/result-constants';
import useDictStore from '@/store/modules/dict'; import useDictStore from '@/store/modules/dict';
import useNeInfoStore from '@/store/modules/neinfo';
import { listAMFDataUE, delAMFDataUE, exportAMFDataUE } from '@/api/neData/amf'; import { listAMFDataUE, delAMFDataUE, exportAMFDataUE } from '@/api/neData/amf';
import { parseDateToStr } from '@/utils/date-utils';
import { OptionsType, WS } from '@/plugins/ws-websocket'; import { OptionsType, WS } from '@/plugins/ws-websocket';
import dayjs, { Dayjs } from 'dayjs';
import saveAs from 'file-saver'; import saveAs from 'file-saver';
import PQueue from 'p-queue'; import PQueue from 'p-queue';
import { useClipboard } from '@vueuse/core';
const { copy } = useClipboard({ legacy: true });
const { t } = useI18n(); const { t } = useI18n();
const { getDict } = useDictStore(); const { getDict } = useDictStore();
const ws = new WS(); const ws = new WS();
const queue = new PQueue({ concurrency: 1, autoStart: true }); const queue = new PQueue({ concurrency: 1, autoStart: true });
/**网元可选 */
let neOtions = ref<Record<string, any>[]>([]);
/**字典数据 */ /**字典数据 */
let dict: { let dict: {
@@ -42,10 +35,7 @@ let dict: {
}); });
/**开始结束时间 */ /**开始结束时间 */
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([ let queryRangePicker = ref<[string, string]>(['', '']);
dayjs().startOf('hour'),
dayjs().endOf('hour'),
]);
/**查询参数 */ /**查询参数 */
let queryParams = reactive({ let queryParams = reactive({
@@ -57,9 +47,9 @@ let queryParams = reactive({
sortField: 'timestamp', sortField: 'timestamp',
sortOrder: 'desc', sortOrder: 'desc',
/**开始时间 */ /**开始时间 */
startTime: undefined as undefined | number, startTime: '',
/**结束时间 */ /**结束时间 */
endTime: undefined as undefined | number, endTime: '',
/**当前页数 */ /**当前页数 */
pageNum: 1, pageNum: 1,
/**每页条数 */ /**每页条数 */
@@ -77,7 +67,7 @@ function fnQueryReset() {
pageNum: 1, pageNum: 1,
pageSize: 20, pageSize: 20,
}); });
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')]; queryRangePicker.value = ['', ''];
tablePagination.current = 1; tablePagination.current = 1;
tablePagination.pageSize = 20; tablePagination.pageSize = 20;
fnGetList(); fnGetList();
@@ -151,15 +141,9 @@ let tableColumns: ColumnsType = [
{ {
title: t('views.dashboard.ue.time'), title: t('views.dashboard.ue.time'),
dataIndex: 'eventJSON', dataIndex: 'eventJSON',
key: 'time',
align: 'left', align: 'left',
width: 150, width: 150,
customRender(opt) {
const record = opt.value;
if (record?.time) {
return record.time;
}
return parseDateToStr(+record.timestamp * 1000);
},
}, },
{ {
title: t('common.operate'), title: t('common.operate'),
@@ -268,19 +252,11 @@ function fnGetList(pageNum?: number) {
if (pageNum) { if (pageNum) {
queryParams.pageNum = pageNum; queryParams.pageNum = pageNum;
} }
if (!queryRangePicker.value) {
// 时间范围 queryRangePicker.value = ['', ''];
if (
Array.isArray(queryRangePicker.value) &&
queryRangePicker.value.length > 0
) {
queryParams.startTime = queryRangePicker.value[0].valueOf();
queryParams.endTime = queryRangePicker.value[1].valueOf();
} else {
queryParams.startTime = undefined;
queryParams.endTime = undefined;
} }
queryParams.startTime = queryRangePicker.value[0];
queryParams.endTime = queryRangePicker.value[1];
listAMFDataUE(toRaw(queryParams)).then(res => { listAMFDataUE(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
// 取消勾选 // 取消勾选
@@ -348,18 +324,6 @@ function fnExportList() {
}); });
} }
/**
* 复制CDR
* @param jsonStr JSON字符串
*/
function fnRecordCopy(jsonStr: string) {
if (!jsonStr) return;
const text = JSON.stringify(jsonStr, null, 2);
copy(text).then(() => {
message.success(t('common.copyOk'), 3);
});
}
/**实时数据开关 */ /**实时数据开关 */
const realTimeData = ref<boolean>(false); const realTimeData = ref<boolean>(false);
@@ -369,30 +333,31 @@ const realTimeData = ref<boolean>(false);
function fnRealTime() { function fnRealTime() {
realTimeData.value = !realTimeData.value; realTimeData.value = !realTimeData.value;
if (realTimeData.value) { if (realTimeData.value) {
tableState.seached = false;
// 建立链接 // 建立链接
const options: OptionsType = { const options: OptionsType = {
url: '/ws', url: '/ws',
params: { params: {
/**订阅通道组 /**订阅通道组
* *
* AMF_UE会话事件(GroupID:1010_neId) * AMF_UE会话事件(GroupID:1010)
*/ */
subGroupID: `1010_${queryParams.neId}`, subGroupID: '1010',
}, },
onmessage: wsMessage, onmessage: wsMessage,
onerror: (ev: any) => { onerror: wsError,
console.error(ev);
},
}; };
ws.connect(options); ws.connect(options);
} else { } else {
ws.close(); ws.close();
tableState.seached = true;
fnGetList(1);
} }
} }
/**接收数据后回调 */
function wsError(ev: any) {
// 接收数据后回调
console.error(ev);
}
/**接收数据后回调 */ /**接收数据后回调 */
function wsMessage(res: Record<string, any>) { function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res; const { code, requestId, data } = res;
@@ -406,7 +371,7 @@ function wsMessage(res: Record<string, any>) {
return; return;
} }
// ueEvent AMF_UE会话事件 // ueEvent AMF_UE会话事件
if (data.groupId === `1010_${queryParams.neId}`) { if (data.groupId === '1010') {
const ueEvent = data.data; const ueEvent = data.data;
queue.add(async () => { queue.add(async () => {
modalState.maxId += 1; modalState.maxId += 1;
@@ -434,40 +399,16 @@ onMounted(() => {
getDict('ue_auth_code'), getDict('ue_auth_code'),
getDict('ue_event_type'), getDict('ue_event_type'),
getDict('ue_event_cm_state'), getDict('ue_event_cm_state'),
]).then(resArr => { ])
if (resArr[0].status === 'fulfilled') { .then(resArr => {
dict.ueAauthCode = resArr[0].value; if (resArr[0].status === 'fulfilled') {
} dict.ueAauthCode = resArr[0].value;
if (resArr[1].status === 'fulfilled') { }
dict.ueEventType = resArr[1].value; if (resArr[1].status === 'fulfilled') {
} dict.ueEventType = resArr[1].value;
if (resArr[2].status === 'fulfilled') { }
dict.ueEventCmState = resArr[2].value; 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 === 'AMF') {
arr.push({ value: i.neId, label: i.neName });
}
});
neOtions.value = arr;
if (arr.length > 0) {
queryParams.neId = arr[0].value;
}
}
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
} }
}) })
.finally(() => { .finally(() => {
@@ -493,16 +434,6 @@ onBeforeUnmount(() => {
<!-- 表格搜索栏 --> <!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal"> <a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item label="AMF" name="neId ">
<a-select
v-model:value="queryParams.neId"
:options="neOtions"
:placeholder="t('common.selectPlease')"
@change="fnGetList(1)"
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24"> <a-col :lg="6" :md="12" :xs="24">
<a-form-item <a-form-item
:label="t('views.dashboard.ue.eventType')" :label="t('views.dashboard.ue.eventType')"
@@ -517,7 +448,7 @@ onBeforeUnmount(() => {
></a-select> ></a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="6" :md="12" :xs="24"> <a-col :lg="4" :md="12" :xs="24">
<a-form-item label="IMSI" name="imsi "> <a-form-item label="IMSI" name="imsi ">
<a-input <a-input
v-model:value="queryParams.imsi" v-model:value="queryParams.imsi"
@@ -526,20 +457,6 @@ onBeforeUnmount(() => {
></a-input> ></a-input>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList(1)">
<template #icon><SearchOutlined /></template>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
{{ t('common.reset') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
<a-col :lg="8" :md="12" :xs="24"> <a-col :lg="8" :md="12" :xs="24">
<a-form-item <a-form-item
:label="t('views.dashboard.cdr.time')" :label="t('views.dashboard.cdr.time')"
@@ -556,6 +473,20 @@ onBeforeUnmount(() => {
></a-range-picker> ></a-range-picker>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList(1)">
<template #icon><SearchOutlined /></template>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
{{ t('common.reset') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
</a-row> </a-row>
</a-form> </a-form>
</a-card> </a-card>
@@ -591,7 +522,6 @@ onBeforeUnmount(() => {
:disabled="tableState.selectedRowKeys.length <= 0" :disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading" :loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')" @click.prevent="fnRecordDelete('0')"
v-perms:has="['cdr:ne:remove']"
> >
<template #icon><DeleteOutlined /></template> <template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }} {{ t('common.deleteText') }}
@@ -614,7 +544,6 @@ onBeforeUnmount(() => {
:checked-children="t('common.switch.show')" :checked-children="t('common.switch.show')"
:un-checked-children="t('common.switch.hide')" :un-checked-children="t('common.switch.hide')"
size="small" size="small"
:disabled="realTimeData"
/> />
</a-tooltip> </a-tooltip>
<a-tooltip> <a-tooltip>
@@ -675,7 +604,7 @@ onBeforeUnmount(() => {
<span v-if="record.eventType === 'auth-result'"> <span v-if="record.eventType === 'auth-result'">
<DictTag <DictTag
:options="dict.ueAauthCode" :options="dict.ueAauthCode"
:value="record.eventJSON.result" :value="record.eventJSON.authCode"
/> />
</span> </span>
<span v-if="record.eventType === 'detach'"> <span v-if="record.eventType === 'detach'">
@@ -684,29 +613,37 @@ onBeforeUnmount(() => {
<span v-if="record.eventType === 'cm-state'"> <span v-if="record.eventType === 'cm-state'">
<DictTag <DictTag
:options="dict.ueEventCmState" :options="dict.ueEventCmState"
:value="record.eventJSON.result" :value="record.eventJSON.status"
/> />
</span> </span>
</template> </template>
<template v-if="column.key === 'time'">
<span
v-if="record.eventType === 'auth-result'"
:title="record.eventJSON.authTime"
>
{{ record.eventJSON.authTime }}
</span>
<span
v-if="record.eventType === 'detach'"
:title="record.eventJSON.detachTime"
>
{{ record.eventJSON.detachTime }}
</span>
<span
v-if="record.eventType === 'cm-state'"
:title="record.eventJSON.changeTime"
>
{{ record.eventJSON.changeTime }}
</span>
</template>
<template v-if="column.key === 'id'"> <template v-if="column.key === 'id'">
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.copyText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordCopy(record.eventJSON)"
>
<template #icon>
<CopyOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.deleteText') }}</template> <template #title>{{ t('common.deleteText') }}</template>
<a-button <a-button
type="link" type="link"
@click.prevent="fnRecordDelete(record.id)" @click.prevent="fnRecordDelete(record.id)"
v-perms:has="['cdr:ne:remove']"
> >
<template #icon> <template #icon>
<DeleteOutlined /> <DeleteOutlined />
@@ -717,60 +654,65 @@ onBeforeUnmount(() => {
</template> </template>
</template> </template>
<template #expandedRowRender="{ record }"> <template #expandedRowRender="{ record }">
<a-row :gutter="16"> <div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
<a-col :lg="8" :md="12" :xs="24" :offset="2"> <a-divider orientation="left">
<a-divider orientation="left"> {{ t('views.dashboard.ue.ueInfo') }}
{{ t('views.dashboard.ue.ueInfo') }} </a-divider>
</a-divider> <div>
<div> <span>{{ t('views.ne.common.neName') }}: </span>
<span>{{ t('views.ne.common.neName') }}: </span> <span>{{ record.neName }}</span>
<span>{{ record.neName }}</span> </div>
</div> <div>
<div> <span>{{ t('views.ne.common.rmUid') }}: </span>
<span>{{ t('views.ne.common.rmUid') }}: </span> <span>{{ record.rmUID }}</span>
<span>{{ record.rmUID }}</span> </div>
</div> <a-divider orientation="left">
</a-col> {{ t('views.dashboard.ue.rowInfo') }}
<a-col :lg="8" :md="12" :xs="24"> </a-divider>
<a-divider orientation="left"> <div>
{{ t('views.dashboard.ue.rowInfo') }} <span>{{ t('views.dashboard.ue.time') }}: </span>
</a-divider> <span
<div> v-if="record.eventType === 'auth-result'"
<span>{{ t('views.dashboard.ue.time') }}: </span> :title="record.eventJSON.authTime"
<template v-if="record.eventJSON?.time"> >
{{ record.eventJSON.time }} {{ record.eventJSON.authTime }}
</template> </span>
<template v-else> <span
{{ parseDateToStr(record.eventJSON.timestamp * 1000) }} v-if="record.eventType === 'detach'"
</template> :title="record.eventJSON.detachTime"
</div> >
<div> {{ record.eventJSON.detachTime }}
<span>{{ t('views.dashboard.ue.eventType') }}: </span> </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 <DictTag
:options="dict.ueEventType" :options="dict.ueAauthCode"
:value="record.eventType" :value="record.eventJSON.authCode"
/> />
</div> </span>
<div> <span v-if="record.eventType === 'detach'">
<span>{{ t('views.dashboard.ue.result') }}: </span> {{ t('views.dashboard.ue.resultOk') }}
<span v-if="record.eventType === 'auth-result'"> </span>
<DictTag <span v-if="record.eventType === 'cm-state'">
:options="dict.ueAauthCode" <DictTag
:value="record.eventJSON.result" :options="dict.ueEventCmState"
/> :value="record.eventJSON.status"
</span> />
<span v-if="record.eventType === 'detach'"> </span>
{{ t('views.dashboard.ue.resultOk') }} </div>
</span> </div>
<span v-if="record.eventType === 'cm-state'">
<DictTag
:options="dict.ueEventCmState"
:value="record.eventJSON.result"
/>
</span>
</div>
</a-col>
</a-row>
</template> </template>
</a-table> </a-table>
</a-card> </a-card>
+23 -72
View File
@@ -21,9 +21,6 @@ import { parseDateToStr, parseDuration } from '@/utils/date-utils';
import { OptionsType, WS } from '@/plugins/ws-websocket'; import { OptionsType, WS } from '@/plugins/ws-websocket';
import saveAs from 'file-saver'; import saveAs from 'file-saver';
import PQueue from 'p-queue'; import PQueue from 'p-queue';
import { useClipboard } from '@vueuse/core';
import dayjs, { type Dayjs } from 'dayjs';
const { copy } = useClipboard({ legacy: true });
const { t } = useI18n(); const { t } = useI18n();
const { getDict } = useDictStore(); const { getDict } = useDictStore();
const ws = new WS(); const ws = new WS();
@@ -44,25 +41,7 @@ let dict: {
let neOtions = ref<Record<string, any>[]>([]); let neOtions = ref<Record<string, any>[]>([]);
/**开始结束时间 */ /**开始结束时间 */
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([ let queryRangePicker = ref<[string, string]>(['', '']);
dayjs().startOf('hour'),
dayjs().endOf('hour'),
]);
/**时间范围 */
let rangePickerPresets = ref([
{
label: 'Now hour',
value: [dayjs().startOf('hour'), dayjs().endOf('hour')],
},
{ label: 'Today', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
{
label: 'Yesterday',
value: [
dayjs().subtract(1, 'day').startOf('day'),
dayjs().subtract(1, 'day').endOf('day'),
],
},
]);
/**查询参数 */ /**查询参数 */
let queryParams = reactive({ let queryParams = reactive({
@@ -75,9 +54,9 @@ let queryParams = reactive({
sortField: 'timestamp', sortField: 'timestamp',
sortOrder: 'desc', sortOrder: 'desc',
/**开始时间 */ /**开始时间 */
startTime: undefined as undefined | number, startTime: '',
/**结束时间 */ /**结束时间 */
endTime: undefined as undefined | number, endTime: '',
/**当前页数 */ /**当前页数 */
pageNum: 1, pageNum: 1,
/**每页条数 */ /**每页条数 */
@@ -91,12 +70,12 @@ function fnQueryReset() {
recordType: '', recordType: '',
callerParty: '', callerParty: '',
calledParty: '', calledParty: '',
startTime: undefined, startTime: '',
endTime: undefined, endTime: '',
pageNum: 1, pageNum: 1,
pageSize: 20, pageSize: 20,
}); });
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')]; queryRangePicker.value = ['', ''];
tablePagination.current = 1; tablePagination.current = 1;
tablePagination.pageSize = 20; tablePagination.pageSize = 20;
fnGetList(); fnGetList();
@@ -328,18 +307,6 @@ function fnRecordDelete(id: string) {
}); });
} }
/**
* 复制CDR
* @param jsonStr JSON字符串
*/
function fnRecordCopy(jsonStr: string) {
if (!jsonStr) return;
const text = JSON.stringify(jsonStr, null, 2);
copy(text).then(() => {
message.success(t('common.copyOk'), 3);
});
}
/**查询列表, pageNum初始页数 */ /**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) { function fnGetList(pageNum?: number) {
if (tableState.loading) return; if (tableState.loading) return;
@@ -347,17 +314,11 @@ function fnGetList(pageNum?: number) {
if (pageNum) { if (pageNum) {
queryParams.pageNum = pageNum; queryParams.pageNum = pageNum;
} }
// 时间范围 if (!queryRangePicker.value) {
if ( queryRangePicker.value = ['', ''];
Array.isArray(queryRangePicker.value) &&
queryRangePicker.value.length > 0
) {
queryParams.startTime = queryRangePicker.value[0].valueOf();
queryParams.endTime = queryRangePicker.value[1].valueOf();
} else {
queryParams.startTime = undefined;
queryParams.endTime = undefined;
} }
queryParams.startTime = queryRangePicker.value[0];
queryParams.endTime = queryRangePicker.value[1];
listIMSDataCDR(toRaw(queryParams)).then(res => { listIMSDataCDR(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
// 取消勾选 // 取消勾选
@@ -446,9 +407,7 @@ function fnRealTime() {
subGroupID: `1005_${queryParams.neId}`, subGroupID: `1005_${queryParams.neId}`,
}, },
onmessage: wsMessage, onmessage: wsMessage,
onerror: (ev: any) => { onerror: wsError,
console.error(ev);
},
}; };
ws.connect(options); ws.connect(options);
} else { } else {
@@ -458,6 +417,12 @@ function fnRealTime() {
} }
} }
/**接收数据后回调 */
function wsError(ev: any) {
// 接收数据后回调
console.error(ev);
}
/**接收数据后回调 */ /**接收数据后回调 */
function wsMessage(res: Record<string, any>) { function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res; const { code, requestId, data } = res;
@@ -557,7 +522,6 @@ onBeforeUnmount(() => {
v-model:value="queryParams.neId" v-model:value="queryParams.neId"
:options="neOtions" :options="neOtions"
:placeholder="t('common.selectPlease')" :placeholder="t('common.selectPlease')"
@change="fnQueryReset()"
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
@@ -620,12 +584,12 @@ onBeforeUnmount(() => {
> >
<a-range-picker <a-range-picker
v-model:value="queryRangePicker" v-model:value="queryRangePicker"
:presets="rangePickerPresets" allow-clear
:bordered="true" bordered
:allow-clear="false"
style="width: 100%"
:show-time="{ format: 'HH:mm:ss' }" :show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
value-format="x"
style="width: 100%"
></a-range-picker> ></a-range-picker>
</a-form-item> </a-form-item>
</a-col> </a-col>
@@ -664,7 +628,6 @@ onBeforeUnmount(() => {
:disabled="tableState.selectedRowKeys.length <= 0" :disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading" :loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')" @click.prevent="fnRecordDelete('0')"
v-perms:has="['cdr:ne:remove']"
> >
<template #icon><DeleteOutlined /></template> <template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }} {{ t('common.deleteText') }}
@@ -761,23 +724,11 @@ onBeforeUnmount(() => {
</template> </template>
<template v-if="column.key === 'id'"> <template v-if="column.key === 'id'">
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.copyText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordCopy(record.cdrJSON)"
>
<template #icon>
<CopyOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.deleteText') }}</template> <template #title>{{ t('common.deleteText') }}</template>
<a-button <a-button
type="link" type="link"
@click.prevent="fnRecordDelete(record.id)" @click.prevent="fnRecordDelete(record.id)"
v-perms:has="['cdr:ne:remove']"
> >
<template #icon> <template #icon>
<DeleteOutlined /> <DeleteOutlined />
@@ -789,7 +740,7 @@ onBeforeUnmount(() => {
</template> </template>
<template #expandedRowRender="{ record }"> <template #expandedRowRender="{ record }">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="8" :md="12" :xs="24" :offset="2"> <a-col :lg="5" :md="12" :xs="24">
<a-divider orientation="left"> <a-divider orientation="left">
{{ t('views.dashboard.cdr.cdrInfo') }} {{ t('views.dashboard.cdr.cdrInfo') }}
</a-divider> </a-divider>
@@ -812,7 +763,7 @@ onBeforeUnmount(() => {
</span> </span>
</div> </div>
</a-col> </a-col>
<a-col :lg="8" :md="12" :xs="24"> <a-col :lg="6" :md="12" :xs="24">
<a-divider orientation="left"> <a-divider orientation="left">
{{ t('views.dashboard.cdr.rowInfo') }} {{ t('views.dashboard.cdr.rowInfo') }}
</a-divider> </a-divider>
+74 -125
View File
@@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import dayjs, { Dayjs } from 'dayjs';
import { reactive, onMounted, toRaw, ref, onBeforeUnmount } from 'vue'; import { reactive, onMounted, toRaw, ref, onBeforeUnmount } from 'vue';
import { PageContainer } from 'antdv-pro-layout'; import { PageContainer } from 'antdv-pro-layout';
import { message, Modal } from 'ant-design-vue/es'; import { message, Modal } from 'ant-design-vue/es';
@@ -18,8 +17,6 @@ import { parseDateToStr } from '@/utils/date-utils';
import { OptionsType, WS } from '@/plugins/ws-websocket'; import { OptionsType, WS } from '@/plugins/ws-websocket';
import saveAs from 'file-saver'; import saveAs from 'file-saver';
import PQueue from 'p-queue'; import PQueue from 'p-queue';
import { useClipboard } from '@vueuse/core';
const { copy } = useClipboard({ legacy: true });
const { t } = useI18n(); const { t } = useI18n();
const { getDict } = useDictStore(); const { getDict } = useDictStore();
const ws = new WS(); const ws = new WS();
@@ -43,10 +40,7 @@ let dict: {
}); });
/**开始结束时间 */ /**开始结束时间 */
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([ let queryRangePicker = ref<[string, string]>(['', '']);
dayjs().startOf('hour'),
dayjs().endOf('hour'),
]);
/**查询参数 */ /**查询参数 */
let queryParams = reactive({ let queryParams = reactive({
@@ -58,9 +52,9 @@ let queryParams = reactive({
sortField: 'timestamp', sortField: 'timestamp',
sortOrder: 'desc', sortOrder: 'desc',
/**开始时间 */ /**开始时间 */
startTime: undefined as undefined | number, startTime: '',
/**结束时间 */ /**结束时间 */
endTime: undefined as undefined | number, endTime: '',
/**当前页数 */ /**当前页数 */
pageNum: 1, pageNum: 1,
/**每页条数 */ /**每页条数 */
@@ -78,7 +72,7 @@ function fnQueryReset() {
pageNum: 1, pageNum: 1,
pageSize: 20, pageSize: 20,
}); });
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')]; queryRangePicker.value = ['', ''];
tablePagination.current = 1; tablePagination.current = 1;
tablePagination.pageSize = 20; tablePagination.pageSize = 20;
fnGetList(); fnGetList();
@@ -155,11 +149,8 @@ let tableColumns: ColumnsType = [
align: 'left', align: 'left',
width: 150, width: 150,
customRender(opt) { customRender(opt) {
const record = opt.value; const cdrJSON = opt.value;
if (record?.time) { return parseDateToStr(+cdrJSON.timestamp * 1000);
return record.time;
}
return parseDateToStr(+record.timestamp * 1000);
}, },
}, },
{ {
@@ -269,19 +260,11 @@ function fnGetList(pageNum?: number) {
if (pageNum) { if (pageNum) {
queryParams.pageNum = pageNum; queryParams.pageNum = pageNum;
} }
if (!queryRangePicker.value) {
// 时间范围 queryRangePicker.value = ['', ''];
if (
Array.isArray(queryRangePicker.value) &&
queryRangePicker.value.length > 0
) {
queryParams.startTime = queryRangePicker.value[0].valueOf();
queryParams.endTime = queryRangePicker.value[1].valueOf();
} else {
queryParams.startTime = undefined;
queryParams.endTime = undefined;
} }
queryParams.startTime = queryRangePicker.value[0];
queryParams.endTime = queryRangePicker.value[1];
listMMEDataUE(toRaw(queryParams)).then(res => { listMMEDataUE(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
// 取消勾选 // 取消勾选
@@ -349,18 +332,6 @@ function fnExportList() {
}); });
} }
/**
* 复制CDR
* @param jsonStr JSON字符串
*/
function fnRecordCopy(jsonStr: string) {
if (!jsonStr) return;
const text = JSON.stringify(jsonStr, null, 2);
copy(text).then(() => {
message.success(t('common.copyOk'), 3);
});
}
/**实时数据开关 */ /**实时数据开关 */
const realTimeData = ref<boolean>(false); const realTimeData = ref<boolean>(false);
@@ -377,14 +348,12 @@ function fnRealTime() {
params: { params: {
/**订阅通道组 /**订阅通道组
* *
* MME_UE会话事件(GroupID:1011_neId) * MME_UE会话事件(GroupID:1011)
*/ */
subGroupID: `1011_${queryParams.neId}`, subGroupID: `1011_${queryParams.neId}`,
}, },
onmessage: wsMessage, onmessage: wsMessage,
onerror: (ev: any) => { onerror: wsError,
console.error(ev);
},
}; };
ws.connect(options); ws.connect(options);
} else { } else {
@@ -394,6 +363,12 @@ function fnRealTime() {
} }
} }
/**接收数据后回调 */
function wsError(ev: any) {
// 接收数据后回调
console.error(ev);
}
/**接收数据后回调 */ /**接收数据后回调 */
function wsMessage(res: Record<string, any>) { function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res; const { code, requestId, data } = res;
@@ -440,7 +415,7 @@ onMounted(() => {
dict.ueAauthCode = resArr[0].value; dict.ueAauthCode = resArr[0].value;
} }
if (resArr[1].status === 'fulfilled') { if (resArr[1].status === 'fulfilled') {
const ueEventType: any[] = JSON.parse(JSON.stringify(resArr[1].value)); const ueEventType: any[] = JSON.parse(JSON.stringify(resArr[1]));
dict.ueEventType = ueEventType.map(item => { dict.ueEventType = ueEventType.map(item => {
if (item.value === 'cm-state') { if (item.value === 'cm-state') {
item.label = item.label.replace('CM', 'ECM'); item.label = item.label.replace('CM', 'ECM');
@@ -506,7 +481,6 @@ onBeforeUnmount(() => {
v-model:value="queryParams.neId" v-model:value="queryParams.neId"
:options="neOtions" :options="neOtions"
:placeholder="t('common.selectPlease')" :placeholder="t('common.selectPlease')"
@change="fnGetList(1)"
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
@@ -524,7 +498,7 @@ onBeforeUnmount(() => {
></a-select> ></a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="6" :md="12" :xs="24"> <a-col :lg="4" :md="12" :xs="24">
<a-form-item label="IMSI" name="imsi "> <a-form-item label="IMSI" name="imsi ">
<a-input <a-input
v-model:value="queryParams.imsi" v-model:value="queryParams.imsi"
@@ -533,20 +507,6 @@ onBeforeUnmount(() => {
></a-input> ></a-input>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList(1)">
<template #icon><SearchOutlined /></template>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
{{ t('common.reset') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
<a-col :lg="8" :md="12" :xs="24"> <a-col :lg="8" :md="12" :xs="24">
<a-form-item <a-form-item
:label="t('views.dashboard.cdr.time')" :label="t('views.dashboard.cdr.time')"
@@ -563,6 +523,20 @@ onBeforeUnmount(() => {
></a-range-picker> ></a-range-picker>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList(1)">
<template #icon><SearchOutlined /></template>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
{{ t('common.reset') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
</a-row> </a-row>
</a-form> </a-form>
</a-card> </a-card>
@@ -598,7 +572,6 @@ onBeforeUnmount(() => {
:disabled="tableState.selectedRowKeys.length <= 0" :disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading" :loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')" @click.prevent="fnRecordDelete('0')"
v-perms:has="['cdr:ne:remove']"
> >
<template #icon><DeleteOutlined /></template> <template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }} {{ t('common.deleteText') }}
@@ -697,23 +670,11 @@ onBeforeUnmount(() => {
</template> </template>
<template v-if="column.key === 'id'"> <template v-if="column.key === 'id'">
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.copyText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordCopy(record.eventJSON)"
>
<template #icon>
<CopyOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.deleteText') }}</template> <template #title>{{ t('common.deleteText') }}</template>
<a-button <a-button
type="link" type="link"
@click.prevent="fnRecordDelete(record.id)" @click.prevent="fnRecordDelete(record.id)"
v-perms:has="['cdr:ne:remove']"
> >
<template #icon> <template #icon>
<DeleteOutlined /> <DeleteOutlined />
@@ -724,60 +685,48 @@ onBeforeUnmount(() => {
</template> </template>
</template> </template>
<template #expandedRowRender="{ record }"> <template #expandedRowRender="{ record }">
<a-row :gutter="16"> <div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
<a-col :lg="8" :md="12" :xs="24" :offset="2"> <a-divider orientation="left">
<a-divider orientation="left"> {{ t('views.dashboard.ue.ueInfo') }}
{{ t('views.dashboard.ue.ueInfo') }} </a-divider>
</a-divider> <div>
<div> <span>{{ t('views.ne.common.neName') }}: </span>
<span>{{ t('views.ne.common.neName') }}: </span> <span>{{ record.neName }}</span>
<span>{{ record.neName }}</span> </div>
</div> <div>
<div> <span>{{ t('views.ne.common.rmUid') }}: </span>
<span>{{ t('views.ne.common.rmUid') }}: </span> <span>{{ record.rmUID }}</span>
<span>{{ record.rmUID }}</span> </div>
</div> <a-divider orientation="left">
</a-col> {{ t('views.dashboard.ue.rowInfo') }}
<a-col :lg="8" :md="12" :xs="24"> </a-divider>
<a-divider orientation="left"> <div>
{{ t('views.dashboard.ue.rowInfo') }} <span>{{ t('views.dashboard.ue.time') }}: </span>
</a-divider> {{ parseDateToStr(record.eventJSON.timestamp * 1000) }}
<div> </div>
<span>{{ t('views.dashboard.ue.time') }}: </span> <div>
<template v-if="record.eventJSON?.time"> <span>{{ t('views.dashboard.ue.eventType') }}: </span>
{{ record.eventJSON.time }} <DictTag :options="dict.ueEventType" :value="record.eventType" />
</template> </div>
<template v-else> <div>
{{ parseDateToStr(record.eventJSON.timestamp * 1000) }} <span>{{ t('views.dashboard.ue.result') }}: </span>
</template> <span v-if="record.eventType === 'auth-result'">
</div>
<div>
<span>{{ t('views.dashboard.ue.eventType') }}: </span>
<DictTag <DictTag
:options="dict.ueEventType" :options="dict.ueAauthCode"
:value="record.eventType" :value="record.eventJSON.result"
/> />
</div> </span>
<div> <span v-if="record.eventType === 'detach'">
<span>{{ t('views.dashboard.ue.result') }}: </span> {{ t('views.dashboard.ue.resultOk') }}
<span v-if="record.eventType === 'auth-result'"> </span>
<DictTag <span v-if="record.eventType === 'cm-state'">
:options="dict.ueAauthCode" <DictTag
:value="record.eventJSON.result" :options="dict.ueEventCmState"
/> :value="record.eventJSON.result"
</span> />
<span v-if="record.eventType === 'detach'"> </span>
{{ t('views.dashboard.ue.resultOk') }} </div>
</span> </div>
<span v-if="record.eventType === 'cm-state'">
<DictTag
:options="dict.ueEventCmState"
:value="record.eventJSON.result"
/>
</span>
</div>
</a-col>
</a-row>
</template> </template>
</a-table> </a-table>
</a-card> </a-card>
@@ -77,12 +77,8 @@ onMounted(() => {
<div></div> <div></div>
<div> <div>
{{ t('views.dashboard.overview.userActivity.time') }}:&nbsp; {{ t('views.dashboard.overview.userActivity.time') }}:&nbsp;
<span :title="item.data.releaseTime"> <span :title="parseDateToStr(item.data.releaseTime * 1000)">
{{ {{ parseDateToStr(item.data.releaseTime * 1000) }}
typeof item.data.releaseTime === 'number'
? parseDateToStr(+item.data.releaseTime * 1000)
: item.data.releaseTime
}}
</span> </span>
</div> </div>
</div> </div>
@@ -137,12 +133,18 @@ onMounted(() => {
</div> </div>
<div> <div>
{{ t('views.dashboard.overview.userActivity.time') }}: {{ t('views.dashboard.overview.userActivity.time') }}:
<template v-if="item.data?.time"> <span
{{ item.data.time }} v-if="item.type === 'auth-result'"
</template> :title="item.data.authTime"
<template v-else> >
{{ parseDateToStr(+item.data.timestamp * 1000) }} {{ item.data.authTime }}
</template> </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> </div>
@@ -161,7 +163,7 @@ onMounted(() => {
<div v-if="item.type === 'auth-result'"> <div v-if="item.type === 'auth-result'">
{{ t('views.dashboard.overview.userActivity.result') }}:&nbsp; {{ t('views.dashboard.overview.userActivity.result') }}:&nbsp;
<span> <span>
<DictTag :options="dict.ueAauthCode" :value="item.data.result" /> <DictTag :options="dict.ueAauthCode" :value="item.data.authCode" />
</span> </span>
</div> </div>
<div v-if="item.type === 'detach'"> <div v-if="item.type === 'detach'">
@@ -171,7 +173,7 @@ onMounted(() => {
<div class="card-ue-w33" v-if="item.type === 'cm-state'"> <div class="card-ue-w33" v-if="item.type === 'cm-state'">
{{ t('views.dashboard.overview.userActivity.result') }}:&nbsp; {{ t('views.dashboard.overview.userActivity.result') }}:&nbsp;
<span> <span>
<DictTag :options="dict.ueEventCmState" :value="item.data.result" /> <DictTag :options="dict.ueEventCmState" :value="item.data.status" />
</span> </span>
</div> </div>
</div> </div>
@@ -196,16 +198,12 @@ onMounted(() => {
</span> </span>
</div> </div>
<div> <div>
IMSI: <span :title="item.data?.imsi">{{ item.data?.imsi }}</span> IMSI: <span :title="item.data.imsi">{{ item.data.imsi }}</span>
</div> </div>
<div> <div>
{{ t('views.dashboard.overview.userActivity.time') }}: {{ t('views.dashboard.overview.userActivity.time') }}:
<span :title="item.data?.timestamp"> <span :title="item.data.timestamp">
{{ {{ parseDateToStr(+item.data.timestamp * 1000) }}
typeof item.data?.timestamp === 'number'
? parseDateToStr(+item.data?.timestamp * 1000)
: item.data?.timestamp
}}
</span> </span>
</div> </div>
</div> </div>
+1 -6
View File
@@ -16,8 +16,6 @@
.column { .column {
flex: 3; flex: 3;
position: relative; position: relative;
display: flex;
flex-direction: column;
} }
/* 边框 */ /* 边框 */
@@ -81,14 +79,13 @@
.upfFlow .inner .chart { .upfFlow .inner .chart {
width: 100%; width: 100%;
height: 100%; height: 100%;
margin-top: 0rem; margin-top: 1rem;
} }
/* 网络拓扑 */ /* 网络拓扑 */
.topology { .topology {
/* min-height: 27.8rem; */ /* min-height: 27.8rem; */
height: 56.4%; height: 56.4%;
flex: 1;
} }
.topology .inner h3 { .topology .inner h3 {
display: flex; display: flex;
@@ -182,7 +179,6 @@
.userActivity { .userActivity {
/* min-height: 35.8rem; */ /* min-height: 35.8rem; */
height: 54.6%; height: 54.6%;
flex: 1;
} }
.userActivity .inner .chart { .userActivity .inner .chart {
width: 100%; width: 100%;
@@ -263,7 +259,6 @@
.alarmType { .alarmType {
/* min-height: 25rem; */ /* min-height: 25rem; */
height: 46%; height: 46%;
flex: 1;
} }
.alarmType .inner .chart { .alarmType .inner .chart {
width: 100%; width: 100%;
@@ -36,6 +36,11 @@ export function upfFlowParse(data: Record<string, string>) {
upfFlowData.value.lineYDown.shift(); upfFlowData.value.lineYDown.shift();
upfFlowData.value.cap -= 1; upfFlowData.value.cap -= 1;
} }
// UPF-总流量数0天 当天24小时
upfTFParse('0', {
up: upfTotalFlow.value['0'].up + +data['UPF.03'],
down: upfTotalFlow.value['0'].down + +data['UPF.06'],
});
} }
type TFType = { type TFType = {
+14 -30
View File
@@ -1,6 +1,6 @@
import { RESULT_CODE_ERROR } from '@/constants/result-constants'; import { RESULT_CODE_ERROR } from '@/constants/result-constants';
import { OptionsType, WS } from '@/plugins/ws-websocket'; import { OptionsType, WS } from '@/plugins/ws-websocket';
import { onBeforeUnmount, ref } from 'vue'; import { onBeforeUnmount, onMounted } from 'vue';
import { import {
eventListParse, eventListParse,
eventItemParseAndPush, eventItemParseAndPush,
@@ -12,15 +12,9 @@ import {
upfFlowParse, upfFlowParse,
upfTotalFlowReset, upfTotalFlowReset,
} from './useUPFTotalFlow'; } from './useUPFTotalFlow';
import { topologyReset, neStateParse, neStateRequestMap } from './useTopology'; import { topologyReset, neStateParse } from './useTopology';
import PQueue from 'p-queue'; import PQueue from 'p-queue';
/**UPF-的Id */
export const upfWhoId = ref<any>('');
/**UPF-的RmUid */
export const upfWhoRmUid = ref<any>('');
/**websocket连接 */ /**websocket连接 */
export default function useWS() { export default function useWS() {
const ws = new WS(); const ws = new WS();
@@ -33,12 +27,13 @@ export default function useWS() {
/**接收数据后回调 */ /**接收数据后回调 */
function wsMessage(res: Record<string, any>) { function wsMessage(res: Record<string, any>) {
//console.log(res); // console.log(res);
const { code, requestId, data } = res; const { code, requestId, data } = res;
if (code === RESULT_CODE_ERROR) { if (code === RESULT_CODE_ERROR) {
console.warn(res.msg); console.warn(res.msg);
return; return;
} }
// 网元状态 // 网元状态
if (requestId && requestId.startsWith('neState')) { if (requestId && requestId.startsWith('neState')) {
const neType = requestId.split('_')[1]; const neType = requestId.split('_')[1];
@@ -49,7 +44,7 @@ export default function useWS() {
// 普通信息 // 普通信息
switch (requestId) { switch (requestId) {
// AMF_UE会话事件 // AMF_UE会话事件
case 'amf_1010_001': case 'amf_1010':
if (Array.isArray(data.rows)) { if (Array.isArray(data.rows)) {
eventListParse('amf_ue', data); eventListParse('amf_ue', data);
} }
@@ -77,19 +72,20 @@ export default function useWS() {
upfTFParse('30', data); upfTFParse('30', data);
break; break;
} }
// 订阅组信息 // 订阅组信息
if (!data?.groupId) { if (!data?.groupId) {
return; return;
} }
switch (data.groupId) { switch (data.groupId) {
// kpiEvent 指标UPF // kpiEvent 指标UPF
case '10_UPF_' + upfWhoId.value: case '12_001':
if (data.data) { if (data.data) {
upfFlowParse(data.data); upfFlowParse(data.data);
} }
break; break;
// AMF_UE会话事件 // AMF_UE会话事件
case '1010_001': case '1010':
if (data.data) { if (data.data) {
queue.add(() => eventItemParseAndPush('amf_ue', data.data)); queue.add(() => eventItemParseAndPush('amf_ue', data.data));
} }
@@ -132,7 +128,7 @@ export default function useWS() {
function userActivitySend() { function userActivitySend() {
// AMF_UE会话事件 // AMF_UE会话事件
ws.send({ ws.send({
requestId: 'amf_1010_001', requestId: 'amf_1010',
type: 'amf_ue', type: 'amf_ue',
data: { data: {
neType: 'AMF', neType: 'AMF',
@@ -172,28 +168,18 @@ export default function useWS() {
}); });
} }
/**重新发送至UPF 10_UPF_neId */ onMounted(() => {
function reSendUPF(neId: string) {
upfWhoId.value = neId;
//初始时时无需还原全部属性以及关闭
if (ws.state() === WebSocket.OPEN) {
ws.close();
// userActivityReset();
upfTotalFlowReset();
neStateRequestMap.value = new Map();
//topologyReset();
}
const options: OptionsType = { const options: OptionsType = {
url: '/ws', url: '/ws',
params: { params: {
/**订阅通道组 /**订阅通道组
* *
* 指标UPF (GroupID:10_neType_neId) * 指标UPF (GroupID:12_neId)
* AMF_UE会话事件(GroupID:1010_neId) * AMF_UE会话事件(GroupID:1010)
* MME_UE会话事件(GroupID:1011_neId) * MME_UE会话事件(GroupID:1011_neId)
* IMS_CDR会话事件(GroupID:1005_neId) * IMS_CDR会话事件(GroupID:1005_neId)
*/ */
subGroupID: '10_UPF_' + neId + ',1010_001,1011_001,1005_001', subGroupID: '12_001,1010,1011_001,1005_001',
}, },
onmessage: wsMessage, onmessage: wsMessage,
onerror: (ev: any) => { onerror: (ev: any) => {
@@ -201,20 +187,18 @@ export default function useWS() {
}, },
}; };
ws.connect(options); ws.connect(options);
} });
onBeforeUnmount(() => { onBeforeUnmount(() => {
ws.close(); ws.close();
userActivityReset(); userActivityReset();
upfTotalFlowReset(); upfTotalFlowReset();
topologyReset(); topologyReset();
upfWhoId.value = '';
}); });
return { return {
wsSend, wsSend,
userActivitySend, userActivitySend,
upfTFSend, upfTFSend,
reSendUPF,
}; };
} }
+69 -262
View File
@@ -26,15 +26,10 @@ import useWS from './hooks/useWS';
import useAppStore from '@/store/modules/app'; import useAppStore from '@/store/modules/app';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import useNeInfoStore from '@/store/modules/neinfo';
import { message } from 'ant-design-vue';
import { upfWhoId } from './hooks/useWS';
const neInfoStore = useNeInfoStore();
const router = useRouter(); const router = useRouter();
const appStore = useAppStore(); const appStore = useAppStore();
const { t } = useI18n(); const { t } = useI18n();
const { wsSend, userActivitySend, upfTFSend, reSendUPF } = useWS(); const { wsSend, userActivitySend, upfTFSend } = useWS();
/**概览状态类型 */ /**概览状态类型 */
type SkimStateType = { type SkimStateType = {
@@ -65,14 +60,10 @@ let skimState: SkimStateType = reactive({
enbUeNum: 0, enbUeNum: 0,
}); });
/**网元参数 */
let neCascaderOptions = ref<Record<string, any>[]>([]);
/**总览节点 */ /**总览节点 */
const viewportDom = ref<HTMLElement | null>(null); const viewportDom = ref<HTMLElement | null>(null);
const { isFullscreen, toggle } = useFullscreen(viewportDom); const { isFullscreen, toggle } = useFullscreen(viewportDom);
let initFlag = false;
/**10s调度器 */ /**10s调度器 */
const interval10s = ref<any>(null); const interval10s = ref<any>(null);
@@ -103,104 +94,62 @@ function fnGetNeState() {
/**获取概览信息 */ /**获取概览信息 */
async function fnGetSkim() { async function fnGetSkim() {
const neHandlers = new Map([ const resArr = await Promise.allSettled([
// [ listUDMSub({
// 'UDM', neid: '001',
// { pageNum: 1,
// request: (neId: string) => pageSize: 1,
// listUDMSub({ neId: neId, pageNum: 1, pageSize: 1 }), }),
// process: (res: any) => listUENumBySMF('001'),
// res.code === RESULT_CODE_SUCCESS && listUENumByIMS('001'),
// (skimState.udmSubNum += res.total), listBase5G({
// }, neType: 'AMF',
// ], neId: '001',
[ }),
'SMF', listBase5G({
{ neType: 'MME',
request: (neId: string) => listUENumBySMF(neId), neId: '001',
process: (res: any) => }),
res.code === RESULT_CODE_SUCCESS && (skimState.smfUeNum += res.data),
},
],
[
'IMS',
{
request: (neId: string) => listUENumByIMS(neId),
process: (res: any) =>
res.code === RESULT_CODE_SUCCESS && (skimState.imsUeNum += res.data),
},
],
[
'AMF',
{
request: (neId: string) => listBase5G({ neType: 'AMF', neId }),
process: (res: any) => {
if (res.code === RESULT_CODE_SUCCESS) {
skimState.gnbNum += res.total;
skimState.gnbUeNum += res.rows.reduce(
(sum: number, item: any) => sum + item.ueNum,
0
);
}
},
},
],
[
'MME',
{
request: (neId: string) => listBase5G({ neType: 'MME', neId }),
process: (res: any) => {
if (res.code === RESULT_CODE_SUCCESS) {
skimState.enbNum += res.total;
skimState.enbUeNum += res.rows.reduce(
(sum: number, item: any) => sum + item.ueNum,
0
);
}
},
},
],
]); ]);
const requests = neCascaderOptions.value.flatMap( if (resArr[0].status === 'fulfilled') {
(ne: any) => const res0 = resArr[0].value;
ne.children if (res0.code === RESULT_CODE_SUCCESS) {
?.map((child: any) => { skimState.udmSubNum = res0.total;
const handler = neHandlers.get(child.neType);
return handler
? {
promise: handler.request(child.neId),
process: handler.process,
}
: null;
})
.filter(Boolean) || []
);
const results = await Promise.allSettled(requests.map(r => r.promise));
// 重置
Object.assign(skimState, {
udmSubNum: 0,
smfUeNum: 0,
imsUeNum: 0,
gnbNum: 0,
gnbUeNum: 0,
enbNum: 0,
enbUeNum: 0,
});
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
requests[index].process(result.value);
} }
}); }
if (resArr[1].status === 'fulfilled') {
// UDM const res1 = resArr[1].value;
listUDMSub({ neId: udmNeId.value, pageNum: 1, pageSize: 1 }).then(res => { if (res1.code === RESULT_CODE_SUCCESS) {
if (res.code === RESULT_CODE_SUCCESS) { skimState.smfUeNum = res1.data;
skimState.udmSubNum = res.total;
} }
}); }
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;
});
}
}
} }
/**初始数据函数 */ /**初始数据函数 */
@@ -213,7 +162,7 @@ function loadData() {
clearInterval(interval10s.value); clearInterval(interval10s.value);
interval10s.value = setInterval(() => { interval10s.value = setInterval(() => {
if (!interval10s.value) return; if (!interval10s.value) return
if (upfTFActive.value === '0') { if (upfTFActive.value === '0') {
upfTFSend('7'); upfTFSend('7');
upfTFActive.value = '7'; upfTFActive.value = '7';
@@ -228,7 +177,7 @@ function loadData() {
clearInterval(interval5s.value); clearInterval(interval5s.value);
interval5s.value = setInterval(() => { interval5s.value = setInterval(() => {
if (!interval5s.value || !initFlag) return; if (!interval5s.value) return
fnGetSkim(); // 获取概览信息 fnGetSkim(); // 获取概览信息
fnGetNeState(); // 获取网元状态 fnGetNeState(); // 获取网元状态
}, 5_000); }, 5_000);
@@ -239,101 +188,10 @@ function fnToRouter(name: string, query?: any) {
router.push({ name, query }); router.push({ name, query });
} }
/**网元参数 */
let neOtions = ref<Record<string, any>[]>([]);
// UPF实时流量下拉框选择
function fnSelectNe(value: any, option: any) {
upfWhoId.value = value;
reSendUPF(value);
// upfTotalFlow.value.map((item: any) => {
// item.requestFlag = false;
// });
for (var key in upfTotalFlow.value) {
upfTotalFlow.value[key].requestFlag = false;
}
// loadData();
}
let udmNeId = ref<string>('001');
let udmOtions = ref<Record<string, any>[]>([]);
/**用户数量-选择UDM */
function fnSelectUDM(e: any) {
udmNeId.value = e.key;
listUDMSub({ neId: udmNeId.value, pageNum: 1, pageSize: 1 }).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
skimState.udmSubNum = res.total;
}
});
}
// 定义一个方法返回 views 容器
const getPopupContainer = () => {
// 使用 ref 或其他方式来引用你的 views 容器
// 如果 views 容器直接在这个组件内部,你可以使用 ref
// 但在这个例子中,我们假设它是通过类名来获取的
return document.querySelector('.viewport');
};
onMounted(() => { onMounted(() => {
neInfoStore fnGetSkim().then(() => {
.fnNelist() loadData();
.then(res => { });
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
// UPF
let arr: Record<string, any>[] = [];
res.data.forEach(i => {
if (i.neType === 'UPF') {
arr.push({ value: i.neId, label: i.neName, rmUid: i.rmUid });
}
});
neOtions.value = arr;
if (arr.length > 0) {
//queryParams.neRealId = arr[0].value;
fnSelectNe(arr[0].value, arr[0]);
}
// UDM
let arr1: Record<string, any>[] = [];
res.data.forEach((v: any) => {
if (v.neType === 'UDM') {
arr1.push({ value: v.neId, label: v.neName, rmUid: v.rmUid });
}
});
udmOtions.value = arr1;
if (arr1.length > 0) {
fnSelectUDM({ key: arr1[0].value });
}
// 过滤不可用的网元
neCascaderOptions.value = neInfoStore.getNeCascaderOptions.filter(
(item: any) => {
return ['UDM', 'SMF', 'IMS', 'AMF', 'MME'].includes(item.value);
}
);
if (neCascaderOptions.value.length === 0) {
message.warning({
content: t('common.noData'),
duration: 2,
});
return;
}
}
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
})
.finally(() => {
initFlag = true;
fnGetSkim().then(() => {
loadData();
});
});
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
@@ -341,7 +199,6 @@ onBeforeUnmount(() => {
interval10s.value = null; interval10s.value = null;
clearInterval(interval5s.value); clearInterval(interval5s.value);
interval5s.value = null; interval5s.value = null;
initFlag = false;
}); });
</script> </script>
@@ -371,28 +228,17 @@ onBeforeUnmount(() => {
<div class="data"> <div class="data">
<div <div
class="item toRouter" class="item toRouter"
@click="fnToRouter('Sub_2010')"
:title="t('views.dashboard.overview.toRouter')" :title="t('views.dashboard.overview.toRouter')"
> >
<div @click="fnToRouter('Sub_2010')"> <div>
<UserOutlined <UserOutlined
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem" style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
/> />
{{ skimState.udmSubNum }} {{ skimState.udmSubNum }}
</div> </div>
<span> <span>
<a-dropdown :trigger="['click']"> {{ t('views.dashboard.overview.skim.users') }}
<div class="toDeep-text">
{{ t('views.dashboard.overview.skim.users') }}
<DownOutlined style="margin-left: 12px; font-size: 12px" />
</div>
<template #overlay>
<a-menu @click="fnSelectUDM">
<a-menu-item v-for="v in udmOtions" :key="v.value" :disabled="udmNeId === v.value">
{{ v.label }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</span> </span>
</div> </div>
<div <div
@@ -400,7 +246,6 @@ onBeforeUnmount(() => {
@click="fnToRouter('Ims_2080')" @click="fnToRouter('Ims_2080')"
:title="t('views.dashboard.overview.toRouter')" :title="t('views.dashboard.overview.toRouter')"
style="margin: 0 12px" style="margin: 0 12px"
v-perms:has="['dashboard:overview:imsUeNum']"
> >
<div> <div>
<img :src="svgUserIMS" style="width: 18px; margin-right: 8px" /> <img :src="svgUserIMS" style="width: 18px; margin-right: 8px" />
@@ -414,7 +259,6 @@ onBeforeUnmount(() => {
class="item toRouter" class="item toRouter"
@click="fnToRouter('Ue_2081')" @click="fnToRouter('Ue_2081')"
:title="t('views.dashboard.overview.toRouter')" :title="t('views.dashboard.overview.toRouter')"
v-perms:has="['dashboard:overview:smfUeNum']"
> >
<div> <div>
<img :src="svgUserSMF" style="width: 18px; margin-right: 8px" /> <img :src="svgUserSMF" style="width: 18px; margin-right: 8px" />
@@ -427,7 +271,7 @@ onBeforeUnmount(() => {
</div> </div>
</div> </div>
</div> </div>
<div class="skim panel base" v-perms:has="['dashboard:overview:gnbBase']"> <div class="skim panel base">
<div class="inner"> <div class="inner">
<h3> <h3>
<GlobalOutlined style="color: #68d8fe" />&nbsp;&nbsp; 5G <GlobalOutlined style="color: #68d8fe" />&nbsp;&nbsp; 5G
@@ -436,7 +280,7 @@ onBeforeUnmount(() => {
<div class="data"> <div class="data">
<div <div
class="item toRouter" class="item toRouter"
@click="fnToRouter('BaseStation_2096', { neType: 'AMF' })" @click="fnToRouter('Base5G_2082', { neType: 'AMF' })"
:title="t('views.dashboard.overview.toRouter')" :title="t('views.dashboard.overview.toRouter')"
> >
<div style="align-items: flex-start"> <div style="align-items: flex-start">
@@ -450,7 +294,7 @@ onBeforeUnmount(() => {
</div> </div>
<div <div
class="item toRouter" class="item toRouter"
@click="fnToRouter('BaseStation_2096', { neType: 'AMF' })" @click="fnToRouter('Base5G_2082', { neType: 'AMF' })"
:title="t('views.dashboard.overview.toRouter')" :title="t('views.dashboard.overview.toRouter')"
> >
<div style="align-items: flex-start"> <div style="align-items: flex-start">
@@ -464,7 +308,7 @@ onBeforeUnmount(() => {
</div> </div>
</div> </div>
</div> </div>
<div class="skim panel base" v-perms:has="['dashboard:overview:enbBase']"> <div class="skim panel base">
<div class="inner"> <div class="inner">
<h3> <h3>
<GlobalOutlined style="color: #68d8fe" />&nbsp;&nbsp; 4G <GlobalOutlined style="color: #68d8fe" />&nbsp;&nbsp; 4G
@@ -473,7 +317,7 @@ onBeforeUnmount(() => {
<div class="data"> <div class="data">
<div <div
class="item toRouter" class="item toRouter"
@click="fnToRouter('BaseStation_2096', { neType: 'MME' })" @click="fnToRouter('Base5G_2082', { neType: 'MME' })"
:title="t('views.dashboard.overview.toRouter')" :title="t('views.dashboard.overview.toRouter')"
> >
<div style="align-items: flex-start"> <div style="align-items: flex-start">
@@ -487,7 +331,7 @@ onBeforeUnmount(() => {
</div> </div>
<div <div
class="item toRouter" class="item toRouter"
@click="fnToRouter('BaseStation_2096', { neType: 'MME' })" @click="fnToRouter('Base5G_2082', { neType: 'MME' })"
:title="t('views.dashboard.overview.toRouter')" :title="t('views.dashboard.overview.toRouter')"
> >
<div style="align-items: flex-start"> <div style="align-items: flex-start">
@@ -521,23 +365,12 @@ onBeforeUnmount(() => {
<div class="inner"> <div class="inner">
<h3 <h3
class="toRouter" class="toRouter"
@click="fnToRouter('GoldTarget_2104')"
:title="t('views.dashboard.overview.toRouter')" :title="t('views.dashboard.overview.toRouter')"
style="display: flex; align-items: center"
> >
<AreaChartOutlined style="color: #68d8fe" />&nbsp;&nbsp; <AreaChartOutlined style="color: #68d8fe" />&nbsp;&nbsp;
<span @click="fnToRouter('GoldTarget_2104')">{{ {{ t('views.dashboard.overview.upfFlow.title') }}
t('views.dashboard.overview.upfFlow.title')
}}</span>
<a-select
v-model:value="upfWhoId"
:options="neOtions"
:get-Popup-Container="getPopupContainer"
class="toDeep"
style="width: 100px; color: #fff; margin-left: auto"
@change="fnSelectNe"
/>
</h3> </h3>
<div class="chart"> <div class="chart">
<UPFFlow /> <UPFFlow />
</div> </div>
@@ -655,30 +488,4 @@ onBeforeUnmount(() => {
<style lang="less" scoped> <style lang="less" scoped>
@import url('./css/index.css'); @import url('./css/index.css');
.toDeep {
--editor-background-color: blue;
}
.toDeep :deep(.ant-select-selector) {
background-color: #101129;
border: none;
}
.toDeep :deep(.ant-select-arrow) {
color: #fff;
}
.toDeep :deep(.ant-select-selection-item) {
color: #fff;
}
.toDeep-text {
color: #4c9bfd !important;
font-size: 0.844rem !important;
position: relative !important;
line-height: 2rem !important;
white-space: nowrap !important;
text-align: start !important;
text-overflow: ellipsis !important;
overflow: hidden !important;
}
</style> </style>
-850
View File
@@ -1,850 +0,0 @@
<script setup lang="ts">
import { reactive, onMounted, toRaw, onBeforeUnmount, ref } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { Modal, message } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider';
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import { ColumnsType } from 'ant-design-vue/es/table';
import useNeInfoStore from '@/store/modules/neinfo';
import useI18n from '@/hooks/useI18n';
import {
RESULT_CODE_ERROR,
RESULT_CODE_SUCCESS,
} from '@/constants/result-constants';
import {
delSGWCDataCDR,
exportSGWCDataCDR,
listSGWCDataCDR,
} from '@/api/neData/sgwc';
import { OptionsType, WS } from '@/plugins/ws-websocket';
import PQueue from 'p-queue';
import saveAs from 'file-saver';
import { useClipboard } from '@vueuse/core';
import dayjs, { type Dayjs } from 'dayjs';
const { copy } = useClipboard({ legacy: true });
const { t } = useI18n();
const ws = new WS();
const queue = new PQueue({ concurrency: 1, autoStart: true });
/**网元可选 */
let neOtions = ref<Record<string, any>[]>([]);
/**开始结束时间 */
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([
dayjs().startOf('hour'),
dayjs().endOf('hour'),
]);
/**时间范围 */
let rangePickerPresets = ref([
{
label: 'Now hour',
value: [dayjs().startOf('hour'), dayjs().endOf('hour')],
},
{ label: 'Today', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
{
label: 'Yesterday',
value: [
dayjs().subtract(1, 'day').startOf('day'),
dayjs().subtract(1, 'day').endOf('day'),
],
},
]);
/**查询参数 */
let queryParams = reactive({
/**网元类型 */
neType: 'SGWC',
neId: '001',
imsi: '',
msisdn: '',
sortField: 'timestamp',
sortOrder: 'desc',
/**开始时间 */
startTime: undefined as undefined | number,
/**结束时间 */
endTime: undefined as undefined | number,
/**当前页数 */
pageNum: 1,
/**每页条数 */
pageSize: 20,
});
/**查询参数重置 */
function fnQueryReset() {
queryParams = Object.assign(queryParams, {
imsi: '',
msisdn: '',
startTime: undefined,
endTime: undefined,
pageNum: 1,
pageSize: 20,
});
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')];
tablePagination.current = 1;
tablePagination.pageSize = 20;
fnGetList();
}
/**表格状态类型 */
type TabeStateType = {
/**加载等待 */
loading: boolean;
/**紧凑型 */
size: SizeType;
/**搜索栏 */
seached: boolean;
/**记录数据 */
data: object[];
/**勾选记录 */
selectedRowKeys: (string | number)[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
size: 'middle',
seached: true,
data: [],
selectedRowKeys: [],
});
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
{
title: t('views.dashboard.cdr.chargingID'), // 计费ID
dataIndex: 'cdrJSON',
align: 'left',
width: 100,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.chargingID;
},
},
{
title: t('views.dashboard.cdr.sgwcServedIMSI'), // IMSI
dataIndex: 'cdrJSON',
align: 'left',
width: 150,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.servedIMSI;
},
},
{
title: t('views.dashboard.cdr.sgwcServedMSISDN'), // MSISDN
dataIndex: 'cdrJSON',
align: 'left',
width: 150,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.servedMSISDN;
},
},
{
title: t('views.dashboard.cdr.sgwcVolumeGPRSUplink'), // GPRS 上行链路
dataIndex: 'cdrJSON',
align: 'left',
width: 150,
customRender(opt) {
const cdrJSON = opt.value;
const listOfTrafficVolumes = cdrJSON.listOfTrafficVolumes;
if (
!Array.isArray(listOfTrafficVolumes) ||
listOfTrafficVolumes.length < 1
) {
return 0;
}
let dataVolumeGPRSUplink = 0;
for (const used of listOfTrafficVolumes) {
const v = +used.dataVolumeGPRSUplink;
dataVolumeGPRSUplink += isNaN(v) ? 0 : v;
}
return dataVolumeGPRSUplink;
},
},
{
title: t('views.dashboard.cdr.sgwcVolumeGPRSDownlink'), // GPRS 下行链路
dataIndex: 'cdrJSON',
align: 'left',
width: 180,
customRender(opt) {
const cdrJSON = opt.value;
const listOfTrafficVolumes = cdrJSON.listOfTrafficVolumes;
if (
!Array.isArray(listOfTrafficVolumes) ||
listOfTrafficVolumes.length < 1
) {
return 0;
}
let dataVolumeGPRSDownlink = 0;
for (const used of listOfTrafficVolumes) {
const v = +used.dataVolumeGPRSDownlink;
dataVolumeGPRSDownlink += isNaN(v) ? 0 : v;
}
return dataVolumeGPRSDownlink;
},
},
{
title: t('views.dashboard.cdr.durationTime'), // 持续时间
dataIndex: 'cdrJSON',
align: 'left',
width: 100,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.duration;
},
},
{
title: t('views.dashboard.cdr.invocationTime'), // 操作时间
dataIndex: 'cdrJSON',
align: 'left',
width: 200,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.recordOpeningTime;
},
},
{
title: t('common.operate'),
key: 'id',
align: 'left',
},
];
/**表格分页器参数 */
let tablePagination = reactive({
/**当前页数 */
current: 1,
/**每页条数 */
pageSize: 20,
/**默认的每页条数 */
defaultPageSize: 20,
/**指定每页可以显示多少条 */
pageSizeOptions: ['10', '20', '50', '100'],
/**只有一页时是否隐藏分页器 */
hideOnSinglePage: false,
/**是否可以快速跳转至某页 */
showQuickJumper: true,
/**是否可以改变 pageSize */
showSizeChanger: true,
/**数据总数 */
total: 0,
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
onChange: (page: number, pageSize: number) => {
tablePagination.current = page;
tablePagination.pageSize = pageSize;
queryParams.pageNum = page;
queryParams.pageSize = pageSize;
fnGetList();
},
});
/**表格紧凑型变更操作 */
function fnTableSize({ key }: MenuInfo) {
tableState.size = key as SizeType;
}
/**表格多选 */
function fnTableSelectedRowKeys(keys: (string | number)[]) {
tableState.selectedRowKeys = keys;
}
/**对话框对象信息状态类型 */
type ModalStateType = {
/**确定按钮 loading */
confirmLoading: boolean;
/**最大ID值 */
maxId: number;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
confirmLoading: false,
maxId: 0,
});
/**
* 记录删除
* @param id 编号
*/
function fnRecordDelete(id: string) {
if (!id || modalState.confirmLoading) return;
let msg = id;
if (id === '0') {
msg = `${id}... ${tableState.selectedRowKeys.length}`;
id = tableState.selectedRowKeys.join(',');
}
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.dashboard.cdr.delTip', { msg }),
onOk() {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
delSGWCDataCDR(id)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
fnGetList(1);
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
},
});
}
/**
* 复制CDR
* @param jsonStr JSON字符串
*/
function fnRecordCopy(jsonStr: string) {
if (!jsonStr) return;
const text = JSON.stringify(jsonStr, null, 2);
copy(text).then(() => {
message.success(t('common.copyOk'), 3);
});
}
/**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (tableState.loading) return;
tableState.loading = true;
if (pageNum) {
queryParams.pageNum = pageNum;
}
// 时间范围
if (
Array.isArray(queryRangePicker.value) &&
queryRangePicker.value.length > 0
) {
queryParams.startTime = queryRangePicker.value[0].valueOf();
queryParams.endTime = queryRangePicker.value[1].valueOf();
} else {
queryParams.startTime = undefined;
queryParams.endTime = undefined;
}
listSGWCDataCDR(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
// 取消勾选
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
tablePagination.total = res.total;
// 遍历处理cdr字符串数据
tableState.data = res.rows.map(item => {
let cdrJSON = item.cdrJSON;
if (!cdrJSON) {
Reflect.set(item, 'cdrJSON', {});
}
try {
cdrJSON = JSON.parse(cdrJSON);
Reflect.set(item, 'cdrJSON', cdrJSON);
} catch (error) {
console.error(error);
Reflect.set(item, 'cdrJSON', {});
}
return item;
});
// 取最大值ID用作实时累加
if (res.total > 0) {
modalState.maxId = Number(res.rows[0].id);
}
}
tableState.loading = false;
});
}
/**列表导出 */
function fnExportList() {
if (modalState.confirmLoading) return;
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.dashboard.cdr.exportTip'),
onOk() {
const hide = message.loading(t('common.loading'), 0);
const querys = toRaw(queryParams);
querys.pageSize = 10000;
exportSGWCDataCDR(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
saveAs(res.data, `sgwc_cdr_event_export_${Date.now()}.xlsx`);
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
},
});
}
/**实时数据开关 */
const realTimeData = ref<boolean>(false);
/**
* 实时数据
*/
function fnRealTime() {
realTimeData.value = !realTimeData.value;
if (realTimeData.value) {
tableState.seached = false;
// 建立链接
const options: OptionsType = {
url: '/ws',
params: {
/**订阅通道组
*
* CDR会话事件-SGWC (GroupID:1008)
*/
subGroupID: `1008_${queryParams.neId}`,
},
onmessage: wsMessage,
onerror: (ev: any) => {
console.error(ev);
},
};
ws.connect(options);
} else {
ws.close();
tableState.seached = true;
fnGetList(1);
}
}
/**接收数据后回调 */
function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res;
if (code === RESULT_CODE_ERROR) {
console.warn(res.msg);
return;
}
// 订阅组信息
if (!data?.groupId) {
return;
}
// cdrEvent CDR会话事件
if (data.groupId === `1008_${queryParams.neId}`) {
const cdrEvent = data.data;
queue.add(async () => {
modalState.maxId += 1;
tableState.data.unshift({
id: modalState.maxId,
neType: cdrEvent.neType,
neName: cdrEvent.neName,
rmUID: cdrEvent.rmUID,
timestamp: cdrEvent.timestamp,
cdrJSON: cdrEvent.CDR,
});
tablePagination.total += 1;
if (tableState.data.length > 100) {
tableState.data.pop();
}
await new Promise(resolve => setTimeout(resolve, 800));
});
}
}
onMounted(() => {
// 获取网元网元列表
useNeInfoStore()
.fnNelist()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
let arr: Record<string, any>[] = [];
res.data.forEach(i => {
if (i.neType === 'SGWC') {
arr.push({ value: i.neId, label: i.neName });
}
});
neOtions.value = arr;
if (arr.length > 0) {
queryParams.neId = arr[0].value;
}
}
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
})
.finally(() => {
// 获取列表数据
fnGetList();
});
});
onBeforeUnmount(() => {
if (ws.state() !== -1) {
ws.close();
}
});
</script>
<template>
<PageContainer>
<a-card
v-show="tableState.seached"
:bordered="false"
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
>
<!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item label="SGWC" name="neId ">
<a-select
v-model:value="queryParams.neId"
:options="neOtions"
:placeholder="t('common.selectPlease')"
@change="fnQueryReset()"
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item
:label="t('views.dashboard.cdr.sgwcServedIMSI')"
name="imsi"
>
<a-input
v-model:value="queryParams.imsi"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="40"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item
:label="t('views.dashboard.cdr.sgwcServedMSISDN')"
name="msisdn"
>
<a-input
v-model:value="queryParams.msisdn"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="40"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="4" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList(1)">
<template #icon><SearchOutlined /></template>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
{{ t('common.reset') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
<a-col :lg="8" :md="12" :xs="24">
<a-form-item
:label="t('views.dashboard.cdr.time')"
name="queryRangePicker"
>
<a-range-picker
v-model:value="queryRangePicker"
:presets="rangePickerPresets"
:bordered="true"
:allow-clear="false"
style="width: 100%"
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
></a-range-picker>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-card>
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-popconfirm
placement="bottomLeft"
:title="
!realTimeData
? t('views.dashboard.cdr.realTimeDataStart')
: t('views.dashboard.cdr.realTimeDataStop')
"
ok-text="Yes"
cancel-text="No"
@confirm="fnRealTime()"
>
<a-button type="primary" :danger="realTimeData">
<template #icon><FundOutlined /> </template>
{{
!realTimeData
? t('views.dashboard.cdr.realTimeDataStart')
: t('views.dashboard.cdr.realTimeDataStop')
}}
</a-button>
</a-popconfirm>
<a-button
type="default"
danger
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')"
v-perms:has="['cdr:ne:remove']"
>
<template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }}
</a-button>
<a-button type="dashed" @click.prevent="fnExportList()">
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
</a-space>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
v-model:checked="tableState.seached"
:checked-children="t('common.switch.show')"
:un-checked-children="t('common.switch.hide')"
size="small"
:disabled="realTimeData"
/>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
<template #icon><ReloadOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.sizeText') }}</template>
<a-dropdown trigger="click" placement="bottomRight">
<a-button type="text">
<template #icon><ColumnHeightOutlined /></template>
</a-button>
<template #overlay>
<a-menu
:selected-keys="[tableState.size as string]"
@click="fnTableSize"
>
<a-menu-item key="default">
{{ t('common.size.default') }}
</a-menu-item>
<a-menu-item key="middle">
{{ t('common.size.middle') }}
</a-menu-item>
<a-menu-item key="small">
{{ t('common.size.small') }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</template>
<!-- 表格列表 -->
<a-table
class="table"
row-key="id"
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: tableColumns.length * 150, y: 'calc(100vh - 480px)' }"
:row-selection="{
type: 'checkbox',
columnWidth: '48px',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'id'">
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.copyText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordCopy(record.cdrJSON)"
>
<template #icon>
<CopyOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.deleteText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordDelete(record.id)"
v-perms:has="['cdr:ne:remove']"
>
<template #icon>
<DeleteOutlined />
</template>
</a-button>
</a-tooltip>
</a-space>
</template>
</template>
<template #expandedRowRender="{ record }">
<a-row :gutter="16">
<a-col :lg="8" :md="12" :xs="24" :offset="2">
<a-divider orientation="left">
{{ t('views.dashboard.cdr.cdrInfo') }}
</a-divider>
<div>
<span>{{ t('views.ne.common.neName') }}: </span>
<span>{{ record.neName }}</span>
</div>
<div>
<span>{{ t('views.ne.common.rmUid') }}: </span>
<span>{{ record.rmUID }}</span>
</div>
<div>
<span>{{ t('views.dashboard.cdr.time') }}: </span>
<span>{{ record.cdrJSON.recordOpeningTime }}</span>
</div>
<a-divider orientation="left">
{{ t('views.dashboard.cdr.rowInfo') }}
</a-divider>
<div>
<span>Record Type: </span>
<span>{{ record.cdrJSON.recordType }}</span>
</div>
<div>
<span>Record Opening Time: </span>
<span>{{ record.cdrJSON.recordOpeningTime }}</span>
</div>
<div>
<span>Charging ID: </span>
<span>{{ record.cdrJSON.chargingID }}</span>
</div>
<div>
<span>Duration: </span>
<span>{{ record.cdrJSON.duration }}</span>
</div>
<div>
<span>Record Access Point Name NI: </span>
<span>{{ record.cdrJSON.accessPointNameNI }}</span>
</div>
<div>
<span>Record Cause For Rec Closing: </span>
<span>{{ record.cdrJSON.causeForRecClosing }}</span>
</div>
<div>
<span>Record Sequence Number: </span>
<span>{{ record.cdrJSON.recordSequenceNumber }}</span>
</div>
<div>
<span>Local Record Sequence Number: </span>
<span>{{ record.cdrJSON.localRecordSequenceNumber }}</span>
</div>
</a-col>
<a-col :lg="8" :md="12" :xs="24">
<a-divider orientation="left"> Server Information </a-divider>
<div>
<span>IMSI: </span>
<span> {{ record.cdrJSON.servedIMSI }} </span>
</div>
<div>
<span>MSISDN: </span>
<span> {{ record.cdrJSON.servedMSISDN }} </span>
</div>
<div v-if="record.cdrJSON.pGWAddressUsed">
<span>PGW Address: </span>
<span> {{ record.cdrJSON.pGWAddressUsed }} </span>
</div>
<div v-if="record.cdrJSON.GGSNAddress">
<span>GGSN Address: </span>
<span> {{ record.cdrJSON.GGSNAddress }} </span>
</div>
<div v-if="record.cdrJSON.sGWAddress">
<span>SGW Address: </span>
<span> {{ record.cdrJSON.sGWAddress }} </span>
</div>
<div v-if="record.cdrJSON.SGSNAddress">
<span>SGSN Address: </span>
<span> {{ record.cdrJSON.SGSNAddress }} </span>
</div>
<div>
<span>RAT Type: </span>
<span> {{ record.cdrJSON.rATType }} </span>
</div>
<a-divider orientation="left"> PDPPD Information </a-divider>
<div>
<span>PDPPDN Type: </span>
<span> {{ record.cdrJSON.pdpPDNType }} </span>
</div>
<div>
<span>PDPPDN Address: </span>
<span> {{ record.cdrJSON.servedPDPPDNAddress }} </span>
</div>
<div>
<span>Node Address: </span>
<span>
{{ record.cdrJSON.servingNodeAddress?.join(', ') }}
</span>
</div>
<div>
<span>Node Type: </span>
<span>
<template v-for="item in record.cdrJSON.servingNodeType">
{{ item.servingNodeType }}
</template>
</span>
</div>
</a-col>
</a-row>
</template>
</a-table>
</a-card>
</PageContainer>
</template>
<style lang="less" scoped>
.table :deep(.ant-pagination) {
padding: 0 24px;
}
</style>
+54 -125
View File
@@ -19,10 +19,6 @@ import {
import { OptionsType, WS } from '@/plugins/ws-websocket'; import { OptionsType, WS } from '@/plugins/ws-websocket';
import PQueue from 'p-queue'; import PQueue from 'p-queue';
import saveAs from 'file-saver'; import saveAs from 'file-saver';
import dayjs, { Dayjs } from 'dayjs';
import { useClipboard } from '@vueuse/core';
import { parseSizeFromByte } from '@/utils/parse-utils';
const { copy } = useClipboard({ legacy: true });
const { t } = useI18n(); const { t } = useI18n();
const ws = new WS(); const ws = new WS();
const queue = new PQueue({ concurrency: 1, autoStart: true }); const queue = new PQueue({ concurrency: 1, autoStart: true });
@@ -31,25 +27,7 @@ const queue = new PQueue({ concurrency: 1, autoStart: true });
let neOtions = ref<Record<string, any>[]>([]); let neOtions = ref<Record<string, any>[]>([]);
/**开始结束时间 */ /**开始结束时间 */
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([ let queryRangePicker = ref<[string, string]>(['', '']);
dayjs().startOf('hour'),
dayjs().endOf('hour'),
]);
/**时间范围 */
let rangePickerPresets = ref([
{
label: 'Now hour',
value: [dayjs().startOf('hour'), dayjs().endOf('hour')],
},
{ label: 'Today', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
{
label: 'Yesterday',
value: [
dayjs().subtract(1, 'day').startOf('day'),
dayjs().subtract(1, 'day').endOf('day'),
],
},
]);
/**查询参数 */ /**查询参数 */
let queryParams = reactive({ let queryParams = reactive({
@@ -60,9 +38,9 @@ let queryParams = reactive({
sortField: 'timestamp', sortField: 'timestamp',
sortOrder: 'desc', sortOrder: 'desc',
/**开始时间 */ /**开始时间 */
startTime: undefined as undefined | number, startTime: '',
/**结束时间 */ /**结束时间 */
endTime: undefined as undefined | number, endTime: '',
/**当前页数 */ /**当前页数 */
pageNum: 1, pageNum: 1,
/**每页条数 */ /**每页条数 */
@@ -73,10 +51,12 @@ let queryParams = reactive({
function fnQueryReset() { function fnQueryReset() {
queryParams = Object.assign(queryParams, { queryParams = Object.assign(queryParams, {
subscriberID: '', subscriberID: '',
startTime: '',
endTime: '',
pageNum: 1, pageNum: 1,
pageSize: 20, pageSize: 20,
}); });
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')]; queryRangePicker.value = ['', ''];
tablePagination.current = 1; tablePagination.current = 1;
tablePagination.pageSize = 20; tablePagination.pageSize = 20;
fnGetList(); fnGetList();
@@ -106,7 +86,7 @@ let tableState: TabeStateType = reactive({
}); });
/**表格字段列 */ /**表格字段列 */
let tableColumns = ref<ColumnsType>([ let tableColumns: ColumnsType = [
{ {
title: t('common.rowId'), title: t('common.rowId'),
dataIndex: 'id', dataIndex: 'id',
@@ -114,7 +94,7 @@ let tableColumns = ref<ColumnsType>([
width: 100, width: 100,
}, },
{ {
title: t('views.dashboard.cdr.chargingID'), // 计费ID title: t('views.dashboard.cdr.smfChargingID'), // 计费ID
dataIndex: 'cdrJSON', dataIndex: 'cdrJSON',
align: 'left', align: 'left',
width: 100, width: 100,
@@ -157,15 +137,11 @@ let tableColumns = ref<ColumnsType>([
) { ) {
return 0; return 0;
} }
let dataVolumeUplink = 0; const usedUnitContainer = listOfMultipleUnitUsage[0].usedUnitContainer;
for (const v of listOfMultipleUnitUsage) { if (!Array.isArray(usedUnitContainer) || usedUnitContainer.length < 1) {
if (Array.isArray(v.usedUnitContainer)) { return 0;
for (const used of v.usedUnitContainer) {
dataVolumeUplink += +used.dataVolumeUplink;
}
}
} }
return parseSizeFromByte(dataVolumeUplink, 'MB'); return usedUnitContainer[0].dataVolumeUplink;
}, },
}, },
{ {
@@ -182,15 +158,11 @@ let tableColumns = ref<ColumnsType>([
) { ) {
return 0; return 0;
} }
let dataVolumeDownlink = 0; const usedUnitContainer = listOfMultipleUnitUsage[0].usedUnitContainer;
for (const v of listOfMultipleUnitUsage) { if (!Array.isArray(usedUnitContainer) || usedUnitContainer.length < 1) {
if (Array.isArray(v.usedUnitContainer)) { return 0;
for (const used of v.usedUnitContainer) {
dataVolumeDownlink += +used.dataVolumeDownlink;
}
}
} }
return parseSizeFromByte(dataVolumeDownlink, 'MB'); return usedUnitContainer[0].dataVolumeDownlink;
}, },
}, },
{ {
@@ -207,19 +179,15 @@ let tableColumns = ref<ColumnsType>([
) { ) {
return 0; return 0;
} }
let dataTotalVolume = 0; const usedUnitContainer = listOfMultipleUnitUsage[0].usedUnitContainer;
for (const v of listOfMultipleUnitUsage) { if (!Array.isArray(usedUnitContainer) || usedUnitContainer.length < 1) {
if (Array.isArray(v.usedUnitContainer)) { return 0;
for (const used of v.usedUnitContainer) {
dataTotalVolume += +used.dataTotalVolume;
}
}
} }
return parseSizeFromByte(dataTotalVolume, 'MB'); return usedUnitContainer[0].dataTotalVolume;
}, },
}, },
{ {
title: t('views.dashboard.cdr.durationTime'), // 持续时间 title: t('views.dashboard.cdr.smfDuration'), // 持续时间
dataIndex: 'cdrJSON', dataIndex: 'cdrJSON',
align: 'left', align: 'left',
width: 100, width: 100,
@@ -229,7 +197,7 @@ let tableColumns = ref<ColumnsType>([
}, },
}, },
{ {
title: t('views.dashboard.cdr.invocationTime'), // 调用时间 title: t('views.dashboard.cdr.smfInvocationTime'), // 调用时间
dataIndex: 'cdrJSON', dataIndex: 'cdrJSON',
align: 'left', align: 'left',
width: 200, width: 200,
@@ -243,7 +211,7 @@ let tableColumns = ref<ColumnsType>([
key: 'id', key: 'id',
align: 'left', align: 'left',
}, },
]); ];
/**表格分页器参数 */ /**表格分页器参数 */
let tablePagination = reactive({ let tablePagination = reactive({
@@ -338,18 +306,6 @@ function fnRecordDelete(id: string) {
}); });
} }
/**
* 复制CDR
* @param jsonStr JSON字符串
*/
function fnRecordCopy(jsonStr: string) {
if (!jsonStr) return;
const text = JSON.stringify(jsonStr, null, 2);
copy(text).then(() => {
message.success(t('common.copyOk'), 3);
});
}
/**查询列表, pageNum初始页数 */ /**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) { function fnGetList(pageNum?: number) {
if (tableState.loading) return; if (tableState.loading) return;
@@ -357,19 +313,11 @@ function fnGetList(pageNum?: number) {
if (pageNum) { if (pageNum) {
queryParams.pageNum = pageNum; queryParams.pageNum = pageNum;
} }
if (!queryRangePicker.value) {
// 时间范围 queryRangePicker.value = ['', ''];
if (
Array.isArray(queryRangePicker.value) &&
queryRangePicker.value.length > 0
) {
queryParams.startTime = queryRangePicker.value[0].valueOf();
queryParams.endTime = queryRangePicker.value[1].valueOf();
} else {
queryParams.startTime = undefined;
queryParams.endTime = undefined;
} }
queryParams.startTime = queryRangePicker.value[0];
queryParams.endTime = queryRangePicker.value[1];
listSMFDataCDR(toRaw(queryParams)).then(res => { listSMFDataCDR(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
// 取消勾选 // 取消勾选
@@ -458,9 +406,7 @@ function fnRealTime() {
subGroupID: `1006_${queryParams.neId}`, subGroupID: `1006_${queryParams.neId}`,
}, },
onmessage: wsMessage, onmessage: wsMessage,
onerror: (ev: any) => { onerror: wsError,
console.error(ev);
},
}; };
ws.connect(options); ws.connect(options);
} else { } else {
@@ -470,6 +416,12 @@ function fnRealTime() {
} }
} }
/**接收数据后回调 */
function wsError(ev: any) {
// 接收数据后回调
console.error(ev);
}
/**接收数据后回调 */ /**接收数据后回调 */
function wsMessage(res: Record<string, any>) { function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res; const { code, requestId, data } = res;
@@ -558,7 +510,6 @@ onBeforeUnmount(() => {
v-model:value="queryParams.neId" v-model:value="queryParams.neId"
:options="neOtions" :options="neOtions"
:placeholder="t('common.selectPlease')" :placeholder="t('common.selectPlease')"
@change="fnQueryReset()"
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
@@ -582,12 +533,12 @@ onBeforeUnmount(() => {
> >
<a-range-picker <a-range-picker
v-model:value="queryRangePicker" v-model:value="queryRangePicker"
:presets="rangePickerPresets" allow-clear
:bordered="true" bordered
:allow-clear="false"
style="width: 100%"
:show-time="{ format: 'HH:mm:ss' }" :show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
value-format="x"
style="width: 100%"
></a-range-picker> ></a-range-picker>
</a-form-item> </a-form-item>
</a-col> </a-col>
@@ -640,7 +591,6 @@ onBeforeUnmount(() => {
:disabled="tableState.selectedRowKeys.length <= 0" :disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading" :loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')" @click.prevent="fnRecordDelete('0')"
v-perms:has="['cdr:ne:remove']"
> >
<template #icon><DeleteOutlined /></template> <template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }} {{ t('common.deleteText') }}
@@ -719,23 +669,11 @@ onBeforeUnmount(() => {
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'id'"> <template v-if="column.key === 'id'">
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.copyText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordCopy(record.cdrJSON)"
>
<template #icon>
<CopyOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.deleteText') }}</template> <template #title>{{ t('common.deleteText') }}</template>
<a-button <a-button
type="link" type="link"
@click.prevent="fnRecordDelete(record.id)" @click.prevent="fnRecordDelete(record.id)"
v-perms:has="['cdr:ne:remove']"
> >
<template #icon> <template #icon>
<DeleteOutlined /> <DeleteOutlined />
@@ -804,33 +742,24 @@ onBeforeUnmount(() => {
<a-divider orientation="left"> <a-divider orientation="left">
List Of Multiple Unit Usage List Of Multiple Unit Usage
</a-divider> </a-divider>
<div v-for="u in record.cdrJSON.listOfMultipleUnitUsage"> <div v-for="u in record.cdrJSON.listOfMultipleUnitUsage">
<!-- <div>RatingGroup: {{ u.ratingGroup }}</div> --> <div>RatingGroup: {{ u.ratingGroup }}</div>
<div <div v-for="udata in u.usedUnitContainer">
v-for="(udata, i) in u.usedUnitContainer"
style="display: flex"
>
<!-- <strong style="margin-right: 12px">
{{ i }}
</strong> -->
<div> <div>
<div> <span>Data Total Volume: </span>
<span>Data Total Volume: </span> <span>{{ udata.dataTotalVolume }}</span>
<span>{{ udata.dataTotalVolume }}</span> </div>
</div> <div>
<div> <span>Data Volume Downlink: </span>
<span>Data Volume Downlink: </span> <span>{{ udata.dataVolumeDownlink }}</span>
<span>{{ udata.dataVolumeDownlink }}</span> </div>
</div> <div>
<div> <span>Data Volume Uplink: </span>
<span>Data Volume Uplink: </span> <span>{{ udata.dataVolumeUplink }}</span>
<span>{{ udata.dataVolumeUplink }}</span> </div>
</div> <div>
<!-- <div> <span>Time: </span>
<span>Time: </span> <span>{{ udata.time }}</span>
<span>{{ udata.time }}</span>
</div> -->
</div> </div>
</div> </div>
</div> </div>
-699
View File
@@ -1,699 +0,0 @@
<script setup lang="ts">
import * as echarts from 'echarts/core';
import {
TitleComponent,
ToolboxComponent,
TooltipComponent,
GridComponent,
LegendComponent,
DataZoomComponent,
} from 'echarts/components';
import { LineChart } from 'echarts/charts';
import { UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
echarts.use([
TitleComponent,
ToolboxComponent,
TooltipComponent,
GridComponent,
LegendComponent,
DataZoomComponent,
LineChart,
CanvasRenderer,
UniversalTransition,
]);
import { reactive, onMounted, toRaw, onBeforeUnmount, ref } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { OptionsType, WS } from '@/plugins/ws-websocket';
import useI18n from '@/hooks/useI18n';
import { listSMFDataCDR } from '@/api/neData/smf';
import {
RESULT_CODE_ERROR,
RESULT_CODE_SUCCESS,
} from '@/constants/result-constants';
import { parseSizeFromByte } from '@/utils/parse-utils';
import { message } from 'ant-design-vue';
import useNeInfoStore from '@/store/modules/neinfo';
import dayjs, { Dayjs } from 'dayjs';
const { t, currentLocale } = useI18n();
const ws = new WS();
/**图DOM节点实例对象 */
const cdrChartDom = ref<HTMLElement | undefined>(undefined);
/**图实例对象 */
let cdrChart: echarts.ECharts | null = null;
/**图表配置 */
const option = {
title: {
text: 'Data Usage Report',
left: 'left',
},
tooltip: {
trigger: 'axis',
axisPointer: {
animation: true,
},
formatter: (params: any) => {
const title = params[0].name;
let uplinkValue = 0;
let downlinkValue = 0;
if (params[0].seriesName === 'Uplink') {
uplinkValue = params[0].value;
} else {
downlinkValue = params[0].value;
}
if (params[1].seriesName === 'Uplink') {
uplinkValue = params[1].value;
} else {
downlinkValue = params[1].value;
}
const uplinkValueF = parseSizeFromByte(uplinkValue, 'MB');
const downlinkValueF = parseSizeFromByte(downlinkValue, 'MB');
return `
<div style="font-weight: bold;">${title}</div>
<div>Downlink: ${downlinkValueF}</div>
<div>Uplink: ${uplinkValueF}</div>
`;
},
},
toolbox: {
feature: {
dataZoom: {
yAxisIndex: 'none',
},
saveAsImage: {},
},
},
axisPointer: {
link: [
{
xAxisIndex: 'all',
},
],
},
dataZoom: [
{
show: false,
realtime: true,
start: 0,
end: 100,
xAxisIndex: [0, 1],
},
{
type: 'inside',
realtime: true,
start: 0,
end: 100,
xAxisIndex: [0, 1],
},
],
grid: [
{
left: '10%',
right: 50,
height: '40%',
},
{
left: '10%',
right: 50,
top: '50%',
height: '40%',
},
],
xAxis: [
{
type: 'category',
boundaryGap: false,
axisLine: { onZero: true },
data: [], // x轴初始数据
axisLabel: {
show: false, // 显示标签
rotate: 15, // 设置倾斜角度(如15度)
},
},
{
gridIndex: 1,
type: 'category',
boundaryGap: false,
axisLine: { onZero: true },
data: [], // x轴初始数据
axisLabel: {
show: false, // 隐藏第二个 x 轴的标签
},
position: 'top',
},
],
yAxis: [
{
name: 'Downlink (Byte)',
type: 'value',
},
{
gridIndex: 1,
name: 'Uplink (Byte)',
type: 'value',
inverse: true,
},
],
series: [
{
name: 'Downlink',
type: 'line',
data: [], // y轴初始数据
symbol: 'circle', // 数据点形状
symbolSize: 6, // 数据点大小
smooth: true, // 平滑曲线
color: 'rgb(0, 190, 99)',
areaStyle: {
color: {
colorStops: [
{ offset: 0, color: 'rgba(0, 190, 99, .5)' },
{ offset: 1, color: 'rgba(0, 190, 99, 0.5)' },
],
x: 0,
y: 0,
x2: 0,
y2: 1,
type: 'linear',
global: false,
},
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 10,
},
},
{
name: 'Uplink',
type: 'line',
xAxisIndex: 1,
yAxisIndex: 1,
data: [], // y轴初始数据
symbol: 'circle', // 数据点形状
symbolSize: 6, // 数据点大小
smooth: true, // 平滑曲线
color: 'rgb(17, 178, 255)',
areaStyle: {
color: {
colorStops: [
{ offset: 0, color: 'rgba(17, 178, 255, .5)' },
{ offset: 1, color: 'rgba(17, 178, 255, 0.5)' },
],
x: 0,
y: 0,
x2: 0,
y2: 1,
type: 'linear',
global: false,
},
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 10,
},
},
],
};
/**绘制图表 */
function fnRanderChart() {
const container: HTMLElement | undefined = cdrChartDom.value;
if (!container) return;
const locale = currentLocale.value.split('_')[0];
cdrChart = echarts.init(container, 'light', {
// https://github.com/apache/echarts/tree/release/src/i18n 取值langEN.ts ==> EN
locale: locale.toUpperCase(),
});
cdrChart.setOption(option);
// cdrChart.showLoading('default', {
// text: 'Please enter IMSI to query user traffic',
// fontSize: 16, // 字体大小
// });
// 创建 ResizeObserver 实例 监听图表容器大小变化,并在变化时调整图表大小
var observer = new ResizeObserver(entries => {
if (cdrChart && !cdrChart.isDisposed) {
cdrChart.resize();
}
});
// 监听元素大小变化
observer.observe(container);
}
/**网元可选 */
let neOtions = ref<Record<string, any>[]>([]);
/**开始结束时间 */
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([
dayjs().startOf('hour'),
dayjs().endOf('hour'),
]);
/**时间范围 */
let rangePickerPresets = ref([
{
label: 'Now hour',
value: [dayjs().startOf('hour'), dayjs().endOf('hour')],
},
{ label: 'Today', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
{
label: 'Yesterday',
value: [
dayjs().subtract(1, 'day').startOf('day'),
dayjs().subtract(1, 'day').endOf('day'),
],
},
]);
/**查询参数 */
let queryParams = reactive({
/**网元类型 */
neType: 'SMF',
neId: '001',
subscriberID: '',
dnn: '',
sortField: 'timestamp',
sortOrder: 'desc',
/**开始时间 */
startTime: undefined as undefined | number,
/**结束时间 */
endTime: undefined as undefined | number,
/**当前页数 */
pageNum: 1,
/**每页条数 */
pageSize: 1000,
});
/**查询参数重置 */
function fnQueryReset() {
queryParams.subscriberID = '';
queryParams.dnn = '';
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')];
fnGetList(1);
}
let state = reactive({
/**表格数据 */
data: [] as any[],
/**表格总数 */
total: 0,
/**表格加载状态 */
loading: false,
/**数据总量 up,down */
dataUsage: ['0 B', '0 B'],
});
/**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (state.loading) return;
state.loading = true;
if (!queryParams.subscriberID) {
message.warning('Please enter IMSI to query user traffic');
state.loading = false;
return;
}
if (pageNum) {
queryParams.pageNum = pageNum;
}
if (cdrChart) {
cdrChart.showLoading('default', {
text: 'Loading...',
fontSize: 16, // 字体大小
});
}
// 时间范围
if (
Array.isArray(queryRangePicker.value) &&
queryRangePicker.value.length > 0
) {
queryParams.startTime = queryRangePicker.value[0].valueOf();
queryParams.endTime = queryRangePicker.value[1].valueOf();
} else {
queryParams.startTime = undefined;
queryParams.endTime = undefined;
}
listSMFDataCDR(toRaw(queryParams))
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
state.total = res.total;
// 遍历处理cdr字符串数据
state.data = res.rows
.map(item => {
let cdrJSON = item.cdrJSON;
if (!cdrJSON) {
Reflect.set(item, 'cdrJSON', {});
}
try {
cdrJSON = JSON.parse(cdrJSON);
Reflect.set(item, 'cdrJSON', cdrJSON);
} catch (error) {
console.error(error);
Reflect.set(item, 'cdrJSON', {});
}
return item;
})
.reverse();
}
})
.finally(() => {
state.loading = false;
fnRanderChartDataLoad();
});
}
/**图表配置数据x轴 */
let dataTimeXAxisData: string[] = [];
/**图表配置数据y轴 */
let dataVolumeUplinkYSeriesData: number[] = [];
let dataVolumeDownlinkYSeriesData: number[] = [];
/**图表数据渲染 */
function fnRanderChartDataLoad() {
if (!cdrChart) return;
dataTimeXAxisData = [];
dataVolumeUplinkYSeriesData = [];
dataVolumeDownlinkYSeriesData = [];
if (state.data.length > 0) {
// 处理数据渲染图表
for (const item of state.data) {
if (!item.cdrJSON.invocationTimestamp) {
break;
}
// 时间
const dataTime = item.cdrJSON.invocationTimestamp;
const listOfMultipleUnitUsage = item.cdrJSON.listOfMultipleUnitUsage;
if (
!Array.isArray(listOfMultipleUnitUsage) ||
listOfMultipleUnitUsage.length < 1
) {
return 0;
}
// 数据
let dataVolumeUplink = 0;
let dataVolumeDownlink = 0;
for (const v of listOfMultipleUnitUsage) {
if (Array.isArray(v.usedUnitContainer)) {
for (const used of v.usedUnitContainer) {
dataVolumeUplink += +used.dataVolumeUplink;
dataVolumeDownlink += +used.dataVolumeDownlink;
}
}
}
dataTimeXAxisData.push(dataTime);
dataVolumeUplinkYSeriesData.push(dataVolumeUplink);
dataVolumeDownlinkYSeriesData.push(dataVolumeDownlink);
}
// 绘制图数据
fnRanderChartDataUpdate();
} else {
message.warning('No Data');
cdrChart.hideLoading();
cdrChart.setOption({
title: {
text: `Data Volume Uplink / Downlink By IMSI ${queryParams.subscriberID}`,
},
xAxis: [
{
data: dataTimeXAxisData,
},
{
data: dataTimeXAxisData,
},
],
series: [
{
data: dataVolumeUplinkYSeriesData,
},
{
data: dataVolumeDownlinkYSeriesData,
},
],
});
}
}
/**图表数据渲染 */
function fnRanderChartDataUpdate() {
if (cdrChart == null) return;
// 绘制图数据
cdrChart.setOption({
title: {
text: `Data Usage Report of IMSI ${queryParams.subscriberID}`,
},
xAxis: [
{
data: dataTimeXAxisData,
},
{
data: dataTimeXAxisData,
},
],
series: [
{
data: dataVolumeUplinkYSeriesData,
},
{
data: dataVolumeDownlinkYSeriesData,
},
],
});
cdrChart.hideLoading();
// 累加总量
let uplinkTotal = 0;
let downlinkTotal = 0;
for (let index = 0; index < dataVolumeUplinkYSeriesData.length; index++) {
uplinkTotal += dataVolumeUplinkYSeriesData[index];
downlinkTotal += dataVolumeDownlinkYSeriesData[index];
}
state.dataUsage = [
parseSizeFromByte(uplinkTotal, 'MB'),
parseSizeFromByte(downlinkTotal, 'MB'),
];
}
/**
* 实时数据
*/
function fnRealTime() {
if (ws.state() === WebSocket.OPEN) {
ws.close();
}
// 建立链接
const options: OptionsType = {
url: '/ws',
params: {
/**订阅通道组
*
* CDR会话事件-SMF (GroupID:1006)
*/
subGroupID: `1006_${queryParams.neId}`,
},
onmessage: (res: Record<string, any>) => {
const { code, requestId, data } = res;
if (code === RESULT_CODE_ERROR) {
console.warn(res.msg);
return;
}
// 订阅组信息
if (!data?.groupId) {
return;
}
// cdrEvent CDR会话事件
if (data.groupId === `1006_${queryParams.neId}`) {
const cdrEvent = data.data;
// 对应结束时间内
if (queryParams.endTime) {
const endTime = Math.round(queryParams.endTime / 1000);
if (cdrEvent.timestamp > endTime) {
return;
}
}
const cdrJSON = cdrEvent.CDR;
if (!cdrJSON.invocationTimestamp) {
return;
}
// 对应IMSI
if (
cdrJSON.subscriberIdentifier.subscriptionIDData !==
queryParams.subscriberID
) {
return;
}
// 时间
const dataTime = cdrJSON.invocationTimestamp;
const listOfMultipleUnitUsage = cdrJSON.listOfMultipleUnitUsage;
if (
!Array.isArray(listOfMultipleUnitUsage) ||
listOfMultipleUnitUsage.length < 1
) {
return 0;
}
// 数据
let dataVolumeUplink = 0;
let dataVolumeDownlink = 0;
for (const v of listOfMultipleUnitUsage) {
if (Array.isArray(v.usedUnitContainer)) {
for (const used of v.usedUnitContainer) {
dataVolumeUplink += +used.dataVolumeUplink;
dataVolumeDownlink += +used.dataVolumeDownlink;
}
}
}
// 添加数据
dataTimeXAxisData.push(dataTime);
dataVolumeUplinkYSeriesData.push(dataVolumeUplink);
dataVolumeDownlinkYSeriesData.push(dataVolumeDownlink);
fnRanderChartDataUpdate();
}
},
onerror: (ev: any) => {
console.error(ev);
},
};
ws.connect(options);
}
onMounted(() => {
// 获取网元网元列表
useNeInfoStore()
.fnNelist()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
let arr: Record<string, any>[] = [];
res.data.forEach((v: any) => {
if (v.neType === 'SMF') {
arr.push({ value: v.neId, label: v.neName });
}
});
neOtions.value = arr;
if (arr.length > 0) {
queryParams.neId = arr[0].value;
}
}
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
})
.finally(() => {
fnRanderChart();
fnRealTime();
});
});
onBeforeUnmount(() => {
ws.close();
if (cdrChart) {
cdrChart.clear();
cdrChart.dispose();
}
});
</script>
<template>
<PageContainer>
<a-card
:bordered="false"
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
>
<!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item label="SMF" name="neId ">
<a-select
v-model:value="queryParams.neId"
:options="neOtions"
:placeholder="t('common.selectPlease')"
@change="fnRealTime()"
:disabled="state.loading"
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item label="IMSI" name="subscriberID" :required="true">
<a-input
v-model:value="queryParams.subscriberID"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="40"
:disabled="state.loading"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item label="DNN" name="dnn">
<a-input
v-model:value="queryParams.dnn"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="40"
:disabled="state.loading"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="4" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button
type="primary"
@click.prevent="fnGetList(1)"
:loading="state.loading"
>
<template #icon><SearchOutlined /></template>
{{ t('common.search') }}
</a-button>
<a-button
type="default"
@click.prevent="fnQueryReset"
:disabled="state.loading"
>
<template #icon><ClearOutlined /></template>
{{ t('common.reset') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
<a-col :lg="8" :md="12" :xs="24">
<a-form-item
:label="t('views.dashboard.cdr.time')"
name="queryRangePicker"
>
<a-range-picker
v-model:value="queryRangePicker"
:presets="rangePickerPresets"
:bordered="true"
:allow-clear="false"
style="width: 100%"
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
:disabled="state.loading"
></a-range-picker>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-card>
<a-card :bordered="false">
<!-- 图数据 -->
<div ref="cdrChartDom" style="height: 600px; width: 100%"></div>
<a-descriptions title="Data Usage" bordered :column="2">
<a-descriptions-item label="Total Uplink">
{{ state.dataUsage[0] }}
</a-descriptions-item>
<a-descriptions-item label="Total Downlink">
{{ state.dataUsage[1] }}
</a-descriptions-item>
</a-descriptions>
</a-card>
</PageContainer>
</template>
<style lang="less" scoped></style>
+77 -150
View File
@@ -21,10 +21,6 @@ import { parseDateToStr } from '@/utils/date-utils';
import { OptionsType, WS } from '@/plugins/ws-websocket'; import { OptionsType, WS } from '@/plugins/ws-websocket';
import saveAs from 'file-saver'; import saveAs from 'file-saver';
import PQueue from 'p-queue'; import PQueue from 'p-queue';
import { useClipboard } from '@vueuse/core';
import { hasPermissions } from '@/plugins/auth-user';
import dayjs, { type Dayjs } from 'dayjs';
const { copy } = useClipboard({ legacy: true });
const { getDict } = useDictStore(); const { getDict } = useDictStore();
const { t } = useI18n(); const { t } = useI18n();
const ws = new WS(); const ws = new WS();
@@ -42,25 +38,7 @@ let dict: {
let neOtions = ref<Record<string, any>[]>([]); let neOtions = ref<Record<string, any>[]>([]);
/**开始结束时间 */ /**开始结束时间 */
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([ let queryRangePicker = ref<[string, string]>(['', '']);
dayjs().startOf('hour'),
dayjs().endOf('hour'),
]);
/**时间范围 */
let rangePickerPresets = ref([
{
label: 'Now hour',
value: [dayjs().startOf('hour'), dayjs().endOf('hour')],
},
{ label: 'Today', value: [dayjs().startOf('day'), dayjs().endOf('day')] },
{
label: 'Yesterday',
value: [
dayjs().subtract(1, 'day').startOf('day'),
dayjs().subtract(1, 'day').endOf('day'),
],
},
]);
/**查询参数 */ /**查询参数 */
let queryParams = reactive({ let queryParams = reactive({
@@ -73,9 +51,9 @@ let queryParams = reactive({
sortField: 'timestamp', sortField: 'timestamp',
sortOrder: 'desc', sortOrder: 'desc',
/**开始时间 */ /**开始时间 */
startTime: undefined as undefined | number, startTime: '',
/**结束时间 */ /**结束时间 */
endTime: undefined as undefined | number, endTime: '',
/**当前页数 */ /**当前页数 */
pageNum: 1, pageNum: 1,
/**每页条数 */ /**每页条数 */
@@ -89,12 +67,12 @@ function fnQueryReset() {
recordType: '', recordType: '',
callerParty: '', callerParty: '',
calledParty: '', calledParty: '',
startTime: undefined, startTime: '',
endTime: undefined, endTime: '',
pageNum: 1, pageNum: 1,
pageSize: 20, pageSize: 20,
}); });
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')]; queryRangePicker.value = ['', ''];
tablePagination.current = 1; tablePagination.current = 1;
tablePagination.pageSize = 20; tablePagination.pageSize = 20;
fnGetList(); fnGetList();
@@ -166,7 +144,7 @@ let tableColumns: ColumnsType = [
dataIndex: 'cdrJSON', dataIndex: 'cdrJSON',
key: 'callerParty', key: 'callerParty',
align: 'left', align: 'left',
width: 150, width: 120,
customRender(opt) { customRender(opt) {
const cdrJSON = opt.value; const cdrJSON = opt.value;
return cdrJSON.callerParty; return cdrJSON.callerParty;
@@ -177,7 +155,7 @@ let tableColumns: ColumnsType = [
dataIndex: 'cdrJSON', dataIndex: 'cdrJSON',
key: 'calledParty', key: 'calledParty',
align: 'left', align: 'left',
width: 150, width: 120,
customRender(opt) { customRender(opt) {
const cdrJSON = opt.value; const cdrJSON = opt.value;
return cdrJSON.calledParty; return cdrJSON.calledParty;
@@ -195,7 +173,7 @@ let tableColumns: ColumnsType = [
title: t('views.dashboard.cdr.time'), title: t('views.dashboard.cdr.time'),
dataIndex: 'cdrJSON', dataIndex: 'cdrJSON',
align: 'left', align: 'left',
width: 200, width: 150,
customRender(opt) { customRender(opt) {
const cdrJSON = opt.value; const cdrJSON = opt.value;
if (typeof cdrJSON.updateTime === 'number') { if (typeof cdrJSON.updateTime === 'number') {
@@ -304,18 +282,6 @@ function fnRecordDelete(id: string) {
}); });
} }
/**
* 复制CDR
* @param jsonStr JSON字符串
*/
function fnRecordCopy(jsonStr: string) {
if (!jsonStr) return;
const text = JSON.stringify(jsonStr, null, 2);
copy(text).then(() => {
message.success(t('common.copyOk'), 3);
});
}
/**查询列表, pageNum初始页数 */ /**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) { function fnGetList(pageNum?: number) {
if (tableState.loading) return; if (tableState.loading) return;
@@ -323,19 +289,11 @@ function fnGetList(pageNum?: number) {
if (pageNum) { if (pageNum) {
queryParams.pageNum = pageNum; queryParams.pageNum = pageNum;
} }
if (!queryRangePicker.value) {
// 时间范围 queryRangePicker.value = ['', ''];
if (
Array.isArray(queryRangePicker.value) &&
queryRangePicker.value.length > 0
) {
queryParams.startTime = queryRangePicker.value[0].valueOf();
queryParams.endTime = queryRangePicker.value[1].valueOf();
} else {
queryParams.startTime = undefined;
queryParams.endTime = undefined;
} }
queryParams.startTime = queryRangePicker.value[0];
queryParams.endTime = queryRangePicker.value[1];
listSMSCDataCDR(toRaw(queryParams)).then(res => { listSMSCDataCDR(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
// 取消勾选 // 取消勾选
@@ -424,9 +382,7 @@ function fnRealTime() {
subGroupID: `1007_${queryParams.neId}`, subGroupID: `1007_${queryParams.neId}`,
}, },
onmessage: wsMessage, onmessage: wsMessage,
onerror: (ev: any) => { onerror: wsError,
console.error(ev);
},
}; };
ws.connect(options); ws.connect(options);
} else { } else {
@@ -436,6 +392,12 @@ function fnRealTime() {
} }
} }
/**接收数据后回调 */
function wsError(ev: any) {
// 接收数据后回调
console.error(ev);
}
/**接收数据后回调 */ /**接收数据后回调 */
function wsMessage(res: Record<string, any>) { function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res; const { code, requestId, data } = res;
@@ -530,7 +492,6 @@ onBeforeUnmount(() => {
v-model:value="queryParams.neId" v-model:value="queryParams.neId"
:options="neOtions" :options="neOtions"
:placeholder="t('common.selectPlease')" :placeholder="t('common.selectPlease')"
@change="fnQueryReset()"
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
@@ -593,12 +554,12 @@ onBeforeUnmount(() => {
> >
<a-range-picker <a-range-picker
v-model:value="queryRangePicker" v-model:value="queryRangePicker"
:presets="rangePickerPresets" allow-clear
:bordered="true" bordered
:allow-clear="false"
style="width: 100%"
:show-time="{ format: 'HH:mm:ss' }" :show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
value-format="x"
style="width: 100%"
></a-range-picker> ></a-range-picker>
</a-form-item> </a-form-item>
</a-col> </a-col>
@@ -637,7 +598,6 @@ onBeforeUnmount(() => {
:disabled="tableState.selectedRowKeys.length <= 0" :disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading" :loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')" @click.prevent="fnRecordDelete('0')"
v-perms:has="['cdr:ne:remove']"
> >
<template #icon><DeleteOutlined /></template> <template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }} {{ t('common.deleteText') }}
@@ -705,7 +665,7 @@ onBeforeUnmount(() => {
:data-source="tableState.data" :data-source="tableState.data"
:size="tableState.size" :size="tableState.size"
:pagination="tablePagination" :pagination="tablePagination"
:scroll="{ x: tableColumns.length * 180, y: 'calc(100vh - 480px)' }" :scroll="{ x: tableColumns.length * 150, y: 'calc(100vh - 480px)' }"
:row-selection="{ :row-selection="{
type: 'checkbox', type: 'checkbox',
columnWidth: '48px', columnWidth: '48px',
@@ -729,23 +689,11 @@ onBeforeUnmount(() => {
</template> </template>
<template v-if="column.key === 'id'"> <template v-if="column.key === 'id'">
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.copyText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordCopy(record.cdrJSON)"
>
<template #icon>
<CopyOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.deleteText') }}</template> <template #title>{{ t('common.deleteText') }}</template>
<a-button <a-button
type="link" type="link"
@click.prevent="fnRecordDelete(record.id)" @click.prevent="fnRecordDelete(record.id)"
v-perms:has="['cdr:ne:remove']"
> >
<template #icon> <template #icon>
<DeleteOutlined /> <DeleteOutlined />
@@ -756,79 +704,58 @@ onBeforeUnmount(() => {
</template> </template>
</template> </template>
<template #expandedRowRender="{ record }"> <template #expandedRowRender="{ record }">
<a-row :gutter="16"> <div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
<a-col :lg="8" :md="12" :xs="22" :offset="2"> <a-divider orientation="left">
<a-divider orientation="left"> {{ t('views.dashboard.cdr.cdrInfo') }}
{{ t('views.dashboard.cdr.cdrInfo') }} </a-divider>
</a-divider> <div>
<div> <span>{{ t('views.ne.common.neName') }}: </span>
<span>{{ t('views.ne.common.neName') }}: </span> <span>{{ record.neName }}</span>
<span>{{ record.neName }}</span> </div>
</div> <div>
<div> <span>{{ t('views.ne.common.rmUid') }}: </span>
<span>{{ t('views.ne.common.rmUid') }}: </span> <span>{{ record.rmUID }}</span>
<span>{{ record.rmUID }}</span> </div>
</div> <div>
<div> <span>{{ t('views.dashboard.cdr.time') }}: </span>
<span>{{ t('views.dashboard.cdr.time') }}: </span> <span>
<span> {{
{{ typeof record.cdrJSON.updateTime === 'number'
typeof record.cdrJSON.updateTime === 'number' ? parseDateToStr(+record.cdrJSON.updateTime * 1000)
? parseDateToStr(+record.cdrJSON.updateTime * 1000) : record.cdrJSON.updateTime
: record.cdrJSON.updateTime }}
}} </span>
</span> </div>
</div> <a-divider orientation="left">
</a-col> {{ t('views.dashboard.cdr.rowInfo') }}
<a-col :lg="8" :md="12" :xs="24"> </a-divider>
<a-divider orientation="left"> <div>
{{ t('views.dashboard.cdr.rowInfo') }} <span>{{ t('views.dashboard.cdr.type') }}: </span>
</a-divider> <span>{{ record.cdrJSON.serviceType }}</span>
<div> </div>
<span>{{ t('views.dashboard.cdr.type') }}: </span> <div>
<span>{{ record.cdrJSON.serviceType }}</span> <span>{{ t('views.dashboard.cdr.caller') }}: </span>
</div> <span>{{ record.cdrJSON.callerParty }}</span>
<div> </div>
<span>{{ t('views.dashboard.cdr.caller') }}: </span> <div>
<span>{{ record.cdrJSON.callerParty }}</span> <span>{{ t('views.dashboard.cdr.called') }}: </span>
</div> <span>{{ record.cdrJSON.calledParty }}</span>
<div> </div>
<span>{{ t('views.dashboard.cdr.called') }}: </span> <div>
<span>{{ record.cdrJSON.calledParty }}</span> <span>{{ t('views.dashboard.cdr.result') }}: </span>
</div> <span v-if="record.cdrJSON.result === 0">
<div> {{ t('views.dashboard.cdr.resultFail') }},
<span>{{ t('views.dashboard.cdr.result') }}: </span> <DictTag
<span v-if="record.cdrJSON.result === 0"> :options="dict.cdrCauseCode"
{{ t('views.dashboard.cdr.resultFail') }}, :value="record.cdrJSON.cause"
<DictTag value-default="0"
:options="dict.cdrCauseCode" />
:value="record.cdrJSON.cause" </span>
value-default="0" <span v-else>
/> {{ t('views.dashboard.cdr.resultOk') }}
</span> </span>
<span v-else> </div>
{{ t('views.dashboard.cdr.resultOk') }} </div>
</span>
</div>
</a-col>
<a-col
:lg="16"
:md="16"
:xs="22"
:offset="2"
v-if="
record.cdrJSON?.smsContent &&
hasPermissions(['cdr:smsc:content'])
"
>
<a-divider orientation="left"> Content </a-divider>
<a-typography-paragraph
copyable
:content="record.cdrJSON.smsContent"
>
</a-typography-paragraph>
</a-col>
</a-row>
</template> </template>
</a-table> </a-table>
</a-card> </a-card>
+53 -23
View File
@@ -145,54 +145,66 @@ let alarmTableState: TabeStateType = reactive({
let tableColumns: ColumnsType = [ let tableColumns: ColumnsType = [
{ {
title: t('views.faultManage.activeAlarm.origLevel'), title: t('views.faultManage.activeAlarm.origLevel'),
align: 'left', align: 'center',
key: 'origSeverity', key: 'origSeverity',
dataIndex: 'origSeverity', dataIndex: 'origSeverity',
width: 100, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.alarmTitle'), title: t('views.faultManage.activeAlarm.alarmTitle'),
dataIndex: 'alarmTitle', dataIndex: 'alarmTitle',
align: 'left', align: 'left',
width: 200, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.neType'), title: t('views.faultManage.activeAlarm.neType'),
dataIndex: 'neType', dataIndex: 'neType',
align: 'left', align: 'center',
width: 100, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.eventTime'), title: t('views.faultManage.activeAlarm.eventTime'),
dataIndex: 'eventTime', dataIndex: 'eventTime',
align: 'left', align: 'center',
sorter: (a: any, b: any) => 1, sorter: (a: any, b: any) => 1,
width: 150, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.alarmCode'), title: t('views.faultManage.activeAlarm.alarmCode'),
dataIndex: 'alarmCode', dataIndex: 'alarmCode',
align: 'left', align: 'center',
width: 100, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.alarmType'), title: t('views.faultManage.activeAlarm.alarmType'),
dataIndex: 'alarmType', dataIndex: 'alarmType',
key: 'alarmType', key: 'alarmType',
align: 'left', align: 'left',
width: 120, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.neName'), title: t('views.faultManage.activeAlarm.neName'),
dataIndex: 'neName', dataIndex: 'neName',
align: 'left', align: 'center',
width: 100, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.neId'), title: t('views.faultManage.activeAlarm.neId'),
dataIndex: 'neId', dataIndex: 'neId',
align: 'left', align: 'center',
width: 100, width: 5,
},
{
title: t('views.faultManage.activeAlarm.pvFlag'),
dataIndex: 'pvFlag',
align: 'center',
width: 5,
},
{
title: t('views.faultManage.activeAlarm.alarmId'),
dataIndex: 'alarmId',
align: 'center',
width: 5,
}, },
{ {
@@ -200,20 +212,20 @@ let tableColumns: ColumnsType = [
dataIndex: 'ackState', dataIndex: 'ackState',
key: 'ackState', key: 'ackState',
align: 'left', align: 'left',
width: 100, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.ackUser'), title: t('views.faultManage.activeAlarm.ackUser'),
dataIndex: 'ackUser', dataIndex: 'ackUser',
align: 'left', align: 'left',
width: 100, width: 5,
}, },
{ {
title: t('common.operate'), title: t('common.operate'),
key: 'alarm_id', key: 'alarm_id',
align: 'left', align: 'center',
fixed: 'right', fixed: 'right',
width: 100, width: 5,
}, },
]; ];
@@ -1010,12 +1022,12 @@ onMounted(() => {
:data-source="tableState.data" :data-source="tableState.data"
:size="tableState.size" :size="tableState.size"
:row-selection="{ :row-selection="{
columnWidth: 48, columnWidth: 2,
selectedRowKeys: state.selectedRowKeys, selectedRowKeys: state.selectedRowKeys,
onChange: onSelectChange, onChange: onSelectChange,
}" }"
:pagination="tablePagination" :pagination="tablePagination"
:scroll="{ x: tableColumns.length * 150, y: 400 }" :scroll="{ x: 2500, y: 400 }"
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'origSeverity'"> <template v-if="column.key === 'origSeverity'">
@@ -1200,7 +1212,15 @@ onMounted(() => {
> >
{{ modalState.from.alarmType }} {{ modalState.from.alarmType }}
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.faultManage.activeAlarm.pvFlag')"
name="pvFlag"
>
{{ modalState.from.pvFlag }}
</a-form-item>
</a-col>
</a-row> </a-row>
<a-form-item <a-form-item
@@ -1210,8 +1230,18 @@ onMounted(() => {
> >
{{ modalState.from.locationInfo }} {{ modalState.from.locationInfo }}
</a-form-item> </a-form-item>
<a-row> </a-row>
<a-row> <a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.faultManage.activeAlarm.province')"
name="province"
>
{{ modalState.from.province }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
:label="t('views.faultManage.activeAlarm.origLevel')" :label="t('views.faultManage.activeAlarm.origLevel')"
@@ -1260,7 +1290,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.ackState')" :label="t('views.faultManage.activeAlarm.ackState')"
name="ackState" name="ackState"
> >
<DictTag :options="dict.activeAckState" :value="modalState.from.ackState" /> {{ modalState.from.ackState }}
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
+28 -14
View File
@@ -118,47 +118,61 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */ /**表格字段列 */
let tableColumns: ColumnsType = [ let tableColumns: ColumnsType = [
{
title: t('views.faultManage.activeAlarm.alarmId'),
dataIndex: 'alarmId',
align: 'center',
width: 5,
},
{ {
title: t('views.faultManage.activeAlarm.neId'), title: t('views.faultManage.activeAlarm.neId'),
dataIndex: 'neId', dataIndex: 'neId',
align: 'left', align: 'center',
width: 150, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.neName'), title: t('views.faultManage.activeAlarm.neName'),
dataIndex: 'neName', dataIndex: 'neName',
align: 'left', align: 'center',
width: 150, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.neType'), title: t('views.faultManage.activeAlarm.neType'),
dataIndex: 'neType', dataIndex: 'neType',
align: 'left', align: 'center',
width: 150, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.alarmCode'), title: t('views.faultManage.activeAlarm.alarmCode'),
dataIndex: 'alarmCode', dataIndex: 'alarmCode',
align: 'left', align: 'center',
width: 100, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.alarmTitle'), title: t('views.faultManage.activeAlarm.alarmTitle'),
dataIndex: 'alarmTitle', dataIndex: 'alarmTitle',
align: 'left', align: 'left',
width: 200, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.eventTime'), title: t('views.faultManage.activeAlarm.eventTime'),
dataIndex: 'eventTime', dataIndex: 'eventTime',
align: 'center',
sorter: (a: any, b: any) => 1, sorter: (a: any, b: any) => 1,
align: 'left', width: 5,
width: 200, },
{
title: t('views.faultManage.activeAlarm.pvFlag'),
dataIndex: 'pvFlag',
align: 'center',
width: 5,
}, },
{ {
title: t('common.operate'), title: t('common.operate'),
key: 'alarm_id', key: 'alarm_id',
align: 'left', align: 'center',
fixed: 'right',
width: 5,
}, },
]; ];
@@ -302,7 +316,7 @@ function mapKeysWithReduce(data: any, titleMapping: any) {
const title = titleMapping[key] || key; const title = titleMapping[key] || key;
newItem[title] = item[key]; newItem[title] = item[key];
return newItem; return newItem;
}, {}); });
}); });
} }
@@ -612,7 +626,7 @@ onMounted(() => {
</a-dropdown> </a-dropdown>
</a-tooltip> </a-tooltip>
<TableColumnsDnd <TableColumnsDnd
cache-id="alarmEvent" cache-id="alarmActive"
:columns="tableColumns" :columns="tableColumns"
v-model:columns-dnd="tableColumnsDnd" v-model:columns-dnd="tableColumnsDnd"
></TableColumnsDnd> ></TableColumnsDnd>
+70 -33
View File
@@ -122,96 +122,108 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */ /**表格字段列 */
let tableColumns: ColumnsType = [ let tableColumns: ColumnsType = [
{
title: t('views.faultManage.activeAlarm.alarmId'),
dataIndex: 'alarmId',
align: 'center',
width: 5,
},
{ {
title: t('views.faultManage.activeAlarm.neId'), title: t('views.faultManage.activeAlarm.neId'),
dataIndex: 'neId', dataIndex: 'neId',
align: 'left', align: 'center',
width: 100, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.neName'), title: t('views.faultManage.activeAlarm.neName'),
dataIndex: 'neName', dataIndex: 'neName',
align: 'left', align: 'center',
width: 100, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.neType'), title: t('views.faultManage.activeAlarm.neType'),
dataIndex: 'neType', dataIndex: 'neType',
align: 'left', align: 'center',
width: 100, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.origLevel'), title: t('views.faultManage.activeAlarm.origLevel'),
align: 'left', align: 'left',
dataIndex: 'origSeverity', dataIndex: 'origSeverity',
key: 'origSeverity', key: 'origSeverity',
width: 100, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.alarmCode'), title: t('views.faultManage.activeAlarm.alarmCode'),
dataIndex: 'alarmCode', dataIndex: 'alarmCode',
align: 'left', align: 'center',
width: 100, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.alarmTitle'), title: t('views.faultManage.activeAlarm.alarmTitle'),
dataIndex: 'alarmTitle', dataIndex: 'alarmTitle',
align: 'left', align: 'left',
width: 200, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.eventTime'), title: t('views.faultManage.activeAlarm.eventTime'),
dataIndex: 'eventTime', dataIndex: 'eventTime',
align: 'left', align: 'center',
sorter: (a: any, b: any) => 1, sorter: (a: any, b: any) => 1,
width: 150, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.alarmType'), title: t('views.faultManage.activeAlarm.alarmType'),
dataIndex: 'alarmType', dataIndex: 'alarmType',
key: 'alarmType', key: 'alarmType',
align: 'left', align: 'left',
width: 120, width: 5,
},
{
title: t('views.faultManage.activeAlarm.pvFlag'),
dataIndex: 'pvFlag',
align: 'center',
width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.clearUser'), title: t('views.faultManage.activeAlarm.clearUser'),
dataIndex: 'clearUser', dataIndex: 'clearUser',
align: 'left', align: 'left',
width: 100, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.clearType'), title: t('views.faultManage.activeAlarm.clearType'),
dataIndex: 'clearType', dataIndex: 'clearType',
key: 'clearType', key: 'clearType',
align: 'left', align: 'left',
width: 120, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.clearTime'), title: t('views.faultManage.activeAlarm.clearTime'),
dataIndex: 'clearTime', dataIndex: 'clearTime',
align: 'left', align: 'left',
sorter: (a: any, b: any) => 1, sorter: (a: any, b: any) => 1,
width: 150, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.ackState'), title: t('views.faultManage.activeAlarm.ackState'),
dataIndex: 'ackState', dataIndex: 'ackState',
key: 'ackState', key: 'ackState',
align: 'left', align: 'left',
width: 100, width: 5,
}, },
{ {
title: t('views.faultManage.activeAlarm.ackUser'), title: t('views.faultManage.activeAlarm.ackUser'),
dataIndex: 'ackUser', dataIndex: 'ackUser',
align: 'left', align: 'left',
width: 100, width: 5,
}, },
{ {
title: t('common.operate'), title: t('common.operate'),
key: 'alarm_id', key: 'alarm_id',
align: 'left', align: 'center',
fixed: 'right', fixed: 'right',
width: 120, width: 5,
}, },
]; ];
@@ -636,7 +648,7 @@ onMounted(() => {
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
<a-row :gutter="16"> <a-row>
<a-col :lg="6" :md="12" :xs="24"> <a-col :lg="6" :md="12" :xs="24">
<a-form-item <a-form-item
:label="t('views.faultManage.activeAlarm.alarmCode')" :label="t('views.faultManage.activeAlarm.alarmCode')"
@@ -676,7 +688,22 @@ onMounted(() => {
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
<a-row :gutter="16"> <a-row>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item
:label="t('views.faultManage.activeAlarm.pvFlag')"
name="pv_flag"
>
<a-select
v-model:value="queryParams.pv_flag"
:placeholder="t('common.selectPlease')"
:options="[
{ label: 'PNF', value: 'PNF' },
{ label: 'VNF', value: 'VNF' },
]"
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24"> <a-col :lg="6" :md="12" :xs="24">
<a-form-item <a-form-item
:label="t('views.faultManage.activeAlarm.alarmType')" :label="t('views.faultManage.activeAlarm.alarmType')"
@@ -777,12 +804,12 @@ onMounted(() => {
:data-source="tableState.data" :data-source="tableState.data"
:size="tableState.size" :size="tableState.size"
:row-selection="{ :row-selection="{
columnWidth: 48, columnWidth: 3,
selectedRowKeys: state.selectedRowKeys, selectedRowKeys: state.selectedRowKeys,
onChange: onSelectChange, onChange: onSelectChange,
}" }"
:pagination="tablePagination" :pagination="tablePagination"
:scroll="{ x: tableColumns.length * 150, y: 400 }" :scroll="{ x: tableColumns.length * 120, y: 400 }"
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'origSeverity'"> <template v-if="column.key === 'origSeverity'">
@@ -836,7 +863,6 @@ onMounted(() => {
@ok="fnModalOk" @ok="fnModalOk"
:ok-text="t('views.faultManage.activeAlarm.confirm')" :ok-text="t('views.faultManage.activeAlarm.confirm')"
@cancel="fnModalCancel" @cancel="fnModalCancel"
:footer="null"
> >
<a-form <a-form
name="modalStateFrom" name="modalStateFrom"
@@ -920,6 +946,15 @@ onMounted(() => {
</a-row> </a-row>
<a-row> <a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.faultManage.activeAlarm.pvFlag')"
name="pvFlag"
>
{{ modalState.from.pvFlag }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
:label="t('views.faultManage.activeAlarm.alarmType')" :label="t('views.faultManage.activeAlarm.alarmType')"
@@ -938,6 +973,14 @@ onMounted(() => {
{{ modalState.from.locationInfo }} {{ modalState.from.locationInfo }}
</a-form-item> </a-form-item>
<a-row> <a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.faultManage.activeAlarm.province')"
name="province"
>
{{ modalState.from.province }}
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
:label="t('views.faultManage.activeAlarm.origLevel')" :label="t('views.faultManage.activeAlarm.origLevel')"
@@ -976,10 +1019,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.clearType')" :label="t('views.faultManage.activeAlarm.clearType')"
name="clearType" name="clearType"
> >
<DictTag {{ modalState.from.clearType }}
:options="dict.activeClearType"
:value="modalState.from.clearType"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
@@ -1006,10 +1046,7 @@ onMounted(() => {
:label="t('views.faultManage.activeAlarm.ackState')" :label="t('views.faultManage.activeAlarm.ackState')"
name="ackState" name="ackState"
> >
<DictTag {{ modalState.from.ackState }}
:options="dict.activeAckState"
:value="modalState.from.ackState"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
+1
View File
@@ -43,6 +43,7 @@ onMounted(() => {
getConfigKey('sys.homePage').then(res => { getConfigKey('sys.homePage').then(res => {
spinning.value = false; spinning.value = false;
if (res.code === RESULT_CODE_SUCCESS && res.data) { if (res.code === RESULT_CODE_SUCCESS && res.data) {
console.log(spinning);
if (res.data) { if (res.data) {
const asyncComponent = findView(`${res.data}`); const asyncComponent = findView(`${res.data}`);
currentComponent.value = defineAsyncComponent(asyncComponent); currentComponent.value = defineAsyncComponent(asyncComponent);
+1 -245
View File
@@ -1,24 +1,19 @@
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref, onMounted, toRaw } from 'vue'; import { reactive, ref, onMounted, toRaw } from 'vue';
import { PageContainer } from 'antdv-pro-layout'; import { PageContainer } from 'antdv-pro-layout';
import { ProModal } from 'antdv-pro-modal';
import { SizeType } from 'ant-design-vue/es/config-provider'; import { SizeType } from 'ant-design-vue/es/config-provider';
import { ColumnsType } from 'ant-design-vue/es/table'; import { ColumnsType } from 'ant-design-vue/es/table';
import { Form, Modal, message } from 'ant-design-vue/es'; import { Modal, message } from 'ant-design-vue/es';
import { parseDateToStr } from '@/utils/date-utils'; import { parseDateToStr } from '@/utils/date-utils';
import { import {
getBakFile, getBakFile,
getBakFileList, getBakFileList,
downFile, downFile,
delFile, delFile,
updateFTPInfo,
getFTPInfo,
putFTPInfo,
} from '@/api/logManage/exportFile'; } from '@/api/logManage/exportFile';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useI18n from '@/hooks/useI18n'; import useI18n from '@/hooks/useI18n';
import saveAs from 'file-saver'; import saveAs from 'file-saver';
import { regExpIPv4 } from '@/utils/regular-utils';
const { t } = useI18n(); const { t } = useI18n();
/**网元参数 */ /**网元参数 */
@@ -274,131 +269,6 @@ onMounted(() => {
// fnGetList(); // fnGetList();
// }); // });
}); });
/**对象信息状态类型 */
type ModalStateType = {
/**新增框或修改框是否显示 */
openByEdit: boolean;
/**标题 */
title: string;
/**表单数据 */
from: Record<string, any>;
/**确定按钮 loading */
confirmLoading: boolean;
};
/**FTP日志对象信息状态 */
let modalState: ModalStateType = reactive({
openByEdit: false,
title: '设置远程备份配置',
from: {
username: '',
password: '',
toIp: '',
toPort: 22,
enable: false,
dir: '',
},
confirmLoading: false,
});
/**FTP日志对象信息内表单属性和校验规则 */
const modalStateFrom = Form.useForm(
modalState.from,
reactive({
toIp: [
{
required: true,
pattern: regExpIPv4,
message: 'Please enter the service login IP',
},
],
username: [
{
required: true,
trigger: 'blur',
message: 'Please enter the service login user name',
},
],
dir: [
{
required: true,
trigger: 'blur',
message: 'Please enter the service address target file directory',
},
],
})
);
/**
* 对话框弹出显示为 新增或者修改
* @param configId 参数编号id, 不传为新增
*/
function fnModalVisibleByEdit() {
if (modalState.confirmLoading) return;
const hide = message.loading(t('common.loading'), 0);
modalState.confirmLoading = true;
getFTPInfo().then(res => {
modalState.confirmLoading = false;
hide();
if (res.code === RESULT_CODE_SUCCESS && res.data) {
modalState.from = Object.assign(modalState.from, res.data);
modalState.title = 'Setting Remote Backup';
modalState.openByEdit = true;
} else {
message.error(res.msg, 3);
modalState.title = 'Setting Remote Backup';
modalState.openByEdit = false;
}
});
}
/**FTP对象保存 */
function fnModalOk() {
modalStateFrom.validate().then(() => {
modalState.confirmLoading = true;
const from = toRaw(modalState.from);
updateFTPInfo(from)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(`Configuration saved successfully`, 3);
fnModalCancel();
} else {
message.warning(`Configuration save exception`, 3);
}
})
.finally(() => {
modalState.confirmLoading = false;
});
});
}
/**
* 对话框弹出关闭执行函数
* 进行表达规则校验
*/
function fnModalCancel() {
modalState.openByEdit = false;
modalStateFrom.resetFields();
}
/**
* 同步文件到FTP
* @param row
*/
function fnSyncFileToFTP(row: Record<string, any>) {
putFTPInfo(row.filePath, row.fileName)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.operateOk'), 3);
} else {
message.warning(res.msg, 3);
}
})
.finally(() => {
modalState.confirmLoading = false;
});
}
</script> </script>
<template> <template>
@@ -435,12 +305,6 @@ function fnSyncFileToFTP(row: Record<string, any>) {
<!-- 插槽-卡片右侧 --> <!-- 插槽-卡片右侧 -->
<template #extra> <template #extra>
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip>
<template #title>Setting Remote Backup</template>
<a-button type="text" @click.prevent="fnModalVisibleByEdit()">
<template #icon><DeliveredProcedureOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.reloadText') }}</template> <template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()"> <a-button type="text" @click.prevent="fnGetList()">
@@ -464,14 +328,6 @@ function fnSyncFileToFTP(row: Record<string, any>) {
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'fileName'"> <template v-if="column.key === 'fileName'">
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-button
type="link"
:loading="downLoading"
@click.prevent="fnSyncFileToFTP(record)"
v-if="record.fileType === 'file'"
>
<template #icon><CloudServerOutlined /></template>
</a-button>
<a-button <a-button
type="link" type="link"
:loading="downLoading" :loading="downLoading"
@@ -493,106 +349,6 @@ function fnSyncFileToFTP(row: Record<string, any>) {
</template> </template>
</a-table> </a-table>
</a-card> </a-card>
<!-- 新增框或修改框 -->
<ProModal
:drag="true"
:width="800"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:open="modalState.openByEdit"
:title="modalState.title"
:confirm-loading="modalState.confirmLoading"
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form
name="modalStateFrom"
layout="horizontal"
:label-col="{ span: 6 }"
:label-wrap="true"
>
<a-form-item label="Enable" name="enable" :label-col="{ span: 3 }">
<a-switch
v-model:checked="modalState.from.enable"
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
/>
</a-form-item>
<template v-if="modalState.from.enable">
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
label="Service IP"
name="toIp"
v-bind="modalStateFrom.validateInfos.toIp"
>
<a-input
v-model:value="modalState.from.toIp"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
label="Service Port"
name="toPort"
v-bind="modalStateFrom.validateInfos.toPort"
>
<a-input-number
v-model:value="modalState.from.toPort"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input-number>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
label="UserName"
name="username"
v-bind="modalStateFrom.validateInfos.username"
>
<a-input
v-model:value="modalState.from.username"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
label="Password"
name="password"
v-bind="modalStateFrom.validateInfos.password"
>
<a-input-password
v-model:value="modalState.from.password"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input-password>
</a-form-item>
</a-col>
</a-row>
<a-form-item
label="Save Dir"
name="dir"
v-bind="modalStateFrom.validateInfos.dir"
:label-col="{ span: 3 }"
>
<a-input
v-model:value="modalState.from.dir"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</template>
</a-form>
</ProModal>
</PageContainer> </PageContainer>
</template> </template>
-1
View File
@@ -50,7 +50,6 @@ async function fnGetState() {
graphG6.value.setItemState(neShape, 'neState', ne.serverState.online); graphG6.value.setItemState(neShape, 'neState', ne.serverState.online);
} }
} }
await new Promise(resolve => setTimeout(resolve, 15_000));
} }
/**查询全部网元数据列表 */ /**查询全部网元数据列表 */
@@ -92,13 +92,13 @@ const graphNodeMenu = new Menu({
${neState.neName ?? '--'} ${neState.neName ?? '--'}
</h3> </h3>
<div id="restart" style="cursor: pointer; margin-bottom: 4px"> <div id="restart" style="cursor: pointer; margin-bottom: 4px">
> ${t('views.ne.common.restart')} > ${t('views.configManage.neManage.restart')}
</div> </div>
<div id="stop" style="cursor: pointer; margin-bottom: 4px;"> <div id="stop" style="cursor: pointer; margin-bottom: 4px;">
> ${t('views.ne.common.stop')} > ${t('views.configManage.neManage.stop')}
</div> </div>
<div id="log" style="cursor: pointer; margin-bottom: 4px;"> <div id="log" style="cursor: pointer; margin-bottom: 4px;">
> ${t('views.ne.common.log')} > ${t('views.configManage.neManage.log')}
</div> </div>
</div> </div>
`; `;
@@ -130,17 +130,26 @@ export function edgeLineAnimateState() {
// circle-move 圆点沿边运动 // circle-move 圆点沿边运动
if (name === 'circle-move') { if (name === 'circle-move') {
const backArr = group.findAll((ele: any) => let back1 = group.find(
ele.get('name').startsWith('circle-stroke') (ele: any) => ele.get('name') === 'circle-stroke1'
); );
if (back1) {
back1.remove();
back1.destroy();
}
let back2 = group.find(
(ele: any) => ele.get('name') === 'circle-stroke2'
);
if (back2) {
back2.remove();
back2.destroy();
}
if (value) { if (value) {
if (backArr.length > 0) return;
// 第一个矩形边 // 第一个矩形边
const fillColor = typeof value === 'string' ? value : '#1890ff'; const fillColor = typeof value === 'string' ? value : '#1890ff';
// 边缘路径的起始位置 // 边缘路径的起始位置
const startPoint = keyShape.getPoint(0); const startPoint = keyShape.getPoint(0);
const back1 = group.addShape('circle', { back1 = group.addShape('circle', {
attrs: { attrs: {
x: startPoint.x, x: startPoint.x,
y: startPoint.y, y: startPoint.y,
@@ -150,7 +159,6 @@ export function edgeLineAnimateState() {
// 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性 // 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性
name: 'circle-stroke1', name: 'circle-stroke1',
}); });
back1.show();
back1.animate( back1.animate(
(ratio: any) => { (ratio: any) => {
// 每帧中的操作。比率范围从 0 到 1,表示动画的进度。返回修改后的配置 // 每帧中的操作。比率范围从 0 到 1,表示动画的进度。返回修改后的配置
@@ -169,7 +177,7 @@ export function edgeLineAnimateState() {
); );
// 第二个矩形边 // 第二个矩形边
const endPoint = keyShape.getPoint(1); const endPoint = keyShape.getPoint(1);
const back2 = group.addShape('circle', { back2 = group.addShape('circle', {
zIndex: -2, zIndex: -2,
attrs: { attrs: {
x: endPoint.x, x: endPoint.x,
@@ -180,7 +188,7 @@ export function edgeLineAnimateState() {
// 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性 // 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性
name: 'circle-stroke2', name: 'circle-stroke2',
}); });
back2.show();
back2.animate( back2.animate(
(ratio: any) => { (ratio: any) => {
// 每帧中的操作。比率范围从 0 到 1,表示动画的进度。返回修改后的配置 // 每帧中的操作。比率范围从 0 到 1,表示动画的进度。返回修改后的配置
@@ -197,14 +205,6 @@ export function edgeLineAnimateState() {
duration: 2 * 1000, // 执行一次的持续时间 duration: 2 * 1000, // 执行一次的持续时间
} }
); );
} else {
if (backArr.length <= 0) return;
backArr.forEach((ele: any) => {
ele.hide();
ele.remove();
ele.destroy();
});
backArr.length = 0;
} }
return; return;
} }
@@ -212,7 +212,11 @@ export function edgeLineAnimateState() {
// line-dash 虚线运动 // line-dash 虚线运动
if (name === 'line-dash') { if (name === 'line-dash') {
if (value) { if (value) {
if (keyShape.cfg.animating) return; keyShape.stopAnimate();
keyShape.attr({
lineDash: null,
lineDashOffset: null,
});
let index = 0; let index = 0;
keyShape.animate( keyShape.animate(
() => { () => {
@@ -230,12 +234,6 @@ export function edgeLineAnimateState() {
duration: 3000, // 执行一次的持续时间 duration: 3000, // 执行一次的持续时间
} }
); );
} else {
keyShape.stopAnimate();
keyShape.attr({
lineDash: null,
lineDashOffset: null,
});
} }
return; return;
} }
@@ -248,7 +246,6 @@ export function edgeLineAnimateState() {
back.remove(); back.remove();
back.destroy(); back.destroy();
} }
const { path, stroke, lineWidth } = keyShape.attr(); const { path, stroke, lineWidth } = keyShape.attr();
back = group.addShape('path', { back = group.addShape('path', {
attrs: { attrs: {
@@ -300,23 +300,12 @@ export function nodeImageAnimateState() {
ele.get('name').startsWith('circle-shape') ele.get('name').startsWith('circle-shape')
); );
if (value) { if (value) {
const fillColor = typeof value === 'string' ? value : '#f5222d'; if (Array.isArray(backArr) && backArr.length === 3) return;
// 移除
if (Array.isArray(backArr) && backArr.length >= 3) {
for (const back of backArr) {
back.stopAnimate();
back.hide();
back.remove();
back.destroy();
}
backArr.length = 0;
}
// 根据大小确定半径
const size = Array.isArray(model?.size) ? model?.size : [40, 40]; const size = Array.isArray(model?.size) ? model?.size : [40, 40];
const x = size[0] / 2; const x = size[0] / 2;
const y = -size[1] / 2; const y = -size[1] / 2;
const r = 3; const r = 3;
const fillColor = typeof value === 'string' ? value : '#f5222d';
// 第一个背景圆 // 第一个背景圆
const back1 = group.addShape('circle', { const back1 = group.addShape('circle', {
@@ -1,383 +0,0 @@
<script setup lang="ts">
import { reactive, ref, toRaw, watch } from 'vue';
import { ProModal } from 'antdv-pro-modal';
import { message, Modal } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider';
import { ColumnsType } from 'ant-design-vue/es/table';
import { saveAs } from 'file-saver';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useI18n from '@/hooks/useI18n';
import { exportNBState, listNBState } from '@/api/neData/nb-state';
import { Dayjs } from 'dayjs';
const { t } = useI18n();
const emit = defineEmits(['cancel', 'update:open']);
const props = defineProps({
title: {
type: String,
default: '标题',
},
open: {
type: Boolean,
default: false,
},
/**网元ID */
neId: {
type: String,
default: false,
},
neType: {
type: String,
default: false,
},
});
/**开始结束时间 */
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>(undefined);
/**状态字典 */
const nbStateOptions = ref<DictType[]>([
{
value: 'ON',
label: t('views.neData.baseStation.online'),
tagType: 'green',
tagClass: '',
},
{
value: 'OFF',
label: t('views.neData.baseStation.offline'),
tagType: 'red',
tagClass: '',
},
]);
/**查询参数 */
let queryParams = reactive({
/**网元 */
neType: '',
neId: '',
/**排序字段 */
sortField: 'id',
sortOrder: 'desc',
/**状态 */
status: undefined as undefined | string,
/**开始时间 */
startTime: undefined as undefined | number,
/**结束时间 */
endTime: undefined as undefined | number,
/**当前页数 */
pageNum: 1,
/**每页条数 */
pageSize: 20,
});
/**查询参数重置 */
function fnQueryReset() {
queryParams = Object.assign(queryParams, {
status: undefined,
startTime: undefined,
endTime: undefined,
pageNum: 1,
pageSize: 20,
});
queryRangePicker.value = undefined;
tablePagination.current = 1;
tablePagination.pageSize = 20;
fnGetList();
}
/**表格状态类型 */
type TabeStateType = {
/**加载等待 */
loading: boolean;
/**紧凑型 */
size: SizeType;
/**搜索栏 */
seached: boolean;
/**记录数据 */
data: object[];
/**勾选记录 */
selectedRowKeys: (string | number)[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
size: 'middle',
seached: true,
data: [],
selectedRowKeys: [],
});
/**表格字段列 */
let tableColumns = ref<ColumnsType>([
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 80,
},
{
title: t('views.neData.baseStation.name'),
dataIndex: 'name',
align: 'left',
resizable: true,
width: 120,
minWidth: 100,
maxWidth: 250,
ellipsis: true,
},
{
title: t('views.neData.baseStation.position'),
dataIndex: 'position',
align: 'left',
resizable: true,
width: 150,
minWidth: 100,
maxWidth: 400,
ellipsis: true,
},
{
title: t('views.neData.baseStation.address'),
dataIndex: 'address',
align: 'left',
width: 100,
},
{
title: t('views.neData.baseStation.nbName'),
dataIndex: 'nbName',
align: 'left',
resizable: true,
width: 100,
minWidth: 100,
maxWidth: 200,
},
{
title: t('views.neData.baseStation.state'),
dataIndex: 'state',
key: 'state',
align: 'left',
width: 80,
},
{
title: t('views.neData.baseStation.time'),
dataIndex: 'time',
align: 'left',
width: 150,
},
]);
/**表格分页器参数 */
let tablePagination = reactive({
/**当前页数 */
current: 1,
/**每页条数 */
pageSize: 20,
/**默认的每页条数 */
defaultPageSize: 20,
/**指定每页可以显示多少条 */
pageSizeOptions: ['10', '20', '50', '100'],
/**只有一页时是否隐藏分页器 */
hideOnSinglePage: false,
/**是否可以快速跳转至某页 */
showQuickJumper: true,
/**是否可以改变 pageSize */
showSizeChanger: true,
/**数据总数 */
total: 0,
showTotal: (total: number) =>
t('common.tablePaginationTotal', { total: total }),
onChange: (page: number, pageSize: number) => {
tablePagination.current = page;
tablePagination.pageSize = pageSize;
queryParams.pageNum = page;
queryParams.pageSize = pageSize;
fnGetList();
},
});
/**列表导出 */
function fnExportList() {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.neData.baseStation.exportTip'),
onOk() {
const hide = message.loading(t('common.loading'), 0);
const querys = toRaw(queryParams);
exportNBState(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
saveAs(res.data, `nb_state_history_records_export_${Date.now()}.xlsx`);
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
});
},
});
}
/**查询字典数据列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (tableState.loading) return;
tableState.loading = true;
if (pageNum) {
queryParams.pageNum = pageNum;
}
// 时间范围
if (
Array.isArray(queryRangePicker.value) &&
queryRangePicker.value.length > 0
) {
queryParams.startTime = queryRangePicker.value[0].valueOf();
queryParams.endTime = queryRangePicker.value[1].valueOf();
} else {
queryParams.startTime = undefined;
queryParams.endTime = undefined;
}
listNBState(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
// 取消勾选
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
tablePagination.total = res.total;
tableState.data = res.rows;
if (
tablePagination.total <=
(queryParams.pageNum - 1) * tablePagination.pageSize &&
queryParams.pageNum !== 1
) {
tableState.loading = false;
fnGetList(queryParams.pageNum - 1);
}
}
tableState.loading = false;
});
}
/**弹框取消按钮事件 */
function fnModalCancel() {
emit('update:open', false);
emit('cancel');
}
/**监听是否显示,初始数据 */
watch(
() => props.open,
val => {
if (val) {
queryParams.neType = props.neType;
queryParams.neId = props.neId;
// 获取列表数据
fnGetList();
}
}
);
</script>
<template>
<ProModal
:drag="true"
:destroyOnClose="true"
:width="1200"
:title="props.title"
:open="props.open"
:keyboard="false"
:mask-closable="false"
@cancel="fnModalCancel"
:footer="null"
>
<!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16" style="margin-left: 0; margin-right: 0">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item
:label="t('views.neData.baseStation.state')"
name="status"
>
<a-select
v-model:value="queryParams.status"
allow-clear
:placeholder="t('common.selectPlease')"
:options="nbStateOptions"
>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="16" :md="12" :xs="24">
<a-form-item
:label="t('views.neData.baseStation.time')"
name="queryRangePicker"
>
<a-range-picker
v-model:value="queryRangePicker"
:bordered="true"
:allow-clear="true"
style="width: 100%"
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
></a-range-picker>
</a-form-item>
</a-col>
<a-col :lg="24" :md="24" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList(1)">
<template #icon><SearchOutlined /></template>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
{{ t('common.reset') }}
</a-button>
<a-button type="dashed" @click.prevent="fnExportList()">
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
</a-row>
</a-form>
<!-- 表格列表 -->
<a-table
class="table"
row-key="id"
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: tableColumns.length * 120, y: 'calc(100vh - 480px)' }"
@resizeColumn="(w:number, col:any) => (col.width = w)"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'state'">
<DictTag
:options="nbStateOptions"
:value="record.state"
value-default="OFF"
/>
</template>
</template>
</a-table>
</ProModal>
</template>
<style lang="less" scoped>
.table :deep(.ant-pagination) {
padding: 0 24px;
}
</style>
-33
View File
@@ -1,33 +0,0 @@
<script setup lang="ts">
import { ref } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import ListComponent from './list.vue';
import TopologyComponent from './topology.vue';
import useI18n from '@/hooks/useI18n';
const { t } = useI18n();
const value = ref<string>('list');
</script>
<template>
<PageContainer>
<template #extra>
<a-radio-group v-model:value="value" button-style="solid">
<a-radio-button value="list">
{{ t('views.neData.baseStation.list') }}
</a-radio-button>
<a-radio-button value="topology">
{{ t('views.neData.baseStation.topology') }}
</a-radio-button>
</a-radio-group>
</template>
<div v-show="value === 'list'">
<ListComponent></ListComponent>
</div>
<div v-if="value === 'topology'">
<TopologyComponent></TopologyComponent>
</div>
</PageContainer>
</template>
<style scoped></style>
-958
View File
@@ -1,958 +0,0 @@
<script setup lang="ts">
import {
reactive,
ref,
onMounted,
toRaw,
computed,
defineAsyncComponent,
} from 'vue';
import { Form, message, Modal } from 'ant-design-vue';
import { SizeType } from 'ant-design-vue/es/config-provider';
import { ColumnsType } from 'ant-design-vue/es/table';
import { ProModal } from 'antdv-pro-modal';
import UploadModal from '@/components/UploadModal/index.vue';
import useNeInfoStore from '@/store/modules/neinfo';
import useI18n from '@/hooks/useI18n';
const { t, currentLocale } = useI18n();
import {
addAMFNbState,
delAMFNbState,
editAMFNbState,
listAMFNbStatelist,
} from '@/api/neData/amf';
import {
addMMENbState,
delMMENbState,
editMMENbState,
listMMENbStatelist,
} from '@/api/neData/mme';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import saveAs from 'file-saver';
import { readSheet, writeSheet } from '@/utils/execl-utils';
import { useRoute } from 'vue-router';
const route = useRoute();
// 异步加载组件
const HistoryModal = defineAsyncComponent(
() => import('./components/history.vue')
);
const nbState = ref<DictType[]>([
{
value: 'ON',
label: t('views.neData.baseStation.online'),
tagType: 'green',
tagClass: '',
},
{
value: 'OFF',
label: t('views.neData.baseStation.offline'),
tagType: 'red',
tagClass: '',
},
]);
/**网元参数 */
let neCascaderOptions = ref<Record<string, any>[]>([]);
/**网元数据 */
let neTypeAndId = ref<string[]>([]);
/**查询参数 */
let queryParams = reactive({
/**网元ID */
neId: '',
/**IMSI */
state: undefined,
});
/**查询参数重置 */
function fnQueryReset() {
queryParams = Object.assign(queryParams, {
state: undefined,
});
fnGetList();
}
/**表格状态类型 */
type TabeStateType = {
/**加载等待 */
loading: boolean;
/**紧凑型 */
size: SizeType;
/**记录数据 */
data: Record<string, any>[];
/**勾选记录 */
selectedRowKeys: (string | number)[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
size: 'small',
data: [],
selectedRowKeys: [],
});
/**表格字段列 */
let tableColumns = ref<ColumnsType>([
// {
// title: t('common.rowId'),
// dataIndex: 'index',
// align: 'left',
// width: 80,
// },
{
title: t('views.neData.baseStation.name'),
dataIndex: 'name',
align: 'left',
resizable: true,
width: 120,
minWidth: 100,
maxWidth: 250,
ellipsis: true,
},
{
title: t('views.neData.baseStation.position'),
dataIndex: 'position',
align: 'left',
resizable: true,
width: 150,
minWidth: 100,
maxWidth: 400,
ellipsis: true,
},
{
title: t('views.neData.baseStation.address'),
dataIndex: 'address',
align: 'left',
resizable: true,
width: 100,
minWidth: 100,
maxWidth: 200,
},
{
title: t('views.neData.baseStation.nbName'),
dataIndex: 'nbName',
align: 'left',
resizable: true,
width: 100,
minWidth: 100,
maxWidth: 200,
},
{
title: t('views.neData.baseStation.ueNum'),
dataIndex: 'ueNum',
align: 'left',
resizable: true,
width: 80,
minWidth: 80,
maxWidth: 120,
},
{
title: t('views.neData.baseStation.state'),
dataIndex: 'state',
key: 'state',
align: 'left',
resizable: true,
width: 80,
minWidth: 80,
maxWidth: 120,
},
{
title: t('views.neData.baseStation.time'),
align: 'left',
width: 150,
customRender(opt) {
const record = opt.value;
if (record.state === 'OFF') {
return record.offTime || '-';
}
return record.onTime || '-';
},
},
]);
/**表格分页器参数 */
let tablePagination = {
/**当前页数 */
current: 1,
/**每页条数 */
pageSize: 20,
/**默认的每页条数 */
defaultPageSize: 20,
/**指定每页可以显示多少条 */
pageSizeOptions: ['10', '20', '50', '100'],
/**只有一页时是否隐藏分页器 */
hideOnSinglePage: true,
/**是否可以快速跳转至某页 */
showQuickJumper: true,
/**是否可以改变 pageSize */
showSizeChanger: true,
/**数据总数 */
total: 0,
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
onChange: (page: number, pageSize: number) => {
tablePagination.current = page;
tablePagination.pageSize = pageSize;
},
};
/**表格多选 */
function fnTableSelectedRowKeys(keys: (string | number)[]) {
tableState.selectedRowKeys = keys;
}
/**
* 记录删除
* @param index ID
*/
function fnRecordDelete(index: string) {
const [neType, neId] = neTypeAndId.value;
if (!neId) return;
let msg = `Delete index as:${index}`;
if (index === '0') {
msg = `Remove the index checkbox:${tableState.selectedRowKeys.length}`;
}
Modal.confirm({
title: t('common.tipTitle'),
content: msg,
onOk() {
const reqArr = [];
if (index === '0') {
if (tableState.selectedRowKeys.length <= 0) {
return;
}
for (const v of tableState.selectedRowKeys) {
if (neType === 'MME') {
reqArr.push(delMMENbState(neId, v));
}
if (neType === 'AMF') {
reqArr.push(delAMFNbState(neId, v));
}
}
} else {
if (neType === 'MME') {
reqArr.push(delMMENbState(neId, index));
}
if (neType === 'AMF') {
reqArr.push(delAMFNbState(neId, index));
}
}
if (reqArr.length <= 0) return;
Promise.all(reqArr).then(res => {
const resArr = res.filter(
(item: any) => item.code !== RESULT_CODE_SUCCESS
);
if (resArr.length <= 0) {
message.success({
content: `${t('common.operateOk')}`,
duration: 3,
});
fnGetList();
} else {
message.error({
content: `${t('common.operateErr')}`,
duration: 3,
});
}
});
},
});
}
/**查询列表 */
function fnGetList() {
if (tableState.loading) return;
tableState.loading = true;
const [neType, neId] = neTypeAndId.value;
queryParams.neId = neId;
let req = null;
if (neType === 'MME') {
req = listMMENbStatelist(toRaw(queryParams));
}
if (neType === 'AMF') {
req = listAMFNbStatelist(toRaw(queryParams));
}
if (req === null) {
tableState.data = [];
tableState.loading = false;
return;
}
req.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
// 取消勾选
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
tableState.data = res.data.filter((item: any) => {
// 状态过滤
if (queryParams.state) {
return item.state === queryParams.state;
}
return true;
});
} else {
tableState.data = [];
}
tableState.loading = false;
});
}
const stateNum = computed(() => {
let onNum = 0;
let offNum = 0;
tableState.data.forEach((item: any) => {
if (item.state === 'ON') {
onNum += 1;
}
if (item.state === 'OFF') {
offNum += 1;
}
});
return [onNum, offNum];
});
/**对话框对象信息状态类型 */
type ModalStateType = {
/**新增框或修改框是否显示 */
openByEdit: boolean;
/**标题 */
title: string;
/**表单数据 */
from: Record<string, any>;
/**确定按钮 loading */
confirmLoading: boolean;
/**历史框 */
openByHistory: boolean;
/**导入框 */
openByImport: boolean;
importMsgArr: string[];
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
openByEdit: false,
title: 'NB Config List',
from: {
address: '',
index: undefined,
name: '',
nbName: undefined,
offTime: undefined,
onTime: undefined,
position: '',
state: undefined,
ueNum: undefined,
},
confirmLoading: false,
openByHistory: false,
openByImport: false,
importMsgArr: [],
});
/**对话框内表单属性和校验规则 */
const modalStateFrom = Form.useForm(
modalState.from,
reactive({
address: [
{ required: true, message: t('views.neData.baseStation.addressPlease') },
],
name: [
{ required: true, message: t('views.neData.baseStation.namePlease') },
],
position: [
{ required: true, message: t('views.neData.baseStation.positionPlease') },
],
})
);
/**
* 对话框弹出显示为 新增或者修改
* @param noticeId 网元id, 不传为新增
*/
function fnModalVisibleByEdit(edit?: string | number) {
if (!edit) {
modalStateFrom.resetFields(); //重置表单
modalState.title = t('views.neData.baseStation.addRadio');
modalState.openByEdit = true;
// 获取最大index
if (tableState.data.length <= 0) {
modalState.from.index = 1;
} else {
const last = tableState.data[tableState.data.length - 1];
modalState.from.index = last.index + 1;
}
}
// 编辑
if (edit === '0') {
const row = tableState.data.find((row: any) => {
return row.index === tableState.selectedRowKeys[0];
});
modalStateFrom.resetFields(); //重置表单
Object.assign(modalState.from, row);
modalState.title = t('views.neData.baseStation.editRadio');
modalState.openByEdit = true;
}
}
/**
* 对话框弹出确认执行函数
* 进行表达规则校验
*/
function fnModalOk() {
const [neType, neId] = neTypeAndId.value;
if (!neId) return;
const from = JSON.parse(JSON.stringify(modalState.from));
modalStateFrom
.validate()
.then(e => {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
let result: any = null;
if (neType === 'MME') {
result = from.state
? editMMENbState(neId, from)
: addMMENbState(neId, from);
}
if (neType === 'AMF') {
result = from.state
? editAMFNbState(neId, from)
: addAMFNbState(neId, from);
}
if (result === null) {
return;
}
result
.then((res: any) => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
fnModalCancel();
fnGetList();
} else {
message.error({
content: t('common.operateErr'),
duration: 3,
});
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
})
.catch(e => {
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
});
}
/**
* 对话框弹出关闭执行函数
* 进行表达规则校验
*/
function fnModalCancel() {
modalState.openByEdit = false;
modalState.openByHistory = false;
modalStateFrom.resetFields();
}
/**导出当前列表 */
function fnExportList() {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.neData.baseStation.exportTip'),
onOk: async () => {
if (modalState.confirmLoading) return;
modalState.confirmLoading = true;
let rows: Record<string, any>[] = [];
// 勾选的网元数据
if (tableState.selectedRowKeys.length > 0) {
rows = tableState.data.filter(item =>
tableState.selectedRowKeys.includes(item.index)
);
} else {
rows = tableState.data;
}
const dataArr: Record<string, any>[] = [];
for (const row of rows) {
let data: any = {};
data[t('views.neData.baseStation.name')] = row.name;
data[t('views.neData.baseStation.position')] = row.position;
data[t('views.neData.baseStation.address')] = row.address;
data[t('views.neData.baseStation.nbName')] = row.nbName;
data[t('views.neData.baseStation.ueNum')] = row.ueNum;
nbState.value.find(item => {
if (item.value === row.state) {
data[t('views.neData.baseStation.state')] = item.label;
}
});
data[t('views.neData.baseStation.time')] = row.time || '-';
dataArr.push(data);
}
// 导出
writeSheet(dataArr, 'Sheet1', {
header: [
t('views.neData.baseStation.name'),
t('views.neData.baseStation.position'),
t('views.neData.baseStation.address'),
t('views.neData.baseStation.nbName'),
t('views.neData.baseStation.ueNum'),
t('views.neData.baseStation.state'),
t('views.neData.baseStation.time'),
],
}).then(fileBlob =>
saveAs(fileBlob, `nb_state_records_export_${Date.now()}.xlsx`)
);
modalState.confirmLoading = false;
tableState.selectedRowKeys = [];
},
});
}
/**对话框弹出历史窗口 */
function fnHistoryView() {
modalState.openByHistory = true;
}
/**对话框表格信息导入弹出窗口 */
function fnModalImportOpen() {
modalState.openByImport = true;
}
function fnModalImportClose() {
modalState.openByImport = false;
fnQueryReset();
}
/**对话框表格信息导入上传 */
function fnModalImportUpload(file: File) {
const hide = message.loading(t('common.loading'), 0);
const [neType, neId] = neTypeAndId.value;
modalState.importMsgArr = [];
// 获取最大index
let index = 0;
if (tableState.data.length <= 0) {
index = 1;
} else {
const last = tableState.data[tableState.data.length - 1];
index = last.index + 1;
}
const reader = new FileReader();
reader.onload = function (e: any) {
const arrayBuffer = e.target.result;
readSheet(arrayBuffer).then(async rows => {
console.log(rows);
if (rows.length <= 0) {
hide();
message.error({
content: t('views.neData.baseStation.importDataEmpty'),
duration: 3,
});
return;
}
// 开始导入
modalState.confirmLoading = true;
for (const row of rows) {
const rowId = row[t('common.rowId')];
const name = row[t('views.neData.baseStation.name')];
const position = row[t('views.neData.baseStation.position')];
const address = row[t('views.neData.baseStation.address')];
let result: any = null;
// 检查IP地址是否定义
const hasAddress = tableState.data.find(
item => item.address === address
);
if (hasAddress) {
// 定义则更新名称位置
if (neType === 'MME') {
result = await editMMENbState(
neId,
Object.assign({}, hasAddress, {
name,
position,
})
);
}
if (neType === 'AMF') {
result = await editAMFNbState(
neId,
Object.assign({}, hasAddress, {
name,
position,
})
);
}
let msg = `${t('common.rowId')}: ${rowId} ${t(
'views.neData.baseStation.editRadio'
)}${t('common.operateErr')}`;
if (result.code === RESULT_CODE_SUCCESS) {
msg = `${t('common.rowId')}: ${rowId} ${t(
'views.neData.baseStation.editRadio'
)}${t('common.operateOk')}`;
}
modalState.importMsgArr.push(msg);
} else {
// 未定义则新增
const form = {
index,
name: `${name}`,
position: `${position}`,
address: `${address}`,
};
if (neType === 'MME') {
result = await addMMENbState(neId, form);
}
if (neType === 'AMF') {
result = await addAMFNbState(neId, form);
}
let msg = `${t('common.rowId')}: ${rowId} ${t(
'views.neData.baseStation.addRadio'
)}${t('common.operateErr')}`;
if (result.code === RESULT_CODE_SUCCESS) {
index += 1;
msg = `${t('common.rowId')}: ${rowId} ${t(
'views.neData.baseStation.addRadio'
)}${t('common.operateOk')}`;
}
modalState.importMsgArr.push(msg);
}
}
hide();
modalState.confirmLoading = false;
});
};
reader.onerror = function (e) {
hide();
console.error('reader file error:', e);
};
reader.readAsArrayBuffer(file);
}
/**对话框表格信息导入模板 */
async function fnModalImportTemplate() {
const baseUrl = import.meta.env.VITE_HISTORY_BASE_URL;
const xlsxUrl = `${
baseUrl.length === 1 && baseUrl.indexOf('/') === 0
? ''
: baseUrl.indexOf('/') === -1
? '/' + baseUrl
: baseUrl
}/nbStateImput`;
const lang = currentLocale.value.split('_')[0];
saveAs(
`${xlsxUrl}/${lang}.xlsx`,
`nb_state_records_import_template_${Date.now()}.xlsx`
);
}
onMounted(() => {
// 获取网元网元列表
useNeInfoStore()
.fnNelist()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
let arr: Record<string, any>[] = [];
useNeInfoStore().neCascaderOptions.forEach(item => {
if (['AMF', 'MME'].includes(item.value)) {
arr.push(JSON.parse(JSON.stringify(item)));
}
});
neCascaderOptions.value = arr;
// 无查询参数neType时 默认选择AMF
const queryNeType = (route.query.neType as string) || 'AMF';
const item = arr.find(s => s.value === queryNeType);
if (item && item.children) {
const info = item.children[0];
neTypeAndId.value = [info.neType, info.neId];
} else {
const info = neCascaderOptions.value[0].children[0];
neTypeAndId.value = [info.neType, info.neId];
}
}
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
})
.finally(() => {
// 获取列表数据
fnGetList();
});
});
</script>
<template>
<div>
<a-card
:bordered="false"
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
>
<!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item :label="t('views.ne.common.neType')" name="neType ">
<a-cascader
v-model:value="neTypeAndId"
:options="neCascaderOptions"
:allow-clear="false"
:placeholder="t('common.selectPlease')"
@change="fnGetList"
/>
</a-form-item>
</a-col>
<a-col :lg="4" :md="6" :xs="24">
<a-form-item
:label="t('views.neData.baseStation.state')"
name="state"
>
<a-select
v-model:value="queryParams.state"
:options="nbState"
:placeholder="t('common.selectPlease')"
@change="fnGetList"
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList()">
<template #icon><SearchOutlined /></template>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
{{ t('common.reset') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-card>
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
<template #icon>
<PlusOutlined />
</template>
{{ t('common.addText') }}
</a-button>
<a-button
type="default"
:disabled="tableState.selectedRowKeys.length != 1"
:loading="modalState.confirmLoading"
@click.prevent="fnModalVisibleByEdit('0')"
>
<template #icon><FormOutlined /></template>
{{ t('common.editText') }}
</a-button>
<a-button
type="default"
danger
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')"
>
<template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }}
</a-button>
<a-button type="dashed" @click.prevent="fnModalImportOpen()">
<template #icon><ImportOutlined /></template>
{{ t('common.import') }}
</a-button>
<a-button type="dashed" @click.prevent="fnExportList()">
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</a-button>
<a-button type="default" @click.prevent="fnHistoryView()">
<template #icon><ContainerOutlined /></template>
{{ t('views.neData.baseStation.history') }}
</a-button>
</a-space>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<div>
<template
v-if="
queryParams.state === undefined || queryParams.state === 'ON'
"
>
{{ t('views.neData.baseStation.online') }}:
<strong style="color: green">{{ stateNum[0] }} </strong>
</template>
<template
v-if="
queryParams.state === undefined || queryParams.state === 'OFF'
"
>
&nbsp;
{{ t('views.neData.baseStation.offline') }}:
<strong style="color: red">
{{ stateNum[1] }}
</strong>
</template>
</div>
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
<template #icon><ReloadOutlined /></template>
</a-button>
</a-tooltip>
</a-space>
</template>
<!-- 表格列表 -->
<a-table
class="table"
row-key="index"
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:row-selection="{
type: 'checkbox',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
:scroll="{ x: tableColumns.length * 120, y: 'calc(100vh - 480px)' }"
@resizeColumn="(w:number, col:any) => (col.width = w)"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'state'">
<DictTag
:options="nbState"
:value="record.state"
value-default="OFF"
/>
</template>
</template>
</a-table>
</a-card>
<!-- 新增框或修改框 -->
<ProModal
:drag="true"
:width="500"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:open="modalState.openByEdit"
:title="modalState.title"
:confirm-loading="modalState.confirmLoading"
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form
name="modalStateFrom"
layout="horizontal"
:label-col="{ span: 6 }"
:labelWrap="true"
>
<a-form-item
:label="t('views.neData.baseStation.name')"
name="name"
v-bind="modalStateFrom.validateInfos.name"
>
<a-input
v-model:value="modalState.from.name"
allow-clear
:maxlength="64"
>
</a-input>
</a-form-item>
<a-form-item
:label="t('views.neData.baseStation.position')"
name="position"
v-bind="modalStateFrom.validateInfos.position"
>
<a-input
v-model:value="modalState.from.position"
allow-clear
:maxlength="64"
>
</a-input>
</a-form-item>
<a-form-item
v-if="!modalState.from.state"
:label="t('views.neData.baseStation.address')"
name="address"
v-bind="modalStateFrom.validateInfos.address"
>
<a-input
v-model:value="modalState.from.address"
allow-clear
:maxlength="64"
>
</a-input>
</a-form-item>
</a-form>
</ProModal>
<!-- 状态历史框 -->
<HistoryModal
v-model:open="modalState.openByHistory"
:title="t('views.neData.baseStation.history')"
:ne-type="neTypeAndId[0]"
:ne-id="neTypeAndId[1]"
@cancel="fnModalCancel"
></HistoryModal>
<!-- 上传导入表格数据文件框 -->
<UploadModal
:title="t('common.import')"
@upload="fnModalImportUpload"
@close="fnModalImportClose"
v-model:open="modalState.openByImport"
:ext="['.xls', '.xlsx']"
:size="10"
>
<template #default>
<a-row justify="space-between" align="middle">
<a-col :span="12"> </a-col>
<a-col :span="6">
<a-button
type="link"
:title="t('views.system.user.downloadObj')"
@click.prevent="fnModalImportTemplate"
>
{{ t('views.system.user.downloadObj') }}
</a-button>
</a-col>
</a-row>
<a-textarea
:disabled="true"
:hidden="modalState.importMsgArr.length <= 0"
:value="modalState.importMsgArr.join('\r\n')"
:auto-size="{ minRows: 2, maxRows: 8 }"
style="background-color: transparent; color: rgba(0, 0, 0, 0.85)"
/>
</template>
</UploadModal>
</div>
</template>
<style lang="less" scoped>
.table :deep(.ant-pagination) {
padding: 0 24px;
}
</style>
-642
View File
@@ -1,642 +0,0 @@
<script setup lang="ts">
import { reactive, onMounted, ref, onBeforeUnmount, useTemplateRef } from 'vue';
import { Graph, GraphData, Menu, Tooltip, Util } from '@antv/g6';
import { listAMFNbStatelist } from '@/api/neData/amf';
import { parseBasePath } from '@/plugins/file-static-url';
import { edgeLineAnimateState } from '@/views/monitor/topologyBuild/hooks/registerEdge';
import { nodeImageAnimateState } from '@/views/monitor/topologyBuild/hooks/registerNode';
import useNeInfoStore from '@/store/modules/neinfo';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { stateNeInfo } from '@/api/ne/neInfo';
import { parseDateToStr } from '@/utils/date-utils';
import { useFullscreen } from '@vueuse/core';
import { listMMENbStatelist } from '@/api/neData/mme';
const { t } = useI18n();
/**图DOM节点实例对象 */
const graphG6Dom = useTemplateRef('graphG6Dom');
/**图数据 */
const graphData = reactive<Record<string, any>>({
nodes: [
{
id: 'omc',
label: 'OMC',
img: parseBasePath('/svg/service_db.svg'),
},
{
id: 'amf1',
label: 'amf1',
img: parseBasePath('/svg/service.svg'),
},
{
id: 'amf2',
label: 'amf2',
img: parseBasePath('/svg/service.svg'),
},
{
id: 'base1',
label: 'base1',
img: parseBasePath('/svg/base.svg'),
},
{
id: 'base2',
label: 'base2',
img: parseBasePath('/svg/base.svg'),
},
],
edges: [
{
source: 'omc',
target: 'amf1',
},
{
source: 'omc',
target: 'amf2',
},
{
source: 'amf1',
target: 'base1',
},
{
source: 'amf2',
target: 'base1',
},
{
source: 'amf2',
target: 'base2',
},
],
});
/**图实例对象 */
const graphG6 = ref<any>(null);
/**图节点右击菜单 */
const graphNodeMenu = new Menu({
offsetX: 6,
offseY: 10,
itemTypes: ['node'],
getContent(evt) {
if (!evt) return t('views.monitor.topologyBuild.graphNotInfo');
const { id, label, nType, nInfo }: any = evt.item?.getModel();
if (['GNB', 'ENB'].includes(nType)) {
return `
<div
style="
display: flex;
flex-direction: column;
width: 140px;
"
>
<span>
${t('views.neData.baseStation.name')}:
${label ?? '--'}
</span>
</div>
`;
}
return `
<div
style="
display: flex;
flex-direction: column;
width: 140px;
"
>
<span>
${t('views.monitor.topology.name')}:
${label ?? '--'}
</span>
</div>
`;
},
});
/**图节点展示 */
const graphNodeTooltip = new Tooltip({
offsetX: 10,
offsetY: 20,
getContent(evt) {
if (!evt) return t('views.monitor.topologyBuild.graphNotInfo');
const { id, label, nType, nInfo }: any = evt.item?.getModel();
if (['GNB', 'ENB'].includes(nType)) {
return `
<div
style="
display: flex;
flex-direction: column;
width: 256px;
"
>
<div><strong>${t('views.neData.baseStation.state')}:</strong><span>
${
nInfo.state === 'ON'
? t('views.neData.baseStation.online')
: t('views.neData.baseStation.offline')
}
</span></div>
<div><strong>${t('views.neData.baseStation.time')}:</strong><span>
${nInfo.state === 'ON' ? nInfo.onTime ?? '--' : nInfo.offTime ?? '--'}
</span></div>
<div>==============================</div>
<div><strong>ID:</strong><span>${nInfo.index}</span></div>
<div><strong>${t('views.neData.baseStation.address')}:</strong><span>
${nInfo.address ?? '--'}</span></div>
<div><strong>${t('views.neData.baseStation.nbName')}:</strong><span>
${nInfo.nbName ?? '--'}</span></div>
<div><strong>${t('views.neData.baseStation.ueNum')}:</strong><span>
${nInfo.ueNum ?? '--'}</span></div>
<div><strong>${t('views.neData.baseStation.name')}:</strong><span>
${nInfo.name ?? '--'}
</span></div>
<div><strong>${t(
'views.neData.baseStation.position'
)}:</strong><span style="word-wrap: break-word;">
${nInfo.position}
</span></div>
</div>
`;
}
return `
<div
style="
display: flex;
flex-direction: column;
width: 200px;
"
>
<div><strong>${t('views.monitor.topology.state')}:</strong><span>
${
nInfo.online
? t('views.monitor.topology.normalcy')
: t('views.monitor.topology.exceptions')
}
</span></div>
<div><strong>${t('views.monitor.topology.refreshTime')}:</strong><span>
${nInfo.refreshTime ?? '--'}
</span></div>
<div>========================</div>
<div><strong>ID:</strong><span>${nInfo.neId}</span></div>
<div><strong>${t('views.monitor.topology.name')}:</strong><span>
${nInfo.neName ?? '--'}
</span></div>
<div><strong>IP:</strong><span>${nInfo.neIP}</span></div>
<div><strong>${t('views.monitor.topology.version')}:</strong><span>
${nInfo.version ?? '--'}
</span></div>
<div><strong>${t('views.monitor.topology.serialNum')}:</strong><span>
${nInfo.sn ?? '--'}
</span></div>
<div><strong>${t('views.monitor.topology.expiryDate')}:</strong><span>
${nInfo.expire ?? '--'}
</span></div>
</div>
`;
},
itemTypes: ['node'],
});
/**注册自定义边或节点 */
function registerEdgeNode() {
// 边
edgeLineAnimateState();
// 节点
nodeImageAnimateState();
}
/**
* format the string
* @param {string} str The origin string
* @param {number} maxWidth max width
* @param {number} fontSize font size
* @return {string} the processed result
*/
function fittingString(str: string, maxWidth: number, fontSize: number) {
let currentWidth = 0;
let res = str;
const pattern = new RegExp('[\u4E00-\u9FA5]+'); // distinguish the Chinese charactors and letters
str.split('').forEach((letter, i) => {
if (currentWidth > maxWidth) return;
if (pattern.test(letter)) {
// Chinese charactors
currentWidth += fontSize;
} else {
// get the width of single letter according to the fontSize
currentWidth += Util.getLetterWidth(letter, fontSize);
}
if (currentWidth > maxWidth) {
res = `${str.substring(0, i)}\n${str.substring(i)}`;
}
});
return res;
}
/**图事件 */
function graphEvent(graph: Graph) {
graph.on('edge:mouseenter', evt => {
const { item } = evt;
if (!item) return;
graph.setItemState(item, 'circle-move', '#b5d6fb');
});
graph.on('edge:mouseleave', evt => {
const { item } = evt;
if (!item) return;
graph.setItemState(item, 'circle-move', false);
graph.setItemState(item, 'circle-move:#b5d6fb', false);
});
}
/**图数据渲染 */
function handleRanderGraph(container: HTMLElement | null, data: GraphData) {
if (!container) return;
const { clientHeight, clientWidth } = container;
// 注册自定义边或节点
registerEdgeNode();
const graph = new Graph({
container: container,
width: clientWidth,
height: clientHeight,
fitCenter: false,
fitView: true,
fitViewPadding: [40, 40, 40, 40],
modes: {
// default: ['drag-canvas', 'drag-node', 'zoom-canvas'],
default: [
'zoom-canvas',
'drag-canvas',
'drag-node',
{
type: 'drag-combo',
onlyChangeComboSize: true, // 不改变层级关系
},
{
type: 'collapse-expand-combo',
trigger: 'dblclick',
relayout: true, // 收缩展开后,不重新布局
},
],
},
groupByTypes: false,
plugins: [graphNodeMenu, graphNodeTooltip],
layout: {
type: 'dagre',
rankdir: 'TB', // 布局的方向,TB:从上到下,BT:从下到上,LR:从左到右,RL:从右到左
//align: 'UL', // 节点对齐方式 UL、UR、DL、DR
controlPoints: true,
nodesep: 20,
ranksep: 40,
},
animate: true,
defaultNode: {
type: 'image-animate-state',
labelCfg: {
offset: 8,
position: 'bottom',
style: { fill: '#ffffff', fontSize: 14, fontWeight: 500 },
},
size: 48,
img: parseBasePath('/svg/cloud.svg'),
width: 48,
height: 48,
},
defaultEdge: {
type: 'line-animate-state',
labelCfg: {
autoRotate: true,
refY: 10,
refX: 40,
},
style: {
stroke: '#fafafa',
lineWidth: 1.5,
},
},
defaultCombo: {
labelCfg: {
offset: 16,
position: 'bottom',
style: { fill: '#ffffff', fontSize: 14, fontWeight: 500 },
},
style: {
stroke: '#BDEFDB',
fill: '#BDEFDB',
opacity: 0.25,
},
collapsedSubstituteIcon: {
show: true,
img: parseBasePath('/svg/service.svg'),
width: 48,
height: 48,
},
},
});
graph.data(data);
graph.render();
graphEvent(graph);
// 创建 ResizeObserver 实例
const observer = new ResizeObserver(function (entries) {
// 当元素大小发生变化时触发回调函数
entries.forEach(function (entry) {
if (!graph) {
return;
}
graph.changeSize(entry.contentRect.width, entry.contentRect.height);
graph.fitCenter();
});
});
// 监听元素大小变化
observer.observe(container);
return graph;
}
/**
* 获取图组数据渲染到画布
*/
async function fnGraphDataLoad() {
// 加载基础网元
await useNeInfoStore().fnNelist();
const dataNe = await fnGraphDataBase();
Object.assign(graphData, dataNe);
graphG6.value = handleRanderGraph(graphG6Dom.value, dataNe);
// 添加基站
const dataNb = await fnGraphDataNb(dataNe);
Object.assign(graphData, dataNb);
// graphG6.value.clear();
graphG6.value.read(dataNb);
// 添加状态
interval.value = true;
repeatFn();
}
/**图数据网元构建 */
async function fnGraphDataBase() {
const data: GraphData = {
nodes: [],
edges: [],
};
// 添加基础网元
for (const item of useNeInfoStore().getNeSelectOtions) {
if ('OMC' === item.value) {
if (item.children?.length === 0) continue;
// 是否存在OMC保证唯一
const hasOMC = data.nodes?.findIndex(v => v.neType === 'OMC');
if (hasOMC !== -1) continue;
// 根网元
const omcInfo = item.children[0];
const node = {
id: 'OMC',
label: omcInfo.neName,
img: parseBasePath('/svg/service_db.svg'),
nInfo: { online: false, neId: omcInfo.neId, neType: omcInfo.neType },
nType: 'OMC',
};
// 添加OMC节点
data.nodes?.push(node);
continue;
}
if (['AMF', 'MME'].includes(item.value)) {
if (item.children?.length === 0) continue;
for (const child of item.children) {
const id = `${child.neType}_${child.neId}`;
const node = {
id: id,
label: child.neName,
img: parseBasePath('/svg/service.svg'),
nInfo: { online: false, neId: child.neId, neType: child.neType },
nType: item.value,
};
// 添加节点
data.nodes?.push(node);
data.edges?.push({
source: 'OMC',
target: id,
});
}
item.children.forEach((v: any) => {});
continue;
}
}
return data;
}
/**图数据基站构建 */
async function fnGraphDataNb(data: GraphData) {
const arr = data.nodes?.filter((v: any) => ['AMF', 'MME'].includes(v.nType));
if (arr === undefined || arr.length === 0) return data;
for (const item of arr) {
if (item.nType === 'AMF') {
const neId = (item.nInfo as any).neId;
const res = await listAMFNbStatelist({ neId });
if (res.code !== RESULT_CODE_SUCCESS || !Array.isArray(res.data)) {
continue;
}
for (const nb of res.data) {
const id = `${item.id}_${nb.index}`;
data.nodes?.push({
id: id,
label: fittingString(`${nb.name}`, 80, 14),
img: parseBasePath('/svg/base5G.svg'),
nInfo: nb,
nType: 'GNB',
});
data.edges?.push({
source: item.id,
target: id,
});
}
}
if (item.nType === 'MME') {
const neId = (item.nInfo as any).neId;
const res = await listMMENbStatelist({ neId });
if (res.code !== RESULT_CODE_SUCCESS || !Array.isArray(res.data)) {
continue;
}
for (const nb of res.data) {
const id = `${item.id}_${nb.index}`;
data.nodes?.push({
id: id,
label: fittingString(`${nb.name}`, 80, 14),
img: parseBasePath('/svg/base4G.svg'),
nInfo: nb,
nType: 'ENB',
});
data.edges?.push({
source: item.id,
target: id,
});
}
}
}
return data;
}
/**
* 图状态构建
* @param reload 是否重载状态
*/
async function fnGraphState(reload: boolean = false) {
// 节点状态
if (!Array.isArray(graphData.nodes)) return;
const onc = graphData.nodes.find((v: any) => v.nType === 'OMC');
if (onc) {
const { id, nInfo } = onc as any;
if (!nInfo) return;
const res = await stateNeInfo(nInfo.neType, nInfo.neId);
if (res.code === RESULT_CODE_SUCCESS) {
Object.assign(nInfo, res.data, {
refreshTime: parseDateToStr(res.data.refreshTime, 'HH:mm:ss'),
});
}
const stateColor = nInfo.online ? '#52c41a' : '#f5222d'; // 状态颜色
graphG6.value.setItemState(id, 'top-right-dot', stateColor);
}
graphData.nodes
.filter((v: any) => ['AMF', 'MME'].includes(v.nType))
.forEach(async (v: any) => {
const { id, nInfo } = v;
if (!nInfo) return;
const res = await stateNeInfo(nInfo.neType, nInfo.neId);
if (res.code === RESULT_CODE_SUCCESS) {
Object.assign(nInfo, res.data, {
refreshTime: parseDateToStr(res.data.refreshTime, 'HH:mm:ss'),
});
}
const stateColor = nInfo.online ? '#52c41a' : '#f5222d'; // 状态颜色
graphG6.value.setItemState(id, 'top-right-dot', stateColor);
// 重载时更新下级基站状态
if (reload && nInfo.neType === 'AMF') {
const res = await listAMFNbStatelist({ neId: nInfo.neId });
if (res.code == RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
for (const nb of res.data) {
const nbItem = graphData.nodes.find(
(v: any) => v.id === `${id}_${nb.index}`
);
if (nbItem) {
Object.assign(nbItem.nInfo, nb);
const stateColor = nb.state === 'ON' ? '#52c41a' : '#f5222d'; // 状态颜色
graphG6.value.setItemState(
nbItem.id,
'top-right-dot',
stateColor
);
}
}
}
}
if (reload && nInfo.neType === 'MME') {
const res = await listMMENbStatelist({ neId: nInfo.neId });
if (res.code == RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
for (const nb of res.data) {
const nbItem = graphData.nodes.find(
(v: any) => v.id === `${id}_${nb.index}`
);
if (nbItem) {
Object.assign(nbItem.nInfo, nb);
const stateColor = nb.state === 'ON' ? '#52c41a' : '#f5222d'; // 状态颜色
graphG6.value.setItemState(
nbItem.id,
'top-right-dot',
stateColor
);
}
}
}
}
});
if (reload) {
await new Promise(resolve => setTimeout(resolve, 15_000));
return;
}
// 非重载时使用初始获取的状态
graphData.nodes
.filter((v: any) => ['GNB', 'ENB'].includes(v.nType))
.forEach(async (v: any) => {
const { id, nInfo } = v;
if (!nInfo) return;
const stateColor = nInfo.state === 'ON' ? '#52c41a' : '#f5222d'; // 状态颜色
graphG6.value.setItemState(id, 'top-right-dot', stateColor);
});
}
/**递归调度器 */
const interval = ref<boolean>(false);
/**递归刷新图状态 */
function repeatFn(reload: boolean = false) {
if (!interval.value || !graphG6Dom.value) {
return;
}
fnGraphState(reload)
.finally(() => {
repeatFn(true); // 递归调用自己
})
.catch(error => {
console.error(error);
});
}
const viewportDom = ref<HTMLElement | null>(null);
const { isFullscreen, toggle } = useFullscreen(viewportDom);
function fullscreen() {
toggle();
if (!graphG6Dom.value) return;
if (isFullscreen.value) {
graphG6Dom.value.style.height = 'calc(100vh - 300px)';
} else {
graphG6Dom.value.style.height = '100vh';
}
const { clientHeight, clientWidth } = graphG6Dom.value;
graphG6.value.changeSize(clientHeight, clientWidth);
graphG6.value.fitView(40);
}
onMounted(() => {
fnGraphDataLoad();
});
onBeforeUnmount(() => {
interval.value = false;
});
</script>
<template>
<a-card :bordered="false" :body-style="{ padding: '0' }" ref="viewportDom">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
{{ t('views.neData.baseStation.topologyTitle') }}
</a-space>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-button type="default" @click.prevent="fullscreen()">
<template #icon>
<FullscreenExitOutlined v-if="isFullscreen" />
<FullscreenOutlined v-else />
</template>
{{ t('loayouts.rightContent.fullscreen') }}
</a-button>
</template>
<div ref="graphG6Dom" class="chart"></div>
</a-card>
</template>
<style lang="less" scoped>
.chart {
width: 100%;
height: calc(100vh - 300px);
background-color: rgb(43, 47, 51);
}
</style>
+21 -16
View File
@@ -195,7 +195,9 @@ function fnActiveConfigNode(key: string | number) {
}, 300); }, 300);
} else { } else {
message.warning({ message.warning({
content: `${param.paramDisplay} ${t('views.ne.neConfig.noConfigData')}`, content: `${param.paramDisplay} ${t(
'views.configManage.configParamForm.noConfigData'
)}`,
duration: 3, duration: 3,
}); });
} }
@@ -207,7 +209,7 @@ function fnGetNeConfig() {
const neType = neTypeSelect.value[0]; const neType = neTypeSelect.value[0];
if (!neType) { if (!neType) {
message.warning({ message.warning({
content: t('views.ne.neConfig.neTypePleace'), content: t('views.configManage.configParamForm.neTypePleace'),
duration: 3, duration: 3,
}); });
return; return;
@@ -414,7 +416,7 @@ onMounted(() => {
<!-- 网元类型 --> <!-- 网元类型 -->
<a-card size="small" :bordered="false" :loading="treeState.loading"> <a-card size="small" :bordered="false" :loading="treeState.loading">
<template #title> <template #title>
{{ t('views.ne.neConfig.treeTitle') }} {{ t('views.configManage.configParamForm.treeTitle') }}
</template> </template>
<a-form layout="vertical" autocomplete="off"> <a-form layout="vertical" autocomplete="off">
<a-form-item name="neId "> <a-form-item name="neId ">
@@ -425,7 +427,7 @@ onMounted(() => {
@change="fnGetNeConfig" @change="fnGetNeConfig"
/> />
</a-form-item> </a-form-item>
<a-form-item name="treeStateData"> <a-form-item name="listeningPort">
<a-tree <a-tree
:tree-data="treeState.data" :tree-data="treeState.data"
:selected-keys="[treeState.selectNode.paramName]" :selected-keys="[treeState.selectNode.paramName]"
@@ -459,12 +461,12 @@ onMounted(() => {
{{ treeState.selectNode.paramDisplay }} {{ treeState.selectNode.paramDisplay }}
</a-typography-text> </a-typography-text>
<a-typography-text type="danger" v-else> <a-typography-text type="danger" v-else>
{{ t('views.ne.neConfig.treeSelectTip') }} {{ t('views.configManage.configParamForm.treeSelectTip') }}
</a-typography-text> </a-typography-text>
</template> </template>
<template #extra> <template #extra>
<a-space :size="8" align="center" v-show="!treeState.selectLoading"> <a-space :size="8" align="center" v-show="!treeState.selectLoading">
<a-tooltip placement="topRight"> <a-tooltip>
<template #title>{{ t('common.reloadText') }}</template> <template #title>{{ t('common.reloadText') }}</template>
<a-button <a-button
type="default" type="default"
@@ -547,9 +549,10 @@ onMounted(() => {
<template #title> {{ t('common.ok') }} </template> <template #title> {{ t('common.ok') }} </template>
<a-popconfirm <a-popconfirm
:title=" :title="
t('views.ne.neConfig.editOkTip', { t(
num: record['display'], 'views.configManage.configParamForm.editOkTip',
}) { num: record['display'] }
)
" "
placement="topRight" placement="topRight"
:disabled="listState.confirmLoading" :disabled="listState.confirmLoading"
@@ -607,7 +610,7 @@ onMounted(() => {
</a-table> </a-table>
<!-- array类型 --> <!-- array类型 -->
<template v-else-if="treeState.selectNode.paramType === 'array'"> <template v-if="treeState.selectNode.paramType === 'array'">
<a-table <a-table
class="table" class="table"
row-key="index" row-key="index"
@@ -686,7 +689,9 @@ onMounted(() => {
" "
> >
<template #icon><BarsOutlined /></template> <template #icon><BarsOutlined /></template>
{{ t('views.ne.neConfig.arrayMore') }} {{
t('views.configManage.configParamForm.arrayMore')
}}
</a-button> </a-button>
<!--特殊字段拓展显示--> <!--特殊字段拓展显示-->
<span <span
@@ -787,7 +792,11 @@ onMounted(() => {
<template v-if="text.array"> <template v-if="text.array">
<a-button type="default" size="small"> <a-button type="default" size="small">
<template #icon><BarsOutlined /></template> <template #icon><BarsOutlined /></template>
{{ t('views.ne.neConfig.arrayMore') }} {{
t(
'views.configManage.configParamForm.arrayMore'
)
}}
</a-button> </a-button>
</template> </template>
@@ -807,10 +816,6 @@ onMounted(() => {
</template> </template>
</a-table> </a-table>
</template> </template>
<template v-else>
<a-alert type="warning" show-icon message="No Data" />
</template>
</a-card> </a-card>
</a-col> </a-col>
</a-row> </a-row>
@@ -1,513 +0,0 @@
import {
addNeConfigData,
delNeConfigData,
editNeConfigData,
} from '@/api/ne/neConfig';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { Modal, message } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider';
import { reactive, watch } from 'vue';
/**
* 参数配置array类型
* @param param 父级传入 { t, treeState, neTypeSelect, neIdSelect, fnActiveConfigNode, ruleVerification, modalState, fnModalCancel}
* @returns
*/
export default function useConfigArray({
t,
treeState,
neTypeSelect,
neIdSelect,
fnActiveConfigNode,
ruleVerification,
modalState,
fnModalCancel,
}: any) {
/**多列列表状态类型 */
type ArrayStateType = {
/**紧凑型 */
size: SizeType;
/**多列嵌套记录字段 */
columns: Record<string, any>[];
/**表格字段列排序 */
columnsDnd: Record<string, any>[];
/**多列记录数据 */
columnsData: Record<string, any>[];
/**多列嵌套展开key */
arrayChildExpandKeys: any[];
/**多列记录数据 */
data: Record<string, any>[];
/**多列记录规则 */
dataRule: Record<string, any>;
};
/**多列列表状态 */
let arrayState: ArrayStateType = reactive({
size: 'small',
columns: [],
columnsDnd: [],
columnsData: [],
arrayChildExpandKeys: [],
data: [],
dataRule: {},
});
/**多列表编辑 */
function arrayEdit(rowIndex: Record<string, any>) {
const item = arrayState.data.find((s: any) => s.key === rowIndex.value);
if (!item) return;
const from = arrayInitEdit(item, arrayState.dataRule);
// 处理信息
const row: Record<string, any> = {};
for (const v of from.record) {
if (Array.isArray(v.array)) {
continue;
}
row[v.name] = Object.assign({}, v);
}
// 特殊SMF-upfid选择
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(row, 'upfId')) {
const v = row.upfId.value;
if (typeof v === 'string') {
if (v === '') {
row.upfId.value = [];
} else if (v.includes(';')) {
row.upfId.value = v.split(';');
} else if (v.includes(',')) {
row.upfId.value = v.split(',');
} else {
row.upfId.value = [v];
}
}
}
modalState.from = row;
modalState.type = 'arrayEdit';
modalState.title = `${treeState.selectNode.paramDisplay} ${from.title}`;
modalState.key = from.key;
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
modalState.open = true;
// 关闭嵌套
arrayState.arrayChildExpandKeys = [];
}
/**多列表编辑关闭 */
function arrayEditClose() {
arrayState.arrayChildExpandKeys = [];
fnModalCancel();
}
/**多列表编辑确认 */
function arrayEditOk(from: Record<string, any>) {
const loc = `${from['index']['value']}`;
// 特殊SMF-upfid选择
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(from, 'upfId')) {
const v = from.upfId.value;
if (Array.isArray(v)) {
from.upfId.value = v.join(';');
}
}
// 遍历提取属性和值
let data: Record<string, any> = {};
for (const key in from) {
// 子嵌套的不插入
if (from[key]['array']) {
continue;
}
// 检查规则
const [ok, msg] = ruleVerification(from[key]);
if (!ok) {
message.warning({
content: `${msg}`,
duration: 3,
});
return;
}
data[key] = from[key]['value'];
}
// 请求
const reqArr = [];
if (neTypeSelect.value[1].startsWith('SYNC')) {
for (const neId of neIdSelect.value) {
reqArr.push(
editNeConfigData({
neType: neTypeSelect.value[0],
neId: neId,
paramName: treeState.selectNode.paramName,
paramData: data,
loc: loc,
})
);
}
} else {
reqArr.push(
editNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
paramData: data,
loc: loc,
})
);
}
// 无请求提示
if (reqArr.length === 0) {
message.warning({
content: t('views.ne.neConfig.neIdSyncPleace'),
duration: 3,
});
arrayEditClose();
return;
}
const hide = message.loading(t('common.loading'), 0);
Promise.allSettled(reqArr)
.then(resArr => {
const rejected = resArr.find(res => res.status === 'rejected');
if (rejected) {
message.warning({
content: t('views.ne.neConfig.updateItemErr'),
duration: 3,
});
} else {
message.success({
content: t('views.ne.neConfig.updateItem', {
num: modalState.title,
}),
duration: 3,
});
}
const fulfilled = resArr.find(res => res.status === 'fulfilled');
if (fulfilled) {
fnActiveConfigNode('#');
}
})
.finally(() => {
hide();
arrayEditClose();
});
}
/**多列表删除单行 */
function arrayDelete(rowIndex: Record<string, any>) {
const loc = `${rowIndex.value}`;
const title = `${treeState.selectNode.paramDisplay} Index-${loc}`;
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.ne.neConfig.delItemTip', {
num: title,
}),
onOk() {
// 请求
const reqArr = [];
if (neTypeSelect.value[1].startsWith('SYNC')) {
for (const neId of neIdSelect.value) {
reqArr.push(
delNeConfigData({
neType: neTypeSelect.value[0],
neId: neId,
paramName: treeState.selectNode.paramName,
loc: loc,
})
);
}
} else {
reqArr.push(
delNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
loc: loc,
})
);
}
// 无请求提示
if (reqArr.length === 0) {
message.warning({
content: t('views.ne.neConfig.neIdSyncPleace'),
duration: 3,
});
arrayEditClose();
return;
}
const hide = message.loading(t('common.loading'), 0);
Promise.allSettled(reqArr)
.then(resArr => {
const rejected = resArr.find(res => res.status === 'rejected');
if (rejected) {
message.error({
content: `${rejected.reason}`,
duration: 2,
});
} else {
message.success({
content: t('views.ne.neConfig.delItemOk', {
num: title,
}),
duration: 2,
});
}
const fulfilled = resArr.find(res => res.status === 'fulfilled');
if (fulfilled) {
fnActiveConfigNode('#');
}
})
.finally(() => {
hide();
arrayEditClose();
});
},
});
}
/**多列表新增单行 */
function arrayAdd() {
const from = arrayInitAdd(arrayState.data, arrayState.dataRule);
// 处理信息
const row: Record<string, any> = {};
for (const v of from.record) {
if (Array.isArray(v.array)) {
continue;
}
row[v.name] = Object.assign({}, v);
}
// 特殊SMF-upfid选择
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(row, 'upfId')) {
const v = row.upfId.value;
if (typeof v === 'string') {
if (v === '') {
row.upfId.value = [];
} else if (v.includes(';')) {
row.upfId.value = v.split(';');
} else if (v.includes(',')) {
row.upfId.value = v.split(',');
} else {
row.upfId.value = [v];
}
}
}
modalState.from = row;
modalState.type = 'arrayAdd';
modalState.title = `${treeState.selectNode.paramDisplay} ${from.title}`;
modalState.key = from.key;
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
modalState.open = true;
}
/**多列表新增单行确认 */
function arrayAddOk(from: Record<string, any>) {
// 特殊SMF-upfid选择
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(from, 'upfId')) {
const v = from.upfId.value;
if (Array.isArray(v)) {
from.upfId.value = v.join(';');
}
}
// 遍历提取属性和值
let data: Record<string, any> = {};
for (const key in from) {
// 子嵌套的不插入
if (from[key]['array']) {
continue;
}
// 检查规则
const [ok, msg] = ruleVerification(from[key]);
if (!ok) {
message.warning({
content: `${msg}`,
duration: 3,
});
return;
}
data[key] = from[key]['value'];
}
// 请求
const reqArr = [];
if (neTypeSelect.value[1].startsWith('SYNC')) {
for (const neId of neIdSelect.value) {
reqArr.push(
addNeConfigData({
neType: neTypeSelect.value[0],
neId: neId,
paramName: treeState.selectNode.paramName,
paramData: data,
loc: `${from['index']['value']}`,
})
);
}
} else {
reqArr.push(
addNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
paramData: data,
loc: `${from['index']['value']}`,
})
);
}
// 无请求提示
if (reqArr.length === 0) {
message.warning({
content: t('views.ne.neConfig.neIdSyncPleace'),
duration: 3,
});
arrayEditClose();
return;
}
const hide = message.loading(t('common.loading'), 0);
Promise.allSettled(reqArr)
.then(resArr => {
const rejected = resArr.find(res => res.status === 'rejected');
if (rejected) {
message.warning({
content: t('views.ne.neConfig.addItemErr'),
duration: 3,
});
} else {
message.success({
content: t('views.ne.neConfig.addItemOk', {
num: modalState.title,
}),
duration: 3,
});
}
const fulfilled = resArr.find(res => res.status === 'fulfilled');
if (fulfilled) {
fnActiveConfigNode('#');
}
})
.finally(() => {
hide();
arrayEditClose();
});
}
/**多列表编辑行数据初始化 */
function arrayInitEdit(data: Record<string, any>, dataRule: any) {
const dataFrom = data.record;
const ruleFrom = Object.assign({}, JSON.parse(JSON.stringify(dataRule)));
for (const row of ruleFrom.record) {
// 子嵌套的不初始
if (row.array) {
row.value = [];
continue;
}
// 查找项的值
const item = dataFrom.find((s: any) => s.name === row.name);
if (!item) {
continue;
}
// 可选的
row.optional = 'true';
// 根据规则类型转值
if (['enum', 'int'].includes(row.type)) {
row.value = Number(item.value);
} else if ('bool' === row.type) {
row.value = Boolean(item.value);
} else {
row.value = item.value;
}
}
ruleFrom.key = data.key;
ruleFrom.title = data.title;
return ruleFrom;
}
/**多列表新增行数据初始化 */
function arrayInitAdd(data: any[], dataRule: any) {
// 有数据时取得最后的index
let dataLastIndex = 0;
if (data.length !== 0) {
const lastFrom = Object.assign(
{},
JSON.parse(JSON.stringify(data.at(-1)))
);
if (lastFrom.record.length > 0) {
dataLastIndex = parseInt(lastFrom.key);
dataLastIndex += 1;
}
}
const ruleFrom = Object.assign({}, JSON.parse(JSON.stringify(dataRule)));
for (const row of ruleFrom.record) {
// 子嵌套的不初始
if (row.array) {
row.value = [];
continue;
}
// 可选的
row.optional = 'true';
// index值
if (row.name === 'index') {
let newIndex =
dataLastIndex !== 0 ? dataLastIndex : parseInt(row.value);
if (isNaN(newIndex)) {
newIndex = 0;
}
row.value = newIndex;
ruleFrom.key = newIndex;
ruleFrom.title = `Index-${newIndex}`;
continue;
}
// 根据规则类型转值
if (['enum', 'int'].includes(row.type)) {
row.value = Number(row.value);
}
if ('bool' === row.type) {
row.value = Boolean(row.value);
}
// 特殊SMF-upfid选择
if (neTypeSelect.value[0] === 'SMF' && row.name === 'upfId') {
const v = row.value;
if (typeof v === 'string') {
if (v === '') {
row.value = [];
} else if (v.includes(';')) {
row.value = v.split(';');
} else if (v.includes(',')) {
row.value = v.split(',');
} else {
row.value = [v];
}
}
}
}
return ruleFrom;
}
// 监听表格字段列排序变化关闭展开
watch(
() => arrayState.columnsDnd,
() => {
arrayEditClose();
}
);
return {
arrayState,
arrayEdit,
arrayEditClose,
arrayEditOk,
arrayDelete,
arrayAdd,
arrayAddOk,
arrayInitEdit,
arrayInitAdd,
};
}
@@ -1,454 +0,0 @@
import {
addNeConfigData,
editNeConfigData,
delNeConfigData,
} from '@/api/ne/neConfig';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { Modal, message } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider';
import { nextTick, reactive } from 'vue';
/**
* 参数配置array类型的嵌套array
* @param param 父级传入 { t, treeState, neTypeSelect, neIdSelect, fnActiveConfigNode, ruleVerification, modalState, arrayState, arrayInitEdit, arrayInitAdd, arrayEditClose}
* @returns
*/
export default function useConfigArrayChild({
t,
treeState,
neTypeSelect,
neIdSelect,
fnActiveConfigNode,
ruleVerification,
modalState,
arrayState,
arrayInitEdit,
arrayInitAdd,
arrayEditClose,
}: any) {
/**多列嵌套列表状态类型 */
type ArrayChildStateType = {
/**标题 */
title: string;
/**层级index */
loc: string;
/**紧凑型 */
size: SizeType;
/**多列嵌套记录字段 */
columns: Record<string, any>[];
/**表格字段列排序 */
columnsDnd: Record<string, any>[];
/**多列记录数据 */
columnsData: Record<string, any>[];
/**多列嵌套记录数据 */
data: Record<string, any>[];
/**多列嵌套记录规则 */
dataRule: Record<string, any>;
};
/**多列嵌套表格状态 */
let arrayChildState: ArrayChildStateType = reactive({
title: '',
loc: '',
size: 'small',
columns: [],
columnsDnd: [],
columnsData: [],
data: [],
dataRule: {},
});
/**多列表展开嵌套行 */
function arrayChildExpand(
indexRow: Record<string, any>,
row: Record<string, any>
) {
const loc = indexRow.value;
if (arrayChildState.loc === `${loc}/${row.name}`) {
arrayChildState.loc = '';
arrayState.arrayChildExpandKeys = [];
return;
}
arrayChildState.loc = '';
arrayState.arrayChildExpandKeys = [];
const from = Object.assign({}, JSON.parse(JSON.stringify(row)));
// 无数据时
if (!Array.isArray(from.value)) {
from.value = [];
}
const dataArr = Object.freeze(from.value);
const ruleArr = Object.freeze(from.array);
// 列表项数据
const dataArray: Record<string, any>[] = [];
for (const item of dataArr) {
const index = item['index'];
let record: Record<string, any>[] = [];
for (const key of Object.keys(item)) {
// 规则为准
for (const rule of ruleArr) {
if (rule['name'] === key) {
const ruleItem = Object.assign({ optional: 'true' }, rule, {
value: item[key],
});
record.push(ruleItem);
break;
}
}
}
// dataArray.push(record);
dataArray.push({ title: `Index-${index}`, key: index, record });
}
arrayChildState.data = dataArray;
// 无数据时,用于新增
arrayChildState.dataRule = {
title: `Index-0`,
key: 0,
record: ruleArr,
};
// 列表数据
const columnsData: Record<string, any>[] = [];
for (const v of arrayChildState.data) {
const row: Record<string, any> = {};
for (const item of v.record) {
row[item.name] = item;
}
columnsData.push(row);
}
arrayChildState.columnsData = columnsData;
// 列表字段
const columns: Record<string, any>[] = [];
for (const rule of arrayChildState.dataRule.record) {
columns.push({
title: rule.display,
dataIndex: rule.name,
align: 'left',
resizable: true,
width: 50,
minWidth: 50,
maxWidth: 250,
});
}
columns.push({
title: t('common.operate'),
dataIndex: 'index',
key: 'index',
align: 'center',
fixed: 'right',
width: 100,
});
arrayChildState.columns = columns;
nextTick(() => {
// 设置展开key
arrayState.arrayChildExpandKeys = [indexRow];
// 层级标识
arrayChildState.loc = `${loc}/${from['name']}`;
// 设置展开列表标题
arrayChildState.title = `${from['display']}`;
});
}
/**多列表嵌套行编辑 */
function arrayChildEdit(rowIndex: Record<string, any>) {
const item = arrayChildState.data.find(
(s: any) => s.key === rowIndex.value
);
if (!item) return;
const from = arrayInitEdit(item, arrayChildState.dataRule);
// 处理信息
const row: Record<string, any> = {};
for (const v of from.record) {
if (Array.isArray(v.array)) {
continue;
}
row[v.name] = Object.assign({}, v);
}
modalState.from = row;
modalState.type = 'arrayChildEdit';
modalState.title = `${arrayChildState.title} ${from.title}`;
modalState.key = from.key;
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
modalState.open = true;
}
/**多列表嵌套行编辑确认 */
function arrayChildEditOk(from: Record<string, any>) {
const loc = `${arrayChildState.loc}/${from['index']['value']}`;
let data: Record<string, any> = {};
for (const key in from) {
// 子嵌套的不插入
if (from[key]['array']) {
continue;
}
// 检查规则
const [ok, msg] = ruleVerification(from[key]);
if (!ok) {
message.warning({
content: `${msg}`,
duration: 3,
});
return;
}
data[key] = from[key]['value'];
}
// 请求
const reqArr = [];
if (neTypeSelect.value[1].startsWith('SYNC')) {
for (const neId of neIdSelect.value) {
reqArr.push(
editNeConfigData({
neType: neTypeSelect.value[0],
neId: neId,
paramName: treeState.selectNode.paramName,
paramData: data,
loc,
})
);
}
} else {
reqArr.push(
editNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
paramData: data,
loc,
})
);
}
// 无请求提示
if (reqArr.length === 0) {
message.warning({
content: t('views.ne.neConfig.neIdSyncPleace'),
duration: 3,
});
arrayEditClose();
return;
}
const hide = message.loading(t('common.loading'), 0);
Promise.allSettled(reqArr)
.then(resArr => {
const rejected = resArr.find(res => res.status === 'rejected');
if (rejected) {
message.warning({
content: t('views.ne.neConfig.updateItemErr'),
duration: 3,
});
} else {
message.success({
content: t('views.ne.neConfig.updateItem', {
num: modalState.title,
}),
duration: 3,
});
}
const fulfilled = resArr.find(res => res.status === 'fulfilled');
if (fulfilled) {
fnActiveConfigNode('#');
}
})
.finally(() => {
hide();
arrayEditClose();
});
}
/**多列表嵌套行删除单行 */
function arrayChildDelete(rowIndex: Record<string, any>) {
const index = rowIndex.value;
const loc = `${arrayChildState.loc}/${index}`;
const title = `${arrayChildState.title} Index-${index}`;
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.ne.neConfig.delItemTip', {
num: title,
}),
onOk() {
// 请求
const reqArr = [];
if (neTypeSelect.value[1].startsWith('SYNC')) {
for (const neId of neIdSelect.value) {
reqArr.push(
delNeConfigData({
neType: neTypeSelect.value[0],
neId: neId,
paramName: treeState.selectNode.paramName,
loc,
})
);
}
} else {
reqArr.push(
delNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
loc,
})
);
}
// 无请求提示
if (reqArr.length === 0) {
message.warning({
content: t('views.ne.neConfig.neIdSyncPleace'),
duration: 3,
});
arrayEditClose();
return;
}
const hide = message.loading(t('common.loading'), 0);
Promise.allSettled(reqArr)
.then(resArr => {
const rejected = resArr.find(res => res.status === 'rejected');
if (rejected) {
message.error({
content: `${rejected.reason}`,
duration: 2,
});
} else {
message.success({
content: t('views.ne.neConfig.delItemOk', {
num: title,
}),
duration: 2,
});
}
const fulfilled = resArr.find(res => res.status === 'fulfilled');
if (fulfilled) {
fnActiveConfigNode('#');
}
})
.finally(() => {
hide();
arrayEditClose();
});
},
});
}
/**多列表嵌套行新增单行 */
function arrayChildAdd() {
const from = arrayInitAdd(arrayChildState.data, arrayChildState.dataRule);
// 处理信息
const row: Record<string, any> = {};
for (const v of from.record) {
if (Array.isArray(v.array)) {
continue;
}
row[v.name] = Object.assign({}, v);
}
modalState.from = row;
modalState.type = 'arrayChildAdd';
modalState.title = `${arrayChildState.title} ${from.title}`;
modalState.key = from.key;
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
modalState.open = true;
}
/**多列表新增单行确认 */
function arrayChildAddOk(from: Record<string, any>) {
const loc = `${arrayChildState.loc}/${from['index']['value']}`;
let data: Record<string, any> = {};
for (const key in from) {
// 子嵌套的不插入
if (from[key]['array']) {
continue;
}
// 检查规则
const [ok, msg] = ruleVerification(from[key]);
if (!ok) {
message.warning({
content: `${msg}`,
duration: 3,
});
return;
}
data[key] = from[key]['value'];
}
// 请求
const reqArr = [];
if (neTypeSelect.value[1].startsWith('SYNC')) {
for (const neId of neIdSelect.value) {
reqArr.push(
addNeConfigData({
neType: neTypeSelect.value[0],
neId: neId,
paramName: treeState.selectNode.paramName,
paramData: data,
loc,
})
);
}
} else {
reqArr.push(
addNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
paramData: data,
loc,
})
);
}
// 无请求提示
if (reqArr.length === 0) {
message.warning({
content: t('views.ne.neConfig.neIdSyncPleace'),
duration: 3,
});
arrayEditClose();
return;
}
const hide = message.loading(t('common.loading'), 0);
Promise.allSettled(reqArr)
.then(resArr => {
const rejected = resArr.find(res => res.status === 'rejected');
if (rejected) {
message.warning({
content: t('views.ne.neConfig.addItemErr'),
duration: 3,
});
} else {
message.success({
content: t('views.ne.neConfig.addItemOk', {
num: modalState.title,
}),
duration: 3,
});
}
const fulfilled = resArr.find(res => res.status === 'fulfilled');
if (fulfilled) {
fnActiveConfigNode('#');
}
})
.finally(() => {
hide();
arrayEditClose();
});
}
return {
arrayChildState,
arrayChildExpand,
arrayChildEdit,
arrayChildEditOk,
arrayChildDelete,
arrayChildAdd,
arrayChildAddOk,
};
}
@@ -1,187 +0,0 @@
import { editNeConfigData } from '@/api/ne/neConfig';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { SizeType } from 'ant-design-vue/es/config-provider';
import { message } from 'ant-design-vue/es';
import { reactive, toRaw } from 'vue';
/**
* list类型参数处理
* @param param 父级传入 {t, treeState, neTypeSelect, neIdSelect, ruleVerification}
* @returns
*/
export default function useConfigList({
t,
treeState,
neTypeSelect,
neIdSelect,
ruleVerification,
}: any) {
/**单列表状态类型 */
type ListStateType = {
/**紧凑型 */
size: SizeType;
/**单列记录字段 */
columns: Record<string, any>[];
/**单列记录数据 */
data: Record<string, any>[];
/**编辑行记录 */
editRecord: Record<string, any>;
/**确认提交等待 */
confirmLoading: boolean;
};
/**单列表状态 */
let listState: ListStateType = reactive({
size: 'small',
columns: [
{
title: 'Key',
dataIndex: 'display',
align: 'left',
width: '30%',
},
{
title: 'Value',
dataIndex: 'value',
align: 'left',
width: '70%',
},
],
data: [],
confirmLoading: false,
editRecord: {},
});
/**单列表编辑 */
function listEdit(row: Record<string, any>) {
if (
listState.confirmLoading ||
['read-only', 'read', 'ro'].includes(row.access)
) {
return;
}
listState.editRecord = Object.assign({}, row);
}
/**单列表编辑关闭 */
function listEditClose() {
listState.confirmLoading = false;
listState.editRecord = {};
}
/**单列表编辑确认 */
function listEditOk() {
if (listState.confirmLoading) return;
const from = toRaw(listState.editRecord);
// 检查规则
const [ok, msg] = ruleVerification(from);
if (!ok) {
message.warning({
content: `${msg}`,
duration: 3,
});
return;
}
// 请求
const reqArr = [];
if (neTypeSelect.value[1].startsWith('SYNC')) {
for (const neId of neIdSelect.value) {
reqArr.push(
editNeConfigData({
neType: neTypeSelect.value[0],
neId: neId,
paramName: treeState.selectNode.paramName,
paramData: {
[from['name']]: from['value'],
},
})
);
}
} else {
reqArr.push(
editNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
paramData: {
[from['name']]: from['value'],
},
})
);
}
// 无请求提示
if (reqArr.length === 0) {
message.warning({
content: t('views.ne.neConfig.neIdSyncPleace'),
duration: 3,
});
listState.confirmLoading = false;
listState.editRecord = {};
return;
}
listState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
Promise.allSettled(reqArr)
.then(resArr => {
const rejected = resArr.find(res => res.status === 'rejected');
if (rejected) {
message.warning({
content: t('views.ne.neConfig.updateValueErr'),
duration: 3,
});
} else {
message.success({
content: t('views.ne.neConfig.updateValue', {
num: from['display'],
}),
duration: 3,
});
}
const fulfilled = resArr.find(res => res.status === 'fulfilled');
if (fulfilled) {
// 改变表格数据
const item = listState.data.find(
(item: Record<string, any>) => from['name'] === item['name']
);
if (item) {
Object.assign(item, listState.editRecord);
}
}
})
.finally(() => {
hide();
listState.confirmLoading = false;
listState.editRecord = {};
});
}
/**表格分页器参数 */
let tablePagination = reactive({
/**当前页数 */
current: 1,
/**每页条数 */
pageSize: 10,
/**默认的每页条数 */
defaultPageSize: 10,
/**指定每页可以显示多少条 */
pageSizeOptions: ['10', '20', '50', '100'],
/**只有一页时是否隐藏分页器 */
hideOnSinglePage: true,
/**是否可以快速跳转至某页 */
showQuickJumper: true,
/**是否可以改变 pageSize */
showSizeChanger: true,
/**数据总数 */
total: 0,
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
onChange: (page: number, pageSize: number) => {
tablePagination.current = page;
tablePagination.pageSize = pageSize;
},
});
return { tablePagination, listState, listEdit, listEditClose, listEditOk };
}
-192
View File
@@ -1,192 +0,0 @@
import { getNeConfigData } from '@/api/ne/neConfig';
import { regExpIPv4, regExpIPv6, validURL } from '@/utils/regular-utils';
import { ref } from 'vue';
/**
* 参数公共函数
* @param param 父级传入 {t}
* @returns
*/
export default function useOptions({ t }: any) {
/**规则校验 */
function ruleVerification(row: Record<string, any>): (string | boolean)[] {
let result = [true, ''];
const type = row.type;
const value = row.value;
const filter = row.filter;
const display = row.display;
// 子嵌套的不检查
if (row.array) {
return result;
}
// 可选的同时没有值不检查
if (row.optional === 'true' && !value) {
return result;
}
switch (type) {
case 'int':
// filter: "0~128"
if (filter && filter.indexOf('~') !== -1) {
const filterArr = filter.split('~');
const minInt = parseInt(filterArr[0]);
const maxInt = parseInt(filterArr[1]);
const valueInt = parseInt(value);
if (valueInt < minInt || valueInt > maxInt) {
return [
false,
t('views.ne.neConfig.requireInt', {
display,
filter,
}),
];
}
}
break;
case 'ipv4':
if (!regExpIPv4.test(value)) {
return [
false,
t('views.ne.neConfig.requireIpv4', { display }),
];
}
break;
case 'ipv6':
if (!regExpIPv6.test(value)) {
return [
false,
t('views.ne.neConfig.requireIpv6', { display }),
];
}
break;
case 'enum':
if (filter && filter.indexOf('{') === 1) {
let filterJson: Record<string, any> = {};
try {
filterJson = JSON.parse(filter); //string---json
} catch (error) {
console.error(error);
}
if (!Object.keys(filterJson).includes(`${value}`)) {
return [
false,
t('views.ne.neConfig.requireEnum', { display }),
];
}
}
break;
case 'bool':
// filter: '{"0":"false", "1":"true"}'
if (filter && filter.indexOf('{') === 1) {
let filterJson: Record<string, any> = {};
try {
filterJson = JSON.parse(filter); //string---json
} catch (error) {
console.error(error);
}
if (!Object.values(filterJson).includes(`${value}`)) {
return [
false,
t('views.ne.neConfig.requireBool', { display }),
];
}
}
break;
case 'string':
// filter: "0~128"
// 字符串长度判断
if (filter && filter.indexOf('~') !== -1) {
try {
const filterArr = filter.split('~');
let rule = new RegExp(
'^\\S{' + filterArr[0] + ',' + filterArr[1] + '}$'
);
if (!rule.test(value)) {
return [
false,
t('views.ne.neConfig.requireString', {
display,
}),
];
}
} catch (error) {
console.error(error);
}
}
// 字符串http判断
if (value.startsWith('http')) {
try {
if (!validURL(value)) {
return [
false,
t('views.ne.neConfig.requireString', {
display,
}),
];
}
} catch (error) {
console.error(error);
}
}
break;
case 'regex':
// filter: "^[0-9]{3}$"
if (filter) {
try {
let regex = new RegExp(filter);
if (!regex.test(value)) {
return [
false,
t('views.ne.neConfig.requireString', {
display,
}),
];
}
} catch (error) {
console.error(error);
}
}
break;
default:
return [
false,
t('views.ne.neConfig.requireUn', { display }),
];
}
return result;
}
/**upfId可选择 */
const smfByUPFIdOptions = ref<{ value: string; label: string }[]>([]);
/**加载smf配置的upfId */
function smfByUPFIdLoadData(neId: string) {
getNeConfigData({
neType: 'SMF',
neId: neId,
paramName: 'upfConfig',
}).then(res => {
smfByUPFIdOptions.value = [];
for (const s of res.data) {
smfByUPFIdOptions.value.push({
value: s.id,
label: s.id,
});
}
});
}
return {
ruleVerification,
smfByUPFIdLoadData,
smfByUPFIdOptions,
};
}
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -113,7 +113,7 @@ let modalState: ModalStateType = reactive({
neAddress: '', neAddress: '',
dn: '', dn: '',
vendorName: '', vendorName: '',
province: 'Area', province: '',
remark: '', remark: '',
// 主机 // 主机
hosts: [ hosts: [
+8 -16
View File
@@ -4,7 +4,7 @@ import { ProModal } from 'antdv-pro-modal';
import { message, Form } from 'ant-design-vue/es'; import { message, Form } from 'ant-design-vue/es';
import useI18n from '@/hooks/useI18n'; import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { getOAMFile, saveOAMFile, serviceNeAction } from '@/api/ne/neInfo'; import { getOAMFile, saveOAMFile } from '@/api/ne/neInfo';
const { t } = useI18n(); const { t } = useI18n();
const emit = defineEmits(['ok', 'cancel', 'update:open']); const emit = defineEmits(['ok', 'cancel', 'update:open']);
const props = defineProps({ const props = defineProps({
@@ -29,8 +29,8 @@ type ModalStateType = {
openByEdit: boolean; openByEdit: boolean;
/**标题 */ /**标题 */
title: string; title: string;
/**是否重启 */ /**是否同步 */
restart: boolean; sync: boolean;
/**表单数据 */ /**表单数据 */
from: Record<string, any>; from: Record<string, any>;
/**确定按钮 loading */ /**确定按钮 loading */
@@ -41,7 +41,7 @@ type ModalStateType = {
let modalState: ModalStateType = reactive({ let modalState: ModalStateType = reactive({
openByEdit: false, openByEdit: false,
title: 'OAM Configuration', title: 'OAM Configuration',
restart: false, sync: true,
from: { from: {
omcIP: '', omcIP: '',
oamEnable: true, oamEnable: true,
@@ -114,19 +114,12 @@ function fnModalOk() {
neType: props.neType, neType: props.neType,
neId: props.neId, neId: props.neId,
content: from, content: from,
sync: true, sync: modalState.sync,
}) })
.then(res => { .then(res => {
if (res.code === RESULT_CODE_SUCCESS) { if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.operateOk'), 3); message.success(t('common.operateOk'), 3);
emit('ok'); emit('ok');
if (modalState.restart) {
serviceNeAction({
neType: props.neType,
neId: props.neId,
action: 'restart',
});
}
fnModalCancel(); fnModalCancel();
} else { } else {
message.error({ message.error({
@@ -152,7 +145,6 @@ function fnModalOk() {
function fnModalCancel() { function fnModalCancel() {
modalState.openByEdit = false; modalState.openByEdit = false;
modalState.confirmLoading = false; modalState.confirmLoading = false;
modalState.restart = false;
modalStateFrom.resetFields(); modalStateFrom.resetFields();
emit('cancel'); emit('cancel');
emit('update:open', false); emit('update:open', false);
@@ -191,15 +183,15 @@ watch(
:labelWrap="true" :labelWrap="true"
> >
<a-form-item <a-form-item
:label="t('views.ne.neInfo.oam.restart')" :label="t('views.ne.neInfo.oam.sync')"
name="restart" name="sync"
:label-col="{ span: 6 }" :label-col="{ span: 6 }"
:labelWrap="true" :labelWrap="true"
> >
<a-switch <a-switch
:checked-children="t('common.switch.open')" :checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')" :un-checked-children="t('common.switch.shut')"
v-model:checked="modalState.restart" v-model:checked="modalState.sync"
></a-switch> ></a-switch>
</a-form-item> </a-form-item>
+44 -41
View File
@@ -237,13 +237,8 @@ function fnModalEditOk(from: Record<string, any>) {
item.neName = from.neName; item.neName = from.neName;
item.ip = from.ip; item.ip = from.ip;
item.port = from.port; item.port = from.port;
if (res.data.online) { if (item.status !== '2') {
item.status = '1'; item.status = res.data.online ? '1' : '0';
if (res.data.standby) {
item.status = '3';
}
} else {
item.status = '0';
} }
Object.assign(item.serverState, res.data); Object.assign(item.serverState, res.data);
const resouresUsage = parseResouresUsage(item.serverState); const resouresUsage = parseResouresUsage(item.serverState);
@@ -362,38 +357,33 @@ function fnGetList(pageNum?: number) {
if (pageNum) { if (pageNum) {
queryParams.pageNum = pageNum; queryParams.pageNum = pageNum;
} }
listNeInfo(toRaw(queryParams)) listNeInfo(toRaw(queryParams)).then(res => {
.then(res => { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) { // 取消勾选
// 取消勾选 if (tableState.selectedRowKeys.length > 0) {
if (tableState.selectedRowKeys.length > 0) { tableState.selectedRowKeys = [];
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; tablePagination.total = res.total;
}) // 遍历处理资源情况数值
.finally(() => { tableState.data = res.rows.map(item => {
// 刷新缓存的网元信息 let resouresUsage = {
useNeInfoStore().fnRefreshNelist(); 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;
});
} }
/**解析网元状态携带的资源利用率 */ /**解析网元状态携带的资源利用率 */
@@ -451,8 +441,13 @@ onMounted(() => {
} }
}); });
// 获取列表数据 // 刷新缓存的网元信息
fnGetList(); useNeInfoStore()
.fnRefreshNelist()
.finally(() => {
// 获取列表数据
fnGetList();
});
}); });
</script> </script>
@@ -667,7 +662,15 @@ onMounted(() => {
</a-divider> </a-divider>
<div> <div>
<span>{{ t('views.ne.neInfo.serviceState') }}:</span> <span>{{ t('views.ne.neInfo.serviceState') }}:</span>
<DictTag :options="dict.neInfoStatus" :value="record.status" /> <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>
<div> <div>
<span>{{ t('views.ne.neVersion.version') }}:</span> <span>{{ t('views.ne.neVersion.version') }}:</span>
+6 -6
View File
@@ -105,7 +105,7 @@ let tableColumns = ref<TableColumnsType>([
title: t('views.ne.common.serialNum'), title: t('views.ne.common.serialNum'),
dataIndex: 'serialNum', dataIndex: 'serialNum',
align: 'left', align: 'left',
width: 120, width: 100,
}, },
{ {
title: t('views.ne.common.expiryDate'), title: t('views.ne.common.expiryDate'),
@@ -126,12 +126,12 @@ let tableColumns = ref<TableColumnsType>([
{ {
title: t('common.updateTime'), title: t('common.updateTime'),
dataIndex: 'updateTime', dataIndex: 'updateTime',
align: 'left', align: 'center',
customRender(opt) { customRender(opt) {
if (!opt.value) return ''; if (!opt.value) return '';
return parseDateToStr(opt.value); return parseDateToStr(opt.value);
}, },
width: 200, width: 150,
}, },
{ {
title: t('common.operate'), title: t('common.operate'),
@@ -413,7 +413,7 @@ onMounted(() => {
:loading="modalState.confirmLoading" :loading="modalState.confirmLoading"
@click.prevent="fnRecordStateBatch()" @click.prevent="fnRecordStateBatch()"
> >
<template #icon><SyncOutlined /></template> <template #icon><CloudSyncOutlined /></template>
{{ t('views.ne.neLicense.reloadBatch') }} {{ t('views.ne.neLicense.reloadBatch') }}
</a-button> </a-button>
</a-space> </a-space>
@@ -508,7 +508,7 @@ onMounted(() => {
<a-tooltip placement="topRight"> <a-tooltip placement="topRight">
<template #title>{{ t('views.ne.neLicense.reload') }}</template> <template #title>{{ t('views.ne.neLicense.reload') }}</template>
<a-button type="link" @click.prevent="fnRecordState(record)"> <a-button type="link" @click.prevent="fnRecordState(record)">
<template #icon><SyncOutlined /> </template> <template #icon><CloudSyncOutlined /> </template>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
<a-tooltip placement="topRight"> <a-tooltip placement="topRight">
@@ -517,7 +517,7 @@ onMounted(() => {
type="link" type="link"
@click.prevent="fnModalVisibleByEdit(record.id)" @click.prevent="fnModalVisibleByEdit(record.id)"
> >
<template #icon><UploadOutlined /> </template> <template #icon><CloudUploadOutlined /> </template>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
</a-space> </a-space>
@@ -54,7 +54,7 @@ let modalState: ModalStateType = reactive({
neAddress: '', neAddress: '',
dn: '-', dn: '-',
vendorName: '-', vendorName: '-',
province: 'Area', province: '-',
// 主机 // 主机
hosts: [ hosts: [
{ {
@@ -50,10 +50,9 @@ let fromState = ref({
ims_sip_ip: '192.168.8.110', ims_sip_ip: '192.168.8.110',
upf_type: 'LightUPF', upf_type: 'LightUPF',
upf_driver_type: 'vmxnet3', upf_driver_type: 'vmxnet3',
upfn3_card_name: 'eth0', upf_card_name: 'eth0',
upfn3_pci: '0000:00:00.0', upfn3_pci: '0000:00:00.0',
upfn3_mac: '00:00:00:00:00:00', upfn3_mac: '00:00:00:00:00:00',
upfn6_card_name: 'eth0',
upfn6_pci: '0000:00:00.0', upfn6_pci: '0000:00:00.0',
upfn6_mac: '00:00:00:00:00:00', upfn6_mac: '00:00:00:00:00:00',
}, },
@@ -244,12 +243,7 @@ watch(
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col <a-col :lg="12" :md="12" :xs="24">
:lg="12"
:md="12"
:xs="24"
v-if="fromState.external.upf_type === 'LightUPF'"
>
<a-form-item label="UE_POOL" name="external.ue_pool"> <a-form-item label="UE_POOL" name="external.ue_pool">
<a-input <a-input
v-model:value="fromState.external.ue_pool" v-model:value="fromState.external.ue_pool"
@@ -266,94 +260,47 @@ watch(
</a-input> </a-input>
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="N3_IP" name="external.upfn3_ip">
<a-input
v-model:value="fromState.external.upfn3_ip"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title> netwrok ip </template>
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
<a-form-item label="N3_GW" name="external.upfn3_gw">
<a-input
v-model:value="fromState.external.upfn3_gw"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title> geteway </template>
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
</a-col>
<a-col <a-col
:lg="12" :lg="12"
:md="12" :md="12"
:xs="24" :xs="24"
v-if="fromState.external.upf_type === 'StandardUPF'" v-if="fromState.external.upf_type === 'StandardUPF'"
> >
<a-form-item label="DRIVER_TYPE" name="external.upf_driver_type">
<a-select
v-model:value="fromState.external.upf_driver_type"
:placeholder="t('common.selectPlease')"
>
<a-select-option value="vmxnet3">vmxnet3</a-select-option>
<a-select-option value="dpdk">dpdk</a-select-option>
<a-select-option value="host">host</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row v-if="fromState.external.upf_type === 'LightUPF'">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="N3_IP" name="external.upfn3_ip">
<a-input
v-model:value="fromState.external.upfn3_ip"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title> netwrok ip </template>
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="N3_GW" name="external.upfn3_gw">
<a-input
v-model:value="fromState.external.upfn3_gw"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title> geteway </template>
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
</a-col>
</a-row>
<a-row v-if="fromState.external.upf_type === 'StandardUPF'">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="N3_IP" name="external.upfn3_ip">
<a-input
v-model:value="fromState.external.upfn3_ip"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title> netwrok ip </template>
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
<a-form-item label="N3_GW" name="external.upfn3_gw">
<a-input
v-model:value="fromState.external.upfn3_gw"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title> geteway </template>
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
<a-form-item label="N3_PCI" name="external.upfn3_pci"> <a-form-item label="N3_PCI" name="external.upfn3_pci">
<a-input <a-input
v-model:value="fromState.external.upfn3_pci" v-model:value="fromState.external.upfn3_pci"
@@ -391,86 +338,84 @@ watch(
</template> </template>
</a-input> </a-input>
</a-form-item> </a-form-item>
<a-form-item label="N3_NIC_NAME" name="external.upfn3_card_name">
<a-input
v-model:value="fromState.external.upfn3_card_name"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>
use `ip a` show info, to inet name
<br />
or <br />
use `lshw -class network -businfo` show device name
</template>
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="N6_IP" name="external.upfn6_ip">
<a-input
v-model:value="fromState.external.upfn6_ip"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
</a-input>
</a-form-item>
<a-form-item label="N6_GW" name="external.upfn6_gw">
<a-input
v-model:value="fromState.external.upfn6_gw"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
</a-input>
</a-form-item>
<a-form-item label="N6_PCI" name="external.upfn6_pci">
<a-input
v-model:value="fromState.external.upfn6_pci"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
</a-input>
</a-form-item>
<a-form-item label="N6_MAC" name="external.upfn6_mac">
<a-input
v-model:value="fromState.external.upfn6_mac"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
</a-input>
</a-form-item>
<a-form-item label="N6_NIC_NAME" name="external.upfn6_card_name">
<a-input
v-model:value="fromState.external.upfn6_card_name"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>
use `ip a` show info, to inet name
<br />
or <br />
use `lshw -class network -businfo` show device name
</template>
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
</a-col> </a-col>
</a-row> </a-row>
<template v-if="fromState.external.upf_type === 'StandardUPF'">
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="N6_IP" name="external.upfn6_ip">
<a-input
v-model:value="fromState.external.upfn6_ip"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
</a-input>
</a-form-item>
<a-form-item label="N6_GW" name="external.upfn6_gw">
<a-input
v-model:value="fromState.external.upfn6_gw"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
</a-input>
</a-form-item>
<a-form-item label="DRIVER_TYPE" name="external.upf_driver_type">
<a-select
v-model:value="fromState.external.upf_driver_type"
:placeholder="t('common.selectPlease')"
>
<a-select-option value="vmxnet3">vmxnet3</a-select-option>
<a-select-option value="dpdk">dpdk</a-select-option>
<a-select-option value="host">host</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="N6_PCI" name="external.upfn6_pci">
<a-input
v-model:value="fromState.external.upfn6_pci"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
</a-input>
</a-form-item>
<a-form-item label="N6_MAC" name="external.upfn6_mac">
<a-input
v-model:value="fromState.external.upfn6_mac"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
</a-input>
</a-form-item>
<a-form-item label="NIC_NAME" name="external.upf_card_name">
<a-input
v-model:value="fromState.external.upf_card_name"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>
use `ip a` show info, to inet name
<br />
or <br />
use `lshw -class network -businfo` show device name
</template>
<InfoCircleOutlined
style="opacity: 0.45; color: inherit"
/>
</a-tooltip>
</template>
</a-input>
</a-form-item>
</a-col>
</a-row>
</template>
</a-col> </a-col>
<a-col :lg="8" :md="8" :xs="24"> <a-col :lg="8" :md="8" :xs="24">
@@ -166,8 +166,8 @@ function fnBeforeUploadFile(file: FileType) {
const suff = fileName.substring(fileName.lastIndexOf('.')); const suff = fileName.substring(fileName.lastIndexOf('.'));
if (!['.deb', '.rpm'].includes(suff)) { if (!['.deb', '.rpm'].includes(suff)) {
message.error( message.error(
t('views.ne.neSoftware.fileTypeNotEq', { t('views.configManage.softwareManage.onlyAble', {
txt: '(.deb、.rpm)', fileText: '(.deb、.rpm)',
}), }),
3 3
); );
@@ -238,8 +238,8 @@ function fnBeforeUploadFileDep(file: FileType) {
const suff = fileName.substring(fileName.lastIndexOf('.')); const suff = fileName.substring(fileName.lastIndexOf('.'));
if (!['.deb', '.rpm'].includes(suff)) { if (!['.deb', '.rpm'].includes(suff)) {
message.error( message.error(
t('views.ne.neSoftware.fileTypeNotEq', { t('views.configManage.softwareManage.onlyAble', {
txt: '(.deb、.rpm)', fileText: '(.deb、.rpm)',
}), }),
3 3
); );
@@ -171,8 +171,8 @@ function fnBeforeUploadFile(file: FileType) {
const suff = fileName.substring(fileName.lastIndexOf('.')); const suff = fileName.substring(fileName.lastIndexOf('.'));
if (!['.deb', '.rpm'].includes(suff)) { if (!['.deb', '.rpm'].includes(suff)) {
message.error( message.error(
t('views.ne.neSoftware.fileTypeNotEq', { t('views.configManage.softwareManage.onlyAble', {
txt: '(.deb、.rpm)', fileText: '(.deb、.rpm)',
}), }),
3 3
); );
@@ -286,8 +286,8 @@ function fnBeforeUploadFileDep(file: FileType) {
const suff = fileName.substring(fileName.lastIndexOf('.')); const suff = fileName.substring(fileName.lastIndexOf('.'));
if (!['.deb', '.rpm'].includes(suff)) { if (!['.deb', '.rpm'].includes(suff)) {
message.error( message.error(
t('views.ne.neSoftware.fileTypeNotEq', { t('views.configManage.softwareManage.onlyAble', {
txt: '(.deb、.rpm)', fileText: '(.deb、.rpm)',
}), }),
3 3
); );
+2 -21
View File
@@ -120,7 +120,6 @@ let tableColumns = ref<TableColumnsType>([
{ {
title: t('views.ne.neVersion.preVersion'), title: t('views.ne.neVersion.preVersion'),
dataIndex: 'preVersion', dataIndex: 'preVersion',
key: 'preVersion',
align: 'left', align: 'left',
width: 150, width: 150,
resizable: true, resizable: true,
@@ -305,10 +304,6 @@ function fnRecordVersion(
message.warning(t('views.ne.neVersion.rollbackTipEmpty'), 3); message.warning(t('views.ne.neVersion.rollbackTipEmpty'), 3);
return; return;
} }
if (row.prePath === '' || row.prePath === '-') {
message.warning(t('views.ne.neVersion.noPath'), 3);
return;
}
if (row.preVersion === row.version) { if (row.preVersion === row.version) {
contentTip = t('views.ne.neVersion.rollbackTipEqual'); contentTip = t('views.ne.neVersion.rollbackTipEqual');
} }
@@ -527,7 +522,8 @@ onMounted(() => {
type="primary" type="primary"
:disabled="tableState.selectedRowKeys.length > 1" :disabled="tableState.selectedRowKeys.length > 1"
@click.prevent=" @click.prevent="
() => (modalState.openByMoreFile = !modalState.openByMoreFile) () =>
(modalState.openByMoreFile = !modalState.openByMoreFile)
" "
> >
<template #icon><UploadOutlined /></template> <template #icon><UploadOutlined /></template>
@@ -619,21 +615,6 @@ onMounted(() => {
<template v-if="column.key === 'status'"> <template v-if="column.key === 'status'">
<DictTag :options="dictStatus" :value="record.status" /> <DictTag :options="dictStatus" :value="record.status" />
</template> </template>
<template v-if="column.key === 'preVersion'">
{{ record.preVersion }}
<a-tooltip
placement="topRight"
v-if="
record.preVersion &&
(record.prePath === '' || record.prePath === '-')
"
>
<template #title>
{{ t('views.ne.neVersion.noPath') }}
</template>
<InfoCircleOutlined />
</a-tooltip>
</template>
<template v-if="column.key === 'id'"> <template v-if="column.key === 'id'">
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip placement="topRight"> <a-tooltip placement="topRight">
+38 -75
View File
@@ -25,7 +25,6 @@ import {
listUDMAuth, listUDMAuth,
} from '@/api/neData/udm_auth'; } from '@/api/neData/udm_auth';
import { uploadFile } from '@/api/tool/file'; import { uploadFile } from '@/api/tool/file';
import { getNeViewFile } from '@/api/tool/neFile';
const { t } = useI18n(); const { t } = useI18n();
/**网元参数 */ /**网元参数 */
@@ -99,6 +98,12 @@ let tableColumns = ref<ColumnsType>([
align: 'center', align: 'center',
width: 80, width: 80,
}, },
{
title: 'Status',
dataIndex: 'status',
align: 'center',
width: 80,
},
// { // {
// title: 'KI', // title: 'KI',
// dataIndex: 'ki', // dataIndex: 'ki',
@@ -593,8 +598,6 @@ type ModalUploadImportStateType = {
loading: boolean; loading: boolean;
/**上传结果信息 */ /**上传结果信息 */
msg: string; msg: string;
/**含失败信息 */
hasFail: boolean;
/**导入类型 */ /**导入类型 */
typeOptions: { label: string; value: string }[]; typeOptions: { label: string; value: string }[];
/**表单 */ /**表单 */
@@ -607,7 +610,6 @@ let uploadImportState: ModalUploadImportStateType = reactive({
title: t('components.UploadModal.uploadTitle'), title: t('components.UploadModal.uploadTitle'),
loading: false, loading: false,
msg: '', msg: '',
hasFail: false,
typeOptions: [ typeOptions: [
{ label: 'Default', value: 'default' }, { label: 'Default', value: 'default' },
{ label: 'K4', value: 'k4' }, { label: 'K4', value: 'k4' },
@@ -624,37 +626,9 @@ function fnModalUploadImportTypeChange() {
uploadImportState.msg = ''; uploadImportState.msg = '';
} }
/**对话框表格信息导入失败原因 */
function fnModalUploadImportFailReason() {
const neId = queryParams.neId;
if (!neId) return;
const hide = message.loading(t('common.loading'), 0);
getNeViewFile({
neType: 'UDM',
neId: neId,
path: '/tmp',
fileName: 'import_authdata_err_records.txt',
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.operateOk'), 3);
const blob = new Blob([res.data], {
type: 'text/plain',
});
saveAs(blob, `import_authdata_err_records_${Date.now()}.txt`);
} else {
message.error(`${res.msg}`, 3);
}
})
.finally(() => {
hide();
});
}
/**对话框表格信息导入弹出窗口 */ /**对话框表格信息导入弹出窗口 */
function fnModalUploadImportOpen() { function fnModalUploadImportOpen() {
uploadImportState.msg = ''; uploadImportState.msg = '';
uploadImportState.hasFail = false;
uploadImportState.from.typeVal = 'default'; uploadImportState.from.typeVal = 'default';
uploadImportState.from.typeData = undefined; uploadImportState.from.typeData = undefined;
uploadImportState.loading = false; uploadImportState.loading = false;
@@ -701,14 +675,6 @@ function fnModalUploadImportUpload(file: File) {
.then(res => { .then(res => {
if (!res) return; if (!res) return;
uploadImportState.msg = res.msg; uploadImportState.msg = res.msg;
const regex = /fail num: (\d+)/;
const match = res.msg.match(regex);
if (match) {
const failNum = Number(match[1]);
uploadImportState.hasFail = failNum > 0;
} else {
uploadImportState.hasFail = false;
}
}) })
.finally(() => { .finally(() => {
hide(); hide();
@@ -1007,7 +973,25 @@ onMounted(() => {
:label-col="{ span: 6 }" :label-col="{ span: 6 }"
:labelWrap="true" :labelWrap="true"
> >
<a-row v-show="!modalState.from.id"> <a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.neUser.auth.numAdd')"
name="num"
v-bind="modalStateFrom.validateInfos.num"
v-show="!modalState.from.id"
>
<a-input-number
v-model:value="modalState.from.num"
style="width: 100%"
:min="1"
:max="10000"
placeholder="<=10000"
></a-input-number>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
label="IMSI" label="IMSI"
@@ -1035,20 +1019,11 @@ onMounted(() => {
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item label="Status" name="status">
:label="t('views.neUser.auth.numAdd')" <a-select value="1">
name="num" <a-select-option value="1">Active</a-select-option>
v-bind="modalStateFrom.validateInfos.num" <a-select-option value="0">Inactive</a-select-option>
:label-col="{ span: 10 }" </a-select>
:labelWrap="false"
>
<a-input-number
v-model:value="modalState.from.num"
style="width: 100%"
:min="1"
:max="10000"
placeholder="<=10000"
></a-input-number>
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
@@ -1081,8 +1056,6 @@ onMounted(() => {
label="Algo Index" label="Algo Index"
name="algo" name="algo"
v-bind="modalStateFrom.validateInfos.algoIndex" v-bind="modalStateFrom.validateInfos.algoIndex"
:label-col="{ span: 10 }"
:labelWrap="false"
> >
<a-input-number <a-input-number
v-model:value="modalState.from.algoIndex" v-model:value="modalState.from.algoIndex"
@@ -1168,7 +1141,7 @@ onMounted(() => {
<a-form <a-form
name="modalStateBatchDelFrom" name="modalStateBatchDelFrom"
layout="horizontal" layout="horizontal"
:label-col="{ span: 8 }" :label-col="{ span: 6 }"
:labelWrap="true" :labelWrap="true"
> >
<a-row> <a-row>
@@ -1237,23 +1210,13 @@ onMounted(() => {
v-model:value="uploadImportState.from.typeData" v-model:value="uploadImportState.from.typeData"
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
/> />
<a-alert <a-textarea
:message="uploadImportState.msg" :disabled="true"
:type="uploadImportState.hasFail ? 'warning' : 'info'" :hidden="!uploadImportState.msg"
v-show="uploadImportState.msg.length > 0" :value="uploadImportState.msg"
> :auto-size="{ minRows: 2, maxRows: 8 }"
<template #action> style="background-color: transparent; color: rgba(0, 0, 0, 0.85)"
<a-button />
size="small"
type="link"
danger
@click="fnModalUploadImportFailReason"
v-if="uploadImportState.hasFail"
>
{{ t('views.neUser.auth.importFail') }}
</a-button>
</template>
</a-alert>
</template> </template>
</UploadModal> </UploadModal>
</PageContainer> </PageContainer>
-1
View File
@@ -314,7 +314,6 @@ onMounted(() => {
v-model:value="queryParams.neType" v-model:value="queryParams.neType"
:options="neCascaderOptions" :options="neCascaderOptions"
:placeholder="t('common.selectPlease')" :placeholder="t('common.selectPlease')"
@change="fnGetList(1)"
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
-356
View File
@@ -1,356 +0,0 @@
<script setup lang="ts">
import { reactive, ref, onMounted, toRaw } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { SizeType } from 'ant-design-vue/es/config-provider';
import { ColumnsType } from 'ant-design-vue/es/table';
import { Modal, message } from 'ant-design-vue/es';
import { parseDateToStr } from '@/utils/date-utils';
import {
getBakFile,
getBakFileList,
downFile,
delFile,
} from '@/api/neUser/exportFile';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useI18n from '@/hooks/useI18n';
import saveAs from 'file-saver';
const { t } = useI18n();
/**网元参数 */
let logSelect = ref<string[]>([]);
/**文件列表 */
let fileList = ref<any>([]);
/**查询参数 */
let queryParams = reactive({
/**读取路径 */
path: '',
/**表名 */
tableName: '',
/**当前页数 */
pageNum: 1,
/**每页条数 */
pageSize: 20,
});
/**表格状态类型 */
type TabeStateType = {
/**加载等待 */
loading: boolean;
/**紧凑型 */
size: SizeType;
/**记录数据 */
data: object[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
size: 'small',
data: [],
});
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('views.logManage.neFile.fileMode'),
dataIndex: 'fileMode',
align: 'center',
width: 150,
},
{
title: t('views.logManage.neFile.owner'),
dataIndex: 'owner',
align: 'left',
width: 100,
},
{
title: t('views.logManage.neFile.group'),
dataIndex: 'group',
align: 'left',
width: 100,
},
{
title: t('views.logManage.neFile.size'),
dataIndex: 'size',
align: 'left',
width: 100,
},
{
title: t('views.logManage.neFile.modifiedTime'),
dataIndex: 'modifiedTime',
align: 'left',
customRender(opt) {
if (!opt.value) return '';
return parseDateToStr(opt.value * 1000);
},
width: 150,
},
{
title: t('views.logManage.neFile.fileName'),
dataIndex: 'fileName',
align: 'left',
},
{
title: t('common.operate'),
key: 'fileName',
align: 'center',
width: 100,
},
];
/**表格分页器参数 */
let tablePagination = reactive({
/**当前页数 */
current: 1,
/**每页条数 */
pageSize: 20,
/**默认的每页条数 */
defaultPageSize: 20,
/**指定每页可以显示多少条 */
pageSizeOptions: ['10', '20', '50', '100'],
/**只有一页时是否隐藏分页器 */
hideOnSinglePage: false,
/**是否可以快速跳转至某页 */
showQuickJumper: true,
/**是否可以改变 pageSize */
showSizeChanger: true,
/**数据总数 */
total: 0,
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
onChange: (page: number, pageSize: number) => {
tablePagination.current = page;
tablePagination.pageSize = pageSize;
queryParams.pageNum = page;
queryParams.pageSize = pageSize;
fnGetList();
},
});
/**下载触发等待 */
let downLoading = ref<boolean>(false);
/**删除触发等待 */
let delLoading = ref<boolean>(false);
/**信息文件下载 */
function fnDownloadFile(row: Record<string, any>) {
if (downLoading.value) return;
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.logManage.exportFile.downTip', {
fileName: row.fileName,
}),
onOk() {
downLoading.value = true;
const hide = message.loading(t('common.loading'), 0);
downFile({
path: queryParams.path,
fileName: row.fileName,
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', {
msg: t('common.downloadText'),
}),
duration: 2,
});
saveAs(res.data, `${row.fileName}`);
} else {
message.error({
content: t('views.logManage.exportFile.downTipErr'),
duration: 2,
});
}
})
.finally(() => {
hide();
downLoading.value = false;
});
},
});
}
function fnRecordDelete(row: Record<string, any>) {
if (delLoading.value) return;
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.logManage.exportFile.deleteTip', {
fileName: row.fileName,
}),
onOk() {
const key = 'delFile';
delLoading.value = true;
message.loading({ content: t('common.loading'), key });
delFile({
fileName: row.fileName,
path: queryParams.path,
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.system.user.delSuss'),
key,
duration: 2,
});
fnGetList();
} else {
message.error({
content: `${res.msg}`,
key: key,
duration: 2,
});
}
})
.finally(() => {
delLoading.value = false;
});
},
});
}
/**网元类型选择对应修改 */
function fnNeChange(keys: any, opt: any) {
queryParams.tableName = keys;
queryParams.path = opt.path;
fnGetList(1);
}
/**查询备份信息列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (queryParams.tableName === '') {
message.warning({
content: t('views.logManage.exportFile.selectTip'),
duration: 2,
});
return;
}
if (tableState.loading) return;
tableState.loading = true;
if (pageNum) {
queryParams.pageNum = pageNum;
}
getBakFileList(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
tablePagination.total = res.total;
tableState.data = res.data;
if (
tablePagination.total <=
(queryParams.pageNum - 1) * tablePagination.pageSize &&
queryParams.pageNum !== 1
) {
tableState.loading = false;
fnGetList(queryParams.pageNum - 1);
}
} else {
message.error(res.msg, 3);
tablePagination.total = 0;
tableState.data = [];
}
tableState.loading = false;
});
}
onMounted(() => {
getBakFile().then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
res.data.forEach((item: any) => {
fileList.value.push({
value: item.tableName,
label: item.tableDisplay,
path: item.filePath,
});
});
}
});
// .finally(() => {
// fnGetList();
// });
});
</script>
<template>
<PageContainer>
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16" align="middle">
<a-col>
<span>{{ t('views.logManage.exportFile.fileName') }}:</span>&nbsp;
<a-select
v-model:value="logSelect"
:options="fileList"
@change="fnNeChange"
:allow-clear="false"
style="width: 200px"
/>
</a-col>
<template v-if="queryParams.path">
<span>{{ t('views.logManage.neFile.nePath') }}:</span>&nbsp;
<a-col>
<a-breadcrumb>
<a-breadcrumb-item>
{{ queryParams.path }}
</a-breadcrumb-item>
</a-breadcrumb>
</a-col>
</template>
</a-row>
</a-form>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
<template #icon><ReloadOutlined /></template>
</a-button>
</a-tooltip>
</a-space>
</template>
<!-- 表格列表 -->
<a-table
class="table"
row-key="fileName"
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: 800 }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'fileName'">
<a-space :size="8" align="center">
<a-button
type="link"
:loading="downLoading"
@click.prevent="fnDownloadFile(record)"
v-if="record.fileType === 'file'"
>
<template #icon><DownloadOutlined /></template>
</a-button>
<a-button
type="link"
:loading="delLoading"
@click.prevent="fnRecordDelete(record)"
v-if="record.fileType === 'file'"
>
<template #icon><DeleteOutlined /></template>
</a-button>
</a-space>
</template>
</template>
</a-table>
</a-card>
</PageContainer>
</template>
<style lang="less" scoped></style>
+24 -107
View File
@@ -25,7 +25,6 @@ import {
updateUDMSub, updateUDMSub,
} from '@/api/neData/udm_sub'; } from '@/api/neData/udm_sub';
import { uploadFile } from '@/api/tool/file'; import { uploadFile } from '@/api/tool/file';
import { getNeViewFile } from '@/api/tool/neFile';
const { t } = useI18n(); const { t } = useI18n();
/**网元参数 */ /**网元参数 */
@@ -277,7 +276,7 @@ let modalState: ModalStateType = reactive({
// //
smData: '', smData: '',
smfSel: 'def_snssai', smfSel: 'def_snssai',
cag: '', cag: 'def_cag',
// 非字段 // 非字段
num: 1, num: 1,
remark: '', remark: '',
@@ -545,22 +544,8 @@ function transformFormData(data: any) {
smStaticIpArr.push(dnnParts); smStaticIpArr.push(dnnParts);
} }
if (!dnnParts.includes('/') && !dnnParts.includes(':')) { if (!dnnParts.includes('/') && !dnnParts.includes(':')) {
const pattern = /^(\d{1,3}\.){3}\d{1,3}$/; //IPV4 没有/ 也没有:
if (pattern.test(dnnParts)) { smStaticIpArr.push(dnnParts);
// 验证数值范围
const isValid = dnnParts.split('.').every((num: any) => {
const n = parseInt(num, 10);
return n >= 0 && n <= 255;
});
// 只有当验证通过时才添加 IP
if (isValid) {
smStaticIpArr.push(dnnParts);
}
} else {
//无/ 无:也有可能为dnn的字符串
smallRowJson.dnn += '-' + dnnParts;
}
} }
if (dnnParts.includes('/') && !dnnParts.includes(':')) { if (dnnParts.includes('/') && !dnnParts.includes(':')) {
@@ -973,8 +958,6 @@ type ModalUploadImportStateType = {
loading: boolean; loading: boolean;
/**上传结果信息 */ /**上传结果信息 */
msg: string; msg: string;
/**含失败信息 */
hasFail: boolean;
}; };
/**对话框表格信息导入对象信息状态 */ /**对话框表格信息导入对象信息状态 */
@@ -983,40 +966,11 @@ let uploadImportState: ModalUploadImportStateType = reactive({
title: t('components.UploadModal.uploadTitle'), title: t('components.UploadModal.uploadTitle'),
loading: false, loading: false,
msg: '', msg: '',
hasFail: false,
}); });
/**对话框表格信息导入失败原因 */
function fnModalUploadImportFailReason() {
const neId = queryParams.neId;
if (!neId) return;
const hide = message.loading(t('common.loading'), 0);
getNeViewFile({
neType: 'UDM',
neId: neId,
path: '/tmp',
fileName: 'import_udmuser_err_records.txt',
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.operateOk'), 3);
const blob = new Blob([res.data], {
type: 'text/plain',
});
saveAs(blob, `import_udmuser_err_records_${Date.now()}.txt`);
} else {
message.error(`${res.msg}`, 3);
}
})
.finally(() => {
hide();
});
}
/**对话框表格信息导入弹出窗口 */ /**对话框表格信息导入弹出窗口 */
function fnModalUploadImportOpen() { function fnModalUploadImportOpen() {
uploadImportState.msg = ''; uploadImportState.msg = '';
uploadImportState.hasFail = false;
uploadImportState.loading = false; uploadImportState.loading = false;
uploadImportState.open = true; uploadImportState.open = true;
} }
@@ -1060,14 +1014,6 @@ function fnModalUploadImportUpload(file: File) {
.then(res => { .then(res => {
if (!res) return; if (!res) return;
uploadImportState.msg = res.msg; uploadImportState.msg = res.msg;
const regex = /fail num: (\d+)/;
const match = res.msg.match(regex);
if (match) {
const failNum = Number(match[1]);
uploadImportState.hasFail = failNum > 0;
} else {
uploadImportState.hasFail = false;
}
}) })
.finally(() => { .finally(() => {
hide(); hide();
@@ -1276,9 +1222,7 @@ onMounted(() => {
:loading="modalState.loadDataLoading" :loading="modalState.loadDataLoading"
@click.prevent="fnRecordDelete('0')" @click.prevent="fnRecordDelete('0')"
> >
<template #icon> <template #icon><DeleteOutlined /></template>
<DeleteOutlined />
</template>
{{ t('views.neUser.sub.checkDel') }} {{ t('views.neUser.sub.checkDel') }}
</a-button> </a-button>
@@ -1294,9 +1238,7 @@ onMounted(() => {
type="default" type="default"
:disabled="tableState.selectedRowKeys.length <= 0" :disabled="tableState.selectedRowKeys.length <= 0"
> >
<template #icon> <template #icon><ExportOutlined /></template>
<ExportOutlined />
</template>
{{ t('views.neUser.sub.checkExport') }} {{ t('views.neUser.sub.checkExport') }}
</a-button> </a-button>
</a-popconfirm> </a-popconfirm>
@@ -1318,24 +1260,19 @@ onMounted(() => {
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.reloadText') }}</template> <template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()"> <a-button type="text" @click.prevent="fnGetList()">
<template #icon> <template #icon><ReloadOutlined /></template>
<ReloadOutlined />
</template>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
<TableColumnsDnd <TableColumnsDnd
cache-id="udmSubData" cache-id="udmSubData"
:columns="tableColumns" :columns="tableColumns"
v-model:columns-dnd="tableColumnsDnd" v-model:columns-dnd="tableColumnsDnd"
> ></TableColumnsDnd>
</TableColumnsDnd>
<a-tooltip placement="topRight"> <a-tooltip placement="topRight">
<template #title>{{ t('common.sizeText') }}</template> <template #title>{{ t('common.sizeText') }}</template>
<a-dropdown placement="bottomRight" trigger="click"> <a-dropdown placement="bottomRight" trigger="click">
<a-button type="text"> <a-button type="text">
<template #icon> <template #icon><ColumnHeightOutlined /></template>
<ColumnHeightOutlined />
</template>
</a-button> </a-button>
<template #overlay> <template #overlay>
<a-menu <a-menu
@@ -1369,7 +1306,7 @@ onMounted(() => {
:pagination="tablePagination" :pagination="tablePagination"
:scroll="{ y: 'calc(100vh - 480px)' }" :scroll="{ y: 'calc(100vh - 480px)' }"
@change="fnTableChange" @change="fnTableChange"
@resizeColumn="(w: number, col: any) => (col.width = w)" @resizeColumn="(w:number, col:any) => (col.width = w)"
:row-selection="{ :row-selection="{
type: 'checkbox', type: 'checkbox',
selectedRowKeys: tableState.selectedRowKeys, selectedRowKeys: tableState.selectedRowKeys,
@@ -1443,13 +1380,12 @@ onMounted(() => {
:labelWrap="true" :labelWrap="true"
> >
<a-row> <a-row>
<a-col :lg="12" :md="12" :xs="24" v-show="!modalState.from.id"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
:label="t('views.neUser.sub.numAdd')" :label="t('views.neUser.sub.numAdd')"
name="num" name="num"
v-bind="modalStateFrom.validateInfos.num" v-bind="modalStateFrom.validateInfos.num"
:label-col="{ span: 12 }" v-show="!modalState.from.id"
:labelWrap="false"
> >
<a-input-number <a-input-number
v-model:value="modalState.from.num" v-model:value="modalState.from.num"
@@ -1568,11 +1504,7 @@ onMounted(() => {
<a-row> <a-row>
<a-col :span="4"> <a-col :span="4">
<a-tooltip title="Add DNN"> <a-tooltip title="Add DNN">
<a-button <a-button shape="circle" @click="addSmallRow(row.id)">
shape="circle"
@click="addSmallRow(row.id)"
style="margin-left: 10px"
>
<template #icon><plus-square-outlined /></template> <template #icon><plus-square-outlined /></template>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
@@ -1594,7 +1526,7 @@ onMounted(() => {
:key="String(smallRow.id)" :key="String(smallRow.id)"
> >
<a-row> <a-row>
<a-col :lg="6" :md="6" :xs="24"> <a-col :lg="6" :md="7" :xs="24">
<a-form-item <a-form-item
label="DNN/APN" label="DNN/APN"
name="dnn" name="dnn"
@@ -1605,7 +1537,7 @@ onMounted(() => {
<a-input v-model:value="smallRow.dnn" allow-clear></a-input> <a-input v-model:value="smallRow.dnn" allow-clear></a-input>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="8" :md="8" :xs="24"> <a-col :lg="8" :md="6" :xs="24">
<a-form-item <a-form-item
label="Static IP" label="Static IP"
name="smStaticIp" name="smStaticIp"
@@ -1618,21 +1550,16 @@ onMounted(() => {
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="8" :md="8" :xs="24"> <a-col :lg="8" :md="8" :xs="24">
<a-form-item <a-form-item label="Routing Behind MS IP" name="msIp">
label="Routing Behind MS IP"
style="margin-left: 10px"
name="msIp"
>
<a-input v-model:value="smallRow.msIp" allow-clear></a-input> <a-input v-model:value="smallRow.msIp" allow-clear></a-input>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="2" :md="2" :xs="24" v-if="smallIndex !== 0"> <a-col :lg="1" :md="1" :xs="24" v-if="smallIndex !== 0">
<a-tooltip title="Delete DNN"> <a-tooltip title="Delete DNN">
<a-button <a-button
danger danger
shape="circle" shape="circle"
@click="delDNN(smallIndex, row.id)" @click="delDNN(smallIndex, row.id)"
style="margin-left: 10px"
> >
<template #icon><close-square-outlined /></template> <template #icon><close-square-outlined /></template>
</a-button> </a-button>
@@ -2048,7 +1975,7 @@ onMounted(() => {
<a-form <a-form
name="modalStateBatchDelFrom" name="modalStateBatchDelFrom"
layout="horizontal" layout="horizontal"
:label-col="{ span: 8 }" :label-col="{ span: 6 }"
:labelWrap="true" :labelWrap="true"
> >
<a-row> <a-row>
@@ -2107,23 +2034,13 @@ onMounted(() => {
:size="10" :size="10"
> >
<template #default> <template #default>
<a-alert <a-textarea
:message="uploadImportState.msg" :disabled="true"
:type="uploadImportState.hasFail ? 'warning' : 'info'" :hidden="!uploadImportState.msg"
v-show="uploadImportState.msg.length > 0" :value="uploadImportState.msg"
> :auto-size="{ minRows: 2, maxRows: 8 }"
<template #action> style="background-color: transparent; color: rgba(0, 0, 0, 0.85)"
<a-button />
size="small"
type="link"
danger
@click="fnModalUploadImportFailReason"
v-if="uploadImportState.hasFail"
>
{{ t('views.neUser.auth.importFail') }}
</a-button>
</template>
</a-alert>
</template> </template>
</UploadModal> </UploadModal>
</PageContainer> </PageContainer>
+2 -2
View File
@@ -6,7 +6,7 @@ import { message } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider'; import { SizeType } from 'ant-design-vue/es/config-provider';
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface'; import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import { ColumnsType } from 'ant-design-vue/es/table'; import { ColumnsType } from 'ant-design-vue/es/table';
import { listSMFSubList } from '@/api/neData/smf'; import { listSMFSubscribers } from '@/api/neData/smf';
import useNeInfoStore from '@/store/modules/neinfo'; import useNeInfoStore from '@/store/modules/neinfo';
import useI18n from '@/hooks/useI18n'; import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
@@ -224,7 +224,7 @@ function fnGetList(pageNum?: number) {
if (pageNum) { if (pageNum) {
queryParams.pageNum = pageNum; queryParams.pageNum = pageNum;
} }
listSMFSubList(toRaw(queryParams)).then(res => { listSMFSubscribers(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
tablePagination.total = res.total; tablePagination.total = res.total;
tableState.data = res.rows; tableState.data = res.rows;
+6 -8
View File
@@ -154,9 +154,7 @@ function fnTableSize({ key }: MenuInfo) {
function fnRecordDelete(row: Record<string, any>) { function fnRecordDelete(row: Record<string, any>) {
Modal.confirm({ Modal.confirm({
title: t('common.tipTitle'), title: t('common.tipTitle'),
content: t('views.perfManage.customTarget.delCustomTip', { content: t('views.perfManage.customTarget.delCustomTip', { num: row.kpiId }),
num: row.kpiId,
}),
onOk() { onOk() {
const key = 'delThreshold'; const key = 'delThreshold';
message.loading({ content: t('common.loading'), key }); message.loading({ content: t('common.loading'), key });
@@ -387,10 +385,8 @@ function fnModalOk() {
message.error(t('views.perfManage.customTarget.expressionNoIdTip'), 3); message.error(t('views.perfManage.customTarget.expressionNoIdTip'), 3);
return false; return false;
} }
//modalState.from.expression = result; modalState.from.expression = result;
//const from = toRaw(modalState.from); const from = toRaw(modalState.from);
const from = { ...toRaw(modalState.from) };
from.expression = result;
//return false; //return false;
modalState.confirmLoading = true; modalState.confirmLoading = true;
const perfTask = from.id ? updateCustom(from) : addCustom(from); const perfTask = from.id ? updateCustom(from) : addCustom(from);
@@ -438,6 +434,7 @@ function fnModalCancel() {
* 选择性能指标,填充进当前计算公式的值 * 选择性能指标,填充进当前计算公式的值
*/ */
function fnSelectPer(s: any, option: any) { function fnSelectPer(s: any, option: any) {
console.log(option);
modalState.from.expression += `'${option.label}'`; modalState.from.expression += `'${option.label}'`;
} }
@@ -698,6 +695,7 @@ onMounted(() => {
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
<a-row> <a-row>
<a-col :lg="24" :md="24" :xs="24"> <a-col :lg="24" :md="24" :xs="24">
<a-form-item <a-form-item
@@ -726,7 +724,6 @@ onMounted(() => {
> >
<a-input <a-input
v-model:value="modalState.from.expression" v-model:value="modalState.from.expression"
:disabled="modalState.from.id"
:maxlength="1024" :maxlength="1024"
autocomplete="off" autocomplete="off"
allow-clear allow-clear
@@ -783,6 +780,7 @@ onMounted(() => {
}, },
]" ]"
> >
</a-auto-complete> </a-auto-complete>
</a-form-item> </a-form-item>
</a-col> </a-col>
+34 -224
View File
@@ -23,7 +23,6 @@ import {
nextTick, nextTick,
onBeforeUnmount, onBeforeUnmount,
h, h,
watch,
} from 'vue'; } from 'vue';
import { PageContainer } from 'antdv-pro-layout'; import { PageContainer } from 'antdv-pro-layout';
import { message, Modal, TableColumnType } from 'ant-design-vue/es'; import { message, Modal, TableColumnType } from 'ant-design-vue/es';
@@ -44,8 +43,6 @@ import { generateColorRGBA } from '@/utils/generate-utils';
import { OptionsType, WS } from '@/plugins/ws-websocket'; import { OptionsType, WS } from '@/plugins/ws-websocket';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { LineOutlined } from '@ant-design/icons-vue'; import { LineOutlined } from '@ant-design/icons-vue';
import useLayoutStore from '@/store/modules/layout';
const layoutStore = useLayoutStore();
const neInfoStore = useNeInfoStore(); const neInfoStore = useNeInfoStore();
const route = useRoute(); const route = useRoute();
const { t, currentLocale } = useI18n(); const { t, currentLocale } = useI18n();
@@ -219,25 +216,12 @@ const statsColumns: TableColumnType<any>[] = [
key: 'title', key: 'title',
width: '65%', width: '65%',
}, },
{
title: t('views.perfManage.kpiOverView.totalValue'),
dataIndex: 'total',
key: 'total',
width: '24%',
sortDirections: ['ascend', 'descend'],
},
{
title: t('views.perfManage.kpiOverView.avgValue'),
dataIndex: 'avg',
key: 'avg',
width: '24%',
sortDirections: ['ascend', 'descend'],
},
{ {
title: t('views.perfManage.kpiOverView.maxValue'), title: t('views.perfManage.kpiOverView.maxValue'),
dataIndex: 'max', dataIndex: 'max',
key: 'max', key: 'max',
width: '17%', width: '17%',
sorter: (a: any, b: any) => a.max - b.max, // 添加排序函数
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
{ {
@@ -245,6 +229,7 @@ const statsColumns: TableColumnType<any>[] = [
dataIndex: 'min', dataIndex: 'min',
key: 'min', key: 'min',
width: '17%', width: '17%',
sorter: (a: any, b: any) => a.min - b.min, // 添加排序函数
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
]; ];
@@ -404,23 +389,11 @@ function fnGetList() {
const values = tableState.data.map((item: any) => { const values = tableState.data.map((item: any) => {
return item[columns.key] ? Number(item[columns.key]) : 0; return item[columns.key] ? Number(item[columns.key]) : 0;
}); });
// 计算总值
const total = Number(
values.reduce((sum, val) => sum + val, 0).toFixed(2)
);
// 计算平均值
const avg =
values.length > 0 ? Number((total / values.length).toFixed(2)) : 0;
kpiStats.value.push({ kpiStats.value.push({
kpiId: columns.key, kpiId: columns.key,
title: columns.title, title: columns.title,
max: values.length > 0 ? Math.max(...values) : 0, max: values.length > 0 ? Math.max(...values) : 0,
min: values.length > 0 ? Math.min(...values) : 0, min: values.length > 0 ? Math.min(...values) : 0,
avg: avg,
total: total,
}); });
} }
} }
@@ -445,7 +418,6 @@ function fnRanderChart() {
position: function (pt: any) { position: function (pt: any) {
return [pt[0], '10%']; return [pt[0], '10%'];
}, },
confine: true, // 限制 tooltip 显示范围
}, },
xAxis: { xAxis: {
//x类别轴 //x类别轴
@@ -528,9 +500,6 @@ function fnRanderChartData() {
key: `${columns.key}`, key: `${columns.key}`,
type: 'line', type: 'line',
symbol: 'none', symbol: 'none',
symbolSize: 6,
smooth: 0.6,
showSymbol: true,
sampling: 'lttb', sampling: 'lttb',
itemStyle: { itemStyle: {
color: color, color: color,
@@ -580,34 +549,8 @@ function fnRanderChartData() {
xAxis: { xAxis: {
type: 'category', type: 'category',
boundaryGap: false, boundaryGap: false,
axisLabel: {
color:
document.documentElement.getAttribute('data-theme') === 'dark'
? '#CACADA'
: '#333',
},
splitLine: {
show: true,
lineStyle: {
color: getSplitLineColor(),
},
},
data: chartDataXAxisData, data: chartDataXAxisData,
}, },
yAxis: {
axisLabel: {
color:
document.documentElement.getAttribute('data-theme') === 'dark'
? '#CACADA'
: '#333',
},
splitLine: {
show: true,
lineStyle: {
color: getSplitLineColor(),
},
},
},
series: chartDataYSeriesData, series: chartDataYSeriesData,
}, },
{ {
@@ -704,38 +647,29 @@ function wsMessage(res: Record<string, any>) {
} }
// 添加一个变量来跟踪当前选中的行 // 添加一个变量来跟踪当前选中的行
const selectedRow = ref<string[]>([]); const selectedRow = ref<string | null>(null);
// 添加处理行点击的方法 // 添加处理行点击的方法
function handleRowClick(record: any) { function handleRowClick(record: any) {
const index = selectedRow.value.indexOf(record.kpiId); if (selectedRow.value === record.kpiId) {
// 如果点击的是当前选中的行,则取消选中
// 如果已经选中,取消选中 selectedRow.value = null;
if (index > -1) { // 更新图表,显示所有指标
selectedRow.value.splice(index, 1); for (let key in chartLegendSelected) {
chartLegendSelected[record.title] = false;
} else {
// 添加新的选中项
selectedRow.value.push(record.kpiId);
// 如果只有一个选中项,重置为 false
if (selectedRow.value.length === 1) {
Object.keys(chartLegendSelected).forEach(key => {
chartLegendSelected[key] = false;
});
}
chartLegendSelected[record.title] = true;
}
// 如果没有选中项,设置所有图例为 true
if (selectedRow.value.length === 0) {
Object.keys(chartLegendSelected).forEach(key => {
chartLegendSelected[key] = true; chartLegendSelected[key] = true;
}); }
} else {
// 选中新行
selectedRow.value = record.kpiId;
// 更新图表,只显示选中的指标
for (let key in chartLegendSelected) {
if (key === record.title) {
chartLegendSelected[key] = true;
} else {
chartLegendSelected[key] = false;
}
}
} }
// 更新图表设置
kpiChart.value.setOption({ kpiChart.value.setOption({
legend: { legend: {
selected: chartLegendSelected, selected: chartLegendSelected,
@@ -743,63 +677,6 @@ function handleRowClick(record: any) {
}); });
} }
// 添加一个函数来获取当前主题下的网格线颜色
function getSplitLineColor() {
return document.documentElement.getAttribute('data-theme') === 'dark'
? '#333333'
: '#E8E8E8'; // 亮色模式返回 undefined,使用默认颜色
}
// 监听主题变化
watch(
() => layoutStore.proConfig.theme, // 监听的值
newValue => {
if (kpiChart.value) {
const splitLineColor = getSplitLineColor();
// 绘制图数据
kpiChart.value.setOption({
tooltip: {
trigger: 'axis',
position: function (pt: any) {
return [pt[0], '10%'];
},
confine: true, // 限制 tooltip 显示范围
backgroundColor:
newValue === 'dark'
? 'rgba(48, 48, 48, 0.8)'
: 'rgba(255, 255, 255, 0.9)',
borderColor: newValue === 'dark' ? '#555' : '#ddd',
textStyle: {
color: newValue === 'dark' ? '#CACADA' : '#333',
},
},
xAxis: {
axisLabel: {
color: newValue === 'dark' ? '#CACADA' : '#333',
},
splitLine: {
show: true,
lineStyle: {
color: splitLineColor,
},
},
},
yAxis: {
axisLabel: {
color: newValue === 'dark' ? '#CACADA' : '#333',
},
splitLine: {
show: true,
lineStyle: {
color: splitLineColor,
},
},
},
});
}
}
);
onMounted(() => { onMounted(() => {
// 目前支持的 AMF AUSF MME MOCNGW NSSF SMF UDM UPF PCF // 目前支持的 AMF AUSF MME MOCNGW NSSF SMF UDM UPF PCF
// 获取网元网元列表 // 获取网元网元列表
@@ -837,16 +714,11 @@ onMounted(() => {
} }
// 查询当前小时 // 查询当前小时
const now = new Date(); const nowDate: Date = new Date();
now.setMinutes(0, 0, 0); nowDate.setMinutes(0, 0, 0);
// 设置起始时间为整点前一小时 queryRangePicker.value[0] = `${nowDate.getTime()}`;
const startTime = new Date(now); nowDate.setMinutes(59, 59, 59);
startTime.setHours(now.getHours() - 1); queryRangePicker.value[1] = `${nowDate.getTime()}`;
queryRangePicker.value[0] = `${startTime.getTime()}`;
// 设置结束时间为整点
const endTime = new Date(now);
endTime.setMinutes(59, 59, 59);
queryRangePicker.value[1] = `${endTime.getTime()}`;
fnGetListTitle(); fnGetListTitle();
// 绘图 // 绘图
fnRanderChart(); fnRanderChart();
@@ -928,9 +800,7 @@ onBeforeUnmount(() => {
:loading="tableState.loading" :loading="tableState.loading"
@click.prevent="fnGetListTitle()" @click.prevent="fnGetListTitle()"
> >
<template #icon> <template #icon><SearchOutlined /></template>
<SearchOutlined />
</template>
{{ t('common.search') }} {{ t('common.search') }}
</a-button> </a-button>
</a-space> </a-space>
@@ -949,9 +819,7 @@ onBeforeUnmount(() => {
:loading="tableState.loading" :loading="tableState.loading"
@click.prevent="fnChangShowType()" @click.prevent="fnChangShowType()"
> >
<template #icon> <template #icon> <AreaChartOutlined /> </template>
<AreaChartOutlined />
</template>
{{ {{
tableState.showTable tableState.showTable
? t('views.perfManage.goldTarget.kpiChartTitle') ? t('views.perfManage.goldTarget.kpiChartTitle')
@@ -978,13 +846,12 @@ onBeforeUnmount(() => {
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.reloadText') }}</template> <template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()"> <a-button type="text" @click.prevent="fnGetList()">
<template #icon> <template #icon><ReloadOutlined /></template>
<ReloadOutlined />
</template>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
<TableColumnsDnd <TableColumnsDnd
v-if="tableColumns.length > 0" v-if="tableColumns.length > 0"
:cache-id="`kpiTarget_${state.neType[0]}`"
:columns="tableColumns" :columns="tableColumns"
v-model:columns-dnd="tableColumnsDnd" v-model:columns-dnd="tableColumnsDnd"
></TableColumnsDnd> ></TableColumnsDnd>
@@ -992,9 +859,7 @@ onBeforeUnmount(() => {
<template #title>{{ t('common.sizeText') }}</template> <template #title>{{ t('common.sizeText') }}</template>
<a-dropdown trigger="click" placement="bottomRight"> <a-dropdown trigger="click" placement="bottomRight">
<a-button type="text"> <a-button type="text">
<template #icon> <template #icon><ColumnHeightOutlined /></template>
<ColumnHeightOutlined />
</template>
</a-button> </a-button>
<template #overlay> <template #overlay>
<a-menu <a-menu
@@ -1056,7 +921,7 @@ onBeforeUnmount(() => {
:size="tableState.size" :size="tableState.size"
:pagination="tablePagination" :pagination="tablePagination"
:scroll="{ x: tableColumnsDnd.length * 200, y: 'calc(100vh - 480px)' }" :scroll="{ x: tableColumnsDnd.length * 200, y: 'calc(100vh - 480px)' }"
@resizeColumn="(w: number, col: any) => (col.width = w)" @resizeColumn="(w:number, col:any) => (col.width = w)"
:show-expand-column="false" :show-expand-column="false"
@change="fnTableChange" @change="fnTableChange"
> >
@@ -1083,67 +948,12 @@ onBeforeUnmount(() => {
:scroll="{ y: 250 }" :scroll="{ y: 250 }"
size="small" size="small"
:custom-row=" :custom-row="
(record:any) => ({ record => ({
onClick: () => handleRowClick(record), onClick: () => handleRowClick(record),
class: selectedRow.includes(record.kpiId) ? 'selected-row' : '', class: record.kpiId === selectedRow ? 'selected-row' : '',
}) })
" "
> />
<template #headerCell="{ column }">
<template v-if="column.key === 'total'">
<span>
{{ t('views.perfManage.kpiOverView.totalValue') }}
<a-tooltip placement="bottom">
<template #title>
<span>
{{ t('views.perfManage.kpiOverView.totalValueTip') }}
</span>
</template>
<InfoCircleOutlined />
</a-tooltip>
</span>
</template>
<template v-if="column.key === 'avg'">
<span>
{{ t('views.perfManage.kpiOverView.avgValue') }}
<a-tooltip placement="bottom">
<template #title>
<span>
{{ t('views.perfManage.kpiOverView.avgValueTip') }}
</span>
</template>
<InfoCircleOutlined />
</a-tooltip>
</span>
</template>
<template v-if="column.key === 'max'">
<span>
{{ t('views.perfManage.kpiOverView.maxValue') }}
<a-tooltip placement="bottom">
<template #title>
<span>
{{ t('views.perfManage.kpiOverView.maxValueTip') }}
</span>
</template>
<InfoCircleOutlined />
</a-tooltip>
</span>
</template>
<template v-if="column.key === 'min'">
<span>
{{ t('views.perfManage.kpiOverView.minValue') }}
<a-tooltip placement="bottom">
<template #title>
<span>
{{ t('views.perfManage.kpiOverView.minValueTip') }}
</span>
</template>
<InfoCircleOutlined />
</a-tooltip>
</span>
</template>
</template>
</a-table>
</div> </div>
</div> </div>
</a-card> </a-card>
+93 -361
View File
@@ -23,7 +23,6 @@ import {
nextTick, nextTick,
onBeforeUnmount, onBeforeUnmount,
h, h,
watch,
} from 'vue'; } from 'vue';
import { PageContainer } from 'antdv-pro-layout'; import { PageContainer } from 'antdv-pro-layout';
import { message, Modal, TableColumnType } from 'ant-design-vue/es'; import { message, Modal, TableColumnType } from 'ant-design-vue/es';
@@ -46,8 +45,6 @@ import { OptionsType, WS } from '@/plugins/ws-websocket';
import { LineOutlined } from '@ant-design/icons-vue'; import { LineOutlined } from '@ant-design/icons-vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import dayjs, { Dayjs } from 'dayjs'; import dayjs, { Dayjs } from 'dayjs';
import useLayoutStore from '@/store/modules/layout';
const layoutStore = useLayoutStore();
const neInfoStore = useNeInfoStore(); const neInfoStore = useNeInfoStore();
const route = useRoute(); const route = useRoute();
const { t, currentLocale } = useI18n(); const { t, currentLocale } = useI18n();
@@ -85,19 +82,12 @@ let queryRangePicker = ref<[string, string]>(['', '']);
/**时间选择 */ /**时间选择 */
const ranges = ref([ const ranges = ref([
{ {label:t('views.perfManage.customTarget.sixHoursAgo'),value:[dayjs().subtract(6, 'hours'),
label: t('views.perfManage.customTarget.sixHoursAgo'), dayjs(),]},
value: [dayjs().subtract(6, 'hours'), dayjs()], {label:t('views.perfManage.customTarget.threeHoursAgo'),value:[dayjs().subtract(3, 'hours'),
}, dayjs(),]},
{ {label:t('views.monitor.monitor.today'),value:[dayjs().startOf('day'), dayjs()]},
label: t('views.perfManage.customTarget.threeHoursAgo'), ])
value: [dayjs().subtract(3, 'hours'), dayjs()],
},
{
label: t('views.monitor.monitor.today'),
value: [dayjs().startOf('day'), dayjs()],
},
]);
/**表格字段列 */ /**表格字段列 */
let tableColumns = ref<any[]>([]); let tableColumns = ref<any[]>([]);
@@ -213,13 +203,6 @@ const kpiColors = new Map<string, string>();
//legend表格数据 //legend表格数据
const kpiStats: any = ref([]); const kpiStats: any = ref([]);
// 添加一个函数来获取当前主题下的网格线颜色
function getSplitLineColor() {
return document.documentElement.getAttribute('data-theme') === 'dark'
? '#333333'
: '#E8E8E8'; // 亮色模式返回 undefined,使用默认颜色
}
// 添加表格列定义 // 添加表格列定义
const statsColumns: TableColumnType<any>[] = [ const statsColumns: TableColumnType<any>[] = [
{ {
@@ -240,33 +223,22 @@ const statsColumns: TableColumnType<any>[] = [
title: t('views.perfManage.kpiOverView.kpiName'), title: t('views.perfManage.kpiOverView.kpiName'),
dataIndex: 'title', dataIndex: 'title',
key: 'title', key: 'title',
width: '65%',
}, },
// {
// title: t('views.perfManage.kpiOverView.totalValue'),
// dataIndex: 'total',
// key: 'total',
// width: '12%',
// sortDirections: ['ascend', 'descend'],
// },
// {
// title: t('views.perfManage.kpiOverView.avgValue'),
// dataIndex: 'avg',
// key: 'avg',
// width: '24%',
// sortDirections: ['ascend', 'descend'],
// },
{ {
title: t('views.perfManage.kpiOverView.maxValue'), title: t('views.perfManage.kpiOverView.maxValue'),
dataIndex: 'max', dataIndex: 'max',
key: 'max', key: 'max',
width: '200px', width: '17%',
sorter: (a: any, b: any) => a.max - b.max, // 添加排序函数
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
{ {
title: t('views.perfManage.kpiOverView.minValue'), title: t('views.perfManage.kpiOverView.minValue'),
dataIndex: 'min', dataIndex: 'min',
key: 'min', key: 'min',
width: '200px', width: '17%',
sorter: (a: any, b: any) => a.min - b.min, // 添加排序函数
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
}, },
]; ];
@@ -332,8 +304,6 @@ function fnGetListTitle() {
var language = currentLocale.value.split('_')[0]; var language = currentLocale.value.split('_')[0];
if (language === 'zh') language = 'cn'; if (language === 'zh') language = 'cn';
if (!state.neType[0]) return false;
// 获取表头文字 // 获取表头文字
listCustom({ neType: state.neType[0], status: 'Active' }) listCustom({ neType: state.neType[0], status: 'Active' })
.then(res => { .then(res => {
@@ -346,7 +316,6 @@ function fnGetListTitle() {
tableState.data = []; tableState.data = [];
tableColumns.value = []; tableColumns.value = [];
tableColumnsDnd.value = []; tableColumnsDnd.value = [];
kpiStats.value = []; //清空数据
fnRanderChartData(); fnRanderChartData();
return false; return false;
} }
@@ -362,7 +331,6 @@ function fnGetListTitle() {
dataIndex: kpiValue, dataIndex: kpiValue,
align: 'left', align: 'left',
key: kpiValue, key: kpiValue,
unit: item[`unit`],
resizable: true, resizable: true,
width: 100, width: 100,
minWidth: 150, minWidth: 150,
@@ -422,11 +390,6 @@ function fnGetList() {
content: t('common.noData'), content: t('common.noData'),
duration: 2, duration: 2,
}); });
tableState.data = [];
// tableColumns.value = [];
// tableColumnsDnd.value = [];
kpiStats.value = []; //清空数据
fnRanderChartData();
return false; return false;
} }
return true; return true;
@@ -450,42 +413,11 @@ function fnGetList() {
const values = tableState.data.map((item: any) => { const values = tableState.data.map((item: any) => {
return item[columns.key] ? Number(item[columns.key]) : 0; return item[columns.key] ? Number(item[columns.key]) : 0;
}); });
// 计算总值
const total = Number(
values.reduce((sum, val) => sum + val, 0).toFixed(2)
);
// 计算平均值
const avg =
values.length > 0 ? Number((total / values.length).toFixed(2)) : 0;
kpiStats.value.push({ kpiStats.value.push({
kpiId: columns.key, kpiId: columns.key,
title: columns.title, title: columns.title,
unit: columns.unit,
max: values.length > 0 ? Math.max(...values) : 0, max: values.length > 0 ? Math.max(...values) : 0,
min: values.length > 0 ? Math.min(...values) : 0, min: values.length > 0 ? Math.min(...values) : 0,
avg,
total: total,
});
}
} else {
kpiStats.value = [];
for (const columns of tableColumns.value) {
if (
columns.key === 'neName' ||
columns.key === 'startIndex' ||
columns.key === 'timeGroup'
) {
continue;
}
kpiStats.value.push({
kpiId: columns.key,
title: columns.title,
unit: columns.unit,
max: 0,
min: 0,
avg: 0,
total: 0,
}); });
} }
} }
@@ -509,21 +441,6 @@ function fnRanderChart() {
position: function (pt: any) { position: function (pt: any) {
return [pt[0], '10%']; return [pt[0], '10%'];
}, },
confine: true, // 限制 tooltip 显示范围
backgroundColor:
document.documentElement.getAttribute('data-theme') === 'dark'
? 'rgba(48, 48, 48, 0.8)'
: 'rgba(255, 255, 255, 0.9)',
borderColor:
document.documentElement.getAttribute('data-theme') === 'dark'
? '#555'
: '#ddd',
textStyle: {
color:
document.documentElement.getAttribute('data-theme') === 'dark'
? '#CACADA'
: '#333',
},
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
@@ -602,9 +519,6 @@ function fnRanderChartData() {
key: `${columns.key}`, key: `${columns.key}`,
type: 'line', type: 'line',
symbol: 'none', symbol: 'none',
symbolSize: 6,
smooth: 0.6,
showSymbol: true,
sampling: 'lttb', sampling: 'lttb',
itemStyle: { itemStyle: {
color: color, color: color,
@@ -634,7 +548,6 @@ function fnRanderChartData() {
for (const item of orgData) { for (const item of orgData) {
const keys = Object.keys(item); const keys = Object.keys(item);
//console.log(keys,item);
for (const y of chartDataYSeriesData) { for (const y of chartDataYSeriesData) {
for (const key of keys) { for (const key of keys) {
if (y.key === key) { if (y.key === key) {
@@ -644,10 +557,8 @@ function fnRanderChartData() {
} }
} }
} }
chartDataXAxisData = Array.from(new Set(chartDataXAxisData)); ///console.log(queryParams.sortOrder, chartDataXAxisData);
console.log(chartDataXAxisData, chartDataYSeriesData);
//console.log(queryParams.sortOrder, chartDataXAxisData);
//console.log(chartDataXAxisData, chartDataYSeriesData);
// 绘制图数据 // 绘制图数据
kpiChart.value.setOption( kpiChart.value.setOption(
@@ -658,34 +569,8 @@ function fnRanderChartData() {
xAxis: { xAxis: {
type: 'category', type: 'category',
boundaryGap: false, boundaryGap: false,
axisLabel: {
color:
document.documentElement.getAttribute('data-theme') === 'dark'
? '#CACADA'
: '#333',
},
splitLine: {
show: true,
lineStyle: {
color: getSplitLineColor(),
},
},
data: chartDataXAxisData, data: chartDataXAxisData,
}, },
yAxis: {
axisLabel: {
color:
document.documentElement.getAttribute('data-theme') === 'dark'
? '#CACADA'
: '#333',
},
splitLine: {
show: true,
lineStyle: {
color: getSplitLineColor(),
},
},
},
series: chartDataYSeriesData, series: chartDataYSeriesData,
}, },
{ {
@@ -694,6 +579,18 @@ function fnRanderChartData() {
); );
} }
/**图表折线显示全部 */
function fnLegendSelected(bool: any) {
for (const key of Object.keys(chartLegendSelected)) {
chartLegendSelected[key] = bool;
}
kpiChart.value.setOption({
legend: {
selected: chartLegendSelected,
},
});
}
/**图表实时统计 */ /**图表实时统计 */
function fnRealTimeSwitch(bool: any) { function fnRealTimeSwitch(bool: any) {
if (bool) { if (bool) {
@@ -770,54 +667,29 @@ function wsMessage(res: Record<string, any>) {
} }
// 添加一个变量来跟踪当前选中的行 // 添加一个变量来跟踪当前选中的行
const selectedRow = ref<string[]>([]); const selectedRow = ref<string | null>(null);
const selectedUnit = ref<string | null>(null);
// 添加处理行点击的方法 // 添加处理行点击的方法
function handleRowClick(record: any) { function handleRowClick(record: any) {
const index = selectedRow.value.indexOf(record.kpiId); if (selectedRow.value === record.kpiId) {
// 如果已经选中,取消选中 // 如果点击的是当前选中的行,则取消选中
if (index > -1) { selectedRow.value = null;
selectedRow.value.splice(index, 1); // 更新图表,显示所有指标
chartLegendSelected[record.title] = false; for (let key in chartLegendSelected) {
// 如果取消选中的是最后一个,重置 selectedUnit chartLegendSelected[key] = true;
if (selectedRow.value.length === 0) {
selectedUnit.value = null;
} }
} else { } else {
// 检查单位是否一致 // 选中新行
if (selectedUnit.value && selectedUnit.value !== record.unit) { selectedRow.value = record.kpiId;
message.error( // 更新图表,只显示选中的指标
`${t('views.perfManage.customTarget.unitSelect')} ${selectedUnit.value}` for (let key in chartLegendSelected) {
); if (key === record.title) {
return; chartLegendSelected[key] = true;
} } else {
// 添加新的选中项
selectedRow.value.push(record.kpiId);
// 设置选中的单位
if (!selectedUnit.value) {
selectedUnit.value = record.unit;
}
// 如果只有一个选中项,重置为 false
if (selectedRow.value.length === 1) {
Object.keys(chartLegendSelected).forEach(key => {
chartLegendSelected[key] = false; chartLegendSelected[key] = false;
}); }
} }
chartLegendSelected[record.title] = true;
} }
// 如果没有选中项,设置所有图例为 true
if (selectedRow.value.length === 0) {
Object.keys(chartLegendSelected).forEach(key => {
chartLegendSelected[key] = true;
});
}
// 更新图表设置
kpiChart.value.setOption({ kpiChart.value.setOption({
legend: { legend: {
selected: chartLegendSelected, selected: chartLegendSelected,
@@ -825,127 +697,57 @@ function handleRowClick(record: any) {
}); });
} }
// 监听主题变化
watch(
() => layoutStore.proConfig.theme, // 监听的值
newValue => {
if (kpiChart.value) {
const splitLineColor = getSplitLineColor();
// 绘制图数据
kpiChart.value.setOption({
tooltip: {
trigger: 'axis',
position: function (pt: any) {
return [pt[0], '10%'];
},
confine: true, // 限制 tooltip 显示范围
backgroundColor:
newValue === 'dark'
? 'rgba(48, 48, 48, 0.8)'
: 'rgba(255, 255, 255, 0.9)',
borderColor: newValue === 'dark' ? '#555' : '#ddd',
textStyle: {
color: newValue === 'dark' ? '#CACADA' : '#333',
},
},
xAxis: {
axisLabel: {
color: newValue === 'dark' ? '#CACADA' : '#333',
},
splitLine: {
show: true,
lineStyle: {
color: splitLineColor,
},
},
},
yAxis: {
axisLabel: {
color: newValue === 'dark' ? '#CACADA' : '#333',
},
splitLine: {
show: true,
lineStyle: {
color: splitLineColor,
},
},
},
});
}
}
);
onMounted(() => { onMounted(() => {
// 目前支持的 AMF AUSF MME MOCNGW NSSF SMF UDM UPF PCF // 目前支持的 AMF AUSF MME MOCNGW NSSF SMF UDM UPF PCF
// 获取网元网元列表 // 获取网元网元列表
listCustom({ status: 'Active' }).then((res: any) => { neInfoStore.fnNelist().then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (!res.data.length) { if (res.data.length > 0) {
message.warning({ // 过滤不可用的网元
content: '无可用的自定义指标,请先添加自定义指标', neCascaderOptions.value = neInfoStore.getNeCascaderOptions.filter(
duration: 2, (item: any) => {
}); return !['OMC', 'NSSF', 'NEF', 'NRF', 'LMF', 'N3IWF'].includes(
return false; item.value
}
let typeArr: any = [];
res.data.forEach((item: any) => {
typeArr.push(item.neType);
});
typeArr = Array.from(new Set(typeArr));
neInfoStore.fnNelist().then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
// 过滤不可用的网元
neCascaderOptions.value = neInfoStore.getNeCascaderOptions.filter(
(item: any) => {
return typeArr.includes(item.value);
}
); );
if (neCascaderOptions.value.length === 0) {
message.warning({
content: t('common.noData'),
duration: 2,
});
return;
}
// 无查询参数neType时 默认选择UPF
const queryNeType = (route.query.neType as string) || 'IMS';
const item = neCascaderOptions.value.find(
s => s.value === queryNeType
);
if (item && item.children) {
const info = item.children[0];
state.neType = [info.neType, info.neId];
queryParams.neType = info.neType;
queryParams.neId = info.neId;
} else {
const info = neCascaderOptions.value[0].children[0];
state.neType = [info.neType, info.neId];
queryParams.neType = info.neType;
queryParams.neId = info.neId;
}
// 查询当前小时
const now = new Date();
now.setMinutes(0, 0, 0);
// 设置起始时间为整点前一小时
const startTime = new Date(now);
startTime.setHours(now.getHours() - 1);
queryRangePicker.value[0] = `${startTime.getTime()}`;
// 设置结束时间为整点
const endTime = new Date(now);
endTime.setMinutes(59, 59, 59);
queryRangePicker.value[1] = `${endTime.getTime()}`;
fnGetListTitle();
// 绘图
fnRanderChart();
} }
} else { );
if (neCascaderOptions.value.length === 0) {
message.warning({ message.warning({
content: t('common.noData'), content: t('common.noData'),
duration: 2, duration: 2,
}); });
return;
} }
// 无查询参数neType时 默认选择UPF
const queryNeType = (route.query.neType as string) || 'UPF';
const item = neCascaderOptions.value.find(s => s.value === queryNeType);
if (item && item.children) {
const info = item.children[0];
state.neType = [info.neType, info.neId];
queryParams.neType = info.neType;
queryParams.neId = info.neId;
} else {
const info = neCascaderOptions.value[0].children[0];
state.neType = [info.neType, info.neId];
queryParams.neType = info.neType;
queryParams.neId = info.neId;
}
// 查询当前小时
const now = new Date();
now.setMinutes(0, 0, 0);
queryRangePicker.value[0] = `${now.getTime()}`;
now.setMinutes(59, 59, 59);
queryRangePicker.value[1] = `${now.getTime()}`;
fnGetListTitle();
// 绘图
fnRanderChart();
}
} else {
message.warning({
content: t('common.noData'),
duration: 2,
}); });
} }
}); });
@@ -972,7 +774,6 @@ onBeforeUnmount(() => {
v-model:value="state.neType" v-model:value="state.neType"
:options="neCascaderOptions" :options="neCascaderOptions"
:allow-clear="false" :allow-clear="false"
@change="fnGetListTitle()"
:placeholder="t('common.selectPlease')" :placeholder="t('common.selectPlease')"
/> />
</a-form-item> </a-form-item>
@@ -1003,9 +804,7 @@ onBeforeUnmount(() => {
:loading="tableState.loading" :loading="tableState.loading"
@click.prevent="fnGetListTitle()" @click.prevent="fnGetListTitle()"
> >
<template #icon> <template #icon><SearchOutlined /></template>
<SearchOutlined />
</template>
{{ t('common.search') }} {{ t('common.search') }}
</a-button> </a-button>
</a-space> </a-space>
@@ -1024,9 +823,7 @@ onBeforeUnmount(() => {
:loading="tableState.loading" :loading="tableState.loading"
@click.prevent="fnChangShowType()" @click.prevent="fnChangShowType()"
> >
<template #icon> <template #icon> <AreaChartOutlined /> </template>
<AreaChartOutlined />
</template>
{{ {{
tableState.showTable tableState.showTable
? t('views.perfManage.goldTarget.kpiChartTitle') ? t('views.perfManage.goldTarget.kpiChartTitle')
@@ -1053,13 +850,12 @@ onBeforeUnmount(() => {
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.reloadText') }}</template> <template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()"> <a-button type="text" @click.prevent="fnGetList()">
<template #icon> <template #icon><ReloadOutlined /></template>
<ReloadOutlined />
</template>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
<TableColumnsDnd <TableColumnsDnd
v-if="tableColumns.length > 0" v-if="tableColumns.length > 0"
:cache-id="`kpiTarget_${state.neType[0]}`"
:columns="tableColumns" :columns="tableColumns"
v-model:columns-dnd="tableColumnsDnd" v-model:columns-dnd="tableColumnsDnd"
></TableColumnsDnd> ></TableColumnsDnd>
@@ -1067,9 +863,7 @@ onBeforeUnmount(() => {
<template #title>{{ t('common.sizeText') }}</template> <template #title>{{ t('common.sizeText') }}</template>
<a-dropdown trigger="click" placement="bottomRight"> <a-dropdown trigger="click" placement="bottomRight">
<a-button type="text"> <a-button type="text">
<template #icon> <template #icon><ColumnHeightOutlined /></template>
<ColumnHeightOutlined />
</template>
</a-button> </a-button>
<template #overlay> <template #overlay>
<a-menu <a-menu
@@ -1131,7 +925,7 @@ onBeforeUnmount(() => {
:size="tableState.size" :size="tableState.size"
:pagination="tablePagination" :pagination="tablePagination"
:scroll="{ x: tableColumnsDnd.length * 200, y: 'calc(100vh - 480px)' }" :scroll="{ x: tableColumnsDnd.length * 200, y: 'calc(100vh - 480px)' }"
@resizeColumn="(w: number, col: any) => (col.width = w)" @resizeColumn="(w:number, col:any) => (col.width = w)"
:show-expand-column="false" :show-expand-column="false"
@change="fnTableChange" @change="fnTableChange"
> >
@@ -1152,75 +946,13 @@ onBeforeUnmount(() => {
:pagination="false" :pagination="false"
:scroll="{ y: 250 }" :scroll="{ y: 250 }"
size="small" size="small"
:custom-row="(record:any) => ({ :custom-row="
onClick: () => handleRowClick(record), record => ({
class: selectedRow.includes(record.kpiId) ? 'selected-row' : '', onClick: () => handleRowClick(record),
}) class: record.kpiId === selectedRow ? 'selected-row' : '',
" })
> "
<template #headerCell="{ column }"> />
<template v-if="column.key === 'total'">
<span>
{{ t('views.perfManage.kpiOverView.totalValue') }}
<a-tooltip placement="bottom">
<template #title>
<span>
{{ t('views.perfManage.kpiOverView.totalValueTip') }}
</span>
</template>
<InfoCircleOutlined />
</a-tooltip>
</span>
</template>
<template v-if="column.key === 'avg'">
<span>
{{ t('views.perfManage.kpiOverView.avgValue') }}
<a-tooltip placement="bottom">
<template #title>
<span>
{{ t('views.perfManage.kpiOverView.avgValueTip') }}
</span>
</template>
<InfoCircleOutlined />
</a-tooltip>
</span>
</template>
<template v-if="column.key === 'max'">
<span>
{{ t('views.perfManage.kpiOverView.maxValue') }}
<a-tooltip placement="bottom">
<template #title>
<span>
{{ t('views.perfManage.kpiOverView.maxValueTip') }}
</span>
</template>
<InfoCircleOutlined />
</a-tooltip>
</span>
</template>
<template v-if="column.key === 'min'">
<span>
{{ t('views.perfManage.kpiOverView.minValue') }}
<a-tooltip placement="bottom">
<template #title>
<span>
{{ t('views.perfManage.kpiOverView.minValueTip') }}
</span>
</template>
<InfoCircleOutlined />
</a-tooltip>
</span>
</template>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'avg'">
<span v-if="record.unit !== '%'">-</span>
</template>
<template v-if="column.key === 'total'">
<span v-if="record.unit == '%'">-</span>
</template>
</template>
</a-table>
</div> </div>
</div> </div>
</a-card> </a-card>
File diff suppressed because it is too large. Load diff
Loaded 100 of 122 files, more files were not shown because too many files have changed in this diff. Show more