Compare commits
101 Commits
practical-
...
2.2409.4-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3a452cfd8 | ||
|
|
d81b8cdf38 | ||
|
|
39a417368a | ||
|
|
adfce5d2f7 | ||
|
|
977286d6b3 | ||
|
|
c33000045a | ||
|
|
b995ac378a | ||
|
|
6bea64f345 | ||
|
|
94886e255e | ||
|
|
45f66afe52 | ||
|
|
b9105c1e77 | ||
|
|
909d306942 | ||
|
|
f7273457e9 | ||
|
|
2e5ad2f65d | ||
|
|
776e9c5837 | ||
|
|
0d4979d3d9 | ||
|
|
686c7dd273 | ||
|
|
d41b308c6d | ||
|
|
84dac247d2 | ||
|
|
f8439bb40a | ||
|
|
d268d920e7 | ||
|
|
f730ef1e3a | ||
|
|
af1ce32063 | ||
|
|
678ff2d09d | ||
|
|
48f674b6ef | ||
|
|
02f0820a69 | ||
|
|
ca8605fd6e | ||
|
|
6d5e96421b | ||
|
|
bcc29007bf | ||
|
|
bdf904078d | ||
|
|
e37cfa5066 | ||
|
|
f1bff23bbc | ||
|
|
53106ddb5c | ||
|
|
3a04882fe5 | ||
|
|
19202a5e81 | ||
|
|
7b311ff673 | ||
|
|
9dba98e0ee | ||
|
|
71338670f0 | ||
|
|
7dcdfabce2 | ||
|
|
ddfe1723c9 | ||
|
|
57b5f76db7 | ||
|
|
9ac3524877 | ||
|
|
ca82a0a74b | ||
|
|
23007c3bf2 | ||
|
|
5d69d7612a | ||
|
|
ddd8930af4 | ||
|
|
757f2ec20a | ||
|
|
30caa79424 | ||
|
|
e3f83a0b98 | ||
|
|
147a3ed77b | ||
|
|
5d35d950b3 | ||
|
|
6874508d3f | ||
|
|
33f468209a | ||
|
|
e8ef2816df | ||
|
|
e38d7bbffa | ||
|
|
2f1265c47a | ||
|
|
66b6b60505 | ||
|
|
249d14320d | ||
|
|
313b90ad31 | ||
|
|
2ebc90e974 | ||
|
|
640257dd55 | ||
|
|
c1a3ce8068 | ||
|
|
0080e9c26e | ||
|
|
2ccafe622d | ||
|
|
d7a515ed9a | ||
| cd82b71b77 | |||
|
|
9d6a7dcd9c | ||
|
|
46c2affcc8 | ||
|
|
3d00a80588 | ||
|
|
a3c1fe154f | ||
|
|
07dce5a27e | ||
|
|
255cf026a6 | ||
|
|
840ea56c42 | ||
|
|
09917cc9c9 | ||
|
|
4c9fe192f2 | ||
|
|
32ec55d44e | ||
|
|
527cf89d1a | ||
|
|
ac7b57c0ae | ||
|
|
8be1a8968e | ||
|
|
999ccf64ad | ||
|
|
03352f3aa8 | ||
|
|
61a58fc661 | ||
|
|
4268fa3198 | ||
|
|
f6b62c6c7e | ||
|
|
b4cbc1c190 | ||
|
|
1871f6f656 | ||
|
|
409f9836a6 | ||
|
|
b3f40ee683 | ||
|
|
aa07b51663 | ||
|
|
19b77ed005 | ||
|
|
06503fd079 | ||
|
|
2321dacd2a | ||
|
|
a8b4e91b95 | ||
|
|
a5075bef43 | ||
|
|
f4ffbc1c86 | ||
|
|
6cafa284c7 | ||
|
|
049c0e7a0f | ||
|
|
377ffc6e10 | ||
|
|
858431e86e | ||
|
|
70fca5ca41 | ||
|
|
e972d14a9a |
@@ -11,7 +11,7 @@ VITE_APP_NAME = "Core Network OMC"
|
|||||||
VITE_APP_CODE = "OMC"
|
VITE_APP_CODE = "OMC"
|
||||||
|
|
||||||
# 应用版本
|
# 应用版本
|
||||||
VITE_APP_VERSION = "2.240801"
|
VITE_APP_VERSION = "2.240927"
|
||||||
|
|
||||||
# 接口基础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.240801"
|
VITE_APP_VERSION = "2.240927"
|
||||||
|
|
||||||
# 接口基础URL地址-不带/后缀
|
# 接口基础URL地址-不带/后缀
|
||||||
VITE_API_BASE_URL = "/omc-api"
|
VITE_API_BASE_URL = "/omc-api"
|
||||||
|
|||||||
19
README.md
19
README.md
@@ -8,14 +8,8 @@
|
|||||||
## 测试环境
|
## 测试环境
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Jenkins: http://192.168.2.166:3185/
|
|
||||||
Nginx: http://192.168.2.166:3188/#/index
|
Nginx: http://192.168.2.166:3188/#/index
|
||||||
后端暴露端口: http://192.168.2.166:33030
|
后端暴露端口: http://192.168.2.166:33030
|
||||||
|
|
||||||
|
|
||||||
新网管:192.168.5.13
|
|
||||||
旧网管:192.168.5.14
|
|
||||||
登录账户:manager/manager
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 程序命令
|
## 程序命令
|
||||||
@@ -59,16 +53,5 @@ export NODE_OPTIONS=--max-old-space-size=50000
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
https://192.168.5.23/
|
https://192.168.5.23/
|
||||||
admin
|
admin / admin
|
||||||
admin
|
|
||||||
```
|
|
||||||
|
|
||||||
## k8s
|
|
||||||
|
|
||||||
master 192.168.5.27 agtuser/admin123
|
|
||||||
|
|
||||||
https://192.168.5.27:31325/#/workloads?namespace=default
|
|
||||||
|
|
||||||
```text
|
|
||||||
eyJhbGciOiJSUzI1NiIsImtpZCI6ImZFVUhIb1puLW04M1dfSUYyRU8zWlZueXBpNUh4T0hTRVlzU19jNlVGQ0kifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLW44ZzRtIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI2M2NmYjAyNS01ZmQ0LTQ0ZTgtOTdiNC0yYWRiYWIxNzc5M2MiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.R3GRygFOjngTj-mEMBAHDeBxm3lpsXZYvC6cdTxByONtLrcMXDebwNVeKtAZ1V9qh2OrjD8n9CIygjULGPdfV6S520vjMh7Oa2q68nOyW49DNWQyYD8xLo-dQ6sX07fI7X_I3H35YUWW80jJAXjJawqIGXBSMG5intlo4tLTUSXmjCfhoQvFsgeRWu0j76pDvhMAvLPcgEXfTCi9tyL3yqJBIKONcKwmMlJeaKSR3pQk3KiibqrBO0MZclRozpke6J0ulfzTemwDDyCqBZmLsRPZ2yDd5hVBIJ9bHEcK0a25NmSFFzmd8XWQPZwg3Y4IbbY-8UhByGq0p9xS-7pGCQ
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
"@codemirror/lang-yaml": "^6.1.1",
|
"@codemirror/lang-yaml": "^6.1.1",
|
||||||
"@codemirror/merge": "^6.6.3",
|
"@codemirror/merge": "^6.6.3",
|
||||||
"@codemirror/theme-one-dark": "^6.1.2",
|
"@codemirror/theme-one-dark": "^6.1.2",
|
||||||
"@tato30/vue-pdf": "~1.9.7",
|
"@tato30/vue-pdf": "^1.10.0",
|
||||||
"@vueuse/core": "~10.10.1",
|
"@vueuse/core": "~10.10.1",
|
||||||
"@xterm/addon-fit": "^0.10.0",
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
"@xterm/xterm": "^5.5.0",
|
"@xterm/xterm": "^5.5.0",
|
||||||
@@ -26,10 +26,11 @@
|
|||||||
"antdv-pro-layout": "~3.3.5",
|
"antdv-pro-layout": "~3.3.5",
|
||||||
"antdv-pro-modal": "^3.1.0",
|
"antdv-pro-modal": "^3.1.0",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
|
"crypto-js": "^4.2.0",
|
||||||
"dayjs": "^1.11.11",
|
"dayjs": "^1.11.11",
|
||||||
"echarts": "~5.5.0",
|
"echarts": "~5.5.0",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"intl-tel-input": "~23.0.12",
|
"intl-tel-input": "^23.8.1",
|
||||||
"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",
|
||||||
@@ -43,6 +44,7 @@
|
|||||||
"xlsx": "~0.18.5"
|
"xlsx": "~0.18.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/file-saver": "^2.0.7",
|
"@types/file-saver": "^2.0.7",
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
"@types/node": "^18.0.0",
|
"@types/node": "^18.0.0",
|
||||||
|
|||||||
BIN
public/wiregasm/test_ethernet.pcap
Normal file
BIN
public/wiregasm/test_ethernet.pcap
Normal file
Binary file not shown.
BIN
public/wiregasm/wiregasm.data.gz
Normal file
BIN
public/wiregasm/wiregasm.data.gz
Normal file
Binary file not shown.
BIN
public/wiregasm/wiregasm.wasm.gz
Normal file
BIN
public/wiregasm/wiregasm.wasm.gz
Normal file
Binary file not shown.
9768
public/wiregasm/wiregasm_load.js
Normal file
9768
public/wiregasm/wiregasm_load.js
Normal file
File diff suppressed because one or more lines are too long
166
public/wiregasm/wiregasm_new.js
Normal file
166
public/wiregasm/wiregasm_new.js
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/**
|
||||||
|
* Wraps the WiregasmLib lib functionality and manages a single DissectSession
|
||||||
|
*/
|
||||||
|
class Wiregasm {
|
||||||
|
constructor() {
|
||||||
|
this.initialized = false;
|
||||||
|
this.session = null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Initialize the wrapper and the Wiregasm module
|
||||||
|
*
|
||||||
|
* @param loader Loader function for the Emscripten module
|
||||||
|
* @param overrides Overrides
|
||||||
|
*/
|
||||||
|
async init(loader, overrides = {}, beforeInit = null) {
|
||||||
|
if (this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.lib = await loader(overrides);
|
||||||
|
this.uploadDir = this.lib.getUploadDirectory();
|
||||||
|
this.pluginsDir = this.lib.getPluginsDirectory();
|
||||||
|
if (beforeInit !== null) {
|
||||||
|
await beforeInit(this.lib);
|
||||||
|
}
|
||||||
|
this.lib.init();
|
||||||
|
this.initialized = true;
|
||||||
|
}
|
||||||
|
list_modules() {
|
||||||
|
return this.lib.listModules();
|
||||||
|
}
|
||||||
|
list_prefs(module) {
|
||||||
|
return this.lib.listPreferences(module);
|
||||||
|
}
|
||||||
|
apply_prefs() {
|
||||||
|
this.lib.applyPreferences();
|
||||||
|
}
|
||||||
|
set_pref(module, key, value) {
|
||||||
|
const ret = this.lib.setPref(module, key, value);
|
||||||
|
if (ret.code != PrefSetResult.PREFS_SET_OK) {
|
||||||
|
const message =
|
||||||
|
ret.error != '' ? ret.error : preferenceSetCodeToError(ret.code);
|
||||||
|
throw new Error(
|
||||||
|
`Failed to set preference (${module}.${key}): ${message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get_pref(module, key) {
|
||||||
|
const response = this.lib.getPref(module, key);
|
||||||
|
if (response.code != 0) {
|
||||||
|
throw new Error(`Failed to get preference (${module}.${key})`);
|
||||||
|
}
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Check the validity of a filter expression.
|
||||||
|
*
|
||||||
|
* @param filter A display filter expression
|
||||||
|
*/
|
||||||
|
test_filter(filter) {
|
||||||
|
return this.lib.checkFilter(filter);
|
||||||
|
}
|
||||||
|
complete_filter(filter) {
|
||||||
|
const out = this.lib.completeFilter(filter);
|
||||||
|
return {
|
||||||
|
err: out.err,
|
||||||
|
fields: vectorToArray(out.fields),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
reload_lua_plugins() {
|
||||||
|
this.lib.reloadLuaPlugins();
|
||||||
|
}
|
||||||
|
add_plugin(name, data, opts = {}) {
|
||||||
|
const path = this.pluginsDir + '/' + name;
|
||||||
|
this.lib.FS.writeFile(path, data, opts);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Load a packet trace file for analysis.
|
||||||
|
*
|
||||||
|
* @returns Response containing the status and summary
|
||||||
|
*/
|
||||||
|
load(name, data, opts = {}) {
|
||||||
|
if (this.session != null) {
|
||||||
|
this.session.delete();
|
||||||
|
}
|
||||||
|
const path = this.uploadDir + '/' + name;
|
||||||
|
this.lib.FS.writeFile(path, data, opts);
|
||||||
|
this.session = new this.lib.DissectSession(path);
|
||||||
|
return this.session.load();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get Packet List information for a range of packets.
|
||||||
|
*
|
||||||
|
* @param filter Output those frames that pass this filter expression
|
||||||
|
* @param skip Skip N frames
|
||||||
|
* @param limit Limit the output to N frames
|
||||||
|
*/
|
||||||
|
frames(filter, skip = 0, limit = 0) {
|
||||||
|
return this.session.getFrames(filter, skip, limit);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get full information about a frame including the protocol tree.
|
||||||
|
*
|
||||||
|
* @param number Frame number
|
||||||
|
*/
|
||||||
|
frame(num) {
|
||||||
|
return this.session.getFrame(num);
|
||||||
|
}
|
||||||
|
follow(follow, filter) {
|
||||||
|
return this.session.follow(follow, filter);
|
||||||
|
}
|
||||||
|
destroy() {
|
||||||
|
if (this.initialized) {
|
||||||
|
if (this.session !== null) {
|
||||||
|
this.session.delete();
|
||||||
|
this.session = null;
|
||||||
|
}
|
||||||
|
this.lib.destroy();
|
||||||
|
this.initialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the column headers
|
||||||
|
*/
|
||||||
|
columns() {
|
||||||
|
const vec = this.lib.getColumns();
|
||||||
|
// convert it from a vector to array
|
||||||
|
return vectorToArray(vec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a Vector to a JS array
|
||||||
|
*
|
||||||
|
* @param vec Vector
|
||||||
|
* @returns JS array of the Vector contents
|
||||||
|
*/
|
||||||
|
function vectorToArray(vec) {
|
||||||
|
return new Array(vec.size()).fill(0).map((_, id) => vec.get(id));
|
||||||
|
}
|
||||||
|
function preferenceSetCodeToError(code) {
|
||||||
|
switch (code) {
|
||||||
|
case PrefSetResult.PREFS_SET_SYNTAX_ERR:
|
||||||
|
return 'Syntax error in string';
|
||||||
|
case PrefSetResult.PREFS_SET_NO_SUCH_PREF:
|
||||||
|
return 'No such preference';
|
||||||
|
case PrefSetResult.PREFS_SET_OBSOLETE:
|
||||||
|
return 'Preference used to exist but no longer does';
|
||||||
|
default:
|
||||||
|
return 'Unknown error';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof exports === 'object' && typeof module === 'object') {
|
||||||
|
module.exports = Wiregasm;
|
||||||
|
module.exports = vectorToArray;
|
||||||
|
} else if (typeof define === 'function' && define['amd']) {
|
||||||
|
define([], function () {
|
||||||
|
return Wiregasm;
|
||||||
|
});
|
||||||
|
define([], function () {
|
||||||
|
return vectorToArray;
|
||||||
|
});
|
||||||
|
} else if (typeof exports === 'object') {
|
||||||
|
exports['loadWiregasm'] = Wiregasm;
|
||||||
|
exports['vectorToArray'] = vectorToArray;
|
||||||
|
}
|
||||||
162
public/wiregasm/worker.js
Normal file
162
public/wiregasm/worker.js
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
// load the Wiregasm library
|
||||||
|
importScripts(
|
||||||
|
'/wiregasm/wiregasm_new.js', // self-compilation es5
|
||||||
|
'/wiregasm/wiregasm_load.js'
|
||||||
|
// 'https://cdn.jsdelivr.net/npm/@goodtools/wiregasm/dist/wiregasm.js'
|
||||||
|
);
|
||||||
|
|
||||||
|
const wg = new Wiregasm();
|
||||||
|
|
||||||
|
const inflateRemoteBuffer = async url => {
|
||||||
|
const res = await fetch(url);
|
||||||
|
return await res.arrayBuffer();
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchPackages = async () => {
|
||||||
|
console.log('Fetching packages');
|
||||||
|
let [wasmBuffer, dataBuffer] = await Promise.all([
|
||||||
|
await inflateRemoteBuffer(
|
||||||
|
'/wiregasm/wiregasm.wasm.gz'
|
||||||
|
// 'https://cdn.jsdelivr.net/npm/@goodtools/wiregasm/dist/wiregasm.wasm.gz'
|
||||||
|
),
|
||||||
|
await inflateRemoteBuffer(
|
||||||
|
'/wiregasm/wiregasm.data.gz'
|
||||||
|
// 'https://cdn.jsdelivr.net/npm/@goodtools/wiregasm/dist/wiregasm.data.gz'
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return { wasmBuffer, dataBuffer };
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load the Wiregasm Wasm data
|
||||||
|
fetchPackages()
|
||||||
|
.then(({ wasmBuffer, dataBuffer }) => {
|
||||||
|
return wg.init(loadWiregasm, {
|
||||||
|
wasmBinary: wasmBuffer,
|
||||||
|
getPreloadedPackage() {
|
||||||
|
return dataBuffer;
|
||||||
|
},
|
||||||
|
handleStatus: (type, status) => {
|
||||||
|
postMessage({ type: 'status', code: type, status: status });
|
||||||
|
},
|
||||||
|
printErr: error => {
|
||||||
|
postMessage({ type: 'error', error: error });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
postMessage({ type: 'init' });
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
postMessage({ type: 'error', error: e });
|
||||||
|
});
|
||||||
|
|
||||||
|
/**Converts a Vector to a JS array */
|
||||||
|
function replacer(key, value) {
|
||||||
|
if (value.constructor.name.startsWith('Vector')) {
|
||||||
|
return vectorToArray(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event listener to receive messages from the main script
|
||||||
|
this.onmessage = ev => {
|
||||||
|
const data = ev.data;
|
||||||
|
switch (data.type) {
|
||||||
|
case 'close':
|
||||||
|
wg.destroy();
|
||||||
|
break;
|
||||||
|
case 'columns':
|
||||||
|
const columns = wg.columns();
|
||||||
|
if (Array.isArray(columns)) {
|
||||||
|
this.postMessage({ type: 'columns', data: columns });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'select': // select a frame
|
||||||
|
const number = data.number;
|
||||||
|
const frameData = wg.frame(number);
|
||||||
|
const frameDataToJSON = JSON.parse(JSON.stringify(frameData, replacer));
|
||||||
|
this.postMessage({
|
||||||
|
type: 'selected',
|
||||||
|
data: frameDataToJSON,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'frames': // get frames list
|
||||||
|
const skip = data.skip;
|
||||||
|
const limit = data.limit;
|
||||||
|
const filter = data.filter;
|
||||||
|
const framesData = wg.frames(filter, skip, limit);
|
||||||
|
const framesDataToJSON = JSON.parse(JSON.stringify(framesData, replacer));
|
||||||
|
this.postMessage({
|
||||||
|
type: 'frames',
|
||||||
|
data: framesDataToJSON,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'process-data':
|
||||||
|
const loadData = wg.load(data.name, new Uint8Array(data.data));
|
||||||
|
this.postMessage({ type: 'processed', data: loadData });
|
||||||
|
break;
|
||||||
|
case 'process':
|
||||||
|
const f = data.file;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.addEventListener('load', event => {
|
||||||
|
// XXX: this blocks the worker thread
|
||||||
|
const loadData = wg.load(f.name, new Uint8Array(event.target.result));
|
||||||
|
postMessage({ type: 'processed', data: loadData });
|
||||||
|
});
|
||||||
|
reader.readAsArrayBuffer(f);
|
||||||
|
break;
|
||||||
|
case 'check-filter':
|
||||||
|
const filterStr = data.filter;
|
||||||
|
const checkFilterRes = wg.lib.checkFilter(filterStr);
|
||||||
|
this.postMessage({ type: 'filter', data: checkFilterRes });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.type === 'reload-quick') {
|
||||||
|
if (wg.session) {
|
||||||
|
// TODO: this is a hack, we should be able to reload the session
|
||||||
|
const name = data.name;
|
||||||
|
const res = wg.session.load();
|
||||||
|
|
||||||
|
postMessage({ type: 'processed', name: name, data: res });
|
||||||
|
}
|
||||||
|
} else if (data.type === 'module-tree') {
|
||||||
|
const res = wg.list_modules();
|
||||||
|
// send it to the correct port
|
||||||
|
event.ports[0].postMessage({
|
||||||
|
result: JSON.parse(JSON.stringify(res, replacer)),
|
||||||
|
});
|
||||||
|
} else if (data.type === 'module-prefs') {
|
||||||
|
const res = wg.list_prefs(data.name);
|
||||||
|
// send it to the correct port
|
||||||
|
event.ports[0].postMessage({
|
||||||
|
result: JSON.parse(JSON.stringify(res, replacer)),
|
||||||
|
});
|
||||||
|
} else if (data.type === 'upload-file') {
|
||||||
|
const f = data.file;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.addEventListener('load', e => {
|
||||||
|
// XXX: this blocks the worker thread
|
||||||
|
const path = '/uploads/' + f.name;
|
||||||
|
wg.lib.FS.writeFile(path, Buffer.from(e.target.result));
|
||||||
|
event.ports[0].postMessage({ result: path });
|
||||||
|
});
|
||||||
|
reader.readAsArrayBuffer(f);
|
||||||
|
} else if (data.type === 'update-pref') {
|
||||||
|
try {
|
||||||
|
console.log(`set_pref(${data.module}, ${data.key}, ${data.value})`);
|
||||||
|
wg.set_pref(data.module, data.key, data.value);
|
||||||
|
event.ports[0].postMessage({ result: 'ok' });
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
`set_pref(${data.module}, ${data.key}, ${data.value}) failed: ${e.message}`
|
||||||
|
);
|
||||||
|
event.ports[0].postMessage({ error: e.message });
|
||||||
|
}
|
||||||
|
} else if (data.type === 'apply-prefs') {
|
||||||
|
console.log(`apply_prefs()`);
|
||||||
|
wg.apply_prefs();
|
||||||
|
event.ports[0].postMessage({ result: 'ok' });
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -241,6 +241,7 @@ export function listSync() {
|
|||||||
return request({
|
return request({
|
||||||
url: `/api/rest/faultManagement/v1/elementType/all/objectType/alarms`,
|
url: `/api/rest/faultManagement/v1/elementType/all/objectType/alarms`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
timeout: 180_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import { parseObjLineToHump } from '@/utils/parse-utils';
|
|||||||
import { parseDateToStr } from '@/utils/date-utils';
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
import useUserStore from '@/store/modules/user';
|
import useUserStore from '@/store/modules/user';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询列表
|
* 查询列表
|
||||||
* @param query 查询参数
|
* @param query 查询参数
|
||||||
@@ -28,8 +26,6 @@ export async function listAct(query: Record<string, any>) {
|
|||||||
querySQL += ` and pv_flag = '${query.pvFlag}' `;
|
querySQL += ` and pv_flag = '${query.pvFlag}' `;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (query.neId) {
|
if (query.neId) {
|
||||||
querySQL += ` and ne_id like '%${query.neId}%' `;
|
querySQL += ` and ne_id like '%${query.neId}%' `;
|
||||||
}
|
}
|
||||||
@@ -69,7 +65,6 @@ export async function listAct(query: Record<string, any>) {
|
|||||||
msg: result.msg,
|
msg: result.msg,
|
||||||
};
|
};
|
||||||
result.data.data.forEach((item: any) => {
|
result.data.data.forEach((item: any) => {
|
||||||
console.log(item)
|
|
||||||
const itemData = item['alarm_event'];
|
const itemData = item['alarm_event'];
|
||||||
if (Array.isArray(itemData)) {
|
if (Array.isArray(itemData)) {
|
||||||
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
|
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
|
||||||
@@ -84,12 +79,6 @@ export async function listAct(query: Record<string, any>) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 事件告警导出
|
* 事件告警导出
|
||||||
* @param query 查询参数
|
* @param query 查询参数
|
||||||
@@ -133,7 +122,3 @@ export async function exportAll(query: Record<string, any>) {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
53
src/api/logManage/exportFile.ts
Normal file
53
src/api/logManage/exportFile.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取下拉框数据
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getBakFile() {
|
||||||
|
return request({
|
||||||
|
url: '/lm/table/list',
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取对应类型的文件列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getBakFileList(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/lm/file/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远端文件
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function downFile(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/lm/file/${query.fileName}`,
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除远端获取文件
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delFile(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/lm/file/${query.fileName}`,
|
||||||
|
method: 'delete',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ export function login(data: Record<string, string>) {
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
data: data,
|
data: data,
|
||||||
whithToken: false,
|
whithToken: false,
|
||||||
|
crypto: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ export function register(data: Record<string, any>) {
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
data: data,
|
data: data,
|
||||||
whithToken: false,
|
whithToken: false,
|
||||||
|
crypto: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ export function getLoad(query: Record<string, any>) {
|
|||||||
url: '/monitor/load',
|
url: '/monitor/load',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ export function getSystemInfo() {
|
|||||||
return request({
|
return request({
|
||||||
url: '/monitor/system-info',
|
url: '/monitor/system-info',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
66
src/api/ne/neConfig.ts
Normal file
66
src/api/ne/neConfig.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置可用属性值列表指定网元类型全部无分页
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getAllNeConfig(neType: string) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/list/${neType}`,
|
||||||
|
method: 'get',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置数据信息
|
||||||
|
* @param params 数据 {neType,neId,paramName}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getNeConfigData(params: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
params,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置数据更新
|
||||||
|
* @param data 数据 {neType,neId,paramName:"参数名",paramData:{参数},loc:"层级index仅array"}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function editNeConfigData(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置数据新增(array)
|
||||||
|
* @param data 数据 {neType,neId,paramName:"参数名",paramData:{参数},loc:"层级index"}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addNeConfigData(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网元参数配置数据删除(array)
|
||||||
|
* @param params 数据 {neType,neId,paramName:"参数名",loc:"层级index"}
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delNeConfigData(params: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/ne/config/data`,
|
||||||
|
method: 'delete',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -36,6 +36,8 @@ export function addNeInfo(data: Record<string, any>) {
|
|||||||
url: `/ne/info`,
|
url: `/ne/info`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data,
|
data: data,
|
||||||
|
crypto: true,
|
||||||
|
timeout: 30_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +51,8 @@ export function updateNeInfo(data: Record<string, any>) {
|
|||||||
url: `/ne/info`,
|
url: `/ne/info`,
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: data,
|
data: data,
|
||||||
|
crypto: true,
|
||||||
|
timeout: 30_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,3 +40,16 @@ export function exportSMFDataCDR(data: Record<string, any>) {
|
|||||||
timeout: 60_000,
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMF-在线订阅用户列表信息
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listSMFSubscribers(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/smf/subscribers',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
42
src/api/neData/smsc.ts
Normal file
42
src/api/neData/smsc.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询SMSC-CDR会话事件
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listSMSCDataCDR(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/smsc/cdr/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMSC-CDR会话删除
|
||||||
|
* @param id 信息ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delSMSCDataCDR(cdrIds: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/smsc/cdr/${cdrIds}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMSC-CDR会话列表导出
|
||||||
|
* @param data 查询列表条件
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function exportSMSCDataCDR(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/smsc/cdr/export',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
19
src/api/perfManage/customData.ts
Normal file
19
src/api/perfManage/customData.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
import { parseObjLineToHump } from '@/utils/parse-utils';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新 查询自定义指标数据
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function listCustomData(query: Record<string, any>) {
|
||||||
|
// 发起请求
|
||||||
|
const result = await request({
|
||||||
|
url: `/pm/kpiC/report`,
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -8,57 +8,72 @@ import { parseDateToStr } from '@/utils/date-utils';
|
|||||||
* @param query 查询参数
|
* @param query 查询参数
|
||||||
* @returns object
|
* @returns object
|
||||||
*/
|
*/
|
||||||
export async function listCustom(query: Record<string, any>) {
|
// export async function listCustom(query: Record<string, any>) {
|
||||||
let totalSQL = 'select count(*) as total from pm_custom_title where 1=1 ';
|
// let totalSQL = 'select count(*) as total from pm_custom_title where 1=1 ';
|
||||||
let rowsSQL = 'select * from pm_custom_title where 1=1 ';
|
// let rowsSQL = 'select * from pm_custom_title where 1=1 ';
|
||||||
|
|
||||||
// 查询
|
// // 查询
|
||||||
let querySQL = '';
|
// let querySQL = '';
|
||||||
if (query.neType) {
|
// if (query.neType) {
|
||||||
querySQL += ` and ne_type like '%${query.neType}%' `;
|
// querySQL += ` and ne_type like '%${query.neType}%' `;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 排序
|
// // 排序
|
||||||
let sortSql = ' order by update_time ';
|
// let sortSql = ' order by update_time ';
|
||||||
if (query.sortOrder === 'asc') {
|
// if (query.sortOrder === 'asc') {
|
||||||
sortSql += ' asc ';
|
// sortSql += ' asc ';
|
||||||
} else {
|
// } else {
|
||||||
sortSql += ' desc ';
|
// sortSql += ' desc ';
|
||||||
}
|
// }
|
||||||
// 分页
|
// // 分页
|
||||||
const pageNum = (query.pageNum - 1) * query.pageSize;
|
// const pageNum = (query.pageNum - 1) * query.pageSize;
|
||||||
const limtSql = ` limit ${pageNum},${query.pageSize} `;
|
// const limtSql = ` limit ${pageNum},${query.pageSize} `;
|
||||||
|
|
||||||
|
// // 发起请求
|
||||||
|
// const result = await request({
|
||||||
|
// url: `/api/rest/databaseManagement/v1/select/omc_db/pm_custom_title`,
|
||||||
|
// method: 'get',
|
||||||
|
// params: {
|
||||||
|
// totalSQL: totalSQL + querySQL,
|
||||||
|
// rowsSQL: rowsSQL + querySQL + sortSql + limtSql,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // 解析数据
|
||||||
|
// if (result.code === RESULT_CODE_SUCCESS) {
|
||||||
|
// const data: DataList = {
|
||||||
|
// total: 0,
|
||||||
|
// rows: [],
|
||||||
|
// code: result.code,
|
||||||
|
// msg: result.msg,
|
||||||
|
// };
|
||||||
|
// result.data.data.forEach((item: any) => {
|
||||||
|
// const itemData = item['pm_custom_title'];
|
||||||
|
// if (Array.isArray(itemData)) {
|
||||||
|
// if (itemData.length === 1 && itemData[0]['total'] >= 0) {
|
||||||
|
// data.total = itemData[0]['total'];
|
||||||
|
// } else {
|
||||||
|
// data.rows = itemData.map(v => parseObjLineToHump(v));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// return data;
|
||||||
|
// }
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新 查询自定义指标
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function listCustom(query?: Record<string, any>) {
|
||||||
// 发起请求
|
// 发起请求
|
||||||
const result = await request({
|
const result = await request({
|
||||||
url: `/api/rest/databaseManagement/v1/select/omc_db/pm_custom_title`,
|
url: `/pm/kpiC/title/totalList`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: {
|
params: query,
|
||||||
totalSQL: totalSQL + querySQL,
|
|
||||||
rowsSQL: rowsSQL + querySQL + sortSql + limtSql,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 解析数据
|
|
||||||
if (result.code === RESULT_CODE_SUCCESS) {
|
|
||||||
const data: DataList = {
|
|
||||||
total: 0,
|
|
||||||
rows: [],
|
|
||||||
code: result.code,
|
|
||||||
msg: result.msg,
|
|
||||||
};
|
|
||||||
result.data.data.forEach((item: any) => {
|
|
||||||
const itemData = item['pm_custom_title'];
|
|
||||||
if (Array.isArray(itemData)) {
|
|
||||||
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
|
|
||||||
data.total = itemData[0]['total'];
|
|
||||||
} else {
|
|
||||||
data.rows = itemData.map(v => parseObjLineToHump(v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,22 +83,10 @@ export async function listCustom(query: Record<string, any>) {
|
|||||||
* @returns object
|
* @returns object
|
||||||
*/
|
*/
|
||||||
export async function getCustom(id: string | number) {
|
export async function getCustom(id: string | number) {
|
||||||
// 发起请求
|
return request({
|
||||||
const result = await request({
|
url: `/pm/kpiC/title/${id}`,
|
||||||
url: `/api/rest/databaseManagement/v1/select/omc_db/pm_custom_title`,
|
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: {
|
|
||||||
SQL: `select * from pm_custom_title where id = ${id}`,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
// 解析数据
|
|
||||||
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
|
|
||||||
let data = result.data.data[0];
|
|
||||||
return Object.assign(result, {
|
|
||||||
data: parseObjLineToHump(data['pm_custom_title'][0]),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,21 +95,10 @@ export async function getCustom(id: string | number) {
|
|||||||
* @returns object
|
* @returns object
|
||||||
*/
|
*/
|
||||||
export function addCustom(data: Record<string, any>) {
|
export function addCustom(data: Record<string, any>) {
|
||||||
let obj: any = {
|
|
||||||
title: data.title,
|
|
||||||
ne_type: data.neType,
|
|
||||||
kpi_id: data.kpiId,
|
|
||||||
object_type: data.objectType,
|
|
||||||
expression: data.expression,
|
|
||||||
period: data.period,
|
|
||||||
description: data.description,
|
|
||||||
kpi_set: data.kpiSet,
|
|
||||||
};
|
|
||||||
|
|
||||||
return request({
|
return request({
|
||||||
url: `/api/rest/databaseManagement/v1/omc_db/pm_custom_title`,
|
url: `/pm/kpiC/title`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: { 'data': [obj] },
|
data: data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,20 +108,10 @@ export function addCustom(data: Record<string, any>) {
|
|||||||
* @returns object
|
* @returns object
|
||||||
*/
|
*/
|
||||||
export function updateCustom(data: Record<string, any>) {
|
export function updateCustom(data: Record<string, any>) {
|
||||||
let obj: any = {
|
|
||||||
title: data.title,
|
|
||||||
ne_type: data.neType,
|
|
||||||
kpi_id: data.kpiId,
|
|
||||||
object_type: data.objectType,
|
|
||||||
expression: data.expression,
|
|
||||||
period: data.period,
|
|
||||||
description: data.description,
|
|
||||||
kpi_set: data.kpiSet,
|
|
||||||
};
|
|
||||||
return request({
|
return request({
|
||||||
url: `/api/rest/databaseManagement/v1/omc_db/pm_custom_title?WHERE=id=${data.id}`,
|
url: `/pm/kpiC/title/${data.id}`,
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: { data: obj },
|
data: data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,8 +121,7 @@ export function updateCustom(data: Record<string, any>) {
|
|||||||
*/
|
*/
|
||||||
export async function delCustom(data: Record<string, any>) {
|
export async function delCustom(data: Record<string, any>) {
|
||||||
return request({
|
return request({
|
||||||
url: `/api/rest/databaseManagement/v1/omc_db/pm_custom_title?WHERE=id=${data.id}`,
|
url: `/pm/kpiC/title/${data.id}`,
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,21 @@ export function dumpStop(data: Record<string, string>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 网元抓包PACP 下载
|
||||||
|
export function dumpDownload(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/trace/tcpdump/download',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// UPF标准版内部抓包
|
// UPF标准版内部抓包
|
||||||
export function traceUPF(data: Record<string, string>) {
|
export function traceUPF(data: Record<string, string>) {
|
||||||
return request({
|
return request({
|
||||||
url: '/trace/tcpdump/traceUPF',
|
url: '/trace/tcpdump/upf',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data,
|
data: data,
|
||||||
});
|
});
|
||||||
104
src/api/trace/task.ts
Normal file
104
src/api/trace/task.ts
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
import { parseObjLineToHump } from '@/utils/parse-utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询跟踪任务列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function listTraceTask(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/trace/task/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询跟踪任务信息
|
||||||
|
* @param id 网元ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function getTraceTask(id: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/trace/task/${id}`,
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增任务
|
||||||
|
* @param data 网元对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addTraceTask(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/trace/task`,
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改任务
|
||||||
|
* @param data 网元对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function updateTraceTask(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/trace/task`,
|
||||||
|
method: 'put',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跟踪任务删除
|
||||||
|
* @param ids ID多个逗号分隔
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function delTraceTask(ids: string) {
|
||||||
|
return request({
|
||||||
|
url: `/trace/task/${ids}`,
|
||||||
|
method: 'delete',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跟踪任务文件
|
||||||
|
* @param query 对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function filePullTask(traceId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/trace/task/filePull',
|
||||||
|
method: 'get',
|
||||||
|
params: { traceId },
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取网元跟踪接口列表
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function getNeTraceInterfaceAll() {
|
||||||
|
// 发起请求
|
||||||
|
const result = await request({
|
||||||
|
url: `/api/rest/databaseManagement/v1/elementType/omc_db/objectType/ne_info`,
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
SQL: `SELECT ne_type,interface FROM trace_info GROUP BY ne_type,interface`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// 解析数据
|
||||||
|
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
|
||||||
|
let data = result.data.data[0];
|
||||||
|
return Object.assign(result, {
|
||||||
|
data: parseObjLineToHump(data['trace_info']),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
83
src/api/trace/taskHLR.ts
Normal file
83
src/api/trace/taskHLR.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询跟踪任务列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listTaskHLR(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/trace/task/hlr/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跟踪任务删除
|
||||||
|
* @param ids 任务ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delTaskHLR(ids: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/trace/task/hlr/${ids}`,
|
||||||
|
method: 'delete',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跟踪任务创建
|
||||||
|
* @param data 对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function startTaskHLR(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/trace/task/hlr/start',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跟踪任务停止
|
||||||
|
* @param data 对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function stopTaskHLR(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/trace/task/hlr/stop',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跟踪任务文件
|
||||||
|
* @param data 对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function fileTaskHLR(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/trace/task/hlr/file',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跟踪任务文件从网元到本地
|
||||||
|
* @param query 对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function filePullTaskHLR(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/trace/task/hlr/filePull',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
|
||||||
import { request } from '@/plugins/http-fetch';
|
|
||||||
import { parseObjLineToHump } from '@/utils/parse-utils';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询任务列表
|
|
||||||
* @param query 查询参数
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function listTraceTask(query: Record<string, any>) {
|
|
||||||
let totalSQL = 'select count(*) as total from trace_task where 1=1 ';
|
|
||||||
let rowsSQL = 'select * from trace_task where 1=1 ';
|
|
||||||
|
|
||||||
// 查询
|
|
||||||
let querySQL = '';
|
|
||||||
if (query.imsi) {
|
|
||||||
querySQL += ` and imsi like '%${query.imsi}%' `;
|
|
||||||
}
|
|
||||||
if (query.beginTime) {
|
|
||||||
querySQL += ` and start_time >= '${query.beginTime}' `;
|
|
||||||
}
|
|
||||||
if (query.endTime) {
|
|
||||||
querySQL += ` and end_time <= '${query.endTime}' `;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分页
|
|
||||||
const pageNum = (query.pageNum - 1) * query.pageSize;
|
|
||||||
const limtSql = ` limit ${pageNum},${query.pageSize} `;
|
|
||||||
|
|
||||||
// 排序
|
|
||||||
let sortSql = ' order by start_time ';
|
|
||||||
if (query.sortOrder === 'asc') {
|
|
||||||
sortSql += ' asc ';
|
|
||||||
} else {
|
|
||||||
sortSql += ' desc ';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发起请求
|
|
||||||
const result = await request({
|
|
||||||
url: `/api/rest/databaseManagement/v1/select/omc_db/trace_task`,
|
|
||||||
method: 'get',
|
|
||||||
params: {
|
|
||||||
totalSQL: totalSQL + querySQL,
|
|
||||||
rowsSQL: rowsSQL + querySQL + sortSql + limtSql,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 解析数据
|
|
||||||
if (result.code === RESULT_CODE_SUCCESS) {
|
|
||||||
const data: DataList = {
|
|
||||||
total: 0,
|
|
||||||
rows: [],
|
|
||||||
code: result.code,
|
|
||||||
msg: result.msg,
|
|
||||||
};
|
|
||||||
result.data.data.forEach((item: any) => {
|
|
||||||
const itemData = item['trace_task'];
|
|
||||||
if (Array.isArray(itemData)) {
|
|
||||||
if (itemData.length === 1 && itemData[0]['total'] >= 0) {
|
|
||||||
data.total = itemData[0]['total'];
|
|
||||||
} else {
|
|
||||||
data.rows = itemData.map(v => parseObjLineToHump(v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询任务详细
|
|
||||||
* @param id 网元ID
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function getTraceTask(id: string | number) {
|
|
||||||
// 发起请求
|
|
||||||
const result = await request({
|
|
||||||
url: `/api/rest/databaseManagement/v1/select/omc_db/trace_task`,
|
|
||||||
method: 'get',
|
|
||||||
params: {
|
|
||||||
SQL: `select * from trace_task where id = ${id}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// 解析数据
|
|
||||||
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
|
|
||||||
let data = result.data.data[0];
|
|
||||||
return Object.assign(result, {
|
|
||||||
data: parseObjLineToHump(data['trace_task'][0]),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增任务
|
|
||||||
* @param data 网元对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function addTraceTask(data: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: `/api/rest/traceManagement/v1/subscriptions`,
|
|
||||||
method: 'post',
|
|
||||||
data: data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改任务
|
|
||||||
* @param data 网元对象
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function updateTraceTask(data: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: `/api/rest/traceManagement/v1/subscriptions`,
|
|
||||||
method: 'put',
|
|
||||||
data: data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除任务
|
|
||||||
* @param noticeId 网元ID
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function delTraceTask(id: string) {
|
|
||||||
return request({
|
|
||||||
url: `/api/rest/traceManagement/v1/subscriptions?id=${id}`,
|
|
||||||
method: 'delete',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取网元跟踪接口列表
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function getNeTraceInterfaceAll() {
|
|
||||||
// 发起请求
|
|
||||||
const result = await request({
|
|
||||||
url: `/api/rest/databaseManagement/v1/elementType/omc_db/objectType/ne_info`,
|
|
||||||
method: 'get',
|
|
||||||
params: {
|
|
||||||
SQL: `SELECT ne_type,interface FROM trace_info GROUP BY ne_type,interface`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// 解析数据
|
|
||||||
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
|
|
||||||
let data = result.data.data[0];
|
|
||||||
return Object.assign(result, {
|
|
||||||
data: parseObjLineToHump(data['trace_info']),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
11
src/assets/js/wiregasm_worker.ts
Normal file
11
src/assets/js/wiregasm_worker.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* worker文件-静态资源文件路径
|
||||||
|
*/
|
||||||
|
const baseUrl = import.meta.env.VITE_HISTORY_BASE_URL;
|
||||||
|
export const scriptUrl = `${
|
||||||
|
baseUrl.length === 1 && baseUrl.indexOf('/') === 0
|
||||||
|
? ''
|
||||||
|
: baseUrl.indexOf('/') === -1
|
||||||
|
? '/' + baseUrl
|
||||||
|
: baseUrl
|
||||||
|
}/wiregasm/worker.js`;
|
||||||
227
src/components/TerminalSSHView/index.vue
Normal file
227
src/components/TerminalSSHView/index.vue
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
|
||||||
|
import { FitAddon } from '@xterm/addon-fit';
|
||||||
|
import { Terminal } from '@xterm/xterm';
|
||||||
|
import '@xterm/xterm/css/xterm.css';
|
||||||
|
import { RESULT_CODE_ERROR } from '@/constants/result-constants';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
const ws = new WS();
|
||||||
|
const emit = defineEmits(['connect', 'close', 'message']);
|
||||||
|
const props = defineProps({
|
||||||
|
/**终端ID,必传 */
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
/**网元类型,必传 */
|
||||||
|
neType: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
/**网元ID,必传 */
|
||||||
|
neId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
/**窗口单行字符数 */
|
||||||
|
cols: {
|
||||||
|
type: Number,
|
||||||
|
default: 80,
|
||||||
|
},
|
||||||
|
/**窗口行数 */
|
||||||
|
rows: {
|
||||||
|
type: Number,
|
||||||
|
default: 40,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**终端输入DOM节点实例对象 */
|
||||||
|
const terminalDom = ref<HTMLElement | undefined>(undefined);
|
||||||
|
|
||||||
|
/**终端输入实例对象 */
|
||||||
|
const terminal = ref<any>(null);
|
||||||
|
|
||||||
|
/**终端输入渲染 */
|
||||||
|
function handleRanderXterm(container: HTMLElement | undefined) {
|
||||||
|
if (!container) return;
|
||||||
|
const xterm = new Terminal({
|
||||||
|
cols: props.cols,
|
||||||
|
rows: props.rows,
|
||||||
|
lineHeight: 1.2,
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: "Monaco, Menlo, Consolas, 'Courier New', monospace",
|
||||||
|
theme: {
|
||||||
|
background: '#000000',
|
||||||
|
},
|
||||||
|
cursorBlink: true, // 光标闪烁
|
||||||
|
cursorStyle: 'block',
|
||||||
|
scrollback: 1000, // 设置历史缓冲区大小为 1000 行
|
||||||
|
scrollSensitivity: 15,
|
||||||
|
tabStopWidth: 4,
|
||||||
|
disableStdin: true, // 禁止输入
|
||||||
|
});
|
||||||
|
// 挂载
|
||||||
|
xterm.open(container);
|
||||||
|
// 自适应尺寸
|
||||||
|
const fitAddon = new FitAddon();
|
||||||
|
xterm.loadAddon(fitAddon);
|
||||||
|
// 终端尺寸变化触发
|
||||||
|
xterm.onResize(({ cols, rows }) => {
|
||||||
|
// console.log('尺寸', cols, rows);
|
||||||
|
ws.send({
|
||||||
|
requestId: `resize_${props.id}`,
|
||||||
|
type: 'resize',
|
||||||
|
data: { cols, rows },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建 ResizeObserver 实例
|
||||||
|
var observer = new ResizeObserver(entries => {
|
||||||
|
fitAddon.fit();
|
||||||
|
});
|
||||||
|
// 监听元素大小变化
|
||||||
|
observer.observe(container);
|
||||||
|
|
||||||
|
terminal.value = xterm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**连接打开后回调 */
|
||||||
|
function wsOpen(ev: any) {
|
||||||
|
// console.info('wsOpen', ev);
|
||||||
|
nextTick(() => {
|
||||||
|
handleRanderXterm(terminalDom.value);
|
||||||
|
// 连接事件
|
||||||
|
emit('connect', {
|
||||||
|
timeStamp: ev.timeStamp,
|
||||||
|
cols: terminal.value.cols,
|
||||||
|
rows: terminal.value.rows,
|
||||||
|
neType: props.neType,
|
||||||
|
neId: props.neId,
|
||||||
|
id: props.id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**连接错误后回调 */
|
||||||
|
function wsError(ev: any) {
|
||||||
|
console.error('wsError', ev);
|
||||||
|
if (terminal.value != null) {
|
||||||
|
let message = 'disconnected';
|
||||||
|
terminal.value.write(`\x1b[31m${message}\x1b[m\r\n`);
|
||||||
|
} else if (terminalDom.value) {
|
||||||
|
terminalDom.value.style.background = '#000';
|
||||||
|
terminalDom.value.style.color = '#ff4d4f';
|
||||||
|
terminalDom.value.style.height = '60%';
|
||||||
|
terminalDom.value.innerText = 'disconnected';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**连接关闭后回调 */
|
||||||
|
function wsClose(code: number) {
|
||||||
|
// console.warn('wsClose', code);
|
||||||
|
if (terminal.value != null) {
|
||||||
|
let message = 'disconnected ' + code;
|
||||||
|
terminal.value.write(`\x1b[31m${message}\x1b[m\r\n`);
|
||||||
|
}
|
||||||
|
// 关闭事件
|
||||||
|
emit('close', {
|
||||||
|
code: code,
|
||||||
|
neType: props.neType,
|
||||||
|
neId: props.neId,
|
||||||
|
id: props.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收消息后回调 */
|
||||||
|
function wsMessage(res: Record<string, any>) {
|
||||||
|
emit('message', res);
|
||||||
|
// console.log('wsMessage', res);
|
||||||
|
const { code, requestId, data } = res;
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!requestId) return;
|
||||||
|
if (terminal.value != null) {
|
||||||
|
// 查找的开始输出标记
|
||||||
|
const parts: string[] = data.split('\u001b[?2004l\r');
|
||||||
|
if (parts.length > 0) {
|
||||||
|
let text = parts[parts.length - 1];
|
||||||
|
// 找到最后输出标记
|
||||||
|
const lestIndex = text.lastIndexOf('\u001b[?2004h\u001b]0;');
|
||||||
|
if (lestIndex !== -1) {
|
||||||
|
text = text.substring(0, lestIndex);
|
||||||
|
}
|
||||||
|
if (text === '' || text === '\r\n' || text.startsWith("^C\r\n") ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// console.log({ parts, text });
|
||||||
|
terminal.value.write(text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 无标记
|
||||||
|
terminal.value.write(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.neType && props.neId) {
|
||||||
|
// 建立链接
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: '/ws/view',
|
||||||
|
params: {
|
||||||
|
neType: props.neType,
|
||||||
|
neId: props.neId,
|
||||||
|
cols: props.cols,
|
||||||
|
rows: props.rows,
|
||||||
|
},
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: wsError,
|
||||||
|
onopen: wsOpen,
|
||||||
|
onclose: wsClose,
|
||||||
|
};
|
||||||
|
ws.connect(options);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
ws.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 给组件设置属性 ref="xxxTerminal"
|
||||||
|
// setup内使用 const xxxTerminal = ref();
|
||||||
|
defineExpose({
|
||||||
|
/**清除 */
|
||||||
|
clear: () => {
|
||||||
|
if (terminal.value != null) {
|
||||||
|
terminal.value.clear();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**发送命令 */
|
||||||
|
send: (type: string, data: Record<string, any>) => {
|
||||||
|
ws.send({
|
||||||
|
requestId: `ssh_${props.id}`,
|
||||||
|
type,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**模拟按下 Ctrl+C */
|
||||||
|
ctrlC: () => {
|
||||||
|
ws.send({
|
||||||
|
requestId: `ssh_${props.id}`,
|
||||||
|
type: 'ctrl-c',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="terminalDom" :id="id" class="terminal"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.terminal {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -119,7 +119,7 @@ function fnAutoCompleteKeydown(evt: any) {
|
|||||||
ws.send({
|
ws.send({
|
||||||
requestId: `telnet_${props.hostId}`,
|
requestId: `telnet_${props.hostId}`,
|
||||||
type: 'telnet',
|
type: 'telnet',
|
||||||
data: `${cmdStr}\r\n'`,
|
data: `${cmdStr}\r\n`,
|
||||||
});
|
});
|
||||||
terminalState.text = ' ';
|
terminalState.text = ' ';
|
||||||
|
|
||||||
@@ -155,6 +155,15 @@ function handleRanderXterm(container: HTMLElement | undefined) {
|
|||||||
// 自适应尺寸
|
// 自适应尺寸
|
||||||
const fitAddon = new FitAddon();
|
const fitAddon = new FitAddon();
|
||||||
xterm.loadAddon(fitAddon);
|
xterm.loadAddon(fitAddon);
|
||||||
|
// 终端尺寸变化触发
|
||||||
|
xterm.onResize(({ cols, rows }) => {
|
||||||
|
// console.log('尺寸', cols, rows);
|
||||||
|
ws.send({
|
||||||
|
requestId: `telnet_resize_${props.hostId}`,
|
||||||
|
type: 'telnet_resize',
|
||||||
|
data: { cols, rows },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// 创建 ResizeObserver 实例
|
// 创建 ResizeObserver 实例
|
||||||
var observer = new ResizeObserver(entries => {
|
var observer = new ResizeObserver(entries => {
|
||||||
@@ -280,7 +289,7 @@ defineExpose({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="terminal">
|
<div class="terminal">
|
||||||
<div ref="terminalDom" style="height: 78%" :id="id"></div>
|
<div ref="terminalDom" style="height: calc(100% - 36px)" :id="id"></div>
|
||||||
<a-auto-complete
|
<a-auto-complete
|
||||||
v-model:value="terminalState.text"
|
v-model:value="terminalState.text"
|
||||||
:dropdown-match-select-width="500"
|
:dropdown-match-select-width="500"
|
||||||
|
|||||||
204
src/components/VirtualList/index.vue
Normal file
204
src/components/VirtualList/index.vue
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref, computed, unref, onUpdated, watchEffect } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
/**列表高度 */
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
default: 300,
|
||||||
|
},
|
||||||
|
/**列表项高度 */
|
||||||
|
itemHeight: {
|
||||||
|
type: Number,
|
||||||
|
default: 30,
|
||||||
|
},
|
||||||
|
/**数据 */
|
||||||
|
data: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
/**预先兜底缓存数量 */
|
||||||
|
cache: {
|
||||||
|
type: Number,
|
||||||
|
default: 2,
|
||||||
|
},
|
||||||
|
/**是否动态加载 */
|
||||||
|
dynamic: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const state = reactive<any>({
|
||||||
|
start: 0,
|
||||||
|
end: 10,
|
||||||
|
scrollOffset: 0,
|
||||||
|
cacheData: [],
|
||||||
|
});
|
||||||
|
const virtualListRef = ref();
|
||||||
|
|
||||||
|
const getWrapperStyle = computed(() => {
|
||||||
|
const { height } = props;
|
||||||
|
return {
|
||||||
|
height: `${height}px`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const getInnerStyle = computed(() => {
|
||||||
|
return {
|
||||||
|
height: `${unref(getTotalHeight)}px`,
|
||||||
|
width: '100%',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const getListStyle = computed(() => {
|
||||||
|
return {
|
||||||
|
willChange: 'transform',
|
||||||
|
transform: `translateY(${state.scrollOffset}px)`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 数据数量
|
||||||
|
const total = computed(() => {
|
||||||
|
return props.data.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 总体高度
|
||||||
|
const getTotalHeight = computed(() => {
|
||||||
|
if (!props.dynamic) return unref(total) * props.itemHeight;
|
||||||
|
return getCurrentTop(unref(total));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 当前屏幕显示的数量
|
||||||
|
const clientCount = computed(() => {
|
||||||
|
return Math.ceil(props.height / props.itemHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 当前屏幕显示的数据
|
||||||
|
const clientData = computed(() => {
|
||||||
|
return props.data.slice(state.start, state.end);
|
||||||
|
});
|
||||||
|
|
||||||
|
const onScroll = (e: any) => {
|
||||||
|
const { scrollTop } = e.target;
|
||||||
|
if (state.scrollOffset === scrollTop) return;
|
||||||
|
const { cache, dynamic, itemHeight } = props;
|
||||||
|
const cacheCount = Math.max(1, cache);
|
||||||
|
|
||||||
|
let startIndex = dynamic
|
||||||
|
? getStartIndex(scrollTop)
|
||||||
|
: Math.floor(scrollTop / itemHeight);
|
||||||
|
|
||||||
|
const endIndex = Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(unref(total), startIndex + unref(clientCount) + cacheCount)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (startIndex > cacheCount) {
|
||||||
|
startIndex = startIndex - cacheCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 偏移量
|
||||||
|
const offset = dynamic
|
||||||
|
? getCurrentTop(startIndex)
|
||||||
|
: scrollTop - (scrollTop % itemHeight);
|
||||||
|
|
||||||
|
Object.assign(state, {
|
||||||
|
start: startIndex,
|
||||||
|
end: endIndex,
|
||||||
|
scrollOffset: offset,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 二分法去查找对应的index
|
||||||
|
const getStartIndex = (scrollTop = 0): number => {
|
||||||
|
let low = 0;
|
||||||
|
let high = state.cacheData.length - 1;
|
||||||
|
while (low <= high) {
|
||||||
|
const middle = low + Math.floor((high - low) / 2);
|
||||||
|
const middleTopValue = getCurrentTop(middle);
|
||||||
|
const middleBottomValue = getCurrentTop(middle + 1);
|
||||||
|
|
||||||
|
if (middleTopValue <= scrollTop && scrollTop <= middleBottomValue) {
|
||||||
|
return middle;
|
||||||
|
} else if (middleBottomValue < scrollTop) {
|
||||||
|
low = middle + 1;
|
||||||
|
} else if (middleBottomValue > scrollTop) {
|
||||||
|
high = middle - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Math.min(
|
||||||
|
unref(total) - unref(clientCount),
|
||||||
|
Math.floor(scrollTop / props.itemHeight)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCurrentTop = (index: number) => {
|
||||||
|
const lastIndex = state.cacheData.length - 1;
|
||||||
|
|
||||||
|
if (Object.hasOwn(state.cacheData, index)) {
|
||||||
|
return state.cacheData[index].top;
|
||||||
|
} else if (Object.hasOwn(state.cacheData, index - 1)) {
|
||||||
|
return state.cacheData[index - 1].bottom;
|
||||||
|
} else if (index > lastIndex) {
|
||||||
|
return (
|
||||||
|
state.cacheData[lastIndex].bottom +
|
||||||
|
Math.max(0, index - state.cacheData[lastIndex].index) * props.itemHeight
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return index * props.itemHeight;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onUpdated(() => {
|
||||||
|
if (!props.dynamic) return;
|
||||||
|
const childrenList = virtualListRef.value.children || [];
|
||||||
|
[...childrenList].forEach((node: any, index: number) => {
|
||||||
|
const height = node.getBoundingClientRect().height;
|
||||||
|
const currentIndex = state.start + index;
|
||||||
|
if (state.cacheData[currentIndex].height === height) return;
|
||||||
|
|
||||||
|
state.cacheData[currentIndex].height = height;
|
||||||
|
state.cacheData[currentIndex].top = getCurrentTop(currentIndex);
|
||||||
|
state.cacheData[currentIndex].bottom =
|
||||||
|
state.cacheData[currentIndex].top + state.cacheData[currentIndex].height;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
clientData.value.forEach((_, index) => {
|
||||||
|
const currentIndex = state.start + index;
|
||||||
|
if (Object.hasOwn(state.cacheData, currentIndex)) return;
|
||||||
|
state.cacheData[currentIndex] = {
|
||||||
|
top: currentIndex * props.itemHeight,
|
||||||
|
height: props.itemHeight,
|
||||||
|
bottom: (currentIndex + 1) * props.itemHeight,
|
||||||
|
index: currentIndex,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="virtual-list-wrapper"
|
||||||
|
ref="wrapperRef"
|
||||||
|
:style="getWrapperStyle"
|
||||||
|
@scroll="onScroll"
|
||||||
|
>
|
||||||
|
<div class="virtual-list-inner" ref="innerRef" :style="getInnerStyle">
|
||||||
|
<div class="virtual-list" :style="getListStyle" ref="virtualListRef">
|
||||||
|
<div v-for="(item, index) in clientData" :key="index + state.start">
|
||||||
|
<slot name="default" :item="item"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.virtual-list-wrapper {
|
||||||
|
position: relative;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,3 +3,6 @@ export const APP_REQUEST_HEADER_CODE = 'X-App-Code';
|
|||||||
|
|
||||||
/**应用-请求头-系统版本 */
|
/**应用-请求头-系统版本 */
|
||||||
export const APP_REQUEST_HEADER_VERSION = 'X-App-Version';
|
export const APP_REQUEST_HEADER_VERSION = 'X-App-Version';
|
||||||
|
|
||||||
|
/**应用-请求数据-密钥 */
|
||||||
|
export const APP_DATA_API_KEY = 'T9ox2DCzpLfJIPzkH9pKhsOTMOEMJcFv';
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export const NE_TYPE_LIST = [
|
|||||||
'N3IWF',
|
'N3IWF',
|
||||||
'MOCNGW',
|
'MOCNGW',
|
||||||
'SMSC',
|
'SMSC',
|
||||||
|
'CBC',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
/**响应-code加密数据 */
|
||||||
|
export const RESULT_CODE_ENCRYPT = 2;
|
||||||
|
|
||||||
|
/**响应-msg加密数据 */
|
||||||
|
export const RESULT_MSG_ENCRYPT: Record<string, string> = {
|
||||||
|
zh_CN: '加密!',
|
||||||
|
en_US: 'encrypt!',
|
||||||
|
};
|
||||||
|
|
||||||
/**响应-code正常成功 */
|
/**响应-code正常成功 */
|
||||||
export const RESULT_CODE_SUCCESS = 1;
|
export const RESULT_CODE_SUCCESS = 1;
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export default {
|
|||||||
day33: "days",
|
day33: "days",
|
||||||
day4: "Designated day (optional)",
|
day4: "Designated day (optional)",
|
||||||
day5: "Last day of the month",
|
day5: "Last day of the month",
|
||||||
hour1: "Hourly",
|
hour1: "Hourly",
|
||||||
hour21: "Execute every",
|
hour21: "Execute every",
|
||||||
hour22: "hours, starting from the",
|
hour22: "hours, starting from the",
|
||||||
hour23: "hour",
|
hour23: "hour",
|
||||||
@@ -149,6 +149,7 @@ export default {
|
|||||||
page403: 'No Access',
|
page403: 'No Access',
|
||||||
page404: 'Match Page Not Found',
|
page404: 'Match Page Not Found',
|
||||||
helpDoc: 'System User Documentation',
|
helpDoc: 'System User Documentation',
|
||||||
|
traceTaskHLR: 'Tracking Tasks HLR',
|
||||||
lockScreen: 'Lock Screen',
|
lockScreen: 'Lock Screen',
|
||||||
account: {
|
account: {
|
||||||
index: "Personal Center",
|
index: "Personal Center",
|
||||||
@@ -185,11 +186,11 @@ export default {
|
|||||||
helpDoc: 'Doc',
|
helpDoc: 'Doc',
|
||||||
},
|
},
|
||||||
rightContent: {
|
rightContent: {
|
||||||
|
alarm: "Active Alarms",
|
||||||
lock: "Lock Screen",
|
lock: "Lock Screen",
|
||||||
lockTip: "Confirmation of the lock screen?",
|
lockTip: "Confirmation of the lock screen?",
|
||||||
lockPasswd: "Unlock Password",
|
lockPasswd: "Unlock Password",
|
||||||
lockPasswdTip: "No password can be set",
|
lockPasswdTip: "No password can be set",
|
||||||
helpDoc: "System User Documentation",
|
|
||||||
fullscreen: "Full Screen",
|
fullscreen: "Full Screen",
|
||||||
logout: "Logout",
|
logout: "Logout",
|
||||||
profile: "Profile",
|
profile: "Profile",
|
||||||
@@ -239,7 +240,7 @@ export default {
|
|||||||
ttile: 'No access',
|
ttile: 'No access',
|
||||||
subTitle:'Please do not perform illegal operations, you can go back to the main page or return to the',
|
subTitle:'Please do not perform illegal operations, you can go back to the main page or return to the',
|
||||||
backHome: 'Back to Home',
|
backHome: 'Back to Home',
|
||||||
back: 'Back',
|
back: 'Back',
|
||||||
},
|
},
|
||||||
err404: {
|
err404: {
|
||||||
ttile: 'Match page not found',
|
ttile: 'Match page not found',
|
||||||
@@ -296,10 +297,10 @@ export default {
|
|||||||
newPassword: "New Password",
|
newPassword: "New Password",
|
||||||
newPasswordTip: "Password contains at least upper and lower case letters, numbers, special symbols, and not less than 6 digits",
|
newPasswordTip: "Password contains at least upper and lower case letters, numbers, special symbols, and not less than 6 digits",
|
||||||
newPassworddPleace: "Please enter a new password",
|
newPassworddPleace: "Please enter a new password",
|
||||||
confirmPassword: "Confirm new password",
|
confirmPassword: "Confirm new password",
|
||||||
confirmPasswordPleace: "Please confirm the new password",
|
confirmPasswordPleace: "Please confirm the new password",
|
||||||
submit: "Submit changes",
|
submit: "Submit changes",
|
||||||
reset: "Reset",
|
reset: "Reset",
|
||||||
submitTip: "Are you sure you want to submit a password change?",
|
submitTip: "Are you sure you want to submit a password change?",
|
||||||
submitOkTip: "Congratulations, {num} account password changed successfully!",
|
submitOkTip: "Congratulations, {num} account password changed successfully!",
|
||||||
submitOk: "Sign in again",
|
submitOk: "Sign in again",
|
||||||
@@ -525,7 +526,7 @@ export default {
|
|||||||
up:'Uplink',
|
up:'Uplink',
|
||||||
down:'Downlink'
|
down:'Downlink'
|
||||||
},
|
},
|
||||||
upfFlowTotal:{
|
upfFlowTotal:{
|
||||||
title:'Traffic Summary',
|
title:'Traffic Summary',
|
||||||
up:'Uplink',
|
up:'Uplink',
|
||||||
down:'Downlink'
|
down:'Downlink'
|
||||||
@@ -569,6 +570,8 @@ export default {
|
|||||||
caller: "Caller",
|
caller: "Caller",
|
||||||
called: "Called",
|
called: "Called",
|
||||||
result: "Result",
|
result: "Result",
|
||||||
|
resultOk: "Success",
|
||||||
|
resultFail: "Fail",
|
||||||
delTip: "Confirm deletion of the data item numbered [{msg}]?",
|
delTip: "Confirm deletion of the data item numbered [{msg}]?",
|
||||||
exportTip: "Do you confirm to export the current query conditions of the CDR data? (Maximum 10,000 items can be exported.)",
|
exportTip: "Do you confirm to export the current query conditions of the CDR data? (Maximum 10,000 items can be exported.)",
|
||||||
smfChargingID: 'Charging ID',
|
smfChargingID: 'Charging ID',
|
||||||
@@ -603,7 +606,7 @@ export default {
|
|||||||
neIdTip: 'Fill in the unique identifier of the network element binding',
|
neIdTip: 'Fill in the unique identifier of the network element binding',
|
||||||
rmUid: 'Resource Unique ID',
|
rmUid: 'Resource Unique ID',
|
||||||
rmUidPlease: 'Please enter a resource unique ID',
|
rmUidPlease: 'Please enter a resource unique ID',
|
||||||
rmUidTip: "Tagging for data reporting of network element logs, alarms, metrics, etc.",
|
rmUidTip: "Tagging for data reporting of network element logs, alarms, metrics, etc.",
|
||||||
neName: 'NE Name',
|
neName: 'NE Name',
|
||||||
neNamePlease: 'Please enter the name of the network element',
|
neNamePlease: 'Please enter the name of the network element',
|
||||||
ipAddr: 'IP Addr',
|
ipAddr: 'IP Addr',
|
||||||
@@ -627,9 +630,9 @@ export default {
|
|||||||
log: 'Logs',
|
log: 'Logs',
|
||||||
},
|
},
|
||||||
neInfo: {
|
neInfo: {
|
||||||
version: "Version",
|
version: "Version",
|
||||||
state: "State",
|
state: "State",
|
||||||
serviceState: "Service Status",
|
serviceState: "Service Status",
|
||||||
info: 'Status Message',
|
info: 'Status Message',
|
||||||
resourceInfo: 'Resource Situation',
|
resourceInfo: 'Resource Situation',
|
||||||
sysMem: "SYS Mem",
|
sysMem: "SYS Mem",
|
||||||
@@ -658,12 +661,13 @@ export default {
|
|||||||
kpiEnable: 'Report',
|
kpiEnable: 'Report',
|
||||||
kpiTimer: 'Reporting Cycle',
|
kpiTimer: 'Reporting Cycle',
|
||||||
kpiTimerPlease: 'Please enter the reporting period (in seconds)',
|
kpiTimerPlease: 'Please enter the reporting period (in seconds)',
|
||||||
|
omcIP: 'OMC IP',
|
||||||
},
|
},
|
||||||
backConf: {
|
backConf: {
|
||||||
export: 'Config Export',
|
export: 'Config Export',
|
||||||
import: 'Config Import',
|
import: 'Config Import',
|
||||||
title: 'Configuration File Import',
|
title: 'Configuration File Import',
|
||||||
importType: 'Source of File',
|
importType: 'Source of File',
|
||||||
server:'Server File',
|
server:'Server File',
|
||||||
local:'Local File',
|
local:'Local File',
|
||||||
localUpload:'Local Upload',
|
localUpload:'Local Upload',
|
||||||
@@ -735,21 +739,21 @@ export default {
|
|||||||
upgrade: "Upgrade To New Version",
|
upgrade: "Upgrade To New Version",
|
||||||
upgradeTip: "Confirmed to upgrade to the new version?",
|
upgradeTip: "Confirmed to upgrade to the new version?",
|
||||||
upgradeTipEmpty: "There are currently no new versions available",
|
upgradeTipEmpty: "There are currently no new versions available",
|
||||||
upgradeTipEqual: "Current version is the same as the new version",
|
upgradeTipEqual: "The current version is the same as the new version, confirmed to update?",
|
||||||
rollback: 'Switch to previous version',
|
rollback: 'Switch to previous version',
|
||||||
rollbackTip: "Confirm switching to the previous version?",
|
rollbackTip: "Confirm switching to the previous version?",
|
||||||
rollbackTipEmpty: "There is currently no previous version available",
|
rollbackTipEmpty: "There is currently no previous version available",
|
||||||
rollbackTipEqual: 'The current version is the same as the previous version',
|
rollbackTipEqual: 'The current version is the same as the previous version, are you sure you want to make the switch?',
|
||||||
version: "Current Version",
|
version: "Current Version",
|
||||||
preVersion: "Previous Version",
|
preVersion: "Previous Version",
|
||||||
newVersion: "New Version",
|
newVersion: "New Version",
|
||||||
status: "Revision Status",
|
status: "Revision Status",
|
||||||
upgradeBatch: "Batch Upgrade",
|
upgradeBatch: "Batch Upgrade",
|
||||||
upgradeBatchTip: "Do you perform new version upgrades on checked records?",
|
upgradeBatchTip: "Do you perform new version upgrades on checked records?",
|
||||||
upgradeNotNewVer: 'No new version found',
|
upgradeNotNewVer: 'No new version found',
|
||||||
upgradeOMCVer: 'Rejection of batch operation upgrades OMC',
|
upgradeOMCVer: 'Rejection of batch operation upgrades OMC',
|
||||||
upgradeDone: 'Update complete, service being reloaded',
|
upgradeDone: 'Update complete, service being reloaded',
|
||||||
upgradeFail: 'The update fails, please check whether the software file exists and whether the service terminal environment is available!',
|
upgradeFail: 'The update fails, please check whether the software file exists and whether the service terminal environment is available!',
|
||||||
upgradeModal: 'Network Element Version Updates',
|
upgradeModal: 'Network Element Version Updates',
|
||||||
},
|
},
|
||||||
neLicense: {
|
neLicense: {
|
||||||
@@ -769,6 +773,31 @@ export default {
|
|||||||
uploadChangeOk: 'Network Element renewed license successfully and is being calibrated in the background!',
|
uploadChangeOk: 'Network Element renewed license successfully and is being calibrated in the background!',
|
||||||
uploadChangeFail: "Some network elements failed to update the license, please check whether the service terminal environment is available!",
|
uploadChangeFail: "Some network elements failed to update the license, please check whether the service terminal environment is available!",
|
||||||
},
|
},
|
||||||
|
neConfig: {
|
||||||
|
treeTitle: "Navigation Configuration",
|
||||||
|
treeSelectTip: "Select configuration item information in the left configuration navigation!",
|
||||||
|
neType: 'NE Type',
|
||||||
|
neTypePleace: "Please select the network element type",
|
||||||
|
noConfigData: "No data on configuration items",
|
||||||
|
updateValue: "[ {num} ] parameter value modified successfully.",
|
||||||
|
updateValueErr: "Attribute value modification failure",
|
||||||
|
updateItem: "Modify Index to {num}.",
|
||||||
|
updateItemErr: "Record modification failure",
|
||||||
|
delItemOk: "Deleting Index as {num} succeeded",
|
||||||
|
addItemOk: "Add Index as {num} Record Succeeded",
|
||||||
|
addItemErr: "Record addition failure",
|
||||||
|
requireUn: "[ {display} ] input value is of unknown type",
|
||||||
|
requireString: "[ {display} ] parameter value is invalid.",
|
||||||
|
requireInt: "[ {display} ] parameter value not in reasonable range {filter}",
|
||||||
|
requireIpv4: "[ {display} ] not a legitimate IPV4 address",
|
||||||
|
requireIpv6: "[ {display} ] not a legitimate IPV6 address.",
|
||||||
|
requireEnum: "[ {display} ] is not a reasonable enumeration value.",
|
||||||
|
requireBool: "[ {display} ] is not a reasonable boolean value.",
|
||||||
|
editOkTip: "Confirm updating the value of this [ {num} ] attribute?",
|
||||||
|
updateItemTip: "Confirm updating the data item with Index [{num}]?",
|
||||||
|
delItemTip: "Confirm deleting the data item with Index [{num}]?",
|
||||||
|
arrayMore: "Expand",
|
||||||
|
},
|
||||||
neConfigBackup: {
|
neConfigBackup: {
|
||||||
name: "Name",
|
name: "Name",
|
||||||
downTip: 'Confirmed to download the backup file [{txt}]?',
|
downTip: 'Confirmed to download the backup file [{txt}]?',
|
||||||
@@ -825,7 +854,7 @@ export default {
|
|||||||
},
|
},
|
||||||
neUser: {
|
neUser: {
|
||||||
auth: {
|
auth: {
|
||||||
authInfo:'Authentication Info',
|
authInfo:' Authentication Info',
|
||||||
neTypePlease: 'Query network element Object',
|
neTypePlease: 'Query network element Object',
|
||||||
neType: 'UDM Object',
|
neType: 'UDM Object',
|
||||||
export: 'Export',
|
export: 'Export',
|
||||||
@@ -848,12 +877,13 @@ export default {
|
|||||||
imsiTip3: 'MSIN = Mobile Subscriber Identification Number, consisting of 10 equal digits.',
|
imsiTip3: 'MSIN = Mobile Subscriber Identification Number, consisting of 10 equal digits.',
|
||||||
amfTip: 'Authentication management field, maximum parameter length is 4',
|
amfTip: 'Authentication management field, maximum parameter length is 4',
|
||||||
algoIndexTip: 'Algorithm index, between 0 and 15',
|
algoIndexTip: 'Algorithm index, between 0 and 15',
|
||||||
kiTip: 'User signing key information, the maximum length of 32',
|
kiTip: 'User signing key information, the length can only be 32',
|
||||||
opcTip: 'The authentication key, OPC, is calculated from Ki and OP, OP is the root key of the operator, ki is the authentication key, and the maximum length is 32.',
|
opcTip: 'The authentication key, OPC, is calculated from Ki and OP, OP is the root key of the operator, ki is the authentication key, and the maximum length is 32.',
|
||||||
delSure:'Are you sure you want to delete the user with IMSI number: {imsi} ?',
|
delSure:'Are you sure you want to delete the user with IMSI number: {imsi} ?',
|
||||||
|
imsiConfirm:'The length of the IMSI must be 15',
|
||||||
},
|
},
|
||||||
sub: {
|
sub: {
|
||||||
subInfo:' Subscription Info',
|
subInfo:' Subscription Info',
|
||||||
neType: 'UDM Object',
|
neType: 'UDM Object',
|
||||||
export: 'Export',
|
export: 'Export',
|
||||||
exportConfirm: 'Are you sure to export all signed user data?',
|
exportConfirm: 'Are you sure to export all signed user data?',
|
||||||
@@ -905,7 +935,7 @@ export default {
|
|||||||
batchAddText: 'Batch Add',
|
batchAddText: 'Batch Add',
|
||||||
batchDelText: 'Batch Delete',
|
batchDelText: 'Batch Delete',
|
||||||
batchUpdateText: 'Batch Modify',
|
batchUpdateText: 'Batch Modify',
|
||||||
batchNum: 'Number of batches',
|
batchNum: 'Number of batches',
|
||||||
imsiTip: 'IMSI=MCC+MNC+MSIN',
|
imsiTip: 'IMSI=MCC+MNC+MSIN',
|
||||||
imsiTip1: 'MCC=Mobile Country Code, consisting of three digits.',
|
imsiTip1: 'MCC=Mobile Country Code, consisting of three digits.',
|
||||||
imsiTip2: 'MNC = Mobile Network Number, consisting of two digits',
|
imsiTip2: 'MNC = Mobile Network Number, consisting of two digits',
|
||||||
@@ -924,16 +954,16 @@ export default {
|
|||||||
sarTip2:'(corresponding parameter setting -Service Area Restriction)',
|
sarTip2:'(corresponding parameter setting -Service Area Restriction)',
|
||||||
rfsfTip:'RAT Frequency Selection Priority',
|
rfsfTip:'RAT Frequency Selection Priority',
|
||||||
},
|
},
|
||||||
base5G: {
|
base5G: {
|
||||||
neType: 'NE Object',
|
neType: 'NE Object',
|
||||||
},
|
},
|
||||||
n3iwf: {
|
n3iwf: {
|
||||||
neType: 'N3IWF Object',
|
neType: 'N3IWF Object',
|
||||||
},
|
},
|
||||||
ue: {
|
ue: {
|
||||||
neType: 'SMF Object',
|
neType: 'SMF Object',
|
||||||
},
|
},
|
||||||
ims: {
|
ims: {
|
||||||
neType: 'IMS Object',
|
neType: 'IMS Object',
|
||||||
},
|
},
|
||||||
nssf:{
|
nssf:{
|
||||||
@@ -1022,6 +1052,7 @@ export default {
|
|||||||
},
|
},
|
||||||
customTarget:{
|
customTarget:{
|
||||||
kpiId:' Custom Indicator',
|
kpiId:' Custom Indicator',
|
||||||
|
kpiIdTip:'This Ne has no custom indicators',
|
||||||
period:' Granularity',
|
period:' Granularity',
|
||||||
title:' Custom Indicator Title',
|
title:' Custom Indicator Title',
|
||||||
objectType:' Object type',
|
objectType:' Object type',
|
||||||
@@ -1033,6 +1064,13 @@ export default {
|
|||||||
addCustom:' Add custom indicator',
|
addCustom:' Add custom indicator',
|
||||||
editCustom:' Edit Custom indicator',
|
editCustom:' Edit Custom indicator',
|
||||||
errorCustomInfo: 'Failed to get information',
|
errorCustomInfo: 'Failed to get information',
|
||||||
|
status: 'Status',
|
||||||
|
active:'Active',
|
||||||
|
inactive:'Inactive',
|
||||||
|
symbol:'Symbol',
|
||||||
|
element:'Element',
|
||||||
|
granularity:'Granularity',
|
||||||
|
unit:'Unit',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
traceManage: {
|
traceManage: {
|
||||||
@@ -1058,18 +1096,23 @@ export default {
|
|||||||
pcap: {
|
pcap: {
|
||||||
capArgPlease: 'Please enter tcpdump -i any support parameter',
|
capArgPlease: 'Please enter tcpdump -i any support parameter',
|
||||||
cmd: 'Command',
|
cmd: 'Command',
|
||||||
execCmd: "Generic tcpdump packet capture command",
|
execCmd: "Common Command Options",
|
||||||
execCmdsSctp: "Generic tcpdump filter sctp and port commands",
|
execCmd2: "Filter Protocol Port Command",
|
||||||
execUPFCmdA: 'Suitable for anomalous packet capture of other NE',
|
execCmd3: "File Split By Time units of seconds (-G 10), Generated Max File Number (-W 7)",
|
||||||
execUPFCmdB: 'Suitable for UPF anomaly packet capture analysis',
|
execUPFCmdA: 'Standard Edition - UPF with other NE anomalous packet capture analysis',
|
||||||
|
execUPFCmdB: 'Standard Edition - UPF anomalies requiring packet capture analysis',
|
||||||
|
batchOper: 'Batch Operations',
|
||||||
|
batchStartText: 'Batch Start',
|
||||||
|
batchStopText: 'Batch Stop',
|
||||||
|
batchDownText: 'Batch Download',
|
||||||
|
fileView: 'Historical Packet Capture Files',
|
||||||
|
fileUPF: 'Standard Edition',
|
||||||
|
fileUPFTip: 'UPF internal packet capture and analysis packet',
|
||||||
textStart: "Start",
|
textStart: "Start",
|
||||||
textStartBatch: "Batch Start",
|
|
||||||
textStop: "Stop",
|
textStop: "Stop",
|
||||||
textStopBatch: "Batch Stop",
|
|
||||||
textLog: "Log",
|
textLog: "Log",
|
||||||
textLogMsg: "Log Info",
|
textLogMsg: "Log Info",
|
||||||
textDown: "Download",
|
textDown: "Download",
|
||||||
textDownBatch: "Batch Download",
|
|
||||||
downTip: "Are you sure you want to download the {title} capture data file?",
|
downTip: "Are you sure you want to download the {title} capture data file?",
|
||||||
downOk: "{title} file download complete",
|
downOk: "{title} file download complete",
|
||||||
downErr: "{title} file download exception",
|
downErr: "{title} file download exception",
|
||||||
@@ -1083,12 +1126,13 @@ export default {
|
|||||||
stopNotRun: "{title} not running",
|
stopNotRun: "{title} not running",
|
||||||
},
|
},
|
||||||
task: {
|
task: {
|
||||||
neTypePlease: 'Query network element type',
|
traceId: 'Tracing No',
|
||||||
neType: 'NE Type',
|
|
||||||
neID: 'NE ID',
|
|
||||||
trackType: 'Tracing Type',
|
trackType: 'Tracing Type',
|
||||||
trackTypePlease: 'Please select a tracing type',
|
trackTypePlease: 'Please select a tracing type',
|
||||||
creater: 'Created by',
|
creater: 'Created by',
|
||||||
|
textStop: "Stop",
|
||||||
|
status: 'Status',
|
||||||
|
time: 'Time',
|
||||||
startTime: 'Start Time',
|
startTime: 'Start Time',
|
||||||
endTime: 'End Time',
|
endTime: 'End Time',
|
||||||
msisdn: 'MSISDN',
|
msisdn: 'MSISDN',
|
||||||
@@ -1098,26 +1142,31 @@ export default {
|
|||||||
imsiPlease: 'Please enter IMSI',
|
imsiPlease: 'Please enter IMSI',
|
||||||
imsiTip: 'Mobile communication IMSI number',
|
imsiTip: 'Mobile communication IMSI number',
|
||||||
srcIp: 'Source IP Address',
|
srcIp: 'Source IP Address',
|
||||||
srcIpPlease: 'Please enter the source IP address',
|
srcIpPlease: 'Please enter the IP address',
|
||||||
srcIpTip: 'Current sender IPv4 address',
|
srcIpTip: 'Current sender IPv4 address',
|
||||||
dstIp: 'Destination IP Address',
|
dstIp: 'Destination IP Address',
|
||||||
dstIpPlease: 'Please enter the destination IP address',
|
dstIpPlease: 'Please enter the IP address',
|
||||||
dstIpTip: 'IPv4 address of the receiving end of the other party',
|
dstIpTip: 'IPv4 address of the receiving end of the other party',
|
||||||
interfaces: 'Signaling Interface',
|
interfaces: 'Signaling Interface',
|
||||||
interfacesPlease: 'Please enter the signaling interface',
|
interfacesPlease: 'Please enter the signaling interface',
|
||||||
signalPort: 'Signal Port',
|
signalPort: 'Signal Port',
|
||||||
signalPortPlease: 'Please enter the signaling port',
|
signalPortPlease: 'Please enter the signaling port',
|
||||||
signalPortTip: 'Port corresponding to the interface',
|
signalPortTip: 'Port of the side corresponding to the destination IP address or source IP address',
|
||||||
rangePicker: 'Start/End Time',
|
rangePicker: 'Start/End Time',
|
||||||
rangePickerPlease: 'Please select the start and end time of the task',
|
rangePickerPlease: 'Please select the start and end time of the task',
|
||||||
comment: 'Task Description',
|
remark: 'Remark',
|
||||||
commentPlease: 'Task description can be entered',
|
remarkPlease: 'Task description can be entered',
|
||||||
addTask: 'Add Task',
|
addTask: 'Add Task',
|
||||||
editTask: 'Modify Task',
|
editTask: 'Modify Task',
|
||||||
viewTask: 'View Task',
|
viewTask: 'View Task',
|
||||||
errorTaskInfo: 'Failed to obtain task information',
|
errorTaskInfo: 'Failed to obtain task information',
|
||||||
delTask: 'Successfully deleted task {num}',
|
delTaskTip: 'Are you sure to delete the data item with record ID {id} ?',
|
||||||
delTaskTip: 'Are you sure to delete the data item with record number {num}?',
|
stopTask: 'Successful cessation of tasks {id}',
|
||||||
|
stopTaskTip: 'Confirm stopping the task with record ID {id} ?',
|
||||||
|
pcapView: "Tracking Data Analysis",
|
||||||
|
traceFile: "Tracking File",
|
||||||
|
errMsg: "Error Message",
|
||||||
|
imsiORmsisdn: "imsi or msisdn is null, cannot start task",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
faultManage: {
|
faultManage: {
|
||||||
@@ -1230,7 +1279,20 @@ export default {
|
|||||||
downTip: "Confirm the download file name is [{fileName}] File?",
|
downTip: "Confirm the download file name is [{fileName}] File?",
|
||||||
downTipErr: "Failed to get file",
|
downTipErr: "Failed to get file",
|
||||||
dirCd: "Enter Dir",
|
dirCd: "Enter Dir",
|
||||||
|
viewAs: 'View Action',
|
||||||
|
reload: "Reload",
|
||||||
|
follow: 'Monitoring Content',
|
||||||
|
tailChar: 'End Characters',
|
||||||
|
tailLines: 'End Lines',
|
||||||
},
|
},
|
||||||
|
exportFile:{
|
||||||
|
fileName:'File Source',
|
||||||
|
downTip: "Confirm the download file name is [{fileName}] File?",
|
||||||
|
downTipErr: "Failed to get file",
|
||||||
|
deleteTip: "Confirm the delete file name is [{fileName}] File?",
|
||||||
|
deleteTipErr: "Failed to delete file",
|
||||||
|
selectTip:"Please select File Name",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
monitor: {
|
monitor: {
|
||||||
session: {
|
session: {
|
||||||
@@ -1548,7 +1610,7 @@ export default {
|
|||||||
edgeTypeLineAnimateState: "Straight line with state animations",
|
edgeTypeLineAnimateState: "Straight line with state animations",
|
||||||
edgeLabelPositionStart: "Start",
|
edgeLabelPositionStart: "Start",
|
||||||
edgeLabelPositionMiddle: "Middle",
|
edgeLabelPositionMiddle: "Middle",
|
||||||
edgeLabelPositionEnd: "End",
|
edgeLabelPositionEnd: "End",
|
||||||
nodeTypeCircle: "Circle",
|
nodeTypeCircle: "Circle",
|
||||||
nodeTypeRect: "Rect",
|
nodeTypeRect: "Rect",
|
||||||
nodeTypeEllipse: "Ellipse",
|
nodeTypeEllipse: "Ellipse",
|
||||||
@@ -1873,7 +1935,7 @@ export default {
|
|||||||
componentTip:' The component path accessed by views in the page component directory, such as: system/user/index Note: The routing address without the .vue file suffix is the network address and can be filled in as a link',
|
componentTip:' The component path accessed by views in the page component directory, such as: system/user/index Note: The routing address without the .vue file suffix is the network address and can be filled in as a link',
|
||||||
perms:`Permission identification example: monitor:server:query Permission identification is used in the back-end controller, such as:['monitor:server:query'] Permission identification is used in the front-end Vue page, such as: v-perms:has="['monitor:server:query']"`
|
perms:`Permission identification example: monitor:server:query Permission identification is used in the back-end controller, such as:['monitor:server:query'] Permission identification is used in the front-end Vue page, such as: v-perms:has="['monitor:server:query']"`
|
||||||
},
|
},
|
||||||
dict:{
|
dict:{
|
||||||
dictInfo:'Dictionary Type Information',
|
dictInfo:'Dictionary Type Information',
|
||||||
dictId:'Dictionary Number',
|
dictId:'Dictionary Number',
|
||||||
dictName:'Dictionary Name',
|
dictName:'Dictionary Name',
|
||||||
@@ -1952,9 +2014,9 @@ export default {
|
|||||||
stepInstallText: 'Select Install',
|
stepInstallText: 'Select Install',
|
||||||
stepInstallTip: 'Confirm the installation of the new version of the chosen Net Meta?',
|
stepInstallTip: 'Confirm the installation of the new version of the chosen Net Meta?',
|
||||||
stepInstallModal: 'Network Element For Install',
|
stepInstallModal: 'Network Element For Install',
|
||||||
stepInstallNotNewVer: 'No new version found',
|
stepInstallNotNewVer: 'No new version found',
|
||||||
stepInstallDone: 'Installation complete, service initialized',
|
stepInstallDone: 'Installation complete, service initialized',
|
||||||
stepInstallFail: 'Installation fails, check if the service terminal environment is available!',
|
stepInstallFail: 'Installation fails, check if the service terminal environment is available!',
|
||||||
stepLicenseTitle: "Service License",
|
stepLicenseTitle: "Service License",
|
||||||
stepLicenseDesc: "Obtaining a license activation code for authorization authentication",
|
stepLicenseDesc: "Obtaining a license activation code for authorization authentication",
|
||||||
stepLicenseReload: 'Select Refresh',
|
stepLicenseReload: 'Select Refresh',
|
||||||
@@ -2043,6 +2105,30 @@ export default {
|
|||||||
hostSelectMore: "Load More {num}",
|
hostSelectMore: "Load More {num}",
|
||||||
hostSelectHeader: "Host List",
|
hostSelectHeader: "Host List",
|
||||||
},
|
},
|
||||||
|
ps:{
|
||||||
|
realTimeHigh:"High",
|
||||||
|
realTimeLow:"Low",
|
||||||
|
realTimeRegular:"Regular",
|
||||||
|
realTimeStop:"Stop",
|
||||||
|
realTime:"Real Time Speed",
|
||||||
|
pid:"PID",
|
||||||
|
name:"APP Name",
|
||||||
|
username:"User Name",
|
||||||
|
runTime:"Run Time",
|
||||||
|
numThreads:"Thread",
|
||||||
|
cpuPercent:"CPU Percent",
|
||||||
|
diskRead:"Disk Read",
|
||||||
|
diskWrite:"Disk Write",
|
||||||
|
},
|
||||||
|
net:{
|
||||||
|
PID:"PID",
|
||||||
|
name:"name",
|
||||||
|
localAddr:"localAddr",
|
||||||
|
remoteAddr:"remoteAddr",
|
||||||
|
status:"status",
|
||||||
|
type:"type",
|
||||||
|
port:"port",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ export default {
|
|||||||
page403: '没有访问权限',
|
page403: '没有访问权限',
|
||||||
page404: '找不到匹配页面',
|
page404: '找不到匹配页面',
|
||||||
helpDoc: '系统使用文档',
|
helpDoc: '系统使用文档',
|
||||||
|
traceTaskHLR: '跟踪任务 HLR',
|
||||||
lockScreen: '锁屏',
|
lockScreen: '锁屏',
|
||||||
account: {
|
account: {
|
||||||
index: "个人中心",
|
index: "个人中心",
|
||||||
@@ -185,11 +186,11 @@ export default {
|
|||||||
helpDoc: '使用手册',
|
helpDoc: '使用手册',
|
||||||
},
|
},
|
||||||
rightContent: {
|
rightContent: {
|
||||||
|
alarm: "活动告警",
|
||||||
lock: "锁屏",
|
lock: "锁屏",
|
||||||
lockTip: "确认要进行锁屏吗?",
|
lockTip: "确认要进行锁屏吗?",
|
||||||
lockPasswd: "解锁密码",
|
lockPasswd: "解锁密码",
|
||||||
lockPasswdTip: "可不设置密码",
|
lockPasswdTip: "可不设置密码",
|
||||||
helpDoc: "系统使用文档",
|
|
||||||
fullscreen: "全屏显示",
|
fullscreen: "全屏显示",
|
||||||
logout: "退出登录",
|
logout: "退出登录",
|
||||||
profile: "个人中心",
|
profile: "个人中心",
|
||||||
@@ -296,10 +297,10 @@ export default {
|
|||||||
newPassword: "新密码",
|
newPassword: "新密码",
|
||||||
newPasswordTip: "密码至少包含大小写字母、数字、特殊符号,且不少于6位",
|
newPasswordTip: "密码至少包含大小写字母、数字、特殊符号,且不少于6位",
|
||||||
newPassworddPleace: "请输入新密码",
|
newPassworddPleace: "请输入新密码",
|
||||||
confirmPassword: "确认新密码",
|
confirmPassword: "确认新密码",
|
||||||
confirmPasswordPleace: "请确认新密码",
|
confirmPasswordPleace: "请确认新密码",
|
||||||
submit: "提交修改",
|
submit: "提交修改",
|
||||||
reset: "重置",
|
reset: "重置",
|
||||||
submitTip: "确认要提交修改密码吗?",
|
submitTip: "确认要提交修改密码吗?",
|
||||||
submitOkTip: "恭喜您,{num} 账号密码修改成功!",
|
submitOkTip: "恭喜您,{num} 账号密码修改成功!",
|
||||||
submitOk: "重新登录",
|
submitOk: "重新登录",
|
||||||
@@ -569,6 +570,8 @@ export default {
|
|||||||
caller: "主叫",
|
caller: "主叫",
|
||||||
called: "被叫",
|
called: "被叫",
|
||||||
result: "结果",
|
result: "结果",
|
||||||
|
resultOk: "成功",
|
||||||
|
resultFail: "失败",
|
||||||
delTip: "确认删除编号为【{msg}】的数据项?",
|
delTip: "确认删除编号为【{msg}】的数据项?",
|
||||||
exportTip: "确认导出当前查询条件的话单数据吗?(导出最大支持一万条)",
|
exportTip: "确认导出当前查询条件的话单数据吗?(导出最大支持一万条)",
|
||||||
smfChargingID: '计费ID',
|
smfChargingID: '计费ID',
|
||||||
@@ -603,7 +606,7 @@ export default {
|
|||||||
neIdTip: '填写网元绑定的唯一标识',
|
neIdTip: '填写网元绑定的唯一标识',
|
||||||
rmUid: '资源唯一标识',
|
rmUid: '资源唯一标识',
|
||||||
rmUidPlease: '请输入资源唯一标识',
|
rmUidPlease: '请输入资源唯一标识',
|
||||||
rmUidTip: "用于网元日志、告警、指标等数据上报的标记",
|
rmUidTip: "用于网元日志、告警、指标等数据上报的标记",
|
||||||
neName: '网元名称',
|
neName: '网元名称',
|
||||||
neNamePlease: '请输入网元名称',
|
neNamePlease: '请输入网元名称',
|
||||||
ipAddr: '服务IP',
|
ipAddr: '服务IP',
|
||||||
@@ -627,9 +630,9 @@ export default {
|
|||||||
log: '日志',
|
log: '日志',
|
||||||
},
|
},
|
||||||
neInfo: {
|
neInfo: {
|
||||||
version: "网元版本",
|
version: "网元版本",
|
||||||
state: "网元状态",
|
state: "网元状态",
|
||||||
serviceState: "服务状态",
|
serviceState: "服务状态",
|
||||||
info: '状态信息',
|
info: '状态信息',
|
||||||
resourceInfo: '资源情况',
|
resourceInfo: '资源情况',
|
||||||
sysMem: "系统内存",
|
sysMem: "系统内存",
|
||||||
@@ -658,12 +661,13 @@ export default {
|
|||||||
kpiEnable: '上报',
|
kpiEnable: '上报',
|
||||||
kpiTimer: '上报周期',
|
kpiTimer: '上报周期',
|
||||||
kpiTimerPlease: '请输入上报周期(单位秒)',
|
kpiTimerPlease: '请输入上报周期(单位秒)',
|
||||||
|
omcIP: 'OMC IP',
|
||||||
},
|
},
|
||||||
backConf: {
|
backConf: {
|
||||||
export: '配置导出',
|
export: '配置导出',
|
||||||
import: '配置导入',
|
import: '配置导入',
|
||||||
title: '配置文件导入',
|
title: '配置文件导入',
|
||||||
importType: '文件来源',
|
importType: '文件来源',
|
||||||
server:'服务器文件',
|
server:'服务器文件',
|
||||||
local:'本地文件',
|
local:'本地文件',
|
||||||
localUpload:'本地上传',
|
localUpload:'本地上传',
|
||||||
@@ -735,22 +739,22 @@ export default {
|
|||||||
upgrade: "升级到新版本",
|
upgrade: "升级到新版本",
|
||||||
upgradeTip: "确认要升级到新版本吗?",
|
upgradeTip: "确认要升级到新版本吗?",
|
||||||
upgradeTipEmpty: "当前没有可用的新版本",
|
upgradeTipEmpty: "当前没有可用的新版本",
|
||||||
upgradeTipEqual: "当前版本与新版本相同",
|
upgradeTipEqual: "当前版本与新版本相同,确认要进行更新吗?",
|
||||||
rollback: '切换到上一个版本',
|
rollback: '切换到上一个版本',
|
||||||
rollbackTip: "确认切换到上一个版本吗?",
|
rollbackTip: "确认切换到上一个版本吗?",
|
||||||
rollbackTipEmpty: "目前没有可用的上一个版本",
|
rollbackTipEmpty: "目前没有可用的上一个版本",
|
||||||
rollbackTipEqual: '当前版本与之前版本相同',
|
rollbackTipEqual: '当前版本与之前版本相同,确认要进行切换吗?',
|
||||||
version: "当前版本",
|
version: "当前版本",
|
||||||
preVersion: "上一个版本",
|
preVersion: "上一个版本",
|
||||||
newVersion: "新版本",
|
newVersion: "新版本",
|
||||||
status: "版本状态",
|
status: "版本状态",
|
||||||
upgradeBatch: "批量更新",
|
upgradeBatch: "批量更新",
|
||||||
upgradeBatchTip: "对勾选的记录进行新版本升级吗?",
|
upgradeBatchTip: "对勾选的记录进行新版本升级吗?",
|
||||||
upgradeNotNewVer: '没有发现新版本',
|
upgradeNotNewVer: '没有发现新版本',
|
||||||
upgradeOMCVer: '拒绝批量操作升级OMC',
|
upgradeOMCVer: '拒绝批量操作升级OMC',
|
||||||
upgradeDone: '更新完成,服务正在重载',
|
upgradeDone: '更新完成,服务正在重载',
|
||||||
upgradeFail: '更新失败,请检查软件文件是否存在且服务终端环境是否可用!',
|
upgradeFail: '更新失败,请检查软件文件是否存在且服务终端环境是否可用!',
|
||||||
upgradeModal: '网元版本更新',
|
upgradeModal: '网元版本更新',
|
||||||
},
|
},
|
||||||
neLicense: {
|
neLicense: {
|
||||||
status: "许可证状态",
|
status: "许可证状态",
|
||||||
@@ -769,6 +773,31 @@ export default {
|
|||||||
uploadChangeOk: '网元更新许可证成功,正在后台校验!',
|
uploadChangeOk: '网元更新许可证成功,正在后台校验!',
|
||||||
uploadChangeFail: "部分网元更新许可证失败,请检查服务终端环境是否可用!",
|
uploadChangeFail: "部分网元更新许可证失败,请检查服务终端环境是否可用!",
|
||||||
},
|
},
|
||||||
|
neConfig: {
|
||||||
|
treeTitle: "配置导航",
|
||||||
|
treeSelectTip: "左侧配置导航中选择配置项信息!",
|
||||||
|
neType: "网元类型",
|
||||||
|
neTypePleace: "请选择网元类型",
|
||||||
|
noConfigData: "暂无配置项数据",
|
||||||
|
updateValue: "【 {num} 】 属性值修改成功",
|
||||||
|
updateValueErr: "属性值修改失败",
|
||||||
|
updateItem: "修改 Index 为 {num} 记录成功",
|
||||||
|
updateItemErr: "记录修改失败",
|
||||||
|
delItemOk: "删除 Index 为 {num} 记录成功",
|
||||||
|
addItemOk: "新增 Index 为 {num} 记录成功",
|
||||||
|
addItemErr: "记录新增失败",
|
||||||
|
requireUn: "【 {display} 】输入值是未知类型",
|
||||||
|
requireString: "【 {display} 】参数值不合理",
|
||||||
|
requireInt: "【 {display} 】参数值不在合理范围 {filter}",
|
||||||
|
requireIpv4: "【 {display} 】不是合法的IPV4地址",
|
||||||
|
requireIpv6: "【 {display} 】不是合法的IPV6地址",
|
||||||
|
requireEnum: "【 {display} 】不是合理的枚举值",
|
||||||
|
requireBool: "【 {display} 】不是合理的布尔类型的值",
|
||||||
|
editOkTip: "确认更新该【 {num} 】属性值吗?",
|
||||||
|
updateItemTip: "确认更新Index为 【{num}】 的数据项?",
|
||||||
|
delItemTip: "确认删除Index为 【{num}】 的数据项?",
|
||||||
|
arrayMore: "展开",
|
||||||
|
},
|
||||||
neConfigBackup: {
|
neConfigBackup: {
|
||||||
name: "名称",
|
name: "名称",
|
||||||
downTip: '确认要下载备份文件【{txt}】吗?',
|
downTip: '确认要下载备份文件【{txt}】吗?',
|
||||||
@@ -848,9 +877,10 @@ export default {
|
|||||||
imsiTip3: 'MSIN=移动客户识别码,采用等长10位数字构成',
|
imsiTip3: 'MSIN=移动客户识别码,采用等长10位数字构成',
|
||||||
amfTip: '鉴权管理域,参数最大长度为 4',
|
amfTip: '鉴权管理域,参数最大长度为 4',
|
||||||
algoIndexTip: '算法索引,介于0到15之间',
|
algoIndexTip: '算法索引,介于0到15之间',
|
||||||
kiTip: '用户签权密钥信息,最大长度为32',
|
kiTip: '用户签权密钥信息,长度只能是32',
|
||||||
opcTip: '鉴权秘钥,OPC是由Ki和OP经过计算得来的,OP为运营商的根秘钥,ki是鉴权秘钥,最大长度为32',
|
opcTip: '鉴权秘钥,OPC是由Ki和OP经过计算得来的,OP为运营商的根秘钥,ki是鉴权秘钥,最大长度为32',
|
||||||
delSure:'确认删除IMSI编号为: {imsi} 的用户吗?',
|
delSure:'确认删除IMSI编号为: {imsi} 的用户吗?',
|
||||||
|
imsiConfirm:'IMSI的长度必须为15',
|
||||||
},
|
},
|
||||||
sub: {
|
sub: {
|
||||||
subInfo:'签约信息',
|
subInfo:'签约信息',
|
||||||
@@ -905,7 +935,7 @@ export default {
|
|||||||
batchAddText: '批量新增',
|
batchAddText: '批量新增',
|
||||||
batchDelText: '批量删除',
|
batchDelText: '批量删除',
|
||||||
batchUpdateText: '批量更新',
|
batchUpdateText: '批量更新',
|
||||||
batchNum: '批量个数',
|
batchNum: '批量个数',
|
||||||
imsiTip: 'IMSI=MCC+MNC+MSIN',
|
imsiTip: 'IMSI=MCC+MNC+MSIN',
|
||||||
imsiTip1: 'MCC=移动国家号码, 由三位数字组成',
|
imsiTip1: 'MCC=移动国家号码, 由三位数字组成',
|
||||||
imsiTip2: 'MNC=移动网络号,由两位数字组成',
|
imsiTip2: 'MNC=移动网络号,由两位数字组成',
|
||||||
@@ -1022,6 +1052,7 @@ export default {
|
|||||||
},
|
},
|
||||||
customTarget:{
|
customTarget:{
|
||||||
kpiId:'自定义指标项',
|
kpiId:'自定义指标项',
|
||||||
|
kpiIdTip:'该网元没有自定义指标',
|
||||||
period:'颗粒度',
|
period:'颗粒度',
|
||||||
title:'自定义指标项标题',
|
title:'自定义指标项标题',
|
||||||
objectType:'对象类型',
|
objectType:'对象类型',
|
||||||
@@ -1032,7 +1063,14 @@ export default {
|
|||||||
delCustom:'成功删除记录编号为 {num} 自定义指标',
|
delCustom:'成功删除记录编号为 {num} 自定义指标',
|
||||||
addCustom:'添加自定义指标',
|
addCustom:'添加自定义指标',
|
||||||
editCustom:'编辑自定义指标',
|
editCustom:'编辑自定义指标',
|
||||||
errorCustomInfo: '获取信息失败',
|
errorCustomInfo: '获取信息失败',
|
||||||
|
status: '状态',
|
||||||
|
active:'正常',
|
||||||
|
inactive:'停用',
|
||||||
|
symbol:"符号",
|
||||||
|
element:'元素',
|
||||||
|
granularity:'颗粒度',
|
||||||
|
unit:'单位',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
traceManage: {
|
traceManage: {
|
||||||
@@ -1058,18 +1096,23 @@ export default {
|
|||||||
pcap: {
|
pcap: {
|
||||||
capArgPlease: '请输入tcpdump -i any支持参数',
|
capArgPlease: '请输入tcpdump -i any支持参数',
|
||||||
cmd: '命令',
|
cmd: '命令',
|
||||||
execCmd: "通用tcpdump抓包命令",
|
execCmd: "通用命令选项",
|
||||||
execCmdsSctp: "过滤sctp和port命令",
|
execCmd2: "过滤协议端口命令",
|
||||||
execUPFCmdA: '适合其他网元异常,UPF配合抓包的情况',
|
execCmd3: "分割文件按时间单位秒 (-G 10 ),最多生成文件数量 (-W 7)",
|
||||||
execUPFCmdB: '适合UPF异常需要抓包分析的情况',
|
execUPFCmdA: '标准版-UPF配合其他网元异常抓包分析',
|
||||||
|
execUPFCmdB: '标准版-UPF异常需要抓包分析',
|
||||||
|
batchOper: '批量操作',
|
||||||
|
batchStartText: '批量开始',
|
||||||
|
batchStopText: '批量停止',
|
||||||
|
batchDownText: '批量下载',
|
||||||
|
fileView: '历史抓包文件',
|
||||||
|
fileUPF: '标准版',
|
||||||
|
fileUPFTip: 'UPF内部抓包分析包',
|
||||||
textStart: "开始",
|
textStart: "开始",
|
||||||
textStartBatch: "批量开始",
|
|
||||||
textStop: "停止",
|
textStop: "停止",
|
||||||
textStopBatch: "批量停止",
|
|
||||||
textLog: "日志",
|
textLog: "日志",
|
||||||
textLogMsg: "日志信息",
|
textLogMsg: "日志信息",
|
||||||
textDown: "下载",
|
textDown: "下载",
|
||||||
textDownBatch: "批量下载",
|
|
||||||
downTip: "确认要下载 {title} 抓包数据文件吗?",
|
downTip: "确认要下载 {title} 抓包数据文件吗?",
|
||||||
downOk: "{title} 文件下载完成",
|
downOk: "{title} 文件下载完成",
|
||||||
downErr: "{title} 文件下载异常",
|
downErr: "{title} 文件下载异常",
|
||||||
@@ -1083,12 +1126,13 @@ export default {
|
|||||||
stopNotRun: "{title} 任务未运行",
|
stopNotRun: "{title} 任务未运行",
|
||||||
},
|
},
|
||||||
task: {
|
task: {
|
||||||
neTypePlease: '请选择网元类型',
|
traceId: '跟踪编号',
|
||||||
neType: '网元类型',
|
|
||||||
neID: '网元内部标识',
|
|
||||||
trackType: '跟踪类型',
|
trackType: '跟踪类型',
|
||||||
trackTypePlease: '请选择跟踪类型',
|
trackTypePlease: '请选择跟踪类型',
|
||||||
creater: '创建人',
|
creater: '创建人',
|
||||||
|
textStop: "停止",
|
||||||
|
status: '状态',
|
||||||
|
time: '时间',
|
||||||
startTime: '开始时间',
|
startTime: '开始时间',
|
||||||
endTime: '结束时间',
|
endTime: '结束时间',
|
||||||
msisdn: 'MSISDN',
|
msisdn: 'MSISDN',
|
||||||
@@ -1107,17 +1151,22 @@ export default {
|
|||||||
interfacesPlease: '请输入信令接口',
|
interfacesPlease: '请输入信令接口',
|
||||||
signalPort: '信令端口',
|
signalPort: '信令端口',
|
||||||
signalPortPlease: '请输入信令端口',
|
signalPortPlease: '请输入信令端口',
|
||||||
signalPortTip: '接口对应的端口',
|
signalPortTip: '目标IP地址或源IP地址对应一方的端口',
|
||||||
rangePicker: '开始结束时间',
|
rangePicker: '开始结束时间',
|
||||||
rangePickerPlease: '请选择任务时间开始结束时间',
|
rangePickerPlease: '请选择任务时间开始结束时间',
|
||||||
comment: '任务说明',
|
remark: '说明',
|
||||||
commentPlease: '可输入任务说明',
|
remarkPlease: '可输入任务说明',
|
||||||
addTask: '添加任务',
|
addTask: '添加任务',
|
||||||
editTask: '修改任务',
|
editTask: '修改任务',
|
||||||
viewTask: '查看任务',
|
viewTask: '查看任务',
|
||||||
errorTaskInfo: '获取任务信息失败',
|
errorTaskInfo: '获取任务信息失败',
|
||||||
delTask: '成功删除任务 {num}',
|
delTaskTip: '确认删除记录编号为 {id} 的数据项?',
|
||||||
delTaskTip: '确认删除记录编号为 {num} 的数据项?',
|
stopTask: '成功停止任务 {id}',
|
||||||
|
stopTaskTip: '确认停止记录编号为 {id} 的任务?',
|
||||||
|
pcapView: "跟踪数据分析",
|
||||||
|
traceFile: "跟踪文件",
|
||||||
|
errMsg: "错误信息",
|
||||||
|
imsiORmsisdn: "imsi 或 msisdn 是空值,不能开始任务",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
faultManage: {
|
faultManage: {
|
||||||
@@ -1230,8 +1279,21 @@ export default {
|
|||||||
downTip: "确认下载文件名为 【{fileName}】 文件?",
|
downTip: "确认下载文件名为 【{fileName}】 文件?",
|
||||||
downTipErr: "文件获取失败",
|
downTipErr: "文件获取失败",
|
||||||
dirCd: "进入目录",
|
dirCd: "进入目录",
|
||||||
|
viewAs: '查看操作',
|
||||||
|
reload: "重载",
|
||||||
|
follow: '监视内容变化',
|
||||||
|
tailChar: '末尾字数',
|
||||||
|
tailLines: '末尾行数',
|
||||||
},
|
},
|
||||||
},
|
exportFile:{
|
||||||
|
fileName:'文件来源',
|
||||||
|
downTip: "确认下载文件名为 【{fileName}】 文件?",
|
||||||
|
downTipErr: "文件获取失败",
|
||||||
|
deleteTip: "确认删除文件名为 【{fileName}】 文件?",
|
||||||
|
deleteTipErr: "文件删除失败",
|
||||||
|
selectTip:"请选择文件名",
|
||||||
|
}
|
||||||
|
},
|
||||||
monitor: {
|
monitor: {
|
||||||
session: {
|
session: {
|
||||||
userName: "登录账号",
|
userName: "登录账号",
|
||||||
@@ -1548,7 +1610,7 @@ export default {
|
|||||||
edgeTypeLineAnimateState: "直线,含有状态动画",
|
edgeTypeLineAnimateState: "直线,含有状态动画",
|
||||||
edgeLabelPositionStart: "开头",
|
edgeLabelPositionStart: "开头",
|
||||||
edgeLabelPositionMiddle: "中间",
|
edgeLabelPositionMiddle: "中间",
|
||||||
edgeLabelPositionEnd: "末尾",
|
edgeLabelPositionEnd: "末尾",
|
||||||
nodeTypeCircle: "圆形",
|
nodeTypeCircle: "圆形",
|
||||||
nodeTypeRect: "矩形",
|
nodeTypeRect: "矩形",
|
||||||
nodeTypeEllipse: "椭圆",
|
nodeTypeEllipse: "椭圆",
|
||||||
@@ -1840,8 +1902,8 @@ export default {
|
|||||||
unlockSure:'确认解锁用户 【{username}】 数据项?',
|
unlockSure:'确认解锁用户 【{username}】 数据项?',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
menu:{
|
menu:{
|
||||||
menuInfo:'菜单信息',
|
menuInfo:'菜单信息',
|
||||||
menuName:'菜单名称',
|
menuName:'菜单名称',
|
||||||
menuId:'菜单编号',
|
menuId:'菜单编号',
|
||||||
menuSort:'菜单排序',
|
menuSort:'菜单排序',
|
||||||
@@ -1873,7 +1935,7 @@ export default {
|
|||||||
componentTip:' 页面组件目录 views 访问的组件路径,如:system/user/index 注意:不带 .vue 文件后缀路由地址是网络地址可填入链接',
|
componentTip:' 页面组件目录 views 访问的组件路径,如:system/user/index 注意:不带 .vue 文件后缀路由地址是网络地址可填入链接',
|
||||||
perms:`权限标识示例:monitor:server:query 后端控制器中使用权限标识,如:['monitor:server:query'] 前端vue页面中使用权限标识,如:v-perms:has="['monitor:server:query']"`
|
perms:`权限标识示例:monitor:server:query 后端控制器中使用权限标识,如:['monitor:server:query'] 前端vue页面中使用权限标识,如:v-perms:has="['monitor:server:query']"`
|
||||||
},
|
},
|
||||||
dict:{
|
dict:{
|
||||||
dictInfo:'字典类型信息',
|
dictInfo:'字典类型信息',
|
||||||
dictId:'字典编号',
|
dictId:'字典编号',
|
||||||
dictName:'字典名称',
|
dictName:'字典名称',
|
||||||
@@ -1952,9 +2014,9 @@ export default {
|
|||||||
stepInstallText: '选择安装',
|
stepInstallText: '选择安装',
|
||||||
stepInstallTip: '确认安装选择的网元新版本吗?',
|
stepInstallTip: '确认安装选择的网元新版本吗?',
|
||||||
stepInstallModal: '网元进行安装',
|
stepInstallModal: '网元进行安装',
|
||||||
stepInstallNotNewVer: '没有发现新版本',
|
stepInstallNotNewVer: '没有发现新版本',
|
||||||
stepInstallDone: '安装完成,服务进入初始化',
|
stepInstallDone: '安装完成,服务进入初始化',
|
||||||
stepInstallFail: '安装失败,请检查服务终端环境是否可用!',
|
stepInstallFail: '安装失败,请检查服务终端环境是否可用!',
|
||||||
stepLicenseTitle: "网元许可授权",
|
stepLicenseTitle: "网元许可授权",
|
||||||
stepLicenseDesc: "获取网元许可激活码进行授权认证",
|
stepLicenseDesc: "获取网元许可激活码进行授权认证",
|
||||||
stepLicenseReload: '选择刷新许可证',
|
stepLicenseReload: '选择刷新许可证',
|
||||||
@@ -2043,6 +2105,30 @@ export default {
|
|||||||
hostSelectMore: "加载更多 {num}",
|
hostSelectMore: "加载更多 {num}",
|
||||||
hostSelectHeader: "主机列表",
|
hostSelectHeader: "主机列表",
|
||||||
},
|
},
|
||||||
|
ps:{
|
||||||
|
realTimeHigh:"高",
|
||||||
|
realTimeLow:"低",
|
||||||
|
realTimeRegular:"常规",
|
||||||
|
realTimeStop:"已暂停",
|
||||||
|
realTime:"实时更新速度",
|
||||||
|
pid:"PID",
|
||||||
|
name:"应用名称",
|
||||||
|
username:"用户名",
|
||||||
|
runTime:"运行时间",
|
||||||
|
numThreads:"线程数",
|
||||||
|
cpuPercent:"CPU使用率",
|
||||||
|
diskRead:"磁盘读取",
|
||||||
|
diskWrite:"磁盘写入",
|
||||||
|
},
|
||||||
|
net:{
|
||||||
|
PID:"PID",
|
||||||
|
name:"名称",
|
||||||
|
localAddr:"localAddr",
|
||||||
|
remoteAddr:"remoteAddr",
|
||||||
|
status:"状态",
|
||||||
|
type:"类型",
|
||||||
|
port:"接口",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ watch(
|
|||||||
// 路由地址含有内嵌地址标识又是隐藏菜单需要处理打开高亮菜单栏
|
// 路由地址含有内嵌地址标识又是隐藏菜单需要处理打开高亮菜单栏
|
||||||
if (v.path.includes(MENU_PATH_INLINE) && v.meta.hideInMenu) {
|
if (v.path.includes(MENU_PATH_INLINE) && v.meta.hideInMenu) {
|
||||||
const idx = v.path.lastIndexOf(MENU_PATH_INLINE);
|
const idx = v.path.lastIndexOf(MENU_PATH_INLINE);
|
||||||
layoutState.openKeys.splice(-1);
|
layoutState.openKeys = layoutState.selectedKeys.slice(0, -1);
|
||||||
layoutState.selectedKeys[matched.length - 1] = v.path.slice(0, idx);
|
layoutState.selectedKeys[matched.length - 1] = v.path.slice(0, idx);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -435,7 +435,8 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
& #serverTimeDom {
|
& #serverTimeDom {
|
||||||
color: #00000085;
|
color: inherit;
|
||||||
|
opacity: 0.85;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,16 +57,6 @@ function fnClickAlarm() {
|
|||||||
router.push({ name: 'ActiveAlarm_2088' });
|
router.push({ name: 'ActiveAlarm_2088' });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**系统使用手册跳转 */
|
|
||||||
function fnClickHelpDoc(language?: string) {
|
|
||||||
const routeData = router.resolve({ name: 'HelpDoc' });
|
|
||||||
let href = routeData.href;
|
|
||||||
if (language) {
|
|
||||||
href = `${routeData.href}?language=${language}`;
|
|
||||||
}
|
|
||||||
window.open(href, '_blank');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**改变多语言 */
|
/**改变多语言 */
|
||||||
function fnChangeLocale(e: any) {
|
function fnChangeLocale(e: any) {
|
||||||
changeLocale(e.key);
|
changeLocale(e.key);
|
||||||
@@ -75,18 +65,21 @@ function fnChangeLocale(e: any) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-space :size="12" align="center">
|
<a-space :size="12" align="center">
|
||||||
<a-button type="text" style="color: inherit" @click="fnClickAlarm">
|
<a-tooltip placement="bottom">
|
||||||
<template #icon>
|
<template #title>{{ t('loayouts.rightContent.alarm') }}</template>
|
||||||
<a-badge
|
<a-button type="text" style="color: inherit" @click="fnClickAlarm">
|
||||||
:count="useAlarmStore().activeAlarmTotal"
|
<template #icon>
|
||||||
:overflow-count="99"
|
<a-badge
|
||||||
status="warning"
|
:count="useAlarmStore().activeAlarmTotal"
|
||||||
style="color: inherit"
|
:overflow-count="99"
|
||||||
>
|
status="warning"
|
||||||
<BellOutlined />
|
style="color: inherit"
|
||||||
</a-badge>
|
>
|
||||||
</template>
|
<BellOutlined />
|
||||||
</a-button>
|
</a-badge>
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
|
||||||
<!-- 锁屏操作 -->
|
<!-- 锁屏操作 -->
|
||||||
<span v-perms:has="['system:setting:lock']">
|
<span v-perms:has="['system:setting:lock']">
|
||||||
@@ -126,18 +119,6 @@ function fnChangeLocale(e: any) {
|
|||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- 用户帮助手册 -->
|
|
||||||
<span v-perms:has="['system:setting:doc']">
|
|
||||||
<a-tooltip placement="bottom">
|
|
||||||
<template #title>{{ t('loayouts.rightContent.helpDoc') }}</template>
|
|
||||||
<a-button type="text" style="color: inherit" @click="fnClickHelpDoc()">
|
|
||||||
<template #icon>
|
|
||||||
<QuestionCircleOutlined />
|
|
||||||
</template>
|
|
||||||
</a-button>
|
|
||||||
</a-tooltip>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<a-tooltip placement="bottom">
|
<a-tooltip placement="bottom">
|
||||||
<template #title>{{ t('loayouts.rightContent.fullscreen') }}</template>
|
<template #title>{{ t('loayouts.rightContent.fullscreen') }}</template>
|
||||||
<a-button type="text" style="color: inherit" @click="toggle">
|
<a-button type="text" style="color: inherit" @click="toggle">
|
||||||
|
|||||||
@@ -13,10 +13,13 @@ import {
|
|||||||
import {
|
import {
|
||||||
APP_REQUEST_HEADER_CODE,
|
APP_REQUEST_HEADER_CODE,
|
||||||
APP_REQUEST_HEADER_VERSION,
|
APP_REQUEST_HEADER_VERSION,
|
||||||
|
APP_DATA_API_KEY,
|
||||||
} from '@/constants/app-constants';
|
} from '@/constants/app-constants';
|
||||||
import {
|
import {
|
||||||
|
RESULT_CODE_ENCRYPT,
|
||||||
RESULT_CODE_ERROR,
|
RESULT_CODE_ERROR,
|
||||||
RESULT_CODE_SUCCESS,
|
RESULT_CODE_SUCCESS,
|
||||||
|
RESULT_MSG_ENCRYPT,
|
||||||
RESULT_MSG_ERROR,
|
RESULT_MSG_ERROR,
|
||||||
RESULT_MSG_NOT_TYPE,
|
RESULT_MSG_NOT_TYPE,
|
||||||
RESULT_MSG_SERVER_ERROR,
|
RESULT_MSG_SERVER_ERROR,
|
||||||
@@ -25,11 +28,12 @@ import {
|
|||||||
RESULT_MSG_URL_NOTFOUND,
|
RESULT_MSG_URL_NOTFOUND,
|
||||||
RESULT_MSG_URL_RESUBMIT,
|
RESULT_MSG_URL_RESUBMIT,
|
||||||
} from '@/constants/result-constants';
|
} from '@/constants/result-constants';
|
||||||
|
import { decryptAES, encryptAES } from '@/utils/encrypt-utils';
|
||||||
|
|
||||||
/**响应结果类型 */
|
/**响应结果类型 */
|
||||||
export type ResultType = {
|
export type ResultType = {
|
||||||
/**响应码 */
|
/**响应码 */
|
||||||
code: number | 1 | 0;
|
code: number;
|
||||||
/**信息 */
|
/**信息 */
|
||||||
msg: string;
|
msg: string;
|
||||||
/**数据 */
|
/**数据 */
|
||||||
@@ -76,6 +80,8 @@ type OptionsType = {
|
|||||||
body?: BodyInit;
|
body?: BodyInit;
|
||||||
/**防止数据重复提交 */
|
/**防止数据重复提交 */
|
||||||
repeatSubmit?: boolean;
|
repeatSubmit?: boolean;
|
||||||
|
/**接口数据加密 */
|
||||||
|
crypto?: boolean;
|
||||||
/**携带授权Token请求头 */
|
/**携带授权Token请求头 */
|
||||||
whithToken?: boolean;
|
whithToken?: boolean;
|
||||||
/**中断控制信号,timeout不会生效 */
|
/**中断控制信号,timeout不会生效 */
|
||||||
@@ -167,24 +173,42 @@ function beforeRequest(options: OptionsType): OptionsType | Promise<any> {
|
|||||||
|
|
||||||
// 请求拼接地址栏参数
|
// 请求拼接地址栏参数
|
||||||
if (options.params) {
|
if (options.params) {
|
||||||
let paramStr = '';
|
|
||||||
const params = options.params;
|
const params = options.params;
|
||||||
|
const queryParams: string[] = [];
|
||||||
for (const key in params) {
|
for (const key in params) {
|
||||||
const value = params[key];
|
const value = params[key];
|
||||||
// 空字符或未定义的值不作为参数发送
|
// 空字符或未定义的值不作为参数发送
|
||||||
if (value === '' || value === undefined) continue;
|
if (value === '' || value === undefined) continue;
|
||||||
paramStr += `&${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
const str = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
||||||
|
queryParams.push(str);
|
||||||
}
|
}
|
||||||
if (paramStr && paramStr.startsWith('&')) {
|
const paramStr = queryParams.join('&');
|
||||||
options.url = `${options.url}?${paramStr.substring(1)}`;
|
if (paramStr) {
|
||||||
|
const separator = options.url.includes('?') ? '&' : '?';
|
||||||
|
// 请求加密
|
||||||
|
if (options.crypto) {
|
||||||
|
debugger;
|
||||||
|
const data = encryptAES(JSON.stringify(paramStr), APP_DATA_API_KEY);
|
||||||
|
options.url += `${separator}data=${encodeURIComponent(data)}`;
|
||||||
|
} else {
|
||||||
|
options.url += `${separator}${paramStr}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.method === 'get') return options;
|
||||||
|
|
||||||
// 非get参数提交
|
// 非get参数提交
|
||||||
if (options.data instanceof FormData) {
|
let body = options.data
|
||||||
options.body = options.data;
|
if (body instanceof FormData) {
|
||||||
} else {
|
options.body = body;
|
||||||
options.body = JSON.stringify(options.data);
|
} else if (body) {
|
||||||
|
// 请求加密
|
||||||
|
if (options.crypto) {
|
||||||
|
const data = encryptAES(JSON.stringify(body), APP_DATA_API_KEY);
|
||||||
|
body = { data };
|
||||||
|
}
|
||||||
|
options.body = JSON.stringify(body);
|
||||||
}
|
}
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
@@ -199,6 +223,28 @@ function interceptorResponse(res: ResultType): ResultType | Promise<any> {
|
|||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 响应数据解密
|
||||||
|
if (res.code === RESULT_CODE_ENCRYPT) {
|
||||||
|
const str = decryptAES(res.data, APP_DATA_API_KEY);
|
||||||
|
let data = {};
|
||||||
|
try {
|
||||||
|
data = JSON.parse(str);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
if (Object.keys(data).length === 0) {
|
||||||
|
return Promise.resolve({
|
||||||
|
code: RESULT_CODE_ERROR,
|
||||||
|
msg: RESULT_MSG_ENCRYPT[language],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve({
|
||||||
|
code: RESULT_CODE_SUCCESS,
|
||||||
|
msg: RESULT_MSG_SUCCESS[language],
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 风格处理
|
// 风格处理
|
||||||
if (!Reflect.has(res, 'code')) {
|
if (!Reflect.has(res, 'code')) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
@@ -266,7 +312,7 @@ export async function request(options: OptionsType): Promise<ResultType> {
|
|||||||
case 'text': // 文本数据
|
case 'text': // 文本数据
|
||||||
const str = await res.text();
|
const str = await res.text();
|
||||||
return {
|
return {
|
||||||
code: 1,
|
code: RESULT_CODE_SUCCESS,
|
||||||
msg: str,
|
msg: str,
|
||||||
};
|
};
|
||||||
case 'json': // json格式数据
|
case 'json': // json格式数据
|
||||||
|
|||||||
124
src/plugins/wk-worker.ts
Normal file
124
src/plugins/wk-worker.ts
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/**连接参数类型 */
|
||||||
|
export type OptionsType = {
|
||||||
|
/**
|
||||||
|
* 线路工作服务地址
|
||||||
|
*
|
||||||
|
* self.onmessage = error => {} - 接收消息
|
||||||
|
*
|
||||||
|
* self.postMessage() -回复消息
|
||||||
|
*/
|
||||||
|
url: string;
|
||||||
|
/**message事件的回调函数 */
|
||||||
|
onmessage: (data: any) => void;
|
||||||
|
/**error事件的回调函数 */
|
||||||
|
onerror: Function;
|
||||||
|
/**close事件的回调函数 */
|
||||||
|
onclose?: (code: number) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web Worker 使用方法
|
||||||
|
*
|
||||||
|
* import { WK, OptionsType } from '@/plugins/wk-worker';
|
||||||
|
*
|
||||||
|
* const wk = new WK();
|
||||||
|
*
|
||||||
|
* 创建连接
|
||||||
|
* const options: OptionsType = { };
|
||||||
|
* wk.connect(options);
|
||||||
|
*
|
||||||
|
* 手动关闭
|
||||||
|
* wk.close();
|
||||||
|
*/
|
||||||
|
export class WK {
|
||||||
|
/**wk 实例 */
|
||||||
|
private wk: Worker | null = null;
|
||||||
|
/**wk 连接参数 */
|
||||||
|
private options: OptionsType | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param {object} params 构造函数参数
|
||||||
|
*/
|
||||||
|
constructor(options?: OptionsType) {
|
||||||
|
if (!window.Worker) {
|
||||||
|
// 检测浏览器支持
|
||||||
|
console.error('Sorry! Browser does not support Web Workers');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
options && this.connect(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建链接
|
||||||
|
* @param {object} options 连接参数
|
||||||
|
*/
|
||||||
|
public connect(options: OptionsType) {
|
||||||
|
this.options = options;
|
||||||
|
try {
|
||||||
|
const wk = new Worker(options.url);
|
||||||
|
// 用于收到来自其消息时的回调函数。
|
||||||
|
wk.onmessage = ev => {
|
||||||
|
// 解析文本消息
|
||||||
|
if (ev.type === 'message') {
|
||||||
|
try {
|
||||||
|
if (typeof options.onmessage === 'function') {
|
||||||
|
options.onmessage(ev.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('worker message formatting error', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 用于发生错误关闭后的回调函数。
|
||||||
|
wk.onmessageerror = ev => {
|
||||||
|
console.error('worker message error anomaly', ev);
|
||||||
|
|
||||||
|
if (typeof options.onclose === 'function') {
|
||||||
|
options.onerror(ev);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 用于发生错误后的回调函数。
|
||||||
|
wk.onerror = ev => {
|
||||||
|
console.error('worker error anomaly', ev);
|
||||||
|
|
||||||
|
if (typeof options.onerror === 'function') {
|
||||||
|
options.onerror(ev);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.wk = wk;
|
||||||
|
} catch (error) {
|
||||||
|
if (typeof options.onerror === 'function') {
|
||||||
|
options.onerror(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息
|
||||||
|
* @param data JSON数据
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public send(data: Record<string, any>): boolean {
|
||||||
|
if (!this.wk) {
|
||||||
|
console.warn('worker unavailable');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.wk.postMessage(data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手动关闭会被立即终止
|
||||||
|
public close() {
|
||||||
|
if (!this.wk) {
|
||||||
|
console.warn('worker unavailable');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.wk.terminate();
|
||||||
|
// 用于关闭后的回调函数。
|
||||||
|
if (this.options && typeof this.options.onclose === 'function') {
|
||||||
|
this.options.onclose(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ export type OptionsType = {
|
|||||||
/**
|
/**
|
||||||
* WebSocket 使用方法
|
* WebSocket 使用方法
|
||||||
*
|
*
|
||||||
* import WS from '@/plugins/ws-websocket.ts';
|
* import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
*
|
*
|
||||||
* const ws = new WS();
|
* const ws = new WS();
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -87,6 +87,12 @@ const constantRoutes: RouteRecordRaw[] = [
|
|||||||
meta: { title: 'router.helpDoc' },
|
meta: { title: 'router.helpDoc' },
|
||||||
component: () => import('@/views/tool/help/index.vue'),
|
component: () => import('@/views/tool/help/index.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/trace-task-hlr',
|
||||||
|
name: 'TraceTaskHLR',
|
||||||
|
meta: { title: 'router.traceTaskHLR' },
|
||||||
|
component: () => import('@/views/traceManage/task-hlr/index.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/quick-start',
|
path: '/quick-start',
|
||||||
name: 'QuickStart',
|
name: 'QuickStart',
|
||||||
@@ -154,6 +160,7 @@ const WHITE_LIST: string[] = [
|
|||||||
'/help',
|
'/help',
|
||||||
'/register',
|
'/register',
|
||||||
'/quick-start',
|
'/quick-start',
|
||||||
|
'/trace-task-hlr',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**全局路由-前置守卫 */
|
/**全局路由-前置守卫 */
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { defineStore } from 'pinia';
|
|||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { listAllNeInfo } from '@/api/ne/neInfo';
|
import { listAllNeInfo } from '@/api/ne/neInfo';
|
||||||
import { parseDataToOptions } from '@/utils/parse-tree-utils';
|
import { parseDataToOptions } from '@/utils/parse-tree-utils';
|
||||||
import { getNeTraceInterfaceAll } from '@/api/traceManage/task';
|
import { getNeTraceInterfaceAll } from '@/api/trace/task';
|
||||||
import { getNePerformanceList } from '@/api/perfManage/taskManage';
|
import { getNePerformanceList } from '@/api/perfManage/taskManage';
|
||||||
|
|
||||||
/**网元信息类型 */
|
/**网元信息类型 */
|
||||||
|
|||||||
50
src/utils/encrypt-utils.ts
Normal file
50
src/utils/encrypt-utils.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import CryptoJS from 'crypto-js';
|
||||||
|
import { isValid, decode } from 'js-base64';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES 加密并转为 base64
|
||||||
|
* @param plaintext 数据字符串
|
||||||
|
* @param aeskey 密钥
|
||||||
|
* @returns 加密字符串
|
||||||
|
*/
|
||||||
|
export function encryptAES(plaintext: string, aeskey: string): string {
|
||||||
|
const nowRoaund = new Date().getTime().toString(6);
|
||||||
|
const key = CryptoJS.enc.Utf8.parse(aeskey);
|
||||||
|
const iv = CryptoJS.enc.Utf8.parse(nowRoaund);
|
||||||
|
const encrypted = CryptoJS.AES.encrypt(`${nowRoaund}${plaintext}`, key, {
|
||||||
|
iv: iv,
|
||||||
|
blockSize: 16,
|
||||||
|
mode: CryptoJS.mode.CBC,
|
||||||
|
padding: CryptoJS.pad.Pkcs7,
|
||||||
|
format: CryptoJS.format.OpenSSL,
|
||||||
|
});
|
||||||
|
return encrypted.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES 解密
|
||||||
|
* @param ciphertext 加密字符串
|
||||||
|
* @param aeskey 密钥
|
||||||
|
* @returns 数据字符串
|
||||||
|
*/
|
||||||
|
export function decryptAES(ciphertext: string, aeskey: string): string {
|
||||||
|
const nowRoaund = new Date().getTime().toString(6);
|
||||||
|
const key = CryptoJS.enc.Utf8.parse(aeskey);
|
||||||
|
const iv = CryptoJS.enc.Utf8.parse(nowRoaund);
|
||||||
|
const decrypted = CryptoJS.AES.decrypt(ciphertext, key, {
|
||||||
|
iv: iv,
|
||||||
|
blockSize: 16,
|
||||||
|
mode: CryptoJS.mode.CBC,
|
||||||
|
padding: CryptoJS.pad.Pkcs7,
|
||||||
|
format: CryptoJS.format.OpenSSL,
|
||||||
|
});
|
||||||
|
const base64Str = decrypted.toString(CryptoJS.enc.Base64);
|
||||||
|
if (isValid(base64Str)) {
|
||||||
|
const str = decode(base64Str);
|
||||||
|
const idx = str.indexOf(':)', 10);
|
||||||
|
if (idx > 10) {
|
||||||
|
return str.substring(idx + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
@@ -37,7 +37,10 @@ export function parseStrLineToHump(str: string): string {
|
|||||||
*/
|
*/
|
||||||
export function parseStrHumpToLine(str: string): string {
|
export function parseStrHumpToLine(str: string): string {
|
||||||
if (!str) return str;
|
if (!str) return str;
|
||||||
return str.replace(/([A-Z])/g, '_$1').toLowerCase();
|
return str
|
||||||
|
.replace(/([A-Z])/g, '_$1')
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/^_/, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,6 +95,22 @@ export function parseObjLineToHump(obj: any): any {
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化文件大小
|
||||||
|
* @param bytes 字节数
|
||||||
|
* @param decimalPlaces 保留小数位,默认2位
|
||||||
|
* @returns 单位 xB
|
||||||
|
*/
|
||||||
|
export function parseSizeFromFile(bytes: number, decimalPlaces: number = 2) {
|
||||||
|
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||||
|
let i = 0;
|
||||||
|
while (bytes >= 1024 && i < units.length - 1) {
|
||||||
|
bytes /= 1024;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return `${bytes.toFixed(decimalPlaces || 1)} ${units[i]}`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换磁盘容量
|
* 转换磁盘容量
|
||||||
* @param size 数值大小
|
* @param size 数值大小
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ 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 {
|
import {
|
||||||
delIMSDataCDR,
|
delIMSDataCDR,
|
||||||
exportIMSDataCDR,
|
exportIMSDataCDR,
|
||||||
@@ -36,6 +37,9 @@ let dict: {
|
|||||||
cdrCallType: [],
|
cdrCallType: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**网元可选 */
|
||||||
|
let neOtions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
/**开始结束时间 */
|
/**开始结束时间 */
|
||||||
let queryRangePicker = ref<[string, string]>(['', '']);
|
let queryRangePicker = ref<[string, string]>(['', '']);
|
||||||
|
|
||||||
@@ -135,17 +139,6 @@ let tableColumns: ColumnsType = [
|
|||||||
align: 'left',
|
align: 'left',
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: t('views.dashboard.cdr.called'),
|
|
||||||
dataIndex: 'cdrJSON',
|
|
||||||
key: 'calledParty',
|
|
||||||
align: 'left',
|
|
||||||
width: 120,
|
|
||||||
customRender(opt) {
|
|
||||||
const cdrJSON = opt.value;
|
|
||||||
return cdrJSON.calledParty;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: t('views.dashboard.cdr.caller'),
|
title: t('views.dashboard.cdr.caller'),
|
||||||
dataIndex: 'cdrJSON',
|
dataIndex: 'cdrJSON',
|
||||||
@@ -157,6 +150,17 @@ let tableColumns: ColumnsType = [
|
|||||||
return cdrJSON.callerParty;
|
return cdrJSON.callerParty;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.called'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
key: 'calledParty',
|
||||||
|
align: 'left',
|
||||||
|
width: 120,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.calledParty;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('views.dashboard.cdr.duration'),
|
title: t('views.dashboard.cdr.duration'),
|
||||||
dataIndex: 'cdrJSON',
|
dataIndex: 'cdrJSON',
|
||||||
@@ -375,6 +379,7 @@ 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',
|
||||||
@@ -383,7 +388,7 @@ function fnRealTime() {
|
|||||||
*
|
*
|
||||||
* IMS_CDR会话事件(GroupID:1005)
|
* IMS_CDR会话事件(GroupID:1005)
|
||||||
*/
|
*/
|
||||||
subGroupID: '1005',
|
subGroupID: `1005_${queryParams.neId}`,
|
||||||
},
|
},
|
||||||
onmessage: wsMessage,
|
onmessage: wsMessage,
|
||||||
onerror: wsError,
|
onerror: wsError,
|
||||||
@@ -391,6 +396,8 @@ function fnRealTime() {
|
|||||||
ws.connect(options);
|
ws.connect(options);
|
||||||
} else {
|
} else {
|
||||||
ws.close();
|
ws.close();
|
||||||
|
tableState.seached = true;
|
||||||
|
fnGetList(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,7 +420,7 @@ function wsMessage(res: Record<string, any>) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// cdrEvent CDR会话事件
|
// cdrEvent CDR会话事件
|
||||||
if (data.groupId === '1005') {
|
if (data.groupId === `1005_${queryParams.neId}`) {
|
||||||
const cdrEvent = data.data;
|
const cdrEvent = data.data;
|
||||||
queue.add(async () => {
|
queue.add(async () => {
|
||||||
modalState.maxId += 1;
|
modalState.maxId += 1;
|
||||||
@@ -436,14 +443,39 @@ function wsMessage(res: Record<string, any>) {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 初始字典数据
|
// 初始字典数据
|
||||||
Promise.allSettled([getDict('cdr_sip_code'), getDict('cdr_call_type')])
|
Promise.allSettled([getDict('cdr_sip_code'), getDict('cdr_call_type')]).then(
|
||||||
.then(resArr => {
|
resArr => {
|
||||||
if (resArr[0].status === 'fulfilled') {
|
if (resArr[0].status === 'fulfilled') {
|
||||||
dict.cdrSipCode = resArr[0].value;
|
dict.cdrSipCode = resArr[0].value;
|
||||||
}
|
}
|
||||||
if (resArr[1].status === 'fulfilled') {
|
if (resArr[1].status === 'fulfilled') {
|
||||||
dict.cdrCallType = resArr[1].value;
|
dict.cdrCallType = resArr[1].value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// 获取网元网元列表
|
||||||
|
useNeInfoStore()
|
||||||
|
.fnNelist()
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length > 0) {
|
||||||
|
let arr: Record<string, any>[] = [];
|
||||||
|
res.data.forEach(i => {
|
||||||
|
if (i.neType === 'IMS') {
|
||||||
|
arr.push({ value: i.neId, label: i.neName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neOtions.value = arr;
|
||||||
|
if (arr.length > 0) {
|
||||||
|
queryParams.neId = arr[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
// 获取列表数据
|
// 获取列表数据
|
||||||
@@ -468,10 +500,57 @@ 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="IMS" name="neId ">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.neId"
|
||||||
|
:options="neOtions"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.called')"
|
||||||
|
name="calledParty"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.calledParty"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.caller')"
|
||||||
|
name="callerParty "
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.callerParty"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="4" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="fnQueryReset">
|
||||||
|
<template #icon><ClearOutlined /></template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
<a-col :lg="8" :md="12" :xs="24">
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.dashboard.cdr.recordType')"
|
:label="t('views.dashboard.cdr.recordType')"
|
||||||
name="recordType "
|
name="recordType"
|
||||||
>
|
>
|
||||||
<a-select
|
<a-select
|
||||||
v-model:value="recordTypes"
|
v-model:value="recordTypes"
|
||||||
@@ -484,30 +563,6 @@ onBeforeUnmount(() => {
|
|||||||
></a-select>
|
></a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="4" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.dashboard.cdr.called')"
|
|
||||||
name="calledParty "
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="queryParams.calledParty"
|
|
||||||
allow-clear
|
|
||||||
:placeholder="t('common.inputPlease')"
|
|
||||||
></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="4" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.dashboard.cdr.caller')"
|
|
||||||
name="callerParty "
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="queryParams.callerParty"
|
|
||||||
allow-clear
|
|
||||||
:placeholder="t('common.inputPlease')"
|
|
||||||
></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="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')"
|
||||||
@@ -524,20 +579,6 @@ onBeforeUnmount(() => {
|
|||||||
></a-range-picker>
|
></a-range-picker>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="4" :md="12" :xs="24">
|
|
||||||
<a-form-item>
|
|
||||||
<a-space :size="8">
|
|
||||||
<a-button type="primary" @click.prevent="fnGetList(1)">
|
|
||||||
<template #icon><SearchOutlined /></template>
|
|
||||||
{{ t('common.search') }}
|
|
||||||
</a-button>
|
|
||||||
<a-button type="default" @click.prevent="fnQueryReset">
|
|
||||||
<template #icon><ClearOutlined /></template>
|
|
||||||
{{ t('common.reset') }}
|
|
||||||
</a-button>
|
|
||||||
</a-space>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -595,6 +636,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>
|
||||||
@@ -663,7 +705,7 @@ onBeforeUnmount(() => {
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ t('views.dashboard.overview.userActivity.resultOK') }}
|
{{ t('views.dashboard.cdr.resultOk') }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'id'">
|
<template v-if="column.key === 'id'">
|
||||||
@@ -734,7 +776,7 @@ onBeforeUnmount(() => {
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ t('views.dashboard.overview.userActivity.resultOK') }}
|
{{ t('views.dashboard.cdr.resultOk') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ 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 { listMMEDataUE, delMMEDataUE, exportMMEDataUE } from '@/api/neData/mme';
|
import { listMMEDataUE, delMMEDataUE, exportMMEDataUE } from '@/api/neData/mme';
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
@@ -21,6 +22,9 @@ 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: {
|
||||||
/**UE 事件认证代码类型 */
|
/**UE 事件认证代码类型 */
|
||||||
@@ -337,6 +341,7 @@ 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',
|
||||||
@@ -345,7 +350,7 @@ function fnRealTime() {
|
|||||||
*
|
*
|
||||||
* MME_UE会话事件(GroupID:1011)
|
* MME_UE会话事件(GroupID:1011)
|
||||||
*/
|
*/
|
||||||
subGroupID: '1011',
|
subGroupID: `1011_${queryParams.neId}`,
|
||||||
},
|
},
|
||||||
onmessage: wsMessage,
|
onmessage: wsMessage,
|
||||||
onerror: wsError,
|
onerror: wsError,
|
||||||
@@ -353,6 +358,8 @@ function fnRealTime() {
|
|||||||
ws.connect(options);
|
ws.connect(options);
|
||||||
} else {
|
} else {
|
||||||
ws.close();
|
ws.close();
|
||||||
|
tableState.seached = true;
|
||||||
|
fnGetList(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +382,7 @@ function wsMessage(res: Record<string, any>) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// ueEvent MME_UE会话事件
|
// ueEvent MME_UE会话事件
|
||||||
if (data.groupId === '1011') {
|
if (data.groupId === `1011_${queryParams.neId}`) {
|
||||||
const ueEvent = data.data;
|
const ueEvent = data.data;
|
||||||
queue.add(async () => {
|
queue.add(async () => {
|
||||||
modalState.maxId += 1;
|
modalState.maxId += 1;
|
||||||
@@ -403,16 +410,45 @@ onMounted(() => {
|
|||||||
getDict('ue_auth_code'),
|
getDict('ue_auth_code'),
|
||||||
getDict('ue_event_type'),
|
getDict('ue_event_type'),
|
||||||
getDict('ue_event_cm_state'),
|
getDict('ue_event_cm_state'),
|
||||||
])
|
]).then(resArr => {
|
||||||
.then(resArr => {
|
if (resArr[0].status === 'fulfilled') {
|
||||||
if (resArr[0].status === 'fulfilled') {
|
dict.ueAauthCode = resArr[0].value;
|
||||||
dict.ueAauthCode = resArr[0].value;
|
}
|
||||||
}
|
if (resArr[1].status === 'fulfilled') {
|
||||||
if (resArr[1].status === 'fulfilled') {
|
dict.ueEventType = resArr[1].value.map(item => {
|
||||||
dict.ueEventType = resArr[1].value;
|
if (item.value === 'cm-state') {
|
||||||
}
|
item.label = item.label.replace('CM', 'ECM');
|
||||||
if (resArr[2].status === 'fulfilled') {
|
}
|
||||||
dict.ueEventCmState = resArr[2].value;
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (resArr[2].status === 'fulfilled') {
|
||||||
|
dict.ueEventCmState = resArr[2].value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取网元网元列表
|
||||||
|
useNeInfoStore()
|
||||||
|
.fnNelist()
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length > 0) {
|
||||||
|
let arr: Record<string, any>[] = [];
|
||||||
|
res.data.forEach(i => {
|
||||||
|
if (i.neType === 'MME') {
|
||||||
|
arr.push({ value: i.neId, label: i.neName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neOtions.value = arr;
|
||||||
|
if (arr.length > 0) {
|
||||||
|
queryParams.neId = arr[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -438,6 +474,15 @@ 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="MME" name="neId ">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.neId"
|
||||||
|
:options="neOtions"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
<a-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')"
|
||||||
@@ -548,6 +593,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>
|
||||||
|
|||||||
@@ -251,36 +251,27 @@ function fnChangeData(data: any[], itemID: string) {
|
|||||||
let nfCpuUsage = 0;
|
let nfCpuUsage = 0;
|
||||||
if (info.neState.cpu) {
|
if (info.neState.cpu) {
|
||||||
nfCpuUsage = info.neState.cpu.nfCpuUsage;
|
nfCpuUsage = info.neState.cpu.nfCpuUsage;
|
||||||
|
const nfCpu = +(info.neState.cpu.nfCpuUsage / 100);
|
||||||
|
nfCpuUsage = +nfCpu.toFixed(2);
|
||||||
if (nfCpuUsage > 100) {
|
if (nfCpuUsage > 100) {
|
||||||
const nfCpu = +(info.neState.cpu.nfCpuUsage / 100);
|
nfCpuUsage = 100;
|
||||||
if (nfCpu > 100) {
|
|
||||||
nfCpuUsage = 100;
|
|
||||||
} else {
|
|
||||||
nfCpuUsage = +nfCpu.toFixed(2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sysCpuUsage = info.neState.cpu.sysCpuUsage;
|
sysCpuUsage = info.neState.cpu.sysCpuUsage;
|
||||||
|
let sysCpu = +(info.neState.cpu.sysCpuUsage / 100);
|
||||||
|
sysCpuUsage = +sysCpu.toFixed(2);
|
||||||
if (sysCpuUsage > 100) {
|
if (sysCpuUsage > 100) {
|
||||||
const sysCpu = +(info.neState.cpu.sysCpuUsage / 100);
|
sysCpuUsage = 100;
|
||||||
if (sysCpu > 100) {
|
|
||||||
sysCpuUsage = 100;
|
|
||||||
} else {
|
|
||||||
sysCpuUsage = +sysCpu.toFixed(2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let sysMemUsage = 0;
|
let sysMemUsage = 0;
|
||||||
if (info.neState.mem) {
|
if (info.neState.mem) {
|
||||||
let men = info.neState.mem.sysMemUsage;
|
const men = info.neState.mem.sysMemUsage;
|
||||||
if (men > 100) {
|
sysMemUsage = +(men / 100).toFixed(2);
|
||||||
men = +(men / 100).toFixed(2);
|
if (sysMemUsage > 100) {
|
||||||
|
sysMemUsage = 100;
|
||||||
}
|
}
|
||||||
if (men > 100) {
|
|
||||||
men = 100;
|
|
||||||
}
|
|
||||||
sysMemUsage = men;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let sysDiskUsage = 0;
|
let sysDiskUsage = 0;
|
||||||
|
|||||||
@@ -186,7 +186,14 @@ onMounted(() => {
|
|||||||
<div class="card-ue-item">
|
<div class="card-ue-item">
|
||||||
<div>
|
<div>
|
||||||
{{ t('views.dashboard.overview.userActivity.type') }}:
|
{{ t('views.dashboard.overview.userActivity.type') }}:
|
||||||
<span>
|
<span v-if="item.type === 'cm-state'">
|
||||||
|
{{
|
||||||
|
dict.ueEventType
|
||||||
|
.find(s => s.value === item.type)
|
||||||
|
?.label.replace('CM', 'ECM')
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
<DictTag :options="dict.ueEventType" :value="item.type" />
|
<DictTag :options="dict.ueEventType" :value="item.type" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const notNeNodes = [
|
|||||||
/**图状态 */
|
/**图状态 */
|
||||||
export const graphState = reactive<Record<string, any>>({
|
export const graphState = reactive<Record<string, any>>({
|
||||||
/**当前图组名 */
|
/**当前图组名 */
|
||||||
group: '5GC System Architecture5',
|
group: '5GC System Architecture',
|
||||||
/**图数据 */
|
/**图数据 */
|
||||||
data: {
|
data: {
|
||||||
combos: [],
|
combos: [],
|
||||||
|
|||||||
@@ -50,37 +50,37 @@ export default function useWS() {
|
|||||||
// 普通信息
|
// 普通信息
|
||||||
switch (requestId) {
|
switch (requestId) {
|
||||||
// AMF_UE会话事件
|
// AMF_UE会话事件
|
||||||
case '1010':
|
case 'amf_1010_001':
|
||||||
if (Array.isArray(data.rows)) {
|
if (Array.isArray(data.rows)) {
|
||||||
eventListParse('amf_ue', data);
|
eventListParse('amf_ue', data);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// MME_UE会话事件
|
// MME_UE会话事件
|
||||||
case '1011':
|
case 'mme_1011_001':
|
||||||
if (Array.isArray(data.rows)) {
|
if (Array.isArray(data.rows)) {
|
||||||
eventListParse('mme_ue', data);
|
eventListParse('mme_ue', data);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// IMS_CDR会话事件
|
// IMS_CDR会话事件
|
||||||
case '1005':
|
case 'ims_1005_001':
|
||||||
if (Array.isArray(data.rows)) {
|
if (Array.isArray(data.rows)) {
|
||||||
eventListParse('ims_cdr', data);
|
eventListParse('ims_cdr', data);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
//UPF-总流量数
|
//UPF-总流量数
|
||||||
case '1030_0':
|
case 'upf_001_0':
|
||||||
const v0 = upfTFParse(data);
|
const v0 = upfTFParse(data);
|
||||||
upfTotalFlow.value[0].up = v0.up;
|
upfTotalFlow.value[0].up = v0.up;
|
||||||
upfTotalFlow.value[0].down = v0.down;
|
upfTotalFlow.value[0].down = v0.down;
|
||||||
upfTotalFlow.value[0].requestFlag = false;
|
upfTotalFlow.value[0].requestFlag = false;
|
||||||
break;
|
break;
|
||||||
case '1030_7':
|
case 'upf_001_7':
|
||||||
const v7 = upfTFParse(data);
|
const v7 = upfTFParse(data);
|
||||||
upfTotalFlow.value[1].up = v7.up;
|
upfTotalFlow.value[1].up = v7.up;
|
||||||
upfTotalFlow.value[1].down = v7.down;
|
upfTotalFlow.value[1].down = v7.down;
|
||||||
upfTotalFlow.value[1].requestFlag = false;
|
upfTotalFlow.value[1].requestFlag = false;
|
||||||
break;
|
break;
|
||||||
case '1030_30':
|
case 'upf_001_30':
|
||||||
const v30 = upfTFParse(data);
|
const v30 = upfTFParse(data);
|
||||||
upfTotalFlow.value[2].up = v30.up;
|
upfTotalFlow.value[2].up = v30.up;
|
||||||
upfTotalFlow.value[2].down = v30.down;
|
upfTotalFlow.value[2].down = v30.down;
|
||||||
@@ -100,19 +100,19 @@ export default function useWS() {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// AMF_UE会话事件
|
// AMF_UE会话事件
|
||||||
case '1010':
|
case '1010_001':
|
||||||
if (data.data) {
|
if (data.data) {
|
||||||
queue.add(() => eventItemParseAndPush('amf_ue', data.data));
|
queue.add(() => eventItemParseAndPush('amf_ue', data.data));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// MME_UE会话事件
|
// MME_UE会话事件
|
||||||
case '1011':
|
case '1011_001':
|
||||||
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':
|
case '1005_001':
|
||||||
if (data.data) {
|
if (data.data) {
|
||||||
queue.add(() => eventItemParseAndPush('ims_cdr', data.data));
|
queue.add(() => eventItemParseAndPush('ims_cdr', data.data));
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ export default function useWS() {
|
|||||||
upfTotalFlow.value[index].requestFlag = true;
|
upfTotalFlow.value[index].requestFlag = true;
|
||||||
|
|
||||||
ws.send({
|
ws.send({
|
||||||
requestId: `1030_${day}`,
|
requestId: `upf_001_${day}`,
|
||||||
type: 'upf_tf',
|
type: 'upf_tf',
|
||||||
data: {
|
data: {
|
||||||
neType: 'UPF',
|
neType: 'UPF',
|
||||||
@@ -151,7 +151,7 @@ export default function useWS() {
|
|||||||
function userActivitySend() {
|
function userActivitySend() {
|
||||||
// AMF_UE会话事件
|
// AMF_UE会话事件
|
||||||
ws.send({
|
ws.send({
|
||||||
requestId: '1010',
|
requestId: 'amf_1010_001',
|
||||||
type: 'amf_ue',
|
type: 'amf_ue',
|
||||||
data: {
|
data: {
|
||||||
neType: 'AMF',
|
neType: 'AMF',
|
||||||
@@ -164,7 +164,7 @@ export default function useWS() {
|
|||||||
});
|
});
|
||||||
// MME_UE会话事件
|
// MME_UE会话事件
|
||||||
ws.send({
|
ws.send({
|
||||||
requestId: '1011',
|
requestId: 'mme_1011_001',
|
||||||
type: 'mme_ue',
|
type: 'mme_ue',
|
||||||
data: {
|
data: {
|
||||||
neType: 'MME',
|
neType: 'MME',
|
||||||
@@ -177,7 +177,7 @@ export default function useWS() {
|
|||||||
});
|
});
|
||||||
// IMS_CDR会话事件
|
// IMS_CDR会话事件
|
||||||
ws.send({
|
ws.send({
|
||||||
requestId: '1005',
|
requestId: 'ims_1005_001',
|
||||||
type: 'ims_cdr',
|
type: 'ims_cdr',
|
||||||
data: {
|
data: {
|
||||||
neType: 'IMS',
|
neType: 'IMS',
|
||||||
@@ -198,11 +198,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)
|
* MME_UE会话事件(GroupID:1011_neId)
|
||||||
* IMS_CDR会话事件(GroupID:1005)
|
* IMS_CDR会话事件(GroupID:1005_neId)
|
||||||
*/
|
*/
|
||||||
subGroupID: '12_001,1010,1011,1005',
|
subGroupID: '12_001,1010_001,1011_001,1005_001',
|
||||||
},
|
},
|
||||||
onmessage: wsMessage,
|
onmessage: wsMessage,
|
||||||
onerror: wsError,
|
onerror: wsError,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Modal, message } from 'ant-design-vue/lib';
|
|||||||
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
||||||
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
|
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
|
||||||
import { ColumnsType } from 'ant-design-vue/lib/table';
|
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import {
|
import {
|
||||||
RESULT_CODE_ERROR,
|
RESULT_CODE_ERROR,
|
||||||
@@ -23,6 +24,9 @@ const { t } = useI18n();
|
|||||||
const ws = new WS();
|
const ws = new WS();
|
||||||
const queue = new PQueue({ concurrency: 1, autoStart: true });
|
const queue = new PQueue({ concurrency: 1, autoStart: true });
|
||||||
|
|
||||||
|
/**网元可选 */
|
||||||
|
let neOtions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
/**开始结束时间 */
|
/**开始结束时间 */
|
||||||
let queryRangePicker = ref<[string, string]>(['', '']);
|
let queryRangePicker = ref<[string, string]>(['', '']);
|
||||||
|
|
||||||
@@ -391,6 +395,7 @@ 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',
|
||||||
@@ -399,7 +404,7 @@ function fnRealTime() {
|
|||||||
*
|
*
|
||||||
* CDR会话事件-SMF (GroupID:1006)
|
* CDR会话事件-SMF (GroupID:1006)
|
||||||
*/
|
*/
|
||||||
subGroupID: '1006',
|
subGroupID: `1006_${queryParams.neId}`,
|
||||||
},
|
},
|
||||||
onmessage: wsMessage,
|
onmessage: wsMessage,
|
||||||
onerror: wsError,
|
onerror: wsError,
|
||||||
@@ -407,6 +412,8 @@ function fnRealTime() {
|
|||||||
ws.connect(options);
|
ws.connect(options);
|
||||||
} else {
|
} else {
|
||||||
ws.close();
|
ws.close();
|
||||||
|
tableState.seached = true;
|
||||||
|
fnGetList(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +436,7 @@ function wsMessage(res: Record<string, any>) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// cdrEvent CDR会话事件
|
// cdrEvent CDR会话事件
|
||||||
if (data.groupId === '1006') {
|
if (data.groupId === `1006_${queryParams.neId}`) {
|
||||||
const cdrEvent = data.data;
|
const cdrEvent = data.data;
|
||||||
queue.add(async () => {
|
queue.add(async () => {
|
||||||
modalState.maxId += 1;
|
modalState.maxId += 1;
|
||||||
@@ -451,8 +458,34 @@ function wsMessage(res: Record<string, any>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 获取列表数据
|
// 获取网元网元列表
|
||||||
fnGetList();
|
useNeInfoStore()
|
||||||
|
.fnNelist()
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length > 0) {
|
||||||
|
let arr: Record<string, any>[] = [];
|
||||||
|
res.data.forEach(i => {
|
||||||
|
if (i.neType === 'SMF') {
|
||||||
|
arr.push({ value: i.neId, label: i.neName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neOtions.value = arr;
|
||||||
|
if (arr.length > 0) {
|
||||||
|
queryParams.neId = arr[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// 获取列表数据
|
||||||
|
fnGetList();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
@@ -472,10 +505,19 @@ 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="SMF" name="neId ">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.neId"
|
||||||
|
:options="neOtions"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.dashboard.cdr.smfSubscriptionIDData')"
|
:label="t('views.dashboard.cdr.smfSubscriptionIDData')"
|
||||||
name="calledParty "
|
name="subscriberID"
|
||||||
>
|
>
|
||||||
<a-input
|
<a-input
|
||||||
v-model:value="queryParams.subscriberID"
|
v-model:value="queryParams.subscriberID"
|
||||||
@@ -572,6 +614,7 @@ onBeforeUnmount(() => {
|
|||||||
:checked-children="t('common.switch.show')"
|
:checked-children="t('common.switch.show')"
|
||||||
:un-checked-children="t('common.switch.hide')"
|
:un-checked-children="t('common.switch.hide')"
|
||||||
size="small"
|
size="small"
|
||||||
|
:disabled="realTimeData"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
|
|||||||
760
src/views/dashboard/smscCDR/index.vue
Normal file
760
src/views/dashboard/smscCDR/index.vue
Normal file
@@ -0,0 +1,760 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, toRaw, ref, onBeforeUnmount } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { message, Modal } from 'ant-design-vue/lib';
|
||||||
|
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
||||||
|
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||||||
|
import useDictStore from '@/store/modules/dict';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import {
|
||||||
|
RESULT_CODE_ERROR,
|
||||||
|
RESULT_CODE_SUCCESS,
|
||||||
|
} from '@/constants/result-constants';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import {
|
||||||
|
delSMSCDataCDR,
|
||||||
|
exportSMSCDataCDR,
|
||||||
|
listSMSCDataCDR,
|
||||||
|
} from '@/api/neData/smsc';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import saveAs from 'file-saver';
|
||||||
|
import PQueue from 'p-queue';
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const ws = new WS();
|
||||||
|
const queue = new PQueue({ concurrency: 1, autoStart: true });
|
||||||
|
|
||||||
|
/**字典数据 */
|
||||||
|
let dict: {
|
||||||
|
/**CDR 响应原因代码类别类型 */
|
||||||
|
cdrCauseCode: DictType[];
|
||||||
|
} = reactive({
|
||||||
|
cdrCauseCode: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**网元可选 */
|
||||||
|
let neOtions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
|
/**开始结束时间 */
|
||||||
|
let queryRangePicker = ref<[string, string]>(['', '']);
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元类型 */
|
||||||
|
neType: 'SMSC',
|
||||||
|
neId: '001',
|
||||||
|
recordType: '',
|
||||||
|
callerParty: '',
|
||||||
|
calledParty: '',
|
||||||
|
sortField: 'timestamp',
|
||||||
|
sortOrder: 'desc',
|
||||||
|
/**开始时间 */
|
||||||
|
startTime: '',
|
||||||
|
/**结束时间 */
|
||||||
|
endTime: '',
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
recordTypes.value = [];
|
||||||
|
queryParams = Object.assign(queryParams, {
|
||||||
|
recordType: '',
|
||||||
|
callerParty: '',
|
||||||
|
calledParty: '',
|
||||||
|
startTime: '',
|
||||||
|
endTime: '',
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
queryRangePicker.value = ['', ''];
|
||||||
|
tablePagination.current = 1;
|
||||||
|
tablePagination.pageSize = 20;
|
||||||
|
fnGetList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**记录类型 */
|
||||||
|
const recordTypes = ref<string[]>([]);
|
||||||
|
|
||||||
|
/**查询记录类型变更 */
|
||||||
|
function fnQueryRecordTypeChange(value: any) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
queryParams.recordType = value.join(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**搜索栏 */
|
||||||
|
seached: boolean;
|
||||||
|
/**记录数据 */
|
||||||
|
data: object[];
|
||||||
|
/**勾选记录 */
|
||||||
|
selectedRowKeys: (string | number)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
size: 'middle',
|
||||||
|
seached: true,
|
||||||
|
data: [],
|
||||||
|
selectedRowKeys: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
let tableColumns: ColumnsType = [
|
||||||
|
{
|
||||||
|
title: t('common.rowId'),
|
||||||
|
dataIndex: 'id',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.recordType'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.recordType;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.type'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.serviceType;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.caller'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
key: 'callerParty',
|
||||||
|
align: 'left',
|
||||||
|
width: 120,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.callerParty;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.called'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
key: 'calledParty',
|
||||||
|
align: 'left',
|
||||||
|
width: 120,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return cdrJSON.calledParty;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.result'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
key: 'cause',
|
||||||
|
align: 'left',
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.dashboard.cdr.time'),
|
||||||
|
dataIndex: 'cdrJSON',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
const cdrJSON = opt.value;
|
||||||
|
return parseDateToStr(+cdrJSON.updateTime * 1000);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('common.operate'),
|
||||||
|
key: 'id',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
let tablePagination = reactive({
|
||||||
|
/**当前页数 */
|
||||||
|
current: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
/**默认的每页条数 */
|
||||||
|
defaultPageSize: 20,
|
||||||
|
/**指定每页可以显示多少条 */
|
||||||
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
|
/**只有一页时是否隐藏分页器 */
|
||||||
|
hideOnSinglePage: false,
|
||||||
|
/**是否可以快速跳转至某页 */
|
||||||
|
showQuickJumper: true,
|
||||||
|
/**是否可以改变 pageSize */
|
||||||
|
showSizeChanger: true,
|
||||||
|
/**数据总数 */
|
||||||
|
total: 0,
|
||||||
|
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
|
||||||
|
onChange: (page: number, pageSize: number) => {
|
||||||
|
tablePagination.current = page;
|
||||||
|
tablePagination.pageSize = pageSize;
|
||||||
|
queryParams.pageNum = page;
|
||||||
|
queryParams.pageSize = pageSize;
|
||||||
|
fnGetList();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格紧凑型变更操作 */
|
||||||
|
function fnTableSize({ key }: MenuInfo) {
|
||||||
|
tableState.size = key as SizeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格多选 */
|
||||||
|
function fnTableSelectedRowKeys(keys: (string | number)[]) {
|
||||||
|
tableState.selectedRowKeys = keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
/**最大ID值 */
|
||||||
|
maxId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
confirmLoading: false,
|
||||||
|
maxId: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录删除
|
||||||
|
* @param id 编号
|
||||||
|
*/
|
||||||
|
function fnRecordDelete(id: string) {
|
||||||
|
if (!id || modalState.confirmLoading) return;
|
||||||
|
let msg = id;
|
||||||
|
if (id === '0') {
|
||||||
|
msg = `${id}... ${tableState.selectedRowKeys.length}`;
|
||||||
|
id = tableState.selectedRowKeys.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.dashboard.cdr.delTip', { msg }),
|
||||||
|
onOk() {
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
delSMSCDataCDR(id)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnGetList(1);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
if (!queryRangePicker.value) {
|
||||||
|
queryRangePicker.value = ['', ''];
|
||||||
|
}
|
||||||
|
queryParams.startTime = queryRangePicker.value[0];
|
||||||
|
queryParams.endTime = queryRangePicker.value[1];
|
||||||
|
listSMSCDataCDR(toRaw(queryParams)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
|
// 取消勾选
|
||||||
|
if (tableState.selectedRowKeys.length > 0) {
|
||||||
|
tableState.selectedRowKeys = [];
|
||||||
|
}
|
||||||
|
tablePagination.total = res.total;
|
||||||
|
// 遍历处理cdr字符串数据
|
||||||
|
tableState.data = res.rows.map(item => {
|
||||||
|
let cdrJSON = item.cdrJSON;
|
||||||
|
if (!cdrJSON) {
|
||||||
|
Reflect.set(item, 'cdrJSON', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
cdrJSON = JSON.parse(cdrJSON);
|
||||||
|
Reflect.set(item, 'cdrJSON', cdrJSON);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
Reflect.set(item, 'cdrJSON', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 取最大值ID用作实时累加
|
||||||
|
if (res.total > 0) {
|
||||||
|
modalState.maxId = Number(res.rows[0].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**列表导出 */
|
||||||
|
function fnExportList() {
|
||||||
|
if (modalState.confirmLoading) return;
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.dashboard.cdr.exportTip'),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
const querys = toRaw(queryParams);
|
||||||
|
querys.pageSize = 10000;
|
||||||
|
exportSMSCDataCDR(querys)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
saveAs(res.data, `smsc_cdr_event_export_${Date.now()}.xlsx`);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**实时数据开关 */
|
||||||
|
const realTimeData = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实时数据
|
||||||
|
*/
|
||||||
|
function fnRealTime() {
|
||||||
|
realTimeData.value = !realTimeData.value;
|
||||||
|
if (realTimeData.value) {
|
||||||
|
tableState.seached = false;
|
||||||
|
// 建立链接
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: '/ws',
|
||||||
|
params: {
|
||||||
|
/**订阅通道组
|
||||||
|
*
|
||||||
|
* SMSC_CDR会话事件(GroupID:1007_neId)
|
||||||
|
*/
|
||||||
|
subGroupID: `1007_${queryParams.neId}`,
|
||||||
|
},
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: wsError,
|
||||||
|
};
|
||||||
|
ws.connect(options);
|
||||||
|
} else {
|
||||||
|
ws.close();
|
||||||
|
tableState.seached = true;
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsError(ev: any) {
|
||||||
|
// 接收数据后回调
|
||||||
|
console.error(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsMessage(res: Record<string, any>) {
|
||||||
|
const { code, requestId, data } = res;
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅组信息
|
||||||
|
if (!data?.groupId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// cdrEvent CDR会话事件
|
||||||
|
if (data.groupId === `1007_${queryParams.neId}`) {
|
||||||
|
const cdrEvent = data.data;
|
||||||
|
queue.add(async () => {
|
||||||
|
modalState.maxId += 1;
|
||||||
|
tableState.data.unshift({
|
||||||
|
id: modalState.maxId,
|
||||||
|
neType: cdrEvent.neType,
|
||||||
|
neName: cdrEvent.neName,
|
||||||
|
rmUID: cdrEvent.rmUID,
|
||||||
|
timestamp: cdrEvent.timestamp,
|
||||||
|
cdrJSON: cdrEvent.CDR,
|
||||||
|
});
|
||||||
|
tablePagination.total += 1;
|
||||||
|
if (tableState.data.length > 100) {
|
||||||
|
tableState.data.pop();
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 800));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始字典数据
|
||||||
|
Promise.allSettled([getDict('cdr_cause_code')]).then(resArr => {
|
||||||
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
dict.cdrCauseCode = resArr[0].value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 获取网元网元列表
|
||||||
|
useNeInfoStore()
|
||||||
|
.fnNelist()
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length > 0) {
|
||||||
|
let arr: Record<string, any>[] = [];
|
||||||
|
res.data.forEach(i => {
|
||||||
|
if (i.neType === 'SMSC') {
|
||||||
|
arr.push({ value: i.neId, label: i.neName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neOtions.value = arr;
|
||||||
|
if (arr.length > 0) {
|
||||||
|
queryParams.neId = arr[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// 获取列表数据
|
||||||
|
fnGetList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (ws.state() !== -1) {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card
|
||||||
|
v-show="tableState.seached"
|
||||||
|
:bordered="false"
|
||||||
|
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
||||||
|
>
|
||||||
|
<!-- 表格搜索栏 -->
|
||||||
|
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item label="SMSC" name="neId ">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.neId"
|
||||||
|
:options="neOtions"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.called')"
|
||||||
|
name="calledParty"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.calledParty"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.caller')"
|
||||||
|
name="callerParty "
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.callerParty"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="4" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="fnQueryReset">
|
||||||
|
<template #icon><ClearOutlined /></template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.recordType')"
|
||||||
|
name="recordType"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="recordTypes"
|
||||||
|
mode="multiple"
|
||||||
|
:options="['MOSM', 'MTSM'].map(v => ({ value: v }))"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnQueryRecordTypeChange"
|
||||||
|
></a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.dashboard.cdr.time')"
|
||||||
|
name="queryRangePicker"
|
||||||
|
>
|
||||||
|
<a-range-picker
|
||||||
|
v-model:value="queryRangePicker"
|
||||||
|
allow-clear
|
||||||
|
bordered
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
value-format="x"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-range-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-popconfirm
|
||||||
|
placement="bottomLeft"
|
||||||
|
:title="
|
||||||
|
!realTimeData
|
||||||
|
? t('views.dashboard.cdr.realTimeDataStart')
|
||||||
|
: t('views.dashboard.cdr.realTimeDataStop')
|
||||||
|
"
|
||||||
|
ok-text="Yes"
|
||||||
|
cancel-text="No"
|
||||||
|
@confirm="fnRealTime()"
|
||||||
|
>
|
||||||
|
<a-button type="primary" :danger="realTimeData">
|
||||||
|
<template #icon><FundOutlined /> </template>
|
||||||
|
{{
|
||||||
|
!realTimeData
|
||||||
|
? t('views.dashboard.cdr.realTimeDataStart')
|
||||||
|
: t('views.dashboard.cdr.realTimeDataStop')
|
||||||
|
}}
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
danger
|
||||||
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
|
:loading="modalState.confirmLoading"
|
||||||
|
@click.prevent="fnRecordDelete('0')"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
{{ t('common.deleteText') }}
|
||||||
|
</a-button>
|
||||||
|
|
||||||
|
<a-button type="dashed" @click.prevent="fnExportList()">
|
||||||
|
<template #icon><ExportOutlined /></template>
|
||||||
|
{{ t('common.export') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.searchBarText') }}</template>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="tableState.seached"
|
||||||
|
:checked-children="t('common.switch.show')"
|
||||||
|
:un-checked-children="t('common.switch.hide')"
|
||||||
|
size="small"
|
||||||
|
:disabled="realTimeData"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.sizeText') }}</template>
|
||||||
|
<a-dropdown trigger="click" placement="bottomRight">
|
||||||
|
<a-button type="text">
|
||||||
|
<template #icon><ColumnHeightOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu
|
||||||
|
:selected-keys="[tableState.size as string]"
|
||||||
|
@click="fnTableSize"
|
||||||
|
>
|
||||||
|
<a-menu-item key="default">
|
||||||
|
{{ t('common.size.default') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="middle">
|
||||||
|
{{ t('common.size.middle') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="small">
|
||||||
|
{{ t('common.size.small') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="id"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: tableColumns.length * 150, y: 'calc(100vh - 480px)' }"
|
||||||
|
:row-selection="{
|
||||||
|
type: 'checkbox',
|
||||||
|
columnWidth: '48px',
|
||||||
|
selectedRowKeys: tableState.selectedRowKeys,
|
||||||
|
onChange: fnTableSelectedRowKeys,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'cause'">
|
||||||
|
<span v-if="record.cdrJSON.result === 0">
|
||||||
|
{{ t('views.dashboard.cdr.resultFail') }},
|
||||||
|
<DictTag
|
||||||
|
:options="dict.cdrCauseCode"
|
||||||
|
:value="record.cdrJSON.cause"
|
||||||
|
value-default="0"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ t('views.dashboard.cdr.resultOk') }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'id'">
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordDelete(record.id)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<DeleteOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template #expandedRowRender="{ record }">
|
||||||
|
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.dashboard.cdr.cdrInfo') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.neName') }}: </span>
|
||||||
|
<span>{{ record.neName }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.ne.common.rmUid') }}: </span>
|
||||||
|
<span>{{ record.rmUID }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.time') }}: </span>
|
||||||
|
<span>{{ parseDateToStr(+record.timestamp * 1000) }}</span>
|
||||||
|
</div>
|
||||||
|
<a-divider orientation="left">
|
||||||
|
{{ t('views.dashboard.cdr.rowInfo') }}
|
||||||
|
</a-divider>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.type') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.serviceType }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.caller') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.callerParty }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.called') }}: </span>
|
||||||
|
<span>{{ record.cdrJSON.calledParty }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>{{ t('views.dashboard.cdr.result') }}: </span>
|
||||||
|
<span v-if="record.cdrJSON.result === 0">
|
||||||
|
{{ t('views.dashboard.cdr.resultFail') }},
|
||||||
|
<DictTag
|
||||||
|
:options="dict.cdrCauseCode"
|
||||||
|
:value="record.cdrJSON.cause"
|
||||||
|
value-default="0"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ t('views.dashboard.cdr.resultOk') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -142,30 +142,6 @@ let alarmTableState: TabeStateType = reactive({
|
|||||||
|
|
||||||
/**表格字段列 */
|
/**表格字段列 */
|
||||||
let tableColumns: ColumnsType = [
|
let tableColumns: ColumnsType = [
|
||||||
{
|
|
||||||
title: t('views.faultManage.activeAlarm.alarmId'),
|
|
||||||
dataIndex: 'alarmId',
|
|
||||||
align: 'center',
|
|
||||||
width: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.faultManage.activeAlarm.neId'),
|
|
||||||
dataIndex: 'neId',
|
|
||||||
align: 'center',
|
|
||||||
width: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.faultManage.activeAlarm.neName'),
|
|
||||||
dataIndex: 'neName',
|
|
||||||
align: 'center',
|
|
||||||
width: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.faultManage.activeAlarm.neType'),
|
|
||||||
dataIndex: 'neType',
|
|
||||||
align: 'center',
|
|
||||||
width: 5,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.origLevel'),
|
title: t('views.faultManage.activeAlarm.origLevel'),
|
||||||
align: 'center',
|
align: 'center',
|
||||||
@@ -173,18 +149,18 @@ let tableColumns: ColumnsType = [
|
|||||||
dataIndex: 'origSeverity',
|
dataIndex: 'origSeverity',
|
||||||
width: 5,
|
width: 5,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: t('views.faultManage.activeAlarm.alarmCode'),
|
|
||||||
dataIndex: 'alarmCode',
|
|
||||||
align: 'center',
|
|
||||||
width: 5,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.alarmTitle'),
|
title: t('views.faultManage.activeAlarm.alarmTitle'),
|
||||||
dataIndex: 'alarmTitle',
|
dataIndex: 'alarmTitle',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 5,
|
width: 5,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('views.faultManage.activeAlarm.neType'),
|
||||||
|
dataIndex: 'neType',
|
||||||
|
align: 'center',
|
||||||
|
width: 5,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.eventTime'),
|
title: t('views.faultManage.activeAlarm.eventTime'),
|
||||||
dataIndex: 'eventTime',
|
dataIndex: 'eventTime',
|
||||||
@@ -192,6 +168,12 @@ let tableColumns: ColumnsType = [
|
|||||||
sorter: (a: any, b: any) => 1,
|
sorter: (a: any, b: any) => 1,
|
||||||
width: 5,
|
width: 5,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('views.faultManage.activeAlarm.alarmCode'),
|
||||||
|
dataIndex: 'alarmCode',
|
||||||
|
align: 'center',
|
||||||
|
width: 5,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.alarmType'),
|
title: t('views.faultManage.activeAlarm.alarmType'),
|
||||||
dataIndex: 'alarmType',
|
dataIndex: 'alarmType',
|
||||||
@@ -199,12 +181,31 @@ let tableColumns: ColumnsType = [
|
|||||||
align: 'left',
|
align: 'left',
|
||||||
width: 5,
|
width: 5,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('views.faultManage.activeAlarm.neName'),
|
||||||
|
dataIndex: 'neName',
|
||||||
|
align: 'center',
|
||||||
|
width: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.faultManage.activeAlarm.neId'),
|
||||||
|
dataIndex: 'neId',
|
||||||
|
align: 'center',
|
||||||
|
width: 5,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.pvFlag'),
|
title: t('views.faultManage.activeAlarm.pvFlag'),
|
||||||
dataIndex: 'pvFlag',
|
dataIndex: 'pvFlag',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 5,
|
width: 5,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('views.faultManage.activeAlarm.alarmId'),
|
||||||
|
dataIndex: 'alarmId',
|
||||||
|
align: 'center',
|
||||||
|
width: 5,
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.ackState'),
|
title: t('views.faultManage.activeAlarm.ackState'),
|
||||||
dataIndex: 'ackState',
|
dataIndex: 'ackState',
|
||||||
@@ -610,6 +611,17 @@ function fnShowSet() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// key替换中文title
|
||||||
|
function mapKeysWithReduce(data: any, titleMapping: any) {
|
||||||
|
return data.map((item:any) => {
|
||||||
|
return Object.keys(item).reduce((newItem:any, key:any) => {
|
||||||
|
const title = titleMapping[key] || key;
|
||||||
|
newItem[title] = item[key];
|
||||||
|
return newItem;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导出全部
|
* 导出全部
|
||||||
*/
|
*/
|
||||||
@@ -621,39 +633,56 @@ function fnExportAll() {
|
|||||||
const key = 'exportAlarm';
|
const key = 'exportAlarm';
|
||||||
message.loading({ content: t('common.loading'), key });
|
message.loading({ content: t('common.loading'), key });
|
||||||
let sortArr: any = [];
|
let sortArr: any = [];
|
||||||
|
let writeSheetOpt: any = [];
|
||||||
|
let mappArr: any = {};
|
||||||
tableColumnsDnd.value.forEach((item: any) => {
|
tableColumnsDnd.value.forEach((item: any) => {
|
||||||
if (item.dataIndex) sortArr.push(item.dataIndex);
|
if (item.dataIndex) {
|
||||||
|
sortArr.push(item.dataIndex);
|
||||||
|
writeSheetOpt.push(item.title);
|
||||||
|
mappArr[item.dataIndex] = item.title;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
//提供给writeSheet排序参数
|
||||||
// 排序字段
|
|
||||||
const sortData = {
|
const sortData = {
|
||||||
header: sortArr,
|
header: writeSheetOpt,
|
||||||
};
|
};
|
||||||
|
|
||||||
exportAll(queryParams).then(res => {
|
exportAll(queryParams).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
res.data = res.data.map((objA: any) => {
|
res.data = res.data.map((objA: any) => {
|
||||||
let filteredObj: any = {};
|
let filteredObj: any = {};
|
||||||
|
//sort Data
|
||||||
sortArr.forEach((key: any) => {
|
sortArr.forEach((key: any) => {
|
||||||
if (objA.hasOwnProperty(key)) {
|
if (objA.hasOwnProperty(key)) {
|
||||||
filteredObj[key] = objA[key];
|
filteredObj[key] = objA[key];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
dict.activeAckState.map((item: any) => {
|
const mapProps = (dict: any, key: string) => {
|
||||||
if (item.value === `${filteredObj.ackState}`)
|
const item = dict.find(
|
||||||
filteredObj.ackState = item.label;
|
(item: any) => item.value === `${filteredObj[key]}`
|
||||||
});
|
);
|
||||||
|
if (item) {
|
||||||
|
filteredObj[key] = item.label;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mapProps(dict.activeAckState, 'ackState');
|
||||||
|
mapProps(dict.activeClearType, 'clearType');
|
||||||
|
mapProps(dict.activeAlarmSeverity, 'origSeverity');
|
||||||
|
mapProps(dict.activeAlarmType, 'alarmType');
|
||||||
|
|
||||||
return filteredObj;
|
return filteredObj;
|
||||||
});
|
});
|
||||||
message.success({
|
|
||||||
content: t('common.msgSuccess', { msg: t('common.export') }),
|
res.data = mapKeysWithReduce(res.data, mappArr);
|
||||||
key,
|
writeSheet(res.data, 'alarm', sortData).then(fileBlob => {
|
||||||
duration: 3,
|
saveAs(fileBlob, `alarm_${Date.now()}.xlsx`);
|
||||||
|
message.success({
|
||||||
|
content: t('common.msgSuccess', { msg: t('common.export') }),
|
||||||
|
key,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
writeSheet(res.data, 'alarm', sortData).then(fileBlob =>
|
|
||||||
saveAs(fileBlob, `alarm_${Date.now()}.xlsx`)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
message.error({
|
message.error({
|
||||||
content: `${res.msg}`,
|
content: `${res.msg}`,
|
||||||
|
|||||||
@@ -308,6 +308,17 @@ const onSelectChange = (
|
|||||||
state.selectedRow = record;
|
state.selectedRow = record;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// key替换中文title
|
||||||
|
function mapKeysWithReduce(data: any, titleMapping: any) {
|
||||||
|
return data.map((item:any) => {
|
||||||
|
return Object.keys(item).reduce((newItem:any, key:any) => {
|
||||||
|
const title = titleMapping[key] || key;
|
||||||
|
newItem[title] = item[key];
|
||||||
|
return newItem;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导出全部
|
* 导出全部
|
||||||
*/
|
*/
|
||||||
@@ -319,13 +330,18 @@ function fnExportAll() {
|
|||||||
const key = 'exportAlarm';
|
const key = 'exportAlarm';
|
||||||
message.loading({ content: t('common.loading'), key });
|
message.loading({ content: t('common.loading'), key });
|
||||||
let sortArr: any = [];
|
let sortArr: any = [];
|
||||||
|
let writeSheetOpt: any = [];
|
||||||
|
let mappArr: any = {};
|
||||||
tableColumnsDnd.value.forEach((item: any) => {
|
tableColumnsDnd.value.forEach((item: any) => {
|
||||||
if (item.dataIndex) sortArr.push(item.dataIndex);
|
if (item.dataIndex) {
|
||||||
|
sortArr.push(item.dataIndex);
|
||||||
|
writeSheetOpt.push(item.title);
|
||||||
|
mappArr[item.dataIndex] = item.title;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 排序字段
|
// 排序字段
|
||||||
const sortData = {
|
const sortData = {
|
||||||
header: sortArr,
|
header: writeSheetOpt,
|
||||||
};
|
};
|
||||||
|
|
||||||
exportAll(queryParams).then(res => {
|
exportAll(queryParams).then(res => {
|
||||||
@@ -337,21 +353,20 @@ function fnExportAll() {
|
|||||||
filteredObj[key] = objA[key];
|
filteredObj[key] = objA[key];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
dict.activeAckState.map((item: any) => {
|
|
||||||
if (item.value === `${filteredObj.ackState}`)
|
|
||||||
filteredObj.ackState = item.label;
|
|
||||||
});
|
|
||||||
|
|
||||||
return filteredObj;
|
return filteredObj;
|
||||||
});
|
});
|
||||||
message.success({
|
res.data = mapKeysWithReduce(res.data, mappArr);
|
||||||
content: t('common.msgSuccess', { msg: t('common.export') }),
|
|
||||||
key,
|
writeSheet(res.data, 'alarm', sortData).then(fileBlob => {
|
||||||
duration: 3,
|
saveAs(fileBlob, `evnet_${Date.now()}.xlsx`);
|
||||||
|
|
||||||
|
message.success({
|
||||||
|
content: t('common.msgSuccess', { msg: t('common.export') }),
|
||||||
|
key,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
writeSheet(res.data, 'alarm', sortData).then(fileBlob =>
|
|
||||||
saveAs(fileBlob, `evnet_${Date.now()}.xlsx`)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
message.error({
|
message.error({
|
||||||
content: `${res.msg}`,
|
content: `${res.msg}`,
|
||||||
@@ -625,11 +640,6 @@ onMounted(() => {
|
|||||||
:loading="tableState.loading"
|
:loading="tableState.loading"
|
||||||
:data-source="tableState.data"
|
:data-source="tableState.data"
|
||||||
:size="tableState.size"
|
:size="tableState.size"
|
||||||
:row-selection="{
|
|
||||||
columnWidth: 2,
|
|
||||||
selectedRowKeys: state.selectedRowKeys,
|
|
||||||
onChange: onSelectChange,
|
|
||||||
}"
|
|
||||||
:pagination="tablePagination"
|
:pagination="tablePagination"
|
||||||
:scroll="{ x: 2500, y: 400 }"
|
:scroll="{ x: 2500, y: 400 }"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -426,6 +426,17 @@ function fnCancelConfirm() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// key替换中文title
|
||||||
|
function mapKeysWithReduce(data: any, titleMapping: any) {
|
||||||
|
return data.map((item:any) => {
|
||||||
|
return Object.keys(item).reduce((newItem:any, key:any) => {
|
||||||
|
const title = titleMapping[key] || key;
|
||||||
|
newItem[title] = item[key];
|
||||||
|
return newItem;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导出全部
|
* 导出全部
|
||||||
*/
|
*/
|
||||||
@@ -437,12 +448,18 @@ function fnExportAll() {
|
|||||||
const key = 'exportAlarmHis';
|
const key = 'exportAlarmHis';
|
||||||
message.loading({ content: t('common.loading'), key });
|
message.loading({ content: t('common.loading'), key });
|
||||||
let sortArr: any = [];
|
let sortArr: any = [];
|
||||||
|
let writeSheetOpt: any = [];
|
||||||
|
let mappArr: any = {};
|
||||||
tableColumnsDnd.value.forEach((item: any) => {
|
tableColumnsDnd.value.forEach((item: any) => {
|
||||||
if (item.dataIndex) sortArr.push(item.dataIndex);
|
if (item.dataIndex) {
|
||||||
|
sortArr.push(item.dataIndex);
|
||||||
|
writeSheetOpt.push(item.title);
|
||||||
|
mappArr[item.dataIndex] = item.title;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// 排序字段
|
//提供给writeSheet排序参数
|
||||||
const sortData = {
|
const sortData = {
|
||||||
header: sortArr,
|
header: writeSheetOpt,
|
||||||
};
|
};
|
||||||
exportAll(queryParams).then(res => {
|
exportAll(queryParams).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
@@ -470,14 +487,17 @@ function fnExportAll() {
|
|||||||
|
|
||||||
return filteredObj;
|
return filteredObj;
|
||||||
});
|
});
|
||||||
message.success({
|
|
||||||
content: t('common.msgSuccess', { msg: t('common.export') }),
|
res.data = mapKeysWithReduce(res.data, mappArr);
|
||||||
key,
|
|
||||||
duration: 3,
|
writeSheet(res.data, 'alarm', sortData).then(fileBlob => {
|
||||||
|
saveAs(fileBlob, `history-alarm_${Date.now()}.xlsx`);
|
||||||
|
message.success({
|
||||||
|
content: t('common.msgSuccess', { msg: t('common.export') }),
|
||||||
|
key,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
writeSheet(res.data, 'alarm', sortData).then(fileBlob =>
|
|
||||||
saveAs(fileBlob, `history-alarm_${Date.now()}.xlsx`)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
message.error({
|
message.error({
|
||||||
content: `${res.msg}`,
|
content: `${res.msg}`,
|
||||||
|
|||||||
362
src/views/logManage/exportFile/index.vue
Normal file
362
src/views/logManage/exportFile/index.vue
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, onMounted, toRaw } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||||||
|
import { Modal, message } from 'ant-design-vue/lib';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import {
|
||||||
|
getBakFile,
|
||||||
|
getBakFileList,
|
||||||
|
downFile,
|
||||||
|
delFile,
|
||||||
|
} from '@/api/logManage/exportFile';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import saveAs from 'file-saver';
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
/**网元参数 */
|
||||||
|
let logSelect = ref<string[]>([]);
|
||||||
|
|
||||||
|
/**文件列表 */
|
||||||
|
let fileList = ref<any>([]);
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**读取路径 */
|
||||||
|
path: '',
|
||||||
|
/**表名 */
|
||||||
|
tableName: '',
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**记录数据 */
|
||||||
|
data: object[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
size: 'small',
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
let tableColumns: ColumnsType = [
|
||||||
|
{
|
||||||
|
title: t('views.logManage.neFile.fileMode'),
|
||||||
|
dataIndex: 'fileMode',
|
||||||
|
align: 'center',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.logManage.neFile.owner'),
|
||||||
|
dataIndex: 'owner',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.logManage.neFile.group'),
|
||||||
|
dataIndex: 'group',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.logManage.neFile.size'),
|
||||||
|
dataIndex: 'size',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.logManage.neFile.modifiedTime'),
|
||||||
|
dataIndex: 'modifiedTime',
|
||||||
|
align: 'left',
|
||||||
|
customRender(opt) {
|
||||||
|
if (!opt.value) return '';
|
||||||
|
return parseDateToStr(opt.value * 1000);
|
||||||
|
},
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.logManage.neFile.fileName'),
|
||||||
|
dataIndex: 'fileName',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('common.operate'),
|
||||||
|
key: 'fileName',
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
let tablePagination = reactive({
|
||||||
|
/**当前页数 */
|
||||||
|
current: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
/**默认的每页条数 */
|
||||||
|
defaultPageSize: 20,
|
||||||
|
/**指定每页可以显示多少条 */
|
||||||
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
|
/**只有一页时是否隐藏分页器 */
|
||||||
|
hideOnSinglePage: false,
|
||||||
|
/**是否可以快速跳转至某页 */
|
||||||
|
showQuickJumper: true,
|
||||||
|
/**是否可以改变 pageSize */
|
||||||
|
showSizeChanger: true,
|
||||||
|
/**数据总数 */
|
||||||
|
total: 0,
|
||||||
|
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
|
||||||
|
onChange: (page: number, pageSize: number) => {
|
||||||
|
tablePagination.current = page;
|
||||||
|
tablePagination.pageSize = pageSize;
|
||||||
|
queryParams.pageNum = page;
|
||||||
|
queryParams.pageSize = pageSize;
|
||||||
|
fnGetList();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**下载触发等待 */
|
||||||
|
let downLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**删除触发等待 */
|
||||||
|
let delLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**信息文件下载 */
|
||||||
|
function fnDownloadFile(row: Record<string, any>) {
|
||||||
|
if (downLoading.value) return;
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.logManage.exportFile.downTip', {
|
||||||
|
fileName: row.fileName,
|
||||||
|
}),
|
||||||
|
onOk() {
|
||||||
|
downLoading.value = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
downFile({
|
||||||
|
path: queryParams.path,
|
||||||
|
fileName: row.fileName,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.msgSuccess', {
|
||||||
|
msg: t('common.downloadText'),
|
||||||
|
}),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
saveAs(res.data, `${row.fileName}`);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: t('views.logManage.exportFile.downTipErr'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
downLoading.value = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fnRecordDelete(row: Record<string, any>) {
|
||||||
|
if (delLoading.value) return;
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.logManage.exportFile.deleteTip', {
|
||||||
|
fileName: row.fileName,
|
||||||
|
}),
|
||||||
|
onOk() {
|
||||||
|
const key = 'delFile';
|
||||||
|
delLoading.value = true;
|
||||||
|
message.loading({ content: t('common.loading'), key });
|
||||||
|
delFile({
|
||||||
|
fileName: row.fileName,
|
||||||
|
path: queryParams.path,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.system.user.delSuss'),
|
||||||
|
key,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
fnGetList();
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
key: key,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
delLoading.value = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**网元类型选择对应修改 */
|
||||||
|
function fnNeChange(keys: any, opt: any) {
|
||||||
|
queryParams.tableName = keys;
|
||||||
|
queryParams.path = opt.path;
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询备份信息列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (queryParams.tableName === '') {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.logManage.exportFile.selectTip'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
getBakFileList(toRaw(queryParams)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
tablePagination.total = res.total;
|
||||||
|
tableState.data = res.data;
|
||||||
|
if (
|
||||||
|
tablePagination.total <=
|
||||||
|
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||||
|
queryParams.pageNum !== 1
|
||||||
|
) {
|
||||||
|
tableState.loading = false;
|
||||||
|
fnGetList(queryParams.pageNum - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.error(res.msg, 3);
|
||||||
|
tablePagination.total = 0;
|
||||||
|
tableState.data = [];
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getBakFile().then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
res.data.forEach((item: any) => {
|
||||||
|
fileList.value.push({
|
||||||
|
value: item.tableName,
|
||||||
|
label: item.tableDisplay,
|
||||||
|
path: item.filePath,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// .finally(() => {
|
||||||
|
// fnGetList();
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="8" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.logManage.exportFile.fileName')"
|
||||||
|
name="fileName"
|
||||||
|
style="margin-bottom: 0"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="logSelect"
|
||||||
|
:options="fileList"
|
||||||
|
@change="fnNeChange"
|
||||||
|
:allow-clear="false"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="16" :md="18" :xs="24" v-if="queryParams.path">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.logManage.neFile.nePath')"
|
||||||
|
name="configName"
|
||||||
|
style="margin-bottom: 0"
|
||||||
|
>
|
||||||
|
<a-breadcrumb>
|
||||||
|
<a-breadcrumb-item>
|
||||||
|
{{ queryParams.path }}
|
||||||
|
</a-breadcrumb-item>
|
||||||
|
</a-breadcrumb>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="fileName"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: 800 }"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'fileName'">
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
:loading="downLoading"
|
||||||
|
@click.prevent="fnDownloadFile(record)"
|
||||||
|
v-if="record.fileType === 'file'"
|
||||||
|
>
|
||||||
|
<template #icon><DownloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
:loading="delLoading"
|
||||||
|
@click.prevent="fnRecordDelete(record)"
|
||||||
|
v-if="record.fileType === 'file'"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
||||||
174
src/views/logManage/neFile/components/ViewDrawer.vue
Normal file
174
src/views/logManage/neFile/components/ViewDrawer.vue
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, watch, ref, nextTick } from 'vue';
|
||||||
|
import { ProModal } from 'antdv-pro-modal';
|
||||||
|
import TerminalSSHView from '@/components/TerminalSSHView/index.vue';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
const { t } = useI18n();
|
||||||
|
const emit = defineEmits(['ok', 'cancel', 'update:visible']);
|
||||||
|
const props = defineProps({
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
/**文件地址 */
|
||||||
|
filePath: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
/**网元类型 */
|
||||||
|
neType: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
/**网元ID */
|
||||||
|
neId: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type StateType = {
|
||||||
|
/**框是否显示 */
|
||||||
|
visible: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**查看命令 */
|
||||||
|
form: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let state: StateType = reactive({
|
||||||
|
visible: false,
|
||||||
|
title: '文件查看',
|
||||||
|
form: {
|
||||||
|
follow: true,
|
||||||
|
showType: 'lines', // lines/char
|
||||||
|
lines: 10,
|
||||||
|
char: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function onClose() {
|
||||||
|
state.visible = false;
|
||||||
|
emit('cancel');
|
||||||
|
emit('update:visible', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**监听是否显示,初始数据 */
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
val => {
|
||||||
|
if (val) {
|
||||||
|
if (props.neType && props.neId) {
|
||||||
|
const filePath = props.filePath;
|
||||||
|
const fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
|
||||||
|
state.title = fileName;
|
||||||
|
state.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**终端实例 */
|
||||||
|
const viewTerminal = ref();
|
||||||
|
|
||||||
|
/**终端初始连接 */
|
||||||
|
function fnInit() {
|
||||||
|
setTimeout(fnReload, 1_500);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**重载查看方式 */
|
||||||
|
function fnReload() {
|
||||||
|
if (!viewTerminal.value) return;
|
||||||
|
viewTerminal.value.ctrlC();
|
||||||
|
|
||||||
|
if (state.form.showType !== 'lines') {
|
||||||
|
state.form.lines = 10;
|
||||||
|
} else {
|
||||||
|
state.form.char = 0;
|
||||||
|
}
|
||||||
|
viewTerminal.value.clear();
|
||||||
|
viewTerminal.value.send('tail', {
|
||||||
|
filePath: props.filePath,
|
||||||
|
lines: state.form.lines,
|
||||||
|
char: state.form.char,
|
||||||
|
follow: state.form.follow,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ProModal
|
||||||
|
:drag="true"
|
||||||
|
:fullscreen="true"
|
||||||
|
:borderDraw="true"
|
||||||
|
:min-width="800"
|
||||||
|
:min-height="500"
|
||||||
|
:center-y="true"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
:keyboard="false"
|
||||||
|
:mask-closable="false"
|
||||||
|
:visible="state.visible"
|
||||||
|
:title="state.title"
|
||||||
|
:body-style="{ padding: '12px', overflow: 'hidden' }"
|
||||||
|
:footer="null"
|
||||||
|
@cancel="onClose"
|
||||||
|
>
|
||||||
|
<TerminalSSHView
|
||||||
|
ref="viewTerminal"
|
||||||
|
:id="`V${Date.now()}`"
|
||||||
|
style="height: calc(100% - 36px)"
|
||||||
|
:ne-type="neType"
|
||||||
|
:ne-id="neId"
|
||||||
|
@connect="fnInit()"
|
||||||
|
></TerminalSSHView>
|
||||||
|
<!-- 命令控制属性 -->
|
||||||
|
<a-form name="form" layout="horizontal">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.logManage.neFile.viewAs')">
|
||||||
|
<a-input-group compact>
|
||||||
|
<a-select v-model:value="state.form.showType" style="width: 50%">
|
||||||
|
<a-select-option value="lines">
|
||||||
|
{{ t('views.logManage.neFile.tailLines') }}
|
||||||
|
</a-select-option>
|
||||||
|
<a-select-option value="char">
|
||||||
|
{{ t('views.logManage.neFile.tailChar') }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<a-input-number
|
||||||
|
style="width: 25%"
|
||||||
|
v-model:value="state.form[state.form.showType]"
|
||||||
|
:min="0"
|
||||||
|
:max="1000"
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input-number>
|
||||||
|
<a-button type="primary" style="width: 25%" @click="fnReload()">
|
||||||
|
{{ t('views.logManage.neFile.reload') }}
|
||||||
|
</a-button>
|
||||||
|
</a-input-group>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.logManage.neFile.follow')"
|
||||||
|
name="follow"
|
||||||
|
>
|
||||||
|
<a-switch v-model:checked="state.form.follow" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</ProModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.ant-form :deep(.ant-form-item) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,12 +3,13 @@ import { reactive, ref, onMounted, toRaw } from 'vue';
|
|||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
||||||
import { ColumnsType } from 'ant-design-vue/lib/table';
|
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||||||
|
import { Modal, message } from 'ant-design-vue/lib';
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
import { getNeFile, listNeFiles } from '@/api/tool/neFile';
|
import { getNeFile, listNeFiles } from '@/api/tool/neFile';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import useNeInfoStore from '@/store/modules/neinfo';
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import { Modal, message } from 'ant-design-vue/lib';
|
import ViewDrawer from './components/ViewDrawer.vue';
|
||||||
import saveAs from 'file-saver';
|
import saveAs from 'file-saver';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
const neInfoStore = useNeInfoStore();
|
const neInfoStore = useNeInfoStore();
|
||||||
@@ -19,11 +20,7 @@ const route = useRoute();
|
|||||||
const routeParams = route.query as Record<string, any>;
|
const routeParams = route.query as Record<string, any>;
|
||||||
|
|
||||||
/**网元参数 */
|
/**网元参数 */
|
||||||
let neType = ref<string[]>([]);
|
let neTypeSelect = ref<string[]>([]);
|
||||||
/**下载触发等待 */
|
|
||||||
let loading = ref(false);
|
|
||||||
/**访问路径 */
|
|
||||||
let nePathArr = ref<string[]>([]);
|
|
||||||
|
|
||||||
/**查询参数 */
|
/**查询参数 */
|
||||||
let queryParams = reactive({
|
let queryParams = reactive({
|
||||||
@@ -134,20 +131,24 @@ let tablePagination = reactive({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**下载触发等待 */
|
||||||
|
let downLoading = ref<boolean>(false);
|
||||||
|
|
||||||
/**信息文件下载 */
|
/**信息文件下载 */
|
||||||
function fnDownloadFile(row: Record<string, any>) {
|
function fnDownloadFile(row: Record<string, any>) {
|
||||||
if (loading.value) return;
|
if (downLoading.value) return;
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: t('common.tipTitle'),
|
title: t('common.tipTitle'),
|
||||||
content: t('views.logManage.neFile.downTip', { fileName: row.fileName }),
|
content: t('views.logManage.neFile.downTip', { fileName: row.fileName }),
|
||||||
onOk() {
|
onOk() {
|
||||||
loading.value = true;
|
downLoading.value = true;
|
||||||
const hide = message.loading(t('common.loading'), 0);
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
getNeFile({
|
getNeFile({
|
||||||
neType: queryParams.neType,
|
neType: queryParams.neType,
|
||||||
neId: queryParams.neId,
|
neId: queryParams.neId,
|
||||||
path: queryParams.path,
|
path: queryParams.path,
|
||||||
fileName: row.fileName,
|
fileName: row.fileName,
|
||||||
|
delTemp: true,
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
@@ -167,12 +168,15 @@ function fnDownloadFile(row: Record<string, any>) {
|
|||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
hide();
|
hide();
|
||||||
loading.value = false;
|
downLoading.value = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**访问路径 */
|
||||||
|
let nePathArr = ref<string[]>([]);
|
||||||
|
|
||||||
/**进入目录 */
|
/**进入目录 */
|
||||||
function fnDirCD(dir: string, index?: number) {
|
function fnDirCD(dir: string, index?: number) {
|
||||||
if (index === undefined) {
|
if (index === undefined) {
|
||||||
@@ -235,15 +239,42 @@ function fnGetList(pageNum?: number) {
|
|||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
tablePagination.total = res.total;
|
tablePagination.total = res.total;
|
||||||
tableState.data = res.rows;
|
tableState.data = res.rows;
|
||||||
if (tablePagination.total <=(queryParams.pageNum - 1) * tablePagination.pageSize &&queryParams.pageNum !== 1) {
|
if (
|
||||||
|
tablePagination.total <=
|
||||||
|
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||||
|
queryParams.pageNum !== 1
|
||||||
|
) {
|
||||||
tableState.loading = false;
|
tableState.loading = false;
|
||||||
fnGetList(queryParams.pageNum - 1);
|
fnGetList(queryParams.pageNum - 1);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
message.error(res.msg, 3);
|
||||||
|
tablePagination.total = 0;
|
||||||
|
tableState.data = [];
|
||||||
}
|
}
|
||||||
tableState.loading = false;
|
tableState.loading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**抽屉状态 */
|
||||||
|
const viewDrawerState = reactive({
|
||||||
|
visible: false,
|
||||||
|
/**文件路径 /var/log/amf.log */
|
||||||
|
filePath: '',
|
||||||
|
/**网元类型 */
|
||||||
|
neType: '',
|
||||||
|
/**网元ID */
|
||||||
|
neId: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
/**打开抽屉查看 */
|
||||||
|
function fnDrawerOpen(row: Record<string, any>) {
|
||||||
|
viewDrawerState.filePath = [...nePathArr.value, row.fileName].join('/');
|
||||||
|
viewDrawerState.neType = neTypeSelect.value[0];
|
||||||
|
viewDrawerState.neId = neTypeSelect.value[1];
|
||||||
|
viewDrawerState.visible = !viewDrawerState.visible;
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 获取网元网元列表
|
// 获取网元网元列表
|
||||||
neInfoStore.fnNelist().then(res => {
|
neInfoStore.fnNelist().then(res => {
|
||||||
@@ -253,9 +284,9 @@ onMounted(() => {
|
|||||||
content: t('common.noData'),
|
content: t('common.noData'),
|
||||||
duration: 2,
|
duration: 2,
|
||||||
});
|
});
|
||||||
} else if (routeParams.neType) {
|
} else if (routeParams.neType) {
|
||||||
neType.value = [routeParams.neType, routeParams.neId]
|
neTypeSelect.value = [routeParams.neType, routeParams.neId];
|
||||||
fnNeChange(neType.value, undefined);
|
fnNeChange(neTypeSelect.value, undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -276,12 +307,12 @@ onMounted(() => {
|
|||||||
style="margin-bottom: 0"
|
style="margin-bottom: 0"
|
||||||
>
|
>
|
||||||
<a-cascader
|
<a-cascader
|
||||||
v-model:value="neType"
|
v-model:value="neTypeSelect"
|
||||||
:options="neInfoStore.getNeCascaderOptions"
|
:options="neInfoStore.getNeCascaderOptions"
|
||||||
@change="fnNeChange"
|
@change="fnNeChange"
|
||||||
:allow-clear="false"
|
:allow-clear="false"
|
||||||
:placeholder="t('views.logManage.neFile.neTypePlease')"
|
:placeholder="t('views.logManage.neFile.neTypePlease')"
|
||||||
:disabled="loading || tableState.loading"
|
:disabled="downLoading || tableState.loading"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -332,8 +363,15 @@ onMounted(() => {
|
|||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'fileName'">
|
<template v-if="column.key === 'fileName'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip v-if="record.fileType === 'file'">
|
||||||
|
<template #title>{{ t('common.viewText') }}</template>
|
||||||
|
<a-button type="link" @click.prevent="fnDrawerOpen(record)">
|
||||||
|
<template #icon><ProfileOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
|
:loading="downLoading"
|
||||||
@click.prevent="fnDownloadFile(record)"
|
@click.prevent="fnDownloadFile(record)"
|
||||||
v-if="record.fileType === 'file'"
|
v-if="record.fileType === 'file'"
|
||||||
>
|
>
|
||||||
@@ -342,6 +380,7 @@ onMounted(() => {
|
|||||||
</a-button>
|
</a-button>
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
|
:loading="downLoading"
|
||||||
@click.prevent="fnDirCD(record.fileName)"
|
@click.prevent="fnDirCD(record.fileName)"
|
||||||
v-if="record.fileType === 'dir'"
|
v-if="record.fileType === 'dir'"
|
||||||
>
|
>
|
||||||
@@ -353,6 +392,14 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 文件内容查看抽屉 -->
|
||||||
|
<ViewDrawer
|
||||||
|
v-model:visible="viewDrawerState.visible"
|
||||||
|
:file-path="viewDrawerState.filePath"
|
||||||
|
:ne-type="viewDrawerState.neType"
|
||||||
|
:ne-id="viewDrawerState.neId"
|
||||||
|
></ViewDrawer>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const graphG6Dom = ref<HTMLElement | undefined>(undefined);
|
|||||||
/**图状态 */
|
/**图状态 */
|
||||||
const graphState = reactive<Record<string, any>>({
|
const graphState = reactive<Record<string, any>>({
|
||||||
/**当前图组名 */
|
/**当前图组名 */
|
||||||
group: '5GC System Architecture5',
|
group: '5GC System Architecture',
|
||||||
/**图数据 */
|
/**图数据 */
|
||||||
data: {
|
data: {
|
||||||
combos: [],
|
combos: [],
|
||||||
|
|||||||
412
src/views/ne/neConfig/hooks/useConfigArray.ts
Normal file
412
src/views/ne/neConfig/hooks/useConfigArray.ts
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
import {
|
||||||
|
addNeConfigData,
|
||||||
|
delNeConfigData,
|
||||||
|
editNeConfigData,
|
||||||
|
} from '@/api/ne/neConfig';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { Modal } from 'ant-design-vue/lib';
|
||||||
|
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
||||||
|
import message from 'ant-design-vue/lib/message';
|
||||||
|
import { reactive, watch } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数配置array类型
|
||||||
|
* @param param 父级传入 { t, treeState, neTypeSelect, fnActiveConfigNode, ruleVerification, modalState, fnModalCancel}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function useConfigArray({
|
||||||
|
t,
|
||||||
|
treeState,
|
||||||
|
neTypeSelect,
|
||||||
|
fnActiveConfigNode,
|
||||||
|
ruleVerification,
|
||||||
|
modalState,
|
||||||
|
fnModalCancel,
|
||||||
|
}: any) {
|
||||||
|
/**多列列表状态类型 */
|
||||||
|
type ArrayStateType = {
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**多列嵌套记录字段 */
|
||||||
|
columns: Record<string, any>[];
|
||||||
|
/**表格字段列排序 */
|
||||||
|
columnsDnd: Record<string, any>[];
|
||||||
|
/**多列记录数据 */
|
||||||
|
columnsData: Record<string, any>[];
|
||||||
|
/**多列嵌套展开key */
|
||||||
|
arrayChildExpandKeys: any[];
|
||||||
|
|
||||||
|
/**多列记录数据 */
|
||||||
|
data: Record<string, any>[];
|
||||||
|
/**多列记录规则 */
|
||||||
|
dataRule: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**多列列表状态 */
|
||||||
|
let arrayState: ArrayStateType = reactive({
|
||||||
|
size: 'small',
|
||||||
|
columns: [],
|
||||||
|
columnsDnd: [],
|
||||||
|
columnsData: [],
|
||||||
|
arrayChildExpandKeys: [],
|
||||||
|
data: [],
|
||||||
|
dataRule: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**多列表编辑 */
|
||||||
|
function arrayEdit(rowIndex: Record<string, any>) {
|
||||||
|
const item = arrayState.data.find((s: any) => s.key === rowIndex.value);
|
||||||
|
if (!item) return;
|
||||||
|
const from = arrayInitEdit(item, arrayState.dataRule);
|
||||||
|
// 处理信息
|
||||||
|
const row: Record<string, any> = {};
|
||||||
|
for (const v of from.record) {
|
||||||
|
if (Array.isArray(v.array)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
row[v.name] = Object.assign({}, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 特殊SMF-upfid选择
|
||||||
|
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(row, 'upfId')) {
|
||||||
|
const v = row.upfId.value;
|
||||||
|
if (typeof v === 'string') {
|
||||||
|
if (v === '') {
|
||||||
|
row.upfId.value = [];
|
||||||
|
} else if (v.includes(';')) {
|
||||||
|
row.upfId.value = v.split(';');
|
||||||
|
} else if (v.includes(',')) {
|
||||||
|
row.upfId.value = v.split(',');
|
||||||
|
} else {
|
||||||
|
row.upfId.value = [v];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modalState.from = row;
|
||||||
|
modalState.type = 'arrayEdit';
|
||||||
|
modalState.title = `${treeState.selectNode.paramDisplay} ${from.title}`;
|
||||||
|
modalState.key = from.key;
|
||||||
|
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
|
||||||
|
modalState.visible = true;
|
||||||
|
|
||||||
|
// 关闭嵌套
|
||||||
|
arrayState.arrayChildExpandKeys = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表编辑关闭 */
|
||||||
|
function arrayEditClose() {
|
||||||
|
arrayState.arrayChildExpandKeys = [];
|
||||||
|
fnModalCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表编辑确认 */
|
||||||
|
function arrayEditOk(from: Record<string, any>) {
|
||||||
|
const loc = `${from['index']['value']}`;
|
||||||
|
|
||||||
|
// 特殊SMF-upfid选择
|
||||||
|
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(from, 'upfId')) {
|
||||||
|
const v = from.upfId.value;
|
||||||
|
if (Array.isArray(v)) {
|
||||||
|
from.upfId.value = v.join(';');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历提取属性和值
|
||||||
|
let data: Record<string, any> = {};
|
||||||
|
for (const key in from) {
|
||||||
|
// 子嵌套的不插入
|
||||||
|
if (from[key]['array']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 检查规则
|
||||||
|
const [ok, msg] = ruleVerification(from[key]);
|
||||||
|
if (!ok) {
|
||||||
|
message.warning({
|
||||||
|
content: `${msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data[key] = from[key]['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
editNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neTypeSelect.value[1],
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc: loc,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.updateItem', {
|
||||||
|
num: modalState.title,
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.updateItemErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
arrayEditClose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表删除单行 */
|
||||||
|
function arrayDelete(rowIndex: Record<string, any>) {
|
||||||
|
const loc = `${rowIndex.value}`;
|
||||||
|
const title = `${treeState.selectNode.paramDisplay} Index-${loc}`;
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.ne.neConfig.delItemTip', {
|
||||||
|
num: title,
|
||||||
|
}),
|
||||||
|
onOk() {
|
||||||
|
delNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neTypeSelect.value[1],
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
loc: loc,
|
||||||
|
}).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.delItemOk', {
|
||||||
|
num: title,
|
||||||
|
}),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
arrayEditClose();
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表新增单行 */
|
||||||
|
function arrayAdd() {
|
||||||
|
const from = arrayInitAdd(arrayState.data, arrayState.dataRule);
|
||||||
|
|
||||||
|
// 处理信息
|
||||||
|
const row: Record<string, any> = {};
|
||||||
|
for (const v of from.record) {
|
||||||
|
if (Array.isArray(v.array)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
row[v.name] = Object.assign({}, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 特殊SMF-upfid选择
|
||||||
|
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(row, 'upfId')) {
|
||||||
|
const v = row.upfId.value;
|
||||||
|
if (typeof v === 'string') {
|
||||||
|
if (v === '') {
|
||||||
|
row.upfId.value = [];
|
||||||
|
} else if (v.includes(';')) {
|
||||||
|
row.upfId.value = v.split(';');
|
||||||
|
} else if (v.includes(',')) {
|
||||||
|
row.upfId.value = v.split(',');
|
||||||
|
} else {
|
||||||
|
row.upfId.value = [v];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modalState.from = row;
|
||||||
|
modalState.type = 'arrayAdd';
|
||||||
|
modalState.title = `${treeState.selectNode.paramDisplay} ${from.title}`;
|
||||||
|
modalState.key = from.key;
|
||||||
|
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
|
||||||
|
modalState.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表新增单行确认 */
|
||||||
|
function arrayAddOk(from: Record<string, any>) {
|
||||||
|
// 特殊SMF-upfid选择
|
||||||
|
if (neTypeSelect.value[0] === 'SMF' && Reflect.has(from, 'upfId')) {
|
||||||
|
const v = from.upfId.value;
|
||||||
|
if (Array.isArray(v)) {
|
||||||
|
from.upfId.value = v.join(';');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历提取属性和值
|
||||||
|
let data: Record<string, any> = {};
|
||||||
|
for (const key in from) {
|
||||||
|
// 子嵌套的不插入
|
||||||
|
if (from[key]['array']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 检查规则
|
||||||
|
const [ok, msg] = ruleVerification(from[key]);
|
||||||
|
if (!ok) {
|
||||||
|
message.warning({
|
||||||
|
content: `${msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data[key] = from[key]['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
addNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neTypeSelect.value[1],
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc: `${from['index']['value']}`,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.addItemOk', {
|
||||||
|
num: modalState.title,
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.addItemErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
arrayEditClose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表编辑行数据初始化 */
|
||||||
|
function arrayInitEdit(data: Record<string, any>, dataRule: any) {
|
||||||
|
const dataFrom = data.record;
|
||||||
|
const ruleFrom = Object.assign({}, JSON.parse(JSON.stringify(dataRule)));
|
||||||
|
for (const row of ruleFrom.record) {
|
||||||
|
// 子嵌套的不初始
|
||||||
|
if (row.array) {
|
||||||
|
row.value = [];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 查找项的值
|
||||||
|
const item = dataFrom.find((s: any) => s.name === row.name);
|
||||||
|
if (!item) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 可选的
|
||||||
|
row.optional = 'true';
|
||||||
|
// 根据规则类型转值
|
||||||
|
if (['enum', 'int'].includes(row.type)) {
|
||||||
|
row.value = Number(item.value);
|
||||||
|
} else if ('bool' === row.type) {
|
||||||
|
row.value = Boolean(item.value);
|
||||||
|
} else {
|
||||||
|
row.value = item.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ruleFrom.key = data.key;
|
||||||
|
ruleFrom.title = data.title;
|
||||||
|
return ruleFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表新增行数据初始化 */
|
||||||
|
function arrayInitAdd(data: any[], dataRule: any) {
|
||||||
|
// 有数据时取得最后的index
|
||||||
|
let dataLastIndex = 0;
|
||||||
|
if (data.length !== 0) {
|
||||||
|
const lastFrom = Object.assign(
|
||||||
|
{},
|
||||||
|
JSON.parse(JSON.stringify(data.at(-1)))
|
||||||
|
);
|
||||||
|
if (lastFrom.record.length > 0) {
|
||||||
|
dataLastIndex = parseInt(lastFrom.key);
|
||||||
|
dataLastIndex += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ruleFrom = Object.assign({}, JSON.parse(JSON.stringify(dataRule)));
|
||||||
|
for (const row of ruleFrom.record) {
|
||||||
|
// 子嵌套的不初始
|
||||||
|
if (row.array) {
|
||||||
|
row.value = [];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 可选的
|
||||||
|
row.optional = 'true';
|
||||||
|
// index值
|
||||||
|
if (row.name === 'index') {
|
||||||
|
let newIndex =
|
||||||
|
dataLastIndex !== 0 ? dataLastIndex : parseInt(row.value);
|
||||||
|
if (isNaN(newIndex)) {
|
||||||
|
newIndex = 0;
|
||||||
|
}
|
||||||
|
row.value = newIndex;
|
||||||
|
ruleFrom.key = newIndex;
|
||||||
|
ruleFrom.title = `Index-${newIndex}`;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 根据规则类型转值
|
||||||
|
if (['enum', 'int'].includes(row.type)) {
|
||||||
|
row.value = Number(row.value);
|
||||||
|
}
|
||||||
|
if ('bool' === row.type) {
|
||||||
|
row.value = Boolean(row.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 特殊SMF-upfid选择
|
||||||
|
if (neTypeSelect.value[0] === 'SMF' && row.name === 'upfId') {
|
||||||
|
const v = row.value;
|
||||||
|
if (typeof v === 'string') {
|
||||||
|
if (v === '') {
|
||||||
|
row.value = [];
|
||||||
|
} else if (v.includes(';')) {
|
||||||
|
row.value = v.split(';');
|
||||||
|
} else if (v.includes(',')) {
|
||||||
|
row.value = v.split(',');
|
||||||
|
} else {
|
||||||
|
row.value = [v];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ruleFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听表格字段列排序变化关闭展开
|
||||||
|
watch(
|
||||||
|
() => arrayState.columnsDnd,
|
||||||
|
() => {
|
||||||
|
arrayEditClose();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
arrayState,
|
||||||
|
arrayEdit,
|
||||||
|
arrayEditClose,
|
||||||
|
arrayEditOk,
|
||||||
|
arrayDelete,
|
||||||
|
arrayAdd,
|
||||||
|
arrayAddOk,
|
||||||
|
arrayInitEdit,
|
||||||
|
arrayInitAdd,
|
||||||
|
};
|
||||||
|
}
|
||||||
353
src/views/ne/neConfig/hooks/useConfigArrayChild.ts
Normal file
353
src/views/ne/neConfig/hooks/useConfigArrayChild.ts
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
import {
|
||||||
|
addNeConfigData,
|
||||||
|
editNeConfigData,
|
||||||
|
delNeConfigData,
|
||||||
|
} from '@/api/ne/neConfig';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { Modal } from 'ant-design-vue/lib';
|
||||||
|
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
||||||
|
import message from 'ant-design-vue/lib/message';
|
||||||
|
import { nextTick, reactive } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数配置array类型的嵌套array
|
||||||
|
* @param param 父级传入 { t, treeState, neTypeSelect, fnActiveConfigNode, ruleVerification, modalState, arrayState, arrayInitEdit, arrayInitAdd, arrayEditClose}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function useConfigArrayChild({
|
||||||
|
t,
|
||||||
|
treeState,
|
||||||
|
neTypeSelect,
|
||||||
|
fnActiveConfigNode,
|
||||||
|
ruleVerification,
|
||||||
|
modalState,
|
||||||
|
arrayState,
|
||||||
|
arrayInitEdit,
|
||||||
|
arrayInitAdd,
|
||||||
|
arrayEditClose,
|
||||||
|
}: any) {
|
||||||
|
/**多列嵌套列表状态类型 */
|
||||||
|
type ArrayChildStateType = {
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**层级index */
|
||||||
|
loc: string;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**多列嵌套记录字段 */
|
||||||
|
columns: Record<string, any>[];
|
||||||
|
/**表格字段列排序 */
|
||||||
|
columnsDnd: Record<string, any>[];
|
||||||
|
/**多列记录数据 */
|
||||||
|
columnsData: Record<string, any>[];
|
||||||
|
|
||||||
|
/**多列嵌套记录数据 */
|
||||||
|
data: Record<string, any>[];
|
||||||
|
/**多列嵌套记录规则 */
|
||||||
|
dataRule: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**多列嵌套表格状态 */
|
||||||
|
let arrayChildState: ArrayChildStateType = reactive({
|
||||||
|
title: '',
|
||||||
|
loc: '',
|
||||||
|
size: 'small',
|
||||||
|
columns: [],
|
||||||
|
columnsDnd: [],
|
||||||
|
columnsData: [],
|
||||||
|
data: [],
|
||||||
|
dataRule: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**多列表展开嵌套行 */
|
||||||
|
function arrayChildExpand(
|
||||||
|
indexRow: Record<string, any>,
|
||||||
|
row: Record<string, any>
|
||||||
|
) {
|
||||||
|
const loc = indexRow.value;
|
||||||
|
if (arrayChildState.loc === `${loc}/${row.name}`) {
|
||||||
|
arrayChildState.loc = '';
|
||||||
|
arrayState.arrayChildExpandKeys = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
arrayChildState.loc = '';
|
||||||
|
arrayState.arrayChildExpandKeys = [];
|
||||||
|
const from = Object.assign({}, JSON.parse(JSON.stringify(row)));
|
||||||
|
// 无数据时
|
||||||
|
if (!Array.isArray(from.value)) {
|
||||||
|
from.value = [];
|
||||||
|
}
|
||||||
|
const dataArr = Object.freeze(from.value);
|
||||||
|
const ruleArr = Object.freeze(from.array);
|
||||||
|
|
||||||
|
// 列表项数据
|
||||||
|
const dataArray: Record<string, any>[] = [];
|
||||||
|
for (const item of dataArr) {
|
||||||
|
const index = item['index'];
|
||||||
|
let record: Record<string, any>[] = [];
|
||||||
|
for (const key of Object.keys(item)) {
|
||||||
|
// 规则为准
|
||||||
|
for (const rule of ruleArr) {
|
||||||
|
if (rule['name'] === key) {
|
||||||
|
const ruleItem = Object.assign({ optional: 'true' }, rule, {
|
||||||
|
value: item[key],
|
||||||
|
});
|
||||||
|
record.push(ruleItem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// dataArray.push(record);
|
||||||
|
dataArray.push({ title: `Index-${index}`, key: index, record });
|
||||||
|
}
|
||||||
|
arrayChildState.data = dataArray;
|
||||||
|
|
||||||
|
// 无数据时,用于新增
|
||||||
|
arrayChildState.dataRule = {
|
||||||
|
title: `Index-0`,
|
||||||
|
key: 0,
|
||||||
|
record: ruleArr,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 列表数据
|
||||||
|
const columnsData: Record<string, any>[] = [];
|
||||||
|
for (const v of arrayChildState.data) {
|
||||||
|
const row: Record<string, any> = {};
|
||||||
|
for (const item of v.record) {
|
||||||
|
row[item.name] = item;
|
||||||
|
}
|
||||||
|
columnsData.push(row);
|
||||||
|
}
|
||||||
|
arrayChildState.columnsData = columnsData;
|
||||||
|
|
||||||
|
// 列表字段
|
||||||
|
const columns: Record<string, any>[] = [];
|
||||||
|
for (const rule of arrayChildState.dataRule.record) {
|
||||||
|
columns.push({
|
||||||
|
title: rule.display,
|
||||||
|
dataIndex: rule.name,
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 50,
|
||||||
|
minWidth: 50,
|
||||||
|
maxWidth: 250,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
columns.push({
|
||||||
|
title: t('common.operate'),
|
||||||
|
dataIndex: 'index',
|
||||||
|
key: 'index',
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 100,
|
||||||
|
});
|
||||||
|
arrayChildState.columns = columns;
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
// 设置展开key
|
||||||
|
arrayState.arrayChildExpandKeys = [indexRow];
|
||||||
|
// 层级标识
|
||||||
|
arrayChildState.loc = `${loc}/${from['name']}`;
|
||||||
|
// 设置展开列表标题
|
||||||
|
arrayChildState.title = `${from['display']}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表嵌套行编辑 */
|
||||||
|
function arrayChildEdit(rowIndex: Record<string, any>) {
|
||||||
|
const item = arrayChildState.data.find(
|
||||||
|
(s: any) => s.key === rowIndex.value
|
||||||
|
);
|
||||||
|
if (!item) return;
|
||||||
|
const from = arrayInitEdit(item, arrayChildState.dataRule);
|
||||||
|
// 处理信息
|
||||||
|
const row: Record<string, any> = {};
|
||||||
|
for (const v of from.record) {
|
||||||
|
if (Array.isArray(v.array)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
row[v.name] = Object.assign({}, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
modalState.from = row;
|
||||||
|
modalState.type = 'arrayChildEdit';
|
||||||
|
modalState.title = `${arrayChildState.title} ${from.title}`;
|
||||||
|
modalState.key = from.key;
|
||||||
|
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
|
||||||
|
modalState.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表嵌套行编辑确认 */
|
||||||
|
function arrayChildEditOk(from: Record<string, any>) {
|
||||||
|
const loc = `${arrayChildState.loc}/${from['index']['value']}`;
|
||||||
|
|
||||||
|
let data: Record<string, any> = {};
|
||||||
|
for (const key in from) {
|
||||||
|
// 子嵌套的不插入
|
||||||
|
if (from[key]['array']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 检查规则
|
||||||
|
const [ok, msg] = ruleVerification(from[key]);
|
||||||
|
if (!ok) {
|
||||||
|
message.warning({
|
||||||
|
content: `${msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data[key] = from[key]['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
editNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neTypeSelect.value[1],
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.updateItem', {
|
||||||
|
num: modalState.title,
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.updateItemErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
arrayEditClose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表嵌套行删除单行 */
|
||||||
|
function arrayChildDelete(rowIndex: Record<string, any>) {
|
||||||
|
const index = rowIndex.value;
|
||||||
|
const loc = `${arrayChildState.loc}/${index}`;
|
||||||
|
const title = `${arrayChildState.title} Index-${index}`;
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.ne.neConfig.delItemTip', {
|
||||||
|
num: title,
|
||||||
|
}),
|
||||||
|
onOk() {
|
||||||
|
delNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neTypeSelect.value[1],
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
loc,
|
||||||
|
}).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.delItemOk', {
|
||||||
|
num: title,
|
||||||
|
}),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
arrayEditClose();
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表嵌套行新增单行 */
|
||||||
|
function arrayChildAdd() {
|
||||||
|
const from = arrayInitAdd(arrayChildState.data, arrayChildState.dataRule);
|
||||||
|
// 处理信息
|
||||||
|
const row: Record<string, any> = {};
|
||||||
|
for (const v of from.record) {
|
||||||
|
if (Array.isArray(v.array)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
row[v.name] = Object.assign({}, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
modalState.from = row;
|
||||||
|
modalState.type = 'arrayChildAdd';
|
||||||
|
modalState.title = `${arrayChildState.title} ${from.title}`;
|
||||||
|
modalState.key = from.key;
|
||||||
|
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
|
||||||
|
modalState.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**多列表新增单行确认 */
|
||||||
|
function arrayChildAddOk(from: Record<string, any>) {
|
||||||
|
const loc = `${arrayChildState.loc}/${from['index']['value']}`;
|
||||||
|
|
||||||
|
let data: Record<string, any> = {};
|
||||||
|
for (const key in from) {
|
||||||
|
// 子嵌套的不插入
|
||||||
|
if (from[key]['array']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 检查规则
|
||||||
|
const [ok, msg] = ruleVerification(from[key]);
|
||||||
|
if (!ok) {
|
||||||
|
message.warning({
|
||||||
|
content: `${msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data[key] = from[key]['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
addNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neTypeSelect.value[1],
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: data,
|
||||||
|
loc,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.addItemOk', {
|
||||||
|
num: modalState.title,
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnActiveConfigNode('#');
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.addItemErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
arrayEditClose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
arrayChildState,
|
||||||
|
arrayChildExpand,
|
||||||
|
arrayChildEdit,
|
||||||
|
arrayChildEditOk,
|
||||||
|
arrayChildDelete,
|
||||||
|
arrayChildAdd,
|
||||||
|
arrayChildAddOk,
|
||||||
|
};
|
||||||
|
}
|
||||||
152
src/views/ne/neConfig/hooks/useConfigList.ts
Normal file
152
src/views/ne/neConfig/hooks/useConfigList.ts
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import { editNeConfigData } from '@/api/ne/neConfig';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
||||||
|
import message from 'ant-design-vue/lib/message';
|
||||||
|
import { reactive, toRaw } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list类型参数处理
|
||||||
|
* @param param 父级传入 {t, treeState, neTypeSelect, ruleVerification}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function useConfigList({
|
||||||
|
t,
|
||||||
|
treeState,
|
||||||
|
neTypeSelect,
|
||||||
|
ruleVerification,
|
||||||
|
}: any) {
|
||||||
|
/**单列表状态类型 */
|
||||||
|
type ListStateType = {
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**单列记录字段 */
|
||||||
|
columns: Record<string, any>[];
|
||||||
|
/**单列记录数据 */
|
||||||
|
data: Record<string, any>[];
|
||||||
|
/**编辑行记录 */
|
||||||
|
editRecord: Record<string, any>;
|
||||||
|
/**确认提交等待 */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**单列表状态 */
|
||||||
|
let listState: ListStateType = reactive({
|
||||||
|
size: 'small',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
title: 'Key',
|
||||||
|
dataIndex: 'display',
|
||||||
|
align: 'left',
|
||||||
|
width: '30%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Value',
|
||||||
|
dataIndex: 'value',
|
||||||
|
align: 'left',
|
||||||
|
width: '70%',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
data: [],
|
||||||
|
confirmLoading: false,
|
||||||
|
editRecord: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**单列表编辑 */
|
||||||
|
function listEdit(row: Record<string, any>) {
|
||||||
|
if (
|
||||||
|
listState.confirmLoading ||
|
||||||
|
['read-only', 'read', 'ro'].includes(row.access)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listState.editRecord = Object.assign({}, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**单列表编辑关闭 */
|
||||||
|
function listEditClose() {
|
||||||
|
listState.confirmLoading = false;
|
||||||
|
listState.editRecord = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**单列表编辑确认 */
|
||||||
|
function listEditOk() {
|
||||||
|
if (listState.confirmLoading) return;
|
||||||
|
const from = toRaw(listState.editRecord);
|
||||||
|
// 检查规则
|
||||||
|
const [ok, msg] = ruleVerification(from);
|
||||||
|
if (!ok) {
|
||||||
|
message.warning({
|
||||||
|
content: `${msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送
|
||||||
|
listState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
editNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neTypeSelect.value[1],
|
||||||
|
paramName: treeState.selectNode.paramName,
|
||||||
|
paramData: {
|
||||||
|
[from['name']]: from['value'],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.ne.neConfig.updateValue', {
|
||||||
|
num: from['display'],
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
// 改变表格数据
|
||||||
|
const item = listState.data.find(
|
||||||
|
(item: Record<string, any>) => from['name'] === item['name']
|
||||||
|
);
|
||||||
|
if (item) {
|
||||||
|
Object.assign(item, listState.editRecord);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.ne.neConfig.updateValueErr'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
listState.confirmLoading = false;
|
||||||
|
listState.editRecord = {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
let tablePagination = reactive({
|
||||||
|
/**当前页数 */
|
||||||
|
current: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 10,
|
||||||
|
/**默认的每页条数 */
|
||||||
|
defaultPageSize: 10,
|
||||||
|
/**指定每页可以显示多少条 */
|
||||||
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
|
/**只有一页时是否隐藏分页器 */
|
||||||
|
hideOnSinglePage: true,
|
||||||
|
/**是否可以快速跳转至某页 */
|
||||||
|
showQuickJumper: true,
|
||||||
|
/**是否可以改变 pageSize */
|
||||||
|
showSizeChanger: true,
|
||||||
|
/**数据总数 */
|
||||||
|
total: 0,
|
||||||
|
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
|
||||||
|
onChange: (page: number, pageSize: number) => {
|
||||||
|
tablePagination.current = page;
|
||||||
|
tablePagination.pageSize = pageSize;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { tablePagination, listState, listEdit, listEditClose, listEditOk };
|
||||||
|
}
|
||||||
192
src/views/ne/neConfig/hooks/useOptions.ts
Normal file
192
src/views/ne/neConfig/hooks/useOptions.ts
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
import { getNeConfigData } from '@/api/ne/neConfig';
|
||||||
|
import { regExpIPv4, regExpIPv6, validURL } from '@/utils/regular-utils';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数公共函数
|
||||||
|
* @param param 父级传入 {t}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function useOptions({ t }: any) {
|
||||||
|
/**规则校验 */
|
||||||
|
function ruleVerification(row: Record<string, any>): (string | boolean)[] {
|
||||||
|
let result = [true, ''];
|
||||||
|
const type = row.type;
|
||||||
|
const value = row.value;
|
||||||
|
const filter = row.filter;
|
||||||
|
const display = row.display;
|
||||||
|
|
||||||
|
// 子嵌套的不检查
|
||||||
|
if (row.array) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 可选的同时没有值不检查
|
||||||
|
if (row.optional === 'true' && !value) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case 'int':
|
||||||
|
// filter: "0~128"
|
||||||
|
|
||||||
|
if (filter && filter.indexOf('~') !== -1) {
|
||||||
|
const filterArr = filter.split('~');
|
||||||
|
const minInt = parseInt(filterArr[0]);
|
||||||
|
const maxInt = parseInt(filterArr[1]);
|
||||||
|
const valueInt = parseInt(value);
|
||||||
|
if (valueInt < minInt || valueInt > maxInt) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireInt', {
|
||||||
|
display,
|
||||||
|
filter,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'ipv4':
|
||||||
|
if (!regExpIPv4.test(value)) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireIpv4', { display }),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'ipv6':
|
||||||
|
if (!regExpIPv6.test(value)) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireIpv6', { display }),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'enum':
|
||||||
|
if (filter && filter.indexOf('{') === 1) {
|
||||||
|
let filterJson: Record<string, any> = {};
|
||||||
|
try {
|
||||||
|
filterJson = JSON.parse(filter); //string---json
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Object.keys(filterJson).includes(`${value}`)) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireEnum', { display }),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'bool':
|
||||||
|
// filter: '{"0":"false", "1":"true"}'
|
||||||
|
|
||||||
|
if (filter && filter.indexOf('{') === 1) {
|
||||||
|
let filterJson: Record<string, any> = {};
|
||||||
|
try {
|
||||||
|
filterJson = JSON.parse(filter); //string---json
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Object.values(filterJson).includes(`${value}`)) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireBool', { display }),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'string':
|
||||||
|
// filter: "0~128"
|
||||||
|
|
||||||
|
// 字符串长度判断
|
||||||
|
if (filter && filter.indexOf('~') !== -1) {
|
||||||
|
try {
|
||||||
|
const filterArr = filter.split('~');
|
||||||
|
let rule = new RegExp(
|
||||||
|
'^\\S{' + filterArr[0] + ',' + filterArr[1] + '}$'
|
||||||
|
);
|
||||||
|
if (!rule.test(value)) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireString', {
|
||||||
|
display,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 字符串http判断
|
||||||
|
if (value.startsWith('http')) {
|
||||||
|
try {
|
||||||
|
if (!validURL(value)) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireString', {
|
||||||
|
display,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'regex':
|
||||||
|
// filter: "^[0-9]{3}$"
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
try {
|
||||||
|
let regex = new RegExp(filter);
|
||||||
|
if (!regex.test(value)) {
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireString', {
|
||||||
|
display,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return [
|
||||||
|
false,
|
||||||
|
t('views.ne.neConfig.requireUn', { display }),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**upfId可选择 */
|
||||||
|
const smfByUPFIdOptions = ref<{ value: string; label: string }[]>([]);
|
||||||
|
/**加载smf配置的upfId */
|
||||||
|
function smfByUPFIdLoadData(neId: string) {
|
||||||
|
getNeConfigData({
|
||||||
|
neType: 'SMF',
|
||||||
|
neId: neId,
|
||||||
|
paramName: 'upfConfig',
|
||||||
|
}).then(res => {
|
||||||
|
smfByUPFIdOptions.value = [];
|
||||||
|
for (const s of res.data) {
|
||||||
|
smfByUPFIdOptions.value.push({
|
||||||
|
value: s.id,
|
||||||
|
label: s.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
ruleVerification,
|
||||||
|
smfByUPFIdLoadData,
|
||||||
|
smfByUPFIdOptions,
|
||||||
|
};
|
||||||
|
}
|
||||||
951
src/views/ne/neConfig/index.vue
Normal file
951
src/views/ne/neConfig/index.vue
Normal file
@@ -0,0 +1,951 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, onMounted, toRaw, watch } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { message } from 'ant-design-vue/lib';
|
||||||
|
import { DataNode } from 'ant-design-vue/lib/tree';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import useOptions from './hooks/useOptions';
|
||||||
|
import useConfigList from './hooks/useConfigList';
|
||||||
|
import useConfigArray from './hooks/useConfigArray';
|
||||||
|
import useConfigArrayChild from './hooks/useConfigArrayChild';
|
||||||
|
import { getAllNeConfig, getNeConfigData } from '@/api/ne/neConfig';
|
||||||
|
const neInfoStore = useNeInfoStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { ruleVerification, smfByUPFIdLoadData, smfByUPFIdOptions } = useOptions({
|
||||||
|
t,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**网元类型_多neId */
|
||||||
|
let neCascaderOptions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
|
/**网元类型选择 type,id */
|
||||||
|
let neTypeSelect = ref<string[]>(['', '']);
|
||||||
|
|
||||||
|
/**左侧导航是否可收起 */
|
||||||
|
let collapsible = ref<boolean>(true);
|
||||||
|
|
||||||
|
/**改变收起状态 */
|
||||||
|
function changeCollapsible() {
|
||||||
|
collapsible.value = !collapsible.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对象信息状态类型 */
|
||||||
|
type TreeStateType = {
|
||||||
|
/**网元 loading */
|
||||||
|
loading: boolean;
|
||||||
|
/**网元配置 tree */
|
||||||
|
data: DataNode[];
|
||||||
|
/**选择对应Node一级 tree */
|
||||||
|
selectNode: {
|
||||||
|
title: string;
|
||||||
|
key: string;
|
||||||
|
// 可选属性数据
|
||||||
|
paramName: string;
|
||||||
|
paramDisplay: string;
|
||||||
|
paramType: string;
|
||||||
|
paramPerms: string[];
|
||||||
|
paramData: Record<string, any>[];
|
||||||
|
};
|
||||||
|
/**选择 loading */
|
||||||
|
selectLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
let treeState: TreeStateType = reactive({
|
||||||
|
loading: true,
|
||||||
|
data: [],
|
||||||
|
selectNode: {
|
||||||
|
paramName: '',
|
||||||
|
paramDisplay: '',
|
||||||
|
paramType: '',
|
||||||
|
paramPerms: [],
|
||||||
|
paramData: [],
|
||||||
|
// 树形节点需要有
|
||||||
|
title: '',
|
||||||
|
key: '',
|
||||||
|
},
|
||||||
|
selectLoading: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询可选命令列表 */
|
||||||
|
function fnSelectConfigNode(_: any, info: any) {
|
||||||
|
const { key } = info.node;
|
||||||
|
if (treeState.selectNode.paramName == key) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fnActiveConfigNode(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**列表项点击监听 */
|
||||||
|
function fnActiveConfigNode(key: string | number) {
|
||||||
|
listState.data = [];
|
||||||
|
arrayState.data = [];
|
||||||
|
treeState.selectLoading = true;
|
||||||
|
if (key === '#') {
|
||||||
|
key = treeState.selectNode.paramName;
|
||||||
|
} else {
|
||||||
|
treeState.selectNode.paramName = key as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const param = treeState.data.find(item => item.key === key);
|
||||||
|
if (!param) {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
treeState.selectNode = JSON.parse(JSON.stringify(param));
|
||||||
|
|
||||||
|
// 获取网元端的配置数据
|
||||||
|
getNeConfigData({
|
||||||
|
neType: neTypeSelect.value[0],
|
||||||
|
neId: neTypeSelect.value[1],
|
||||||
|
paramName: key,
|
||||||
|
}).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
const ruleArr = param.paramData;
|
||||||
|
const dataArr = res.data;
|
||||||
|
if (param.paramType === 'list') {
|
||||||
|
// 列表项数据
|
||||||
|
const dataList = [];
|
||||||
|
for (const item of dataArr) {
|
||||||
|
for (const key in item) {
|
||||||
|
// 规则为准
|
||||||
|
for (const rule of ruleArr) {
|
||||||
|
if (rule['name'] === key) {
|
||||||
|
const ruleItem = Object.assign(rule, {
|
||||||
|
optional: 'true',
|
||||||
|
value: item[key],
|
||||||
|
});
|
||||||
|
dataList.push(ruleItem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listState.data = dataList;
|
||||||
|
tablePagination.current = 1;
|
||||||
|
listEditClose();
|
||||||
|
}
|
||||||
|
if (param.paramType === 'array') {
|
||||||
|
// 列表项数据
|
||||||
|
const dataArray = [];
|
||||||
|
for (const item of dataArr) {
|
||||||
|
const index = item['index'];
|
||||||
|
let record: Record<string, any>[] = [];
|
||||||
|
for (const key in item) {
|
||||||
|
// 规则为准
|
||||||
|
for (const rule of ruleArr) {
|
||||||
|
if (rule['name'] === key) {
|
||||||
|
const ruleItem = Object.assign({ optional: 'true' }, rule, {
|
||||||
|
value: item[key],
|
||||||
|
});
|
||||||
|
record.push(ruleItem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataArray.push({ title: `Index-${index}`, key: index, record });
|
||||||
|
}
|
||||||
|
arrayState.data = dataArray;
|
||||||
|
// 无数据时,用于新增
|
||||||
|
arrayState.dataRule = { title: `Index-0`, key: 0, record: ruleArr };
|
||||||
|
|
||||||
|
// 列表数据
|
||||||
|
const columnsData: Record<string, any>[] = [];
|
||||||
|
for (const v of arrayState.data) {
|
||||||
|
const row: Record<string, any> = {};
|
||||||
|
for (const item of v.record) {
|
||||||
|
row[item.name] = item;
|
||||||
|
}
|
||||||
|
columnsData.push(row);
|
||||||
|
}
|
||||||
|
arrayState.columnsData = columnsData;
|
||||||
|
// 列表字段
|
||||||
|
const columns: Record<string, any>[] = [];
|
||||||
|
for (const rule of arrayState.dataRule.record) {
|
||||||
|
columns.push({
|
||||||
|
title: rule.display,
|
||||||
|
dataIndex: rule.name,
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 150,
|
||||||
|
minWidth: 100,
|
||||||
|
maxWidth: 350,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
columns.push({
|
||||||
|
title: t('common.operate'),
|
||||||
|
dataIndex: 'index',
|
||||||
|
key: 'index',
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 100,
|
||||||
|
});
|
||||||
|
arrayState.columns = columns;
|
||||||
|
tablePagination.current = 1;
|
||||||
|
arrayEditClose();
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
treeState.selectLoading = false;
|
||||||
|
}, 300);
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: `${param.paramDisplay} ${t(
|
||||||
|
'views.configManage.configParamForm.noConfigData'
|
||||||
|
)}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询配置可选属性值列表 */
|
||||||
|
function fnGetNeConfig() {
|
||||||
|
const neType = neTypeSelect.value[0];
|
||||||
|
if (!neType) {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.configManage.configParamForm.neTypePleace'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
treeState.loading = true;
|
||||||
|
// 获取数据
|
||||||
|
getAllNeConfig(neType).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
const arr = [];
|
||||||
|
for (const item of res.data) {
|
||||||
|
let paramPerms: string[] = [];
|
||||||
|
if (item.paramPerms) {
|
||||||
|
paramPerms = item.paramPerms.split(',');
|
||||||
|
} else {
|
||||||
|
paramPerms = ['post', 'put', 'delete'];
|
||||||
|
}
|
||||||
|
arr.push({
|
||||||
|
...item,
|
||||||
|
children: undefined,
|
||||||
|
title: item.paramDisplay,
|
||||||
|
key: item.paramName,
|
||||||
|
paramPerms,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
treeState.data = arr;
|
||||||
|
treeState.loading = false;
|
||||||
|
// 取首个tag
|
||||||
|
if (res.data.length > 0) {
|
||||||
|
const item = JSON.parse(JSON.stringify(treeState.data[0]));
|
||||||
|
treeState.selectNode = item;
|
||||||
|
treeState.selectLoading = false;
|
||||||
|
fnActiveConfigNode(item.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**添加框是否显示 */
|
||||||
|
visible: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**表单数据 */
|
||||||
|
from: Record<string, any>;
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
/**项类型 */
|
||||||
|
type: 'arrayAdd' | 'arrayEdit' | 'arrayChildAdd' | 'arrayChildEdit';
|
||||||
|
/**项Key */
|
||||||
|
key: string | number;
|
||||||
|
/**项数据 */
|
||||||
|
data: Record<string, any>[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
visible: false,
|
||||||
|
title: 'Item',
|
||||||
|
from: {},
|
||||||
|
confirmLoading: false,
|
||||||
|
type: 'arrayAdd',
|
||||||
|
key: '',
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出确认执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalOk() {
|
||||||
|
const from = toRaw(modalState.from);
|
||||||
|
if (modalState.type === 'arrayAdd') {
|
||||||
|
arrayAddOk(from);
|
||||||
|
}
|
||||||
|
if (modalState.type === 'arrayEdit') {
|
||||||
|
arrayEditOk(from);
|
||||||
|
}
|
||||||
|
if (modalState.type === 'arrayChildAdd') {
|
||||||
|
arrayChildAddOk(from);
|
||||||
|
}
|
||||||
|
if (modalState.type === 'arrayChildEdit') {
|
||||||
|
arrayChildEditOk(from);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出关闭执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalCancel() {
|
||||||
|
modalState.visible = false;
|
||||||
|
modalState.from = {};
|
||||||
|
modalState.type = 'arrayAdd';
|
||||||
|
modalState.key = '';
|
||||||
|
modalState.data = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听新增编辑弹窗
|
||||||
|
watch(
|
||||||
|
() => modalState.visible,
|
||||||
|
val => {
|
||||||
|
// SMF需要选择配置的UPF id
|
||||||
|
if (val && neTypeSelect.value[0] === 'SMF') {
|
||||||
|
smfByUPFIdLoadData(neTypeSelect.value[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const { tablePagination, listState, listEdit, listEditClose, listEditOk } =
|
||||||
|
useConfigList({ t, treeState, neTypeSelect, ruleVerification });
|
||||||
|
|
||||||
|
const {
|
||||||
|
arrayState,
|
||||||
|
arrayEdit,
|
||||||
|
arrayEditClose,
|
||||||
|
arrayEditOk,
|
||||||
|
arrayDelete,
|
||||||
|
arrayAdd,
|
||||||
|
arrayAddOk,
|
||||||
|
arrayInitEdit,
|
||||||
|
arrayInitAdd,
|
||||||
|
} = useConfigArray({
|
||||||
|
t,
|
||||||
|
treeState,
|
||||||
|
neTypeSelect,
|
||||||
|
fnActiveConfigNode,
|
||||||
|
ruleVerification,
|
||||||
|
modalState,
|
||||||
|
fnModalCancel,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
arrayChildState,
|
||||||
|
arrayChildExpand,
|
||||||
|
arrayChildEdit,
|
||||||
|
arrayChildEditOk,
|
||||||
|
arrayChildDelete,
|
||||||
|
arrayChildAdd,
|
||||||
|
arrayChildAddOk,
|
||||||
|
} = useConfigArrayChild({
|
||||||
|
t,
|
||||||
|
treeState,
|
||||||
|
neTypeSelect,
|
||||||
|
fnActiveConfigNode,
|
||||||
|
ruleVerification,
|
||||||
|
modalState,
|
||||||
|
arrayState,
|
||||||
|
arrayInitEdit,
|
||||||
|
arrayInitAdd,
|
||||||
|
arrayEditClose,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 获取网元网元列表
|
||||||
|
neInfoStore.fnNelist().then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length > 0) {
|
||||||
|
// 过滤不可用的网元
|
||||||
|
neCascaderOptions.value = neInfoStore.getNeSelectOtions.filter(
|
||||||
|
(item: any) => {
|
||||||
|
return !['LMF', 'NEF'].includes(item.value);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (neCascaderOptions.value.length === 0) {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 默认选择AMF
|
||||||
|
const item = neCascaderOptions.value.find(s => s.value === 'AMF');
|
||||||
|
if (item && item.children) {
|
||||||
|
const info = item.children[0];
|
||||||
|
neTypeSelect.value = [info.neType, info.neId];
|
||||||
|
} else {
|
||||||
|
const info = neCascaderOptions.value[0].children[0];
|
||||||
|
neTypeSelect.value = [info.neType, info.neId];
|
||||||
|
}
|
||||||
|
fnGetNeConfig();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col
|
||||||
|
:lg="6"
|
||||||
|
:md="6"
|
||||||
|
:xs="24"
|
||||||
|
style="margin-bottom: 24px"
|
||||||
|
v-show="collapsible"
|
||||||
|
>
|
||||||
|
<!-- 网元类型 -->
|
||||||
|
<a-card size="small" :bordered="false" :loading="treeState.loading">
|
||||||
|
<template #title>
|
||||||
|
{{ t('views.configManage.configParamForm.treeTitle') }}
|
||||||
|
</template>
|
||||||
|
<a-form layout="vertical" autocomplete="off">
|
||||||
|
<a-form-item name="neId ">
|
||||||
|
<a-cascader
|
||||||
|
v-model:value="neTypeSelect"
|
||||||
|
:options="neCascaderOptions"
|
||||||
|
:allow-clear="false"
|
||||||
|
@change="fnGetNeConfig"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="listeningPort">
|
||||||
|
<a-tree
|
||||||
|
:tree-data="treeState.data"
|
||||||
|
:selected-keys="[treeState.selectNode.paramName]"
|
||||||
|
@select="fnSelectConfigNode"
|
||||||
|
>
|
||||||
|
</a-tree>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="collapsible ? 18 : 24" :md="collapsible ? 18 : 24" :xs="24">
|
||||||
|
<a-card
|
||||||
|
size="small"
|
||||||
|
:bordered="false"
|
||||||
|
:body-style="{ maxHeight: '650px', 'overflow-y': 'auto' }"
|
||||||
|
:loading="treeState.selectLoading"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<a-button
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
@click.prevent="changeCollapsible()"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<MenuFoldOutlined v-show="collapsible" />
|
||||||
|
<MenuUnfoldOutlined v-show="!collapsible" />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
|
||||||
|
<a-typography-text strong v-if="treeState.selectNode.paramDisplay">
|
||||||
|
{{ treeState.selectNode.paramDisplay }}
|
||||||
|
</a-typography-text>
|
||||||
|
<a-typography-text type="danger" v-else>
|
||||||
|
{{ t('views.configManage.configParamForm.treeSelectTip') }}
|
||||||
|
</a-typography-text>
|
||||||
|
</template>
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center" v-show="!treeState.selectLoading">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
size="small"
|
||||||
|
@click.prevent="fnActiveConfigNode('#')"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<ReloadOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 单列表格列表 -->
|
||||||
|
<a-table
|
||||||
|
v-if="treeState.selectNode.paramType === 'list'"
|
||||||
|
class="table"
|
||||||
|
row-key="name"
|
||||||
|
:size="listState.size"
|
||||||
|
:columns="listState.columns"
|
||||||
|
:data-source="listState.data"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:bordered="true"
|
||||||
|
:scroll="{ x: true, y: '500px' }"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, text, record }">
|
||||||
|
<template v-if="column.dataIndex === 'value'">
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title v-if="record.comment">
|
||||||
|
{{ record.comment }}
|
||||||
|
</template>
|
||||||
|
<div class="editable-cell">
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
listState.editRecord['display'] === record['display']
|
||||||
|
"
|
||||||
|
class="editable-cell__input-wrapper"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-if="record['type'] === 'int'"
|
||||||
|
v-model:value="listState.editRecord['value']"
|
||||||
|
:disabled="listState.confirmLoading"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-input-number>
|
||||||
|
<a-switch
|
||||||
|
v-else-if="record['type'] === 'bool'"
|
||||||
|
v-model:checked="listState.editRecord['value']"
|
||||||
|
:checked-children="t('common.switch.open')"
|
||||||
|
:un-checked-children="t('common.switch.shut')"
|
||||||
|
:disabled="listState.confirmLoading"
|
||||||
|
></a-switch>
|
||||||
|
<a-select
|
||||||
|
v-else-if="record['type'] === 'enum'"
|
||||||
|
v-model:value="listState.editRecord['value']"
|
||||||
|
:allow-clear="true"
|
||||||
|
:disabled="listState.confirmLoading"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
:value="+v"
|
||||||
|
:key="+v"
|
||||||
|
v-for="(k, v) in JSON.parse(record['filter'])"
|
||||||
|
>
|
||||||
|
{{ k }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<a-input
|
||||||
|
v-else
|
||||||
|
v-model:value="listState.editRecord['value']"
|
||||||
|
:disabled="listState.confirmLoading"
|
||||||
|
></a-input>
|
||||||
|
<a-space
|
||||||
|
:size="8"
|
||||||
|
align="center"
|
||||||
|
direction="horizontal"
|
||||||
|
style="margin-left: 18px"
|
||||||
|
>
|
||||||
|
<a-tooltip placement="bottomRight">
|
||||||
|
<template #title> {{ t('common.ok') }} </template>
|
||||||
|
<a-popconfirm
|
||||||
|
:title="
|
||||||
|
t(
|
||||||
|
'views.configManage.configParamForm.editOkTip',
|
||||||
|
{ num: record['display'] }
|
||||||
|
)
|
||||||
|
"
|
||||||
|
placement="topRight"
|
||||||
|
:disabled="listState.confirmLoading"
|
||||||
|
@confirm="listEditOk()"
|
||||||
|
>
|
||||||
|
<a-button
|
||||||
|
type="text"
|
||||||
|
class="editable-cell__icon-edit"
|
||||||
|
:disabled="listState.confirmLoading"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<CheckOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip placement="bottomRight">
|
||||||
|
<template #title> {{ t('common.cancel') }} </template>
|
||||||
|
<a-button
|
||||||
|
type="text"
|
||||||
|
class="editable-cell__icon-edit"
|
||||||
|
:disabled="listState.confirmLoading"
|
||||||
|
@click.prevent="listEditClose()"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<CloseOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="editable-cell__text-wrapper"
|
||||||
|
@dblclick="listEdit(record)"
|
||||||
|
>
|
||||||
|
<template v-if="record['type'] === 'enum'">
|
||||||
|
{{ JSON.parse(record['filter'])[text] || ' ' }}
|
||||||
|
</template>
|
||||||
|
<template v-else>{{ `${text}` || ' ' }}</template>
|
||||||
|
<EditOutlined
|
||||||
|
class="editable-cell__icon"
|
||||||
|
@click="listEdit(record)"
|
||||||
|
style="margin-left: 18px"
|
||||||
|
v-if="
|
||||||
|
!listState.confirmLoading &&
|
||||||
|
!['read-only', 'read', 'ro'].includes(record.access)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
|
||||||
|
<!-- array类型 -->
|
||||||
|
<template v-if="treeState.selectNode.paramType === 'array'">
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="index"
|
||||||
|
:columns="treeState.selectNode.paramPerms.includes('get') ? arrayState.columnsDnd.filter((s:any)=>s.key !== 'index') : arrayState.columnsDnd"
|
||||||
|
:data-source="arrayState.columnsData"
|
||||||
|
:size="arrayState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:bordered="true"
|
||||||
|
:scroll="{ x: arrayState.columnsDnd.length * 200, y: 480 }"
|
||||||
|
@resizeColumn="(w:number, col:any) => (col.width = w)"
|
||||||
|
:show-expand-column="false"
|
||||||
|
v-model:expanded-row-keys="arrayState.arrayChildExpandKeys"
|
||||||
|
>
|
||||||
|
<!-- 多列新增操作 -->
|
||||||
|
<template #title>
|
||||||
|
<a-space :size="16" align="center">
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
@click.prevent="arrayAdd()"
|
||||||
|
size="small"
|
||||||
|
v-if="treeState.selectNode.paramPerms.includes('post')"
|
||||||
|
>
|
||||||
|
<template #icon> <PlusOutlined /> </template>
|
||||||
|
{{ t('common.addText') }}
|
||||||
|
</a-button>
|
||||||
|
<TableColumnsDnd
|
||||||
|
type="ghost"
|
||||||
|
:cache-id="treeState.selectNode.key"
|
||||||
|
:columns="treeState.selectNode.paramPerms.includes('get') ? [...arrayState.columns.filter((s:any)=>s.key !== 'index')] : arrayState.columns"
|
||||||
|
v-model:columns-dnd="arrayState.columnsDnd"
|
||||||
|
></TableColumnsDnd>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 多列数据渲染 -->
|
||||||
|
<template #bodyCell="{ column, text, record }">
|
||||||
|
<template v-if="column?.key === 'index'">
|
||||||
|
<a-space :size="16" align="center">
|
||||||
|
<a-tooltip
|
||||||
|
v-if="treeState.selectNode.paramPerms.includes('put')"
|
||||||
|
>
|
||||||
|
<template #title>{{ t('common.editText') }}</template>
|
||||||
|
<a-button type="link" @click.prevent="arrayEdit(text)">
|
||||||
|
<template #icon><FormOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip
|
||||||
|
v-if="
|
||||||
|
treeState.selectNode.paramPerms.includes('delete') &&
|
||||||
|
!(
|
||||||
|
neTypeSelect[0] === 'IMS' &&
|
||||||
|
treeState.selectNode.paramName === 'plmn' &&
|
||||||
|
text['value'] === 0
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
|
<a-button type="link" @click.prevent="arrayDelete(text)">
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="text">
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title v-if="text.comment">
|
||||||
|
{{ text.comment }}
|
||||||
|
</template>
|
||||||
|
<div class="editable-cell">
|
||||||
|
<template v-if="text.array">
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
size="small"
|
||||||
|
@click.prevent="
|
||||||
|
arrayChildExpand(record['index'], text)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #icon><BarsOutlined /></template>
|
||||||
|
{{
|
||||||
|
t('views.configManage.configParamForm.arrayMore')
|
||||||
|
}}
|
||||||
|
</a-button>
|
||||||
|
<!--特殊字段拓展显示-->
|
||||||
|
<span
|
||||||
|
v-if="
|
||||||
|
text.name === 'dnnList' && Array.isArray(text.value)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
({{
|
||||||
|
text.value.length > 4
|
||||||
|
? `${text.value
|
||||||
|
.slice(0, 3)
|
||||||
|
.map((s: any) => s.dnn)
|
||||||
|
.join()}...${text.value.length}`
|
||||||
|
: text.value.map((s: any) => s.dnn).join()
|
||||||
|
}})
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div v-else class="editable-cell__text-wrapper">
|
||||||
|
<template v-if="text['type'] === 'enum'">
|
||||||
|
{{ JSON.parse(text['filter'])[text.value] }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ `${text.value}` || ' ' }}
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 多列嵌套类型 -->
|
||||||
|
<template #expandedRowRender>
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="index"
|
||||||
|
:columns="arrayChildState.columnsDnd"
|
||||||
|
:data-source="arrayChildState.columnsData"
|
||||||
|
:size="arrayChildState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:bordered="true"
|
||||||
|
:scroll="{
|
||||||
|
x: arrayChildState.columnsDnd.length * 200,
|
||||||
|
y: 200,
|
||||||
|
}"
|
||||||
|
@resizeColumn="(w:number, col:any) => (col.width = w)"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<a-space :size="16" align="center">
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
@click.prevent="arrayChildAdd"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<template #icon> <PlusOutlined /> </template>
|
||||||
|
{{ t('common.addText') }} {{ arrayChildState.title }}
|
||||||
|
</a-button>
|
||||||
|
<TableColumnsDnd
|
||||||
|
type="ghost"
|
||||||
|
:cache-id="`${treeState.selectNode.key}:${arrayChildState.loc}`"
|
||||||
|
:columns="[...arrayChildState.columns]"
|
||||||
|
v-model:columns-dnd="arrayChildState.columnsDnd"
|
||||||
|
v-if="arrayChildState.loc"
|
||||||
|
></TableColumnsDnd>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column, text, record }">
|
||||||
|
<template v-if="column?.key === 'index'">
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.editText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="arrayChildEdit(text)"
|
||||||
|
>
|
||||||
|
<template #icon><FormOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>
|
||||||
|
{{ t('common.deleteText') }}
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="arrayChildDelete(text)"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="text">
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title v-if="text.comment">
|
||||||
|
{{ text.comment }}
|
||||||
|
</template>
|
||||||
|
<div class="editable-cell">
|
||||||
|
<template v-if="text.array">
|
||||||
|
<a-button type="default" size="small">
|
||||||
|
<template #icon><BarsOutlined /></template>
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
'views.configManage.configParamForm.arrayMore'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<template v-if="text['type'] === 'enum'">
|
||||||
|
{{ JSON.parse(text['filter'])[text.value] }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ `${text.value}` || ' ' }}
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</template>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<!-- 新增框或修改框 -->
|
||||||
|
<ProModal
|
||||||
|
:drag="true"
|
||||||
|
:width="800"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
:body-style="{ maxHeight: '650px', 'overflow-y': 'auto' }"
|
||||||
|
:keyboard="false"
|
||||||
|
:mask-closable="false"
|
||||||
|
:visible="modalState.visible"
|
||||||
|
:title="modalState.title"
|
||||||
|
:confirm-loading="modalState.confirmLoading"
|
||||||
|
@ok="fnModalOk"
|
||||||
|
@cancel="fnModalCancel"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
class="form"
|
||||||
|
layout="horizontal"
|
||||||
|
autocomplete="off"
|
||||||
|
:validate-on-rule-change="false"
|
||||||
|
:validateTrigger="[]"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:labelWrap="true"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
v-for="item in modalState.data"
|
||||||
|
:label="item.display"
|
||||||
|
:name="item.name"
|
||||||
|
:required="item.optional === 'false'"
|
||||||
|
style="margin-bottom: 4px"
|
||||||
|
>
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title v-if="item.comment">
|
||||||
|
{{ item.comment }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
!Array.isArray(item.array) &&
|
||||||
|
modalState.from[item.name] !== undefined
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<!-- 特殊SMF-upfid选择 -->
|
||||||
|
<a-select
|
||||||
|
v-if="
|
||||||
|
neTypeSelect[0] === 'SMF' &&
|
||||||
|
modalState.from[item.name]['name'] === 'upfId'
|
||||||
|
"
|
||||||
|
v-model:value="modalState.from[item.name]['value']"
|
||||||
|
:options="smfByUPFIdOptions"
|
||||||
|
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
|
||||||
|
:token-separators="[',', ';']"
|
||||||
|
mode="multiple"
|
||||||
|
:max-tag-count="5"
|
||||||
|
:allow-clear="true"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
</a-select>
|
||||||
|
<!-- 常规 -->
|
||||||
|
<a-input-number
|
||||||
|
v-else-if="item['type'] === 'int'"
|
||||||
|
v-model:value="modalState.from[item.name]['value']"
|
||||||
|
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-input-number>
|
||||||
|
<a-switch
|
||||||
|
v-else-if="item['type'] === 'bool'"
|
||||||
|
v-model:checked="modalState.from[item.name]['value']"
|
||||||
|
:checked-children="t('common.switch.open')"
|
||||||
|
:un-checked-children="t('common.switch.shut')"
|
||||||
|
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
|
||||||
|
></a-switch>
|
||||||
|
<a-select
|
||||||
|
v-else-if="item['type'] === 'enum'"
|
||||||
|
v-model:value="modalState.from[item.name]['value']"
|
||||||
|
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
|
||||||
|
:allow-clear="true"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
:value="+v"
|
||||||
|
:key="+v"
|
||||||
|
v-for="(k, v) in JSON.parse(item['filter'])"
|
||||||
|
>
|
||||||
|
{{ k }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<a-input
|
||||||
|
v-else
|
||||||
|
v-model:value="modalState.from[item.name]['value']"
|
||||||
|
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
|
||||||
|
></a-input>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
{{ `${item.value || ' '}` }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</ProModal>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.editable-cell {
|
||||||
|
&__icon {
|
||||||
|
display: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
&__icon:hover {
|
||||||
|
color: var(--ant-primary-color);
|
||||||
|
}
|
||||||
|
&__icon-edit:hover {
|
||||||
|
color: var(--ant-primary-color);
|
||||||
|
}
|
||||||
|
&__text-wrapper {
|
||||||
|
font-size: 16px;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
&__text-wrapper:hover &__icon {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
&__input-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -281,9 +281,7 @@ function fnModalCancel() {
|
|||||||
emit('update:visible', false);
|
emit('update:visible', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**表单修改网元类型 */
|
||||||
* 表单修改网元类型
|
|
||||||
*/
|
|
||||||
function fnNeTypeChange(v: any) {
|
function fnNeTypeChange(v: any) {
|
||||||
const hostsLen = modalState.from.hosts.length;
|
const hostsLen = modalState.from.hosts.length;
|
||||||
// 网元默认只含22和4100
|
// 网元默认只含22和4100
|
||||||
@@ -305,11 +303,18 @@ function fnNeTypeChange(v: any) {
|
|||||||
remark: '',
|
remark: '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
modalState.from.rmUid = `4400HX${v}${modalState.from.neId}`; // 4400HX1AMF001
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**表单修改网元neId */
|
||||||
* 表单修改网元IP
|
function fnNeIdChange(e: any) {
|
||||||
*/
|
const v = e.target.value;
|
||||||
|
if (v.length < 1) return;
|
||||||
|
modalState.from.rmUid = `4400HX${modalState.from.neType}${v}`; // 4400HX1AMF001
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表单修改网元IP */
|
||||||
function fnNeIPChange(e: any) {
|
function fnNeIPChange(e: any) {
|
||||||
const v = e.target.value;
|
const v = e.target.value;
|
||||||
if (v.length < 7) return;
|
if (v.length < 7) return;
|
||||||
@@ -428,6 +433,7 @@ onMounted(() => {
|
|||||||
allow-clear
|
allow-clear
|
||||||
:placeholder="t('common.inputPlease')"
|
:placeholder="t('common.inputPlease')"
|
||||||
:maxlength="32"
|
:maxlength="32"
|
||||||
|
@change="fnNeIdChange"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<a-tooltip placement="topLeft">
|
<a-tooltip placement="topLeft">
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ let modalState: ModalStateType = reactive({
|
|||||||
title: 'OAM Configuration',
|
title: 'OAM Configuration',
|
||||||
sync: true,
|
sync: true,
|
||||||
from: {
|
from: {
|
||||||
|
omcIP: '',
|
||||||
oamEnable: true,
|
oamEnable: true,
|
||||||
oamPort: 33030,
|
oamPort: 33030,
|
||||||
snmpEnable: true,
|
snmpEnable: true,
|
||||||
@@ -77,6 +78,7 @@ function fnModalVisibleByTypeAndId(neType: string, neId: string) {
|
|||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
const data = res.data;
|
const data = res.data;
|
||||||
Object.assign(modalState.from, {
|
Object.assign(modalState.from, {
|
||||||
|
omcIP: data.oamConfig[data.oamConfig.ipType],
|
||||||
oamEnable: data.oamConfig.enable,
|
oamEnable: data.oamConfig.enable,
|
||||||
oamPort: data.oamConfig.port,
|
oamPort: data.oamConfig.port,
|
||||||
snmpEnable: data.snmpConfig.enable,
|
snmpEnable: data.snmpConfig.enable,
|
||||||
@@ -224,6 +226,17 @@ watch(
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neInfo.oam.omcIP')"
|
||||||
|
name="omcIP"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:labelWrap="true"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.omcIP"
|
||||||
|
:maxlength="128"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
<a-collapse-panel header="SNMP">
|
<a-collapse-panel header="SNMP">
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
|
|||||||
@@ -237,7 +237,9 @@ function fnModalEditOk(from: Record<string, any>) {
|
|||||||
item.neName = from.neName;
|
item.neName = from.neName;
|
||||||
item.ip = from.ip;
|
item.ip = from.ip;
|
||||||
item.port = from.port;
|
item.port = from.port;
|
||||||
item.status = res.data.online ? '1' : '0';
|
if (item.status !== '2') {
|
||||||
|
item.status = res.data.online ? '1' : '0';
|
||||||
|
}
|
||||||
Object.assign(item.serverState, res.data);
|
Object.assign(item.serverState, res.data);
|
||||||
const resouresUsage = parseResouresUsage(item.serverState);
|
const resouresUsage = parseResouresUsage(item.serverState);
|
||||||
Reflect.set(item, 'resoures', resouresUsage);
|
Reflect.set(item, 'resoures', resouresUsage);
|
||||||
@@ -364,8 +366,18 @@ function fnGetList(pageNum?: number) {
|
|||||||
tablePagination.total = res.total;
|
tablePagination.total = res.total;
|
||||||
// 遍历处理资源情况数值
|
// 遍历处理资源情况数值
|
||||||
tableState.data = res.rows.map(item => {
|
tableState.data = res.rows.map(item => {
|
||||||
|
let resouresUsage = {
|
||||||
|
sysDiskUsage: 0,
|
||||||
|
sysMemUsage: 0,
|
||||||
|
sysCpuUsage: 0,
|
||||||
|
nfCpuUsage: 0,
|
||||||
|
};
|
||||||
const neState = item.serverState;
|
const neState = item.serverState;
|
||||||
const resouresUsage = parseResouresUsage(neState);
|
if (neState) {
|
||||||
|
resouresUsage = parseResouresUsage(neState);
|
||||||
|
} else {
|
||||||
|
item.serverState = { online: false };
|
||||||
|
}
|
||||||
Reflect.set(item, 'resoures', resouresUsage);
|
Reflect.set(item, 'resoures', resouresUsage);
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
@@ -380,33 +392,27 @@ function parseResouresUsage(neState: Record<string, any>) {
|
|||||||
let nfCpuUsage = 0;
|
let nfCpuUsage = 0;
|
||||||
if (neState.cpu) {
|
if (neState.cpu) {
|
||||||
nfCpuUsage = neState.cpu.nfCpuUsage;
|
nfCpuUsage = neState.cpu.nfCpuUsage;
|
||||||
|
const nfCpu = +(nfCpuUsage / 100);
|
||||||
|
nfCpuUsage = +nfCpu.toFixed(2);
|
||||||
if (nfCpuUsage > 100) {
|
if (nfCpuUsage > 100) {
|
||||||
const nfCpu = +(neState.cpu.nfCpuUsage / 100);
|
nfCpuUsage = 100;
|
||||||
if (nfCpu > 100) {
|
|
||||||
nfCpuUsage = 100;
|
|
||||||
} else {
|
|
||||||
nfCpuUsage = +nfCpu.toFixed(2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sysCpuUsage = neState.cpu.sysCpuUsage;
|
sysCpuUsage = neState.cpu.sysCpuUsage;
|
||||||
|
const sysCpu = +(sysCpuUsage / 100);
|
||||||
|
sysCpuUsage = +sysCpu.toFixed(2);
|
||||||
if (sysCpuUsage > 100) {
|
if (sysCpuUsage > 100) {
|
||||||
const sysCpu = +(neState.cpu.sysCpuUsage / 100);
|
sysCpuUsage = 100;
|
||||||
if (sysCpu > 100) {
|
|
||||||
sysCpuUsage = 100;
|
|
||||||
} else {
|
|
||||||
sysCpuUsage = +sysCpu.toFixed(2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let sysMemUsage = 0;
|
let sysMemUsage = 0;
|
||||||
if (neState.mem) {
|
if (neState.mem) {
|
||||||
let men = neState.mem.sysMemUsage;
|
const men = neState.mem.sysMemUsage;
|
||||||
if (men > 100) {
|
sysMemUsage = +(men / 100).toFixed(2);
|
||||||
men = +(men / 100).toFixed(2);
|
if (sysMemUsage > 100) {
|
||||||
|
sysMemUsage = 100;
|
||||||
}
|
}
|
||||||
sysMemUsage = men;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let sysDiskUsage = 0;
|
let sysDiskUsage = 0;
|
||||||
@@ -625,7 +631,10 @@ onMounted(() => {
|
|||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
{{ t('common.deleteText') }}
|
{{ t('common.deleteText') }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="oam">
|
<a-menu-item
|
||||||
|
key="oam"
|
||||||
|
v-if="!['OMC'].includes(record.neType)"
|
||||||
|
>
|
||||||
<FileTextOutlined />
|
<FileTextOutlined />
|
||||||
{{ t('views.ne.common.oam') }}
|
{{ t('views.ne.common.oam') }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
|
|||||||
@@ -47,13 +47,6 @@ function fnNext() {
|
|||||||
|
|
||||||
<div style="padding: 24px 12px 0; text-align: end">
|
<div style="padding: 24px 12px 0; text-align: end">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
<a-button
|
|
||||||
type="primary"
|
|
||||||
:loading="state.confirmLoading"
|
|
||||||
@click="fnNext()"
|
|
||||||
>
|
|
||||||
{{ t('views.ne.neQuickSetup.stepNext') }}
|
|
||||||
</a-button>
|
|
||||||
<a-button
|
<a-button
|
||||||
type="default"
|
type="default"
|
||||||
:disabled="state.confirmLoading"
|
:disabled="state.confirmLoading"
|
||||||
@@ -62,6 +55,13 @@ function fnNext() {
|
|||||||
<template #icon><ReloadOutlined /></template>
|
<template #icon><ReloadOutlined /></template>
|
||||||
{{ t('views.ne.neQuickSetup.reloadPara5G') }}
|
{{ t('views.ne.neQuickSetup.reloadPara5G') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
:loading="state.confirmLoading"
|
||||||
|
@click="fnNext()"
|
||||||
|
>
|
||||||
|
{{ t('views.ne.neQuickSetup.stepNext') }}
|
||||||
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|||||||
@@ -294,8 +294,7 @@ function fnRecordVersion(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (row.newVersion === row.version) {
|
if (row.newVersion === row.version) {
|
||||||
message.warning(t('views.ne.neVersion.upgradeTipEqual'), 3);
|
contentTip = t('views.ne.neVersion.upgradeTipEqual');
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (action === 'rollback') {
|
if (action === 'rollback') {
|
||||||
@@ -305,8 +304,7 @@ function fnRecordVersion(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (row.preVersion === row.version) {
|
if (row.preVersion === row.version) {
|
||||||
message.warning(t('views.ne.neVersion.rollbackTipEqual'), 3);
|
contentTip = t('views.ne.neVersion.rollbackTipEqual');
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -194,8 +194,6 @@ type ModalStateType = {
|
|||||||
/**表单数据 */
|
/**表单数据 */
|
||||||
from: Record<string, any>;
|
from: Record<string, any>;
|
||||||
/**表单数据 */
|
/**表单数据 */
|
||||||
BatchForm: Record<string, any>;
|
|
||||||
/**表单数据 */
|
|
||||||
BatchDelForm: Record<string, any>;
|
BatchDelForm: Record<string, any>;
|
||||||
/**确定按钮 loading */
|
/**确定按钮 loading */
|
||||||
confirmLoading: boolean;
|
confirmLoading: boolean;
|
||||||
@@ -210,15 +208,8 @@ let modalState: ModalStateType = reactive({
|
|||||||
visibleByBatchDel: false,
|
visibleByBatchDel: false,
|
||||||
title: 'UDM鉴权用户',
|
title: 'UDM鉴权用户',
|
||||||
from: {
|
from: {
|
||||||
id: '',
|
|
||||||
imsi: '',
|
|
||||||
amf: '8000',
|
|
||||||
ki: '',
|
|
||||||
algoIndex: 0,
|
|
||||||
opc: '',
|
|
||||||
},
|
|
||||||
BatchForm: {
|
|
||||||
num: 1,
|
num: 1,
|
||||||
|
id: '',
|
||||||
imsi: '',
|
imsi: '',
|
||||||
amf: '8000',
|
amf: '8000',
|
||||||
ki: '',
|
ki: '',
|
||||||
@@ -236,19 +227,6 @@ let modalState: ModalStateType = reactive({
|
|||||||
/**对话框内表单属性和校验规则 */
|
/**对话框内表单属性和校验规则 */
|
||||||
const modalStateFrom = Form.useForm(
|
const modalStateFrom = Form.useForm(
|
||||||
modalState.from,
|
modalState.from,
|
||||||
reactive({
|
|
||||||
imsi: [{ required: true, message: 'IMSI' + t('common.unableNull') }],
|
|
||||||
amf: [{ required: true, message: 'AMF' + t('common.unableNull') }],
|
|
||||||
ki: [{ required: true, message: 'KI' + t('common.unableNull') }],
|
|
||||||
algoIndex: [
|
|
||||||
{ required: true, message: 'algoIndex' + t('common.unableNull') },
|
|
||||||
],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
/**对话框内批量添加表单属性和校验规则 */
|
|
||||||
const modalStateBatchFrom = Form.useForm(
|
|
||||||
modalState.BatchForm,
|
|
||||||
reactive({
|
reactive({
|
||||||
num: [
|
num: [
|
||||||
{
|
{
|
||||||
@@ -256,9 +234,15 @@ const modalStateBatchFrom = Form.useForm(
|
|||||||
message: t('views.neUser.auth.numAdd') + t('common.unableNull'),
|
message: t('views.neUser.auth.numAdd') + t('common.unableNull'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
imsi: [{ required: true, message: 'IMSI' + t('common.unableNull') }],
|
imsi: [
|
||||||
|
{ required: true, message: 'IMSI' + t('common.unableNull') },
|
||||||
|
{ min: 15, max: 15, message: t('views.neUser.auth.imsiConfirm') },
|
||||||
|
],
|
||||||
amf: [{ required: true, message: 'AMF' + t('common.unableNull') }],
|
amf: [{ required: true, message: 'AMF' + t('common.unableNull') }],
|
||||||
ki: [{ required: true, message: 'KI' + t('common.unableNull') }],
|
ki: [
|
||||||
|
{ required: true, message: 'KI' + t('common.unableNull') },
|
||||||
|
{ min: 32, max: 32, message: t('views.neUser.auth.kiTip') },
|
||||||
|
],
|
||||||
algoIndex: [
|
algoIndex: [
|
||||||
{ required: true, message: 'algoIndex' + t('common.unableNull') },
|
{ required: true, message: 'algoIndex' + t('common.unableNull') },
|
||||||
],
|
],
|
||||||
@@ -312,21 +296,14 @@ function fnModalVisibleByEdit(row?: Record<string, any>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对话框弹出显示为 批量新增,批量删除
|
* 对话框弹出显示为 批量删除
|
||||||
* @param noticeId 网元id, 不传为新增
|
* @param noticeId 网元id, 不传为新增
|
||||||
*/
|
*/
|
||||||
function fnModalVisibleByBatch(batchFlag?: number) {
|
function fnModalVisibleByBatch() {
|
||||||
if (batchFlag) {
|
modalStateBatchDelFrom.resetFields(); //重置表单
|
||||||
modalStateBatchFrom.resetFields(); //重置表单
|
modalState.title =
|
||||||
modalState.title =
|
t('views.neUser.auth.batchDelText') + t('views.neUser.auth.authInfo');
|
||||||
t('views.neUser.auth.batchAddText') + t('views.neUser.auth.authInfo');
|
modalState.visibleByBatchDel = true;
|
||||||
modalState.visibleByBatch = true;
|
|
||||||
} else {
|
|
||||||
modalStateBatchFrom.resetFields(); //重置表单
|
|
||||||
modalState.title =
|
|
||||||
t('views.neUser.auth.batchDelText') + t('views.neUser.auth.authInfo');
|
|
||||||
modalState.visibleByBatchDel = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -341,23 +318,52 @@ function fnModalOk() {
|
|||||||
const from = toRaw(modalState.from);
|
const from = toRaw(modalState.from);
|
||||||
from.neId = queryParams.neId || '-';
|
from.neId = queryParams.neId || '-';
|
||||||
from.algoIndex = `${from.algoIndex}`;
|
from.algoIndex = `${from.algoIndex}`;
|
||||||
const result = from.id ? updateUDMAuth(from) : addUDMAuth(from);
|
const result = from.id
|
||||||
|
? updateUDMAuth(from)
|
||||||
|
: from.num === 1
|
||||||
|
? addUDMAuth(from)
|
||||||
|
: batchAddUDMAuth(from, from.num);
|
||||||
const hide = message.loading(t('common.loading'), 0);
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
result
|
result
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
message.success({
|
if (from.num === 1) {
|
||||||
content: t('common.msgSuccess', { msg: modalState.title }),
|
//新增时
|
||||||
duration: 3,
|
message.success({
|
||||||
});
|
content: t('common.msgSuccess', { msg: modalState.title }),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnGetList();
|
||||||
|
} else {
|
||||||
|
//批量新增时
|
||||||
|
const timerS = Math.max(
|
||||||
|
Math.ceil(+from.num / 500),
|
||||||
|
`${from.num}`.length
|
||||||
|
);
|
||||||
|
notification.success({
|
||||||
|
message: modalState.title,
|
||||||
|
description: t('common.operateOk'),
|
||||||
|
duration: timerS,
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
fnGetList(1);
|
||||||
|
}, timerS * 1000);
|
||||||
|
}
|
||||||
modalState.visibleByEdit = false;
|
modalState.visibleByEdit = false;
|
||||||
modalStateFrom.resetFields();
|
modalStateFrom.resetFields();
|
||||||
fnGetList();
|
|
||||||
} else {
|
} else {
|
||||||
message.error({
|
if (from.num === 1) {
|
||||||
content: `${res.msg}`,
|
message.error({
|
||||||
duration: 3,
|
content: `${res.msg}`,
|
||||||
});
|
duration: 3,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
notification.error({
|
||||||
|
message: modalState.title,
|
||||||
|
description: res.msg,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -370,51 +376,6 @@ function fnModalOk() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 对话框弹出 批量新增操作确认执行函数
|
|
||||||
* 进行表达规则校验
|
|
||||||
*/
|
|
||||||
function fnBatchModalOk() {
|
|
||||||
modalStateBatchFrom
|
|
||||||
.validate()
|
|
||||||
.then(e => {
|
|
||||||
modalState.confirmLoading = true;
|
|
||||||
const from = toRaw(modalState.BatchForm);
|
|
||||||
from.neId = queryParams.neId || '-';
|
|
||||||
from.algoIndex = `${from.algoIndex}`;
|
|
||||||
const result = batchAddUDMAuth(from, from.num);
|
|
||||||
result.then(res => {
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
|
||||||
const timerS = Math.max(
|
|
||||||
Math.ceil(+from.num / 500),
|
|
||||||
`${from.num}`.length
|
|
||||||
);
|
|
||||||
notification.success({
|
|
||||||
message: modalState.title,
|
|
||||||
description: t('common.operateOk'),
|
|
||||||
duration: timerS,
|
|
||||||
});
|
|
||||||
setTimeout(() => {
|
|
||||||
modalState.confirmLoading = false;
|
|
||||||
modalState.visibleByBatch = false;
|
|
||||||
modalStateBatchFrom.resetFields();
|
|
||||||
fnGetList(1);
|
|
||||||
}, timerS * 1000);
|
|
||||||
} else {
|
|
||||||
modalState.confirmLoading = false;
|
|
||||||
notification.error({
|
|
||||||
message: modalState.title,
|
|
||||||
description: res.msg,
|
|
||||||
duration: 3,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对话框弹出 批量删除确认执行函数
|
* 对话框弹出 批量删除确认执行函数
|
||||||
* 进行表达规则校验
|
* 进行表达规则校验
|
||||||
@@ -455,15 +416,6 @@ function fnBatchDelModalOk() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量添加对话框弹出关闭执行函数
|
|
||||||
* 进行表达规则校验
|
|
||||||
*/
|
|
||||||
function fnBatchModalCancel() {
|
|
||||||
modalState.visibleByBatch = false;
|
|
||||||
modalStateBatchFrom.resetFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量删除对话框弹出关闭执行函数
|
* 批量删除对话框弹出关闭执行函数
|
||||||
* 进行表达规则校验
|
* 进行表达规则校验
|
||||||
@@ -831,29 +783,11 @@ onMounted(() => {
|
|||||||
{{ t('common.addText') }}
|
{{ t('common.addText') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
|
|
||||||
<a-button type="primary" @click.prevent="fnModalVisibleByBatch(1)">
|
|
||||||
<template #icon>
|
|
||||||
<PlusOutlined />
|
|
||||||
</template>
|
|
||||||
{{ t('views.neUser.auth.batchAddText') }}
|
|
||||||
</a-button>
|
|
||||||
|
|
||||||
<a-button
|
|
||||||
type="default"
|
|
||||||
danger
|
|
||||||
:disabled="tableState.selectedRowKeys.length <= 0"
|
|
||||||
:loading="modalState.loadDataLoading"
|
|
||||||
@click.prevent="fnRecordDelete('0')"
|
|
||||||
>
|
|
||||||
<template #icon><DeleteOutlined /></template>
|
|
||||||
{{ t('views.neUser.auth.checkDel') }}
|
|
||||||
</a-button>
|
|
||||||
|
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
danger
|
danger
|
||||||
ghost
|
ghost
|
||||||
@click.prevent="fnModalVisibleByBatch(0)"
|
@click.prevent="fnModalVisibleByBatch()"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
@@ -900,6 +834,17 @@ onMounted(() => {
|
|||||||
</a-button>
|
</a-button>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
|
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
danger
|
||||||
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
|
:loading="modalState.loadDataLoading"
|
||||||
|
@click.prevent="fnRecordDelete('0')"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
{{ t('views.neUser.auth.checkDel') }}
|
||||||
|
</a-button>
|
||||||
|
|
||||||
<a-popconfirm
|
<a-popconfirm
|
||||||
:title="t('views.neUser.auth.checkExportConfirm')"
|
:title="t('views.neUser.auth.checkExportConfirm')"
|
||||||
placement="topRight"
|
placement="topRight"
|
||||||
@@ -1040,6 +985,24 @@ onMounted(() => {
|
|||||||
:label-col="{ span: 6 }"
|
:label-col="{ span: 6 }"
|
||||||
:labelWrap="true"
|
:labelWrap="true"
|
||||||
>
|
>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.neUser.auth.numAdd')"
|
||||||
|
name="num"
|
||||||
|
v-bind="modalStateFrom.validateInfos.num"
|
||||||
|
v-show="!modalState.from.id"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="modalState.from.num"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="1"
|
||||||
|
:max="10000"
|
||||||
|
placeholder="<=10000"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
@@ -1175,175 +1138,6 @@ onMounted(() => {
|
|||||||
</a-form>
|
</a-form>
|
||||||
</ProModal>
|
</ProModal>
|
||||||
|
|
||||||
<!-- 批量新增框 -->
|
|
||||||
<ProModal
|
|
||||||
:drag="true"
|
|
||||||
:width="800"
|
|
||||||
:destroyOnClose="true"
|
|
||||||
:keyboard="false"
|
|
||||||
:mask-closable="false"
|
|
||||||
:visible="modalState.visibleByBatch"
|
|
||||||
:title="modalState.title"
|
|
||||||
:confirm-loading="modalState.confirmLoading"
|
|
||||||
@ok="fnBatchModalOk"
|
|
||||||
@cancel="fnBatchModalCancel"
|
|
||||||
>
|
|
||||||
<a-form
|
|
||||||
name="modalStateBatchFrom"
|
|
||||||
layout="horizontal"
|
|
||||||
:label-col="{ span: 6 }"
|
|
||||||
:labelWrap="true"
|
|
||||||
>
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.neUser.auth.numAdd')"
|
|
||||||
name="num"
|
|
||||||
v-bind="modalStateBatchFrom.validateInfos.num"
|
|
||||||
>
|
|
||||||
<a-input-number
|
|
||||||
v-model:value="modalState.BatchForm.num"
|
|
||||||
style="width: 100%"
|
|
||||||
:min="1"
|
|
||||||
:max="10000"
|
|
||||||
placeholder="<=10000"
|
|
||||||
></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
label="IMSI"
|
|
||||||
name="imsi"
|
|
||||||
v-bind="modalStateBatchFrom.validateInfos.imsi"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="modalState.BatchForm.imsi"
|
|
||||||
allow-clear
|
|
||||||
:maxlength="15"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<a-tooltip placement="topLeft">
|
|
||||||
<template #title>
|
|
||||||
{{ t('views.neUser.auth.imsiTip') }}<br />
|
|
||||||
{{ t('views.neUser.auth.imsiTip1') }}<br />
|
|
||||||
{{ t('views.neUser.auth.imsiTip2') }}<br />
|
|
||||||
{{ t('views.neUser.auth.imsiTip3') }}
|
|
||||||
</template>
|
|
||||||
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
|
|
||||||
</a-tooltip>
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item label="Status" name="status">
|
|
||||||
<a-select value="1">
|
|
||||||
<a-select-option value="1">Active</a-select-option>
|
|
||||||
<a-select-option value="0">Inactive</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
label="AMF"
|
|
||||||
name="amf"
|
|
||||||
v-bind="modalStateBatchFrom.validateInfos.amf"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="modalState.BatchForm.amf"
|
|
||||||
allow-clear
|
|
||||||
:maxlength="4"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<a-tooltip placement="topLeft">
|
|
||||||
<template #title>
|
|
||||||
{{ t('views.neUser.auth.amfTip') }}
|
|
||||||
</template>
|
|
||||||
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
|
|
||||||
</a-tooltip>
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
label="Algo Index"
|
|
||||||
name="algo"
|
|
||||||
v-bind="modalStateBatchFrom.validateInfos.algoIndex"
|
|
||||||
>
|
|
||||||
<a-input-number
|
|
||||||
v-model:value="modalState.BatchForm.algoIndex"
|
|
||||||
style="width: 100%"
|
|
||||||
:min="0"
|
|
||||||
:max="15"
|
|
||||||
placeholder="0 ~ 15"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<a-tooltip placement="topLeft">
|
|
||||||
<template #title>
|
|
||||||
{{ t('views.neUser.auth.algoIndexTip') }}
|
|
||||||
</template>
|
|
||||||
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
|
|
||||||
</a-tooltip>
|
|
||||||
</template>
|
|
||||||
</a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-form-item
|
|
||||||
label="KI"
|
|
||||||
name="ki"
|
|
||||||
v-bind="modalStateBatchFrom.validateInfos.ki"
|
|
||||||
:label-col="{ span: 3 }"
|
|
||||||
:labelWrap="true"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="modalState.BatchForm.ki"
|
|
||||||
allow-clear
|
|
||||||
:maxlength="32"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<a-tooltip placement="topLeft">
|
|
||||||
<template #title>
|
|
||||||
{{ t('views.neUser.auth.kiTip') }}
|
|
||||||
</template>
|
|
||||||
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
|
|
||||||
</a-tooltip>
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item
|
|
||||||
label="OPC"
|
|
||||||
name="opc"
|
|
||||||
v-bind="modalStateBatchFrom.validateInfos.opc"
|
|
||||||
:label-col="{ span: 3 }"
|
|
||||||
:labelWrap="true"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="modalState.BatchForm.opc"
|
|
||||||
allow-clear
|
|
||||||
:maxlength="32"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<a-tooltip placement="topLeft">
|
|
||||||
<template #title>
|
|
||||||
{{ t('views.neUser.auth.opcTip') }}
|
|
||||||
</template>
|
|
||||||
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
|
|
||||||
</a-tooltip>
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
|
||||||
</ProModal>
|
|
||||||
|
|
||||||
<!-- 批量删除框 -->
|
<!-- 批量删除框 -->
|
||||||
<ProModal
|
<ProModal
|
||||||
:drag="true"
|
:drag="true"
|
||||||
|
|||||||
@@ -74,6 +74,15 @@ let tableColumns = ref<TableColumnsType>([
|
|||||||
align: 'center',
|
align: 'center',
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'NE Name',
|
||||||
|
dataIndex: 'neName',
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 200,
|
||||||
|
minWidth: 150,
|
||||||
|
maxWidth: 400,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'UE Number',
|
title: 'UE Number',
|
||||||
dataIndex: 'ueNum',
|
dataIndex: 'ueNum',
|
||||||
@@ -89,6 +98,7 @@ let tableColumns = ref<TableColumnsType>([
|
|||||||
minWidth: 150,
|
minWidth: 150,
|
||||||
maxWidth: 400,
|
maxWidth: 400,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: 'Radio Address',
|
title: 'Radio Address',
|
||||||
dataIndex: 'address',
|
dataIndex: 'address',
|
||||||
@@ -129,6 +139,7 @@ function fnTableSize({ key }: MenuInfo) {
|
|||||||
tableState.size = key as SizeType;
|
tableState.size = key as SizeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let promises = ref<any[]>([]);
|
||||||
/**查询列表, pageNum初始页数 */
|
/**查询列表, pageNum初始页数 */
|
||||||
function fnGetList(pageNum?: number) {
|
function fnGetList(pageNum?: number) {
|
||||||
if (tableState.loading) return;
|
if (tableState.loading) return;
|
||||||
@@ -136,6 +147,63 @@ function fnGetList(pageNum?: number) {
|
|||||||
if (pageNum) {
|
if (pageNum) {
|
||||||
queryParams.pageNum = pageNum;
|
queryParams.pageNum = pageNum;
|
||||||
}
|
}
|
||||||
|
if (!queryParams.neType) {
|
||||||
|
tableState.data = [];
|
||||||
|
promises.value = [];
|
||||||
|
//同时获取45G基站信息 且在每条信息中添加45G字段(原始数据没有) 已经筛选后的
|
||||||
|
neCascaderOptions.value.map((item: any) => {
|
||||||
|
item.children.forEach((child: any) => {
|
||||||
|
promises.value.push(
|
||||||
|
listBase5G({
|
||||||
|
neId: child.neId,
|
||||||
|
neType: child.neType,
|
||||||
|
nbId: queryParams.id,
|
||||||
|
pageNum: queryParams.pageNum,
|
||||||
|
pageSize: 10000,
|
||||||
|
}).then(res => {
|
||||||
|
// 添加 neName 字段到每一项数据
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
|
res.rows.forEach(row => {
|
||||||
|
row.neName = `${child.neType}_${child.neId}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Promise.allSettled(promises.value).then(results => {
|
||||||
|
results.forEach(result => {
|
||||||
|
if (result.status === 'fulfilled') {
|
||||||
|
const allBaseData = result.value;
|
||||||
|
if (
|
||||||
|
allBaseData.code === RESULT_CODE_SUCCESS &&
|
||||||
|
Array.isArray(allBaseData.rows)
|
||||||
|
) {
|
||||||
|
// 处理成功结果
|
||||||
|
tablePagination.total += allBaseData.total;
|
||||||
|
tableState.data = [...tableState.data, ...allBaseData.rows];
|
||||||
|
if (
|
||||||
|
tablePagination.total <=
|
||||||
|
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||||
|
queryParams.pageNum !== 1
|
||||||
|
) {
|
||||||
|
tableState.loading = false;
|
||||||
|
fnGetList(queryParams.pageNum - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//AMF返回404是代表没找到这个数据 GNB_NOT_FOUND
|
||||||
|
tablePagination.total = 0;
|
||||||
|
tableState.data = [];
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let toBack: Record<string, any> = {
|
let toBack: Record<string, any> = {
|
||||||
neType: queryParams.neType[0],
|
neType: queryParams.neType[0],
|
||||||
@@ -152,6 +220,11 @@ function fnGetList(pageNum?: number) {
|
|||||||
}
|
}
|
||||||
tablePagination.total = res.total;
|
tablePagination.total = res.total;
|
||||||
tableState.data = res.rows;
|
tableState.data = res.rows;
|
||||||
|
|
||||||
|
res.rows.forEach((item: any) => {
|
||||||
|
item.neName = queryParams.neType.join('_');
|
||||||
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
tablePagination.total <=
|
tablePagination.total <=
|
||||||
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||||
@@ -160,7 +233,8 @@ function fnGetList(pageNum?: number) {
|
|||||||
tableState.loading = false;
|
tableState.loading = false;
|
||||||
fnGetList(queryParams.pageNum - 1);
|
fnGetList(queryParams.pageNum - 1);
|
||||||
}
|
}
|
||||||
} else {//AMF返回404是代表没找到这个数据 GNB_NOT_FOUND
|
} else {
|
||||||
|
//AMF返回404是代表没找到这个数据 GNB_NOT_FOUND
|
||||||
tablePagination.total = 0;
|
tablePagination.total = 0;
|
||||||
tableState.data = [];
|
tableState.data = [];
|
||||||
}
|
}
|
||||||
@@ -176,11 +250,19 @@ onMounted(() => {
|
|||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
if (res.data.length > 0) {
|
if (res.data.length > 0) {
|
||||||
// 过滤不可用的网元
|
// 过滤不可用的网元
|
||||||
neCascaderOptions.value = neInfoStore.getNeCascaderOptions.filter(
|
for (const item of neInfoStore.getNeCascaderOptions) {
|
||||||
(item: any) => {
|
if (!['AMF', 'MME'].includes(item.value)) continue;
|
||||||
return ['AMF', 'MME'].includes(item.value);
|
const v = JSON.parse(JSON.stringify(item));
|
||||||
|
|
||||||
|
if (v.label === 'AMF') {
|
||||||
|
v.label = '5G';
|
||||||
}
|
}
|
||||||
);
|
if (v.label === 'MME') {
|
||||||
|
v.label = '4G';
|
||||||
|
}
|
||||||
|
neCascaderOptions.value.push(v);
|
||||||
|
}
|
||||||
|
|
||||||
if (neCascaderOptions.value.length === 0) {
|
if (neCascaderOptions.value.length === 0) {
|
||||||
message.warning({
|
message.warning({
|
||||||
content: t('common.noData'),
|
content: t('common.noData'),
|
||||||
@@ -188,8 +270,9 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 无查询参数neType时 默认选择AMF
|
// 无查询参数neType时 默认选择AMF
|
||||||
const queryNeType = (route.query.neType as string) || 'AMF';
|
const queryNeType = (route.query.neType as string) || '5G';
|
||||||
const item = neCascaderOptions.value.find(
|
const item = neCascaderOptions.value.find(
|
||||||
s => s.value === queryNeType
|
s => s.value === queryNeType
|
||||||
);
|
);
|
||||||
@@ -230,7 +313,6 @@ onMounted(() => {
|
|||||||
<a-cascader
|
<a-cascader
|
||||||
v-model:value="queryParams.neType"
|
v-model:value="queryParams.neType"
|
||||||
:options="neCascaderOptions"
|
:options="neCascaderOptions"
|
||||||
:allow-clear="false"
|
|
||||||
:placeholder="t('common.selectPlease')"
|
:placeholder="t('common.selectPlease')"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@ import { message } from 'ant-design-vue/lib';
|
|||||||
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
||||||
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
|
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
|
||||||
import { ColumnsType } from 'ant-design-vue/lib/table';
|
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||||||
import { listUEInfoBySMF } from '@/api/neUser/smf';
|
import { listSMFSubscribers } from '@/api/neData/smf';
|
||||||
import useNeInfoStore from '@/store/modules/neinfo';
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
@@ -51,8 +51,6 @@ type TabeStateType = {
|
|||||||
seached: boolean;
|
seached: boolean;
|
||||||
/**记录数据 */
|
/**记录数据 */
|
||||||
data: object[];
|
data: object[];
|
||||||
/**勾选记录 */
|
|
||||||
selectedRowKeys: (string | number)[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**表格状态 */
|
/**表格状态 */
|
||||||
@@ -61,7 +59,6 @@ let tableState: TabeStateType = reactive({
|
|||||||
size: 'middle',
|
size: 'middle',
|
||||||
seached: true,
|
seached: true,
|
||||||
data: [],
|
data: [],
|
||||||
selectedRowKeys: [],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**表格字段列 */
|
/**表格字段列 */
|
||||||
@@ -69,7 +66,7 @@ let tableColumns: ColumnsType = [
|
|||||||
{
|
{
|
||||||
title: 'IMSI',
|
title: 'IMSI',
|
||||||
dataIndex: 'imsi',
|
dataIndex: 'imsi',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
sorter: (a: any, b: any) => Number(a.imsi) - Number(b.imsi),
|
sorter: (a: any, b: any) => Number(a.imsi) - Number(b.imsi),
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
const idx = opt.value.lastIndexOf('-');
|
const idx = opt.value.lastIndexOf('-');
|
||||||
@@ -83,7 +80,7 @@ let tableColumns: ColumnsType = [
|
|||||||
{
|
{
|
||||||
title: 'MSISDN',
|
title: 'MSISDN',
|
||||||
dataIndex: 'msisdn',
|
dataIndex: 'msisdn',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
sorter: (a: any, b: any) => Number(a.msisdn) - Number(b.msisdn),
|
sorter: (a: any, b: any) => Number(a.msisdn) - Number(b.msisdn),
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
const idx = opt.value.lastIndexOf('-');
|
const idx = opt.value.lastIndexOf('-');
|
||||||
@@ -97,7 +94,7 @@ let tableColumns: ColumnsType = [
|
|||||||
{
|
{
|
||||||
title: 'RAT Type',
|
title: 'RAT Type',
|
||||||
dataIndex: 'ratType',
|
dataIndex: 'ratType',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -116,12 +113,18 @@ let tableColumns: ColumnsType = [
|
|||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
width: 150,
|
width: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('common.operate'),
|
title: t('common.operate'),
|
||||||
key: 'imsi',
|
key: 'imsi',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Remark',
|
||||||
|
dataIndex: 'remark',
|
||||||
|
align: 'left',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -220,12 +223,8 @@ function fnGetList(pageNum?: number) {
|
|||||||
if (pageNum) {
|
if (pageNum) {
|
||||||
queryParams.pageNum = pageNum;
|
queryParams.pageNum = pageNum;
|
||||||
}
|
}
|
||||||
listUEInfoBySMF(toRaw(queryParams)).then(res => {
|
listSMFSubscribers(toRaw(queryParams)).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
// 取消勾选
|
|
||||||
if (tableState.selectedRowKeys.length > 0) {
|
|
||||||
tableState.selectedRowKeys = [];
|
|
||||||
}
|
|
||||||
tablePagination.total = res.total;
|
tablePagination.total = res.total;
|
||||||
tableState.data = res.rows;
|
tableState.data = res.rows;
|
||||||
if (
|
if (
|
||||||
@@ -286,7 +285,7 @@ onMounted(() => {
|
|||||||
<!-- 表格搜索栏 -->
|
<!-- 表格搜索栏 -->
|
||||||
<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="8" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item :label="t('views.neUser.ue.neType')" name="neId ">
|
<a-form-item :label="t('views.neUser.ue.neType')" name="neId ">
|
||||||
<a-select
|
<a-select
|
||||||
v-model:value="queryParams.neId"
|
v-model:value="queryParams.neId"
|
||||||
@@ -295,18 +294,17 @@ onMounted(() => {
|
|||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="8" :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 v-model:value="queryParams.imsi" allow-clear></a-input>
|
<a-input v-model:value="queryParams.imsi" allow-clear></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="8" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item label="MSISDN" name="msisdn">
|
<a-form-item label="MSISDN" name="msisdn">
|
||||||
<a-input v-model:value="queryParams.msisdn" allow-clear></a-input>
|
<a-input v-model:value="queryParams.msisdn" allow-clear></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-col :lg="8" :md="12" :xs="24">
|
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-space :size="8">
|
<a-space :size="8">
|
||||||
<a-button type="primary" @click.prevent="fnGetList(1)">
|
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||||
@@ -382,7 +380,7 @@ onMounted(() => {
|
|||||||
:data-source="tableState.data"
|
:data-source="tableState.data"
|
||||||
:size="tableState.size"
|
:size="tableState.size"
|
||||||
:pagination="tablePagination"
|
:pagination="tablePagination"
|
||||||
:scroll="{ x: 1000, y: 400 }"
|
:scroll="{ x: true, y: 400 }"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'imsi'">
|
<template v-if="column.key === 'imsi'">
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
|
|||||||
import { ColumnsType } from 'ant-design-vue/lib/table';
|
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import useDictStore from '@/store/modules/dict';
|
|
||||||
import useNeInfoStore from '@/store/modules/neinfo';
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import { parseObjLineToHump } from '@/utils/parse-utils';
|
||||||
import {
|
import {
|
||||||
addCustom,
|
addCustom,
|
||||||
delCustom,
|
delCustom,
|
||||||
@@ -16,15 +16,17 @@ import {
|
|||||||
listCustom,
|
listCustom,
|
||||||
updateCustom,
|
updateCustom,
|
||||||
} from '@/api/perfManage/customTarget';
|
} from '@/api/perfManage/customTarget';
|
||||||
const { getDict } = useDictStore();
|
import { getKPITitle } from '@/api/perfManage/goldTarget';
|
||||||
|
import useDictStore from '@/store/modules/dict';
|
||||||
const { t, currentLocale } = useI18n();
|
const { t, currentLocale } = useI18n();
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
|
||||||
/**字典数据 */
|
/**字典数据 */
|
||||||
let dict: {
|
let dict: {
|
||||||
/**原始严重程度 */
|
/**状态 */
|
||||||
activeAlarmSeverity: DictType[];
|
sysNormalDisable: DictType[];
|
||||||
} = reactive({
|
} = reactive({
|
||||||
activeAlarmSeverity: [],
|
sysNormalDisable: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
/**查询参数 */
|
/**查询参数 */
|
||||||
@@ -85,15 +87,22 @@ let tableColumns: ColumnsType = [
|
|||||||
align: 'center',
|
align: 'center',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.perfManage.customTarget.kpiSet'),
|
title: t('views.perfManage.customTarget.title'),
|
||||||
dataIndex: 'kpiSet',
|
dataIndex: 'title',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.perfManage.customTarget.period'),
|
title: t('views.perfManage.customTarget.description'),
|
||||||
dataIndex: 'threshold',
|
dataIndex: 'description',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('views.perfManage.customTarget.status'),
|
||||||
|
dataIndex: 'status',
|
||||||
|
key: 'status',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('common.operate'),
|
title: t('common.operate'),
|
||||||
key: 'id',
|
key: 'id',
|
||||||
@@ -175,13 +184,14 @@ function fnGetList(pageNum?: number) {
|
|||||||
queryParams.pageNum = pageNum;
|
queryParams.pageNum = pageNum;
|
||||||
}
|
}
|
||||||
listCustom(toRaw(queryParams)).then(res => {
|
listCustom(toRaw(queryParams)).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
// 取消勾选
|
// 取消勾选
|
||||||
if (tableState.selectedRowKeys.length > 0) {
|
if (tableState.selectedRowKeys.length > 0) {
|
||||||
tableState.selectedRowKeys = [];
|
tableState.selectedRowKeys = [];
|
||||||
}
|
}
|
||||||
tablePagination.total = res.total;
|
tablePagination.total = res.total;
|
||||||
tableState.data = res.rows;
|
tableState.data = res.data;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
tablePagination.total <=
|
tablePagination.total <=
|
||||||
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||||
@@ -207,8 +217,6 @@ type ModalStateType = {
|
|||||||
neType: string[];
|
neType: string[];
|
||||||
/**网元类型性能测量集 */
|
/**网元类型性能测量集 */
|
||||||
neTypPerformance: Record<string, any>[];
|
neTypPerformance: Record<string, any>[];
|
||||||
/**网元类型对象类型集 */
|
|
||||||
objectTypeArr: Record<string, any>[];
|
|
||||||
/**已选择性能测量项 */
|
/**已选择性能测量项 */
|
||||||
selectedPre: string[];
|
selectedPre: string[];
|
||||||
/**表单数据 */
|
/**表单数据 */
|
||||||
@@ -224,21 +232,32 @@ let modalState: ModalStateType = reactive({
|
|||||||
title: '',
|
title: '',
|
||||||
neType: [],
|
neType: [],
|
||||||
neTypPerformance: [],
|
neTypPerformance: [],
|
||||||
objectTypeArr: [],
|
|
||||||
selectedPre: [],
|
selectedPre: [],
|
||||||
from: {
|
from: {
|
||||||
id: '',
|
id: undefined,
|
||||||
neType: '',
|
neType: 'UDM',
|
||||||
objectType: '',
|
|
||||||
expression: '',
|
|
||||||
kpiSet: '',
|
|
||||||
title: '',
|
|
||||||
kpiId: '',
|
kpiId: '',
|
||||||
period: 900,
|
title: '',
|
||||||
|
expression: '',
|
||||||
|
status: 'Active',
|
||||||
|
unit: '',
|
||||||
|
description: '',
|
||||||
},
|
},
|
||||||
confirmLoading: false,
|
confirmLoading: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**表单中多选的OPTION */
|
||||||
|
const modalStateFromOption = reactive({
|
||||||
|
symbolJson: [
|
||||||
|
{ label: '(', value: '(' },
|
||||||
|
{ label: ')', value: ')' },
|
||||||
|
{ label: '+', value: '+' },
|
||||||
|
{ label: '-', value: '-' },
|
||||||
|
{ label: '*', value: '*' },
|
||||||
|
{ label: '/', value: '/' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
/**对话框内表单属性和校验规则 */
|
/**对话框内表单属性和校验规则 */
|
||||||
const modalStateFrom = Form.useForm(
|
const modalStateFrom = Form.useForm(
|
||||||
modalState.from,
|
modalState.from,
|
||||||
@@ -246,15 +265,7 @@ const modalStateFrom = Form.useForm(
|
|||||||
neType: [
|
neType: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: t('views.traceManage.task.neTypePlease'),
|
message: t('views.ne.common.neTypePlease'),
|
||||||
},
|
|
||||||
],
|
|
||||||
objectType: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message:
|
|
||||||
t('views.perfManage.customTarget.objectType') +
|
|
||||||
t('common.unableNull'),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
expression: [
|
expression: [
|
||||||
@@ -279,60 +290,45 @@ const modalStateFrom = Form.useForm(
|
|||||||
t('views.perfManage.customTarget.title') + t('common.unableNull'),
|
t('views.perfManage.customTarget.title') + t('common.unableNull'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
period: [
|
unit: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message:
|
message:
|
||||||
t('views.perfManage.customTarget.period') + t('common.unableNull'),
|
t('views.perfManage.customTarget.unit') + t('common.unableNull'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
/**性能测量数据集选择初始 */
|
/**性能测量数据集选择初始 value:neType*/
|
||||||
function fnSelectPerformanceInit(value: any) {
|
function fnSelectPerformanceInit(value: any) {
|
||||||
const performance = useNeInfoStore().perMeasurementList.filter(
|
|
||||||
i => i.neType === value
|
|
||||||
);
|
|
||||||
if (modalState.from.objectType) modalState.from.objectType = '';
|
|
||||||
if (modalState.selectedPre.length > 0) modalState.selectedPre = [];
|
|
||||||
|
|
||||||
modalState.from.expression = '';
|
modalState.from.expression = '';
|
||||||
const arrSet = new Set<string>();
|
modalState.neTypPerformance = [
|
||||||
performance.forEach((data: any) => {
|
{
|
||||||
arrSet.add(data.objectType);
|
value: 'granularity',
|
||||||
});
|
label: t('views.perfManage.customTarget.granularity'),
|
||||||
// 组装对象类型options
|
},
|
||||||
modalState.objectTypeArr = Array.from(arrSet).map((value: any) => ({
|
];
|
||||||
label: value,
|
// 当前语言
|
||||||
value: value,
|
var language = currentLocale.value.split('_')[0];
|
||||||
}));
|
if (language === 'zh') language = 'cn';
|
||||||
|
// 获取表头文字
|
||||||
//进行分组选择
|
getKPITitle(value).then(res => {
|
||||||
const groupedData = performance.reduce((groups: any, item: any) => {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
const { kpiCode, ...rest } = item;
|
for (const item of res.data) {
|
||||||
if (!groups[kpiCode]) {
|
const kpiDisplay = item[`${language}Title`];
|
||||||
groups[kpiCode] = [];
|
const kpiValue = item[`kpiId`];
|
||||||
|
modalState.neTypPerformance.push({
|
||||||
|
value: kpiValue,
|
||||||
|
label: kpiDisplay,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.getInfoFail'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
groups[kpiCode].push(rest);
|
|
||||||
return groups;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
//渲染出性能测量集的选择项
|
|
||||||
modalState.neTypPerformance = Object.keys(groupedData).map(kpiCode => {
|
|
||||||
return {
|
|
||||||
label: kpiCode,
|
|
||||||
options: groupedData[kpiCode].map((item: any) => {
|
|
||||||
return {
|
|
||||||
value: item.kpiId,
|
|
||||||
label:
|
|
||||||
currentLocale.value === 'zh_CN'
|
|
||||||
? JSON.parse(item.titleJson).cn
|
|
||||||
: JSON.parse(item.titleJson).en,
|
|
||||||
kpiCode: kpiCode,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,30 +336,17 @@ function fnSelectPerformanceInit(value: any) {
|
|||||||
* 对话框弹出显示为 新增或者修改
|
* 对话框弹出显示为 新增或者修改
|
||||||
* @param noticeId 网元id, 不传为新增
|
* @param noticeId 网元id, 不传为新增
|
||||||
*/
|
*/
|
||||||
function fnModalVisibleByEdit(id?: string) {
|
function fnModalVisibleByEdit(row?: any, id?: any) {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
modalStateFrom.resetFields();
|
modalStateFrom.resetFields();
|
||||||
modalState.title = t('views.perfManage.customTarget.addCustom');
|
modalState.title = t('views.perfManage.customTarget.addCustom');
|
||||||
modalState.visibleByEdit = true;
|
modalState.visibleByEdit = true;
|
||||||
|
fnSelectPerformanceInit(modalState.from.neType);
|
||||||
} else {
|
} else {
|
||||||
if (modalState.confirmLoading) return;
|
fnSelectPerformanceInit(row.neType);
|
||||||
const hide = message.loading(t('common.loading'), 0);
|
modalState.from = Object.assign(modalState.from, row);
|
||||||
modalState.confirmLoading = true;
|
modalState.title = t('views.perfManage.customTarget.editCustom');
|
||||||
getCustom(id).then(res => {
|
modalState.visibleByEdit = true;
|
||||||
modalState.confirmLoading = false;
|
|
||||||
hide();
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
|
||||||
fnSelectPerformanceInit(res.data.neType);
|
|
||||||
modalState.selectedPre = res.data.kpiSet
|
|
||||||
? res.data.kpiSet.split(',')
|
|
||||||
: [];
|
|
||||||
modalState.from = Object.assign(modalState.from, res.data);
|
|
||||||
modalState.title = t('views.perfManage.customTarget.editCustom');
|
|
||||||
modalState.visibleByEdit = true;
|
|
||||||
} else {
|
|
||||||
message.error(t('views.perfManage.customTarget.errorCustomInfo'), 3);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,13 +358,6 @@ function fnModalOk() {
|
|||||||
modalStateFrom
|
modalStateFrom
|
||||||
.validate()
|
.validate()
|
||||||
.then(e => {
|
.then(e => {
|
||||||
// if (modalState.selectedPre.length === 0) {
|
|
||||||
// message.error({
|
|
||||||
// content: `${res.msg}`,
|
|
||||||
// duration: 3,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
modalState.from.kpiSet = modalState.selectedPre.join(',');
|
|
||||||
const from = toRaw(modalState.from);
|
const from = toRaw(modalState.from);
|
||||||
//return false;
|
//return false;
|
||||||
modalState.confirmLoading = true;
|
modalState.confirmLoading = true;
|
||||||
@@ -424,46 +400,57 @@ function fnModalCancel() {
|
|||||||
modalStateFrom.resetFields();
|
modalStateFrom.resetFields();
|
||||||
modalState.neType = [];
|
modalState.neType = [];
|
||||||
modalState.neTypPerformance = [];
|
modalState.neTypPerformance = [];
|
||||||
modalState.selectedPre = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 选择性能指标,填充进当前计算公式的值
|
* 选择性能指标,填充进当前计算公式的值
|
||||||
*/
|
*/
|
||||||
function fnSelectPer(s: any, option: any) {
|
function fnSelectPer(s: any, option: any) {
|
||||||
|
modalState.from.expression += `'${s}'`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fnSelectSymbol(s: any) {
|
||||||
modalState.from.expression += s;
|
modalState.from.expression += s;
|
||||||
}
|
}
|
||||||
|
/**网元参数 */
|
||||||
/**
|
let neCascaderOptions = ref<Record<string, any>[]>([]);
|
||||||
* 多选框取消性能指标 表达式的变化
|
|
||||||
*/
|
|
||||||
function fnDelPer(s: any, option: any) {
|
|
||||||
modalState.from.expression = modalState.from.expression.replace(s, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
// function checkText(e:any){
|
|
||||||
// console.log(e);
|
|
||||||
// const reg = /^[*+-/]*$/;
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 初始字典数据
|
|
||||||
Promise.allSettled([getDict('active_alarm_severity')]).then(resArr => {
|
|
||||||
if (resArr[0].status === 'fulfilled') {
|
|
||||||
dict.activeAlarmSeverity = resArr[0].value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Promise.allSettled([
|
Promise.allSettled([
|
||||||
// 获取网元网元列表
|
// 获取网元网元列表
|
||||||
|
getDict('sys_normal_disable'),
|
||||||
useNeInfoStore().fnNelist(),
|
useNeInfoStore().fnNelist(),
|
||||||
// 获取性能测量集列表
|
])
|
||||||
useNeInfoStore().fnNeTaskPerformance(),
|
.then(resArr => {
|
||||||
]).finally(() => {
|
if (resArr[0].status === 'fulfilled') {
|
||||||
// 获取列表数据
|
dict.sysNormalDisable = resArr[0].value;
|
||||||
fnGetList();
|
}
|
||||||
});
|
|
||||||
|
if (
|
||||||
|
resArr[1].status === 'fulfilled' &&
|
||||||
|
Array.isArray(resArr[1].value.data)
|
||||||
|
) {
|
||||||
|
if (resArr[1].value.data.length > 0) {
|
||||||
|
// 过滤不可用的网元
|
||||||
|
neCascaderOptions.value =
|
||||||
|
useNeInfoStore().getNeCascaderOptions.filter((item: any) => {
|
||||||
|
return !['OMC', 'NSSF', 'NEF', 'NRF', 'LMF', 'N3IWF'].includes(
|
||||||
|
item.value
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (neCascaderOptions.value.length === 0) {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// 获取列表数据
|
||||||
|
fnGetList();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -479,14 +466,14 @@ onMounted(() => {
|
|||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.traceManage.task.neType')"
|
:label="t('views.ne.common.neType')"
|
||||||
name="neType "
|
name="neType "
|
||||||
>
|
>
|
||||||
<a-auto-complete
|
<a-auto-complete
|
||||||
v-model:value="queryParams.neType"
|
v-model:value="queryParams.neType"
|
||||||
:options="useNeInfoStore().getNeSelectOtions"
|
:options="neCascaderOptions"
|
||||||
allow-clear
|
allow-clear
|
||||||
:placeholder="t('views.traceManage.task.neTypePlease')"
|
:placeholder="t('views.ne.common.neTypePlease')"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -580,7 +567,7 @@ onMounted(() => {
|
|||||||
<template #title>{{ t('common.editText') }}</template>
|
<template #title>{{ t('common.editText') }}</template>
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
@click.prevent="fnModalVisibleByEdit(record.id)"
|
@click.prevent="fnModalVisibleByEdit(record, record.id)"
|
||||||
>
|
>
|
||||||
<template #icon><FormOutlined /></template>
|
<template #icon><FormOutlined /></template>
|
||||||
</a-button>
|
</a-button>
|
||||||
@@ -593,6 +580,24 @@ onMounted(() => {
|
|||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template v-if="column.key === 'status'">
|
||||||
|
<DictTag
|
||||||
|
:options="[
|
||||||
|
{
|
||||||
|
label: t('views.perfManage.customTarget.active'),
|
||||||
|
value: 'Active',
|
||||||
|
tagType: 'success',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('views.perfManage.customTarget.inactive'),
|
||||||
|
value: 'Inactive',
|
||||||
|
tagType: 'error',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:value="record.status"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -610,35 +615,25 @@ onMounted(() => {
|
|||||||
@ok="fnModalOk"
|
@ok="fnModalOk"
|
||||||
@cancel="fnModalCancel"
|
@cancel="fnModalCancel"
|
||||||
>
|
>
|
||||||
<a-form name="modalStateFrom" layout="horizontal">
|
<a-form
|
||||||
|
name="modalStateFrom"
|
||||||
|
layout="horizontal"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.traceManage.task.neType')"
|
:label="t('views.ne.common.neType')"
|
||||||
name="neType"
|
name="neType"
|
||||||
v-bind="modalStateFrom.validateInfos.neType"
|
v-bind="modalStateFrom.validateInfos.neType"
|
||||||
>
|
>
|
||||||
<a-select
|
<a-select
|
||||||
v-model:value="modalState.from.neType"
|
v-model:value="modalState.from.neType"
|
||||||
:options="useNeInfoStore().getNeSelectOtions"
|
:options="neCascaderOptions"
|
||||||
@change="fnSelectPerformanceInit"
|
@change="fnSelectPerformanceInit"
|
||||||
:allow-clear="false"
|
:allow-clear="false"
|
||||||
:placeholder="t('views.traceManage.task.neTypePlease')"
|
:placeholder="t('views.ne.common.neTypePlease')"
|
||||||
>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.perfManage.customTarget.objectType')"
|
|
||||||
name="kpiSobjectTypeet"
|
|
||||||
v-show="modalState.from.neType"
|
|
||||||
v-bind="modalStateFrom.validateInfos.objectType"
|
|
||||||
>
|
|
||||||
<a-select
|
|
||||||
placeholder="Please select"
|
|
||||||
v-model:value="modalState.from.objectType"
|
|
||||||
:options="modalState.objectTypeArr"
|
|
||||||
>
|
>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -646,6 +641,16 @@ onMounted(() => {
|
|||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.perfManage.customTarget.title')"
|
||||||
|
name="title"
|
||||||
|
v-bind="modalStateFrom.validateInfos.title"
|
||||||
|
>
|
||||||
|
<a-input v-model:value="modalState.from.title" allow-clear>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.perfManage.customTarget.kpiId')"
|
:label="t('views.perfManage.customTarget.kpiId')"
|
||||||
@@ -656,64 +661,96 @@ onMounted(() => {
|
|||||||
</a-input>
|
</a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.perfManage.customTarget.period')"
|
|
||||||
name="period"
|
|
||||||
v-bind="modalStateFrom.validateInfos.period"
|
|
||||||
>
|
|
||||||
<a-select
|
|
||||||
v-model:value="modalState.from.period"
|
|
||||||
:placeholder="t('common.selectPlease')"
|
|
||||||
:options="[
|
|
||||||
{ label: '5S', value: 5 },
|
|
||||||
{ label: '1M', value: 60 },
|
|
||||||
{ label: '5M', value: 300 },
|
|
||||||
{ label: '15M', value: 900 },
|
|
||||||
{ label: '30M', value: 1800 },
|
|
||||||
{ label: '60M', value: 3600 },
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.perfManage.customTarget.title')"
|
|
||||||
name="title"
|
|
||||||
v-bind="modalStateFrom.validateInfos.title"
|
|
||||||
>
|
|
||||||
<a-input v-model:value="modalState.from.title" allow-clear> </a-input>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.perfManage.customTarget.expression')"
|
:label="t('views.perfManage.customTarget.expression')"
|
||||||
name="expression"
|
name="expression"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
v-bind="modalStateFrom.validateInfos.expression"
|
v-bind="modalStateFrom.validateInfos.expression"
|
||||||
>
|
>
|
||||||
<a-input v-model:value="modalState.from.expression" allow-clear>
|
<a-input v-model:value="modalState.from.expression" allow-clear>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="21" :md="21" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item name="perSelect">
|
<a-form-item
|
||||||
|
name="perSelect"
|
||||||
|
:label="t('views.perfManage.customTarget.symbol')"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
placeholder="Please select"
|
||||||
|
:options="modalStateFromOption.symbolJson"
|
||||||
|
@select="fnSelectSymbol"
|
||||||
|
></a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
name="perSelect"
|
||||||
|
:label="t('views.perfManage.customTarget.element')"
|
||||||
|
>
|
||||||
<a-select
|
<a-select
|
||||||
mode="multiple"
|
|
||||||
style="margin-left: 80px"
|
|
||||||
placeholder="Please select"
|
placeholder="Please select"
|
||||||
allow-clear
|
|
||||||
v-model:value="modalState.selectedPre"
|
|
||||||
:options="modalState.neTypPerformance"
|
:options="modalState.neTypPerformance"
|
||||||
@select="fnSelectPer"
|
@select="fnSelectPer"
|
||||||
@deselect="fnDelPer"
|
|
||||||
></a-select>
|
></a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.perfManage.customTarget.unit')"
|
||||||
|
name="expression"
|
||||||
|
v-bind="modalStateFrom.validateInfos.unit"
|
||||||
|
>
|
||||||
|
<a-auto-complete
|
||||||
|
v-model:value="modalState.from.unit"
|
||||||
|
:options="[
|
||||||
|
{
|
||||||
|
label: 'Mbps',
|
||||||
|
value: 'Mbps',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '%',
|
||||||
|
value: '%',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
></a-auto-complete>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.perfManage.customTarget.status')"
|
||||||
|
name="status"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="modalState.from.status"
|
||||||
|
default-value="0"
|
||||||
|
:options="[
|
||||||
|
{
|
||||||
|
label: t('views.perfManage.customTarget.active'),
|
||||||
|
value: 'Active',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('views.perfManage.customTarget.inactive'),
|
||||||
|
value: 'Inactive',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.perfManage.customTarget.description')"
|
:label="t('views.perfManage.customTarget.description')"
|
||||||
name="description"
|
name="description"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
>
|
>
|
||||||
<a-textarea
|
<a-textarea
|
||||||
v-model:value="modalState.from.description"
|
v-model:value="modalState.from.description"
|
||||||
|
|||||||
@@ -649,7 +649,7 @@ onBeforeUnmount(() => {
|
|||||||
<a-col :lg="6" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
name="neType"
|
name="neType"
|
||||||
:label="t('views.traceManage.task.neType')"
|
:label="t('views.ne.common.neType')"
|
||||||
>
|
>
|
||||||
<a-cascader
|
<a-cascader
|
||||||
v-model:value="state.neType"
|
v-model:value="state.neType"
|
||||||
|
|||||||
834
src/views/perfManage/kpiCReport/index.vue
Normal file
834
src/views/perfManage/kpiCReport/index.vue
Normal file
@@ -0,0 +1,834 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import * as echarts from 'echarts/core';
|
||||||
|
import {
|
||||||
|
TooltipComponent,
|
||||||
|
TooltipComponentOption,
|
||||||
|
GridComponent,
|
||||||
|
GridComponentOption,
|
||||||
|
LegendComponent,
|
||||||
|
LegendComponentOption,
|
||||||
|
DataZoomComponent,
|
||||||
|
DataZoomComponentOption,
|
||||||
|
} from 'echarts/components';
|
||||||
|
import { LineChart, LineSeriesOption } from 'echarts/charts';
|
||||||
|
import { UniversalTransition } from 'echarts/features';
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
|
|
||||||
|
import {
|
||||||
|
reactive,
|
||||||
|
ref,
|
||||||
|
onMounted,
|
||||||
|
toRaw,
|
||||||
|
markRaw,
|
||||||
|
nextTick,
|
||||||
|
onBeforeUnmount,
|
||||||
|
} from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { message, Modal } from 'ant-design-vue/lib';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||||||
|
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
||||||
|
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
|
||||||
|
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
|
||||||
|
import {
|
||||||
|
RESULT_CODE_ERROR,
|
||||||
|
RESULT_CODE_SUCCESS,
|
||||||
|
} from '@/constants/result-constants';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { listCustom } from '@/api/perfManage/customTarget';
|
||||||
|
import { listCustomData } from '@/api/perfManage/customData';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { writeSheet } from '@/utils/execl-utils';
|
||||||
|
import saveAs from 'file-saver';
|
||||||
|
import { generateColorRGBA } from '@/utils/generate-utils';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
const neInfoStore = useNeInfoStore();
|
||||||
|
const route = useRoute();
|
||||||
|
const { t, currentLocale } = useI18n();
|
||||||
|
const ws = new WS();
|
||||||
|
|
||||||
|
echarts.use([
|
||||||
|
TooltipComponent,
|
||||||
|
GridComponent,
|
||||||
|
LegendComponent,
|
||||||
|
DataZoomComponent,
|
||||||
|
LineChart,
|
||||||
|
CanvasRenderer,
|
||||||
|
UniversalTransition,
|
||||||
|
]);
|
||||||
|
|
||||||
|
type EChartsOption = echarts.ComposeOption<
|
||||||
|
| TooltipComponentOption
|
||||||
|
| GridComponentOption
|
||||||
|
| LegendComponentOption
|
||||||
|
| DataZoomComponentOption
|
||||||
|
| LineSeriesOption
|
||||||
|
>;
|
||||||
|
|
||||||
|
/**图DOM节点实例对象 */
|
||||||
|
const kpiChartDom = ref<HTMLElement | undefined>(undefined);
|
||||||
|
|
||||||
|
/**图实例对象 */
|
||||||
|
const kpiChart = ref<any>(null);
|
||||||
|
|
||||||
|
/**网元参数 */
|
||||||
|
let neCascaderOptions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
|
/**记录开始结束时间 */
|
||||||
|
let queryRangePicker = ref<[string, string]>(['', '']);
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
let tableColumns = ref<ColumnsType>([]);
|
||||||
|
|
||||||
|
/**表格字段列排序 */
|
||||||
|
let tableColumnsDnd = ref<ColumnsType>([]);
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
let tablePagination = reactive({
|
||||||
|
/**当前页数 */
|
||||||
|
current: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
/**默认的每页条数 */
|
||||||
|
defaultPageSize: 20,
|
||||||
|
/**指定每页可以显示多少条 */
|
||||||
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
|
/**只有一页时是否隐藏分页器 */
|
||||||
|
hideOnSinglePage: false,
|
||||||
|
/**是否可以快速跳转至某页 */
|
||||||
|
showQuickJumper: true,
|
||||||
|
/**是否可以改变 pageSize */
|
||||||
|
showSizeChanger: true,
|
||||||
|
/**数据总数 */
|
||||||
|
total: 0,
|
||||||
|
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
|
||||||
|
onChange: (page: number, pageSize: number) => {
|
||||||
|
tablePagination.current = page;
|
||||||
|
tablePagination.pageSize = pageSize;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**搜索栏 */
|
||||||
|
seached: boolean;
|
||||||
|
/**记录数据 */
|
||||||
|
data: Record<string, any>[];
|
||||||
|
/**显示表格 */
|
||||||
|
showTable: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
tableColumns: [],
|
||||||
|
loading: false,
|
||||||
|
size: 'middle',
|
||||||
|
seached: true,
|
||||||
|
data: [],
|
||||||
|
showTable: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格紧凑型变更操作 */
|
||||||
|
function fnTableSize({ key }: MenuInfo) {
|
||||||
|
tableState.size = key as SizeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams: any = reactive({
|
||||||
|
/**网元类型 */
|
||||||
|
neType: '',
|
||||||
|
/**网元标识 */
|
||||||
|
neId: '',
|
||||||
|
/**开始时间 */
|
||||||
|
startTime: '',
|
||||||
|
/**结束时间 */
|
||||||
|
endTime: '',
|
||||||
|
/**排序字段 */
|
||||||
|
sortField: 'created_at',
|
||||||
|
/**排序方式 */
|
||||||
|
sortOrder: 'desc',
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格分页、排序、筛选变化时触发操作, 排序方式,取值为 ascend descend */
|
||||||
|
function fnTableChange(pagination: any, filters: any, sorter: any, extra: any) {
|
||||||
|
const { columnKey, order } = sorter;
|
||||||
|
if (!order) return;
|
||||||
|
if (order.startsWith(queryParams.sortOrder)) return;
|
||||||
|
|
||||||
|
if (order) {
|
||||||
|
queryParams.sortField = columnKey;
|
||||||
|
queryParams.sortOrder = order.replace('end', '');
|
||||||
|
} else {
|
||||||
|
queryParams.sortOrder = 'asc';
|
||||||
|
}
|
||||||
|
|
||||||
|
fnGetList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对象信息状态类型 */
|
||||||
|
type StateType = {
|
||||||
|
/**网元类型 */
|
||||||
|
neType: string[];
|
||||||
|
/**图表实时统计 */
|
||||||
|
chartRealTime: boolean;
|
||||||
|
/**图表标签选择 */
|
||||||
|
chartLegendSelectedFlag: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对象信息状态 */
|
||||||
|
let state: StateType = reactive({
|
||||||
|
neType: [],
|
||||||
|
chartRealTime: false,
|
||||||
|
chartLegendSelectedFlag: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据列表导出
|
||||||
|
*/
|
||||||
|
function fnRecordExport() {
|
||||||
|
Modal.confirm({
|
||||||
|
title: 'Tip',
|
||||||
|
content: t('views.perfManage.goldTarget.exportSure'),
|
||||||
|
onOk() {
|
||||||
|
const key = 'exportKPI';
|
||||||
|
message.loading({ content: t('common.loading'), key });
|
||||||
|
|
||||||
|
if (tableState.data.length <= 0) {
|
||||||
|
message.error({
|
||||||
|
content: t('views.perfManage.goldTarget.exportEmpty'),
|
||||||
|
key,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tableColumnsTitleArr: string[] = [];
|
||||||
|
const tableColumnsKeyArr: string[] = [];
|
||||||
|
for (const columns of tableColumnsDnd.value) {
|
||||||
|
tableColumnsTitleArr.push(`${columns.title}`);
|
||||||
|
tableColumnsKeyArr.push(`${columns.key}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const kpiDataArr = [];
|
||||||
|
for (const item of tableState.data) {
|
||||||
|
const kpiData: Record<string, any> = {};
|
||||||
|
const keys = Object.keys(item);
|
||||||
|
for (let i = 0; i <= tableColumnsKeyArr.length; i++) {
|
||||||
|
for (const key of keys) {
|
||||||
|
if (tableColumnsKeyArr[i] === key) {
|
||||||
|
const title = tableColumnsTitleArr[i];
|
||||||
|
kpiData[title] = item[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kpiDataArr.push(kpiData);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSheet(kpiDataArr, 'KPI', { header: tableColumnsTitleArr })
|
||||||
|
.then(fileBlob => saveAs(fileBlob, `kpi_data_${Date.now()}.xlsx`))
|
||||||
|
.finally(() => {
|
||||||
|
message.success({
|
||||||
|
content: t('common.msgSuccess', { msg: t('common.export') }),
|
||||||
|
key,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询数据列表表头 */
|
||||||
|
function fnGetListTitle() {
|
||||||
|
// 当前语言
|
||||||
|
var language = currentLocale.value.split('_')[0];
|
||||||
|
if (language === 'zh') language = 'cn';
|
||||||
|
|
||||||
|
// 获取表头文字
|
||||||
|
listCustom({ neType: state.neType[0], status: 'Active' })
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length === 0) {
|
||||||
|
message.error({
|
||||||
|
content: t('views.perfManage.customTarget.kpiIdTip'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
tableState.data = [];
|
||||||
|
tableColumns.value = [];
|
||||||
|
tableColumnsDnd.value = [];
|
||||||
|
fnRanderChartData();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tableColumns.value = [];
|
||||||
|
const columns: ColumnsType = [];
|
||||||
|
for (const item of res.data) {
|
||||||
|
const kpiDisplay = item[`unit`]? item[`title`]+ `(${item['unit']})`:item[`title`];
|
||||||
|
const kpiValue = item[`kpiId`];
|
||||||
|
columns.push({
|
||||||
|
title: kpiDisplay,
|
||||||
|
dataIndex: kpiValue,
|
||||||
|
align: 'left',
|
||||||
|
key: kpiValue,
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
minWidth: 150,
|
||||||
|
maxWidth: 300,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
columns.push({
|
||||||
|
title: t('views.perfManage.perfData.neName'),
|
||||||
|
dataIndex: 'neName',
|
||||||
|
key: 'neName',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
});
|
||||||
|
columns.push({
|
||||||
|
title: t('views.perfManage.goldTarget.time'),
|
||||||
|
dataIndex: 'timeGroup',
|
||||||
|
align: 'left',
|
||||||
|
fixed: 'right',
|
||||||
|
key: 'timeGroup',
|
||||||
|
sorter: true,
|
||||||
|
width: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
tableColumns.value = columns;
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.getInfoFail'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(result => {
|
||||||
|
result && fnGetList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询数据列表 */
|
||||||
|
function fnGetList() {
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
queryParams.neType = state.neType[0];
|
||||||
|
queryParams.neId = state.neType[1];
|
||||||
|
queryParams.startTime = queryRangePicker.value[0];
|
||||||
|
queryParams.endTime = queryRangePicker.value[1];
|
||||||
|
listCustomData(toRaw(queryParams))
|
||||||
|
.then(res => {
|
||||||
|
tableState.loading = false;
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
tablePagination.total = res.data.length;
|
||||||
|
tableState.data = res.data;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.then(result => {
|
||||||
|
if (result) {
|
||||||
|
fnRanderChartData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**切换显示类型 图或表格 */
|
||||||
|
function fnChangShowType() {
|
||||||
|
tableState.showTable = !tableState.showTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**绘制图表 */
|
||||||
|
function fnRanderChart() {
|
||||||
|
const container: HTMLElement | undefined = kpiChartDom.value;
|
||||||
|
if (!container) return;
|
||||||
|
kpiChart.value = markRaw(echarts.init(container, 'light'));
|
||||||
|
|
||||||
|
const option: EChartsOption = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
position: function (pt: any) {
|
||||||
|
return [pt[0], '10%'];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: [], // 数据x轴
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
boundaryGap: [0, '100%'],
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
type: 'scroll',
|
||||||
|
orient: 'vertical',
|
||||||
|
top: 40,
|
||||||
|
right: 20,
|
||||||
|
itemWidth: 20,
|
||||||
|
itemGap: 25,
|
||||||
|
textStyle: {
|
||||||
|
color: '#646A73',
|
||||||
|
},
|
||||||
|
icon: 'circle',
|
||||||
|
selected: {},
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '10%',
|
||||||
|
right: '30%',
|
||||||
|
bottom: '20%',
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
start: 90,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 90,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [], // 数据y轴
|
||||||
|
};
|
||||||
|
kpiChart.value.setOption(option);
|
||||||
|
|
||||||
|
// 创建 ResizeObserver 实例
|
||||||
|
var observer = new ResizeObserver(entries => {
|
||||||
|
if (kpiChart.value) {
|
||||||
|
kpiChart.value.resize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 监听元素大小变化
|
||||||
|
observer.observe(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**图表标签选择 */
|
||||||
|
let chartLegendSelected: Record<string, boolean> = {};
|
||||||
|
/**图表配置数据x轴 */
|
||||||
|
let chartDataXAxisData: string[] = [];
|
||||||
|
/**图表配置数据y轴 */
|
||||||
|
let chartDataYSeriesData: Record<string, any>[] = [];
|
||||||
|
|
||||||
|
/**图表数据渲染 */
|
||||||
|
function fnRanderChartData() {
|
||||||
|
if (kpiChart.value == null && tableState.data.length <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
chartLegendSelected = {};
|
||||||
|
chartDataXAxisData = [];
|
||||||
|
chartDataYSeriesData = [];
|
||||||
|
|
||||||
|
for (var columns of tableColumns.value) {
|
||||||
|
if (
|
||||||
|
columns.key === 'neName' ||
|
||||||
|
columns.key === 'startIndex' ||
|
||||||
|
columns.key === 'timeGroup'
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const color = generateColorRGBA();
|
||||||
|
chartDataYSeriesData.push({
|
||||||
|
name: `${columns.title}`,
|
||||||
|
key: `${columns.key}`,
|
||||||
|
type: 'line',
|
||||||
|
symbol: 'none',
|
||||||
|
sampling: 'lttb',
|
||||||
|
itemStyle: {
|
||||||
|
color: color,
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: color.replace(')', ',0.8)'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: color.replace(')', ',0.3)'),
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
chartLegendSelected[`${columns.title}`] = state.chartLegendSelectedFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用降序就反转
|
||||||
|
let orgData = tableState.data;
|
||||||
|
if (queryParams.sortOrder === 'desc') {
|
||||||
|
orgData = orgData.toReversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of orgData) {
|
||||||
|
chartDataXAxisData.push(item['timeGroup']);
|
||||||
|
const keys = Object.keys(item);
|
||||||
|
for (const y of chartDataYSeriesData) {
|
||||||
|
for (const key of keys) {
|
||||||
|
if (y.key === key) {
|
||||||
|
y.data.push(+item[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// console.log(queryParams.sortOrder, chartLegendSelected);
|
||||||
|
// console.log(chartDataXAxisData, chartDataYSeriesData);
|
||||||
|
|
||||||
|
// 绘制图数据
|
||||||
|
kpiChart.value.setOption(
|
||||||
|
{
|
||||||
|
legend: {
|
||||||
|
selected: chartLegendSelected,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: chartDataXAxisData,
|
||||||
|
},
|
||||||
|
series: chartDataYSeriesData,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
replaceMerge: ['xAxis', 'series'],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**图表折线显示全部 */
|
||||||
|
function fnLegendSelected(bool: any) {
|
||||||
|
for (const key of Object.keys(chartLegendSelected)) {
|
||||||
|
chartLegendSelected[key] = bool;
|
||||||
|
}
|
||||||
|
kpiChart.value.setOption({
|
||||||
|
legend: {
|
||||||
|
selected: chartLegendSelected,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**图表实时统计 */
|
||||||
|
function fnRealTimeSwitch(bool: any) {
|
||||||
|
if (bool) {
|
||||||
|
tableState.seached = false;
|
||||||
|
// 建立链接
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: '/ws',
|
||||||
|
params: {
|
||||||
|
/**订阅通道组
|
||||||
|
*
|
||||||
|
* 指标(GroupID:10_neType_neId)
|
||||||
|
*/
|
||||||
|
subGroupID: `20_${queryParams.neType}_${queryParams.neId}`,
|
||||||
|
},
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: wsError,
|
||||||
|
};
|
||||||
|
ws.connect(options);
|
||||||
|
} else {
|
||||||
|
tableState.seached = true;
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsError(ev: any) {
|
||||||
|
// 接收数据后回调
|
||||||
|
console.error(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsMessage(res: Record<string, any>) {
|
||||||
|
const { code, requestId, data } = res;
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅组信息
|
||||||
|
if (!data?.groupId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// kpiEvent 黄金指标指标事件
|
||||||
|
const kpiEvent = data.data;
|
||||||
|
tableState.data.unshift(kpiEvent);
|
||||||
|
tablePagination.total++;
|
||||||
|
|
||||||
|
// 非对应网元类型
|
||||||
|
if (kpiEvent.neType !== queryParams.neType) return;
|
||||||
|
|
||||||
|
for (const key of Object.keys(data.data)) {
|
||||||
|
const v = kpiEvent[key];
|
||||||
|
// x轴
|
||||||
|
if (key === 'timeGroup') {
|
||||||
|
// chartDataXAxisData.shift();
|
||||||
|
chartDataXAxisData.push(v);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// y轴
|
||||||
|
const yItem = chartDataYSeriesData.find(item => item.key === key);
|
||||||
|
if (yItem) {
|
||||||
|
// yItem.data.shift();
|
||||||
|
yItem.data.push(+v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制图数据
|
||||||
|
kpiChart.value.setOption({
|
||||||
|
xAxis: {
|
||||||
|
data: chartDataXAxisData,
|
||||||
|
},
|
||||||
|
series: chartDataYSeriesData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 目前支持的 AMF AUSF MME MOCNGW NSSF SMF UDM UPF PCF
|
||||||
|
// 获取网元网元列表
|
||||||
|
neInfoStore.fnNelist().then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length > 0) {
|
||||||
|
// 过滤不可用的网元
|
||||||
|
neCascaderOptions.value = neInfoStore.getNeCascaderOptions.filter(
|
||||||
|
(item: any) => {
|
||||||
|
return !['OMC', 'NSSF', 'NEF', 'NRF', 'LMF', 'N3IWF'].includes(
|
||||||
|
item.value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (neCascaderOptions.value.length === 0) {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 无查询参数neType时 默认选择UPF
|
||||||
|
const queryNeType = (route.query.neType as string) || 'UPF';
|
||||||
|
const item = neCascaderOptions.value.find(s => s.value === queryNeType);
|
||||||
|
if (item && item.children) {
|
||||||
|
const info = item.children[0];
|
||||||
|
state.neType = [info.neType, info.neId];
|
||||||
|
queryParams.neType = info.neType;
|
||||||
|
queryParams.neId = info.neId;
|
||||||
|
} else {
|
||||||
|
const info = neCascaderOptions.value[0].children[0];
|
||||||
|
state.neType = [info.neType, info.neId];
|
||||||
|
queryParams.neType = info.neType;
|
||||||
|
queryParams.neId = info.neId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询当前小时
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
now.setMinutes(0, 0, 0);
|
||||||
|
queryRangePicker.value[0] = parseDateToStr(now.getTime());
|
||||||
|
now.setMinutes(59, 59, 59);
|
||||||
|
queryRangePicker.value[1] = parseDateToStr(now.getTime());
|
||||||
|
fnGetListTitle();
|
||||||
|
// 绘图
|
||||||
|
fnRanderChart();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
ws.close();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card
|
||||||
|
v-show="tableState.seached"
|
||||||
|
:bordered="false"
|
||||||
|
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
||||||
|
>
|
||||||
|
<!-- 表格搜索栏 -->
|
||||||
|
<a-form :model="queryParams" name="queryParamsFrom" layout="horizontal">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
name="neType"
|
||||||
|
:label="t('views.ne.common.neType')"
|
||||||
|
>
|
||||||
|
<a-cascader
|
||||||
|
v-model:value="state.neType"
|
||||||
|
:options="neCascaderOptions"
|
||||||
|
:allow-clear="false"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="10" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.perfManage.goldTarget.timeFrame')"
|
||||||
|
name="timeFrame"
|
||||||
|
>
|
||||||
|
<a-range-picker
|
||||||
|
v-model:value="queryRangePicker"
|
||||||
|
bordered
|
||||||
|
:allow-clear="false"
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-range-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :lg="2" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
@click.prevent="fnGetListTitle()"
|
||||||
|
>
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</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"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
@click.prevent="fnChangShowType()"
|
||||||
|
>
|
||||||
|
<template #icon> <AreaChartOutlined /> </template>
|
||||||
|
{{
|
||||||
|
tableState.showTable
|
||||||
|
? t('views.perfManage.goldTarget.kpiChartTitle')
|
||||||
|
: t('views.perfManage.goldTarget.kpiTableTitle')
|
||||||
|
}}
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="dashed"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
@click.prevent="fnRecordExport()"
|
||||||
|
v-show="tableState.showTable"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<ExportOutlined />
|
||||||
|
</template>
|
||||||
|
{{ t('common.export') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center" v-show="tableState.showTable">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<TableColumnsDnd
|
||||||
|
v-if="tableColumns.length > 0"
|
||||||
|
:cache-id="`kpiTarget_${state.neType[0]}`"
|
||||||
|
:columns="tableColumns"
|
||||||
|
v-model:columns-dnd="tableColumnsDnd"
|
||||||
|
></TableColumnsDnd>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.sizeText') }}</template>
|
||||||
|
<a-dropdown trigger="click" placement="bottomRight">
|
||||||
|
<a-button type="text">
|
||||||
|
<template #icon><ColumnHeightOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu
|
||||||
|
:selected-keys="[tableState.size as string]"
|
||||||
|
@click="fnTableSize"
|
||||||
|
>
|
||||||
|
<a-menu-item key="default">
|
||||||
|
{{ t('common.size.default') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="middle">
|
||||||
|
{{ t('common.size.middle') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="small">
|
||||||
|
{{ t('common.size.small') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
<a-form layout="inline" v-show="!tableState.showTable">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.perfManage.goldTarget.showChartSelected')"
|
||||||
|
name="chartLegendSelectedFlag"
|
||||||
|
>
|
||||||
|
<a-switch
|
||||||
|
:disabled="tableState.loading"
|
||||||
|
v-model:checked="state.chartLegendSelectedFlag"
|
||||||
|
:checked-children="t('common.switch.open')"
|
||||||
|
:un-checked-children="t('common.switch.shut')"
|
||||||
|
@change="fnLegendSelected"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.perfManage.goldTarget.realTimeData')"
|
||||||
|
name="chartRealTime"
|
||||||
|
>
|
||||||
|
<a-switch
|
||||||
|
:disabled="tableState.loading"
|
||||||
|
v-model:checked="state.chartRealTime"
|
||||||
|
:checked-children="t('common.switch.open')"
|
||||||
|
:un-checked-children="t('common.switch.shut')"
|
||||||
|
@change="fnRealTimeSwitch"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
v-show="tableState.showTable"
|
||||||
|
class="table"
|
||||||
|
row-key="id"
|
||||||
|
:columns="tableColumnsDnd"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: tableColumnsDnd.length * 200, y: 'calc(100vh - 480px)' }"
|
||||||
|
@resizeColumn="(w:number, col:any) => (col.width = w)"
|
||||||
|
:show-expand-column="false"
|
||||||
|
@change="fnTableChange"
|
||||||
|
>
|
||||||
|
</a-table>
|
||||||
|
|
||||||
|
<!-- 图表 -->
|
||||||
|
<div style="padding: 24px" v-show="!tableState.showTable">
|
||||||
|
<div ref="kpiChartDom" style="height: 450px; width: 100%"></div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
||||||
@@ -1,543 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted, toRaw } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { Modal } from 'ant-design-vue/lib';
|
|
||||||
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
|
||||||
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
|
|
||||||
import { ColumnsType } from 'ant-design-vue/lib/table';
|
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
|
||||||
import { saveAs } from 'file-saver';
|
|
||||||
import useI18n from '@/hooks/useI18n';
|
|
||||||
import { getTraceRawInfo, listTraceData } from '@/api/traceManage/analysis';
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
/**查询参数 */
|
onMounted(() => {});
|
||||||
let queryParams = reactive({
|
|
||||||
/**移动号 */
|
|
||||||
imsi: '',
|
|
||||||
/**移动号 */
|
|
||||||
msisdn: '',
|
|
||||||
/**当前页数 */
|
|
||||||
pageNum: 1,
|
|
||||||
/**每页条数 */
|
|
||||||
pageSize: 20,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**查询参数重置 */
|
|
||||||
function fnQueryReset() {
|
|
||||||
queryParams = Object.assign(queryParams, {
|
|
||||||
imsi: '',
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 20,
|
|
||||||
});
|
|
||||||
tablePagination.current = 1;
|
|
||||||
tablePagination.pageSize = 20;
|
|
||||||
fnGetList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**表格状态类型 */
|
|
||||||
type TabeStateType = {
|
|
||||||
/**加载等待 */
|
|
||||||
loading: boolean;
|
|
||||||
/**紧凑型 */
|
|
||||||
size: SizeType;
|
|
||||||
/**搜索栏 */
|
|
||||||
seached: boolean;
|
|
||||||
/**记录数据 */
|
|
||||||
data: object[];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**表格状态 */
|
|
||||||
let tableState: TabeStateType = reactive({
|
|
||||||
loading: false,
|
|
||||||
size: 'middle',
|
|
||||||
seached: true,
|
|
||||||
data: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
/**表格字段列 */
|
|
||||||
let tableColumns: ColumnsType = [
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.trackTaskId'),
|
|
||||||
dataIndex: 'taskId',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.imsi'),
|
|
||||||
dataIndex: 'imsi',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.msisdn'),
|
|
||||||
dataIndex: 'msisdn',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.srcIp'),
|
|
||||||
dataIndex: 'srcAddr',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.dstIp'),
|
|
||||||
dataIndex: 'dstAddr',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.signalType'),
|
|
||||||
dataIndex: 'ifType',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.msgType'),
|
|
||||||
dataIndex: 'msgType',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.msgDirect'),
|
|
||||||
dataIndex: 'msgDirect',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.rowTime'),
|
|
||||||
dataIndex: 'timestamp',
|
|
||||||
align: 'center',
|
|
||||||
customRender(opt) {
|
|
||||||
if (!opt.value) return '';
|
|
||||||
return parseDateToStr(opt.value);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('common.operate'),
|
|
||||||
key: 'id',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
/**表格分页器参数 */
|
|
||||||
let tablePagination = reactive({
|
|
||||||
/**当前页数 */
|
|
||||||
current: 1,
|
|
||||||
/**每页条数 */
|
|
||||||
pageSize: 20,
|
|
||||||
/**默认的每页条数 */
|
|
||||||
defaultPageSize: 20,
|
|
||||||
/**指定每页可以显示多少条 */
|
|
||||||
pageSizeOptions: ['10', '20', '50', '100'],
|
|
||||||
/**只有一页时是否隐藏分页器 */
|
|
||||||
hideOnSinglePage: false,
|
|
||||||
/**是否可以快速跳转至某页 */
|
|
||||||
showQuickJumper: true,
|
|
||||||
/**是否可以改变 pageSize */
|
|
||||||
showSizeChanger: true,
|
|
||||||
/**数据总数 */
|
|
||||||
total: 0,
|
|
||||||
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
|
|
||||||
onChange: (page: number, pageSize: number) => {
|
|
||||||
tablePagination.current = page;
|
|
||||||
tablePagination.pageSize = pageSize;
|
|
||||||
queryParams.pageNum = page;
|
|
||||||
queryParams.pageSize = pageSize;
|
|
||||||
fnGetList();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**表格紧凑型变更操作 */
|
|
||||||
function fnTableSize({ key }: MenuInfo) {
|
|
||||||
tableState.size = key as SizeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**查询备份信息列表, pageNum初始页数 */
|
|
||||||
function fnGetList(pageNum?: number) {
|
|
||||||
if (tableState.loading) return;
|
|
||||||
tableState.loading = true;
|
|
||||||
if (pageNum) {
|
|
||||||
queryParams.pageNum = pageNum;
|
|
||||||
}
|
|
||||||
listTraceData(toRaw(queryParams)).then(res => {
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
|
||||||
tablePagination.total = res.total;
|
|
||||||
tableState.data = res.rows;
|
|
||||||
if (
|
|
||||||
tablePagination.total <=
|
|
||||||
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
|
||||||
queryParams.pageNum !== 1
|
|
||||||
) {
|
|
||||||
tableState.loading = false;
|
|
||||||
fnGetList(queryParams.pageNum - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tableState.loading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**抽屉对象信息状态类型 */
|
|
||||||
type ModalStateType = {
|
|
||||||
/**抽屉框是否显示 */
|
|
||||||
visible: boolean;
|
|
||||||
/**标题 */
|
|
||||||
title: string;
|
|
||||||
/**表单数据 */
|
|
||||||
from: Record<string, any>;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**抽屉对象信息状态 */
|
|
||||||
let modalState: ModalStateType = reactive({
|
|
||||||
visible: false,
|
|
||||||
title: '',
|
|
||||||
from: {
|
|
||||||
rawData: '',
|
|
||||||
rawDataHTML: '',
|
|
||||||
downBtn: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对话框弹出显示
|
|
||||||
* @param row 记录信息
|
|
||||||
*/
|
|
||||||
function fnModalVisible(row: Record<string, any>) {
|
|
||||||
// 进制转数据
|
|
||||||
const hexString = parseBase64Data(row.rawMsg);
|
|
||||||
const rawData = convertToReadableFormat(hexString);
|
|
||||||
modalState.from.rawData = rawData;
|
|
||||||
// RAW解析HTML
|
|
||||||
getTraceRawInfo(row.id).then(res => {
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
|
||||||
const htmlString = rawDataHTMLScript(res.msg);
|
|
||||||
modalState.from.rawDataHTML = htmlString;
|
|
||||||
modalState.from.downBtn = true;
|
|
||||||
} else {
|
|
||||||
modalState.from.rawDataHTML = t('views.traceManage.analysis.noData');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
modalState.title = t('views.traceManage.analysis.taskTitle', {
|
|
||||||
num: row.imsi,
|
|
||||||
});
|
|
||||||
modalState.visible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对话框弹出关闭
|
|
||||||
*/
|
|
||||||
function fnModalVisibleClose() {
|
|
||||||
modalState.visible = false;
|
|
||||||
modalState.from.downBtn = false;
|
|
||||||
modalState.from.rawDataHTML = '';
|
|
||||||
modalState.from.rawData = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将Base64编码解码为字节数组
|
|
||||||
function parseBase64Data(hexData: string) {
|
|
||||||
// 将Base64编码解码为字节数组
|
|
||||||
const byteString = atob(hexData);
|
|
||||||
const byteArray = new Uint8Array(byteString.length);
|
|
||||||
for (let i = 0; i < byteString.length; i++) {
|
|
||||||
byteArray[i] = byteString.charCodeAt(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将每一个字节转换为2位16进制数表示,并拼接起来
|
|
||||||
let hexString = '';
|
|
||||||
for (let i = 0; i < byteArray.length; i++) {
|
|
||||||
const hex = byteArray[i].toString(16);
|
|
||||||
hexString += hex.length === 1 ? '0' + hex : hex;
|
|
||||||
}
|
|
||||||
return hexString;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 转换十六进制字节流为可读格式和ASCII码表示
|
|
||||||
function convertToReadableFormat(hexString: string) {
|
|
||||||
let result = '';
|
|
||||||
let asciiResult = '';
|
|
||||||
let arr = [];
|
|
||||||
let row = 100;
|
|
||||||
for (let i = 0; i < hexString.length; i += 2) {
|
|
||||||
const hexChars = hexString.substring(i, i + 2);
|
|
||||||
const decimal = parseInt(hexChars, 16);
|
|
||||||
const asciiChar =
|
|
||||||
decimal >= 32 && decimal <= 126 ? String.fromCharCode(decimal) : '.';
|
|
||||||
|
|
||||||
result += hexChars + ' ';
|
|
||||||
asciiResult += asciiChar;
|
|
||||||
|
|
||||||
if ((i + 2) % 32 === 0) {
|
|
||||||
arr.push({
|
|
||||||
row: row,
|
|
||||||
code: result,
|
|
||||||
asciiText: asciiResult,
|
|
||||||
});
|
|
||||||
result = '';
|
|
||||||
asciiResult = '';
|
|
||||||
row += 10;
|
|
||||||
}
|
|
||||||
if (2 + i == hexString.length) {
|
|
||||||
arr.push({
|
|
||||||
row: row,
|
|
||||||
code: result,
|
|
||||||
asciiText: asciiResult,
|
|
||||||
});
|
|
||||||
result = '';
|
|
||||||
asciiResult = '';
|
|
||||||
row += 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 信息详情HTMl内容处理
|
|
||||||
function rawDataHTMLScript(htmlString: string) {
|
|
||||||
// 删除所有 <a> 标签
|
|
||||||
// const withoutATags = htmlString.replace(/<a\b[^>]*>(.*?)<\/a>/gi, '');
|
|
||||||
// 删除所有 <script> 标签
|
|
||||||
let withoutScriptTags = htmlString.replace(
|
|
||||||
/<script\b[^>]*>([\s\S]*?)<\/script>/gi,
|
|
||||||
''
|
|
||||||
);
|
|
||||||
// 默认全展开
|
|
||||||
// const withoutHiddenElements = withoutScriptTags.replace(
|
|
||||||
// /style="display:none"/gi,
|
|
||||||
// 'style="background:#ffffff"'
|
|
||||||
// );
|
|
||||||
|
|
||||||
function set_node(node: any, str: string) {
|
|
||||||
if (!node) return;
|
|
||||||
node.style.display = str;
|
|
||||||
node.style.background = '#ffffff';
|
|
||||||
}
|
|
||||||
Reflect.set(window, 'set_node', set_node);
|
|
||||||
function toggle_node(node: any) {
|
|
||||||
node = document.getElementById(node);
|
|
||||||
if (!node) return;
|
|
||||||
set_node(node, node.style.display != 'none' ? 'none' : 'block');
|
|
||||||
}
|
|
||||||
Reflect.set(window, 'toggle_node', toggle_node);
|
|
||||||
function hide_node(node: any) {
|
|
||||||
node = document.getElementById(node);
|
|
||||||
if (!node) return;
|
|
||||||
set_node(node, 'none');
|
|
||||||
}
|
|
||||||
Reflect.set(window, 'hide_node', hide_node);
|
|
||||||
|
|
||||||
// 展开第一个
|
|
||||||
withoutScriptTags = withoutScriptTags.replace(
|
|
||||||
'id="f1c" style="display:none"',
|
|
||||||
'id="f1c" style="display:block"'
|
|
||||||
);
|
|
||||||
return withoutScriptTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**信息文件下载 */
|
|
||||||
function fnDownloadFile() {
|
|
||||||
Modal.confirm({
|
|
||||||
title: t('common.tipTitle'),
|
|
||||||
content: t('views.traceManage.analysis.taskDownTip'),
|
|
||||||
onOk() {
|
|
||||||
const blob = new Blob([modalState.from.rawDataHTML], {
|
|
||||||
type: 'text/plain',
|
|
||||||
});
|
|
||||||
saveAs(blob, `${modalState.title}_${Date.now()}.html`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
// 获取列表数据
|
|
||||||
fnGetList();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<a-card
|
|
||||||
v-show="tableState.seached"
|
|
||||||
:bordered="false"
|
|
||||||
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
|
||||||
>
|
|
||||||
<!-- 表格搜索栏 -->
|
|
||||||
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.traceManage.analysis.imsi')"
|
|
||||||
name="imsi"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="queryParams.imsi"
|
|
||||||
:allow-clear="true"
|
|
||||||
:placeholder="t('views.traceManage.analysis.imsiPlease')"
|
|
||||||
></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.traceManage.analysis.msisdn')"
|
|
||||||
name="imsi"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="queryParams.msisdn"
|
|
||||||
:allow-clear="true"
|
|
||||||
:placeholder="t('views.traceManage.analysis.msisdnPlease')"
|
|
||||||
></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
|
||||||
<a-form-item>
|
|
||||||
<a-space :size="8">
|
|
||||||
<a-button type="primary" @click.prevent="fnGetList(1)">
|
|
||||||
<template #icon><SearchOutlined /></template>
|
|
||||||
{{ t('common.search') }}
|
|
||||||
</a-button>
|
|
||||||
<a-button type="default" @click.prevent="fnQueryReset">
|
|
||||||
<template #icon><ClearOutlined /></template>
|
|
||||||
{{ t('common.reset') }}
|
|
||||||
</a-button>
|
|
||||||
</a-space>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</a-form>
|
|
||||||
</a-card>
|
|
||||||
|
|
||||||
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
<!-- 插槽-卡片左侧侧 -->
|
<h1>Perf Report</h1>
|
||||||
<template #title> </template>
|
|
||||||
|
|
||||||
<!-- 插槽-卡片右侧 -->
|
|
||||||
<template #extra>
|
|
||||||
<a-space :size="8" align="center">
|
|
||||||
<a-tooltip>
|
|
||||||
<template #title>{{ t('common.searchBarText') }}</template>
|
|
||||||
<a-switch
|
|
||||||
v-model:checked="tableState.seached"
|
|
||||||
:checked-children="t('common.switch.show')"
|
|
||||||
:un-checked-children="t('common.switch.hide')"
|
|
||||||
size="small"
|
|
||||||
/>
|
|
||||||
</a-tooltip>
|
|
||||||
<a-tooltip>
|
|
||||||
<template #title>{{ t('common.reloadText') }}</template>
|
|
||||||
<a-button type="text" @click.prevent="fnGetList()">
|
|
||||||
<template #icon><ReloadOutlined /></template>
|
|
||||||
</a-button>
|
|
||||||
</a-tooltip>
|
|
||||||
<a-tooltip>
|
|
||||||
<template #title>{{ t('common.sizeText') }}</template>
|
|
||||||
<a-dropdown trigger="click">
|
|
||||||
<a-button type="text">
|
|
||||||
<template #icon><ColumnHeightOutlined /></template>
|
|
||||||
</a-button>
|
|
||||||
<template #overlay>
|
|
||||||
<a-menu
|
|
||||||
:selected-keys="[tableState.size as string]"
|
|
||||||
@click="fnTableSize"
|
|
||||||
>
|
|
||||||
<a-menu-item key="default">{{
|
|
||||||
t('common.size.default')
|
|
||||||
}}</a-menu-item>
|
|
||||||
<a-menu-item key="middle">{{
|
|
||||||
t('common.size.middle')
|
|
||||||
}}</a-menu-item>
|
|
||||||
<a-menu-item key="small">{{
|
|
||||||
t('common.size.small')
|
|
||||||
}}</a-menu-item>
|
|
||||||
</a-menu>
|
|
||||||
</template>
|
|
||||||
</a-dropdown>
|
|
||||||
</a-tooltip>
|
|
||||||
</a-space>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- 表格列表 -->
|
|
||||||
<a-table
|
|
||||||
class="table"
|
|
||||||
row-key="id"
|
|
||||||
:columns="tableColumns"
|
|
||||||
:loading="tableState.loading"
|
|
||||||
:data-source="tableState.data"
|
|
||||||
:size="tableState.size"
|
|
||||||
:pagination="tablePagination"
|
|
||||||
:scroll="{ x: true }"
|
|
||||||
>
|
|
||||||
<template #bodyCell="{ column, record }">
|
|
||||||
<template v-if="column.key === 'id'">
|
|
||||||
<a-space :size="8" align="center">
|
|
||||||
<a-tooltip>
|
|
||||||
<template #title>查看详情</template>
|
|
||||||
<a-button type="link" @click.prevent="fnModalVisible(record)">
|
|
||||||
<template #icon><ProfileOutlined /></template>
|
|
||||||
</a-button>
|
|
||||||
</a-tooltip>
|
|
||||||
</a-space>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</a-table>
|
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
<!-- 详情框 -->
|
|
||||||
<ProModal
|
|
||||||
:drag="true"
|
|
||||||
:width="800"
|
|
||||||
:title="modalState.title"
|
|
||||||
:visible="modalState.visible"
|
|
||||||
@cancel="fnModalVisibleClose"
|
|
||||||
>
|
|
||||||
<div class="raw-title">
|
|
||||||
{{ t('views.traceManage.analysis.signalData') }}
|
|
||||||
</div>
|
|
||||||
<a-row
|
|
||||||
class="raw"
|
|
||||||
:gutter="16"
|
|
||||||
v-for="v in modalState.from.rawData"
|
|
||||||
:key="v.row"
|
|
||||||
>
|
|
||||||
<a-col class="num" :span="2">{{ v.row }}</a-col>
|
|
||||||
<a-col class="code" :span="12">{{ v.code }}</a-col>
|
|
||||||
<a-col class="txt" :span="10">{{ v.asciiText }}</a-col>
|
|
||||||
</a-row>
|
|
||||||
<a-divider />
|
|
||||||
<div class="raw-title">
|
|
||||||
{{ t('views.traceManage.analysis.signalDetail') }}
|
|
||||||
<a-button
|
|
||||||
type="dashed"
|
|
||||||
size="small"
|
|
||||||
@click.prevent="fnDownloadFile"
|
|
||||||
v-if="modalState.from.downBtn"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<DownloadOutlined />
|
|
||||||
</template>
|
|
||||||
{{ t('views.traceManage.analysis.taskDownText') }}
|
|
||||||
</a-button>
|
|
||||||
</div>
|
|
||||||
<div class="raw-html" v-html="modalState.from.rawDataHTML"></div>
|
|
||||||
</ProModal>
|
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped></style>
|
||||||
.table :deep(.ant-pagination) {
|
|
||||||
padding: 0 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.raw {
|
|
||||||
&-title {
|
|
||||||
color: #000000d9;
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: 1.8;
|
|
||||||
}
|
|
||||||
.num {
|
|
||||||
background-color: #e5e5e5;
|
|
||||||
}
|
|
||||||
.code {
|
|
||||||
background-color: #e7e6ff;
|
|
||||||
}
|
|
||||||
.txt {
|
|
||||||
background-color: #ffe3e5;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-html {
|
|
||||||
max-height: 300px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,543 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted, toRaw } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { Modal } from 'ant-design-vue/lib';
|
|
||||||
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
|
||||||
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
|
|
||||||
import { ColumnsType } from 'ant-design-vue/lib/table';
|
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
|
||||||
import { saveAs } from 'file-saver';
|
|
||||||
import useI18n from '@/hooks/useI18n';
|
|
||||||
import { getTraceRawInfo, listTraceData } from '@/api/traceManage/analysis';
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
/**查询参数 */
|
onMounted(() => {});
|
||||||
let queryParams = reactive({
|
|
||||||
/**移动号 */
|
|
||||||
imsi: '',
|
|
||||||
/**移动号 */
|
|
||||||
msisdn: '',
|
|
||||||
/**当前页数 */
|
|
||||||
pageNum: 1,
|
|
||||||
/**每页条数 */
|
|
||||||
pageSize: 20,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**查询参数重置 */
|
|
||||||
function fnQueryReset() {
|
|
||||||
queryParams = Object.assign(queryParams, {
|
|
||||||
imsi: '',
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 20,
|
|
||||||
});
|
|
||||||
tablePagination.current = 1;
|
|
||||||
tablePagination.pageSize = 20;
|
|
||||||
fnGetList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**表格状态类型 */
|
|
||||||
type TabeStateType = {
|
|
||||||
/**加载等待 */
|
|
||||||
loading: boolean;
|
|
||||||
/**紧凑型 */
|
|
||||||
size: SizeType;
|
|
||||||
/**搜索栏 */
|
|
||||||
seached: boolean;
|
|
||||||
/**记录数据 */
|
|
||||||
data: object[];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**表格状态 */
|
|
||||||
let tableState: TabeStateType = reactive({
|
|
||||||
loading: false,
|
|
||||||
size: 'middle',
|
|
||||||
seached: true,
|
|
||||||
data: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
/**表格字段列 */
|
|
||||||
let tableColumns: ColumnsType = [
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.trackTaskId'),
|
|
||||||
dataIndex: 'taskId',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.imsi'),
|
|
||||||
dataIndex: 'imsi',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.msisdn'),
|
|
||||||
dataIndex: 'msisdn',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.srcIp'),
|
|
||||||
dataIndex: 'srcAddr',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.dstIp'),
|
|
||||||
dataIndex: 'dstAddr',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.signalType'),
|
|
||||||
dataIndex: 'ifType',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.msgType'),
|
|
||||||
dataIndex: 'msgType',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.msgDirect'),
|
|
||||||
dataIndex: 'msgDirect',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.analysis.rowTime'),
|
|
||||||
dataIndex: 'timestamp',
|
|
||||||
align: 'center',
|
|
||||||
customRender(opt) {
|
|
||||||
if (!opt.value) return '';
|
|
||||||
return parseDateToStr(opt.value);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('common.operate'),
|
|
||||||
key: 'id',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
/**表格分页器参数 */
|
|
||||||
let tablePagination = reactive({
|
|
||||||
/**当前页数 */
|
|
||||||
current: 1,
|
|
||||||
/**每页条数 */
|
|
||||||
pageSize: 20,
|
|
||||||
/**默认的每页条数 */
|
|
||||||
defaultPageSize: 20,
|
|
||||||
/**指定每页可以显示多少条 */
|
|
||||||
pageSizeOptions: ['10', '20', '50', '100'],
|
|
||||||
/**只有一页时是否隐藏分页器 */
|
|
||||||
hideOnSinglePage: false,
|
|
||||||
/**是否可以快速跳转至某页 */
|
|
||||||
showQuickJumper: true,
|
|
||||||
/**是否可以改变 pageSize */
|
|
||||||
showSizeChanger: true,
|
|
||||||
/**数据总数 */
|
|
||||||
total: 0,
|
|
||||||
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
|
|
||||||
onChange: (page: number, pageSize: number) => {
|
|
||||||
tablePagination.current = page;
|
|
||||||
tablePagination.pageSize = pageSize;
|
|
||||||
queryParams.pageNum = page;
|
|
||||||
queryParams.pageSize = pageSize;
|
|
||||||
fnGetList();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**表格紧凑型变更操作 */
|
|
||||||
function fnTableSize({ key }: MenuInfo) {
|
|
||||||
tableState.size = key as SizeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**查询备份信息列表, pageNum初始页数 */
|
|
||||||
function fnGetList(pageNum?: number) {
|
|
||||||
if (tableState.loading) return;
|
|
||||||
tableState.loading = true;
|
|
||||||
if (pageNum) {
|
|
||||||
queryParams.pageNum = pageNum;
|
|
||||||
}
|
|
||||||
listTraceData(toRaw(queryParams)).then(res => {
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
|
||||||
tablePagination.total = res.total;
|
|
||||||
tableState.data = res.rows;
|
|
||||||
if (
|
|
||||||
tablePagination.total <=
|
|
||||||
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
|
||||||
queryParams.pageNum !== 1
|
|
||||||
) {
|
|
||||||
tableState.loading = false;
|
|
||||||
fnGetList(queryParams.pageNum - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tableState.loading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**抽屉对象信息状态类型 */
|
|
||||||
type ModalStateType = {
|
|
||||||
/**抽屉框是否显示 */
|
|
||||||
visible: boolean;
|
|
||||||
/**标题 */
|
|
||||||
title: string;
|
|
||||||
/**表单数据 */
|
|
||||||
from: Record<string, any>;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**抽屉对象信息状态 */
|
|
||||||
let modalState: ModalStateType = reactive({
|
|
||||||
visible: false,
|
|
||||||
title: '',
|
|
||||||
from: {
|
|
||||||
rawData: '',
|
|
||||||
rawDataHTML: '',
|
|
||||||
downBtn: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对话框弹出显示
|
|
||||||
* @param row 记录信息
|
|
||||||
*/
|
|
||||||
function fnModalVisible(row: Record<string, any>) {
|
|
||||||
// 进制转数据
|
|
||||||
const hexString = parseBase64Data(row.rawMsg);
|
|
||||||
const rawData = convertToReadableFormat(hexString);
|
|
||||||
modalState.from.rawData = rawData;
|
|
||||||
// RAW解析HTML
|
|
||||||
getTraceRawInfo(row.id).then(res => {
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
|
||||||
const htmlString = rawDataHTMLScript(res.msg);
|
|
||||||
modalState.from.rawDataHTML = htmlString;
|
|
||||||
modalState.from.downBtn = true;
|
|
||||||
} else {
|
|
||||||
modalState.from.rawDataHTML = t('views.traceManage.analysis.noData');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
modalState.title = t('views.traceManage.analysis.taskTitle', {
|
|
||||||
num: row.imsi,
|
|
||||||
});
|
|
||||||
modalState.visible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对话框弹出关闭
|
|
||||||
*/
|
|
||||||
function fnModalVisibleClose() {
|
|
||||||
modalState.visible = false;
|
|
||||||
modalState.from.downBtn = false;
|
|
||||||
modalState.from.rawDataHTML = '';
|
|
||||||
modalState.from.rawData = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将Base64编码解码为字节数组
|
|
||||||
function parseBase64Data(hexData: string) {
|
|
||||||
// 将Base64编码解码为字节数组
|
|
||||||
const byteString = atob(hexData);
|
|
||||||
const byteArray = new Uint8Array(byteString.length);
|
|
||||||
for (let i = 0; i < byteString.length; i++) {
|
|
||||||
byteArray[i] = byteString.charCodeAt(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将每一个字节转换为2位16进制数表示,并拼接起来
|
|
||||||
let hexString = '';
|
|
||||||
for (let i = 0; i < byteArray.length; i++) {
|
|
||||||
const hex = byteArray[i].toString(16);
|
|
||||||
hexString += hex.length === 1 ? '0' + hex : hex;
|
|
||||||
}
|
|
||||||
return hexString;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 转换十六进制字节流为可读格式和ASCII码表示
|
|
||||||
function convertToReadableFormat(hexString: string) {
|
|
||||||
let result = '';
|
|
||||||
let asciiResult = '';
|
|
||||||
let arr = [];
|
|
||||||
let row = 100;
|
|
||||||
for (let i = 0; i < hexString.length; i += 2) {
|
|
||||||
const hexChars = hexString.substring(i, i + 2);
|
|
||||||
const decimal = parseInt(hexChars, 16);
|
|
||||||
const asciiChar =
|
|
||||||
decimal >= 32 && decimal <= 126 ? String.fromCharCode(decimal) : '.';
|
|
||||||
|
|
||||||
result += hexChars + ' ';
|
|
||||||
asciiResult += asciiChar;
|
|
||||||
|
|
||||||
if ((i + 2) % 32 === 0) {
|
|
||||||
arr.push({
|
|
||||||
row: row,
|
|
||||||
code: result,
|
|
||||||
asciiText: asciiResult,
|
|
||||||
});
|
|
||||||
result = '';
|
|
||||||
asciiResult = '';
|
|
||||||
row += 10;
|
|
||||||
}
|
|
||||||
if (2 + i == hexString.length) {
|
|
||||||
arr.push({
|
|
||||||
row: row,
|
|
||||||
code: result,
|
|
||||||
asciiText: asciiResult,
|
|
||||||
});
|
|
||||||
result = '';
|
|
||||||
asciiResult = '';
|
|
||||||
row += 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 信息详情HTMl内容处理
|
|
||||||
function rawDataHTMLScript(htmlString: string) {
|
|
||||||
// 删除所有 <a> 标签
|
|
||||||
// const withoutATags = htmlString.replace(/<a\b[^>]*>(.*?)<\/a>/gi, '');
|
|
||||||
// 删除所有 <script> 标签
|
|
||||||
let withoutScriptTags = htmlString.replace(
|
|
||||||
/<script\b[^>]*>([\s\S]*?)<\/script>/gi,
|
|
||||||
''
|
|
||||||
);
|
|
||||||
// 默认全展开
|
|
||||||
// const withoutHiddenElements = withoutScriptTags.replace(
|
|
||||||
// /style="display:none"/gi,
|
|
||||||
// 'style="background:#ffffff"'
|
|
||||||
// );
|
|
||||||
|
|
||||||
function set_node(node: any, str: string) {
|
|
||||||
if (!node) return;
|
|
||||||
node.style.display = str;
|
|
||||||
node.style.background = '#ffffff';
|
|
||||||
}
|
|
||||||
Reflect.set(window, 'set_node', set_node);
|
|
||||||
function toggle_node(node: any) {
|
|
||||||
node = document.getElementById(node);
|
|
||||||
if (!node) return;
|
|
||||||
set_node(node, node.style.display != 'none' ? 'none' : 'block');
|
|
||||||
}
|
|
||||||
Reflect.set(window, 'toggle_node', toggle_node);
|
|
||||||
function hide_node(node: any) {
|
|
||||||
node = document.getElementById(node);
|
|
||||||
if (!node) return;
|
|
||||||
set_node(node, 'none');
|
|
||||||
}
|
|
||||||
Reflect.set(window, 'hide_node', hide_node);
|
|
||||||
|
|
||||||
// 展开第一个
|
|
||||||
withoutScriptTags = withoutScriptTags.replace(
|
|
||||||
'id="f1c" style="display:none"',
|
|
||||||
'id="f1c" style="display:block"'
|
|
||||||
);
|
|
||||||
return withoutScriptTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**信息文件下载 */
|
|
||||||
function fnDownloadFile() {
|
|
||||||
Modal.confirm({
|
|
||||||
title: t('common.tipTitle'),
|
|
||||||
content: t('views.traceManage.analysis.taskDownTip'),
|
|
||||||
onOk() {
|
|
||||||
const blob = new Blob([modalState.from.rawDataHTML], {
|
|
||||||
type: 'text/plain',
|
|
||||||
});
|
|
||||||
saveAs(blob, `${modalState.title}_${Date.now()}.html`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
// 获取列表数据
|
|
||||||
fnGetList();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<a-card
|
|
||||||
v-show="tableState.seached"
|
|
||||||
:bordered="false"
|
|
||||||
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
|
||||||
>
|
|
||||||
<!-- 表格搜索栏 -->
|
|
||||||
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.traceManage.analysis.imsi')"
|
|
||||||
name="imsi"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="queryParams.imsi"
|
|
||||||
:allow-clear="true"
|
|
||||||
:placeholder="t('views.traceManage.analysis.imsiPlease')"
|
|
||||||
></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.traceManage.analysis.msisdn')"
|
|
||||||
name="imsi"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="queryParams.msisdn"
|
|
||||||
:allow-clear="true"
|
|
||||||
:placeholder="t('views.traceManage.analysis.msisdnPlease')"
|
|
||||||
></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
|
||||||
<a-form-item>
|
|
||||||
<a-space :size="8">
|
|
||||||
<a-button type="primary" @click.prevent="fnGetList(1)">
|
|
||||||
<template #icon><SearchOutlined /></template>
|
|
||||||
{{ t('common.search') }}
|
|
||||||
</a-button>
|
|
||||||
<a-button type="default" @click.prevent="fnQueryReset">
|
|
||||||
<template #icon><ClearOutlined /></template>
|
|
||||||
{{ t('common.reset') }}
|
|
||||||
</a-button>
|
|
||||||
</a-space>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</a-form>
|
|
||||||
</a-card>
|
|
||||||
|
|
||||||
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
<!-- 插槽-卡片左侧侧 -->
|
<h1>Perf Set</h1>
|
||||||
<template #title> </template>
|
|
||||||
|
|
||||||
<!-- 插槽-卡片右侧 -->
|
|
||||||
<template #extra>
|
|
||||||
<a-space :size="8" align="center">
|
|
||||||
<a-tooltip>
|
|
||||||
<template #title>{{ t('common.searchBarText') }}</template>
|
|
||||||
<a-switch
|
|
||||||
v-model:checked="tableState.seached"
|
|
||||||
:checked-children="t('common.switch.show')"
|
|
||||||
:un-checked-children="t('common.switch.hide')"
|
|
||||||
size="small"
|
|
||||||
/>
|
|
||||||
</a-tooltip>
|
|
||||||
<a-tooltip>
|
|
||||||
<template #title>{{ t('common.reloadText') }}</template>
|
|
||||||
<a-button type="text" @click.prevent="fnGetList()">
|
|
||||||
<template #icon><ReloadOutlined /></template>
|
|
||||||
</a-button>
|
|
||||||
</a-tooltip>
|
|
||||||
<a-tooltip>
|
|
||||||
<template #title>{{ t('common.sizeText') }}</template>
|
|
||||||
<a-dropdown trigger="click">
|
|
||||||
<a-button type="text">
|
|
||||||
<template #icon><ColumnHeightOutlined /></template>
|
|
||||||
</a-button>
|
|
||||||
<template #overlay>
|
|
||||||
<a-menu
|
|
||||||
:selected-keys="[tableState.size as string]"
|
|
||||||
@click="fnTableSize"
|
|
||||||
>
|
|
||||||
<a-menu-item key="default">{{
|
|
||||||
t('common.size.default')
|
|
||||||
}}</a-menu-item>
|
|
||||||
<a-menu-item key="middle">{{
|
|
||||||
t('common.size.middle')
|
|
||||||
}}</a-menu-item>
|
|
||||||
<a-menu-item key="small">{{
|
|
||||||
t('common.size.small')
|
|
||||||
}}</a-menu-item>
|
|
||||||
</a-menu>
|
|
||||||
</template>
|
|
||||||
</a-dropdown>
|
|
||||||
</a-tooltip>
|
|
||||||
</a-space>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- 表格列表 -->
|
|
||||||
<a-table
|
|
||||||
class="table"
|
|
||||||
row-key="id"
|
|
||||||
:columns="tableColumns"
|
|
||||||
:loading="tableState.loading"
|
|
||||||
:data-source="tableState.data"
|
|
||||||
:size="tableState.size"
|
|
||||||
:pagination="tablePagination"
|
|
||||||
:scroll="{ x: true }"
|
|
||||||
>
|
|
||||||
<template #bodyCell="{ column, record }">
|
|
||||||
<template v-if="column.key === 'id'">
|
|
||||||
<a-space :size="8" align="center">
|
|
||||||
<a-tooltip>
|
|
||||||
<template #title>查看详情</template>
|
|
||||||
<a-button type="link" @click.prevent="fnModalVisible(record)">
|
|
||||||
<template #icon><ProfileOutlined /></template>
|
|
||||||
</a-button>
|
|
||||||
</a-tooltip>
|
|
||||||
</a-space>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</a-table>
|
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
<!-- 详情框 -->
|
|
||||||
<ProModal
|
|
||||||
:drag="true"
|
|
||||||
:width="800"
|
|
||||||
:title="modalState.title"
|
|
||||||
:visible="modalState.visible"
|
|
||||||
@cancel="fnModalVisibleClose"
|
|
||||||
>
|
|
||||||
<div class="raw-title">
|
|
||||||
{{ t('views.traceManage.analysis.signalData') }}
|
|
||||||
</div>
|
|
||||||
<a-row
|
|
||||||
class="raw"
|
|
||||||
:gutter="16"
|
|
||||||
v-for="v in modalState.from.rawData"
|
|
||||||
:key="v.row"
|
|
||||||
>
|
|
||||||
<a-col class="num" :span="2">{{ v.row }}</a-col>
|
|
||||||
<a-col class="code" :span="12">{{ v.code }}</a-col>
|
|
||||||
<a-col class="txt" :span="10">{{ v.asciiText }}</a-col>
|
|
||||||
</a-row>
|
|
||||||
<a-divider />
|
|
||||||
<div class="raw-title">
|
|
||||||
{{ t('views.traceManage.analysis.signalDetail') }}
|
|
||||||
<a-button
|
|
||||||
type="dashed"
|
|
||||||
size="small"
|
|
||||||
@click.prevent="fnDownloadFile"
|
|
||||||
v-if="modalState.from.downBtn"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<DownloadOutlined />
|
|
||||||
</template>
|
|
||||||
{{ t('views.traceManage.analysis.taskDownText') }}
|
|
||||||
</a-button>
|
|
||||||
</div>
|
|
||||||
<div class="raw-html" v-html="modalState.from.rawDataHTML"></div>
|
|
||||||
</ProModal>
|
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped></style>
|
||||||
.table :deep(.ant-pagination) {
|
|
||||||
padding: 0 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.raw {
|
|
||||||
&-title {
|
|
||||||
color: #000000d9;
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: 1.8;
|
|
||||||
}
|
|
||||||
.num {
|
|
||||||
background-color: #e5e5e5;
|
|
||||||
}
|
|
||||||
.code {
|
|
||||||
background-color: #e7e6ff;
|
|
||||||
}
|
|
||||||
.txt {
|
|
||||||
background-color: #ffe3e5;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-html {
|
|
||||||
max-height: 300px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ const modalStateFrom = Form.useForm(
|
|||||||
neType: [
|
neType: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: t('views.traceManage.task.neTypePlease'),
|
message: t('views.ne.common.neTypePlease'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -513,14 +513,14 @@ onMounted(() => {
|
|||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.traceManage.task.neType')"
|
:label="t('views.ne.common.neType')"
|
||||||
name="neType "
|
name="neType "
|
||||||
>
|
>
|
||||||
<a-auto-complete
|
<a-auto-complete
|
||||||
v-model:value="queryParams.neType"
|
v-model:value="queryParams.neType"
|
||||||
:options="useNeInfoStore().getNeSelectOtions"
|
:options="useNeInfoStore().getNeSelectOtions"
|
||||||
allow-clear
|
allow-clear
|
||||||
:placeholder="t('views.traceManage.task.neTypePlease')"
|
:placeholder="t('views.ne.common.neTypePlease')"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -686,7 +686,7 @@ onMounted(() => {
|
|||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.traceManage.task.neType')"
|
:label="t('views.ne.common.neType')"
|
||||||
name="neType"
|
name="neType"
|
||||||
v-bind="modalStateFrom.validateInfos.neType"
|
v-bind="modalStateFrom.validateInfos.neType"
|
||||||
>
|
>
|
||||||
@@ -695,7 +695,7 @@ onMounted(() => {
|
|||||||
:options="useNeInfoStore().getNeSelectOtions"
|
:options="useNeInfoStore().getNeSelectOtions"
|
||||||
@change="fnSelectPerformanceInit"
|
@change="fnSelectPerformanceInit"
|
||||||
:allow-clear="false"
|
:allow-clear="false"
|
||||||
:placeholder="t('views.traceManage.task.neTypePlease')"
|
:placeholder="t('views.ne.common.neTypePlease')"
|
||||||
>
|
>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ const modalStateFrom = Form.useForm(
|
|||||||
neId: [
|
neId: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: t('views.traceManage.task.neTypePlease'),
|
message: t('views.ne.common.neTypePlease'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
granulOption: [
|
granulOption: [
|
||||||
@@ -725,14 +725,14 @@ onMounted(() => {
|
|||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.traceManage.task.neType')"
|
:label="t('views.ne.common.neType')"
|
||||||
name="neType "
|
name="neType "
|
||||||
>
|
>
|
||||||
<a-auto-complete
|
<a-auto-complete
|
||||||
v-model:value="queryParams.neType"
|
v-model:value="queryParams.neType"
|
||||||
:options="neInfoStore.getNeSelectOtions"
|
:options="neInfoStore.getNeSelectOtions"
|
||||||
allow-clear
|
allow-clear
|
||||||
:placeholder="t('views.traceManage.task.neTypePlease')"
|
:placeholder="t('views.ne.common.neTypePlease')"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -891,7 +891,7 @@ onMounted(() => {
|
|||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.traceManage.task.neType')"
|
:label="t('views.ne.common.neType')"
|
||||||
name="neType"
|
name="neType"
|
||||||
>
|
>
|
||||||
<a-cascader
|
<a-cascader
|
||||||
@@ -1007,7 +1007,7 @@ onMounted(() => {
|
|||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.traceManage.task.neType')"
|
:label="t('views.ne.common.neType')"
|
||||||
name="neType"
|
name="neType"
|
||||||
v-bind="modalStateFrom.validateInfos.neId"
|
v-bind="modalStateFrom.validateInfos.neId"
|
||||||
>
|
>
|
||||||
@@ -1016,7 +1016,7 @@ onMounted(() => {
|
|||||||
:options="neInfoStore.getNeCascaderOptions"
|
:options="neInfoStore.getNeCascaderOptions"
|
||||||
@change="fnNeChange"
|
@change="fnNeChange"
|
||||||
:allow-clear="false"
|
:allow-clear="false"
|
||||||
:placeholder="t('views.traceManage.task.neTypePlease')"
|
:placeholder="t('views.ne.common.neTypePlease')"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -1128,7 +1128,7 @@ onMounted(() => {
|
|||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.traceManage.task.comment')"
|
:label="t('views.traceManage.task.remark')"
|
||||||
name="comment"
|
name="comment"
|
||||||
>
|
>
|
||||||
<a-textarea
|
<a-textarea
|
||||||
@@ -1136,7 +1136,7 @@ onMounted(() => {
|
|||||||
:auto-size="{ minRows: 2, maxRows: 6 }"
|
:auto-size="{ minRows: 2, maxRows: 6 }"
|
||||||
:maxlength="250"
|
:maxlength="250"
|
||||||
:show-count="true"
|
:show-count="true"
|
||||||
:placeholder="t('views.traceManage.task.commentPlease')"
|
:placeholder="t('views.traceManage.task.remarkPlease')"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
|||||||
274
src/views/tool/net/index.vue
Normal file
274
src/views/tool/net/index.vue
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive ,toRaw, onMounted } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
||||||
|
import { ColumnsType} from 'ant-design-vue/lib/table';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import {
|
||||||
|
RESULT_CODE_ERROR
|
||||||
|
} from '@/constants/result-constants';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const ws = new WS();
|
||||||
|
|
||||||
|
//查询数据
|
||||||
|
let queryParams = reactive({
|
||||||
|
pid: undefined,
|
||||||
|
name:"",
|
||||||
|
port:undefined,
|
||||||
|
flag: false,
|
||||||
|
changeTime:5000,
|
||||||
|
});
|
||||||
|
|
||||||
|
//临时缓存
|
||||||
|
let queryParams2 = reactive({
|
||||||
|
pid: undefined,
|
||||||
|
name:"",
|
||||||
|
port:undefined,
|
||||||
|
flag: false,
|
||||||
|
changeTime:5000,
|
||||||
|
})
|
||||||
|
|
||||||
|
//时间粒度
|
||||||
|
let timeOptions =[
|
||||||
|
{label:t('views.tool.ps.fastSpeed'),value:3000},
|
||||||
|
{label:t('views.tool.ps.normalSpeed'),value:5000},
|
||||||
|
{label:t('views.tool.ps.slowSpeed'),value:10000},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**钩子函数,界面打开初始化*/
|
||||||
|
onMounted(() =>{
|
||||||
|
fnRealTime()//建立连接
|
||||||
|
extracted()//先立刻发送请求获取第一次的数据
|
||||||
|
queryReset2(false)//设置定时器
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询按钮**/
|
||||||
|
function queryTime(){//将表单中的数据传递给缓存数据(缓存数据自动同步请求信息中)
|
||||||
|
queryParams2.pid=queryParams.pid;
|
||||||
|
queryParams2.port=queryParams.port;
|
||||||
|
queryParams2.name=queryParams.name;
|
||||||
|
//queryParams.flag = true
|
||||||
|
queryParams2.flag=true
|
||||||
|
queryParams.pid=undefined;
|
||||||
|
queryParams.name="";
|
||||||
|
queryParams.port=undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**重置按钮**/
|
||||||
|
function queryReset(){
|
||||||
|
queryParams.flag = false
|
||||||
|
queryParams2.flag = false
|
||||||
|
}
|
||||||
|
|
||||||
|
let s:any = null
|
||||||
|
/**定时器**/
|
||||||
|
function queryReset2(c:boolean){
|
||||||
|
if(c){
|
||||||
|
clearInterval(s)//清理旧定时器
|
||||||
|
ws.close();//断开原来的wb连接
|
||||||
|
fnRealTime();//建立新的实时数据连接
|
||||||
|
}
|
||||||
|
s = setInterval(()=>{//设置新的定时器s
|
||||||
|
extracted();
|
||||||
|
},queryParams.changeTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**刷新频率改变**/
|
||||||
|
function fnRealTime2() {//时间粒度改变时触发
|
||||||
|
queryReset2(true)//改变定时器
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 实时数据
|
||||||
|
*/
|
||||||
|
function fnRealTime() {
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: '/ws',
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: wsError,
|
||||||
|
};
|
||||||
|
//建立连接
|
||||||
|
ws.connect(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调(失败) */
|
||||||
|
function wsError(ev: any) {
|
||||||
|
// 接收数据后回调
|
||||||
|
console.error(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调(成功) */
|
||||||
|
function wsMessage(res: Record<string, any>) {
|
||||||
|
const { code, requestId, data } = res;//获取数据
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 处理数据,组成ip : port
|
||||||
|
let processedData: any;
|
||||||
|
|
||||||
|
processedData = data.map((item:any) => {
|
||||||
|
const localAddr = `${item.localaddr.ip} : ${item.localaddr.port}`;
|
||||||
|
const remoteAddr = `${item.remoteaddr.ip} : ${item.remoteaddr.port}`;
|
||||||
|
return { ...item, localAddr, remoteAddr };
|
||||||
|
});
|
||||||
|
|
||||||
|
if (requestId) {
|
||||||
|
tableState.data = processedData; // 将数据填入表格
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extracted() {//将表单各条件值取出,并打包在data中发送请求
|
||||||
|
const {pid,name, port, flag} = toRaw(queryParams2)//从queryParams中取出各属性值
|
||||||
|
let data = {} // { 'PID': PID, 'name': name, 'port': port }
|
||||||
|
if (flag){//若flag为真则把值封装进data
|
||||||
|
data = { 'pid': pid, 'name': name, 'port': port }
|
||||||
|
}
|
||||||
|
//发送请求
|
||||||
|
ws.send({
|
||||||
|
'requestId': 'dxxx',
|
||||||
|
'type': 'net',
|
||||||
|
'data': data,
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TableStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
size: 'large',
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TableStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**记录数据 */
|
||||||
|
data: object[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
const tableColumns: ColumnsType<any> = [
|
||||||
|
{
|
||||||
|
title: t('views.tool.net.PID'),
|
||||||
|
dataIndex: 'pid',
|
||||||
|
align: 'center',
|
||||||
|
width: 50,
|
||||||
|
sorter:{//PID排序
|
||||||
|
compare:(a:any, b:any)=>a.pid-b.pid,
|
||||||
|
multiple:1,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.tool.net.name'),
|
||||||
|
dataIndex: 'name',
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.tool.net.localAddr'),
|
||||||
|
dataIndex: 'localAddr',
|
||||||
|
align: 'center',
|
||||||
|
width: 70,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.tool.net.remoteAddr'),
|
||||||
|
dataIndex:'remoteAddr',
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.tool.net.status'),
|
||||||
|
dataIndex: 'status',
|
||||||
|
align: 'center',
|
||||||
|
width: 70,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.tool.net.type'),
|
||||||
|
dataIndex: 'type',
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card
|
||||||
|
:bordered="false"
|
||||||
|
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
||||||
|
>
|
||||||
|
<a-form :model="queryParams" name="formParams" layout="horizontal">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="3" :md="6" :xs="12">
|
||||||
|
<a-form-item :label="t('views.tool.ps.changeTime')" name="changeTime">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.changeTime"
|
||||||
|
:options="timeOptions"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change='fnRealTime2'
|
||||||
|
/>
|
||||||
|
</a-form-item></a-col>
|
||||||
|
<a-col :lg="3" :md="6" :xs="12">
|
||||||
|
<a-form-item :label="t('views.tool.ps.PID')" name="pid">
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="queryParams.pid"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
style='width: 100%'
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item></a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.tool.net.name')" name="name">
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.name"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item></a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.tool.net.port')" name="port">
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.port"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item></a-col>
|
||||||
|
<a-col :lg="3" :md="4" :xs="5">
|
||||||
|
<a-form-item>
|
||||||
|
<a-button type="primary" @click='queryTime()'>
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="queryReset()" style='left: 20px;'>
|
||||||
|
<template #icon><ClearOutlined /></template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="id"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:scroll="{ y: 'calc(100vh - 480px)' }"
|
||||||
|
:pagination='false'
|
||||||
|
></a-table>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
367
src/views/tool/ps/index.vue
Normal file
367
src/views/tool/ps/index.vue
Normal file
@@ -0,0 +1,367 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, onBeforeUnmount, nextTick } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import { RESULT_CODE_ERROR } from '@/constants/result-constants';
|
||||||
|
import { diffValue, parseDuration } from '@/utils/date-utils';
|
||||||
|
import { parseSizeFromFile } from '@/utils/parse-utils';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const ws = new WS();
|
||||||
|
|
||||||
|
/**表单查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
pid: undefined,
|
||||||
|
name: '',
|
||||||
|
username: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
/**状态对象 */
|
||||||
|
let state = reactive({
|
||||||
|
/**调度器 */
|
||||||
|
interval: null as any,
|
||||||
|
/**刷新周期 */
|
||||||
|
intervalTime: 5_000,
|
||||||
|
/**查询参数 */
|
||||||
|
query: {
|
||||||
|
pid: undefined,
|
||||||
|
name: '',
|
||||||
|
username: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**接收数据后回调(成功) */
|
||||||
|
function wsMessage(res: Record<string, any>) {
|
||||||
|
const { code, requestId, data } = res;
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 建联时发送请求
|
||||||
|
if (!requestId && data.clientId) {
|
||||||
|
fnGetList();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收到消息数据
|
||||||
|
if (requestId.startsWith('ps_')) {
|
||||||
|
// 将数据填入表格
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
tableState.data = data;
|
||||||
|
} else {
|
||||||
|
tableState.data = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**实时数据*/
|
||||||
|
function fnRealTime(reLink: boolean) {
|
||||||
|
if (reLink) {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: '/ws',
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: (ev: any) => {
|
||||||
|
// 接收数据后回调
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
//建立连接
|
||||||
|
ws.connect(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**调度器周期变更*/
|
||||||
|
function fnIntervalChange(v: any) {
|
||||||
|
clearInterval(state.interval);
|
||||||
|
const timer = parseInt(v);
|
||||||
|
if (timer > 1_000) {
|
||||||
|
state.intervalTime = v;
|
||||||
|
fnGetList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询列表 */
|
||||||
|
function fnGetList() {
|
||||||
|
if (tableState.loading || ws.state() === -1) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
const msg = {
|
||||||
|
requestId: `ps_${state.interval}`,
|
||||||
|
type: 'ps',
|
||||||
|
data: state.query,
|
||||||
|
};
|
||||||
|
// 首发
|
||||||
|
ws.send(msg);
|
||||||
|
// 定时刷新数据
|
||||||
|
state.interval = setInterval(() => {
|
||||||
|
msg.data = state.query;
|
||||||
|
ws.send(msg);
|
||||||
|
}, state.intervalTime);
|
||||||
|
tableState.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询参数传入 */
|
||||||
|
function fnQuery() {
|
||||||
|
state.query = JSON.parse(JSON.stringify(queryParams));
|
||||||
|
nextTick(() => {
|
||||||
|
ws.send({
|
||||||
|
requestId: `ps_${state.interval}`,
|
||||||
|
type: 'ps',
|
||||||
|
data: state.query,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
Object.assign(queryParams, {
|
||||||
|
pid: undefined,
|
||||||
|
name: '',
|
||||||
|
username: '',
|
||||||
|
});
|
||||||
|
tablePagination.current = 1;
|
||||||
|
tablePagination.pageSize = 20;
|
||||||
|
// 重置查询条件
|
||||||
|
Object.assign(state.query, {
|
||||||
|
pid: undefined,
|
||||||
|
name: '',
|
||||||
|
username: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TableStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**记录数据 */
|
||||||
|
data: object[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TableStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
const tableColumns: ColumnsType<any> = [
|
||||||
|
{
|
||||||
|
title: t('views.tool.ps.pid'),
|
||||||
|
dataIndex: 'pid',
|
||||||
|
align: 'right',
|
||||||
|
width: 100,
|
||||||
|
sorter: {
|
||||||
|
compare: (a: any, b: any) => a.pid - b.pid,
|
||||||
|
multiple: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.tool.ps.cpuPercent'),
|
||||||
|
dataIndex: 'cpuPercent',
|
||||||
|
align: 'left',
|
||||||
|
width: 120,
|
||||||
|
sorter: {
|
||||||
|
compare: (a: any, b: any) => a.cpuPercent - b.cpuPercent,
|
||||||
|
multiple: 3,
|
||||||
|
},
|
||||||
|
customRender(opt) {
|
||||||
|
return `${opt.value} %`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.tool.ps.diskRead'),
|
||||||
|
dataIndex: 'diskRead',
|
||||||
|
align: 'right',
|
||||||
|
width: 100,
|
||||||
|
sorter: {
|
||||||
|
compare: (a: any, b: any) => a.diskRead - b.diskRead,
|
||||||
|
multiple: 3,
|
||||||
|
},
|
||||||
|
customRender(opt) {
|
||||||
|
return parseSizeFromFile(+opt.value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.tool.ps.diskWrite'),
|
||||||
|
dataIndex: 'diskWrite',
|
||||||
|
align: 'right',
|
||||||
|
width: 100,
|
||||||
|
sorter: {
|
||||||
|
compare: (a: any, b: any) => a.diskWrite - b.diskWrite,
|
||||||
|
multiple: 3,
|
||||||
|
},
|
||||||
|
customRender(opt) {
|
||||||
|
return parseSizeFromFile(+opt.value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.tool.ps.numThreads'),
|
||||||
|
dataIndex: 'numThreads',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
sorter: {
|
||||||
|
//线程数比较大小
|
||||||
|
compare: (a: any, b: any) => a.numThreads - b.numThreads,
|
||||||
|
multiple: 4, //优先级4
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.tool.ps.runTime'),
|
||||||
|
dataIndex: 'startTime',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
customRender(opt) {
|
||||||
|
const second = diffValue(Date.now(), +opt.value, 'second');
|
||||||
|
return parseDuration(second);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.tool.ps.username'),
|
||||||
|
dataIndex: 'username',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.tool.ps.name'),
|
||||||
|
dataIndex: 'name',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
let tablePagination = reactive({
|
||||||
|
/**当前页数 */
|
||||||
|
current: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
/**默认的每页条数 */
|
||||||
|
defaultPageSize: 20,
|
||||||
|
/**指定每页可以显示多少条 */
|
||||||
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
|
/**只有一页时是否隐藏分页器 */
|
||||||
|
hideOnSinglePage: false,
|
||||||
|
/**是否可以快速跳转至某页 */
|
||||||
|
showQuickJumper: true,
|
||||||
|
/**是否可以改变 pageSize */
|
||||||
|
showSizeChanger: true,
|
||||||
|
/**数据总数 */
|
||||||
|
total: 0,
|
||||||
|
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
|
||||||
|
onChange: (page: number, pageSize: number) => {
|
||||||
|
tablePagination.current = page;
|
||||||
|
tablePagination.pageSize = pageSize;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**钩子函数,界面打开初始化*/
|
||||||
|
onMounted(() => {
|
||||||
|
fnRealTime(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**钩子函数,界面关闭*/
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
ws.close();
|
||||||
|
});
|
||||||
|
</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="4" :md="6" :xs="12">
|
||||||
|
<a-form-item :label="t('views.tool.ps.pid')" name="pid">
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="queryParams.pid"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.tool.ps.name')" name="name">
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.name"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item :label="t('views.tool.ps.username')" name="username">
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.username"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="4" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button type="primary" @click.prevent="fnQuery()">
|
||||||
|
<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>
|
||||||
|
<div>{{ state.query }}</div>
|
||||||
|
<div>{{ queryParams }}</div>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-form layout="inline">
|
||||||
|
<a-form-item :label="t('views.tool.ps.realTime')" name="realTime">
|
||||||
|
<a-select
|
||||||
|
v-model:value="state.intervalTime"
|
||||||
|
:options="[
|
||||||
|
{ label: t('views.tool.ps.realTimeHigh'), value: 3_000 },
|
||||||
|
{ label: t('views.tool.ps.realTimeRegular'), value: 5_000 },
|
||||||
|
{ label: t('views.tool.ps.realTimeLow'), value: 10_000 },
|
||||||
|
{ label: t('views.tool.ps.realTimeStop'), value: -1 },
|
||||||
|
]"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnIntervalChange"
|
||||||
|
style="width: 100px"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="pid"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
size="small"
|
||||||
|
:scroll="{ x: tableColumns.length * 120 }"
|
||||||
|
></a-table>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -226,10 +226,11 @@ function fnTabClose(id: string) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<a-card :bordered="false" :body-style="{ padding: '12px' }">
|
<a-card :bordered="false" size="small" :body-style="{ padding: '12px' }">
|
||||||
<a-tabs
|
<a-tabs
|
||||||
class="terminal-tabs"
|
class="terminal-tabs"
|
||||||
hide-add
|
hide-add
|
||||||
|
size="small"
|
||||||
tab-position="top"
|
tab-position="top"
|
||||||
type="editable-card"
|
type="editable-card"
|
||||||
:tab-bar-gutter="8"
|
:tab-bar-gutter="8"
|
||||||
@@ -343,7 +344,12 @@ function fnTabClose(id: string) {
|
|||||||
<template #title>
|
<template #title>
|
||||||
{{ t('views.tool.terminal.new') }}
|
{{ t('views.tool.terminal.new') }}
|
||||||
</template>
|
</template>
|
||||||
<a-button type="default" shape="circle" @click="fnTabMenu('new')">
|
<a-button
|
||||||
|
type="default"
|
||||||
|
shape="circle"
|
||||||
|
size="small"
|
||||||
|
@click="fnTabMenu('new')"
|
||||||
|
>
|
||||||
<template #icon><PlusOutlined /></template>
|
<template #icon><PlusOutlined /></template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
@@ -354,7 +360,7 @@ function fnTabClose(id: string) {
|
|||||||
{{ t('views.tool.terminal.more') }}
|
{{ t('views.tool.terminal.more') }}
|
||||||
</template>
|
</template>
|
||||||
<a-dropdown trigger="click" placement="bottomRight">
|
<a-dropdown trigger="click" placement="bottomRight">
|
||||||
<a-button type="ghost" shape="circle">
|
<a-button type="ghost" shape="circle" size="small">
|
||||||
<template #icon><EllipsisOutlined /></template>
|
<template #icon><EllipsisOutlined /></template>
|
||||||
</a-button>
|
</a-button>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
@@ -385,8 +391,7 @@ function fnTabClose(id: string) {
|
|||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.pane-box {
|
.pane-box {
|
||||||
padding: 16px;
|
height: calc(100vh - 200px);
|
||||||
height: calc(100vh - 320px);
|
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { parseDateToStr } from '@/utils/date-utils';
|
|||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import { getTraceRawInfo, listTraceData } from '@/api/traceManage/analysis';
|
import { getTraceRawInfo, listTraceData } from '@/api/trace/analysis';
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
/**查询参数 */
|
/**查询参数 */
|
||||||
@@ -202,15 +202,15 @@ function fnModalVisible(row: Record<string, any>) {
|
|||||||
const rawData = convertToReadableFormat(hexString);
|
const rawData = convertToReadableFormat(hexString);
|
||||||
modalState.from.rawData = rawData;
|
modalState.from.rawData = rawData;
|
||||||
// RAW解析HTML
|
// RAW解析HTML
|
||||||
getTraceRawInfo(row.id).then(res => {
|
// getTraceRawInfo(row.id).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
// if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
const htmlString = rawDataHTMLScript(res.msg);
|
// const htmlString = rawDataHTMLScript(res.msg);
|
||||||
modalState.from.rawDataHTML = htmlString;
|
// modalState.from.rawDataHTML = htmlString;
|
||||||
modalState.from.downBtn = true;
|
// modalState.from.downBtn = true;
|
||||||
} else {
|
// } else {
|
||||||
modalState.from.rawDataHTML = t('views.traceManage.analysis.noData');
|
// modalState.from.rawDataHTML = t('views.traceManage.analysis.noData');
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
modalState.title = t('views.traceManage.analysis.taskTitle', {
|
modalState.title = t('views.traceManage.analysis.taskTitle', {
|
||||||
num: row.imsi,
|
num: row.imsi,
|
||||||
});
|
});
|
||||||
@@ -495,7 +495,7 @@ onMounted(() => {
|
|||||||
<a-col class="txt" :span="10">{{ v.asciiText }}</a-col>
|
<a-col class="txt" :span="10">{{ v.asciiText }}</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
<a-divider />
|
<a-divider />
|
||||||
<div class="raw-title">
|
<!-- <div class="raw-title">
|
||||||
{{ t('views.traceManage.analysis.signalDetail') }}
|
{{ t('views.traceManage.analysis.signalDetail') }}
|
||||||
<a-button
|
<a-button
|
||||||
type="dashed"
|
type="dashed"
|
||||||
@@ -508,8 +508,8 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
{{ t('views.traceManage.analysis.taskDownText') }}
|
{{ t('views.traceManage.analysis.taskDownText') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div> -->
|
||||||
<div class="raw-html" v-html="modalState.from.rawDataHTML"></div>
|
<!-- <div class="raw-html" v-html="modalState.from.rawDataHTML"></div> -->
|
||||||
</ProModal>
|
</ProModal>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
404
src/views/traceManage/pcap/file.vue
Normal file
404
src/views/traceManage/pcap/file.vue
Normal file
@@ -0,0 +1,404 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, onMounted, toRaw } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||||||
|
import { Modal, message } from 'ant-design-vue/lib';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { getNeFile, listNeFiles } from '@/api/tool/neFile';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import useTabsStore from '@/store/modules/tabs';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import saveAs from 'file-saver';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
const tabsStore = useTabsStore();
|
||||||
|
const neInfoStore = useNeInfoStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
// 获取地址栏参数
|
||||||
|
const routeParams = route.query as Record<string, any>;
|
||||||
|
|
||||||
|
/**网元参数 */
|
||||||
|
let neTypeSelect = ref<string[]>([]);
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元类型 */
|
||||||
|
neType: '',
|
||||||
|
neId: '',
|
||||||
|
/**读取路径 */
|
||||||
|
path: '',
|
||||||
|
/**前缀过滤 */
|
||||||
|
search: '',
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**记录数据 */
|
||||||
|
data: object[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
size: 'small',
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
let tableColumns: ColumnsType = reactive([
|
||||||
|
{
|
||||||
|
title: t('views.logManage.neFile.fileMode'),
|
||||||
|
dataIndex: 'fileMode',
|
||||||
|
align: 'center',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.logManage.neFile.size'),
|
||||||
|
dataIndex: 'size',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.logManage.neFile.modifiedTime'),
|
||||||
|
dataIndex: 'modifiedTime',
|
||||||
|
align: 'left',
|
||||||
|
customRender(opt) {
|
||||||
|
if (!opt.value) return '';
|
||||||
|
return parseDateToStr(opt.value * 1000);
|
||||||
|
},
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.logManage.neFile.fileName'),
|
||||||
|
dataIndex: 'fileName',
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 200,
|
||||||
|
minWidth: 100,
|
||||||
|
maxWidth: 350,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('common.operate'),
|
||||||
|
key: 'fileName',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
let tablePagination = reactive({
|
||||||
|
/**当前页数 */
|
||||||
|
current: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
/**默认的每页条数 */
|
||||||
|
defaultPageSize: 20,
|
||||||
|
/**指定每页可以显示多少条 */
|
||||||
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
|
/**只有一页时是否隐藏分页器 */
|
||||||
|
hideOnSinglePage: false,
|
||||||
|
/**是否可以快速跳转至某页 */
|
||||||
|
showQuickJumper: true,
|
||||||
|
/**是否可以改变 pageSize */
|
||||||
|
showSizeChanger: true,
|
||||||
|
/**数据总数 */
|
||||||
|
total: 0,
|
||||||
|
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
|
||||||
|
onChange: (page: number, pageSize: number) => {
|
||||||
|
tablePagination.current = page;
|
||||||
|
tablePagination.pageSize = pageSize;
|
||||||
|
queryParams.pageNum = page;
|
||||||
|
queryParams.pageSize = pageSize;
|
||||||
|
fnGetList();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**下载触发等待 */
|
||||||
|
let downLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**信息文件下载 */
|
||||||
|
function fnDownloadFile(row: Record<string, any>) {
|
||||||
|
if (downLoading.value) return;
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.logManage.neFile.downTip', { fileName: row.fileName }),
|
||||||
|
onOk() {
|
||||||
|
downLoading.value = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
getNeFile({
|
||||||
|
neType: queryParams.neType,
|
||||||
|
neId: queryParams.neId,
|
||||||
|
path: queryParams.path,
|
||||||
|
fileName: row.fileName,
|
||||||
|
delTemp: true,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.msgSuccess', {
|
||||||
|
msg: t('common.downloadText'),
|
||||||
|
}),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
saveAs(res.data, `${row.fileName}`);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: t('views.logManage.neFile.downTipErr'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
downLoading.value = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**tmp目录下,UPF标准版内部输出目录 */
|
||||||
|
let tmp = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**UPF标准版内部抓包的输出目录 */
|
||||||
|
function fnUPFTmp() {
|
||||||
|
fnDirCD('/tmp', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**关闭跳转 */
|
||||||
|
function fnClose() {
|
||||||
|
const to = tabsStore.tabClose(route.path);
|
||||||
|
if (to) {
|
||||||
|
router.push(to);
|
||||||
|
} else {
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**访问路径 */
|
||||||
|
let nePathArr = ref<string[]>([]);
|
||||||
|
|
||||||
|
/**进入目录 */
|
||||||
|
function fnDirCD(dir: string, index?: number) {
|
||||||
|
if (index === undefined) {
|
||||||
|
nePathArr.value.push(dir);
|
||||||
|
queryParams.search = '';
|
||||||
|
fnGetList(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (index === 0) {
|
||||||
|
const neType = queryParams.neType;
|
||||||
|
if (neType === 'UPF' && tmp.value) {
|
||||||
|
nePathArr.value = ['/tmp'];
|
||||||
|
queryParams.search = `${neType}_${queryParams.neId}`;
|
||||||
|
} else {
|
||||||
|
nePathArr.value = [
|
||||||
|
`/tmp/omc/tcpdump/${neType.toLowerCase()}/${queryParams.neId}`,
|
||||||
|
];
|
||||||
|
queryParams.search = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
fnGetList(1);
|
||||||
|
} else {
|
||||||
|
nePathArr.value = nePathArr.value.slice(0, index + 1);
|
||||||
|
queryParams.search = '';
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**网元类型选择对应修改 */
|
||||||
|
function fnNeChange(keys: any, _: any) {
|
||||||
|
// 不是同类型时需要重新加载
|
||||||
|
if (Array.isArray(keys) && queryParams.neType !== keys[0]) {
|
||||||
|
const neType = keys[0];
|
||||||
|
queryParams.neType = neType;
|
||||||
|
queryParams.neId = keys[1];
|
||||||
|
if (neType === 'UPF' && tmp.value) {
|
||||||
|
nePathArr.value = ['/tmp'];
|
||||||
|
queryParams.search = `${neType}_${keys[1]}`;
|
||||||
|
} else {
|
||||||
|
nePathArr.value = [`/tmp/omc/tcpdump/${neType.toLowerCase()}/${keys[1]}`];
|
||||||
|
queryParams.search = '';
|
||||||
|
}
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (queryParams.neId === '') {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.logManage.neFile.neTypePlease'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
queryParams.path = nePathArr.value.join('/');
|
||||||
|
listNeFiles(toRaw(queryParams)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
|
tablePagination.total = res.total;
|
||||||
|
tableState.data = res.rows;
|
||||||
|
if (
|
||||||
|
tablePagination.total <=
|
||||||
|
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||||
|
queryParams.pageNum !== 1
|
||||||
|
) {
|
||||||
|
tableState.loading = false;
|
||||||
|
fnGetList(queryParams.pageNum - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.error(res.msg, 3);
|
||||||
|
tablePagination.total = 0;
|
||||||
|
tableState.data = [];
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 获取网元网元列表
|
||||||
|
neInfoStore.fnNelist().then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
|
if (res.data.length === 0) {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
} else if (routeParams.neType && routeParams.neId) {
|
||||||
|
neTypeSelect.value = [routeParams.neType, routeParams.neId];
|
||||||
|
fnNeChange(neTypeSelect.value, undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||||
|
<a-row :gutter="16" :wrap="true">
|
||||||
|
<a-col>
|
||||||
|
<a-form-item style="margin-bottom: 0">
|
||||||
|
<a-button type="default" @click.prevent="fnClose()">
|
||||||
|
<template #icon><CloseOutlined /></template>
|
||||||
|
{{ t('common.close') }}
|
||||||
|
</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.logManage.neFile.neType')"
|
||||||
|
name="neType"
|
||||||
|
style="margin-bottom: 0"
|
||||||
|
>
|
||||||
|
<a-cascader
|
||||||
|
v-model:value="neTypeSelect"
|
||||||
|
:options="neInfoStore.getNeCascaderOptions"
|
||||||
|
@change="fnNeChange"
|
||||||
|
:allow-clear="false"
|
||||||
|
:placeholder="t('views.logManage.neFile.neTypePlease')"
|
||||||
|
:disabled="downLoading || tableState.loading"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col v-if="nePathArr.length > 0">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.logManage.neFile.nePath')"
|
||||||
|
name="configName"
|
||||||
|
style="margin-bottom: 0"
|
||||||
|
>
|
||||||
|
<a-breadcrumb>
|
||||||
|
<a-breadcrumb-item
|
||||||
|
v-for="(path, index) in nePathArr"
|
||||||
|
:key="path"
|
||||||
|
@click="fnDirCD(path, index)"
|
||||||
|
>
|
||||||
|
{{ path }}
|
||||||
|
</a-breadcrumb-item>
|
||||||
|
</a-breadcrumb>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip placement="topRight" v-if="neTypeSelect[0] === 'UPF'">
|
||||||
|
<template #title>
|
||||||
|
{{ t('views.traceManage.pcap.fileUPFTip') }}
|
||||||
|
</template>
|
||||||
|
<a-checkbox v-model:checked="tmp" @change="fnUPFTmp()">
|
||||||
|
{{ t('views.traceManage.pcap.fileUPF') }}
|
||||||
|
</a-checkbox>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip placement="topRight">
|
||||||
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="fileName"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: 800 }"
|
||||||
|
@resizeColumn="(w:number, col:any) => (col.width = w)"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'fileName'">
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
:loading="downLoading"
|
||||||
|
@click.prevent="fnDownloadFile(record)"
|
||||||
|
v-if="record.fileType === 'file'"
|
||||||
|
>
|
||||||
|
<template #icon><DownloadOutlined /></template>
|
||||||
|
{{ t('common.downloadText') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
:loading="downLoading"
|
||||||
|
@click.prevent="fnDirCD(record.fileName)"
|
||||||
|
v-if="record.fileType === 'dir'"
|
||||||
|
>
|
||||||
|
<template #icon><FolderOutlined /></template>
|
||||||
|
{{ t('views.logManage.neFile.dirCd') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
||||||
@@ -1,24 +1,67 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, reactive } from 'vue';
|
import { onMounted, reactive } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { message, Modal } from 'ant-design-vue/lib';
|
import { message, Modal } from 'ant-design-vue/lib';
|
||||||
import { ColumnsType } from 'ant-design-vue/lib/table';
|
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { dumpStart, dumpStop, traceUPF } from '@/api/traceManage/pcap';
|
import { dumpStart, dumpStop, dumpDownload, traceUPF } from '@/api/trace/pcap';
|
||||||
import { listAllNeInfo } from '@/api/ne/neInfo';
|
import { listAllNeInfo } from '@/api/ne/neInfo';
|
||||||
import { getNeFile } from '@/api/tool/neFile';
|
import { getNeFile } from '@/api/tool/neFile';
|
||||||
import saveAs from 'file-saver';
|
import saveAs from 'file-saver';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { MENU_PATH_INLINE } from '@/constants/menu-constants';
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
/**对话框对象信息状态类型 */
|
/**对话框对象信息状态类型 */
|
||||||
type ModalStateType = {
|
type ModalStateType = {
|
||||||
/**表单数据 */
|
/**表单数据 */
|
||||||
from: Record<string, any>;
|
from: Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
loading: boolean;
|
||||||
|
/**网元名 */
|
||||||
|
title: string;
|
||||||
|
/**命令 */
|
||||||
|
cmdStart: string;
|
||||||
|
/**upf标准版需要停止命令,一般空字符 */
|
||||||
|
cmdStop: string;
|
||||||
|
/**任务编号 */
|
||||||
|
taskCode: string;
|
||||||
|
/**任务日志,upf标准版为空字符串 */
|
||||||
|
logMsg: string;
|
||||||
|
/**提交表单参数 */
|
||||||
|
data: {
|
||||||
|
neType: string;
|
||||||
|
neId: string;
|
||||||
|
cmd?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
>;
|
||||||
/**tcpdump命令组 */
|
/**tcpdump命令组 */
|
||||||
cmdOptions: Record<string, any>[];
|
cmdOptions: {
|
||||||
|
/**命令名称 */
|
||||||
|
label: string;
|
||||||
|
/**命令选中值 */
|
||||||
|
value: string;
|
||||||
|
/**开始命令 */
|
||||||
|
start: string;
|
||||||
|
/**停止命令 */
|
||||||
|
stop: string;
|
||||||
|
}[];
|
||||||
/**UPF命令组 */
|
/**UPF命令组 */
|
||||||
cmdOptionsUPF: Record<string, any>[];
|
cmdOptionsUPF: {
|
||||||
|
/**命令名称 */
|
||||||
|
label: string;
|
||||||
|
/**命令选中值 */
|
||||||
|
value: string;
|
||||||
|
/**开始命令 */
|
||||||
|
start: string;
|
||||||
|
/**停止命令 */
|
||||||
|
stop: string;
|
||||||
|
}[];
|
||||||
/**详情框是否显示 */
|
/**详情框是否显示 */
|
||||||
visibleByView: boolean;
|
visibleByView: boolean;
|
||||||
/**详情框内容 */
|
/**详情框内容 */
|
||||||
@@ -31,29 +74,35 @@ let modalState: ModalStateType = reactive({
|
|||||||
cmdOptions: [
|
cmdOptions: [
|
||||||
{
|
{
|
||||||
label: t('views.traceManage.pcap.execCmd'),
|
label: t('views.traceManage.pcap.execCmd'),
|
||||||
start: '-n -s 0 -v -w',
|
|
||||||
stop: '',
|
|
||||||
value: 'any',
|
value: 'any',
|
||||||
|
start: '-n -v -s 0',
|
||||||
|
stop: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('views.traceManage.pcap.execCmdsSctp'),
|
label: t('views.traceManage.pcap.execCmd2'),
|
||||||
|
value: 'any2',
|
||||||
start: 'sctp or tcp port 3030 or 8088',
|
start: 'sctp or tcp port 3030 or 8088',
|
||||||
stop: '',
|
stop: '',
|
||||||
value: 'any2',
|
},
|
||||||
|
{
|
||||||
|
label: t('views.traceManage.pcap.execCmd3'),
|
||||||
|
value: 'any3',
|
||||||
|
start: '-n -s 0 -v -G 10 -W 7',
|
||||||
|
stop: '',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
cmdOptionsUPF: [
|
cmdOptionsUPF: [
|
||||||
{
|
{
|
||||||
label: t('views.traceManage.pcap.execUPFCmdA'),
|
label: t('views.traceManage.pcap.execUPFCmdA'),
|
||||||
|
value: 'pcap trace',
|
||||||
start: 'pcap trace rx tx max 100000 intfc any',
|
start: 'pcap trace rx tx max 100000 intfc any',
|
||||||
stop: 'pcap trace rx tx off',
|
stop: 'pcap trace rx tx off',
|
||||||
value: 'pcap trace',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('views.traceManage.pcap.execUPFCmdB'),
|
label: t('views.traceManage.pcap.execUPFCmdB'),
|
||||||
|
value: 'pcap dispatch',
|
||||||
start: 'pcap dispatch trace on max 100000',
|
start: 'pcap dispatch trace on max 100000',
|
||||||
stop: 'pcap dispatch trace off',
|
stop: 'pcap dispatch trace off',
|
||||||
value: 'pcap dispatch',
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
visibleByView: false,
|
visibleByView: false,
|
||||||
@@ -80,25 +129,25 @@ let tableState: TabeStateType = reactive({
|
|||||||
/**表格字段列 */
|
/**表格字段列 */
|
||||||
let tableColumns: ColumnsType = [
|
let tableColumns: ColumnsType = [
|
||||||
{
|
{
|
||||||
title: t('views.configManage.neManage.neType'),
|
title: t('views.ne.common.neType'),
|
||||||
dataIndex: 'neType',
|
dataIndex: 'neType',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.configManage.neManage.neId'),
|
title: t('views.ne.common.neId'),
|
||||||
dataIndex: 'neId',
|
dataIndex: 'neId',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.configManage.neManage.neName'),
|
title: t('views.ne.common.neName'),
|
||||||
dataIndex: 'neName',
|
dataIndex: 'neName',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.configManage.neManage.ip'),
|
title: t('views.ne.common.ipAddr'),
|
||||||
dataIndex: 'ip',
|
dataIndex: 'ip',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 150,
|
width: 150,
|
||||||
@@ -108,13 +157,12 @@ let tableColumns: ColumnsType = [
|
|||||||
key: 'cmd',
|
key: 'cmd',
|
||||||
dataIndex: 'serverState',
|
dataIndex: 'serverState',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 300,
|
width: 350,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('common.operate'),
|
title: t('common.operate'),
|
||||||
key: 'id',
|
key: 'id',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
fixed: 'right',
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -137,20 +185,22 @@ function fnGetList() {
|
|||||||
) {
|
) {
|
||||||
tableState.data = res.data;
|
tableState.data = res.data;
|
||||||
// 初始网元参数表单
|
// 初始网元参数表单
|
||||||
const { start, stop } = modalState.cmdOptions[0];
|
if (tableState.data.length > 0) {
|
||||||
for (const item of res.data) {
|
const { start, stop } = modalState.cmdOptions[0];
|
||||||
modalState.from[item.id] = {
|
for (const item of res.data) {
|
||||||
loading: false,
|
modalState.from[item.id] = {
|
||||||
title: item.neName, // 网元名
|
loading: false,
|
||||||
cmdStart: start,
|
title: item.neName,
|
||||||
cmdStop: stop, // upf需要停止命令
|
cmdStart: start,
|
||||||
out: '',
|
cmdStop: stop,
|
||||||
log: '',
|
taskCode: '',
|
||||||
data: {
|
logMsg: '',
|
||||||
neType: item.neType,
|
data: {
|
||||||
neId: item.neId,
|
neType: item.neType,
|
||||||
},
|
neId: item.neId,
|
||||||
};
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
message.warning({
|
message.warning({
|
||||||
@@ -166,6 +216,9 @@ function fnGetList() {
|
|||||||
function fnSelectCmd(id: any, option: any) {
|
function fnSelectCmd(id: any, option: any) {
|
||||||
modalState.from[id].cmdStart = option.start;
|
modalState.from[id].cmdStart = option.start;
|
||||||
modalState.from[id].cmdStop = option.stop;
|
modalState.from[id].cmdStop = option.stop;
|
||||||
|
// 重置任务
|
||||||
|
modalState.from[id].taskCode = '';
|
||||||
|
modalState.from[id].logMsg = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -203,8 +256,9 @@ function fnRecordStart(row?: Record<string, any>) {
|
|||||||
if (res.status === 'fulfilled') {
|
if (res.status === 'fulfilled') {
|
||||||
const resV = res.value;
|
const resV = res.value;
|
||||||
if (resV.code === RESULT_CODE_SUCCESS) {
|
if (resV.code === RESULT_CODE_SUCCESS) {
|
||||||
fromArr[idx].out = resV.data.out;
|
if (!fromArr[idx].cmdStop) {
|
||||||
fromArr[idx].log = resV.data.log;
|
fromArr[idx].taskCode = resV.data;
|
||||||
|
}
|
||||||
fromArr[idx].loading = true;
|
fromArr[idx].loading = true;
|
||||||
message.success({
|
message.success({
|
||||||
content: t('views.traceManage.pcap.startOk', { title }),
|
content: t('views.traceManage.pcap.startOk', { title }),
|
||||||
@@ -250,24 +304,51 @@ function fnRecordStop(row?: Record<string, any>) {
|
|||||||
title: t('common.tipTitle'),
|
title: t('common.tipTitle'),
|
||||||
content: t('views.traceManage.pcap.stopTip', { title: row.neName }),
|
content: t('views.traceManage.pcap.stopTip', { title: row.neName }),
|
||||||
onOk() {
|
onOk() {
|
||||||
const hide = message.loading(t('common.loading'), 0);
|
|
||||||
const fromArr = neIDs.map(id => modalState.from[id]);
|
const fromArr = neIDs.map(id => modalState.from[id]);
|
||||||
const reqArr = fromArr.map(from => {
|
const reqArr: any = [];
|
||||||
|
for (const from of fromArr) {
|
||||||
if (from.data.neType === 'UPF' && from.cmdStart.startsWith('pcap')) {
|
if (from.data.neType === 'UPF' && from.cmdStart.startsWith('pcap')) {
|
||||||
return traceUPF(Object.assign({ cmd: from.cmdStop }, from.data));
|
reqArr.push(
|
||||||
|
traceUPF(Object.assign({ cmd: from.cmdStop }, from.data))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const taskCode = from.taskCode;
|
||||||
|
if (!taskCode) {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.traceManage.pcap.stopNotRun', {
|
||||||
|
title: from.title,
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
reqArr.push(
|
||||||
|
dumpStop(Object.assign({ taskCode: from.taskCode }, from.data))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return dumpStop(Object.assign({ fileName: from.out }, from.data));
|
}
|
||||||
});
|
if (reqArr.length === 0) return;
|
||||||
|
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
Promise.allSettled(reqArr)
|
Promise.allSettled(reqArr)
|
||||||
.then(resArr => {
|
.then(resArr => {
|
||||||
resArr.forEach((res, idx) => {
|
resArr.forEach((res, idx) => {
|
||||||
const title = fromArr[idx].title;
|
const title = fromArr[idx].title;
|
||||||
|
|
||||||
if (res.status === 'fulfilled') {
|
if (res.status === 'fulfilled') {
|
||||||
const resV = res.value;
|
const resV = res.value;
|
||||||
|
fromArr[idx].loading = false;
|
||||||
|
fromArr[idx].logMsg = '';
|
||||||
|
if (fromArr[idx].cmdStop) {
|
||||||
|
fromArr[idx].taskCode = '';
|
||||||
|
}
|
||||||
|
|
||||||
if (resV.code === RESULT_CODE_SUCCESS) {
|
if (resV.code === RESULT_CODE_SUCCESS) {
|
||||||
fromArr[idx].out = resV.data.out;
|
if (fromArr[idx].cmdStop) {
|
||||||
fromArr[idx].log = resV.data.log;
|
fromArr[idx].taskCode = resV.data;
|
||||||
fromArr[idx].loading = false;
|
} else {
|
||||||
|
fromArr[idx].logMsg = resV.msg;
|
||||||
|
}
|
||||||
message.success({
|
message.success({
|
||||||
content: t('views.traceManage.pcap.stopOk', { title }),
|
content: t('views.traceManage.pcap.stopOk', { title }),
|
||||||
duration: 3,
|
duration: 3,
|
||||||
@@ -314,11 +395,11 @@ function fnDownPCAP(row?: Record<string, any>) {
|
|||||||
title: t('common.tipTitle'),
|
title: t('common.tipTitle'),
|
||||||
content: t('views.traceManage.pcap.downTip', { title: row.neName }),
|
content: t('views.traceManage.pcap.downTip', { title: row.neName }),
|
||||||
onOk() {
|
onOk() {
|
||||||
const hide = message.loading(t('common.loading'), 0);
|
|
||||||
const fromArr = neIDs.map(id => modalState.from[id]);
|
const fromArr = neIDs.map(id => modalState.from[id]);
|
||||||
const reqArr = [];
|
const reqArr: any = [];
|
||||||
for (const from of fromArr) {
|
for (const from of fromArr) {
|
||||||
if (!from.out) {
|
const taskCode = from.taskCode;
|
||||||
|
if (!taskCode) {
|
||||||
message.warning({
|
message.warning({
|
||||||
content: t('views.traceManage.pcap.stopNotRun', {
|
content: t('views.traceManage.pcap.stopNotRun', {
|
||||||
title: from.title,
|
title: from.title,
|
||||||
@@ -327,24 +408,32 @@ function fnDownPCAP(row?: Record<string, any>) {
|
|||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (from.data.neType === 'UPF' && taskCode.startsWith('/tmp')) {
|
||||||
reqArr.push(
|
const fileName = taskCode.substring(taskCode.lastIndexOf('/') + 1);
|
||||||
getNeFile(
|
reqArr.push(
|
||||||
Object.assign(
|
getNeFile(
|
||||||
{
|
Object.assign(
|
||||||
path: '/tmp',
|
{ path: '/tmp', fileName, delTemp: true },
|
||||||
fileName: `${from.out}.pcap`,
|
from.data
|
||||||
},
|
)
|
||||||
from.data
|
|
||||||
)
|
)
|
||||||
)
|
);
|
||||||
);
|
} else {
|
||||||
|
reqArr.push(
|
||||||
|
dumpDownload(
|
||||||
|
Object.assign({ taskCode: taskCode, delTemp: true }, from.data)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (reqArr.length === 0) return;
|
||||||
|
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
Promise.allSettled(reqArr)
|
Promise.allSettled(reqArr)
|
||||||
.then(resArr => {
|
.then(resArr => {
|
||||||
resArr.forEach((res, idx) => {
|
resArr.forEach((res, idx) => {
|
||||||
const title = fromArr[idx].title;
|
const title = fromArr[idx].title;
|
||||||
|
const taskCode = fromArr[idx].taskCode;
|
||||||
|
|
||||||
if (res.status === 'fulfilled') {
|
if (res.status === 'fulfilled') {
|
||||||
const resV = res.value;
|
const resV = res.value;
|
||||||
@@ -354,9 +443,10 @@ function fnDownPCAP(row?: Record<string, any>) {
|
|||||||
duration: 3,
|
duration: 3,
|
||||||
});
|
});
|
||||||
// 文件名
|
// 文件名
|
||||||
const fileName = `${fromArr[idx].out}.pcap`;
|
if (taskCode.startsWith('/tmp')) {
|
||||||
if (fileName.length > 6) {
|
saveAs(resV.data, `${title}_${Date.now()}.pcap`);
|
||||||
saveAs(resV.data, fileName);
|
} else {
|
||||||
|
saveAs(resV.data, `${title}_${Date.now()}.zip`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
message.warning({
|
message.warning({
|
||||||
@@ -379,15 +469,33 @@ function fnDownPCAP(row?: Record<string, any>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**批量操作 */
|
||||||
|
function fnBatchOper(key: string) {
|
||||||
|
switch (key) {
|
||||||
|
case 'start':
|
||||||
|
fnRecordStart();
|
||||||
|
break;
|
||||||
|
case 'stop':
|
||||||
|
fnRecordStop();
|
||||||
|
break;
|
||||||
|
case 'down':
|
||||||
|
fnDownPCAP();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn('undefined batch oper', key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对话框弹出显示为 查看
|
* 对话框弹出显示为 查看
|
||||||
* @param dictId 字典编号id
|
* @param dictId 编号id
|
||||||
*/
|
*/
|
||||||
function fnModalVisibleByVive(id: string | number) {
|
function fnModalVisibleByVive(id: string | number) {
|
||||||
const from = modalState.from[id];
|
const from = modalState.from[id];
|
||||||
if (!from) return;
|
if (!from) return;
|
||||||
modalState.visibleByView = true;
|
modalState.visibleByView = true;
|
||||||
modalState.logMsg = from.log;
|
modalState.logMsg = from.logMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -399,6 +507,22 @@ function fnModalCancel() {
|
|||||||
modalState.logMsg = '';
|
modalState.logMsg = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**跳转文件数据页面 */
|
||||||
|
function fnFileView(row?: Record<string, any>) {
|
||||||
|
let query = undefined;
|
||||||
|
if (row) {
|
||||||
|
const from = modalState.from[row.id];
|
||||||
|
query = {
|
||||||
|
neId: from.data.neId,
|
||||||
|
neType: from.data.neType,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
router.push({
|
||||||
|
path: `${route.path}${MENU_PATH_INLINE}/file`,
|
||||||
|
query: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 获取网元列表
|
// 获取网元列表
|
||||||
fnGetList();
|
fnGetList();
|
||||||
@@ -411,30 +535,33 @@ onMounted(() => {
|
|||||||
<!-- 插槽-卡片左侧侧 -->
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
<template #title>
|
<template #title>
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
<a-button
|
<a-button @click="fnFileView()">
|
||||||
type="primary"
|
<FileSearchOutlined />
|
||||||
:disabled="tableState.selectedRowKeys.length <= 0"
|
{{ t('views.traceManage.pcap.fileView') }}
|
||||||
@click.prevent="fnRecordStart()"
|
|
||||||
>
|
|
||||||
<template #icon><PlayCircleOutlined /> </template>
|
|
||||||
{{ t('views.traceManage.pcap.textStartBatch') }}
|
|
||||||
</a-button>
|
|
||||||
<a-button
|
|
||||||
danger
|
|
||||||
:disabled="tableState.selectedRowKeys.length <= 0"
|
|
||||||
@click.prevent="fnRecordStop()"
|
|
||||||
>
|
|
||||||
<template #icon><CloseSquareOutlined /> </template>
|
|
||||||
{{ t('views.traceManage.pcap.textStopBatch') }}
|
|
||||||
</a-button>
|
|
||||||
<a-button
|
|
||||||
type="dashed"
|
|
||||||
:disabled="tableState.selectedRowKeys.length <= 0"
|
|
||||||
@click.prevent="fnDownPCAP()"
|
|
||||||
>
|
|
||||||
<template #icon><DownloadOutlined /></template>
|
|
||||||
{{ t('views.traceManage.pcap.textDownBatch') }}
|
|
||||||
</a-button>
|
</a-button>
|
||||||
|
|
||||||
|
<a-dropdown trigger="click">
|
||||||
|
<a-button :disabled="tableState.selectedRowKeys.length <= 0">
|
||||||
|
{{ t('views.traceManage.pcap.batchOper') }}
|
||||||
|
<DownOutlined />
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu @click="({ key }:any) => fnBatchOper(key)">
|
||||||
|
<a-menu-item key="start">
|
||||||
|
<PlayCircleOutlined />
|
||||||
|
{{ t('views.traceManage.pcap.batchStartText') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="stop">
|
||||||
|
<StopOutlined />
|
||||||
|
{{ t('views.traceManage.pcap.batchStopText') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="down">
|
||||||
|
<DownloadOutlined />
|
||||||
|
{{ t('views.traceManage.pcap.batchDownText') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -459,7 +586,7 @@ onMounted(() => {
|
|||||||
:loading="tableState.loading"
|
:loading="tableState.loading"
|
||||||
:data-source="tableState.data"
|
:data-source="tableState.data"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:scroll="{ x: tableColumns.length * 120 }"
|
:scroll="{ x: tableColumns.length * 170 }"
|
||||||
:row-selection="{
|
:row-selection="{
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
selectedRowKeys: tableState.selectedRowKeys,
|
selectedRowKeys: tableState.selectedRowKeys,
|
||||||
@@ -484,57 +611,84 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'id'">
|
<template v-if="column.key === 'id'">
|
||||||
<a-space :size="8" align="start" direction="horizontal">
|
<a-space :size="8" align="start" direction="horizontal">
|
||||||
<a-button
|
<a-tooltip placement="topRight">
|
||||||
type="primary"
|
<template #title>
|
||||||
size="small"
|
<div>{{ t('views.traceManage.pcap.textStart') }}</div>
|
||||||
:loading="modalState.from[record.id].loading"
|
</template>
|
||||||
@click.prevent="fnRecordStart(record)"
|
<a-button
|
||||||
>
|
type="primary"
|
||||||
<template #icon><PlayCircleOutlined /> </template>
|
size="small"
|
||||||
{{ t('views.traceManage.pcap.textStart') }}
|
:disabled="modalState.from[record.id].loading"
|
||||||
</a-button>
|
@click.prevent="fnRecordStart(record)"
|
||||||
|
>
|
||||||
<a-button
|
<template #icon><PlayCircleOutlined /> </template>
|
||||||
type="default"
|
</a-button>
|
||||||
danger
|
</a-tooltip>
|
||||||
size="small"
|
<a-tooltip
|
||||||
@click.prevent="fnRecordStop(record)"
|
placement="topRight"
|
||||||
>
|
|
||||||
<template #icon><CloseSquareOutlined /> </template>
|
|
||||||
{{ t('views.traceManage.pcap.textStop') }}
|
|
||||||
</a-button>
|
|
||||||
|
|
||||||
<a-button
|
|
||||||
type="primary"
|
|
||||||
ghost
|
|
||||||
size="small"
|
|
||||||
@click.prevent="fnModalVisibleByVive(record.id)"
|
|
||||||
v-if="modalState.from[record.id].log"
|
|
||||||
>
|
|
||||||
<template #icon><FileTextOutlined /> </template>
|
|
||||||
{{ t('views.traceManage.pcap.textLog') }}
|
|
||||||
</a-button>
|
|
||||||
|
|
||||||
<a-button
|
|
||||||
type="primary"
|
|
||||||
ghost
|
|
||||||
size="small"
|
|
||||||
@click.prevent="fnDownPCAP(record)"
|
|
||||||
v-if="
|
v-if="
|
||||||
!modalState.from[record.id].loading &&
|
modalState.from[record.id].loading ||
|
||||||
modalState.from[record.id].out
|
modalState.from[record.id].cmdStop
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<template #icon><DownloadOutlined /></template>
|
<template #title>
|
||||||
{{ t('views.traceManage.pcap.textDown') }}
|
<div>{{ t('views.traceManage.pcap.textStop') }}</div>
|
||||||
</a-button>
|
</template>
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
danger
|
||||||
|
size="small"
|
||||||
|
@click.prevent="fnRecordStop(record)"
|
||||||
|
>
|
||||||
|
<template #icon><StopOutlined /> </template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
|
||||||
|
<a-tooltip
|
||||||
|
placement="topRight"
|
||||||
|
v-if="
|
||||||
|
!modalState.from[record.id].loading &&
|
||||||
|
!!modalState.from[record.id].logMsg
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<div>{{ t('views.traceManage.pcap.textLog') }}</div>
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
ghost
|
||||||
|
size="small"
|
||||||
|
@click.prevent="fnModalVisibleByVive(record.id)"
|
||||||
|
>
|
||||||
|
<template #icon><FileTextOutlined /> </template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip
|
||||||
|
placement="topRight"
|
||||||
|
v-if="
|
||||||
|
!modalState.from[record.id].loading &&
|
||||||
|
!!modalState.from[record.id].taskCode
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<div>{{ t('views.traceManage.pcap.textDown') }}</div>
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
ghost
|
||||||
|
size="small"
|
||||||
|
@click.prevent="fnDownPCAP(record)"
|
||||||
|
>
|
||||||
|
<template #icon><DownloadOutlined /> </template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
<!-- 详情框 -->
|
<!-- 日志信息框 -->
|
||||||
<ProModal
|
<ProModal
|
||||||
:drag="true"
|
:drag="true"
|
||||||
:width="800"
|
:width="800"
|
||||||
@@ -542,12 +696,13 @@ onMounted(() => {
|
|||||||
:footer="false"
|
:footer="false"
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
:keyboard="false"
|
:keyboard="false"
|
||||||
|
:body-style="{ padding: '12px' }"
|
||||||
:title="t('views.traceManage.pcap.textLogMsg')"
|
:title="t('views.traceManage.pcap.textLogMsg')"
|
||||||
@cancel="fnModalCancel"
|
@cancel="fnModalCancel"
|
||||||
>
|
>
|
||||||
<a-textarea
|
<a-textarea
|
||||||
v-model:value="modalState.logMsg"
|
v-model:value="modalState.logMsg"
|
||||||
:auto-size="{ minRows: 2, maxRows: 24 }"
|
:auto-size="{ minRows: 2, maxRows: 18 }"
|
||||||
:disabled="true"
|
:disabled="true"
|
||||||
style="color: rgba(0, 0, 0, 0.85)"
|
style="color: rgba(0, 0, 0, 0.85)"
|
||||||
/>
|
/>
|
||||||
|
|||||||
801
src/views/traceManage/task-hlr/index.vue
Normal file
801
src/views/traceManage/task-hlr/index.vue
Normal file
@@ -0,0 +1,801 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, toRaw, ref } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { Form, message, Modal } from 'ant-design-vue/lib';
|
||||||
|
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import {
|
||||||
|
delTaskHLR,
|
||||||
|
listTaskHLR,
|
||||||
|
startTaskHLR,
|
||||||
|
stopTaskHLR,
|
||||||
|
fileTaskHLR,
|
||||||
|
filePullTaskHLR,
|
||||||
|
} from '@/api/trace/taskHLR';
|
||||||
|
import { getNeFile } from '@/api/tool/neFile';
|
||||||
|
import saveAs from 'file-saver';
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
/**开始结束时间 */
|
||||||
|
let queryRangePicker = ref<[string, string]>(['', '']);
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
imsi: '',
|
||||||
|
msisdn: '',
|
||||||
|
/**记录时间 */
|
||||||
|
startTime: '',
|
||||||
|
endTime: '',
|
||||||
|
/**排序字段 */
|
||||||
|
sortField: '',
|
||||||
|
/**排序方式 */
|
||||||
|
sortOrder: 'asc',
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
queryParams = Object.assign(queryParams, {
|
||||||
|
imsi: '',
|
||||||
|
msisdn: '',
|
||||||
|
startTime: '',
|
||||||
|
endTime: '',
|
||||||
|
sortField: '',
|
||||||
|
sortOrder: 'asc',
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
queryRangePicker.value = ['', ''];
|
||||||
|
tablePagination.current = 1;
|
||||||
|
tablePagination.pageSize = 20;
|
||||||
|
fnGetList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**搜索栏 */
|
||||||
|
seached: boolean;
|
||||||
|
/**记录数据 */
|
||||||
|
data: object[];
|
||||||
|
/**勾选记录 */
|
||||||
|
selectedRowKeys: (string | number)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
size: 'middle',
|
||||||
|
seached: true,
|
||||||
|
data: [],
|
||||||
|
selectedRowKeys: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
let tableColumns: ColumnsType = reactive([
|
||||||
|
{
|
||||||
|
title: t('common.rowId'),
|
||||||
|
dataIndex: 'id',
|
||||||
|
align: 'right',
|
||||||
|
width: 50,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.imsi'),
|
||||||
|
dataIndex: 'imsi',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.msisdn'),
|
||||||
|
dataIndex: 'msisdn',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: t('views.traceManage.task.status'),
|
||||||
|
// dataIndex: 'status',
|
||||||
|
// align: 'left',
|
||||||
|
// width: 100,
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.startTime'),
|
||||||
|
dataIndex: 'startTime',
|
||||||
|
align: 'left',
|
||||||
|
customRender(opt) {
|
||||||
|
if (!opt.value) return '';
|
||||||
|
return parseDateToStr(opt.value);
|
||||||
|
},
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.endTime'),
|
||||||
|
dataIndex: 'endTime',
|
||||||
|
align: 'left',
|
||||||
|
customRender(opt) {
|
||||||
|
if (!opt.value) return '';
|
||||||
|
return parseDateToStr(opt.value);
|
||||||
|
},
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('common.operate'),
|
||||||
|
key: 'id',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
let tablePagination = reactive({
|
||||||
|
/**当前页数 */
|
||||||
|
current: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
/**默认的每页条数 */
|
||||||
|
defaultPageSize: 20,
|
||||||
|
/**指定每页可以显示多少条 */
|
||||||
|
pageSizeOptions: ['10', '20', '50', '100'],
|
||||||
|
/**只有一页时是否隐藏分页器 */
|
||||||
|
hideOnSinglePage: false,
|
||||||
|
/**是否可以快速跳转至某页 */
|
||||||
|
showQuickJumper: true,
|
||||||
|
/**是否可以改变 pageSize */
|
||||||
|
showSizeChanger: true,
|
||||||
|
/**数据总数 */
|
||||||
|
total: 0,
|
||||||
|
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
|
||||||
|
onChange: (page: number, pageSize: number) => {
|
||||||
|
tablePagination.current = page;
|
||||||
|
tablePagination.pageSize = pageSize;
|
||||||
|
queryParams.pageNum = page;
|
||||||
|
queryParams.pageSize = pageSize;
|
||||||
|
fnGetList();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格多选 */
|
||||||
|
function fnTableSelectedRowKeys(keys: (string | number)[]) {
|
||||||
|
tableState.selectedRowKeys = keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 信息删除
|
||||||
|
* @param row 记录编号ID
|
||||||
|
*/
|
||||||
|
function fnRecordDelete(row: Record<string, any>) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.traceManage.task.delTaskTip', { id: row.id }),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
delTaskHLR(row.id)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.traceManage.task.delTask', { id: row.id }),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnGetList();
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询信息列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
if (!queryRangePicker.value) {
|
||||||
|
queryRangePicker.value = ['', ''];
|
||||||
|
}
|
||||||
|
queryParams.startTime = queryRangePicker.value[0];
|
||||||
|
queryParams.endTime = queryRangePicker.value[1];
|
||||||
|
listTaskHLR(toRaw(queryParams)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
|
||||||
|
// 取消勾选
|
||||||
|
if (tableState.selectedRowKeys.length > 0) {
|
||||||
|
tableState.selectedRowKeys = [];
|
||||||
|
}
|
||||||
|
tablePagination.total = res.total;
|
||||||
|
tableState.data = res.rows;
|
||||||
|
if (
|
||||||
|
tablePagination.total <=
|
||||||
|
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||||
|
queryParams.pageNum !== 1
|
||||||
|
) {
|
||||||
|
tableState.loading = false;
|
||||||
|
fnGetList(queryParams.pageNum - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 信息停止
|
||||||
|
* @param row 记录编号ID
|
||||||
|
*/
|
||||||
|
function fnRecordStop(id: string) {
|
||||||
|
if (!id || modalState.confirmLoading) return;
|
||||||
|
if (id === '0') {
|
||||||
|
id = tableState.selectedRowKeys.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.traceManage.task.stopTaskTip', { id }),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
stopTaskHLR({ id })
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('views.traceManage.task.stopTask', { id }),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnGetList();
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**详情框是否显示 */
|
||||||
|
visibleByView: boolean;
|
||||||
|
/**新增框或修改框是否显示 */
|
||||||
|
visibleByEdit: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**任务开始结束时间 */
|
||||||
|
timeRangePicker: [string, string];
|
||||||
|
/**表单数据 */
|
||||||
|
from: Record<string, any>;
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
/**文件列表数据 */
|
||||||
|
fileList: any[];
|
||||||
|
/**错误信息 */
|
||||||
|
fileErrMsg: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
visibleByView: false,
|
||||||
|
visibleByEdit: false,
|
||||||
|
title: '',
|
||||||
|
timeRangePicker: ['', ''],
|
||||||
|
from: {
|
||||||
|
id: undefined,
|
||||||
|
startTime: 0,
|
||||||
|
endTime: 0,
|
||||||
|
remark: '',
|
||||||
|
// 跟踪类型用户
|
||||||
|
imsi: '',
|
||||||
|
msisdn: '',
|
||||||
|
},
|
||||||
|
confirmLoading: false,
|
||||||
|
fileList: [],
|
||||||
|
fileErrMsg: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
/**对话框内表单属性和校验规则 */
|
||||||
|
const modalStateFrom = Form.useForm(
|
||||||
|
modalState.from,
|
||||||
|
reactive({
|
||||||
|
endTime: [
|
||||||
|
{
|
||||||
|
required: false,
|
||||||
|
message: t('views.traceManage.task.rangePickerPlease'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 跟踪用户
|
||||||
|
imsi: [
|
||||||
|
{
|
||||||
|
required: false,
|
||||||
|
message: t('views.traceManage.task.imsiPlease'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
msisdn: [
|
||||||
|
{
|
||||||
|
required: false,
|
||||||
|
message: t('views.traceManage.task.msisdnPlease'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**开始结束时间选择对应修改 */
|
||||||
|
function fnRangePickerChange(item: any, _: any) {
|
||||||
|
modalState.from.startTime = +item[0];
|
||||||
|
modalState.from.endTime = +item[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出显示
|
||||||
|
*/
|
||||||
|
function fnModalVisibleByVive(id: Record<string, any>) {
|
||||||
|
if (modalState.confirmLoading) return;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
fileTaskHLR({ id, dir: '/usr/local/log' }).then(res => {
|
||||||
|
modalState.fileErrMsg = '';
|
||||||
|
modalState.fileList = [];
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
hide();
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
for (const item of res.data) {
|
||||||
|
if (item.err != '') {
|
||||||
|
modalState.fileErrMsg += `${item.neName}: ${item.err} \n`;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
modalState.fileList.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
hide();
|
||||||
|
modalState.title = t('views.traceManage.task.viewTask');
|
||||||
|
modalState.visibleByView = true;
|
||||||
|
} else {
|
||||||
|
message.error(t('views.traceManage.task.errorTaskInfo'), 3);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出显示为 新增或者修改
|
||||||
|
* @param id 不传为新增
|
||||||
|
*/
|
||||||
|
function fnModalVisibleByEdit(id?: string) {
|
||||||
|
if (!id) {
|
||||||
|
fnModalCancel();
|
||||||
|
modalState.title = t('views.traceManage.task.addTask');
|
||||||
|
modalState.visibleByEdit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出确认执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalOk() {
|
||||||
|
const from = toRaw(modalState.from);
|
||||||
|
if (from.imsi === '' && from.msisdn === '') {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.traceManage.task.imsiORmsisdn'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
modalStateFrom
|
||||||
|
.validate()
|
||||||
|
.then(e => {
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
|
||||||
|
startTaskHLR(from)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.msgSuccess', { msg: modalState.title }),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
fnModalCancel();
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
fnGetList(1);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出关闭执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalCancel() {
|
||||||
|
modalState.visibleByView = false;
|
||||||
|
modalState.visibleByEdit = false;
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
modalStateFrom.resetFields();
|
||||||
|
modalState.timeRangePicker = ['', ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**下载触发等待 */
|
||||||
|
let downLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**信息文件下载 */
|
||||||
|
function fnDownloadFile(row: Record<string, any>) {
|
||||||
|
if (downLoading.value) return;
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.logManage.neFile.downTip', { fileName: row.fileName }),
|
||||||
|
onOk() {
|
||||||
|
downLoading.value = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
const path = row.filePath.substring(0, row.filePath.lastIndexOf('/'));
|
||||||
|
filePullTaskHLR({
|
||||||
|
neType: row.neType,
|
||||||
|
neId: row.neId,
|
||||||
|
path: path,
|
||||||
|
fileName: row.fileName,
|
||||||
|
delTemp: true,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.msgSuccess', {
|
||||||
|
msg: t('common.downloadText'),
|
||||||
|
}),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
saveAs(res.data, `${row.fileName}`);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: t('views.logManage.neFile.downTipErr'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
downLoading.value = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 获取列表数据
|
||||||
|
fnGetList();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card
|
||||||
|
v-show="tableState.seached"
|
||||||
|
:bordered="false"
|
||||||
|
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
||||||
|
>
|
||||||
|
<!-- 表格搜索栏 -->
|
||||||
|
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item label="IMSI" name="imsi ">
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.imsi"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item label="MSISDN" name="msisdn ">
|
||||||
|
<a-input
|
||||||
|
v-model:value="queryParams.msisdn"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.traceManage.task.time')"
|
||||||
|
name="queryRangePicker"
|
||||||
|
>
|
||||||
|
<a-range-picker
|
||||||
|
v-model:value="queryRangePicker"
|
||||||
|
allow-clear
|
||||||
|
bordered
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
value-format="x"
|
||||||
|
:placeholder="[
|
||||||
|
t('views.traceManage.task.startTime'),
|
||||||
|
t('views.traceManage.task.endTime'),
|
||||||
|
]"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-range-picker>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||||
|
<template #icon><SearchOutlined /></template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="fnQueryReset">
|
||||||
|
<template #icon><ClearOutlined /></template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
|
||||||
|
<template #icon><PlusOutlined /></template>
|
||||||
|
{{ t('common.addText') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
danger
|
||||||
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
|
:loading="modalState.confirmLoading"
|
||||||
|
@click.prevent="fnRecordStop('0')"
|
||||||
|
>
|
||||||
|
<template #icon><StopOutlined /></template>
|
||||||
|
{{ t('views.traceManage.task.textStop') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip placement="topRight">
|
||||||
|
<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="id"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ x: true }"
|
||||||
|
:row-selection="{
|
||||||
|
type: 'checkbox',
|
||||||
|
columnWidth: '48px',
|
||||||
|
selectedRowKeys: tableState.selectedRowKeys,
|
||||||
|
onChange: fnTableSelectedRowKeys,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'id'">
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip v-if="record.status === '1'">
|
||||||
|
<template #title>
|
||||||
|
{{ t('views.traceManage.task.textStop') }}
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
danger
|
||||||
|
@click.prevent="fnRecordStop(record.id)"
|
||||||
|
>
|
||||||
|
<template #icon><StopOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.viewText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnModalVisibleByVive(record.id)"
|
||||||
|
>
|
||||||
|
<template #icon><ProfileOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
|
<a-button type="link" @click.prevent="fnRecordDelete(record)">
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 详情框 -->
|
||||||
|
<ProModal
|
||||||
|
:drag="true"
|
||||||
|
:visible="modalState.visibleByView"
|
||||||
|
:title="modalState.title"
|
||||||
|
@cancel="fnModalCancel"
|
||||||
|
:footer="null"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
name="fileList"
|
||||||
|
layout="horizontal"
|
||||||
|
autocomplete="off"
|
||||||
|
:label-col="{ span: 5 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.traceManage.task.traceFile')"
|
||||||
|
name="fileTree"
|
||||||
|
v-show="modalState.fileList.length > 0"
|
||||||
|
>
|
||||||
|
<a-tree
|
||||||
|
:height="250"
|
||||||
|
:tree-data="modalState.fileList"
|
||||||
|
:field-names="{ title: 'fileName', key: 'filePath' }"
|
||||||
|
>
|
||||||
|
<template #title="item">
|
||||||
|
<span>{{ item.fileName }}</span>
|
||||||
|
<span
|
||||||
|
class="fileTree-download"
|
||||||
|
@click.prevent="fnDownloadFile(item)"
|
||||||
|
>
|
||||||
|
{{ t('common.downloadText') }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</a-tree>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.traceManage.task.errMsg')"
|
||||||
|
name="fileErrMsg"
|
||||||
|
v-show="modalState.fileErrMsg.length > 0"
|
||||||
|
>
|
||||||
|
<a-textarea
|
||||||
|
v-model:value="modalState.fileErrMsg"
|
||||||
|
:auto-size="{ minRows: 2, maxRows: 6 }"
|
||||||
|
:disabled="true"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</ProModal>
|
||||||
|
|
||||||
|
<!-- 新增框或修改框 -->
|
||||||
|
<ProModal
|
||||||
|
:drag="true"
|
||||||
|
:width="800"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
:keyboard="false"
|
||||||
|
:mask-closable="false"
|
||||||
|
:visible="modalState.visibleByEdit"
|
||||||
|
:title="modalState.title"
|
||||||
|
:confirm-loading="modalState.confirmLoading"
|
||||||
|
@ok="fnModalOk"
|
||||||
|
@cancel="fnModalCancel"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
name="modalStateFrom"
|
||||||
|
layout="horizontal"
|
||||||
|
:label-col="{ span: 4 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<!-- 用户跟踪 -->
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.traceManage.task.imsi')"
|
||||||
|
name="imsi"
|
||||||
|
v-bind="modalStateFrom.validateInfos.imsi"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.imsi"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('views.traceManage.task.imsiPlease')"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title>
|
||||||
|
<div>{{ t('views.traceManage.task.imsiTip') }}</div>
|
||||||
|
</template>
|
||||||
|
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.traceManage.task.msisdn')"
|
||||||
|
name="msisdn"
|
||||||
|
v-bind="modalStateFrom.validateInfos.msisdn"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="modalState.from.msisdn"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('views.traceManage.task.msisdnPlease')"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<template #title>
|
||||||
|
<div>{{ t('views.traceManage.task.msisdnTip') }}</div>
|
||||||
|
</template>
|
||||||
|
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.traceManage.task.rangePicker')"
|
||||||
|
name="endTime"
|
||||||
|
v-bind="modalStateFrom.validateInfos.endTime"
|
||||||
|
>
|
||||||
|
<a-range-picker
|
||||||
|
v-model:value="modalState.timeRangePicker"
|
||||||
|
allow-clear
|
||||||
|
bordered
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
value-format="x"
|
||||||
|
:placeholder="[
|
||||||
|
t('views.traceManage.task.startTime'),
|
||||||
|
t('views.traceManage.task.endTime'),
|
||||||
|
]"
|
||||||
|
style="width: 100%"
|
||||||
|
@change="fnRangePickerChange"
|
||||||
|
></a-range-picker>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :label="t('views.traceManage.task.remark')" name="remark">
|
||||||
|
<a-textarea
|
||||||
|
v-model:value="modalState.from.remark"
|
||||||
|
:auto-size="{ minRows: 2, maxRows: 6 }"
|
||||||
|
:maxlength="250"
|
||||||
|
:show-count="true"
|
||||||
|
:placeholder="t('views.traceManage.task.remarkPlease')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</ProModal>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileTree-download {
|
||||||
|
margin-left: 12px;
|
||||||
|
color: #eb2f96;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
335
src/views/traceManage/task/analyze.vue
Normal file
335
src/views/traceManage/task/analyze.vue
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onBeforeUnmount, ref, watch } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { message, Modal } from 'ant-design-vue/lib';
|
||||||
|
import DissectionTree from '../tshark/components/DissectionTree.vue';
|
||||||
|
import DissectionDump from '../tshark/components/DissectionDump.vue';
|
||||||
|
import PacketTable from '../tshark/components/PacketTable.vue';
|
||||||
|
import { usePCAP, NO_SELECTION } from '../tshark/hooks/usePCAP';
|
||||||
|
import {
|
||||||
|
RESULT_CODE_ERROR,
|
||||||
|
RESULT_CODE_SUCCESS,
|
||||||
|
} from '@/constants/result-constants';
|
||||||
|
import { filePullTask } from '@/api/trace/task';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import useTabsStore from '@/store/modules/tabs';
|
||||||
|
import saveAs from 'file-saver';
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const tabsStore = useTabsStore();
|
||||||
|
const ws = new WS();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const {
|
||||||
|
state,
|
||||||
|
handleSelectedTreeEntry,
|
||||||
|
handleSelectedFindSelection,
|
||||||
|
handleSelectedFrame,
|
||||||
|
handleScrollBottom,
|
||||||
|
handleFilterFrames,
|
||||||
|
handleLoadFile,
|
||||||
|
} = usePCAP();
|
||||||
|
|
||||||
|
/**跟踪编号 */
|
||||||
|
const traceId = ref<string>(route.query.traceId as string);
|
||||||
|
|
||||||
|
/**关闭跳转 */
|
||||||
|
function fnClose() {
|
||||||
|
const to = tabsStore.tabClose(route.path);
|
||||||
|
if (to) {
|
||||||
|
router.push(to);
|
||||||
|
} else {
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**下载触发等待 */
|
||||||
|
let downLoading = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**信息文件下载 */
|
||||||
|
function fnDownloadFile() {
|
||||||
|
if (downLoading.value) return;
|
||||||
|
const fileName = `trace_${traceId.value}.pcap`
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.logManage.neFile.downTip', { fileName }),
|
||||||
|
onOk() {
|
||||||
|
downLoading.value = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
filePullTask(traceId.value)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.msgSuccess', {
|
||||||
|
msg: t('common.downloadText'),
|
||||||
|
}),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
saveAs(res.data, `${fileName}`);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: t('views.logManage.neFile.downTipErr'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
downLoading.value = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**获取PCAP文件 */
|
||||||
|
function fnFilePCAP() {
|
||||||
|
filePullTask(traceId.value).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
handleLoadFile(res.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsMessage(res: Record<string, any>) {
|
||||||
|
const { code, requestId, data } = res;
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 建联时发送请求
|
||||||
|
if (!requestId && data.clientId) {
|
||||||
|
fnFilePCAP();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅组信息
|
||||||
|
if (!data?.groupId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data.groupId === `2_${traceId.value}`) {
|
||||||
|
fnFilePCAP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**建立WS连接 */
|
||||||
|
function fnWS() {
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: '/ws',
|
||||||
|
params: {
|
||||||
|
/**订阅通道组
|
||||||
|
*
|
||||||
|
* 跟踪任务PCAP文件 (GroupID:2_traceId)
|
||||||
|
*/
|
||||||
|
subGroupID: `2_${traceId.value}`,
|
||||||
|
},
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: (ev: any) => {
|
||||||
|
// 接收数据后回调
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
//建立连接
|
||||||
|
ws.connect(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => state.initialized,
|
||||||
|
v => {
|
||||||
|
v && fnWS();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
ws.close();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card
|
||||||
|
:bordered="false"
|
||||||
|
:loading="!state.initialized"
|
||||||
|
:body-style="{ padding: '12px' }"
|
||||||
|
>
|
||||||
|
<div class="toolbar">
|
||||||
|
<a-space :size="8" class="toolbar-oper">
|
||||||
|
<a-button type="default" @click.prevent="fnClose()">
|
||||||
|
<template #icon><CloseOutlined /></template>
|
||||||
|
{{ t('common.close') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
:loading="downLoading"
|
||||||
|
@click.prevent="fnDownloadFile()"
|
||||||
|
>
|
||||||
|
<template #icon><DownloadOutlined /></template>
|
||||||
|
{{ t('common.downloadText') }}
|
||||||
|
</a-button>
|
||||||
|
<span>
|
||||||
|
{{ t('views.traceManage.task.traceId') }}:
|
||||||
|
<strong>{{ traceId }}</strong>
|
||||||
|
</span>
|
||||||
|
</a-space>
|
||||||
|
|
||||||
|
<div class="toolbar-info">
|
||||||
|
<a-tag color="green" v-show="!!state.currentFilter">
|
||||||
|
{{ state.currentFilter }}
|
||||||
|
</a-tag>
|
||||||
|
<span> Matched Frame: {{ state.totalFrames }} </span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 包信息 -->
|
||||||
|
<a-popover
|
||||||
|
trigger="click"
|
||||||
|
placement="bottomLeft"
|
||||||
|
v-if="state.summary.filename"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="summary">
|
||||||
|
<div class="summary-item">
|
||||||
|
<span>Type:</span>
|
||||||
|
<span>{{ state.summary.file_type }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span>Encapsulation:</span>
|
||||||
|
<span>{{ state.summary.file_encap_type }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span>Packets:</span>
|
||||||
|
<span>{{ state.summary.packet_count }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span>Duration:</span>
|
||||||
|
<span>{{ Math.round(state.summary.elapsed_time) }}s</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</a-popover>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 包数据表过滤 -->
|
||||||
|
<a-input-group compact>
|
||||||
|
<a-input
|
||||||
|
v-model:value="state.filter"
|
||||||
|
placeholder="display filter, example: tcp"
|
||||||
|
:allow-clear="true"
|
||||||
|
style="width: calc(100% - 100px)"
|
||||||
|
@pressEnter="handleFilterFrames"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<FilterOutlined />
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
html-type="submit"
|
||||||
|
style="width: 100px"
|
||||||
|
@click="handleFilterFrames"
|
||||||
|
>
|
||||||
|
Filter
|
||||||
|
</a-button>
|
||||||
|
</a-input-group>
|
||||||
|
<a-alert
|
||||||
|
:message="state.filterError"
|
||||||
|
type="error"
|
||||||
|
v-if="state.filterError != null"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 包数据表 -->
|
||||||
|
<PacketTable
|
||||||
|
:columns="state.columns"
|
||||||
|
:data="state.packetFrames"
|
||||||
|
:selectedFrame="state.selectedFrame"
|
||||||
|
:onSelectedFrame="handleSelectedFrame"
|
||||||
|
:onScrollBottom="handleScrollBottom"
|
||||||
|
></PacketTable>
|
||||||
|
|
||||||
|
<a-row :gutter="20">
|
||||||
|
<a-col :lg="12" :md="12" :xs="24" class="tree">
|
||||||
|
<!-- 帧数据 -->
|
||||||
|
<DissectionTree
|
||||||
|
id="root"
|
||||||
|
:select="handleSelectedTreeEntry"
|
||||||
|
:selected="state.selectedTreeEntry"
|
||||||
|
:tree="state.selectedPacket.tree"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24" class="dump">
|
||||||
|
<!-- 报文数据 -->
|
||||||
|
<a-tabs
|
||||||
|
v-model:activeKey="state.selectedDataSourceIndex"
|
||||||
|
:tab-bar-gutter="16"
|
||||||
|
:tab-bar-style="{ marginBottom: '8px' }"
|
||||||
|
>
|
||||||
|
<a-tab-pane
|
||||||
|
:key="idx"
|
||||||
|
:tab="v.name"
|
||||||
|
v-for="(v, idx) in state.selectedPacket.data_sources"
|
||||||
|
style="overflow: auto"
|
||||||
|
>
|
||||||
|
<DissectionDump
|
||||||
|
:base64="v.data"
|
||||||
|
:select="(pos:number)=>handleSelectedFindSelection(idx, pos)"
|
||||||
|
:selected="
|
||||||
|
idx === state.selectedTreeEntry.idx
|
||||||
|
? state.selectedTreeEntry
|
||||||
|
: NO_SELECTION
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.toolbar-info {
|
||||||
|
flex: 1;
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.summary-item > span:first-child {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
padding-bottom: 0.75rem;
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-y: auto;
|
||||||
|
user-select: none;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.tree > ul.tree {
|
||||||
|
min-height: 15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dump {
|
||||||
|
padding-bottom: 0.75rem;
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.dump .ant-tabs-tabpane {
|
||||||
|
min-height: calc(15rem - 56px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
File diff suppressed because it is too large
Load Diff
141
src/views/traceManage/tshark/components/DissectionDump.vue
Normal file
141
src/views/traceManage/tshark/components/DissectionDump.vue
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
import DissectionDumpHigh from './DissectionDumpHigh.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
base64: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
type: Object,
|
||||||
|
default: { id: '', idx: 0, start: 0, length: 0 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const addrLines = ref<string[]>([]);
|
||||||
|
const hexLines = ref<string[]>([]);
|
||||||
|
const asciiLines = ref<string[]>([]);
|
||||||
|
|
||||||
|
const asciiHighlight = ref([0, 0]);
|
||||||
|
const hexHighlight = ref([0, 0]);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.selected,
|
||||||
|
newSelected => {
|
||||||
|
const { start, length: size } = newSelected;
|
||||||
|
|
||||||
|
const hexSize = size * 2 + size - 1;
|
||||||
|
const hexPos = start * 2 + start;
|
||||||
|
const asciiPos = start + Math.floor(start / 16);
|
||||||
|
const asciiSize = start + size + Math.floor((start + size) / 16) - asciiPos;
|
||||||
|
|
||||||
|
asciiHighlight.value = [asciiPos, size > 0 ? asciiSize : 0];
|
||||||
|
hexHighlight.value = [hexPos, size > 0 ? hexSize : 0];
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.base64,
|
||||||
|
base64Str => {
|
||||||
|
// Decode base64 to a string
|
||||||
|
const binaryString = atob(base64Str);
|
||||||
|
// Convert binary string to Uint8Array
|
||||||
|
const newBuffer = new Uint8Array(binaryString.length);
|
||||||
|
for (let i = 0; i < binaryString.length; i++) {
|
||||||
|
newBuffer[i] = binaryString.charCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
let addrLinesTemp: string[] = [];
|
||||||
|
let hexLinesTemp: string[] = [];
|
||||||
|
let asciiLinesTemp: string[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < newBuffer.length; i += 16) {
|
||||||
|
let address = i.toString(16).padStart(8, '0');
|
||||||
|
let block = newBuffer.slice(i, i + 16);
|
||||||
|
let hexArray = [];
|
||||||
|
let asciiArray = [];
|
||||||
|
|
||||||
|
for (let value of block) {
|
||||||
|
hexArray.push(value.toString(16).padStart(2, '0'));
|
||||||
|
asciiArray.push(
|
||||||
|
value >= 0x20 && value < 0x7f ? String.fromCharCode(value) : '.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let hexString =
|
||||||
|
hexArray.length > 8
|
||||||
|
? hexArray.slice(0, 8).join(' ') + ' ' + hexArray.slice(8).join(' ')
|
||||||
|
: hexArray.join(' ');
|
||||||
|
|
||||||
|
let asciiString = asciiArray.join('');
|
||||||
|
|
||||||
|
addrLinesTemp.push(address);
|
||||||
|
hexLinesTemp.push(hexString);
|
||||||
|
asciiLinesTemp.push(asciiString);
|
||||||
|
}
|
||||||
|
|
||||||
|
addrLines.value = addrLinesTemp;
|
||||||
|
hexLines.value = hexLinesTemp;
|
||||||
|
asciiLines.value = asciiLinesTemp;
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const onHexClick = (offset: number) => {
|
||||||
|
if (typeof props.select !== 'function') return;
|
||||||
|
props.select(Math.floor(offset / 3));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAsciiClick = (offset: number) => {
|
||||||
|
if (typeof props.select !== 'function') return;
|
||||||
|
props.select(offset - Math.floor(offset / 17));
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="tbd">
|
||||||
|
<div class="tbd-offset">
|
||||||
|
{{ addrLines.join('\n') }}
|
||||||
|
</div>
|
||||||
|
<div class="tbd-box">
|
||||||
|
<DissectionDumpHigh
|
||||||
|
:text="hexLines.join('\n')"
|
||||||
|
:start="hexHighlight[0]"
|
||||||
|
:size="hexHighlight[1]"
|
||||||
|
:onOffsetClicked="onHexClick"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="tbd-box">
|
||||||
|
<DissectionDumpHigh
|
||||||
|
:text="asciiLines.join('\n')"
|
||||||
|
:start="asciiHighlight[0]"
|
||||||
|
:size="asciiHighlight[1]"
|
||||||
|
:onOffsetClicked="onAsciiClick"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.tbd {
|
||||||
|
display: flex;
|
||||||
|
white-space: pre;
|
||||||
|
word-break: break-all;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
}
|
||||||
|
.tbd-offset {
|
||||||
|
color: #6b7280;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.tbd-box {
|
||||||
|
margin-left: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
const props = defineProps({
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
start: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
onOffsetClicked: {
|
||||||
|
type: Function,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const before = computed(() => props.text.substring(0, props.start));
|
||||||
|
const hl = computed(() =>
|
||||||
|
props.text.substring(props.start, props.start + props.size)
|
||||||
|
);
|
||||||
|
const end = computed(() => props.text.substring(props.start + props.size));
|
||||||
|
|
||||||
|
const handleClick = (offset: number) => {
|
||||||
|
const selection = window.getSelection();
|
||||||
|
if (!selection) return;
|
||||||
|
props.onOffsetClicked(selection.anchorOffset + offset);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<span @click="handleClick(0)">{{ before }}</span>
|
||||||
|
<span @click="handleClick(before.length)" class="hl">
|
||||||
|
{{ hl }}
|
||||||
|
</span>
|
||||||
|
<span @click="handleClick(before.length + hl.length)">
|
||||||
|
{{ end }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.hl {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #4b5563;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
60
src/views/traceManage/tshark/components/DissectionTree.vue
Normal file
60
src/views/traceManage/tshark/components/DissectionTree.vue
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
import DissectionTreeSub from './DissectionTreeSub.vue';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
tree: {
|
||||||
|
type: Array as PropType<Record<string, any>[]>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
sub: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
type: Object,
|
||||||
|
default: { id: '', idx: 0, start: 0, length: 0 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ul :class="{ tree: true, 'tree-issub': sub }">
|
||||||
|
<li v-for="(n, i) in tree" :key="`${id}-${i}`" class="tree-li">
|
||||||
|
<DissectionTreeSub
|
||||||
|
:id="`${id}-${i}`"
|
||||||
|
:node="n"
|
||||||
|
:select="select"
|
||||||
|
:selected="selected"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.tree {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0 solid #e5e7eb;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.tree-issub {
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
border-left-width: 1px;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
.tree-li {
|
||||||
|
display: list-item;
|
||||||
|
text-align: -webkit-match-parent;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
116
src/views/traceManage/tshark/components/DissectionTreeSub.vue
Normal file
116
src/views/traceManage/tshark/components/DissectionTreeSub.vue
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
import DissectionTree from './DissectionTree.vue';
|
||||||
|
import {
|
||||||
|
CaretDownOutlined,
|
||||||
|
CaretRightOutlined,
|
||||||
|
MinusOutlined,
|
||||||
|
} from '@ant-design/icons-vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
node: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
type: Function,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const emit = defineEmits(['update:selected']);
|
||||||
|
|
||||||
|
const open = ref(false);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.selected,
|
||||||
|
() => {
|
||||||
|
if (!open.value) {
|
||||||
|
open.value = props.selected.id.startsWith(props.id + '-');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const toggle = () => {
|
||||||
|
if (open.value && props.selected.id.startsWith(props.id + '-')) {
|
||||||
|
const NO_SELECTION = { id: '', idx: 0, start: 0, length: 0 };
|
||||||
|
emit('update:selected', NO_SELECTION);
|
||||||
|
if (typeof props.select === 'function') {
|
||||||
|
props.select(NO_SELECTION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
open.value = !open.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (props.node.length > 0) {
|
||||||
|
const select = {
|
||||||
|
id: props.id,
|
||||||
|
idx: props.node.data_source_idx,
|
||||||
|
start: props.node.start,
|
||||||
|
length: props.node.length,
|
||||||
|
};
|
||||||
|
emit('update:selected', select);
|
||||||
|
if (typeof props.select === 'function') {
|
||||||
|
props.select(select);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="{ 'tree-sub': true, 'tree-sub_hl': id === selected.id }">
|
||||||
|
<component
|
||||||
|
:is="
|
||||||
|
node.tree && node.tree.length > 0
|
||||||
|
? open
|
||||||
|
? CaretDownOutlined
|
||||||
|
: CaretRightOutlined
|
||||||
|
: MinusOutlined
|
||||||
|
"
|
||||||
|
class="tree-sub_icon"
|
||||||
|
@click="toggle"
|
||||||
|
/>
|
||||||
|
<span @click="handleClick" @dblclick="toggle" class="tree-sub_text">
|
||||||
|
{{ node.label }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<DissectionTree
|
||||||
|
v-if="node.tree && node.tree.length > 0 && open"
|
||||||
|
:id="id"
|
||||||
|
:tree="node.tree"
|
||||||
|
:select="select"
|
||||||
|
:selected="selected"
|
||||||
|
sub
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.tree-sub {
|
||||||
|
display: inline-flex;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.tree-sub_hl {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #4b5563;
|
||||||
|
}
|
||||||
|
.tree-sub_icon {
|
||||||
|
color: #6b7280;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
.tree-sub_text {
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
293
src/views/traceManage/tshark/components/PacketTable.vue
Normal file
293
src/views/traceManage/tshark/components/PacketTable.vue
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref, computed, unref, onUpdated, watchEffect } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
/**列表高度 */
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
default: 300,
|
||||||
|
},
|
||||||
|
/**列表项高度 */
|
||||||
|
itemHeight: {
|
||||||
|
type: Number,
|
||||||
|
default: 30,
|
||||||
|
},
|
||||||
|
/**数据 */
|
||||||
|
data: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
/**预先兜底缓存数量 */
|
||||||
|
cache: {
|
||||||
|
type: Number,
|
||||||
|
default: 2,
|
||||||
|
},
|
||||||
|
|
||||||
|
/**列 */
|
||||||
|
columns: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
selectedFrame: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
onSelectedFrame: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
onScrollBottom: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const state = reactive<any>({
|
||||||
|
start: 0,
|
||||||
|
end: 10,
|
||||||
|
scrollOffset: 0,
|
||||||
|
cacheData: [],
|
||||||
|
});
|
||||||
|
const virtualListRef = ref();
|
||||||
|
|
||||||
|
const getWrapperStyle = computed(() => {
|
||||||
|
const { height } = props;
|
||||||
|
return {
|
||||||
|
height: `${height}px`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const getInnerStyle = computed(() => {
|
||||||
|
return {
|
||||||
|
height: `${unref(getTotalHeight)}px`,
|
||||||
|
width: '100%',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const getListStyle = computed(() => {
|
||||||
|
return {
|
||||||
|
willChange: 'transform',
|
||||||
|
transform: `translateY(${state.scrollOffset}px)`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 数据数量
|
||||||
|
const total = computed(() => {
|
||||||
|
return props.data.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 总体高度
|
||||||
|
const getTotalHeight = computed(() => {
|
||||||
|
return unref(total) * props.itemHeight;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 当前屏幕显示的数量
|
||||||
|
const clientCount = computed(() => {
|
||||||
|
return Math.ceil(props.height / props.itemHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 当前屏幕显示的数据
|
||||||
|
const clientData = computed<any[]>(() => {
|
||||||
|
return props.data.slice(state.start, state.end);
|
||||||
|
});
|
||||||
|
|
||||||
|
const onScroll = (e: any) => {
|
||||||
|
const { scrollHeight, scrollTop, clientHeight } = e.target;
|
||||||
|
if (state.scrollOffset === scrollTop) return;
|
||||||
|
const { cache, height, itemHeight } = props;
|
||||||
|
const cacheCount = Math.max(1, cache);
|
||||||
|
|
||||||
|
let startIndex = Math.floor(scrollTop / itemHeight);
|
||||||
|
|
||||||
|
const endIndex = Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(unref(total), startIndex + unref(clientCount) + cacheCount)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (startIndex > cacheCount) {
|
||||||
|
startIndex = startIndex - cacheCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 偏移量
|
||||||
|
const offset = scrollTop - (scrollTop % itemHeight);
|
||||||
|
Object.assign(state, {
|
||||||
|
start: startIndex,
|
||||||
|
end: endIndex,
|
||||||
|
scrollOffset: offset,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 底部小于高度时触发
|
||||||
|
if (scrollHeight - scrollTop - clientHeight < height) {
|
||||||
|
props.onScrollBottom(endIndex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onUpdated(() => {});
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
clientData.value.forEach((_, index) => {
|
||||||
|
const currentIndex = state.start + index;
|
||||||
|
if (Object.hasOwn(state.cacheData, currentIndex)) return;
|
||||||
|
state.cacheData[currentIndex] = {
|
||||||
|
top: currentIndex * props.itemHeight,
|
||||||
|
height: props.itemHeight,
|
||||||
|
bottom: (currentIndex + 1) * props.itemHeight,
|
||||||
|
index: currentIndex,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const tableState = reactive({
|
||||||
|
selected: false,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="table">
|
||||||
|
<div class="thead">
|
||||||
|
<div class="thead-item" v-for="v in columns">
|
||||||
|
{{ v }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="virtual-list-wrapper"
|
||||||
|
ref="wrapperRef"
|
||||||
|
:style="getWrapperStyle"
|
||||||
|
@scroll="onScroll"
|
||||||
|
>
|
||||||
|
<div class="virtual-list-inner" ref="innerRef" :style="getInnerStyle">
|
||||||
|
<div class="virtual-list" :style="getListStyle" ref="virtualListRef">
|
||||||
|
<div
|
||||||
|
class="tbody"
|
||||||
|
v-for="(item, index) in clientData"
|
||||||
|
:key="index + state.start"
|
||||||
|
:style="{
|
||||||
|
height: itemHeight + 'px',
|
||||||
|
backgroundColor:
|
||||||
|
item.number === props.selectedFrame
|
||||||
|
? 'blue'
|
||||||
|
: item.bg
|
||||||
|
? `#${item.bg.toString(16).padStart(6, '0')}`
|
||||||
|
: '',
|
||||||
|
color:
|
||||||
|
item.number === props.selectedFrame
|
||||||
|
? 'white'
|
||||||
|
: item.fg
|
||||||
|
? `#${item.fg.toString(16).padStart(6, '0')}`
|
||||||
|
: '',
|
||||||
|
}"
|
||||||
|
@click="onSelectedFrame(item.number)"
|
||||||
|
>
|
||||||
|
<div class="tbody-item" v-for="col in item.columns">
|
||||||
|
{{ col }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.virtual-list-wrapper {
|
||||||
|
position: relative;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.thead {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
.thead-item {
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-bottom: 0.25rem;
|
||||||
|
padding-top: 0.25rem;
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
padding-right: 0.5rem;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
/* flex-basis: 100%; */
|
||||||
|
}
|
||||||
|
.tbody {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
border-top: 1px #f0f0f0 solid;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.tbody-item {
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
padding-right: 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
/* flex-basis: 100%; */
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thead-item:nth-child(1),
|
||||||
|
.tbody-item:nth-child(1) {
|
||||||
|
flex-basis: 5rem;
|
||||||
|
width: 5rem;
|
||||||
|
}
|
||||||
|
.tbody-item:nth-child(1) {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.thead-item:nth-child(2),
|
||||||
|
.tbody-item:nth-child(2) {
|
||||||
|
flex-basis: 8rem;
|
||||||
|
width: 8rem;
|
||||||
|
}
|
||||||
|
.thead-item:nth-child(3),
|
||||||
|
.tbody-item:nth-child(3) {
|
||||||
|
flex-basis: 8rem;
|
||||||
|
width: 8rem;
|
||||||
|
}
|
||||||
|
.thead-item:nth-child(4),
|
||||||
|
.tbody-item:nth-child(4) {
|
||||||
|
flex-basis: 8rem;
|
||||||
|
width: 8rem;
|
||||||
|
}
|
||||||
|
.thead-item:nth-child(5),
|
||||||
|
.tbody-item:nth-child(5) {
|
||||||
|
flex-basis: 6rem;
|
||||||
|
width: 6rem;
|
||||||
|
}
|
||||||
|
.thead-item:nth-child(6),
|
||||||
|
.tbody-item:nth-child(6) {
|
||||||
|
flex-basis: 6rem;
|
||||||
|
width: 6rem;
|
||||||
|
}
|
||||||
|
.tbody-item:nth-child(6) {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.thead-item:nth-child(7),
|
||||||
|
.tbody-item:nth-child(7) {
|
||||||
|
text-align: left;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
flex: 1;
|
||||||
|
width: 5rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 修改滚动条的样式 */
|
||||||
|
.tbody-item:nth-child(7)::-webkit-scrollbar {
|
||||||
|
width: 4px; /* 设置滚动条宽度 */
|
||||||
|
height: 4px;
|
||||||
|
}
|
||||||
|
.tbody-item:nth-child(7)::-webkit-scrollbar-track {
|
||||||
|
background-color: #f0f0f0; /* 设置滚动条轨道背景颜色 */
|
||||||
|
}
|
||||||
|
.tbody-item:nth-child(7)::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #bfbfbf; /* 设置滚动条滑块颜色 */
|
||||||
|
}
|
||||||
|
.tbody-item:nth-child(7)::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: #1890ff; /* 设置鼠标悬停时滚动条滑块颜色 */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
317
src/views/traceManage/tshark/hooks/usePCAP.ts
Normal file
317
src/views/traceManage/tshark/hooks/usePCAP.ts
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
import { onBeforeUnmount, onMounted, reactive } from 'vue';
|
||||||
|
import { scriptUrl } from '@/assets/js/wiregasm_worker';
|
||||||
|
import { WK, OptionsType } from '@/plugins/wk-worker';
|
||||||
|
const wk = new WK();
|
||||||
|
|
||||||
|
export const NO_SELECTION = { id: '', idx: 0, start: 0, length: 0 };
|
||||||
|
|
||||||
|
type StateType = {
|
||||||
|
/**初始化 */
|
||||||
|
initialized: boolean;
|
||||||
|
/**pcap信息 */
|
||||||
|
summary: {
|
||||||
|
filename: string;
|
||||||
|
file_type: string;
|
||||||
|
file_length: number;
|
||||||
|
file_encap_type: string;
|
||||||
|
packet_count: number;
|
||||||
|
start_time: number;
|
||||||
|
stop_time: number;
|
||||||
|
elapsed_time: number;
|
||||||
|
};
|
||||||
|
/**字段 */
|
||||||
|
columns: string[];
|
||||||
|
/**过滤条件 */
|
||||||
|
filter: string;
|
||||||
|
/**过滤条件错误信息 */
|
||||||
|
filterError: string | null;
|
||||||
|
/**当前过滤条件 */
|
||||||
|
currentFilter: string;
|
||||||
|
/**当前选中的帧编号 */
|
||||||
|
selectedFrame: number;
|
||||||
|
/**当前选中的帧数据 */
|
||||||
|
selectedPacket: { tree: any[]; data_sources: any[] };
|
||||||
|
/**pcap包帧数据 */
|
||||||
|
packetFrameData: Map<string, any> | null;
|
||||||
|
/**当前选中的帧数据-空占位 */
|
||||||
|
selectedTreeEntry: typeof NO_SELECTION;
|
||||||
|
/**选择帧的Dump数据标签 */
|
||||||
|
selectedDataSourceIndex: number;
|
||||||
|
/**处理完成状态 */
|
||||||
|
finishedProcessing: boolean;
|
||||||
|
/**pcap包帧数,匹配帧数 */
|
||||||
|
totalFrames: number;
|
||||||
|
/**pcap包帧数据 */
|
||||||
|
packetFrames: any[];
|
||||||
|
/**加载帧数 */
|
||||||
|
nextPageSize: number;
|
||||||
|
/**加载页数 */
|
||||||
|
nextPageNum: number;
|
||||||
|
/**加载下一页 */
|
||||||
|
nextPageLoad: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function usePCAP() {
|
||||||
|
const state = reactive<StateType>({
|
||||||
|
initialized: false,
|
||||||
|
summary: {
|
||||||
|
filename: '',
|
||||||
|
file_type: 'Wireshark/tcpdump/... - pcap',
|
||||||
|
file_length: 0,
|
||||||
|
file_encap_type: 'Ethernet',
|
||||||
|
packet_count: 0,
|
||||||
|
start_time: 0,
|
||||||
|
stop_time: 0,
|
||||||
|
elapsed_time: 0,
|
||||||
|
},
|
||||||
|
columns: [],
|
||||||
|
filter: '',
|
||||||
|
filterError: null,
|
||||||
|
currentFilter: '',
|
||||||
|
selectedFrame: 1,
|
||||||
|
/**当前选中的帧数据 */
|
||||||
|
selectedPacket: { tree: [], data_sources: [] },
|
||||||
|
packetFrameData: null, // 注意:Map 需要额外处理
|
||||||
|
selectedTreeEntry: NO_SELECTION, // NO_SELECTION 需要定义
|
||||||
|
/**选择帧的Dump数据标签 */
|
||||||
|
selectedDataSourceIndex: 0,
|
||||||
|
/**处理完成状态 */
|
||||||
|
finishedProcessing: false,
|
||||||
|
totalFrames: 0,
|
||||||
|
packetFrames: [],
|
||||||
|
nextPageNum: 1,
|
||||||
|
nextPageSize: 40,
|
||||||
|
nextPageLoad: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 清除帧数据和报文信息状态
|
||||||
|
function handleStateReset() {
|
||||||
|
// 加载pcap包的数据
|
||||||
|
state.nextPageNum = 1;
|
||||||
|
// 选择帧的数据
|
||||||
|
state.selectedFrame = 0;
|
||||||
|
state.selectedPacket = { tree: [], data_sources: [] };
|
||||||
|
state.packetFrameData = null;
|
||||||
|
state.selectedTreeEntry = NO_SELECTION;
|
||||||
|
state.selectedDataSourceIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**解析帧数据为简单结构 */
|
||||||
|
function parseFrameData(id: string, node: Record<string, any>) {
|
||||||
|
let map = new Map();
|
||||||
|
|
||||||
|
if (node.tree && node.tree.length > 0) {
|
||||||
|
for (let i = 0; i < node.tree.length; i++) {
|
||||||
|
const subMap = parseFrameData(`${id}-${i}`, node.tree[i]);
|
||||||
|
subMap.forEach((value, key) => {
|
||||||
|
map.set(key, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (node.length > 0) {
|
||||||
|
map.set(id, {
|
||||||
|
id: id,
|
||||||
|
idx: node.data_source_idx,
|
||||||
|
start: node.start,
|
||||||
|
length: node.length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**帧数据点击选中 */
|
||||||
|
function handleSelectedTreeEntry(e: any) {
|
||||||
|
console.log('fnSelectedTreeEntry', e);
|
||||||
|
state.selectedTreeEntry = e;
|
||||||
|
}
|
||||||
|
/**报文数据点击选中 */
|
||||||
|
function handleSelectedFindSelection(src_idx: number, pos: number) {
|
||||||
|
console.log('fnSelectedFindSelection', pos);
|
||||||
|
if (state.packetFrameData == null) return;
|
||||||
|
// find the smallest one
|
||||||
|
let current = null;
|
||||||
|
for (let [k, pp] of state.packetFrameData) {
|
||||||
|
if (pp.idx !== src_idx) continue;
|
||||||
|
|
||||||
|
if (pos >= pp.start && pos <= pp.start + pp.length) {
|
||||||
|
if (
|
||||||
|
current != null &&
|
||||||
|
state.packetFrameData.get(current).length > pp.length
|
||||||
|
) {
|
||||||
|
current = k;
|
||||||
|
} else {
|
||||||
|
current = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current != null) {
|
||||||
|
state.selectedTreeEntry = state.packetFrameData.get(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**包数据表点击选中 */
|
||||||
|
function handleSelectedFrame(no: number) {
|
||||||
|
console.log('fnSelectedFrame', no, state.totalFrames);
|
||||||
|
state.selectedFrame = no;
|
||||||
|
wk.send({ type: 'select', number: state.selectedFrame });
|
||||||
|
}
|
||||||
|
/**包数据表滚动底部加载 */
|
||||||
|
function handleScrollBottom() {
|
||||||
|
const totalFetched = state.packetFrames.length;
|
||||||
|
console.log('fnScrollBottom', totalFetched);
|
||||||
|
if (!state.nextPageLoad && totalFetched < state.totalFrames) {
|
||||||
|
state.nextPageLoad = true;
|
||||||
|
state.nextPageNum++;
|
||||||
|
loaldFrames(state.filter, state.nextPageNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**包数据表过滤 */
|
||||||
|
function handleFilterFrames() {
|
||||||
|
console.log('fnFilterFinish', state.filter);
|
||||||
|
wk.send({ type: 'check-filter', filter: state.filter });
|
||||||
|
}
|
||||||
|
/**包数据表加载 */
|
||||||
|
function loaldFrames(filter: string, page: number = 1) {
|
||||||
|
if (!(state.initialized && state.finishedProcessing)) return;
|
||||||
|
const limit = state.nextPageSize;
|
||||||
|
wk.send({
|
||||||
|
type: 'frames',
|
||||||
|
filter: filter,
|
||||||
|
skip: (page - 1) * limit,
|
||||||
|
limit: limit,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**加载包文件 */
|
||||||
|
function handleLoadFile(file: File | Blob) {
|
||||||
|
state.summary = {
|
||||||
|
filename: '',
|
||||||
|
file_type: 'Wireshark/tcpdump/... - pcap',
|
||||||
|
file_length: 0,
|
||||||
|
file_encap_type: 'Ethernet',
|
||||||
|
packet_count: 0,
|
||||||
|
start_time: 0,
|
||||||
|
stop_time: 0,
|
||||||
|
elapsed_time: 0,
|
||||||
|
};
|
||||||
|
state.finishedProcessing = false;
|
||||||
|
|
||||||
|
wk.send({ type: 'process', file: file });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**本地示例文件 */
|
||||||
|
async function handleLoadExample() {
|
||||||
|
const name = 'test_ethernet.pcap';
|
||||||
|
const res = await fetch('/wiregasm/test_ethernet.pcap');
|
||||||
|
const body = await res.arrayBuffer();
|
||||||
|
|
||||||
|
state.summary = {
|
||||||
|
filename: '',
|
||||||
|
file_type: 'Wireshark/tcpdump/... - pcap',
|
||||||
|
file_length: 0,
|
||||||
|
file_encap_type: 'Ethernet',
|
||||||
|
packet_count: 0,
|
||||||
|
start_time: 0,
|
||||||
|
stop_time: 0,
|
||||||
|
elapsed_time: 0,
|
||||||
|
};
|
||||||
|
state.finishedProcessing = false;
|
||||||
|
|
||||||
|
wk.send({ type: 'process-data', name: name, data: body });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wkMessage(res: Record<string, any>) {
|
||||||
|
switch (res.type) {
|
||||||
|
case 'status':
|
||||||
|
console.info(res.status);
|
||||||
|
break;
|
||||||
|
case 'error':
|
||||||
|
console.warn(res.error);
|
||||||
|
break;
|
||||||
|
case 'init':
|
||||||
|
wk.send({ type: 'columns' });
|
||||||
|
state.initialized = true;
|
||||||
|
break;
|
||||||
|
case 'columns':
|
||||||
|
state.columns = res.data;
|
||||||
|
break;
|
||||||
|
case 'frames':
|
||||||
|
// console.log(res.data);
|
||||||
|
const { matched, frames } = res.data;
|
||||||
|
state.totalFrames = matched;
|
||||||
|
|
||||||
|
if (state.nextPageNum == 1) {
|
||||||
|
state.packetFrames = frames;
|
||||||
|
// 有匹配的选择第一个
|
||||||
|
if (frames.length > 0) {
|
||||||
|
state.selectedFrame = frames[0].number;
|
||||||
|
handleSelectedFrame(state.selectedFrame);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.packetFrames = state.packetFrames.concat(frames);
|
||||||
|
state.nextPageLoad = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'selected':
|
||||||
|
state.selectedPacket = res.data;
|
||||||
|
state.packetFrameData = parseFrameData('root', res.data);
|
||||||
|
state.selectedTreeEntry = NO_SELECTION;
|
||||||
|
state.selectedDataSourceIndex = 0;
|
||||||
|
break;
|
||||||
|
case 'processed':
|
||||||
|
// setStatus(`Error: non-zero return code (${e.data.code})`);
|
||||||
|
state.finishedProcessing = true;
|
||||||
|
if (res.data.code === 0) {
|
||||||
|
state.summary = res.data.summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
handleStateReset();
|
||||||
|
loaldFrames(state.filter);
|
||||||
|
break;
|
||||||
|
case 'filter':
|
||||||
|
const filterRes = res.data;
|
||||||
|
if (filterRes.ok) {
|
||||||
|
state.currentFilter = state.filter;
|
||||||
|
state.filterError = null;
|
||||||
|
// 加载数据
|
||||||
|
handleStateReset();
|
||||||
|
loaldFrames(state.filter);
|
||||||
|
} else {
|
||||||
|
state.filterError = filterRes.error;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn(res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 建立链接
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: scriptUrl,
|
||||||
|
onmessage: wkMessage,
|
||||||
|
onerror: (ev: any) => {
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
wk.connect(options);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
wk.send({ type: 'close' }) && wk.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
state,
|
||||||
|
handleSelectedTreeEntry,
|
||||||
|
handleSelectedFindSelection,
|
||||||
|
handleSelectedFrame,
|
||||||
|
handleScrollBottom,
|
||||||
|
handleFilterFrames,
|
||||||
|
handleLoadExample,
|
||||||
|
handleLoadFile,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export default usePCAP;
|
||||||
230
src/views/traceManage/tshark/index.vue
Normal file
230
src/views/traceManage/tshark/index.vue
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { message, Upload } from 'ant-design-vue/lib';
|
||||||
|
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
|
||||||
|
import { FileType } from 'ant-design-vue/lib/upload/interface';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import DissectionTree from './components/DissectionTree.vue';
|
||||||
|
import DissectionDump from './components/DissectionDump.vue';
|
||||||
|
import PacketTable from './components/PacketTable.vue';
|
||||||
|
import { usePCAP, NO_SELECTION } from './hooks/usePCAP';
|
||||||
|
import { parseSizeFromFile } from '@/utils/parse-utils';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
const { t } = useI18n();
|
||||||
|
const {
|
||||||
|
state,
|
||||||
|
handleSelectedTreeEntry,
|
||||||
|
handleSelectedFindSelection,
|
||||||
|
handleSelectedFrame,
|
||||||
|
handleScrollBottom,
|
||||||
|
handleFilterFrames,
|
||||||
|
handleLoadExample,
|
||||||
|
handleLoadFile,
|
||||||
|
} = usePCAP();
|
||||||
|
|
||||||
|
/**上传前检查或转换压缩 */
|
||||||
|
function fnBeforeUpload(file: FileType) {
|
||||||
|
const fileName = file.name;
|
||||||
|
const suff = fileName.substring(fileName.lastIndexOf('.'));
|
||||||
|
const allowList = ['.pcap', '.cap', '.pcapng', '.pcap0'];
|
||||||
|
if (!allowList.includes(suff)) {
|
||||||
|
const msg = `${t('components.UploadModal.onlyAllow')} ${allowList.join(
|
||||||
|
','
|
||||||
|
)}`;
|
||||||
|
message.error(msg, 3);
|
||||||
|
return Upload.LIST_IGNORE;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表单上传文件 */
|
||||||
|
function fnUpload(up: UploadRequestOption) {
|
||||||
|
handleLoadFile(up.file as File);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card
|
||||||
|
:bordered="false"
|
||||||
|
:loading="!state.initialized"
|
||||||
|
:body-style="{ padding: '12px' }"
|
||||||
|
>
|
||||||
|
<div class="toolbar">
|
||||||
|
<a-space :size="8" class="toolbar-oper">
|
||||||
|
<a-upload
|
||||||
|
name="file"
|
||||||
|
list-type="picture"
|
||||||
|
:max-count="1"
|
||||||
|
accept=".pcap,.cap,.pcapng,.pcap0"
|
||||||
|
:show-upload-list="false"
|
||||||
|
:before-upload="fnBeforeUpload"
|
||||||
|
:custom-request="fnUpload"
|
||||||
|
>
|
||||||
|
<a-button type="primary"> Upload </a-button>
|
||||||
|
</a-upload>
|
||||||
|
<a-button @click="handleLoadExample()">Example</a-button>
|
||||||
|
</a-space>
|
||||||
|
|
||||||
|
<div class="toolbar-info">
|
||||||
|
<a-tag color="green" v-show="!!state.currentFilter">
|
||||||
|
{{ state.currentFilter }}
|
||||||
|
</a-tag>
|
||||||
|
<span> Matched Frame: {{ state.totalFrames }} </span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 包信息 -->
|
||||||
|
<a-popover
|
||||||
|
trigger="click"
|
||||||
|
placement="bottomLeft"
|
||||||
|
v-if="state.summary.filename"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="summary">
|
||||||
|
<div class="summary-item">
|
||||||
|
<span>Type:</span>
|
||||||
|
<span>{{ state.summary.file_type }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span>Size:</span>
|
||||||
|
<span>{{ parseSizeFromFile(state.summary.file_length) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span>Encapsulation:</span>
|
||||||
|
<span>{{ state.summary.file_encap_type }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span>Packets:</span>
|
||||||
|
<span>{{ state.summary.packet_count }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span>Duration:</span>
|
||||||
|
<span>{{ Math.round(state.summary.elapsed_time) }}s</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</a-popover>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 包数据表过滤 -->
|
||||||
|
<a-input-group compact>
|
||||||
|
<a-input
|
||||||
|
v-model:value="state.filter"
|
||||||
|
placeholder="display filter, example: tcp"
|
||||||
|
:allow-clear="true"
|
||||||
|
style="width: calc(100% - 100px)"
|
||||||
|
@pressEnter="handleFilterFrames"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<FilterOutlined />
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
html-type="submit"
|
||||||
|
style="width: 100px"
|
||||||
|
@click="handleFilterFrames"
|
||||||
|
>
|
||||||
|
Filter
|
||||||
|
</a-button>
|
||||||
|
</a-input-group>
|
||||||
|
<a-alert
|
||||||
|
:message="state.filterError"
|
||||||
|
type="error"
|
||||||
|
v-if="state.filterError != null"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 包数据表 -->
|
||||||
|
<PacketTable
|
||||||
|
:columns="state.columns"
|
||||||
|
:data="state.packetFrames"
|
||||||
|
:selectedFrame="state.selectedFrame"
|
||||||
|
:onSelectedFrame="handleSelectedFrame"
|
||||||
|
:onScrollBottom="handleScrollBottom"
|
||||||
|
></PacketTable>
|
||||||
|
|
||||||
|
<a-row :gutter="20">
|
||||||
|
<a-col :lg="12" :md="12" :xs="24" class="tree">
|
||||||
|
<!-- 帧数据 -->
|
||||||
|
<DissectionTree
|
||||||
|
id="root"
|
||||||
|
:select="handleSelectedTreeEntry"
|
||||||
|
:selected="state.selectedTreeEntry"
|
||||||
|
:tree="state.selectedPacket.tree"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24" class="dump">
|
||||||
|
<!-- 报文数据 -->
|
||||||
|
<a-tabs
|
||||||
|
v-model:activeKey="state.selectedDataSourceIndex"
|
||||||
|
:tab-bar-gutter="16"
|
||||||
|
:tab-bar-style="{ marginBottom: '8px' }"
|
||||||
|
>
|
||||||
|
<a-tab-pane
|
||||||
|
:key="idx"
|
||||||
|
:tab="v.name"
|
||||||
|
v-for="(v, idx) in state.selectedPacket.data_sources"
|
||||||
|
style="overflow: auto"
|
||||||
|
>
|
||||||
|
<DissectionDump
|
||||||
|
:base64="v.data"
|
||||||
|
:select="(pos:number)=>handleSelectedFindSelection(idx, pos)"
|
||||||
|
:selected="
|
||||||
|
idx === state.selectedTreeEntry.idx
|
||||||
|
? state.selectedTreeEntry
|
||||||
|
: NO_SELECTION
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.toolbar-info {
|
||||||
|
flex: 1;
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.summary-item > span:first-child {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
padding-bottom: 0.75rem;
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-y: auto;
|
||||||
|
user-select: none;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.tree > ul.tree {
|
||||||
|
min-height: 15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dump {
|
||||||
|
padding-bottom: 0.75rem;
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.dump .ant-tabs-tabpane {
|
||||||
|
min-height: calc(15rem - 56px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
16
src/views/traceManage/wireshark/index.vue
Normal file
16
src/views/traceManage/wireshark/index.vue
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, onMounted, toRaw } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
|
||||||
|
onMounted(() => {});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<h1>JS</h1>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
||||||
@@ -20,7 +20,7 @@ export default defineConfig(({ mode }) => {
|
|||||||
proxy: {
|
proxy: {
|
||||||
// https://cn.vitejs.dev/config/#server-proxy
|
// https://cn.vitejs.dev/config/#server-proxy
|
||||||
[env.VITE_API_BASE_URL]: {
|
[env.VITE_API_BASE_URL]: {
|
||||||
// target: 'http://192.168.2.166:3030',
|
// target: 'http://192.168.2.166:33030',
|
||||||
target: 'http://192.168.5.58:33040',
|
target: 'http://192.168.5.58:33040',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: p => p.replace(env.VITE_API_BASE_URL, ''),
|
rewrite: p => p.replace(env.VITE_API_BASE_URL, ''),
|
||||||
@@ -54,6 +54,7 @@ export default defineConfig(({ mode }) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
|
target: 'esnext', // Use 'esnext' to support the latest features
|
||||||
sourcemap: false,
|
sourcemap: false,
|
||||||
chunkSizeWarningLimit: 500, // 调整区块大小警告限制(以kB为单位)
|
chunkSizeWarningLimit: 500, // 调整区块大小警告限制(以kB为单位)
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
@@ -74,6 +75,11 @@ export default defineConfig(({ mode }) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
|
esbuildOptions: {
|
||||||
|
supported: {
|
||||||
|
'top-level-await': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
include: ['@ant-design/icons-vue', 'ant-design-vue'],
|
include: ['@ant-design/icons-vue', 'ant-design-vue'],
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|||||||
Reference in New Issue
Block a user