Merge remote-tracking branch 'origin/main' into multi-tenant
This commit is contained in:
@@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network OMC"
|
|||||||
VITE_APP_CODE = "OMC"
|
VITE_APP_CODE = "OMC"
|
||||||
|
|
||||||
# 应用版本
|
# 应用版本
|
||||||
VITE_APP_VERSION = "2.241220"
|
VITE_APP_VERSION = "2.240103"
|
||||||
|
|
||||||
# 接口基础URL地址-不带/后缀
|
# 接口基础URL地址-不带/后缀
|
||||||
VITE_API_BASE_URL = "/omc-api"
|
VITE_API_BASE_URL = "/omc-api"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network OMC"
|
|||||||
VITE_APP_CODE = "OMC"
|
VITE_APP_CODE = "OMC"
|
||||||
|
|
||||||
# 应用版本
|
# 应用版本
|
||||||
VITE_APP_VERSION = "2.241220"
|
VITE_APP_VERSION = "2.240103"
|
||||||
|
|
||||||
# 接口基础URL地址-不带/后缀
|
# 接口基础URL地址-不带/后缀
|
||||||
VITE_API_BASE_URL = "/omc-api"
|
VITE_API_BASE_URL = "/omc-api"
|
||||||
|
|||||||
16
package.json
16
package.json
@@ -13,13 +13,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons-vue": "^7.0.1",
|
"@ant-design/icons-vue": "^7.0.1",
|
||||||
"@antv/g6": "~4.8.24",
|
"@antv/g6": "4.8.24",
|
||||||
"@codemirror/lang-javascript": "^6.2.2",
|
"@codemirror/lang-javascript": "^6.2.2",
|
||||||
"@codemirror/lang-yaml": "^6.1.2",
|
"@codemirror/lang-yaml": "^6.1.2",
|
||||||
"@codemirror/merge": "^6.7.4",
|
"@codemirror/merge": "^6.8.0",
|
||||||
"@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.3",
|
||||||
"@vueuse/core": "^12.0.0",
|
"@vueuse/core": "^12.3.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.6",
|
||||||
@@ -28,10 +28,10 @@
|
|||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"dayjs": "^1.11.11",
|
"dayjs": "^1.11.11",
|
||||||
"echarts": "~5.5.0",
|
"echarts": "~5.6.0",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"grid-layout-plus": "^1.0.6",
|
"grid-layout-plus": "^1.0.6",
|
||||||
"intl-tel-input": "^25.2.0",
|
"intl-tel-input": "~25.2.0",
|
||||||
"js-base64": "^3.7.7",
|
"js-base64": "^3.7.7",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
"p-queue": "~8.0.1",
|
"p-queue": "~8.0.1",
|
||||||
"pinia": "^2.3.0",
|
"pinia": "^2.3.0",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-i18n": "^10.0.5",
|
"vue-i18n": "^11.0.1",
|
||||||
"vue-router": "^4.5.0",
|
"vue-router": "^4.5.0",
|
||||||
"vue3-smooth-dnd": "^0.0.6",
|
"vue3-smooth-dnd": "^0.0.6",
|
||||||
"xlsx": "~0.18.5"
|
"xlsx": "~0.18.5"
|
||||||
@@ -53,8 +53,8 @@
|
|||||||
"@vitejs/plugin-vue": "^5.2.1",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
"less": "^4.2.1",
|
"less": "^4.2.1",
|
||||||
"typescript": "~5.6.3",
|
"typescript": "~5.6.3",
|
||||||
"unplugin-vue-components": "^0.27.5",
|
"unplugin-vue-components": "^0.28.0",
|
||||||
"vite": "^6.0.3",
|
"vite": "^6.0.6",
|
||||||
"vite-plugin-compression": "~0.5.1",
|
"vite-plugin-compression": "~0.5.1",
|
||||||
"vue-tsc": "~2.1.10"
|
"vue-tsc": "~2.1.10"
|
||||||
}
|
}
|
||||||
|
|||||||
28
public/svg/base4G.svg
Normal file
28
public/svg/base4G.svg
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<!-- Created with Method Draw - http://github.com/duopixel/Method-Draw/ -->
|
||||||
|
|
||||||
|
<g>
|
||||||
|
<title>background</title>
|
||||||
|
<rect fill="none" id="canvas_background" height="1026" width="1026" y="-1" x="-1"/>
|
||||||
|
<g display="none" overflow="visible" y="0" x="0" height="100%" width="100%" id="canvasGrid">
|
||||||
|
<rect fill="url(#gridpattern)" stroke-width="0" y="1" x="1" height="768" width="1024"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<title>Layer 1</title>
|
||||||
|
<g stroke="null" id="svg_15">
|
||||||
|
<path stroke="null" id="svg_4" fill="#B5D6FB" d="m512.094844,961.632039c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-405.588164,-251.575028c-2.275921,-1.419037 -3.698372,-4.054392 -3.698372,-6.892467l0,-90.007504c0,-2.838074 1.422451,-5.473429 3.698372,-6.892467l405.588164,-255.426701c1.137961,-0.709519 2.465582,-1.114958 3.793202,-1.114958s2.655242,0.405439 3.793202,1.114958l405.493334,255.426701c2.275921,1.419037 3.698372,4.054392 3.698372,6.892467l0,90.007504c0,2.838074 -1.422451,5.473429 -3.698372,6.892467l-405.588164,251.575028c-1.137961,0.709519 -2.465582,1.114958 -3.698372,1.114958z"/>
|
||||||
|
<path stroke="null" id="svg_5" fill="#0276F7" d="m512.094844,356.615382l405.398504,255.426701l0,90.007504l-66.096551,40.94936l-339.301952,210.625668l-339.491613,-210.625668l-66.096551,-40.94936l0,-90.007504l405.588164,-255.426701m0,-16.014849c-2.655242,0 -5.215653,0.709519 -7.586405,2.229916l-405.588164,255.426701c-4.551843,2.838074 -7.396745,8.108784 -7.396745,13.784933l0,90.007504c0,5.676149 2.844902,10.946859 7.491575,13.886293l66.096551,41.05072l339.491613,210.625668c2.275921,1.419037 4.931163,2.128556 7.491575,2.128556s5.215653,-0.709519 7.491575,-2.128556l339.301952,-210.625668l66.096551,-40.94936c4.646673,-2.838074 7.491575,-8.108784 7.491575,-13.886293l0,-90.007504c0,-5.676149 -2.844902,-10.946859 -7.396745,-13.784933l-405.398504,-255.426701c-2.370751,-1.520397 -5.025993,-2.331275 -7.586405,-2.331275z"/>
|
||||||
|
<path stroke="null" id="svg_6" fill="#FFFFFF" d="m106.50668,612.042083l405.493334,253.298145l405.493334,-253.298145l-405.398504,-255.426701l-405.588164,255.426701z"/>
|
||||||
|
<path stroke="null" id="svg_7" fill="#D4E4FC" d="m501.473877,64.192353l-254.9032,498.487506l263.343075,161.162085l266.662127,-162.074323l-275.102002,-497.575268z"/>
|
||||||
|
<path stroke="null" id="svg_8" fill="#0276F7" d="m229.975417,602.311542c-1.232791,0 -2.465582,-0.304079 -3.698372,-1.013598c-3.603542,-2.128556 -4.931163,-6.993826 -2.844902,-10.845499l279.653845,-532.13896c1.327621,-2.533995 3.793202,-4.054392 6.543274,-4.054392c2.655242,0 5.120823,1.520397 6.543274,4.054392l284.395348,532.13896c2.086261,3.851672 0.75864,8.716943 -2.750072,10.946859c-3.603542,2.128556 -8.155385,0.810878 -10.241646,-2.939434l-277.852074,-519.874424l-273.205401,519.671704c-1.422451,2.635355 -3.982862,4.054392 -6.543274,4.054392z"/>
|
||||||
|
<path stroke="null" id="svg_9" fill="#0276F7" d="m509.913752,755.567562c-4.172523,0 -7.491575,-3.547593 -7.491575,-8.007424l0,-666.744777c0,-4.459831 3.319052,-8.007424 7.491575,-8.007424s7.491575,3.547593 7.491575,8.007424l0,666.846137c0,4.358471 -3.413882,7.906065 -7.491575,7.906065z"/>
|
||||||
|
<path stroke="null" id="svg_10" fill="#0276F7" d="m509.913752,731.849369c-1.327621,0 -2.560412,-0.405439 -3.698372,-1.013598l-263.343075,-161.162085c-3.603542,-2.229916 -4.836333,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.939434l259.549873,158.83081l262.963755,-159.844408c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-266.662127,162.074323c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598z"/>
|
||||||
|
<path stroke="null" id="svg_11" fill="#0276F7" d="m509.913752,579.708306c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-201.988026,-125.686154c-3.603542,-2.229916 -4.741503,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.838074l198.289654,123.354879l201.798366,-122.138561c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.845499l-205.496739,124.469837c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598zm-2.465582,-157.513132c-1.232791,0 -2.370751,-0.304079 -3.508712,-0.912238l-140.917468,-79.668804c-3.698372,-2.128556 -5.025993,-6.892467 -3.129392,-10.845499c1.896601,-3.953032 6.448444,-5.37207 10.146816,-3.344873l137.503586,77.742968l143.00373,-79.871524c3.698372,-2.027196 8.155385,-0.506799 10.146816,3.344873c1.896601,3.953032 0.47415,8.716943 -3.129392,10.845499l-146.512442,81.79736c-1.232791,0.608159 -2.370751,0.912238 -3.603542,0.912238zm2.465582,-148.49211c-1.232791,0 -2.465582,-0.304079 -3.508712,-0.912238l-82.312492,-47.436387c-3.603542,-2.128556 -5.025993,-6.993826 -3.034562,-10.845499c1.991431,-3.953032 6.543274,-5.27071 10.146816,-3.243514l78.708949,45.409191l78.329629,-47.537747c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-81.933171,49.666303c-1.232791,0.608159 -2.560412,0.912238 -3.793202,0.912238z"/>
|
||||||
|
<path stroke="null" id="svg_12" fill="#0276F7" d="m509.913752,579.708306l-0.28449,0l-263.248245,-9.021022c-4.172523,-0.10136 -7.396745,-3.851672 -7.207085,-8.210144c0.09483,-4.459831 4.077693,-7.703345 7.681235,-7.703345l263.343075,9.021022c4.172523,0.10136 7.396745,3.851672 7.207085,8.210144c-0.18966,4.257112 -3.508712,7.703345 -7.491575,7.703345zm0,152.141063c-1.612111,0 -3.224222,-0.608159 -4.646673,-1.723117c-3.224222,-2.736715 -3.793202,-7.804705 -1.232791,-11.250938l205.496739,-276.610899c2.560412,-3.446233 7.301915,-4.054392 10.526137,-1.317677c3.224222,2.736715 3.793202,7.804705 1.232791,11.250938l-205.496739,276.610899c-1.517281,2.027196 -3.698372,3.040794 -5.879464,3.040794z"/>
|
||||||
|
<path stroke="null" id="svg_13" fill="#0276F7" d="m509.913752,579.708306c-1.422451,0 -2.750072,-0.405439 -4.077693,-1.216318c-3.508712,-2.432635 -4.457013,-7.297906 -2.275921,-11.048218l144.14169,-239.310492c2.275921,-3.750313 6.922594,-4.763911 10.336476,-2.432635c3.508712,2.432635 4.457013,7.297906 2.275921,11.048218l-144.14169,239.310492c-1.422451,2.331275 -3.793202,3.648953 -6.258784,3.648953zm-2.465582,-157.513132c-1.043131,0 -2.086261,-0.20272 -3.129392,-0.709519c-3.793202,-1.824476 -5.405313,-6.588387 -3.698372,-10.642779l84.398753,-198.158413c1.706941,-4.054392 6.069124,-5.777509 9.957156,-3.953032c3.793202,1.824476 5.405313,6.588387 3.698372,10.642779l-84.303923,198.158413c-1.327621,2.939434 -4.077693,4.662551 -6.922594,4.662551z"/>
|
||||||
|
<path stroke="null" id="svg_14" fill="#0276F7" d="m591.846924,375.062866c-2.750072,0 -5.405313,-1.621757 -6.732934,-4.459831c-1.801771,-3.953032 -0.28449,-8.716943 3.413882,-10.642779l129.253371,-67.302908l-365.759539,-178.089172l20.862613,208.091673l133.994874,-64.262114c3.698372,-1.824476 8.155385,0 9.862326,4.054392c1.706941,4.054392 0,8.716943 -3.793202,10.541419l-143.38305,68.823305c-2.181091,1.013598 -4.646673,0.912238 -6.827764,-0.405439c-2.086261,-1.317677 -3.413882,-3.547593 -3.698372,-6.081588l-23.328195,-233.026185c-0.28449,-2.838074 0.853471,-5.676149 3.034562,-7.297906c2.181091,-1.621757 5.025993,-2.027196 7.491575,-0.810878l392.217126,190.961867c2.655242,1.317677 4.362183,4.054392 4.362183,7.196546c0,3.142154 -1.612111,5.980228 -4.172523,7.297906l-143.57271,74.600814c-1.043131,0.608159 -2.181091,0.810878 -3.224222,0.810878zm-283.921198,78.959286c-3.603542,0 -6.827764,-2.838074 -7.396745,-6.791107c-0.56898,-4.358471 2.181091,-8.412864 6.258784,-9.122382l199.617275,-31.826978c4.077693,-0.608159 7.870895,2.331275 8.534705,6.689747c0.56898,4.358471 -2.181091,8.412864 -6.258784,9.122382l-199.617275,31.826978c-0.47415,0.10136 -0.853471,0.10136 -1.137961,0.10136z"/>
|
||||||
|
</g>
|
||||||
|
<text stroke="null" font-style="italic" transform="matrix(6.577099502228161,0,0,7.449448263868419,-1073.2057632249744,-908.8606073938396) " xml:space="preserve" text-anchor="start" font-family="Arvo, sans-serif" font-size="24" id="svg_16" y="177.898525" x="178.621382" stroke-width="0" fill="#B5D6FB">4G</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.9 KiB |
28
public/svg/base5G.svg
Normal file
28
public/svg/base5G.svg
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<!-- Created with Method Draw - http://github.com/duopixel/Method-Draw/ -->
|
||||||
|
|
||||||
|
<g>
|
||||||
|
<title>background</title>
|
||||||
|
<rect fill="none" id="canvas_background" height="1026" width="1026" y="-1" x="-1"/>
|
||||||
|
<g display="none" overflow="visible" y="0" x="0" height="100%" width="100%" id="canvasGrid">
|
||||||
|
<rect fill="url(#gridpattern)" stroke-width="0" y="1" x="1" height="768" width="1024"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<title>Layer 1</title>
|
||||||
|
<g stroke="null" id="svg_15">
|
||||||
|
<path stroke="null" id="svg_4" fill="#B5D6FB" d="m512.094844,961.632039c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-405.588164,-251.575028c-2.275921,-1.419037 -3.698372,-4.054392 -3.698372,-6.892467l0,-90.007504c0,-2.838074 1.422451,-5.473429 3.698372,-6.892467l405.588164,-255.426701c1.137961,-0.709519 2.465582,-1.114958 3.793202,-1.114958s2.655242,0.405439 3.793202,1.114958l405.493334,255.426701c2.275921,1.419037 3.698372,4.054392 3.698372,6.892467l0,90.007504c0,2.838074 -1.422451,5.473429 -3.698372,6.892467l-405.588164,251.575028c-1.137961,0.709519 -2.465582,1.114958 -3.698372,1.114958z"/>
|
||||||
|
<path stroke="null" id="svg_5" fill="#0276F7" d="m512.094844,356.615382l405.398504,255.426701l0,90.007504l-66.096551,40.94936l-339.301952,210.625668l-339.491613,-210.625668l-66.096551,-40.94936l0,-90.007504l405.588164,-255.426701m0,-16.014849c-2.655242,0 -5.215653,0.709519 -7.586405,2.229916l-405.588164,255.426701c-4.551843,2.838074 -7.396745,8.108784 -7.396745,13.784933l0,90.007504c0,5.676149 2.844902,10.946859 7.491575,13.886293l66.096551,41.05072l339.491613,210.625668c2.275921,1.419037 4.931163,2.128556 7.491575,2.128556s5.215653,-0.709519 7.491575,-2.128556l339.301952,-210.625668l66.096551,-40.94936c4.646673,-2.838074 7.491575,-8.108784 7.491575,-13.886293l0,-90.007504c0,-5.676149 -2.844902,-10.946859 -7.396745,-13.784933l-405.398504,-255.426701c-2.370751,-1.520397 -5.025993,-2.331275 -7.586405,-2.331275z"/>
|
||||||
|
<path stroke="null" id="svg_6" fill="#FFFFFF" d="m106.50668,612.042083l405.493334,253.298145l405.493334,-253.298145l-405.398504,-255.426701l-405.588164,255.426701z"/>
|
||||||
|
<path stroke="null" id="svg_7" fill="#D4E4FC" d="m501.473877,64.192353l-254.9032,498.487506l263.343075,161.162085l266.662127,-162.074323l-275.102002,-497.575268z"/>
|
||||||
|
<path stroke="null" id="svg_8" fill="#0276F7" d="m229.975417,602.311542c-1.232791,0 -2.465582,-0.304079 -3.698372,-1.013598c-3.603542,-2.128556 -4.931163,-6.993826 -2.844902,-10.845499l279.653845,-532.13896c1.327621,-2.533995 3.793202,-4.054392 6.543274,-4.054392c2.655242,0 5.120823,1.520397 6.543274,4.054392l284.395348,532.13896c2.086261,3.851672 0.75864,8.716943 -2.750072,10.946859c-3.603542,2.128556 -8.155385,0.810878 -10.241646,-2.939434l-277.852074,-519.874424l-273.205401,519.671704c-1.422451,2.635355 -3.982862,4.054392 -6.543274,4.054392z"/>
|
||||||
|
<path stroke="null" id="svg_9" fill="#0276F7" d="m509.913752,755.567562c-4.172523,0 -7.491575,-3.547593 -7.491575,-8.007424l0,-666.744777c0,-4.459831 3.319052,-8.007424 7.491575,-8.007424s7.491575,3.547593 7.491575,8.007424l0,666.846137c0,4.358471 -3.413882,7.906065 -7.491575,7.906065z"/>
|
||||||
|
<path stroke="null" id="svg_10" fill="#0276F7" d="m509.913752,731.849369c-1.327621,0 -2.560412,-0.405439 -3.698372,-1.013598l-263.343075,-161.162085c-3.603542,-2.229916 -4.836333,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.939434l259.549873,158.83081l262.963755,-159.844408c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-266.662127,162.074323c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598z"/>
|
||||||
|
<path stroke="null" id="svg_11" fill="#0276F7" d="m509.913752,579.708306c-1.327621,0 -2.560412,-0.405439 -3.793202,-1.114958l-201.988026,-125.686154c-3.603542,-2.229916 -4.741503,-7.095186 -2.750072,-10.946859c2.086261,-3.851672 6.638104,-5.16935 10.241646,-2.838074l198.289654,123.354879l201.798366,-122.138561c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.845499l-205.496739,124.469837c-1.137961,0.709519 -2.465582,1.013598 -3.698372,1.013598zm-2.465582,-157.513132c-1.232791,0 -2.370751,-0.304079 -3.508712,-0.912238l-140.917468,-79.668804c-3.698372,-2.128556 -5.025993,-6.892467 -3.129392,-10.845499c1.896601,-3.953032 6.448444,-5.37207 10.146816,-3.344873l137.503586,77.742968l143.00373,-79.871524c3.698372,-2.027196 8.155385,-0.506799 10.146816,3.344873c1.896601,3.953032 0.47415,8.716943 -3.129392,10.845499l-146.512442,81.79736c-1.232791,0.608159 -2.370751,0.912238 -3.603542,0.912238zm2.465582,-148.49211c-1.232791,0 -2.465582,-0.304079 -3.508712,-0.912238l-82.312492,-47.436387c-3.603542,-2.128556 -5.025993,-6.993826 -3.034562,-10.845499c1.991431,-3.953032 6.543274,-5.27071 10.146816,-3.243514l78.708949,45.409191l78.329629,-47.537747c3.603542,-2.229916 8.155385,-0.810878 10.241646,3.040794c2.086261,3.851672 0.75864,8.716943 -2.844902,10.946859l-81.933171,49.666303c-1.232791,0.608159 -2.560412,0.912238 -3.793202,0.912238z"/>
|
||||||
|
<path stroke="null" id="svg_12" fill="#0276F7" d="m509.913752,579.708306l-0.28449,0l-263.248245,-9.021022c-4.172523,-0.10136 -7.396745,-3.851672 -7.207085,-8.210144c0.09483,-4.459831 4.077693,-7.703345 7.681235,-7.703345l263.343075,9.021022c4.172523,0.10136 7.396745,3.851672 7.207085,8.210144c-0.18966,4.257112 -3.508712,7.703345 -7.491575,7.703345zm0,152.141063c-1.612111,0 -3.224222,-0.608159 -4.646673,-1.723117c-3.224222,-2.736715 -3.793202,-7.804705 -1.232791,-11.250938l205.496739,-276.610899c2.560412,-3.446233 7.301915,-4.054392 10.526137,-1.317677c3.224222,2.736715 3.793202,7.804705 1.232791,11.250938l-205.496739,276.610899c-1.517281,2.027196 -3.698372,3.040794 -5.879464,3.040794z"/>
|
||||||
|
<path stroke="null" id="svg_13" fill="#0276F7" d="m509.913752,579.708306c-1.422451,0 -2.750072,-0.405439 -4.077693,-1.216318c-3.508712,-2.432635 -4.457013,-7.297906 -2.275921,-11.048218l144.14169,-239.310492c2.275921,-3.750313 6.922594,-4.763911 10.336476,-2.432635c3.508712,2.432635 4.457013,7.297906 2.275921,11.048218l-144.14169,239.310492c-1.422451,2.331275 -3.793202,3.648953 -6.258784,3.648953zm-2.465582,-157.513132c-1.043131,0 -2.086261,-0.20272 -3.129392,-0.709519c-3.793202,-1.824476 -5.405313,-6.588387 -3.698372,-10.642779l84.398753,-198.158413c1.706941,-4.054392 6.069124,-5.777509 9.957156,-3.953032c3.793202,1.824476 5.405313,6.588387 3.698372,10.642779l-84.303923,198.158413c-1.327621,2.939434 -4.077693,4.662551 -6.922594,4.662551z"/>
|
||||||
|
<path stroke="null" id="svg_14" fill="#0276F7" d="m591.846924,375.062866c-2.750072,0 -5.405313,-1.621757 -6.732934,-4.459831c-1.801771,-3.953032 -0.28449,-8.716943 3.413882,-10.642779l129.253371,-67.302908l-365.759539,-178.089172l20.862613,208.091673l133.994874,-64.262114c3.698372,-1.824476 8.155385,0 9.862326,4.054392c1.706941,4.054392 0,8.716943 -3.793202,10.541419l-143.38305,68.823305c-2.181091,1.013598 -4.646673,0.912238 -6.827764,-0.405439c-2.086261,-1.317677 -3.413882,-3.547593 -3.698372,-6.081588l-23.328195,-233.026185c-0.28449,-2.838074 0.853471,-5.676149 3.034562,-7.297906c2.181091,-1.621757 5.025993,-2.027196 7.491575,-0.810878l392.217126,190.961867c2.655242,1.317677 4.362183,4.054392 4.362183,7.196546c0,3.142154 -1.612111,5.980228 -4.172523,7.297906l-143.57271,74.600814c-1.043131,0.608159 -2.181091,0.810878 -3.224222,0.810878zm-283.921198,78.959286c-3.603542,0 -6.827764,-2.838074 -7.396745,-6.791107c-0.56898,-4.358471 2.181091,-8.412864 6.258784,-9.122382l199.617275,-31.826978c4.077693,-0.608159 7.870895,2.331275 8.534705,6.689747c0.56898,4.358471 -2.181091,8.412864 -6.258784,9.122382l-199.617275,31.826978c-0.47415,0.10136 -0.853471,0.10136 -1.137961,0.10136z"/>
|
||||||
|
</g>
|
||||||
|
<text stroke="null" font-style="italic" transform="matrix(6.577099502228161,0,0,7.449448263868419,-1073.2057632249744,-908.8606073938396) " xml:space="preserve" text-anchor="start" font-family="Arvo, sans-serif" font-size="24" id="svg_16" y="177.898525" x="178.621382" stroke-width="0" fill="#D4E4FC">5G</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.9 KiB |
@@ -10,6 +10,7 @@ export function listAMFDataUE(query: Record<string, any>) {
|
|||||||
url: '/neData/amf/ue/list',
|
url: '/neData/amf/ue/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,3 +41,90 @@ export function exportAMFDataUE(data: Record<string, any>) {
|
|||||||
timeout: 60_000,
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AMF-接入基站信息列表
|
||||||
|
* @param query 查询参数 neId=001&id=1
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listAMFNblist(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/amf/nb/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AMF-接入基站状态信息列表
|
||||||
|
* @param query 查询参数 neId=001&state=1
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listAMFNbStatelist(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/amf/nb/list-cfg',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AMF-接入基站状态信息新增
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param data 数据 { "index": 1, "name": "Gnb", "address": "192.168.8.1", "position": "Area-B" }
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addAMFNbState(neId: string, data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
neType: 'AMF',
|
||||||
|
neId: neId,
|
||||||
|
paramName: 'gnbList',
|
||||||
|
paramData: data,
|
||||||
|
loc: `${data.index}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AMF-接入基站状态信息修改
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param data 数据 { "index": 1, "name": "Gnb", "address": "192.168.8.1", "position": "Area-B" }
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function editAMFNbState(neId: string, data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
method: 'put',
|
||||||
|
data: {
|
||||||
|
neType: 'AMF',
|
||||||
|
neId: neId,
|
||||||
|
paramName: 'gnbList',
|
||||||
|
paramData: data,
|
||||||
|
loc: `${data.index}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AMF-接入基站状态信息删除
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param index 数据index
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delAMFNbState(neId: string, index: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
method: 'delete',
|
||||||
|
params: {
|
||||||
|
neType: 'AMF',
|
||||||
|
neId: neId,
|
||||||
|
paramName: 'gnbList',
|
||||||
|
loc: `${index}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export function listIMSDataCDR(query: Record<string, any>) {
|
|||||||
url: '/neData/ims/cdr/list',
|
url: '/neData/ims/cdr/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export function listMMEDataUE(query: Record<string, any>) {
|
|||||||
url: '/neData/mme/ue/list',
|
url: '/neData/mme/ue/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export function listSGWCDataCDR(query: Record<string, any>) {
|
|||||||
url: '/neData/sgwc/cdr/list',
|
url: '/neData/sgwc/cdr/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export function listSMFDataCDR(query: Record<string, any>) {
|
|||||||
url: '/neData/smf/cdr/list',
|
url: '/neData/smf/cdr/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export function listSMSCDataCDR(query: Record<string, any>) {
|
|||||||
url: '/neData/smsc/cdr/list',
|
url: '/neData/smsc/cdr/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
|
||||||
import { request } from '@/plugins/http-fetch';
|
import { request } from '@/plugins/http-fetch';
|
||||||
import { parseObjLineToHump } from '@/utils/parse-utils';
|
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新 查询自定义指标数据
|
* 新 查询自定义指标数据
|
||||||
@@ -14,6 +11,7 @@ export async function listCustomData(query: Record<string, any>) {
|
|||||||
url: `/pm/kpiC/report`,
|
url: `/pm/kpiC/report`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,8 +171,8 @@ export function parseSizeFromKbs(sizeByte: number, timeInterval: number): any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字节数转换单位
|
* 位数据转换单位
|
||||||
* @param bits 字节Bit大小 64009540 = 512.08 MB
|
* @param bits 位Bit大小 64009540 = 512.08 MB
|
||||||
* @returns xx B / KB / MB / GB / TB / PB / EB / ZB / YB
|
* @returns xx B / KB / MB / GB / TB / PB / EB / ZB / YB
|
||||||
*/
|
*/
|
||||||
export function parseSizeFromBits(bits: number | string): string {
|
export function parseSizeFromBits(bits: number | string): string {
|
||||||
@@ -181,7 +181,28 @@ export function parseSizeFromBits(bits: number | string): string {
|
|||||||
bits = bits * 8;
|
bits = bits * 8;
|
||||||
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||||
const unitIndex = Math.floor(Math.log2(bits) / 10);
|
const unitIndex = Math.floor(Math.log2(bits) / 10);
|
||||||
const value = (bits / Math.pow(1000, unitIndex)).toFixed(2);
|
const value = bits / Math.pow(1000, unitIndex);
|
||||||
const unti = units[unitIndex];
|
const unti = units[unitIndex];
|
||||||
|
if (unitIndex > 0) {
|
||||||
|
return `${value.toFixed(2)} ${unti}`;
|
||||||
|
}
|
||||||
|
return `${value} ${unti}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字节数转换单位
|
||||||
|
* @param byte 字节Byte大小 64009540 = 512.08 MB
|
||||||
|
* @returns xx B / KB / MB / GB / TB / PB / EB / ZB / YB
|
||||||
|
*/
|
||||||
|
export function parseSizeFromByte(byte: number | string): string {
|
||||||
|
byte = Number(byte) || 0;
|
||||||
|
if (byte <= 0) return '0 B';
|
||||||
|
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||||
|
const unitIndex = Math.floor(Math.log2(byte) / 10);
|
||||||
|
const unti = units[unitIndex];
|
||||||
|
const value = byte / Math.pow(1000, unitIndex);
|
||||||
|
if (unitIndex > 0) {
|
||||||
|
return `${value.toFixed(2)} ${unti}`;
|
||||||
|
}
|
||||||
return `${value} ${unti}`;
|
return `${value} ${unti}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,17 +12,24 @@ 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 useUserStore from '@/store/modules/user';
|
import useUserStore from '@/store/modules/user';
|
||||||
import { TENANTADMIN_ROLE_KEY } from '@/constants/admin-constants';
|
import { TENANTADMIN_ROLE_KEY } from '@/constants/admin-constants';
|
||||||
import { listTenant } from '@/api/system/tenant';
|
import { listTenant } from '@/api/system/tenant';
|
||||||
|
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: {
|
||||||
@@ -39,7 +46,10 @@ let dict: {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**开始结束时间 */
|
/**开始结束时间 */
|
||||||
let queryRangePicker = ref<[string, string]>(['', '']);
|
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([
|
||||||
|
dayjs().startOf('hour'),
|
||||||
|
dayjs().endOf('hour'),
|
||||||
|
]);
|
||||||
|
|
||||||
/**查询参数 */
|
/**查询参数 */
|
||||||
let queryParams = reactive({
|
let queryParams = reactive({
|
||||||
@@ -53,9 +63,9 @@ let queryParams = reactive({
|
|||||||
sortField: 'timestamp',
|
sortField: 'timestamp',
|
||||||
sortOrder: 'desc',
|
sortOrder: 'desc',
|
||||||
/**开始时间 */
|
/**开始时间 */
|
||||||
startTime: '',
|
startTime: undefined as undefined | number,
|
||||||
/**结束时间 */
|
/**结束时间 */
|
||||||
endTime: '',
|
endTime: undefined as undefined | number,
|
||||||
/**当前页数 */
|
/**当前页数 */
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
/**每页条数 */
|
/**每页条数 */
|
||||||
@@ -74,7 +84,7 @@ function fnQueryReset() {
|
|||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
});
|
});
|
||||||
queryRangePicker.value = ['', ''];
|
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')];
|
||||||
tablePagination.current = 1;
|
tablePagination.current = 1;
|
||||||
tablePagination.pageSize = 20;
|
tablePagination.pageSize = 20;
|
||||||
fnGetList();
|
fnGetList();
|
||||||
@@ -151,9 +161,15 @@ let tableColumns: ColumnsType = [
|
|||||||
{
|
{
|
||||||
title: t('views.dashboard.ue.time'),
|
title: t('views.dashboard.ue.time'),
|
||||||
dataIndex: 'eventJSON',
|
dataIndex: 'eventJSON',
|
||||||
key: 'time',
|
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 150,
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const record = opt.value;
|
||||||
|
if (record?.time) {
|
||||||
|
return record.time;
|
||||||
|
}
|
||||||
|
return parseDateToStr(+record.timestamp * 1000);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.dashboard.cdr.tenantName'),
|
title: t('views.dashboard.cdr.tenantName'),
|
||||||
@@ -269,11 +285,19 @@ function fnGetList(pageNum?: number) {
|
|||||||
if (pageNum) {
|
if (pageNum) {
|
||||||
queryParams.pageNum = pageNum;
|
queryParams.pageNum = pageNum;
|
||||||
}
|
}
|
||||||
if (!queryRangePicker.value) {
|
|
||||||
queryRangePicker.value = ['', ''];
|
// 时间范围
|
||||||
|
if (
|
||||||
|
Array.isArray(queryRangePicker.value) &&
|
||||||
|
queryRangePicker.value.length > 0
|
||||||
|
) {
|
||||||
|
queryParams.startTime = queryRangePicker.value[0].valueOf();
|
||||||
|
queryParams.endTime = queryRangePicker.value[1].valueOf();
|
||||||
|
} else {
|
||||||
|
queryParams.startTime = undefined;
|
||||||
|
queryParams.endTime = undefined;
|
||||||
}
|
}
|
||||||
queryParams.startTime = queryRangePicker.value[0];
|
|
||||||
queryParams.endTime = queryRangePicker.value[1];
|
|
||||||
listAMFDataUE(toRaw(queryParams)).then(res => {
|
listAMFDataUE(toRaw(queryParams)).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
// 取消勾选
|
// 取消勾选
|
||||||
@@ -341,6 +365,18 @@ function fnExportList() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制CDR
|
||||||
|
* @param jsonStr JSON字符串
|
||||||
|
*/
|
||||||
|
function fnRecordCopy(jsonStr: string) {
|
||||||
|
if (!jsonStr) return;
|
||||||
|
const text = JSON.stringify(jsonStr, null, 2);
|
||||||
|
copy(text).then(() => {
|
||||||
|
message.success(t('common.copyOk'), 3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**实时数据开关 */
|
/**实时数据开关 */
|
||||||
const realTimeData = ref<boolean>(false);
|
const realTimeData = ref<boolean>(false);
|
||||||
|
|
||||||
@@ -350,31 +386,30 @@ const realTimeData = ref<boolean>(false);
|
|||||||
function fnRealTime() {
|
function fnRealTime() {
|
||||||
realTimeData.value = !realTimeData.value;
|
realTimeData.value = !realTimeData.value;
|
||||||
if (realTimeData.value) {
|
if (realTimeData.value) {
|
||||||
|
tableState.seached = false;
|
||||||
// 建立链接
|
// 建立链接
|
||||||
const options: OptionsType = {
|
const options: OptionsType = {
|
||||||
url: '/ws',
|
url: '/ws',
|
||||||
params: {
|
params: {
|
||||||
/**订阅通道组
|
/**订阅通道组
|
||||||
*
|
*
|
||||||
* AMF_UE会话事件(GroupID:1010)
|
* AMF_UE会话事件(GroupID:1010_neId)
|
||||||
*/
|
*/
|
||||||
subGroupID: '1010',
|
subGroupID: `1010_${queryParams.neId}`,
|
||||||
},
|
},
|
||||||
onmessage: wsMessage,
|
onmessage: wsMessage,
|
||||||
onerror: wsError,
|
onerror: (ev: any) => {
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
ws.connect(options);
|
ws.connect(options);
|
||||||
} else {
|
} else {
|
||||||
ws.close();
|
ws.close();
|
||||||
|
tableState.seached = true;
|
||||||
|
fnGetList(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**接收数据后回调 */
|
|
||||||
function wsError(ev: any) {
|
|
||||||
// 接收数据后回调
|
|
||||||
console.error(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**接收数据后回调 */
|
/**接收数据后回调 */
|
||||||
function wsMessage(res: Record<string, any>) {
|
function wsMessage(res: Record<string, any>) {
|
||||||
const { code, requestId, data } = res;
|
const { code, requestId, data } = res;
|
||||||
@@ -388,7 +423,7 @@ function wsMessage(res: Record<string, any>) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ueEvent AMF_UE会话事件
|
// ueEvent AMF_UE会话事件
|
||||||
if (data.groupId === '1010') {
|
if (data.groupId === `1010_${queryParams.neId}`) {
|
||||||
const ueEvent = data.data;
|
const ueEvent = data.data;
|
||||||
queue.add(async () => {
|
queue.add(async () => {
|
||||||
modalState.maxId += 1;
|
modalState.maxId += 1;
|
||||||
@@ -410,16 +445,13 @@ function wsMessage(res: Record<string, any>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 初始字典数据
|
// 初始字典数据
|
||||||
Promise.allSettled([
|
Promise.allSettled([
|
||||||
getDict('ue_auth_code'),
|
getDict('ue_auth_code'),
|
||||||
getDict('ue_event_type'),
|
getDict('ue_event_type'),
|
||||||
getDict('ue_event_cm_state'),
|
getDict('ue_event_cm_state'),
|
||||||
])
|
]).then(resArr => {
|
||||||
.then(resArr => {
|
|
||||||
if (resArr[0].status === 'fulfilled') {
|
if (resArr[0].status === 'fulfilled') {
|
||||||
dict.ueAauthCode = resArr[0].value;
|
dict.ueAauthCode = resArr[0].value;
|
||||||
}
|
}
|
||||||
@@ -429,6 +461,31 @@ onMounted(() => {
|
|||||||
if (resArr[2].status === 'fulfilled') {
|
if (resArr[2].status === 'fulfilled') {
|
||||||
dict.ueEventCmState = resArr[2].value;
|
dict.ueEventCmState = resArr[2].value;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取网元网元列表
|
||||||
|
useNeInfoStore()
|
||||||
|
.fnNelist()
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length > 0) {
|
||||||
|
let arr: Record<string, any>[] = [];
|
||||||
|
res.data.forEach(i => {
|
||||||
|
if (i.neType === 'AMF') {
|
||||||
|
arr.push({ value: i.neId, label: i.neName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neOtions.value = arr;
|
||||||
|
if (arr.length > 0) {
|
||||||
|
queryParams.neId = arr[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
// 获取列表数据
|
// 获取列表数据
|
||||||
@@ -478,6 +535,16 @@ onBeforeUnmount(() => {
|
|||||||
<!-- 表格搜索栏 -->
|
<!-- 表格搜索栏 -->
|
||||||
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item label="AMF" name="neId ">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.neId"
|
||||||
|
:options="neOtions"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnGetList(1)"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.dashboard.ue.eventType')"
|
:label="t('views.dashboard.ue.eventType')"
|
||||||
@@ -492,7 +559,7 @@ onBeforeUnmount(() => {
|
|||||||
></a-select>
|
></a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="4" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item label="IMSI" name="imsi ">
|
<a-form-item label="IMSI" name="imsi ">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="queryParams.imsi"
|
v-model:value="queryParams.imsi"
|
||||||
@@ -512,6 +579,20 @@ onBeforeUnmount(() => {
|
|||||||
></a-auto-complete>
|
></a-auto-complete>
|
||||||
</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')"
|
||||||
@@ -528,20 +609,6 @@ onBeforeUnmount(() => {
|
|||||||
></a-range-picker>
|
></a-range-picker>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
|
||||||
<a-form-item>
|
|
||||||
<a-space :size="8">
|
|
||||||
<a-button type="primary" @click.prevent="fnGetList(1)">
|
|
||||||
<template #icon><SearchOutlined /></template>
|
|
||||||
{{ t('common.search') }}
|
|
||||||
</a-button>
|
|
||||||
<a-button type="default" @click.prevent="fnQueryReset">
|
|
||||||
<template #icon><ClearOutlined /></template>
|
|
||||||
{{ t('common.reset') }}
|
|
||||||
</a-button>
|
|
||||||
</a-space>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -604,6 +671,7 @@ onBeforeUnmount(() => {
|
|||||||
:checked-children="t('common.switch.show')"
|
:checked-children="t('common.switch.show')"
|
||||||
:un-checked-children="t('common.switch.hide')"
|
:un-checked-children="t('common.switch.hide')"
|
||||||
size="small"
|
size="small"
|
||||||
|
:disabled="realTimeData"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
@@ -669,7 +737,7 @@ onBeforeUnmount(() => {
|
|||||||
<span v-if="record.eventType === 'auth-result'">
|
<span v-if="record.eventType === 'auth-result'">
|
||||||
<DictTag
|
<DictTag
|
||||||
:options="dict.ueAauthCode"
|
:options="dict.ueAauthCode"
|
||||||
:value="record.eventJSON.authCode"
|
:value="record.eventJSON.result"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="record.eventType === 'detach'">
|
<span v-if="record.eventType === 'detach'">
|
||||||
@@ -678,32 +746,23 @@ onBeforeUnmount(() => {
|
|||||||
<span v-if="record.eventType === 'cm-state'">
|
<span v-if="record.eventType === 'cm-state'">
|
||||||
<DictTag
|
<DictTag
|
||||||
:options="dict.ueEventCmState"
|
:options="dict.ueEventCmState"
|
||||||
:value="record.eventJSON.status"
|
:value="record.eventJSON.result"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'time'">
|
|
||||||
<span
|
|
||||||
v-if="record.eventType === 'auth-result'"
|
|
||||||
:title="record.eventJSON.authTime"
|
|
||||||
>
|
|
||||||
{{ record.eventJSON.authTime }}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
v-if="record.eventType === 'detach'"
|
|
||||||
:title="record.eventJSON.detachTime"
|
|
||||||
>
|
|
||||||
{{ record.eventJSON.detachTime }}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
v-if="record.eventType === 'cm-state'"
|
|
||||||
:title="record.eventJSON.changeTime"
|
|
||||||
>
|
|
||||||
{{ record.eventJSON.changeTime }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<template v-if="column.key === 'id'">
|
<template v-if="column.key === 'id'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.copyText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordCopy(record.eventJSON)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<CopyOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template #title>{{ t('common.deleteText') }}</template>
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
<a-button
|
<a-button
|
||||||
@@ -722,29 +781,30 @@ onBeforeUnmount(() => {
|
|||||||
<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="8" :md="12" :xs="24" :offset="2">
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.dashboard.ue.ueInfo') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.neName') }}: </span>
|
||||||
|
<span>{{ record.neName }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.rmUid') }}: </span>
|
||||||
|
<span>{{ record.rmUID }}</span>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
<a-divider orientation="left">
|
<a-divider orientation="left">
|
||||||
{{ t('views.dashboard.ue.rowInfo') }}
|
{{ t('views.dashboard.ue.rowInfo') }}
|
||||||
</a-divider>
|
</a-divider>
|
||||||
<div>
|
<div>
|
||||||
<span>{{ t('views.dashboard.ue.time') }}: </span>
|
<span>{{ t('views.dashboard.ue.time') }}: </span>
|
||||||
<span
|
<template v-if="record.eventJSON?.time">
|
||||||
v-if="record.eventType === 'auth-result'"
|
{{ record.eventJSON.time }}
|
||||||
:title="record.eventJSON.authTime"
|
</template>
|
||||||
>
|
<template v-else>
|
||||||
{{ record.eventJSON.authTime }}
|
{{ parseDateToStr(record.eventJSON.timestamp * 1000) }}
|
||||||
</span>
|
</template>
|
||||||
<span
|
|
||||||
v-if="record.eventType === 'detach'"
|
|
||||||
:title="record.eventJSON.detachTime"
|
|
||||||
>
|
|
||||||
{{ record.eventJSON.detachTime }}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
v-if="record.eventType === 'cm-state'"
|
|
||||||
:title="record.eventJSON.changeTime"
|
|
||||||
>
|
|
||||||
{{ record.eventJSON.changeTime }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>{{ t('views.dashboard.ue.eventType') }}: </span>
|
<span>{{ t('views.dashboard.ue.eventType') }}: </span>
|
||||||
@@ -758,7 +818,7 @@ onBeforeUnmount(() => {
|
|||||||
<span v-if="record.eventType === 'auth-result'">
|
<span v-if="record.eventType === 'auth-result'">
|
||||||
<DictTag
|
<DictTag
|
||||||
:options="dict.ueAauthCode"
|
:options="dict.ueAauthCode"
|
||||||
:value="record.eventJSON.authCode"
|
:value="record.eventJSON.result"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="record.eventType === 'detach'">
|
<span v-if="record.eventType === 'detach'">
|
||||||
@@ -767,7 +827,7 @@ onBeforeUnmount(() => {
|
|||||||
<span v-if="record.eventType === 'cm-state'">
|
<span v-if="record.eventType === 'cm-state'">
|
||||||
<DictTag
|
<DictTag
|
||||||
:options="dict.ueEventCmState"
|
:options="dict.ueEventCmState"
|
||||||
:value="record.eventJSON.status"
|
:value="record.eventJSON.result"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import { reactive, onMounted, toRaw, ref, onBeforeUnmount } from 'vue';
|
import { reactive, onMounted, toRaw, ref, onBeforeUnmount } from 'vue';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { message, Modal } from 'ant-design-vue/es';
|
import { message, Modal } from 'ant-design-vue/es';
|
||||||
@@ -18,7 +19,8 @@ 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 { listTenant } from '@/api/system/tenant';
|
import { listTenant } from '@/api/system/tenant';
|
||||||
|
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();
|
||||||
@@ -42,7 +44,10 @@ let dict: {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**开始结束时间 */
|
/**开始结束时间 */
|
||||||
let queryRangePicker = ref<[string, string]>(['', '']);
|
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>([
|
||||||
|
dayjs().startOf('hour'),
|
||||||
|
dayjs().endOf('hour'),
|
||||||
|
]);
|
||||||
|
|
||||||
/**查询参数 */
|
/**查询参数 */
|
||||||
let queryParams = reactive({
|
let queryParams = reactive({
|
||||||
@@ -56,9 +61,9 @@ let queryParams = reactive({
|
|||||||
sortField: 'timestamp',
|
sortField: 'timestamp',
|
||||||
sortOrder: 'desc',
|
sortOrder: 'desc',
|
||||||
/**开始时间 */
|
/**开始时间 */
|
||||||
startTime: '',
|
startTime: undefined as undefined | number,
|
||||||
/**结束时间 */
|
/**结束时间 */
|
||||||
endTime: '',
|
endTime: undefined as undefined | number,
|
||||||
/**当前页数 */
|
/**当前页数 */
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
/**每页条数 */
|
/**每页条数 */
|
||||||
@@ -77,7 +82,7 @@ function fnQueryReset() {
|
|||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
});
|
});
|
||||||
queryRangePicker.value = ['', ''];
|
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')];
|
||||||
tablePagination.current = 1;
|
tablePagination.current = 1;
|
||||||
tablePagination.pageSize = 20;
|
tablePagination.pageSize = 20;
|
||||||
fnGetList();
|
fnGetList();
|
||||||
@@ -154,8 +159,11 @@ let tableColumns: ColumnsType = [
|
|||||||
align: 'left',
|
align: 'left',
|
||||||
width: 150,
|
width: 150,
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
const cdrJSON = opt.value;
|
const record = opt.value;
|
||||||
return parseDateToStr(+cdrJSON.timestamp * 1000);
|
if (record?.time) {
|
||||||
|
return record.time;
|
||||||
|
}
|
||||||
|
return parseDateToStr(+record.timestamp * 1000);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -272,11 +280,19 @@ function fnGetList(pageNum?: number) {
|
|||||||
if (pageNum) {
|
if (pageNum) {
|
||||||
queryParams.pageNum = pageNum;
|
queryParams.pageNum = pageNum;
|
||||||
}
|
}
|
||||||
if (!queryRangePicker.value) {
|
|
||||||
queryRangePicker.value = ['', ''];
|
// 时间范围
|
||||||
|
if (
|
||||||
|
Array.isArray(queryRangePicker.value) &&
|
||||||
|
queryRangePicker.value.length > 0
|
||||||
|
) {
|
||||||
|
queryParams.startTime = queryRangePicker.value[0].valueOf();
|
||||||
|
queryParams.endTime = queryRangePicker.value[1].valueOf();
|
||||||
|
} else {
|
||||||
|
queryParams.startTime = undefined;
|
||||||
|
queryParams.endTime = undefined;
|
||||||
}
|
}
|
||||||
queryParams.startTime = queryRangePicker.value[0];
|
|
||||||
queryParams.endTime = queryRangePicker.value[1];
|
|
||||||
listMMEDataUE(toRaw(queryParams)).then(res => {
|
listMMEDataUE(toRaw(queryParams)).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
// 取消勾选
|
// 取消勾选
|
||||||
@@ -344,6 +360,18 @@ function fnExportList() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制CDR
|
||||||
|
* @param jsonStr JSON字符串
|
||||||
|
*/
|
||||||
|
function fnRecordCopy(jsonStr: string) {
|
||||||
|
if (!jsonStr) return;
|
||||||
|
const text = JSON.stringify(jsonStr, null, 2);
|
||||||
|
copy(text).then(() => {
|
||||||
|
message.success(t('common.copyOk'), 3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**实时数据开关 */
|
/**实时数据开关 */
|
||||||
const realTimeData = ref<boolean>(false);
|
const realTimeData = ref<boolean>(false);
|
||||||
|
|
||||||
@@ -360,12 +388,14 @@ function fnRealTime() {
|
|||||||
params: {
|
params: {
|
||||||
/**订阅通道组
|
/**订阅通道组
|
||||||
*
|
*
|
||||||
* MME_UE会话事件(GroupID:1011)
|
* MME_UE会话事件(GroupID:1011_neId)
|
||||||
*/
|
*/
|
||||||
subGroupID: `1011_${queryParams.neId}`,
|
subGroupID: `1011_${queryParams.neId}`,
|
||||||
},
|
},
|
||||||
onmessage: wsMessage,
|
onmessage: wsMessage,
|
||||||
onerror: wsError,
|
onerror: (ev: any) => {
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
ws.connect(options);
|
ws.connect(options);
|
||||||
} else {
|
} else {
|
||||||
@@ -375,12 +405,6 @@ function fnRealTime() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**接收数据后回调 */
|
|
||||||
function wsError(ev: any) {
|
|
||||||
// 接收数据后回调
|
|
||||||
console.error(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**接收数据后回调 */
|
/**接收数据后回调 */
|
||||||
function wsMessage(res: Record<string, any>) {
|
function wsMessage(res: Record<string, any>) {
|
||||||
const { code, requestId, data } = res;
|
const { code, requestId, data } = res;
|
||||||
@@ -508,6 +532,7 @@ onBeforeUnmount(() => {
|
|||||||
v-model:value="queryParams.neId"
|
v-model:value="queryParams.neId"
|
||||||
:options="neOtions"
|
:options="neOtions"
|
||||||
:placeholder="t('common.selectPlease')"
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnGetList(1)"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -525,7 +550,7 @@ onBeforeUnmount(() => {
|
|||||||
></a-select>
|
></a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="4" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item label="IMSI" name="imsi ">
|
<a-form-item label="IMSI" name="imsi ">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="queryParams.imsi"
|
v-model:value="queryParams.imsi"
|
||||||
@@ -545,6 +570,20 @@ onBeforeUnmount(() => {
|
|||||||
></a-auto-complete>
|
></a-auto-complete>
|
||||||
</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')"
|
||||||
@@ -561,20 +600,6 @@ onBeforeUnmount(() => {
|
|||||||
></a-range-picker>
|
></a-range-picker>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
|
||||||
<a-form-item>
|
|
||||||
<a-space :size="8">
|
|
||||||
<a-button type="primary" @click.prevent="fnGetList(1)">
|
|
||||||
<template #icon><SearchOutlined /></template>
|
|
||||||
{{ t('common.search') }}
|
|
||||||
</a-button>
|
|
||||||
<a-button type="default" @click.prevent="fnQueryReset">
|
|
||||||
<template #icon><ClearOutlined /></template>
|
|
||||||
{{ t('common.reset') }}
|
|
||||||
</a-button>
|
|
||||||
</a-space>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -709,6 +734,17 @@ 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
|
||||||
@@ -745,7 +781,12 @@ onBeforeUnmount(() => {
|
|||||||
</a-divider>
|
</a-divider>
|
||||||
<div>
|
<div>
|
||||||
<span>{{ t('views.dashboard.ue.time') }}: </span>
|
<span>{{ t('views.dashboard.ue.time') }}: </span>
|
||||||
|
<template v-if="record.eventJSON?.time">
|
||||||
|
{{ record.eventJSON.time }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
{{ parseDateToStr(record.eventJSON.timestamp * 1000) }}
|
{{ parseDateToStr(record.eventJSON.timestamp * 1000) }}
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>{{ t('views.dashboard.ue.eventType') }}: </span>
|
<span>{{ t('views.dashboard.ue.eventType') }}: </span>
|
||||||
|
|||||||
@@ -137,18 +137,12 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{{ t('views.dashboard.overview.userActivity.time') }}:
|
{{ t('views.dashboard.overview.userActivity.time') }}:
|
||||||
<span
|
<template v-if="item.data?.time">
|
||||||
v-if="item.type === 'auth-result'"
|
{{ item.data.time }}
|
||||||
:title="item.data.authTime"
|
</template>
|
||||||
>
|
<template v-else>
|
||||||
{{ item.data.authTime }}
|
{{ parseDateToStr(+item.data.timestamp * 1000) }}
|
||||||
</span>
|
</template>
|
||||||
<span v-if="item.type === 'detach'" :title="item.data.detachTime">
|
|
||||||
{{ item.data.detachTime }}
|
|
||||||
</span>
|
|
||||||
<span v-if="item.type === 'cm-state'" :title="item.data.changeTime">
|
|
||||||
{{ item.data.changeTime }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -167,7 +161,7 @@ onMounted(() => {
|
|||||||
<div v-if="item.type === 'auth-result'">
|
<div v-if="item.type === 'auth-result'">
|
||||||
{{ t('views.dashboard.overview.userActivity.result') }}:
|
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||||
<span>
|
<span>
|
||||||
<DictTag :options="dict.ueAauthCode" :value="item.data.authCode" />
|
<DictTag :options="dict.ueAauthCode" :value="item.data.result" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="item.type === 'detach'">
|
<div v-if="item.type === 'detach'">
|
||||||
@@ -177,7 +171,7 @@ onMounted(() => {
|
|||||||
<div class="card-ue-w33" v-if="item.type === 'cm-state'">
|
<div class="card-ue-w33" v-if="item.type === 'cm-state'">
|
||||||
{{ t('views.dashboard.overview.userActivity.result') }}:
|
{{ t('views.dashboard.overview.userActivity.result') }}:
|
||||||
<span>
|
<span>
|
||||||
<DictTag :options="dict.ueEventCmState" :value="item.data.status" />
|
<DictTag :options="dict.ueEventCmState" :value="item.data.result" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { RESULT_CODE_ERROR } from '@/constants/result-constants';
|
import { RESULT_CODE_ERROR } from '@/constants/result-constants';
|
||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
import { onBeforeUnmount, ref } from 'vue';
|
||||||
import {
|
import {
|
||||||
eventListParse,
|
eventListParse,
|
||||||
eventItemParseAndPush,
|
eventItemParseAndPush,
|
||||||
@@ -49,7 +49,7 @@ export default function useWS() {
|
|||||||
// 普通信息
|
// 普通信息
|
||||||
switch (requestId) {
|
switch (requestId) {
|
||||||
// AMF_UE会话事件
|
// AMF_UE会话事件
|
||||||
case 'amf_1010':
|
case 'amf_1010_001':
|
||||||
if (Array.isArray(data.rows)) {
|
if (Array.isArray(data.rows)) {
|
||||||
eventListParse('amf_ue', data);
|
eventListParse('amf_ue', data);
|
||||||
}
|
}
|
||||||
@@ -95,13 +95,13 @@ export default function useWS() {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// MME_UE会话事件
|
// MME_UE会话事件
|
||||||
case '1011_001':
|
case '1011':
|
||||||
if (data.data) {
|
if (data.data) {
|
||||||
queue.add(() => eventItemParseAndPush('mme_ue', data.data));
|
queue.add(() => eventItemParseAndPush('mme_ue', data.data));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// IMS_CDR会话事件
|
// IMS_CDR会话事件
|
||||||
case '1005_001':
|
case '1005':
|
||||||
if (data.data) {
|
if (data.data) {
|
||||||
queue.add(() => eventItemParseAndPush('ims_cdr', data.data));
|
queue.add(() => eventItemParseAndPush('ims_cdr', data.data));
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ export default function useWS() {
|
|||||||
function userActivitySend() {
|
function userActivitySend() {
|
||||||
// AMF_UE会话事件
|
// AMF_UE会话事件
|
||||||
ws.send({
|
ws.send({
|
||||||
requestId: 'amf_1010',
|
requestId: 'amf_1010_001',
|
||||||
type: 'amf_ue',
|
type: 'amf_ue',
|
||||||
data: {
|
data: {
|
||||||
neType: 'AMF',
|
neType: 'AMF',
|
||||||
@@ -189,11 +189,11 @@ export default function useWS() {
|
|||||||
/**订阅通道组
|
/**订阅通道组
|
||||||
*
|
*
|
||||||
* 指标UPF (GroupID:12_neId)
|
* 指标UPF (GroupID:12_neId)
|
||||||
* AMF_UE会话事件(GroupID:1010)
|
* AMF_UE会话事件(GroupID:1010_neId)
|
||||||
* MME_UE会话事件(GroupID:1011_neId)
|
* MME_UE会话事件(GroupID:1011_neId)
|
||||||
* IMS_CDR会话事件(GroupID:1005_neId)
|
* IMS_CDR会话事件(GroupID:1005_neId)
|
||||||
*/
|
*/
|
||||||
subGroupID: '12_' + rmUid + ',1010,1011_001,1005_001',
|
subGroupID: '12_' + rmUid + ',1010,1011,1005',
|
||||||
},
|
},
|
||||||
onmessage: wsMessage,
|
onmessage: wsMessage,
|
||||||
onerror: (ev: any) => {
|
onerror: (ev: any) => {
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ function fnSelectNe(value: any, option: any) {
|
|||||||
for (var key in upfTotalFlow.value) {
|
for (var key in upfTotalFlow.value) {
|
||||||
upfTotalFlow.value[key].requestFlag = false;
|
upfTotalFlow.value[key].requestFlag = false;
|
||||||
}
|
}
|
||||||
loadData();
|
// loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义一个方法返回 views 容器
|
// 定义一个方法返回 views 容器
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ let tableState: TabeStateType = reactive({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**表格字段列 */
|
/**表格字段列 */
|
||||||
let tableColumns: ColumnsType = [
|
let tableColumns = ref<ColumnsType>([
|
||||||
{
|
{
|
||||||
title: t('common.rowId'),
|
title: t('common.rowId'),
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
@@ -238,7 +238,7 @@ let tableColumns: ColumnsType = [
|
|||||||
key: 'id',
|
key: 'id',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
];
|
]);
|
||||||
|
|
||||||
/**表格分页器参数 */
|
/**表格分页器参数 */
|
||||||
let tablePagination = reactive({
|
let tablePagination = reactive({
|
||||||
@@ -818,14 +818,14 @@ onBeforeUnmount(() => {
|
|||||||
</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, i) in u.usedUnitContainer"
|
v-for="(udata, i) in u.usedUnitContainer"
|
||||||
style="display: flex"
|
style="display: flex"
|
||||||
>
|
>
|
||||||
<strong style="margin-right: 12px">
|
<!-- <strong style="margin-right: 12px">
|
||||||
{{ i }}
|
{{ i }}
|
||||||
</strong>
|
</strong> -->
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<span>Data Total Volume: </span>
|
<span>Data Total Volume: </span>
|
||||||
@@ -839,10 +839,10 @@ onBeforeUnmount(() => {
|
|||||||
<span>Data Volume Uplink: </span>
|
<span>Data Volume Uplink: </span>
|
||||||
<span>{{ udata.dataVolumeUplink }}</span>
|
<span>{{ udata.dataVolumeUplink }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<!-- <div>
|
||||||
<span>Time: </span>
|
<span>Time: </span>
|
||||||
<span>{{ udata.time }}</span>
|
<span>{{ udata.time }}</span>
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
664
src/views/dashboard/smfCDRByIMSI/index.vue
Normal file
664
src/views/dashboard/smfCDRByIMSI/index.vue
Normal file
@@ -0,0 +1,664 @@
|
|||||||
|
<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);
|
||||||
|
const downlinkValueF = parseSizeFromByte(downlinkValue);
|
||||||
|
return `
|
||||||
|
<div style="font-weight: bold;">${title}</div>
|
||||||
|
<div>Uplink: ${uplinkValueF}</div>
|
||||||
|
<div>Downlink: ${downlinkValueF}</div>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
toolbox: {
|
||||||
|
feature: {
|
||||||
|
dataZoom: {
|
||||||
|
yAxisIndex: 'none',
|
||||||
|
},
|
||||||
|
saveAsImage: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisPointer: {
|
||||||
|
link: [
|
||||||
|
{
|
||||||
|
xAxisIndex: 'all',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
show: true,
|
||||||
|
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: '30%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
left: '10%',
|
||||||
|
right: 50,
|
||||||
|
top: '50%',
|
||||||
|
height: '30%',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
axisLine: { onZero: true },
|
||||||
|
data: [], // x轴初始数据
|
||||||
|
axisLabel: {
|
||||||
|
show: true, // 显示标签
|
||||||
|
rotate: 15, // 设置倾斜角度(如15度)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gridIndex: 1,
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
axisLine: { onZero: true },
|
||||||
|
data: [], // x轴初始数据
|
||||||
|
axisLabel: {
|
||||||
|
show: false, // 隐藏第二个 x 轴的标签
|
||||||
|
},
|
||||||
|
position: 'top',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
name: 'Uplink (Byte)',
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gridIndex: 1,
|
||||||
|
name: 'Downlink (Byte)',
|
||||||
|
type: 'value',
|
||||||
|
inverse: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'Uplink',
|
||||||
|
type: 'line',
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Downlink',
|
||||||
|
type: 'line',
|
||||||
|
xAxisIndex: 1,
|
||||||
|
yAxisIndex: 1,
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
/**绘制图表 */
|
||||||
|
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.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: '',
|
||||||
|
sortField: 'timestamp',
|
||||||
|
sortOrder: 'desc',
|
||||||
|
/**开始时间 */
|
||||||
|
startTime: undefined as undefined | number,
|
||||||
|
/**结束时间 */
|
||||||
|
endTime: undefined as undefined | number,
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 1000,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
queryRangePicker.value = [dayjs().startOf('hour'), dayjs().endOf('hour')];
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = reactive({
|
||||||
|
/**表格数据 */
|
||||||
|
data: [] as any[],
|
||||||
|
/**表格总数 */
|
||||||
|
total: 0,
|
||||||
|
/**表格加载状态 */
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询列表, 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 {
|
||||||
|
cdrChart.showLoading('default', {
|
||||||
|
text: 'No Data',
|
||||||
|
fontSize: 16, // 字体大小
|
||||||
|
});
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实时数据
|
||||||
|
*/
|
||||||
|
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(i => {
|
||||||
|
if (i.neType === 'SMF') {
|
||||||
|
arr.push({ value: i.neId, label: i.neName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neOtions.value = arr;
|
||||||
|
if (arr.length > 0) {
|
||||||
|
queryParams.neId = arr[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
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="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-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-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false">
|
||||||
|
<!-- 图数据 -->
|
||||||
|
<div ref="cdrChartDom" style="height: 600px; width: 100%"></div>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
||||||
@@ -130,26 +130,17 @@ export function edgeLineAnimateState() {
|
|||||||
|
|
||||||
// circle-move 圆点沿边运动
|
// circle-move 圆点沿边运动
|
||||||
if (name === 'circle-move') {
|
if (name === 'circle-move') {
|
||||||
let back1 = group.find(
|
const backArr = group.findAll((ele: any) =>
|
||||||
(ele: any) => ele.get('name') === 'circle-stroke1'
|
ele.get('name').startsWith('circle-stroke')
|
||||||
);
|
);
|
||||||
if (back1) {
|
|
||||||
back1.remove();
|
|
||||||
back1.destroy();
|
|
||||||
}
|
|
||||||
let back2 = group.find(
|
|
||||||
(ele: any) => ele.get('name') === 'circle-stroke2'
|
|
||||||
);
|
|
||||||
if (back2) {
|
|
||||||
back2.remove();
|
|
||||||
back2.destroy();
|
|
||||||
}
|
|
||||||
if (value) {
|
if (value) {
|
||||||
|
if (backArr.length > 0) return;
|
||||||
|
|
||||||
// 第一个矩形边
|
// 第一个矩形边
|
||||||
const fillColor = typeof value === 'string' ? value : '#1890ff';
|
const fillColor = typeof value === 'string' ? value : '#1890ff';
|
||||||
// 边缘路径的起始位置
|
// 边缘路径的起始位置
|
||||||
const startPoint = keyShape.getPoint(0);
|
const startPoint = keyShape.getPoint(0);
|
||||||
back1 = group.addShape('circle', {
|
const back1 = group.addShape('circle', {
|
||||||
attrs: {
|
attrs: {
|
||||||
x: startPoint.x,
|
x: startPoint.x,
|
||||||
y: startPoint.y,
|
y: startPoint.y,
|
||||||
@@ -159,6 +150,7 @@ export function edgeLineAnimateState() {
|
|||||||
// 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性
|
// 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性
|
||||||
name: 'circle-stroke1',
|
name: 'circle-stroke1',
|
||||||
});
|
});
|
||||||
|
back1.show();
|
||||||
back1.animate(
|
back1.animate(
|
||||||
(ratio: any) => {
|
(ratio: any) => {
|
||||||
// 每帧中的操作。比率范围从 0 到 1,表示动画的进度。返回修改后的配置
|
// 每帧中的操作。比率范围从 0 到 1,表示动画的进度。返回修改后的配置
|
||||||
@@ -177,7 +169,7 @@ export function edgeLineAnimateState() {
|
|||||||
);
|
);
|
||||||
// 第二个矩形边
|
// 第二个矩形边
|
||||||
const endPoint = keyShape.getPoint(1);
|
const endPoint = keyShape.getPoint(1);
|
||||||
back2 = group.addShape('circle', {
|
const back2 = group.addShape('circle', {
|
||||||
zIndex: -2,
|
zIndex: -2,
|
||||||
attrs: {
|
attrs: {
|
||||||
x: endPoint.x,
|
x: endPoint.x,
|
||||||
@@ -188,7 +180,7 @@ export function edgeLineAnimateState() {
|
|||||||
// 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性
|
// 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性
|
||||||
name: 'circle-stroke2',
|
name: 'circle-stroke2',
|
||||||
});
|
});
|
||||||
|
back2.show();
|
||||||
back2.animate(
|
back2.animate(
|
||||||
(ratio: any) => {
|
(ratio: any) => {
|
||||||
// 每帧中的操作。比率范围从 0 到 1,表示动画的进度。返回修改后的配置
|
// 每帧中的操作。比率范围从 0 到 1,表示动画的进度。返回修改后的配置
|
||||||
@@ -205,6 +197,14 @@ export function edgeLineAnimateState() {
|
|||||||
duration: 2 * 1000, // 执行一次的持续时间
|
duration: 2 * 1000, // 执行一次的持续时间
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
if (backArr.length <= 0) return;
|
||||||
|
backArr.forEach((ele: any) => {
|
||||||
|
ele.hide();
|
||||||
|
ele.remove();
|
||||||
|
ele.destroy();
|
||||||
|
});
|
||||||
|
backArr.length = 0;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -212,11 +212,7 @@ export function edgeLineAnimateState() {
|
|||||||
// line-dash 虚线运动
|
// line-dash 虚线运动
|
||||||
if (name === 'line-dash') {
|
if (name === 'line-dash') {
|
||||||
if (value) {
|
if (value) {
|
||||||
keyShape.stopAnimate();
|
if (keyShape.cfg.animating) return;
|
||||||
keyShape.attr({
|
|
||||||
lineDash: null,
|
|
||||||
lineDashOffset: null,
|
|
||||||
});
|
|
||||||
let index = 0;
|
let index = 0;
|
||||||
keyShape.animate(
|
keyShape.animate(
|
||||||
() => {
|
() => {
|
||||||
@@ -234,6 +230,12 @@ export function edgeLineAnimateState() {
|
|||||||
duration: 3000, // 执行一次的持续时间
|
duration: 3000, // 执行一次的持续时间
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
keyShape.stopAnimate();
|
||||||
|
keyShape.attr({
|
||||||
|
lineDash: null,
|
||||||
|
lineDashOffset: null,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -246,6 +248,7 @@ export function edgeLineAnimateState() {
|
|||||||
back.remove();
|
back.remove();
|
||||||
back.destroy();
|
back.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
const { path, stroke, lineWidth } = keyShape.attr();
|
const { path, stroke, lineWidth } = keyShape.attr();
|
||||||
back = group.addShape('path', {
|
back = group.addShape('path', {
|
||||||
attrs: {
|
attrs: {
|
||||||
|
|||||||
@@ -300,12 +300,23 @@ export function nodeImageAnimateState() {
|
|||||||
ele.get('name').startsWith('circle-shape')
|
ele.get('name').startsWith('circle-shape')
|
||||||
);
|
);
|
||||||
if (value) {
|
if (value) {
|
||||||
if (Array.isArray(backArr) && backArr.length === 3) return;
|
const fillColor = typeof value === 'string' ? value : '#f5222d';
|
||||||
|
// 移除
|
||||||
|
if (Array.isArray(backArr) && backArr.length >= 3) {
|
||||||
|
for (const back of backArr) {
|
||||||
|
back.stopAnimate();
|
||||||
|
back.hide();
|
||||||
|
back.remove();
|
||||||
|
back.destroy();
|
||||||
|
}
|
||||||
|
backArr.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据大小确定半径
|
||||||
const size = Array.isArray(model?.size) ? model?.size : [40, 40];
|
const size = Array.isArray(model?.size) ? model?.size : [40, 40];
|
||||||
const x = size[0] / 2;
|
const x = size[0] / 2;
|
||||||
const y = -size[1] / 2;
|
const y = -size[1] / 2;
|
||||||
const r = 3;
|
const r = 3;
|
||||||
const fillColor = typeof value === 'string' ? value : '#f5222d';
|
|
||||||
|
|
||||||
// 第一个背景圆
|
// 第一个背景圆
|
||||||
const back1 = group.addShape('circle', {
|
const back1 = group.addShape('circle', {
|
||||||
|
|||||||
593
src/views/ne-data/base-station/components/list.vue
Normal file
593
src/views/ne-data/base-station/components/list.vue
Normal file
@@ -0,0 +1,593 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, onMounted, toRaw } 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 useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import {
|
||||||
|
addAMFNbState,
|
||||||
|
delAMFNbState,
|
||||||
|
editAMFNbState,
|
||||||
|
listAMFNbStatelist,
|
||||||
|
} from '@/api/neData/amf';
|
||||||
|
const { t } = useI18n();
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const nbState = ref<DictType[]>([
|
||||||
|
{
|
||||||
|
value: 'ON',
|
||||||
|
label: 'Online',
|
||||||
|
tagType: 'green',
|
||||||
|
tagClass: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'OFF',
|
||||||
|
label: '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: 'Index',
|
||||||
|
dataIndex: 'index',
|
||||||
|
align: 'left',
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Position',
|
||||||
|
dataIndex: 'position',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Address',
|
||||||
|
dataIndex: 'address',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'State',
|
||||||
|
dataIndex: 'state',
|
||||||
|
key: 'state',
|
||||||
|
align: 'left',
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '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(delAMFNbState(neId, v));
|
||||||
|
}
|
||||||
|
if (neType === 'AMF') {
|
||||||
|
reqArr.push(delAMFNbState(neId, v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (neType === 'MME') {
|
||||||
|
// reqArr.push(delAMFNbState(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 = listAMFNbStatelist(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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**新增框或修改框是否显示 */
|
||||||
|
openByEdit: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**表单数据 */
|
||||||
|
from: Record<string, any>;
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
openByEdit: false,
|
||||||
|
title: 'NB Config List',
|
||||||
|
from: {
|
||||||
|
index: undefined,
|
||||||
|
address: '',
|
||||||
|
name: '',
|
||||||
|
position: '',
|
||||||
|
},
|
||||||
|
confirmLoading: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**对话框内表单属性和校验规则 */
|
||||||
|
const modalStateFrom = Form.useForm(
|
||||||
|
modalState.from,
|
||||||
|
reactive({
|
||||||
|
address: [{ required: true, message: `text content length 0~64` }],
|
||||||
|
name: [{ required: true, message: `text content length 0~64` }],
|
||||||
|
position: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: `location description. Prohibition of spaces, length of text content 0-64`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出显示为 新增或者修改
|
||||||
|
* @param noticeId 网元id, 不传为新增
|
||||||
|
*/
|
||||||
|
function fnModalVisibleByEdit(edit?: string | number) {
|
||||||
|
if (!edit) {
|
||||||
|
modalStateFrom.resetFields(); //重置表单
|
||||||
|
modalState.title = 'Add Radio Info';
|
||||||
|
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 = 'Edit Radio Info';
|
||||||
|
modalState.openByEdit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出确认执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalOk() {
|
||||||
|
const neID = queryParams.neId;
|
||||||
|
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 = modalState.title.startsWith('Edit')
|
||||||
|
? editAMFNbState(neID, from)
|
||||||
|
: addAMFNbState(neID, from);
|
||||||
|
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;
|
||||||
|
modalStateFrom.resetFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
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="6" :md="12" :xs="24">
|
||||||
|
<a-form-item label="State" name="state">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.state"
|
||||||
|
:options="nbState"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
/>
|
||||||
|
</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-space>
|
||||||
|
</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="index"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: tableColumns.length * 120 }"
|
||||||
|
:row-selection="{
|
||||||
|
type: 'checkbox',
|
||||||
|
selectedRowKeys: tableState.selectedRowKeys,
|
||||||
|
onChange: fnTableSelectedRowKeys,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<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="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="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-item
|
||||||
|
label="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>
|
||||||
|
</ProModal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
557
src/views/ne-data/base-station/components/topology.vue
Normal file
557
src/views/ne-data/base-station/components/topology.vue
Normal file
@@ -0,0 +1,557 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, ref, onBeforeUnmount, useTemplateRef } from 'vue';
|
||||||
|
import { Graph, GraphData, Menu, Tooltip } 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';
|
||||||
|
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();
|
||||||
|
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: 228px;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div><strong>${t('views.monitor.topology.state')}:</strong><span>
|
||||||
|
${
|
||||||
|
nInfo.state === 'ON'
|
||||||
|
? t('views.monitor.topology.normalcy')
|
||||||
|
: t('views.monitor.topology.exceptions')
|
||||||
|
}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>OnTime:</strong><span>
|
||||||
|
${nInfo.onTime ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>OffTime:</strong><span>
|
||||||
|
${nInfo.offTime ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div>===========================</div>
|
||||||
|
<div><strong>ID:</strong><span>${nInfo.index}</span></div>
|
||||||
|
<div><strong>${t('views.monitor.topology.name')}:</strong><span>
|
||||||
|
${nInfo.name ?? '--'}
|
||||||
|
</span></div>
|
||||||
|
<div><strong>Address:</strong><span>${nInfo.address}</span></div>
|
||||||
|
<div><strong>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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**图事件 */
|
||||||
|
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: 'BT', // 布局的方向,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.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 (['AMF'].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: `${nb.name}`,
|
||||||
|
img: parseBasePath('/svg/base5G.svg'),
|
||||||
|
nInfo: nb,
|
||||||
|
nType: 'GNB',
|
||||||
|
});
|
||||||
|
data.edges?.push({
|
||||||
|
source: item.id,
|
||||||
|
target: id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item.nType === 'MME') {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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') {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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"> Radio State Graph </a-space>
|
||||||
|
</template>
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-button type="default" @click.prevent="fullscreen()">
|
||||||
|
<template #icon>
|
||||||
|
<FullscreenExitOutlined v-if="isFullscreen" />
|
||||||
|
<FullscreenOutlined v-else />
|
||||||
|
</template>
|
||||||
|
Full Screen
|
||||||
|
</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>
|
||||||
40
src/views/ne-data/base-station/index.vue
Normal file
40
src/views/ne-data/base-station/index.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
onMounted,
|
||||||
|
type Component,
|
||||||
|
defineAsyncComponent,
|
||||||
|
shallowRef,
|
||||||
|
} from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
const defineComponent = shallowRef<Component | null>(null);
|
||||||
|
|
||||||
|
function fnSwitch(name: string) {
|
||||||
|
if (name === 'topology') {
|
||||||
|
defineComponent.value = defineAsyncComponent(
|
||||||
|
() => import('@/views/ne-data/base-station/components/topology.vue')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (name === 'list') {
|
||||||
|
defineComponent.value = defineAsyncComponent(
|
||||||
|
() => import('@/views/ne-data/base-station/components/list.vue')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fnSwitch('topology');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<template #extra>
|
||||||
|
<a-button @click="fnSwitch('list')">List</a-button>
|
||||||
|
<a-button type="primary" @click="fnSwitch('topology')">Topology</a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<component :is="defineComponent" />
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
Reference in New Issue
Block a user