Merge branch 'lichang' into lite
This commit is contained in:
18
package.json
18
package.json
@@ -12,19 +12,19 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons-vue": "^7.0.1",
|
"@ant-design/icons-vue": "7.0.1",
|
||||||
"@antv/g6": "4.8.24",
|
"@antv/g6": "4.8.25",
|
||||||
"@codemirror/lang-javascript": "6.2.3",
|
"@codemirror/lang-javascript": "6.2.3",
|
||||||
"@codemirror/lang-yaml": "6.1.2",
|
"@codemirror/lang-yaml": "6.1.2",
|
||||||
"@codemirror/merge": "6.10.0",
|
"@codemirror/merge": "6.10.0",
|
||||||
"@codemirror/theme-one-dark": "6.1.2",
|
"@codemirror/theme-one-dark": "6.1.2",
|
||||||
"@tato30/vue-pdf": "1.11.3",
|
"@tato30/vue-pdf": "1.11.3",
|
||||||
"@vueuse/core": "12.8.2",
|
"@vueuse/core": "13.0.0",
|
||||||
"@xterm/addon-fit": "0.10.0",
|
"@xterm/addon-fit": "0.10.0",
|
||||||
"@xterm/xterm": "5.5.0",
|
"@xterm/xterm": "5.5.0",
|
||||||
"ant-design-vue": "4.2.6",
|
"ant-design-vue": "4.2.6",
|
||||||
"antdv-pro-layout": "4.2.0",
|
"antdv-pro-layout": "4.2.0",
|
||||||
"antdv-pro-modal": "4.0.6",
|
"antdv-pro-modal": "4.0.8",
|
||||||
"codemirror": "6.0.1",
|
"codemirror": "6.0.1",
|
||||||
"crypto-js": "4.2.0",
|
"crypto-js": "4.2.0",
|
||||||
"dayjs": "1.11.13",
|
"dayjs": "1.11.13",
|
||||||
@@ -50,12 +50,12 @@
|
|||||||
"@types/js-cookie": "3.0.6",
|
"@types/js-cookie": "3.0.6",
|
||||||
"@types/node": "^18.0.0",
|
"@types/node": "^18.0.0",
|
||||||
"@types/nprogress": "0.2.3",
|
"@types/nprogress": "0.2.3",
|
||||||
"@vitejs/plugin-vue": "5.2.1",
|
"@vitejs/plugin-vue": "5.2.3",
|
||||||
"less": "4.2.2",
|
"less": "4.2.2",
|
||||||
"typescript": "5.6.3",
|
"typescript": "5.8.2",
|
||||||
"unplugin-vue-components": "0.28.0",
|
"unplugin-vue-components": "0.28.0",
|
||||||
"vite": "6.2.0",
|
"vite": "6.3.3",
|
||||||
"vite-plugin-compression": "~0.5.1",
|
"vite-plugin-compression": "0.5.1",
|
||||||
"vue-tsc": "2.2.0"
|
"vue-tsc": "2.2.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
imsi,msisdn,sess_rules,pcc_rules,hdr_enrich,rfsp,sar,qos_audio,qos_video
|
imsi,msisdn,sess_rules,pcc_rules,hdr_enrich,rfsp,sar,qos_audio,qos_video
|
||||||
001012082101039,1234,internet|ims_sig,internet|ims_sig,321321,255,321312,32131,32131
|
001012082101039,1234,internet|ims_sig,internet|ims_sig,321321,255,321312,32131,32131
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
001011100001157,1234567890ABCDEF1234567890ABCDEF,0,8000,11111111111111111111111111111111
|
001011100001157,1234567890ABCDEF1234567890ABCDEF,0,8000,11111111111111111111111111111111
|
||||||
001011100001158,1234567890ABCDEF1234567890ABCDEF,0,8000,11111111111111111111111111111111
|
001011100001158,1234567890ABCDEF1234567890ABCDEF,0,8000,11111111111111111111111111111111
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
001011100001157,62357000583,def_ambr,def_nssai,def_arfb,def_sar,0,3,def_snssai,1-000001&internet&ims,1,64,24,65,def_eps,1,010200000000,-
|
001011100001157,62357000583,def_ambr,def_nssai,def_arfb,def_sar,0,3,def_snssai,1-000001&internet&ims,1,64,24,65,def_eps,1,010200000000,-
|
||||||
001011100001158,62357000585,def_ambr,def_nssai,def_arfb,def_sar,0,3,def_snssai,1-000001&internet&ims,1,64,24,65,def_eps,1,010200000000,-
|
001011100001158,62357000585,def_ambr,def_nssai,def_arfb,def_sar,0,3,def_snssai,1-000001&internet&ims,1,64,24,65,def_eps,1,010200000000,-
|
||||||
|
|||||||
3
public/neDataImput/udm_voip_template.txt
Normal file
3
public/neDataImput/udm_voip_template.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#username,password
|
||||||
|
62357000580,123456
|
||||||
|
62357000581,123456
|
||||||
4
public/neDataImput/udm_volte_template.txt
Normal file
4
public/neDataImput/udm_volte_template.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#vlote=0 MSISDN and IMSI need to be filled in the same way.
|
||||||
|
#imsi,msisdn,vlote,vni
|
||||||
|
460996650000580,62357000580,1,ims.mnc000.mcc460.3gppnetwork.org
|
||||||
|
62357000581,62357000581,0,ims.mnc000.mcc460.3gppnetwork.org
|
||||||
@@ -1,15 +1,28 @@
|
|||||||
import { CACHE_SESSION_CRYPTO_API } from '@/constants/cache-keys-constants';
|
|
||||||
import { sessionGet } from '@/utils/cache-session-utils';
|
|
||||||
import { request } from '@/plugins/http-fetch';
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
// 登录方法
|
/**
|
||||||
|
* 登录方法
|
||||||
|
* @param data 数据
|
||||||
|
* @returns 结果
|
||||||
|
*/
|
||||||
export function login(data: Record<string, string>) {
|
export function login(data: Record<string, string>) {
|
||||||
return request({
|
return request({
|
||||||
url: '/login',
|
url: '/auth/login',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: data,
|
data: data,
|
||||||
whithToken: false,
|
whithToken: false,
|
||||||
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出方法
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function logout() {
|
||||||
|
return request({
|
||||||
|
url: '/auth/logout',
|
||||||
|
method: 'POST',
|
||||||
|
repeatSubmit: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,11 +33,24 @@ export function login(data: Record<string, string>) {
|
|||||||
*/
|
*/
|
||||||
export function register(data: Record<string, any>) {
|
export function register(data: Record<string, any>) {
|
||||||
return request({
|
return request({
|
||||||
url: '/register',
|
url: '/auth/register',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: data,
|
data: data,
|
||||||
whithToken: false,
|
whithToken: false,
|
||||||
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新登录令牌
|
||||||
|
* @param data 数据
|
||||||
|
* @returns 结果
|
||||||
|
*/
|
||||||
|
export function refreshToken(refreshToken: string) {
|
||||||
|
return request({
|
||||||
|
url: '/auth/refresh-token',
|
||||||
|
method: 'POST',
|
||||||
|
data: { refreshToken },
|
||||||
|
whithToken: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,16 +66,15 @@ export function getInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 退出方法
|
* 获取路由
|
||||||
* @returns object
|
* @returns object
|
||||||
*/
|
*/
|
||||||
export function logout() {
|
export const getRouter = () => {
|
||||||
return request({
|
return request({
|
||||||
url: '/logout',
|
url: '/router',
|
||||||
method: 'POST',
|
method: 'GET',
|
||||||
repeatSubmit: false,
|
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取验证码
|
* 获取验证码
|
||||||
@@ -116,6 +116,21 @@ export function clearAlarm(ids: number[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 告警信息导出
|
||||||
|
* @param params 查询列表条件
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function exportAlarm(params: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/alarm/export',
|
||||||
|
method: 'GET',
|
||||||
|
params: params,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 手工同步
|
* 手工同步
|
||||||
* @param data 鉴权对象
|
* @param data 鉴权对象
|
||||||
|
|||||||
@@ -4,19 +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 查询参数
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function listAct(query: Record<string, any>) {
|
|
||||||
return await request({
|
|
||||||
url: `/neData/alarm/list`,
|
|
||||||
method: 'GET',
|
|
||||||
params: query,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 确认告警信息
|
* 确认告警信息
|
||||||
* @param data 鉴权对象
|
* @param data 鉴权对象
|
||||||
|
|||||||
@@ -1,65 +1,5 @@
|
|||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { request } from '@/plugins/http-fetch';
|
import { request } from '@/plugins/http-fetch';
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
|
||||||
import { NE_TYPE_LIST } from '@/constants/ne-constants';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询公告列表
|
|
||||||
* @param query 查询参数
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function listMain() {
|
|
||||||
const result = await request({
|
|
||||||
url: '/api/rest/systemManagement/v1/elementType/all/objectType/systemState',
|
|
||||||
method: 'GET',
|
|
||||||
timeout: 60_000,
|
|
||||||
});
|
|
||||||
// console.log(result);
|
|
||||||
let realData = result.data.data;
|
|
||||||
const mergedData = realData.map((obj: any) => {
|
|
||||||
// console.log(obj);
|
|
||||||
const [key, value] = Object.entries(obj)[0];
|
|
||||||
const ipAddress = (value as any).ipAddress;
|
|
||||||
const systemState = (value as any).systemState;
|
|
||||||
const serialNum = (value as any).serialNum;
|
|
||||||
const version = (value as any).version;
|
|
||||||
|
|
||||||
const errCode = systemState && systemState['errorCode'];
|
|
||||||
var time = new Date();
|
|
||||||
// console.log(key, value);
|
|
||||||
let mergedObj;
|
|
||||||
if (errCode === undefined && systemState) {
|
|
||||||
mergedObj = {
|
|
||||||
...systemState,
|
|
||||||
refresh: parseDateToStr(time),
|
|
||||||
ipAddress: ipAddress,
|
|
||||||
name: key.split('/').join('_'),
|
|
||||||
status: 'Normal',
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
mergedObj = {
|
|
||||||
version,
|
|
||||||
refresh: parseDateToStr(time),
|
|
||||||
ipAddress,
|
|
||||||
serialNum,
|
|
||||||
name: key.split('/').join('_'),
|
|
||||||
expiryDate: '-',
|
|
||||||
status: 'Abnormal',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return mergedObj;
|
|
||||||
});
|
|
||||||
//通过sort进行冒泡排序
|
|
||||||
mergedData.sort((a: any, b: any) => {
|
|
||||||
const typeA = NE_TYPE_LIST.indexOf(a.name.split('_')[0]);
|
|
||||||
const typeB = NE_TYPE_LIST.indexOf(b.name.split('_')[0]);
|
|
||||||
if (typeA === -1) return 1; // 如果不在特定顺序中,排到后面
|
|
||||||
if (typeB === -1) return -1; // 如果不在特定顺序中,排到后面
|
|
||||||
return typeA - typeB;
|
|
||||||
});
|
|
||||||
|
|
||||||
return mergedData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取服务器时间
|
* 获取服务器时间
|
||||||
|
|||||||
@@ -53,43 +53,3 @@ export function delFile(query: Record<string, any>) {
|
|||||||
params: query,
|
params: query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新FTP信息
|
|
||||||
* @param data 数据
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function updateFTPInfo(data: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: `/lm/table/ftp`,
|
|
||||||
method: 'POST',
|
|
||||||
data: data,
|
|
||||||
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取FTP信息
|
|
||||||
* @param data 数据
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function getFTPInfo() {
|
|
||||||
return request({
|
|
||||||
url: `/lm/table/ftp`,
|
|
||||||
method: 'GET',
|
|
||||||
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送FTP文件
|
|
||||||
* @param data 数据
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function putFTPInfo(filePath: string, fileName: string) {
|
|
||||||
return request({
|
|
||||||
url: `/lm/table/ftp`,
|
|
||||||
method: 'PUT',
|
|
||||||
data: { filePath, fileName },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -83,43 +83,3 @@ export function importNeConfigBackup(data: Record<string, any>) {
|
|||||||
data: data,
|
data: data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新FTP信息
|
|
||||||
* @param data 数据
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function updateFTPInfo(data: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/config/backup/ftp`,
|
|
||||||
method: 'POST',
|
|
||||||
data: data,
|
|
||||||
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取FTP信息
|
|
||||||
* @param data 数据
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function getFTPInfo() {
|
|
||||||
return request({
|
|
||||||
url: `/ne/config/backup/ftp`,
|
|
||||||
method: 'GET',
|
|
||||||
crypto: sessionGet(CACHE_SESSION_CRYPTO_API) !== 'false',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送FTP文件
|
|
||||||
* @param data 数据
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export function putFTPInfo(path: string) {
|
|
||||||
return request({
|
|
||||||
url: `/ne/config/backup/ftp`,
|
|
||||||
method: 'PUT',
|
|
||||||
data: { path },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
38
src/api/neData/backup.ts
Normal file
38
src/api/neData/backup.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备份文件-获取FTP配置
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getBackupFTP() {
|
||||||
|
return request({
|
||||||
|
url: '/neData/backup/ftp',
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备份文件-文件FTP发送
|
||||||
|
* @param data 对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function pushBackupFTP(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/backup/ftp',
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备份文件-更新FTP配置
|
||||||
|
* @param data 对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function updateBackupFTP(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/backup/ftp',
|
||||||
|
method: 'PUT',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
134
src/api/neData/udm_voip.ts
Normal file
134
src/api/neData/udm_voip.ts
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVOIP用户重载数据
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function resetUDMVOIP(neId: string) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/voip/resetData/${neId}`,
|
||||||
|
method: 'PUT',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVOIP用户列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listUDMVOIP(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/udm/voip/list',
|
||||||
|
method: 'GET',
|
||||||
|
params: query,
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVOIP用户信息
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param username username
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getUDMVOIP(neId: string, username: string) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/voip/${neId}/${username}`,
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVOIP用户新增
|
||||||
|
* @param data VOIP对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addUDMVOIP(
|
||||||
|
neId: string,
|
||||||
|
data: { username: string; password: string }
|
||||||
|
) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/voip/${neId}`,
|
||||||
|
method: 'POST',
|
||||||
|
data: data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVOIP用户批量新增
|
||||||
|
* @param data VOIP对象
|
||||||
|
* @param num 数量
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function batchAddUDMVOIP(
|
||||||
|
neId: string,
|
||||||
|
data: { username: string; password: string },
|
||||||
|
num: number
|
||||||
|
) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/voip/${neId}/${num}`,
|
||||||
|
method: 'POST',
|
||||||
|
data: data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVOIP用户删除
|
||||||
|
* @param data VOIP对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delUDMVOIP(neId: string, username: string) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/voip/${neId}/${username}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVOIP用户批量删除
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param username username
|
||||||
|
* @param num 数量
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function batchDelUDMVOIP(neId: string, username: string, num: number) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/voip/${neId}/${username}/${num}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVOIP用户导出
|
||||||
|
* @param data 数据参数
|
||||||
|
* @returns bolb
|
||||||
|
*/
|
||||||
|
export function exportUDMVOIP(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/udm/voip/export',
|
||||||
|
method: 'GET',
|
||||||
|
params: data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVOIP用户导入
|
||||||
|
* @param data 表单数据对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function importUDMVOIP(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/voip/import`,
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
127
src/api/neData/udm_volte_ims.ts
Normal file
127
src/api/neData/udm_volte_ims.ts
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVolteIMS用户重载数据
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function resetUDMVolteIMS(neId: string) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/volte-ims/resetData/${neId}`,
|
||||||
|
method: 'PUT',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVolteIMS用户列表
|
||||||
|
* @param query 查询参数
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function listUDMVolteIMS(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/udm/volte-ims/list',
|
||||||
|
method: 'GET',
|
||||||
|
params: query,
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVolteIMS用户信息
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param imsi IMSI
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function getUDMVolteIMS(neId: string, imsi: string) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/volte-ims/${neId}/${imsi}`,
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVolteIMS用户新增
|
||||||
|
* @param data 签约对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function addUDMVolteIMS(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/volte-ims/${data.neId}`,
|
||||||
|
method: 'POST',
|
||||||
|
data: data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVolteIMS用户批量新增
|
||||||
|
* @param data 签约对象
|
||||||
|
* @param num 数量
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function batchAddUDMVolteIMS(data: Record<string, any>, num: number) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/volte-ims/${data.neId}/${num}`,
|
||||||
|
method: 'POST',
|
||||||
|
data: data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVolteIMS用户删除
|
||||||
|
* @param data 签约对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function delUDMVolteIMS(neId: string, imsi: string) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/volte-ims/${neId}/${imsi}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVolteIMS用户批量删除
|
||||||
|
* @param neId 网元ID
|
||||||
|
* @param imsi IMSI
|
||||||
|
* @param num 数量
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function batchDelUDMVolteIMS(neId: string, imsi: string, num: number) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/volte-ims/${neId}/${imsi}/${num}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVolteIMS用户导出
|
||||||
|
* @param data 数据参数
|
||||||
|
* @returns bolb
|
||||||
|
*/
|
||||||
|
export function exportUDMVolteIMS(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: '/neData/udm/volte-ims/export',
|
||||||
|
method: 'GET',
|
||||||
|
params: data,
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UDMVolteIMS用户导入
|
||||||
|
* @param data 表单数据对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function importUDMVolteIMS(data: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/neData/udm/volte-ims/import`,
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -220,6 +220,6 @@ export function taskRun(data: Record<string, any>) {
|
|||||||
export function taskStop(data: Record<string, any>) {
|
export function taskStop(data: Record<string, any>) {
|
||||||
return request({
|
return request({
|
||||||
url: `/api/rest/performanceManagement/v1/elementType/${data.neType.toLowerCase()}/objectType/measureTask?id=${data.id}`,
|
url: `/api/rest/performanceManagement/v1/elementType/${data.neType.toLowerCase()}/objectType/measureTask?id=${data.id}`,
|
||||||
method: 'PATCH',
|
method: 'PUT',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import { request } from '@/plugins/http-fetch';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取路由
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export const getRouters = () => {
|
|
||||||
return request({
|
|
||||||
url: '/router',
|
|
||||||
method: 'GET',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -136,3 +136,16 @@ export function changeUserStatus(
|
|||||||
data: { userId, statusFlag },
|
data: { userId, statusFlag },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户强制重置密码
|
||||||
|
* @param password 密码
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function updateUserPasswordForce(password: string) {
|
||||||
|
return request({
|
||||||
|
url: '/system/user/profile/password-force',
|
||||||
|
method: 'PUT',
|
||||||
|
data: { password },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -210,6 +210,50 @@ export function chunkUpload(data: FormData) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地文件列表
|
||||||
|
* @param path 文件路径
|
||||||
|
* @param search search prefix
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function listFile(query: Record<string, any>) {
|
||||||
|
return request({
|
||||||
|
url: `/file/list`,
|
||||||
|
method: 'GET',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地文件获取下载
|
||||||
|
* @param path 文件路径
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function getFile(path: string, fileName: string) {
|
||||||
|
return request({
|
||||||
|
url: `/file`,
|
||||||
|
method: 'GET',
|
||||||
|
params: { path, fileName },
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地文件删除
|
||||||
|
* @param path 文件路径
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function delFile(path: string, fileName: string) {
|
||||||
|
return request({
|
||||||
|
url: `/file`,
|
||||||
|
method: 'DELETE',
|
||||||
|
params: { path, fileName },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转存上传文件到静态资源
|
* 转存上传文件到静态资源
|
||||||
* @returns object
|
* @returns object
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
import { request } from '@/plugins/http-fetch';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 跟踪任务数据列表
|
|
||||||
* @param query 查询参数
|
|
||||||
* @returns object
|
|
||||||
*/
|
|
||||||
export async function listTraceData(query: Record<string, any>) {
|
|
||||||
return request({
|
|
||||||
url: '/trace/task/list',
|
|
||||||
method: 'GET',
|
|
||||||
params: query,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 信令数据解析HTML
|
|
||||||
* @param id 任务ID
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function getTraceRawInfo(id: Record<string, string>) {
|
|
||||||
return request({
|
|
||||||
url: `/api/rest/traceManagement/v1/decMessage/${id}`,
|
|
||||||
method: 'GET',
|
|
||||||
responseType: 'text',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -62,3 +62,18 @@ export function packetKeep(taskNo: string, duration: number = 120) {
|
|||||||
data: { taskNo, duration },
|
data: { taskNo, duration },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 信令跟踪文件
|
||||||
|
* @param taskNo 对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function packetPCAPFile(taskNo: string) {
|
||||||
|
return request({
|
||||||
|
url: '/trace/packet/filePull',
|
||||||
|
method: 'GET',
|
||||||
|
params: { taskNo },
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 180_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -76,29 +76,31 @@ export function filePullTask(traceId: string) {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: { traceId },
|
params: { traceId },
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
timeout: 60_000,
|
timeout: 180_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取网元跟踪接口列表
|
* 跟踪任务数据列表
|
||||||
|
* @param query 查询参数
|
||||||
* @returns object
|
* @returns object
|
||||||
*/
|
*/
|
||||||
export async function getNeTraceInterfaceAll() {
|
export async function listTraceData(query: Record<string, any>) {
|
||||||
// 发起请求
|
return request({
|
||||||
const result = await request({
|
url: '/trace/data/list',
|
||||||
url: `/api/rest/databaseManagement/v1/elementType/omc_db/objectType/ne_info`,
|
method: 'GET',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询跟踪任务数据信息
|
||||||
|
* @param id ID
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export async function getTraceData(id: string | number) {
|
||||||
|
return request({
|
||||||
|
url: `/trace/data/${id}`,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
|
||||||
SQL: `SELECT ne_type,interface FROM trace_info GROUP BY ne_type,interface`,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
// 解析数据
|
|
||||||
if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) {
|
|
||||||
let data = result.data.data[0];
|
|
||||||
return Object.assign(result, {
|
|
||||||
data: parseObjLineToHump(data['trace_info']),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|||||||
218
src/components/ForcePasswdChange/index.vue
Normal file
218
src/components/ForcePasswdChange/index.vue
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||||
|
import { regExpPasswd, regExpUserName } from '@/utils/regular-utils';
|
||||||
|
import { Form, message, Modal } from 'ant-design-vue';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import useUserStore from '@/store/modules/user';
|
||||||
|
import { updateUserPasswordForce } from '@/api/system/user';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { getConfigKey } from '@/api/system/config';
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**重置密码框是否显示 */
|
||||||
|
open: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**表单数据 */
|
||||||
|
from: Record<string, any>;
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
/**密码策略 */
|
||||||
|
passwordPolicy: Record<string, any>;
|
||||||
|
/**密码有效期 */
|
||||||
|
passwdExpireEnable: boolean;
|
||||||
|
passwdExpire: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
open: userStore.forcePasswdChange,
|
||||||
|
title: t('components.ForcePasswdChange.title'),
|
||||||
|
from: {
|
||||||
|
userId: userStore.userId,
|
||||||
|
userName: userStore.userName,
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
confirmLoading: false,
|
||||||
|
passwordPolicy: { minLength: 8, specialChars: 2, uppercase: 1, lowercase: 1 },
|
||||||
|
passwdExpireEnable: false,
|
||||||
|
passwdExpire: { expHours: 2, alertHours: 1 },
|
||||||
|
});
|
||||||
|
|
||||||
|
/**对话框内表单属性和校验规则 */
|
||||||
|
const modalStateFrom = Form.useForm(
|
||||||
|
modalState.from,
|
||||||
|
reactive({
|
||||||
|
userName: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
pattern: regExpUserName,
|
||||||
|
message: t('views.system.user.userNameTip'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
pattern: regExpPasswd,
|
||||||
|
message: t('views.system.user.passwdTip'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**对话框提交确认 */
|
||||||
|
function fnModalOk() {
|
||||||
|
const { password } = modalState.from;
|
||||||
|
if (!password) {
|
||||||
|
message.error({
|
||||||
|
content: t('views.system.user.passwdTip'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateUserPasswordForce(password).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
userStore.fnLogOut();
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
Modal.success({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.account.settings.submitOkTip', {
|
||||||
|
num: modalState.from.userName,
|
||||||
|
}),
|
||||||
|
okText: t('views.account.settings.submitOk'),
|
||||||
|
onOk() {
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**组件实例挂载之后调用 */
|
||||||
|
onMounted(() => {
|
||||||
|
Promise.all([
|
||||||
|
getConfigKey('sys.user.passwordPolicy'),
|
||||||
|
getConfigKey('sys.user.passwdExpire'),
|
||||||
|
]).then(resArr => {
|
||||||
|
if (resArr[0].code === RESULT_CODE_SUCCESS) {
|
||||||
|
try {
|
||||||
|
modalState.passwordPolicy = JSON.parse(resArr[0].data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('passwordPolicy', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resArr[1].code === RESULT_CODE_SUCCESS) {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(resArr[1].data);
|
||||||
|
if (data.expHours % 24 === 0) {
|
||||||
|
data.expHours = data.expHours / 24;
|
||||||
|
} else {
|
||||||
|
data.expHours = (data.expHours / 24).toFixed(2);
|
||||||
|
}
|
||||||
|
if (data.alertHours % 24 === 0) {
|
||||||
|
data.alertHours = data.alertHours / 24;
|
||||||
|
} else {
|
||||||
|
data.alertHours = (data.alertHours / 24).toFixed(2);
|
||||||
|
}
|
||||||
|
modalState.passwdExpire = data;
|
||||||
|
modalState.passwdExpireEnable = data.expHours > 0;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('passwdExpire', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**组件实例被卸载之后调用 */
|
||||||
|
onUnmounted(() => {});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:open="modalState.open"
|
||||||
|
get-container="#app"
|
||||||
|
:footer="null"
|
||||||
|
:zIndex="1008"
|
||||||
|
:closable="false"
|
||||||
|
:keyboard="false"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
:mask-closable="false"
|
||||||
|
:title="modalState.title"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
name="modalStateFromByResetPwd"
|
||||||
|
layout="horizontal"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.system.user.account')"
|
||||||
|
name="userName"
|
||||||
|
v-bind="modalStateFrom.validateInfos.userName"
|
||||||
|
>
|
||||||
|
<a-input :value="modalState.from.userName" disabled :maxlength="30">
|
||||||
|
<template #prefix>
|
||||||
|
<UserOutlined />
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.system.user.loginPwd')"
|
||||||
|
name="password"
|
||||||
|
v-bind="modalStateFrom.validateInfos.password"
|
||||||
|
>
|
||||||
|
<a-input-password
|
||||||
|
v-model:value="modalState.from.password"
|
||||||
|
:disabled="modalState.confirmLoading"
|
||||||
|
:maxlength="26"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<LockOutlined />
|
||||||
|
</template>
|
||||||
|
</a-input-password>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="ok" :wrapper-col="{ offset: 6 }">
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
@click="fnModalOk()"
|
||||||
|
:disabled="modalState.confirmLoading"
|
||||||
|
>
|
||||||
|
{{ t('common.ok') }}
|
||||||
|
</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 提示信息 -->
|
||||||
|
<a-form-item name="info" :label="t('components.ForcePasswdChange.desc')">
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
{{ t('components.ForcePasswdChange.passwordPolicy') }}<br />
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
'components.ForcePasswdChange.passwordPolicyMsg',
|
||||||
|
modalState.passwordPolicy
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
<p v-if="modalState.passwdExpireEnable">
|
||||||
|
{{ t('components.ForcePasswdChange.passwdExpire') }}<br />
|
||||||
|
{{
|
||||||
|
t('components.ForcePasswdChange.passwdExpireMsg', {
|
||||||
|
expDay: modalState.passwdExpire.expHours,
|
||||||
|
alertDay: modalState.passwdExpire.alertHours,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
/**响应-code加密数据 */
|
/**响应-code加密数据 */
|
||||||
export const RESULT_CODE_ENCRYPT = 2;
|
export const RESULT_CODE_ENCRYPT = 200999;
|
||||||
|
|
||||||
/**响应-msg加密数据 */
|
/**响应-msg加密数据 */
|
||||||
export const RESULT_MSG_ENCRYPT: Record<string, string> = {
|
export const RESULT_MSG_ENCRYPT: Record<string, string> = {
|
||||||
zh_CN: '加密!',
|
zh_CN: '加密!',
|
||||||
en_US: 'encrypt!',
|
en_US: 'Encrypt!',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**响应-code正常成功 */
|
/**响应-code正常成功 */
|
||||||
export const RESULT_CODE_SUCCESS = 1;
|
export const RESULT_CODE_SUCCESS = 200001;
|
||||||
|
|
||||||
/**响应-msg正常成功 */
|
/**响应-msg正常成功 */
|
||||||
export const RESULT_MSG_SUCCESS: Record<string, string> = {
|
export const RESULT_MSG_SUCCESS: Record<string, string> = {
|
||||||
@@ -17,7 +17,16 @@ export const RESULT_MSG_SUCCESS: Record<string, string> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**响应-code错误失败 */
|
/**响应-code错误失败 */
|
||||||
export const RESULT_CODE_ERROR = 0;
|
export const RESULT_CODE_ERROR = 400001;
|
||||||
|
|
||||||
|
/**响应-code错误异常 */
|
||||||
|
export const RESULT_CODE_EXCEPTION = 500001;
|
||||||
|
|
||||||
|
/**响应-服务器连接出错 */
|
||||||
|
export const RESULT_MSG_SERVER_ERROR: Record<string, string> = {
|
||||||
|
zh_CN: '服务器连接出错!',
|
||||||
|
en_US: 'Server Connection Error!',
|
||||||
|
};
|
||||||
|
|
||||||
/**响应-msg错误失败 */
|
/**响应-msg错误失败 */
|
||||||
export const RESULT_MSG_ERROR: Record<string, string> = {
|
export const RESULT_MSG_ERROR: Record<string, string> = {
|
||||||
@@ -37,18 +46,6 @@ export const RESULT_MSG_NOT_TYPE: Record<string, string> = {
|
|||||||
en_US: 'Unknown Response Data Type!',
|
en_US: 'Unknown Response Data Type!',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**响应-服务器连接出错 */
|
|
||||||
export const RESULT_MSG_SERVER_ERROR: Record<string, string> = {
|
|
||||||
zh_CN: '服务器连接出错!',
|
|
||||||
en_US: 'Server Connection Error!',
|
|
||||||
};
|
|
||||||
|
|
||||||
/**响应-请求地址未找到 */
|
|
||||||
export const RESULT_MSG_URL_NOTFOUND: Record<string, string> = {
|
|
||||||
zh_CN: '请求地址未找到!',
|
|
||||||
en_US: 'Request Address Not Found!',
|
|
||||||
};
|
|
||||||
|
|
||||||
/**响应-数据正在处理,请勿重复提交 */
|
/**响应-数据正在处理,请勿重复提交 */
|
||||||
export const RESULT_MSG_URL_RESUBMIT: Record<string, string> = {
|
export const RESULT_MSG_URL_RESUBMIT: Record<string, string> = {
|
||||||
zh_CN: '数据正在处理,请勿重复提交!',
|
zh_CN: '数据正在处理,请勿重复提交!',
|
||||||
|
|||||||
@@ -2,10 +2,13 @@
|
|||||||
export const TOKEN_RESPONSE_FIELD = 'accessToken';
|
export const TOKEN_RESPONSE_FIELD = 'accessToken';
|
||||||
|
|
||||||
/**令牌-请求头标识前缀 */
|
/**令牌-请求头标识前缀 */
|
||||||
export const TOKEN_KEY_PREFIX = 'Bearer ';
|
export const TOKEN_KEY_PREFIX = 'Bearer';
|
||||||
|
|
||||||
/**令牌-请求头标识 */
|
/**令牌-请求头标识 */
|
||||||
export const TOKEN_KEY = 'Authorization';
|
export const TOKEN_KEY = 'Authorization';
|
||||||
|
|
||||||
/**令牌-存放Cookie标识 */
|
/**令牌-访问令牌存放Cookie标识 */
|
||||||
export const TOKEN_COOKIE = 'AuthOMC';
|
export const TOKEN_ACCESS_COOKIE = 'omc_access';
|
||||||
|
|
||||||
|
/**令牌-刷新令牌存放Cookie标识 */
|
||||||
|
export const TOKEN_REFRESH_COOKIE = 'omc_refresh';
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ export default {
|
|||||||
errorFields: 'Please fill in the required information in {num} correctly!',
|
errorFields: 'Please fill in the required information in {num} correctly!',
|
||||||
tablePaginationTotal: 'Total {total} items',
|
tablePaginationTotal: 'Total {total} items',
|
||||||
noData: "No Data",
|
noData: "No Data",
|
||||||
zebra:'Tabular zebra pattern',
|
|
||||||
ok: 'Ok',
|
ok: 'Ok',
|
||||||
cancel: 'Cancel',
|
cancel: 'Cancel',
|
||||||
close: 'Close',
|
close: 'Close',
|
||||||
@@ -131,7 +130,7 @@ export default {
|
|||||||
},
|
},
|
||||||
LockScreen: {
|
LockScreen: {
|
||||||
inputPlacePwd:'Lock Screen Password',
|
inputPlacePwd:'Lock Screen Password',
|
||||||
validSucc:'Validation Passed',
|
enter:'Enter',
|
||||||
validError:'Validation Failure',
|
validError:'Validation Failure',
|
||||||
backLogin:'Logout to Relogin',
|
backLogin:'Logout to Relogin',
|
||||||
backReload:'Restarting now, please wait...',
|
backReload:'Restarting now, please wait...',
|
||||||
@@ -139,6 +138,14 @@ export default {
|
|||||||
systemReset:'Resetting now, please wait...',
|
systemReset:'Resetting now, please wait...',
|
||||||
systemReset2:'Data information is being reset.',
|
systemReset2:'Data information is being reset.',
|
||||||
},
|
},
|
||||||
|
ForcePasswdChange: {
|
||||||
|
title: 'Password Change',
|
||||||
|
desc: 'Instruction',
|
||||||
|
passwordPolicy: 'Password policy strength',
|
||||||
|
passwordPolicyMsg: 'At least {minLength} bits, containing at least {specialChars} special characters and at least {uppercase} uppercase and at least {lowercase} lowercase letters.',
|
||||||
|
passwdExpire: 'Password expiration date',
|
||||||
|
passwdExpireMsg: 'Valid for {expDay} days, please change your password {alertDay} days before expiration.',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// 静态路由
|
// 静态路由
|
||||||
@@ -163,7 +170,7 @@ export default {
|
|||||||
userNameReg: 'The account cannot start with a number and can contain uppercase and lowercase letters, numbers, and no less than 5 digits.',
|
userNameReg: 'The account cannot start with a number and can contain uppercase and lowercase letters, numbers, and no less than 5 digits.',
|
||||||
userNamePlease: 'Please enter the correct login account',
|
userNamePlease: 'Please enter the correct login account',
|
||||||
userNameHit: 'Login account',
|
userNameHit: 'Login account',
|
||||||
passwordReg: 'The password should contain at least uppercase and lowercase letters, numbers, special symbols, and no less than 6 digits.',
|
passwordReg: 'Please enter the correct password format',
|
||||||
passwordPlease: 'Please enter the correct login password',
|
passwordPlease: 'Please enter the correct login password',
|
||||||
passwordHit: 'Login password',
|
passwordHit: 'Login password',
|
||||||
passwordConfirmHit: 'Confirm login password',
|
passwordConfirmHit: 'Confirm login password',
|
||||||
@@ -295,7 +302,7 @@ export default {
|
|||||||
oldPasswordTip: "The old password must not be empty and must be at least 6 digits long",
|
oldPasswordTip: "The old password must not be empty and must be at least 6 digits long",
|
||||||
oldPasswordPleace: "Please enter the old password",
|
oldPasswordPleace: "Please enter the old password",
|
||||||
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: "Please enter the correct password format",
|
||||||
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",
|
||||||
@@ -506,7 +513,7 @@ export default {
|
|||||||
delTip: 'Confirm deletion of network element information data items?',
|
delTip: 'Confirm deletion of network element information data items?',
|
||||||
oam: {
|
oam: {
|
||||||
title: 'OAM Configuration',
|
title: 'OAM Configuration',
|
||||||
sync: 'Sync to NE',
|
restart: 'Restart NE',
|
||||||
oamEnable: 'Service',
|
oamEnable: 'Service',
|
||||||
oamPort: 'Port',
|
oamPort: 'Port',
|
||||||
snmpEnable: 'Service',
|
snmpEnable: 'Service',
|
||||||
@@ -659,6 +666,19 @@ export default {
|
|||||||
name: "Name",
|
name: "Name",
|
||||||
downTip: 'Confirmed to download the backup file [{txt}]?',
|
downTip: 'Confirmed to download the backup file [{txt}]?',
|
||||||
title: "Modify Backup {txt}",
|
title: "Modify Backup {txt}",
|
||||||
|
backupModal: {
|
||||||
|
pushFileOper: "Send Current File To Remote Backup",
|
||||||
|
title: "Setting Remote Backup Service",
|
||||||
|
enable: "Enable",
|
||||||
|
toIp: "Service IP",
|
||||||
|
toIpPleace: "Please input the remote backup server IP address",
|
||||||
|
toPort: "Service Port",
|
||||||
|
username: "UserName",
|
||||||
|
usernamePleace: 'Please enter the service login username',
|
||||||
|
password: "Password",
|
||||||
|
dir: "Save Dir",
|
||||||
|
dirPleace: 'Please enter the service address target file directory',
|
||||||
|
}
|
||||||
},
|
},
|
||||||
neQuickSetup: {
|
neQuickSetup: {
|
||||||
reloadPara5G: 'Reload',
|
reloadPara5G: 'Reload',
|
||||||
@@ -711,8 +731,45 @@ export default {
|
|||||||
},
|
},
|
||||||
neData: {
|
neData: {
|
||||||
common: {
|
common: {
|
||||||
|
startIMSI: 'Starting IMSI',
|
||||||
|
imsi: 'IMSI',
|
||||||
|
imsiTip: 'IMSI=MCC+MNC+MSIN',
|
||||||
|
imsiTip1: 'MCC=Mobile Country Code, consisting of three digits.',
|
||||||
|
imsiTip2: 'MNC = Mobile Network Number, consisting of two digits',
|
||||||
|
imsiTip3: 'MSIN = Mobile Subscriber Identification Number, consisting of 10 equal digits.',
|
||||||
|
imsiPlease: "Please enter IMSI correctly",
|
||||||
|
msisdn: 'Mobile Customer Identification Number',
|
||||||
|
msisdnPlease: "Please enter the Mobile Customer Identification Number correctly",
|
||||||
|
loadDataConfirm: 'Confirmed to reload data?',
|
||||||
|
loadData: 'Load Data',
|
||||||
|
loadDataTip: 'Successfully fetched loaded data: {num} items, the system is internally updating the data, it will take about {timer} seconds, please wait!!!!!.',
|
||||||
|
batchOper: 'Batch Operation',
|
||||||
|
batchAddText: 'Batch Addition',
|
||||||
|
batchDelText: 'Batch Deletion',
|
||||||
|
batchUpdateText: 'Batch Update',
|
||||||
|
batchNum: 'Number of releases',
|
||||||
|
checkDel:'Check Delete',
|
||||||
importTemplate: 'Download Template',
|
importTemplate: 'Download Template',
|
||||||
},
|
},
|
||||||
|
udmVOIP: {
|
||||||
|
startUsername: 'Starting username',
|
||||||
|
username: 'username',
|
||||||
|
usernamePlease: "Please enter your username correctly",
|
||||||
|
password: "password",
|
||||||
|
passwordPlease: "Please enter your password correctly",
|
||||||
|
addTitle: 'Add VOIP subscriber',
|
||||||
|
delTip: 'Confirm that you want to delete the information of VOIP user as [{num}]?',
|
||||||
|
exportTip: "Confirm exporting xlsx table files based on search criteria?",
|
||||||
|
},
|
||||||
|
udmVolteIMS: {
|
||||||
|
startMSISDN: 'Starting MSISDN',
|
||||||
|
voipTip: 'When VoIP is selected MSISDN will be equal to IMSI',
|
||||||
|
addTitle: 'Addition of new IMS subscribers',
|
||||||
|
vniTip: 'Example: ims.mnc000.mcc000.3gppnetwork.org',
|
||||||
|
vniPlease: 'Please enter VNI correctly',
|
||||||
|
delTip: 'Are you sure you want to delete the information of IMS signing as [{num}]?',
|
||||||
|
exportTip: "Confirm exporting xlsx table files based on search criteria?",
|
||||||
|
},
|
||||||
baseStation: {
|
baseStation: {
|
||||||
list: "List",
|
list: "List",
|
||||||
topology: "Topology",
|
topology: "Topology",
|
||||||
@@ -735,6 +792,12 @@ export default {
|
|||||||
exportTip: "Confirm exporting xlsx table files based on search criteria?",
|
exportTip: "Confirm exporting xlsx table files based on search criteria?",
|
||||||
importDataEmpty: "Imported data is empty",
|
importDataEmpty: "Imported data is empty",
|
||||||
},
|
},
|
||||||
|
backupData: {
|
||||||
|
auth: "UDM Authentication",
|
||||||
|
sub: "UDM Subscribers",
|
||||||
|
voip: "VoIP Authentication",
|
||||||
|
volte: "IMS Subscribers",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
neUser: {
|
neUser: {
|
||||||
auth: {
|
auth: {
|
||||||
@@ -1004,25 +1067,6 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
traceManage: {
|
traceManage: {
|
||||||
analysis: {
|
|
||||||
imsi: 'IMSI',
|
|
||||||
imsiPlease: 'Please enter IMSI',
|
|
||||||
msisdn: 'MSISDN',
|
|
||||||
msisdnPlease: 'Please enter MSISDN',
|
|
||||||
trackTaskId: 'Task ID',
|
|
||||||
srcIp: 'Source IP Address',
|
|
||||||
dstIp: 'Destination IP Address',
|
|
||||||
signalType: 'Signaling Type',
|
|
||||||
msgDirect: 'Message Direction',
|
|
||||||
msgType: 'Message Type',
|
|
||||||
rowTime: 'Record Time',
|
|
||||||
signalData: 'Signaling Data',
|
|
||||||
signalDetail: 'Signaling Details',
|
|
||||||
noData: 'No information content',
|
|
||||||
taskTitle: 'Task {num}',
|
|
||||||
taskDownText: 'Download HTML',
|
|
||||||
taskDownTip: 'Confirm downloading the signaling details HTML file?',
|
|
||||||
},
|
|
||||||
pcap: {
|
pcap: {
|
||||||
capArgPlease: 'Please enter tcpdump -i any support parameter',
|
capArgPlease: 'Please enter tcpdump -i any support parameter',
|
||||||
cmd: 'Command',
|
cmd: 'Command',
|
||||||
@@ -1073,30 +1117,35 @@ export default {
|
|||||||
imsiTip: 'Mobile communication IMSI number',
|
imsiTip: 'Mobile communication IMSI number',
|
||||||
srcIp: 'Source IP Address',
|
srcIp: 'Source IP Address',
|
||||||
srcIpPlease: 'Please enter the IP address',
|
srcIpPlease: 'Please enter the IP address',
|
||||||
srcIpTip: 'Current sender IPv4 address',
|
srcIpTip: 'sending IPv4 address',
|
||||||
dstIp: 'Destination IP Address',
|
dstIp: 'Destination IP Address',
|
||||||
dstIpPlease: 'Please enter the IP address',
|
dstIpPlease: 'Please enter the IP address',
|
||||||
dstIpTip: 'IPv4 address of the receiving end of the other party',
|
dstIpTip: 'receiving end IPv4 address',
|
||||||
interfaces: 'Signaling Interface',
|
interfaces: 'Signaling Interface',
|
||||||
interfacesPlease: 'Please enter the signaling interface',
|
interfacesPlease: 'Please enter the signaling interface',
|
||||||
signalPort: 'Signal Port',
|
rangePicker: 'Task Time',
|
||||||
signalPortPlease: 'Please enter the signaling port',
|
|
||||||
signalPortTip: 'Port of the side corresponding to the destination IP address or source IP address',
|
|
||||||
rangePicker: 'Start/End Time',
|
|
||||||
rangePickerPlease: 'Please select the start and end time of the task',
|
rangePickerPlease: 'Please select the start and end time of the task',
|
||||||
remark: 'Remark',
|
remark: 'Remark',
|
||||||
remarkPlease: 'Task description can be entered',
|
remarkPlease: 'Task description can be entered',
|
||||||
addTask: 'Add Task',
|
addTask: 'Add Task',
|
||||||
editTask: 'Modify Task',
|
|
||||||
viewTask: 'View Task',
|
viewTask: 'View Task',
|
||||||
errorTaskInfo: 'Failed to obtain task information',
|
errorTaskInfo: 'Failed to obtain task information',
|
||||||
delTaskTip: 'Are you sure to delete the data item with record ID {id} ?',
|
delTaskTip: 'Are you sure to delete the data item with record ID {id} ?',
|
||||||
stopTask: 'Successful cessation of tasks {id}',
|
stopTask: 'Successful cessation of tasks {id}',
|
||||||
stopTaskTip: 'Confirm stopping the task with record ID {id} ?',
|
stopTaskTip: 'Confirm stopping the task with record ID {id} ?',
|
||||||
pcapView: "Tracking Data Analysis",
|
pcapView: "Track Data Analysis",
|
||||||
traceFile: "Tracking File",
|
traceFile: "Track File",
|
||||||
errMsg: "Error Message",
|
errMsg: "Error Message",
|
||||||
imsiORmsisdn: "imsi or msisdn is null, cannot start task",
|
imsiORmsisdn: "imsi or msisdn is null, cannot start task",
|
||||||
|
dataView: "Track Data",
|
||||||
|
protocolOrInterface: "Protocol/Interface",
|
||||||
|
msgNe: 'Network Element',
|
||||||
|
msgEvent: 'Event',
|
||||||
|
msgType: 'Type',
|
||||||
|
msgDirect: 'Direction',
|
||||||
|
msgLen: 'Length',
|
||||||
|
rowTime: 'Time',
|
||||||
|
taskInfo: 'Task information',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
faultManage: {
|
faultManage: {
|
||||||
@@ -1141,13 +1190,11 @@ export default {
|
|||||||
delSuss:'Clear successfully',
|
delSuss:'Clear successfully',
|
||||||
delSure:'Whether to clear this alarm',
|
delSure:'Whether to clear this alarm',
|
||||||
showSet:'Show filter settings',
|
showSet:'Show filter settings',
|
||||||
|
exportTip: "Confirm exporting xlsx table files based on search criteria?",
|
||||||
exportSure:'Confirm whether to export all active alarm information',
|
exportSure:'Confirm whether to export all active alarm information',
|
||||||
viewIdInfo:'View {alarmId} record information',
|
viewIdInfo:'View {alarmId} record information',
|
||||||
closeModal:'Close',
|
closeModal:'Close',
|
||||||
},
|
},
|
||||||
historyAlarm:{
|
|
||||||
exportSure:'Confirm whether to export all historical alarm information',
|
|
||||||
},
|
|
||||||
faultSetting:{
|
faultSetting:{
|
||||||
interfaceType:'Type',
|
interfaceType:'Type',
|
||||||
email:'Email',
|
email:'Email',
|
||||||
@@ -1216,12 +1263,17 @@ export default {
|
|||||||
tailLines: 'End Lines',
|
tailLines: 'End Lines',
|
||||||
},
|
},
|
||||||
exportFile:{
|
exportFile:{
|
||||||
fileName:'File Source',
|
fileSource:'File Source',
|
||||||
|
fileSourcePlease:'Please select the source of the document',
|
||||||
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",
|
||||||
deleteTip: "Confirm the delete file name is [{fileName}] File?",
|
deleteTip: "Confirm the delete file name is [{fileName}] File?",
|
||||||
deleteTipErr: "Failed to delete file",
|
deleteTipErr: "Failed to delete file",
|
||||||
selectTip:"Please select File Name",
|
operateLog:'Operation Log',
|
||||||
|
cdrIMS:'Voice CDR',
|
||||||
|
cdrSMF:'Data CDR',
|
||||||
|
cdrSMSC:'SMS CDR',
|
||||||
|
cdrSGWC:'Roaming Data CDR',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
monitor: {
|
monitor: {
|
||||||
@@ -1599,7 +1651,7 @@ export default {
|
|||||||
loginTime: 'Login Time',
|
loginTime: 'Login Time',
|
||||||
status: 'Status',
|
status: 'Status',
|
||||||
userNameTip:'The account number can only contain strings of uppercase letters, lowercase letters and numbers with a minimum length of 6 digits',
|
userNameTip:'The account number can only contain strings of uppercase letters, lowercase letters and numbers with a minimum length of 6 digits',
|
||||||
passwdTip:'The password should contain at least uppercase and lowercase letters, numbers, special symbols, and no less than 6 digits',
|
passwdTip:'Please enter the correct password format',
|
||||||
nickNameTip:'Nicknames no less than 2 digits',
|
nickNameTip:'Nicknames no less than 2 digits',
|
||||||
emailTip:'Please enter the correct email address',
|
emailTip:'Please enter the correct email address',
|
||||||
phoneTip:'Please enter the correct phone number',
|
phoneTip:'Please enter the correct phone number',
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ export default {
|
|||||||
errorFields: '请正确填写 {num} 处必填信息!',
|
errorFields: '请正确填写 {num} 处必填信息!',
|
||||||
tablePaginationTotal: '总共 {total} 条',
|
tablePaginationTotal: '总共 {total} 条',
|
||||||
noData: "暂无数据",
|
noData: "暂无数据",
|
||||||
zebra:'表格斑马纹',
|
|
||||||
ok: '确定',
|
ok: '确定',
|
||||||
cancel: '取消',
|
cancel: '取消',
|
||||||
close: '关闭',
|
close: '关闭',
|
||||||
@@ -131,7 +130,7 @@ export default {
|
|||||||
},
|
},
|
||||||
LockScreen: {
|
LockScreen: {
|
||||||
inputPlacePwd:'请输入锁屏密码',
|
inputPlacePwd:'请输入锁屏密码',
|
||||||
validSucc:'校验通过',
|
enter:'进入',
|
||||||
validError:'校验失败',
|
validError:'校验失败',
|
||||||
backLogin:'退出并重新登录',
|
backLogin:'退出并重新登录',
|
||||||
backReload:'正在重启,请稍等...',
|
backReload:'正在重启,请稍等...',
|
||||||
@@ -139,6 +138,14 @@ export default {
|
|||||||
systemReset:'正在重置,请稍等...',
|
systemReset:'正在重置,请稍等...',
|
||||||
systemReset2:'数据信息正在重置',
|
systemReset2:'数据信息正在重置',
|
||||||
},
|
},
|
||||||
|
ForcePasswdChange: {
|
||||||
|
title: '密码修改',
|
||||||
|
desc: '说明',
|
||||||
|
passwordPolicy: '密码策略强度',
|
||||||
|
passwordPolicyMsg: '至少{minLength}位,至少包含{specialChars}个特殊字符和至少{uppercase}大写字母和至少{lowercase}小写字母',
|
||||||
|
passwdExpire: '密码有效期',
|
||||||
|
passwdExpireMsg: '有效期为{expDay}天,请在过期前{alertDay}天进行密码修改',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// 静态路由
|
// 静态路由
|
||||||
@@ -163,7 +170,7 @@ export default {
|
|||||||
userNameReg: '账号不能以数字开头,可包含大写小写字母,数字,且不少于5位',
|
userNameReg: '账号不能以数字开头,可包含大写小写字母,数字,且不少于5位',
|
||||||
userNamePlease: '请输入正确登录账号',
|
userNamePlease: '请输入正确登录账号',
|
||||||
userNameHit: '登录账号',
|
userNameHit: '登录账号',
|
||||||
passwordReg: '密码至少包含大小写字母、数字、特殊符号,且不少于6位',
|
passwordReg: '请输入正确的密码格式',
|
||||||
passwordPlease: '请输入正确登录密码',
|
passwordPlease: '请输入正确登录密码',
|
||||||
passwordHit: '登录密码',
|
passwordHit: '登录密码',
|
||||||
passwordConfirmHit: '确认登录密码',
|
passwordConfirmHit: '确认登录密码',
|
||||||
@@ -295,7 +302,7 @@ export default {
|
|||||||
oldPasswordTip: "旧密码不能为空,且不少于6位",
|
oldPasswordTip: "旧密码不能为空,且不少于6位",
|
||||||
oldPasswordPleace: "请输入旧密码",
|
oldPasswordPleace: "请输入旧密码",
|
||||||
newPassword: "新密码",
|
newPassword: "新密码",
|
||||||
newPasswordTip: "密码至少包含大小写字母、数字、特殊符号,且不少于6位",
|
newPasswordTip: "请输入正确的密码格式",
|
||||||
newPassworddPleace: "请输入新密码",
|
newPassworddPleace: "请输入新密码",
|
||||||
confirmPassword: "确认新密码",
|
confirmPassword: "确认新密码",
|
||||||
confirmPasswordPleace: "请确认新密码",
|
confirmPasswordPleace: "请确认新密码",
|
||||||
@@ -506,7 +513,7 @@ export default {
|
|||||||
delTip: '确认删除网元信息数据项吗?',
|
delTip: '确认删除网元信息数据项吗?',
|
||||||
oam: {
|
oam: {
|
||||||
title: 'OAM配置',
|
title: 'OAM配置',
|
||||||
sync: '同步到网元',
|
restart: '下发后重启网元',
|
||||||
oamEnable: '服务',
|
oamEnable: '服务',
|
||||||
oamPort: '端口',
|
oamPort: '端口',
|
||||||
snmpEnable: '服务',
|
snmpEnable: '服务',
|
||||||
@@ -659,6 +666,19 @@ export default {
|
|||||||
name: "名称",
|
name: "名称",
|
||||||
downTip: '确认要下载备份文件【{txt}】吗?',
|
downTip: '确认要下载备份文件【{txt}】吗?',
|
||||||
title: "修改备份信息 {txt}",
|
title: "修改备份信息 {txt}",
|
||||||
|
backupModal: {
|
||||||
|
pushFileOper: "将当前文件发送到远程备份",
|
||||||
|
title: "设置远程备份服务",
|
||||||
|
enable: "启用",
|
||||||
|
toIp: "服务IP",
|
||||||
|
toIpPleace: "请输入远程备份服务器 IP 地址",
|
||||||
|
toPort: "服务端口",
|
||||||
|
username: "登录用户名",
|
||||||
|
usernamePleace: '请输入服务登录用户名',
|
||||||
|
password: "登录密码",
|
||||||
|
dir: "保存目录",
|
||||||
|
dirPleace: '请输入服务地址目标文件目录',
|
||||||
|
}
|
||||||
},
|
},
|
||||||
neQuickSetup: {
|
neQuickSetup: {
|
||||||
reloadPara5G: '刷新',
|
reloadPara5G: '刷新',
|
||||||
@@ -711,8 +731,45 @@ export default {
|
|||||||
},
|
},
|
||||||
neData: {
|
neData: {
|
||||||
common: {
|
common: {
|
||||||
|
startIMSI: '起始IMSI',
|
||||||
|
imsi: 'IMSI',
|
||||||
|
imsiTip: 'IMSI=MCC+MNC+MSIN',
|
||||||
|
imsiTip1: 'MCC=移动国家号码, 由三位数字组成',
|
||||||
|
imsiTip2: 'MNC=移动网络号,由两位数字组成',
|
||||||
|
imsiTip3: 'MSIN=移动客户识别码,采用等长10位数字构成',
|
||||||
|
imsiPlease: "请正确输入IMSI",
|
||||||
|
msisdn: '移动客户识别码',
|
||||||
|
msisdnPlease: "请正确输入移动客户识别码",
|
||||||
|
loadDataConfirm: '确认要重新加载数据吗?',
|
||||||
|
loadData: '加载数据',
|
||||||
|
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新,大约需要{timer}秒,请稍候!!!',
|
||||||
|
batchOper: '批量操作',
|
||||||
|
batchAddText: '批量新增',
|
||||||
|
batchDelText: '批量删除',
|
||||||
|
batchUpdateText: '批量更新',
|
||||||
|
batchNum: '批量个数',
|
||||||
|
checkDel:'勾选删除',
|
||||||
importTemplate: '导入模板',
|
importTemplate: '导入模板',
|
||||||
},
|
},
|
||||||
|
udmVOIP: {
|
||||||
|
startUsername: '起始用户名',
|
||||||
|
username: '用户名',
|
||||||
|
usernamePlease: "请正确输入用户名",
|
||||||
|
password: "密码",
|
||||||
|
passwordPlease: "请正确输入密码",
|
||||||
|
addTitle: '新增VOIP用户',
|
||||||
|
delTip: '确认要删除VOIP用户为【{num}】的信息吗?',
|
||||||
|
exportTip: "确认根据搜索条件导出xlsx表格文件吗?",
|
||||||
|
},
|
||||||
|
udmVolteIMS: {
|
||||||
|
startMSISDN: '起始MSISDN',
|
||||||
|
voipTip: '当选择VoIP时MSISDN会等于IMSI',
|
||||||
|
addTitle: '新增IMS签约用户',
|
||||||
|
vniTip: '示例:ims.mnc000.mcc000.3gppnetwork.org',
|
||||||
|
vniPlease: '请正确输入VNI',
|
||||||
|
delTip: '确认要删除IMS签约为【{num}】的信息吗?',
|
||||||
|
exportTip: "确认根据搜索条件导出xlsx表格文件吗?",
|
||||||
|
},
|
||||||
baseStation: {
|
baseStation: {
|
||||||
list: "列表",
|
list: "列表",
|
||||||
topology: "拓扑图",
|
topology: "拓扑图",
|
||||||
@@ -735,6 +792,12 @@ export default {
|
|||||||
exportTip: "确认根据搜索条件导出xlsx表格文件吗?",
|
exportTip: "确认根据搜索条件导出xlsx表格文件吗?",
|
||||||
importDataEmpty: "导入数据为空",
|
importDataEmpty: "导入数据为空",
|
||||||
},
|
},
|
||||||
|
backupData: {
|
||||||
|
auth: "UDM鉴权用户",
|
||||||
|
sub: "UDM签约用户",
|
||||||
|
voip: "VOIP鉴权用户",
|
||||||
|
volte: "IMS签约用户",
|
||||||
|
}
|
||||||
},
|
},
|
||||||
neUser: {
|
neUser: {
|
||||||
auth: {
|
auth: {
|
||||||
@@ -1004,25 +1067,6 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
traceManage: {
|
traceManage: {
|
||||||
analysis: {
|
|
||||||
imsi: 'IMSI',
|
|
||||||
imsiPlease: '请输入IMSI',
|
|
||||||
msisdn: 'MSISDN',
|
|
||||||
msisdnPlease: '请输入MSISDN',
|
|
||||||
trackTaskId: '跟踪任务标记',
|
|
||||||
srcIp: '源IP地址',
|
|
||||||
dstIp: '目标IP地址',
|
|
||||||
signalType: '信令类型',
|
|
||||||
msgDirect: '消息元',
|
|
||||||
msgType: '消息类型',
|
|
||||||
rowTime: '记录时间',
|
|
||||||
signalData: '信令数据',
|
|
||||||
signalDetail: '信令详情',
|
|
||||||
noData: '无信息内容',
|
|
||||||
taskTitle: '任务 {num}',
|
|
||||||
taskDownText: '下载HTML',
|
|
||||||
taskDownTip: '确认下载信令详情HTML文件?',
|
|
||||||
},
|
|
||||||
pcap: {
|
pcap: {
|
||||||
capArgPlease: '请输入tcpdump -i any支持参数',
|
capArgPlease: '请输入tcpdump -i any支持参数',
|
||||||
cmd: '命令',
|
cmd: '命令',
|
||||||
@@ -1073,21 +1117,17 @@ export default {
|
|||||||
imsiTip: '移动通信IMSI编号',
|
imsiTip: '移动通信IMSI编号',
|
||||||
srcIp: '源IP地址',
|
srcIp: '源IP地址',
|
||||||
srcIpPlease: '请输入源IP地址',
|
srcIpPlease: '请输入源IP地址',
|
||||||
srcIpTip: '当前发送端IPv4地址',
|
srcIpTip: '发送端IPv4地址',
|
||||||
dstIp: '目标IP地址',
|
dstIp: '目标IP地址',
|
||||||
dstIpPlease: '请输入目标IP地址',
|
dstIpPlease: '请输入目标IP地址',
|
||||||
dstIpTip: '对方接收端IPv4地址',
|
dstIpTip: '接收端IPv4地址',
|
||||||
interfaces: '信令接口',
|
interfaces: '信令接口',
|
||||||
interfacesPlease: '请输入信令接口',
|
interfacesPlease: '请输入信令接口',
|
||||||
signalPort: '信令端口',
|
rangePicker: '任务时间',
|
||||||
signalPortPlease: '请输入信令端口',
|
|
||||||
signalPortTip: '目标IP地址或源IP地址对应一方的端口',
|
|
||||||
rangePicker: '开始结束时间',
|
|
||||||
rangePickerPlease: '请选择任务时间开始结束时间',
|
rangePickerPlease: '请选择任务时间开始结束时间',
|
||||||
remark: '说明',
|
remark: '说明',
|
||||||
remarkPlease: '可输入任务说明',
|
remarkPlease: '可输入任务说明',
|
||||||
addTask: '添加任务',
|
addTask: '添加任务',
|
||||||
editTask: '修改任务',
|
|
||||||
viewTask: '查看任务',
|
viewTask: '查看任务',
|
||||||
errorTaskInfo: '获取任务信息失败',
|
errorTaskInfo: '获取任务信息失败',
|
||||||
delTaskTip: '确认删除记录编号为 {id} 的数据项?',
|
delTaskTip: '确认删除记录编号为 {id} 的数据项?',
|
||||||
@@ -1097,6 +1137,15 @@ export default {
|
|||||||
traceFile: "跟踪文件",
|
traceFile: "跟踪文件",
|
||||||
errMsg: "错误信息",
|
errMsg: "错误信息",
|
||||||
imsiORmsisdn: "imsi 或 msisdn 是空值,不能开始任务",
|
imsiORmsisdn: "imsi 或 msisdn 是空值,不能开始任务",
|
||||||
|
dataView: "跟踪数据",
|
||||||
|
protocolOrInterface: "协议/接口",
|
||||||
|
msgNe: '消息网元',
|
||||||
|
msgEvent: '消息事件',
|
||||||
|
msgType: '消息类型',
|
||||||
|
msgDirect: '消息方向',
|
||||||
|
msgLen: '消息长度',
|
||||||
|
rowTime: '消息时间',
|
||||||
|
taskInfo: '任务信息',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
faultManage: {
|
faultManage: {
|
||||||
@@ -1141,13 +1190,11 @@ export default {
|
|||||||
delSuss:'清除成功',
|
delSuss:'清除成功',
|
||||||
delSure:'是否清除该告警',
|
delSure:'是否清除该告警',
|
||||||
showSet:'显示过滤设置',
|
showSet:'显示过滤设置',
|
||||||
|
exportTip: "确认根据搜索条件导出xlsx表格文件吗?",
|
||||||
exportSure:'确认是否导出全部活动告警信息',
|
exportSure:'确认是否导出全部活动告警信息',
|
||||||
viewIdInfo:'查看{alarmId} 记录信息',
|
viewIdInfo:'查看{alarmId} 记录信息',
|
||||||
closeModal:'关闭',
|
closeModal:'关闭',
|
||||||
},
|
},
|
||||||
historyAlarm:{
|
|
||||||
exportSure:'确认是否导出全部历史告警信息?',
|
|
||||||
},
|
|
||||||
faultSetting:{
|
faultSetting:{
|
||||||
interfaceType:'类型',
|
interfaceType:'类型',
|
||||||
email:'Email',
|
email:'Email',
|
||||||
@@ -1179,7 +1226,7 @@ export default {
|
|||||||
type:'网元类型',
|
type:'网元类型',
|
||||||
neId:'网元唯一标识',
|
neId:'网元唯一标识',
|
||||||
MML:'MML',
|
MML:'MML',
|
||||||
logTime:'log Time'
|
logTime:'记录时间'
|
||||||
},
|
},
|
||||||
forwarding:{
|
forwarding:{
|
||||||
alarmId:'告警唯一标识',
|
alarmId:'告警唯一标识',
|
||||||
@@ -1216,12 +1263,17 @@ export default {
|
|||||||
tailLines: '末尾行数',
|
tailLines: '末尾行数',
|
||||||
},
|
},
|
||||||
exportFile:{
|
exportFile:{
|
||||||
fileName:'文件来源',
|
fileSource:'文件来源',
|
||||||
|
fileSourcePlease:'请选择文件来源',
|
||||||
downTip: "确认下载文件名为 【{fileName}】 文件?",
|
downTip: "确认下载文件名为 【{fileName}】 文件?",
|
||||||
downTipErr: "文件获取失败",
|
downTipErr: "文件获取失败",
|
||||||
deleteTip: "确认删除文件名为 【{fileName}】 文件?",
|
deleteTip: "确认删除文件名为 【{fileName}】 文件?",
|
||||||
deleteTipErr: "文件删除失败",
|
deleteTipErr: "文件删除失败",
|
||||||
selectTip:"请选择文件名",
|
operateLog:'操作日志',
|
||||||
|
cdrIMS:'语音话单',
|
||||||
|
cdrSMF:'数据话单',
|
||||||
|
cdrSMSC:'短信话单',
|
||||||
|
cdrSGWC:'漫游数据话单',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
monitor: {
|
monitor: {
|
||||||
@@ -1599,7 +1651,7 @@ export default {
|
|||||||
loginTime: '登录时间',
|
loginTime: '登录时间',
|
||||||
status: '用户状态',
|
status: '用户状态',
|
||||||
userNameTip:'账号只能包含大写字母、小写字母和数字的字符串,长度至少为6位',
|
userNameTip:'账号只能包含大写字母、小写字母和数字的字符串,长度至少为6位',
|
||||||
passwdTip:'密码至少包含大小写字母、数字、特殊符号,且不少于6位',
|
passwdTip:'请输入正确的密码格式',
|
||||||
nickNameTip:'昵称不少于2位',
|
nickNameTip:'昵称不少于2位',
|
||||||
emailTip:'请输入正确的邮箱地址',
|
emailTip:'请输入正确的邮箱地址',
|
||||||
phoneTip:'请输入正确的手机号码',
|
phoneTip:'请输入正确的手机号码',
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
import RightContent from './components/RightContent.vue';
|
import RightContent from './components/RightContent.vue';
|
||||||
import Tabs from './components/Tabs.vue';
|
import Tabs from './components/Tabs.vue';
|
||||||
import GlobalMask from '@/components/GlobalMask/index.vue';
|
import GlobalMask from '@/components/GlobalMask/index.vue';
|
||||||
|
import ForcePasswdChange from '@/components/ForcePasswdChange/index.vue';
|
||||||
import { scriptUrl } from '@/assets/js/icon_font_8d5l8fzk5b87iudi';
|
import { scriptUrl } from '@/assets/js/icon_font_8d5l8fzk5b87iudi';
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
@@ -362,6 +363,8 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<!-- 全局遮罩 -->
|
<!-- 全局遮罩 -->
|
||||||
<GlobalMask />
|
<GlobalMask />
|
||||||
|
<!-- 强制密码修改 -->
|
||||||
|
<ForcePasswdChange />
|
||||||
</a-watermark>
|
</a-watermark>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,53 @@
|
|||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
import { TOKEN_COOKIE } from '@/constants/token-constants';
|
|
||||||
import { localRemove, localSet } from '@/utils/cache-local-utils';
|
import { localRemove, localSet } from '@/utils/cache-local-utils';
|
||||||
import {
|
import {
|
||||||
CACHE_LOCAL_LOCK_PASSWD,
|
CACHE_LOCAL_LOCK_PASSWD,
|
||||||
CACHE_LOCAL_MASK,
|
CACHE_LOCAL_MASK,
|
||||||
} from '@/constants/cache-keys-constants';
|
} from '@/constants/cache-keys-constants';
|
||||||
|
import {
|
||||||
|
TOKEN_ACCESS_COOKIE,
|
||||||
|
TOKEN_REFRESH_COOKIE,
|
||||||
|
} from '@/constants/token-constants';
|
||||||
|
|
||||||
/**获取cookis中Token字符串 */
|
/**获取访问令牌 */
|
||||||
export function getToken(): string {
|
export function getAccessToken(): string {
|
||||||
return Cookies.get(TOKEN_COOKIE) || '';
|
return Cookies.get(TOKEN_ACCESS_COOKIE) || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**设置cookis中Token字符串 */
|
/**
|
||||||
export function setToken(token: string): void {
|
* 设置访问令牌
|
||||||
Cookies.set(TOKEN_COOKIE, token || '');
|
* @param token token字符串
|
||||||
|
* @param exp 过期时间(秒)
|
||||||
|
*/
|
||||||
|
export function setAccessToken(token: string, exp: number): void {
|
||||||
|
const expires = new Date(new Date().getTime() + exp * 1000);
|
||||||
|
Cookies.set(TOKEN_ACCESS_COOKIE, token, { expires });
|
||||||
localSet(CACHE_LOCAL_MASK, 'none');
|
localSet(CACHE_LOCAL_MASK, 'none');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**移除cookis中Token字符串,localStorage中锁屏字符串 */
|
/**移除访问令牌 */
|
||||||
export function removeToken(): void {
|
export function delAccessToken(): void {
|
||||||
Cookies.remove(TOKEN_COOKIE);
|
Cookies.remove(TOKEN_ACCESS_COOKIE);
|
||||||
localRemove(CACHE_LOCAL_MASK);
|
localRemove(CACHE_LOCAL_MASK);
|
||||||
localRemove(CACHE_LOCAL_LOCK_PASSWD);
|
localRemove(CACHE_LOCAL_LOCK_PASSWD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**获取刷新令牌 */
|
||||||
|
export function getRefreshToken(): string {
|
||||||
|
return Cookies.get(TOKEN_REFRESH_COOKIE) || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置刷新令牌
|
||||||
|
* @param token token字符串
|
||||||
|
* @param exp 过期时间(秒)
|
||||||
|
*/
|
||||||
|
export function setRefreshToken(token: string, exp: number): void {
|
||||||
|
const expires = new Date(new Date().getTime() + exp * 1000);
|
||||||
|
Cookies.set(TOKEN_REFRESH_COOKIE, token, { expires });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**移除刷新令牌 */
|
||||||
|
export function delRefreshToken(): void {
|
||||||
|
Cookies.remove(TOKEN_REFRESH_COOKIE);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import { getToken, removeToken } from '@/plugins/auth-token';
|
import {
|
||||||
|
getAccessToken,
|
||||||
|
setAccessToken,
|
||||||
|
delAccessToken,
|
||||||
|
getRefreshToken,
|
||||||
|
setRefreshToken,
|
||||||
|
delRefreshToken,
|
||||||
|
} from '@/plugins/auth-token';
|
||||||
import {
|
import {
|
||||||
sessionGet,
|
sessionGet,
|
||||||
sessionGetJSON,
|
sessionGetJSON,
|
||||||
sessionSetJSON,
|
sessionSetJSON,
|
||||||
} from '@/utils/cache-session-utils';
|
} from '@/utils/cache-session-utils';
|
||||||
import { localGet } from '@/utils/cache-local-utils';
|
|
||||||
import { TOKEN_KEY, TOKEN_KEY_PREFIX } from '@/constants/token-constants';
|
import { TOKEN_KEY, TOKEN_KEY_PREFIX } from '@/constants/token-constants';
|
||||||
import {
|
import {
|
||||||
CACHE_LOCAL_I18N,
|
CACHE_LOCAL_I18N,
|
||||||
@@ -18,6 +24,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
RESULT_CODE_ENCRYPT,
|
RESULT_CODE_ENCRYPT,
|
||||||
RESULT_CODE_ERROR,
|
RESULT_CODE_ERROR,
|
||||||
|
RESULT_CODE_EXCEPTION,
|
||||||
RESULT_CODE_SUCCESS,
|
RESULT_CODE_SUCCESS,
|
||||||
RESULT_MSG_ENCRYPT,
|
RESULT_MSG_ENCRYPT,
|
||||||
RESULT_MSG_ERROR,
|
RESULT_MSG_ERROR,
|
||||||
@@ -25,10 +32,11 @@ import {
|
|||||||
RESULT_MSG_SERVER_ERROR,
|
RESULT_MSG_SERVER_ERROR,
|
||||||
RESULT_MSG_SUCCESS,
|
RESULT_MSG_SUCCESS,
|
||||||
RESULT_MSG_TIMEOUT,
|
RESULT_MSG_TIMEOUT,
|
||||||
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';
|
import { decryptAES, encryptAES } from '@/utils/encrypt-utils';
|
||||||
|
import { localGet } from '@/utils/cache-local-utils';
|
||||||
|
import { refreshToken } from '@/api/auth';
|
||||||
|
|
||||||
/**响应结果类型 */
|
/**响应结果类型 */
|
||||||
export type ResultType = {
|
export type ResultType = {
|
||||||
@@ -61,7 +69,7 @@ type OptionsType = {
|
|||||||
/**请求地址 */
|
/**请求地址 */
|
||||||
url: string;
|
url: string;
|
||||||
/**请求方法 */
|
/**请求方法 */
|
||||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
||||||
/**请求头 */
|
/**请求头 */
|
||||||
headers?: HeadersInit;
|
headers?: HeadersInit;
|
||||||
/**地址栏参数 */
|
/**地址栏参数 */
|
||||||
@@ -133,16 +141,21 @@ function beforeRequest(options: OptionsType): OptionsType | Promise<any> {
|
|||||||
Reflect.set(options.headers, 'Accept-Language', `${language};q=0.9`);
|
Reflect.set(options.headers, 'Accept-Language', `${language};q=0.9`);
|
||||||
|
|
||||||
// 是否需要设置 token
|
// 是否需要设置 token
|
||||||
const token = getToken();
|
const accessToken = getAccessToken();
|
||||||
if (options.whithToken && token) {
|
if (options.whithToken && accessToken) {
|
||||||
Reflect.set(options.headers, TOKEN_KEY, TOKEN_KEY_PREFIX + token);
|
Reflect.set(
|
||||||
|
options.headers,
|
||||||
|
TOKEN_KEY,
|
||||||
|
TOKEN_KEY_PREFIX + ' ' + accessToken
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 是否需要防止数据重复提交
|
// 是否需要防止数据重复提交
|
||||||
if (
|
if (
|
||||||
options.repeatSubmit &&
|
options.repeatSubmit &&
|
||||||
options.dataType === 'json' &&
|
options.dataType === 'json' &&
|
||||||
['post', 'put'].includes(options.method)
|
!(options.data instanceof FormData) &&
|
||||||
|
['POST', 'PUT'].includes(options.method)
|
||||||
) {
|
) {
|
||||||
const requestObj: RepeatSubmitType = {
|
const requestObj: RepeatSubmitType = {
|
||||||
url: options.url,
|
url: options.url,
|
||||||
@@ -212,13 +225,31 @@ function beforeRequest(options: OptionsType): OptionsType | Promise<any> {
|
|||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**请求后的拦截 */
|
/**响应前的拦截 */
|
||||||
function interceptorResponse(res: ResultType): ResultType | Promise<any> {
|
async function beforeResponse(
|
||||||
|
options: OptionsType,
|
||||||
|
res: ResultType
|
||||||
|
): Promise<any> {
|
||||||
// console.log('请求后的拦截', res);
|
// console.log('请求后的拦截', res);
|
||||||
|
|
||||||
// 登录失效时,移除授权令牌并重新刷新页面
|
// 登录失效时,移除授权令牌并重新刷新页面
|
||||||
if (res.code === 401) {
|
// 登录失效时,移除访问令牌并重新请求
|
||||||
removeToken();
|
if (res.code === 401001) {
|
||||||
|
const result = await refreshToken(getRefreshToken());
|
||||||
|
// 更新访问令牌和刷新令牌
|
||||||
|
if (result.code === RESULT_CODE_SUCCESS) {
|
||||||
|
setAccessToken(result.data.accessToken, result.data.refreshExpiresIn);
|
||||||
|
setRefreshToken(result.data.refreshToken, result.data.refreshExpiresIn);
|
||||||
|
return await request(options);
|
||||||
|
} else {
|
||||||
|
delAccessToken();
|
||||||
|
delRefreshToken();
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ([401002, 401003].includes(res.code)) {
|
||||||
|
delAccessToken();
|
||||||
|
delRefreshToken();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,43 +302,45 @@ function interceptorResponse(res: ResultType): ResultType | Promise<any> {
|
|||||||
* @returns 返回 Promise<ResultType>
|
* @returns 返回 Promise<ResultType>
|
||||||
*/
|
*/
|
||||||
export async function request(options: OptionsType): Promise<ResultType> {
|
export async function request(options: OptionsType): Promise<ResultType> {
|
||||||
options = Object.assign({}, FATCH_OPTIONS, options);
|
let reqOptions = Object.assign({}, FATCH_OPTIONS, options);
|
||||||
let timeoutId: any = 0;
|
|
||||||
// 请求超时控制请求终止
|
// 请求超时控制请求终止
|
||||||
if (!options.signal) {
|
let timeoutId: any = null;
|
||||||
|
if (!reqOptions.signal) {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const { signal } = controller;
|
reqOptions.signal = controller.signal;
|
||||||
options.signal = signal;
|
|
||||||
timeoutId = setTimeout(() => {
|
timeoutId = setTimeout(() => {
|
||||||
controller.abort(); // 终止请求
|
controller.abort(); // 终止请求
|
||||||
}, options.timeout);
|
}, reqOptions.timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查请求拦截
|
// 检查请求拦截
|
||||||
const beforeReq = beforeRequest(options);
|
const beforeReq = beforeRequest(reqOptions);
|
||||||
if (beforeReq instanceof Promise) {
|
if (beforeReq instanceof Promise) {
|
||||||
return await beforeReq;
|
return await beforeReq;
|
||||||
}
|
}
|
||||||
options = beforeReq;
|
reqOptions = beforeReq;
|
||||||
|
|
||||||
// 判断用户传递的URL是否http或/开头
|
// 判断用户传递的URL是否http或/开头
|
||||||
if (!options.url.startsWith('http')) {
|
if (!reqOptions.url.startsWith('http')) {
|
||||||
const uri = options.url.startsWith('/') ? options.url : `/${options.url}`;
|
const uri = reqOptions.url.startsWith('/')
|
||||||
options.url = options.baseUrl + uri;
|
? reqOptions.url
|
||||||
|
: `/${reqOptions.url}`;
|
||||||
|
reqOptions.url = reqOptions.baseUrl + uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(options.url, options);
|
const res = await fetch(reqOptions.url, reqOptions);
|
||||||
// console.log('请求结果:', res);
|
// console.log('请求结果:', res);
|
||||||
|
if (res.status === 500) {
|
||||||
// 状态码拦截处理
|
return {
|
||||||
const reqNot = stateCode(res);
|
code: RESULT_CODE_EXCEPTION,
|
||||||
if (reqNot != false) {
|
msg: RESULT_MSG_SERVER_ERROR[language],
|
||||||
return reqNot;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据响应数据类型返回
|
// 根据响应数据类型返回
|
||||||
switch (options.responseType) {
|
switch (reqOptions.responseType) {
|
||||||
case 'text': // 文本数据
|
case 'text': // 文本数据
|
||||||
const str = await res.text();
|
const str = await res.text();
|
||||||
return {
|
return {
|
||||||
@@ -317,11 +350,7 @@ export async function request(options: OptionsType): Promise<ResultType> {
|
|||||||
case 'json': // json格式数据
|
case 'json': // json格式数据
|
||||||
const result = await res.json();
|
const result = await res.json();
|
||||||
// 请求后的拦截
|
// 请求后的拦截
|
||||||
const beforeRes = interceptorResponse(result);
|
return await beforeResponse(options, result);
|
||||||
if (beforeRes instanceof Promise) {
|
|
||||||
return await beforeRes;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
case 'blob': // 二进制数据则直接返回
|
case 'blob': // 二进制数据则直接返回
|
||||||
case 'arrayBuffer':
|
case 'arrayBuffer':
|
||||||
const contentType = res.headers.get('content-type') || '';
|
const contentType = res.headers.get('content-type') || '';
|
||||||
@@ -330,7 +359,7 @@ export async function request(options: OptionsType): Promise<ResultType> {
|
|||||||
return result as ResultType;
|
return result as ResultType;
|
||||||
}
|
}
|
||||||
const data =
|
const data =
|
||||||
options.responseType === 'blob'
|
reqOptions.responseType === 'blob'
|
||||||
? await res.blob()
|
? await res.blob()
|
||||||
: await res.arrayBuffer();
|
: await res.arrayBuffer();
|
||||||
return {
|
return {
|
||||||
@@ -356,41 +385,7 @@ export async function request(options: OptionsType): Promise<ResultType> {
|
|||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearTimeout(timeoutId); // 请求成功,清除超时计时器
|
clearTimeout(timeoutId); // 清除超时计时器
|
||||||
|
timeoutId = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断状态码处理结果信息(不可处理)
|
|
||||||
* @param res 请求结果
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function stateCode(res: Response) {
|
|
||||||
// 网络异常
|
|
||||||
if (res.status === 500) {
|
|
||||||
return {
|
|
||||||
code: RESULT_CODE_ERROR,
|
|
||||||
msg: RESULT_MSG_SERVER_ERROR[language],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// 上传文件成功无内容返回
|
|
||||||
if (res.status === 204 || res.statusText === 'No Content') {
|
|
||||||
return {
|
|
||||||
code: RESULT_CODE_SUCCESS,
|
|
||||||
msg: RESULT_MSG_SUCCESS[language],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// 地址找不到
|
|
||||||
if (res.status === 404 || res.status === 405) {
|
|
||||||
return {
|
|
||||||
code: RESULT_CODE_ERROR,
|
|
||||||
msg: RESULT_MSG_URL_NOTFOUND[language],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// 身份授权
|
|
||||||
if (res.status === 401) {
|
|
||||||
removeToken();
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { sessionGet } from '@/utils/cache-session-utils';
|
import { sessionGet } from '@/utils/cache-session-utils';
|
||||||
import { getToken } from './auth-token';
|
import { getAccessToken } from './auth-token';
|
||||||
import { localGet } from '@/utils/cache-local-utils';
|
import { localGet } from '@/utils/cache-local-utils';
|
||||||
import { CACHE_LOCAL_I18N } from '@/constants/cache-keys-constants';
|
import { CACHE_LOCAL_I18N } from '@/constants/cache-keys-constants';
|
||||||
import { TOKEN_RESPONSE_FIELD } from '@/constants/token-constants';
|
import { TOKEN_RESPONSE_FIELD } from '@/constants/token-constants';
|
||||||
@@ -92,7 +92,7 @@ export class WS {
|
|||||||
// 地址栏参数
|
// 地址栏参数
|
||||||
let params = Object.assign({}, options.params, {
|
let params = Object.assign({}, options.params, {
|
||||||
// 设置 token
|
// 设置 token
|
||||||
[TOKEN_RESPONSE_FIELD]: getToken(),
|
[TOKEN_RESPONSE_FIELD]: getAccessToken(),
|
||||||
// 多语言
|
// 多语言
|
||||||
['language']: localGet(CACHE_LOCAL_I18N) || 'en_US',
|
['language']: localGet(CACHE_LOCAL_I18N) || 'en_US',
|
||||||
});
|
});
|
||||||
@@ -119,17 +119,15 @@ export class WS {
|
|||||||
};
|
};
|
||||||
// 用于指定当从服务器接受到信息时的回调函数。
|
// 用于指定当从服务器接受到信息时的回调函数。
|
||||||
ws.onmessage = ev => {
|
ws.onmessage = ev => {
|
||||||
|
if (ev.type !== 'message') return;
|
||||||
// 解析文本消息
|
// 解析文本消息
|
||||||
if (ev.type === 'message') {
|
try {
|
||||||
const data = ev.data;
|
const jsonData = JSON.parse(ev.data);
|
||||||
try {
|
if (typeof options.onmessage === 'function') {
|
||||||
const jsonData = JSON.parse(data);
|
options.onmessage(jsonData);
|
||||||
if (typeof options.onmessage === 'function') {
|
|
||||||
options.onmessage(jsonData);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('websocket message formatting error', error);
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('websocket message formatting error', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// 用于指定连接关闭后的回调函数。
|
// 用于指定连接关闭后的回调函数。
|
||||||
@@ -221,7 +219,7 @@ export class WS {
|
|||||||
this.heartInterval = window.setInterval(() => {
|
this.heartInterval = window.setInterval(() => {
|
||||||
this.send({
|
this.send({
|
||||||
requestId: `${Date.now()}`,
|
requestId: `${Date.now()}`,
|
||||||
type: 'ping',
|
type: 'PING',
|
||||||
});
|
});
|
||||||
}, heartTimer);
|
}, heartTimer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import NProgress from 'nprogress';
|
|||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
import BasicLayout from '../layouts/BasicLayout.vue';
|
import BasicLayout from '../layouts/BasicLayout.vue';
|
||||||
import BlankLayout from '../layouts/BlankLayout.vue';
|
import BlankLayout from '../layouts/BlankLayout.vue';
|
||||||
import { getToken } from '@/plugins/auth-token';
|
import { getAccessToken } from '@/plugins/auth-token';
|
||||||
import { validHttp } from '@/utils/regular-utils';
|
import { validHttp } from '@/utils/regular-utils';
|
||||||
import useUserStore from '@/store/modules/user';
|
import useUserStore from '@/store/modules/user';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
@@ -182,7 +182,7 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
// next({ name: 'Index' });
|
// next({ name: 'Index' });
|
||||||
// }
|
// }
|
||||||
|
|
||||||
let token = getToken();
|
let token = getAccessToken();
|
||||||
|
|
||||||
// 免用户登录认证
|
// 免用户登录认证
|
||||||
if (!appStore.loginAuth) {
|
if (!appStore.loginAuth) {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { getSysConf } from '@/api';
|
import { getSysConf } from '@/api';
|
||||||
import { CACHE_LOCAL_I18N, CACHE_SESSION_CRYPTO_API } from '@/constants/cache-keys-constants';
|
import {
|
||||||
|
CACHE_LOCAL_I18N,
|
||||||
|
CACHE_SESSION_CRYPTO_API,
|
||||||
|
} from '@/constants/cache-keys-constants';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
// import { removeToken } from '@/plugins/auth-token';
|
|
||||||
import { parseUrlPath } from '@/plugins/file-static-url';
|
import { parseUrlPath } from '@/plugins/file-static-url';
|
||||||
import { localGet, localSet } from '@/utils/cache-local-utils';
|
import { localGet, localSet } from '@/utils/cache-local-utils';
|
||||||
import { sessionSet } from '@/utils/cache-session-utils';
|
import { sessionSet } from '@/utils/cache-session-utils';
|
||||||
@@ -16,11 +18,12 @@ type AppStore = {
|
|||||||
/**应用版本 */
|
/**应用版本 */
|
||||||
appVersion: string;
|
appVersion: string;
|
||||||
|
|
||||||
/**服务版本 */
|
/**版本号 */
|
||||||
version: string;
|
version: string;
|
||||||
// buildTime: string;
|
|
||||||
/**系统引导使用 */
|
/**系统引导使用 */
|
||||||
// bootloader: boolean;
|
// bootloader: boolean;
|
||||||
|
/**服务版本 */
|
||||||
|
serverVersion: string;
|
||||||
// 用户登录认证
|
// 用户登录认证
|
||||||
loginAuth: boolean;
|
loginAuth: boolean;
|
||||||
// 用户接口加密
|
// 用户接口加密
|
||||||
@@ -54,13 +57,13 @@ const useAppStore = defineStore('app', {
|
|||||||
appCode: import.meta.env.VITE_APP_CODE,
|
appCode: import.meta.env.VITE_APP_CODE,
|
||||||
appVersion: import.meta.env.VITE_APP_VERSION,
|
appVersion: import.meta.env.VITE_APP_VERSION,
|
||||||
|
|
||||||
version: `-`,
|
version: '-',
|
||||||
// buildTime: `-`,
|
|
||||||
// bootloader: false,
|
// bootloader: false,
|
||||||
|
serverVersion: '-',
|
||||||
loginAuth: true,
|
loginAuth: true,
|
||||||
cryptoApi: true,
|
cryptoApi: true,
|
||||||
serialNum: `-`,
|
serialNum: '-',
|
||||||
copyright: `Copyright ©2023 For ${import.meta.env.VITE_APP_NAME}`,
|
copyright: `Copyright ©2023-2025 For ${import.meta.env.VITE_APP_NAME}`,
|
||||||
logoType: 'icon',
|
logoType: 'icon',
|
||||||
filePathIcon: '',
|
filePathIcon: '',
|
||||||
filePathBrand: '',
|
filePathBrand: '',
|
||||||
@@ -86,15 +89,16 @@ const useAppStore = defineStore('app', {
|
|||||||
const res = await getSysConf();
|
const res = await getSysConf();
|
||||||
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
||||||
this.version = res.data.version;
|
this.version = res.data.version;
|
||||||
// this.buildTime = res.data.buildTime;
|
this.serverVersion = res.data.serverVersion;
|
||||||
// this.bootloader = res.data.bootloader === 'true';
|
// this.bootloader = res.data.bootloader === 'true';
|
||||||
// // 引导时
|
// // 引导时
|
||||||
// if (this.bootloader) {
|
// if (this.bootloader) {
|
||||||
// removeToken();
|
// delAccessToken();
|
||||||
|
// delRefreshToken();
|
||||||
// }
|
// }
|
||||||
this.loginAuth = res.data.loginAuth !== 'false';
|
this.loginAuth = res.data.loginAuth !== 'false';
|
||||||
this.cryptoApi = res.data.cryptoApi !== 'false';
|
this.cryptoApi = res.data.cryptoApi !== 'false';
|
||||||
sessionSet(CACHE_SESSION_CRYPTO_API, res.data.cryptoApi);
|
sessionSet(CACHE_SESSION_CRYPTO_API, res.data.cryptoApi);
|
||||||
this.serialNum = res.data.serialNum;
|
this.serialNum = res.data.serialNum;
|
||||||
this.appName = res.data.title;
|
this.appName = res.data.title;
|
||||||
this.copyright = res.data.copyright;
|
this.copyright = res.data.copyright;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ type MaskStateType = {
|
|||||||
const useMaskStore = defineStore('mask', {
|
const useMaskStore = defineStore('mask', {
|
||||||
state: (): MaskStateType => ({
|
state: (): MaskStateType => ({
|
||||||
type: (localGet(CACHE_LOCAL_MASK) || 'none') as MaskStateType['type'],
|
type: (localGet(CACHE_LOCAL_MASK) || 'none') as MaskStateType['type'],
|
||||||
lockPasswd: localGet(CACHE_LOCAL_LOCK_PASSWD) || '',
|
lockPasswd: atob(localGet(CACHE_LOCAL_LOCK_PASSWD) || ''),
|
||||||
lockTimeout: 0,
|
lockTimeout: 0,
|
||||||
}),
|
}),
|
||||||
getters: {},
|
getters: {},
|
||||||
@@ -59,7 +59,7 @@ const useMaskStore = defineStore('mask', {
|
|||||||
}, 5_000);
|
}, 5_000);
|
||||||
}
|
}
|
||||||
if (type === 'lock') {
|
if (type === 'lock') {
|
||||||
localSet(CACHE_LOCAL_LOCK_PASSWD, this.lockPasswd);
|
localSet(CACHE_LOCAL_LOCK_PASSWD, btoa(this.lockPasswd));
|
||||||
} else {
|
} else {
|
||||||
localRemove(CACHE_LOCAL_LOCK_PASSWD);
|
localRemove(CACHE_LOCAL_LOCK_PASSWD);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { defineStore } from 'pinia';
|
|||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { listAllNeInfo } from '@/api/ne/neInfo';
|
import { listAllNeInfo } from '@/api/ne/neInfo';
|
||||||
import { parseDataToOptions } from '@/utils/parse-tree-utils';
|
import { parseDataToOptions } from '@/utils/parse-tree-utils';
|
||||||
import { getNeTraceInterfaceAll } from '@/api/trace/task';
|
|
||||||
import { getNePerformanceList } from '@/api/perfManage/taskManage';
|
import { getNePerformanceList } from '@/api/perfManage/taskManage';
|
||||||
|
|
||||||
/**网元信息类型 */
|
/**网元信息类型 */
|
||||||
@@ -13,8 +12,6 @@ type NeInfo = {
|
|||||||
neCascaderOptions: Record<string, any>[];
|
neCascaderOptions: Record<string, any>[];
|
||||||
/**选择器单级父类型 */
|
/**选择器单级父类型 */
|
||||||
neSelectOtions: Record<string, any>[];
|
neSelectOtions: Record<string, any>[];
|
||||||
/**跟踪接口列表 */
|
|
||||||
traceInterfaceList: Record<string, any>[];
|
|
||||||
/**性能测量数据集 */
|
/**性能测量数据集 */
|
||||||
perMeasurementList: Record<string, any>[];
|
perMeasurementList: Record<string, any>[];
|
||||||
};
|
};
|
||||||
@@ -24,7 +21,6 @@ const useNeInfoStore = defineStore('neinfo', {
|
|||||||
neList: [],
|
neList: [],
|
||||||
neCascaderOptions: [],
|
neCascaderOptions: [],
|
||||||
neSelectOtions: [],
|
neSelectOtions: [],
|
||||||
traceInterfaceList: [],
|
|
||||||
perMeasurementList: [],
|
perMeasurementList: [],
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
@@ -56,7 +52,7 @@ const useNeInfoStore = defineStore('neinfo', {
|
|||||||
async fnNelist() {
|
async fnNelist() {
|
||||||
// 有数据不请求
|
// 有数据不请求
|
||||||
if (this.neList.length > 0) {
|
if (this.neList.length > 0) {
|
||||||
return { code: 1, data: this.neList, msg: 'success' };
|
return { code: RESULT_CODE_SUCCESS, data: this.neList, msg: 'success' };
|
||||||
}
|
}
|
||||||
const res = await listAllNeInfo({
|
const res = await listAllNeInfo({
|
||||||
bandStatus: false,
|
bandStatus: false,
|
||||||
@@ -79,29 +75,11 @@ const useNeInfoStore = defineStore('neinfo', {
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
// 刷新跟踪接口列表
|
|
||||||
async fnRefreshNeTraceInterface() {
|
|
||||||
this.traceInterfaceList = [];
|
|
||||||
const res = await this.fnNeTraceInterface();
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
// 获取跟踪接口列表
|
|
||||||
async fnNeTraceInterface() {
|
|
||||||
// 有数据不请求
|
|
||||||
if (this.traceInterfaceList.length > 0) {
|
|
||||||
return { code: 1, data: this.traceInterfaceList, msg: 'success' };
|
|
||||||
}
|
|
||||||
const res = await getNeTraceInterfaceAll();
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
|
||||||
this.traceInterfaceList = res.data;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
// 获取性能测量数据集列表
|
// 获取性能测量数据集列表
|
||||||
async fnNeTaskPerformance() {
|
async fnNeTaskPerformance() {
|
||||||
// 有数据不请求
|
// 有数据不请求
|
||||||
if (this.perMeasurementList.length > 0) {
|
if (this.perMeasurementList.length > 0) {
|
||||||
return { code: 1, data: this.perMeasurementList, msg: 'success' };
|
return { code: RESULT_CODE_SUCCESS, data: this.perMeasurementList, msg: 'success' };
|
||||||
}
|
}
|
||||||
const res = await getNePerformanceList();
|
const res = await getNePerformanceList();
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type {
|
|||||||
RouteMeta,
|
RouteMeta,
|
||||||
RouteRecordRaw,
|
RouteRecordRaw,
|
||||||
} from 'vue-router';
|
} from 'vue-router';
|
||||||
import { getRouters } from '@/api/router';
|
import { getRouter } from '@/api/auth';
|
||||||
import BasicLayout from '@/layouts/BasicLayout.vue';
|
import BasicLayout from '@/layouts/BasicLayout.vue';
|
||||||
import BlankLayout from '@/layouts/BlankLayout.vue';
|
import BlankLayout from '@/layouts/BlankLayout.vue';
|
||||||
import LinkLayout from '@/layouts/LinkLayout.vue';
|
import LinkLayout from '@/layouts/LinkLayout.vue';
|
||||||
@@ -46,7 +46,7 @@ const useRouterStore = defineStore('router', {
|
|||||||
* @returns 生成的路由菜单
|
* @returns 生成的路由菜单
|
||||||
*/
|
*/
|
||||||
async generateRoutes() {
|
async generateRoutes() {
|
||||||
const res = await getRouters();
|
const res = await getRouter();
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
const buildRoutes = buildRouters(res.data.concat());
|
const buildRoutes = buildRouters(res.data.concat());
|
||||||
this.buildRouterData = buildRoutes;
|
this.buildRouterData = buildRoutes;
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
import defaultAvatar from '@/assets/images/default_avatar.png';
|
import defaultAvatar from '@/assets/images/default_avatar.png';
|
||||||
import useLayoutStore from './layout';
|
import useLayoutStore from './layout';
|
||||||
import { login, logout, getInfo } from '@/api/login';
|
import { login, logout, getInfo } from '@/api/auth';
|
||||||
import { setToken, removeToken } from '@/plugins/auth-token';
|
import {
|
||||||
|
delAccessToken,
|
||||||
|
delRefreshToken,
|
||||||
|
setAccessToken,
|
||||||
|
setRefreshToken,
|
||||||
|
} from '@/plugins/auth-token';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { TOKEN_RESPONSE_FIELD } from '@/constants/token-constants';
|
import { RESULT_CODE_EXCEPTION, RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
|
||||||
import { parseUrlPath } from '@/plugins/file-static-url';
|
import { parseUrlPath } from '@/plugins/file-static-url';
|
||||||
|
|
||||||
/**用户信息类型 */
|
/**用户信息类型 */
|
||||||
type UserInfo = {
|
type UserInfo = {
|
||||||
|
/**用户ID */
|
||||||
|
forcePasswdChange: boolean;
|
||||||
/**用户ID */
|
/**用户ID */
|
||||||
userId: string;
|
userId: string;
|
||||||
/**登录账号 */
|
/**登录账号 */
|
||||||
@@ -33,6 +39,7 @@ type UserInfo = {
|
|||||||
|
|
||||||
const useUserStore = defineStore('user', {
|
const useUserStore = defineStore('user', {
|
||||||
state: (): UserInfo => ({
|
state: (): UserInfo => ({
|
||||||
|
forcePasswdChange: false,
|
||||||
userId: '',
|
userId: '',
|
||||||
userName: '',
|
userName: '',
|
||||||
roles: [],
|
roles: [],
|
||||||
@@ -102,8 +109,11 @@ const useUserStore = defineStore('user', {
|
|||||||
async fnLogin(loginBody: Record<string, string>) {
|
async fnLogin(loginBody: Record<string, string>) {
|
||||||
const res = await login(loginBody);
|
const res = await login(loginBody);
|
||||||
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
||||||
const token = res.data[TOKEN_RESPONSE_FIELD];
|
setAccessToken(res.data.accessToken, res.data.refreshExpiresIn);
|
||||||
setToken(token);
|
setRefreshToken(res.data.refreshToken, res.data.refreshExpiresIn);
|
||||||
|
if (res.data?.forcePasswdChange) {
|
||||||
|
this.forcePasswdChange = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
@@ -139,10 +149,15 @@ const useUserStore = defineStore('user', {
|
|||||||
// }
|
// }
|
||||||
// useLayoutStore().changeWaterMark(waterMarkContent);
|
// useLayoutStore().changeWaterMark(waterMarkContent);
|
||||||
useLayoutStore().changeWaterMark('');
|
useLayoutStore().changeWaterMark('');
|
||||||
|
// 强制修改密码
|
||||||
|
if (res.data?.forcePasswdChange) {
|
||||||
|
this.forcePasswdChange = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 网络错误时退出登录状态
|
// 网络错误时退出登录状态
|
||||||
if (res.code === 0) {
|
if (res.code === RESULT_CODE_EXCEPTION) {
|
||||||
removeToken();
|
delAccessToken();
|
||||||
|
delRefreshToken();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@@ -156,7 +171,8 @@ const useUserStore = defineStore('user', {
|
|||||||
} finally {
|
} finally {
|
||||||
this.roles = [];
|
this.roles = [];
|
||||||
this.permissions = [];
|
this.permissions = [];
|
||||||
removeToken();
|
delAccessToken();
|
||||||
|
delRefreshToken();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,8 +20,10 @@ export const YYYYMMDDHHMMSS = 'YYYYMMDDHHmmss';
|
|||||||
/**年-月-日 时:分:秒 列如:2022-12-30 01:01:59 */
|
/**年-月-日 时:分:秒 列如:2022-12-30 01:01:59 */
|
||||||
export const YYYY_MM_DD_HH_MM_SS = 'YYYY-MM-DD HH:mm:ss';
|
export const YYYY_MM_DD_HH_MM_SS = 'YYYY-MM-DD HH:mm:ss';
|
||||||
|
|
||||||
/**特殊 列如:2025-04-28 08:00:46 GMT+08:00 */
|
/**国际时间 列如:2022-12-30T01:01:59+08:00 */
|
||||||
export const YYYY_MM_DD_HH_MM_SSZ = 'YYYY-MM-DD HH:mm:ss [GMT]Z';
|
export const RFC3339 = 'YYYY-MM-DDTHH:mm:ssZ';
|
||||||
|
/**国际时间 列如:Thu, Nov 14 2024 10:19 GMT+08:00 */
|
||||||
|
export const RFC822Z = 'ddd, MMM DD YYYY HH:mm [GMT]Z';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式时间字符串
|
* 格式时间字符串
|
||||||
@@ -39,12 +41,12 @@ export function parseStrToDate(
|
|||||||
/**
|
/**
|
||||||
* 格式时间
|
* 格式时间
|
||||||
* @param date 可转的Date对象
|
* @param date 可转的Date对象
|
||||||
* @param formatStr 时间格式 默认YYYY-MM-DD HH:mm:ssZZ
|
* @param formatStr 时间格式 默认 RFC3339
|
||||||
* @returns 时间格式字符串
|
* @returns 时间格式字符串
|
||||||
*/
|
*/
|
||||||
export function parseDateToStr(
|
export function parseDateToStr(
|
||||||
date: string | number | Date,
|
date: string | number | Date,
|
||||||
formatStr: string = YYYY_MM_DD_HH_MM_SSZ
|
formatStr: string = RFC3339
|
||||||
): string {
|
): string {
|
||||||
return dayjs(date).format(formatStr);
|
return dayjs(date).format(formatStr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,14 @@ export function parseObjLineToHump(obj: any): any {
|
|||||||
* @param decimalPlaces 保留小数位,默认2位
|
* @param decimalPlaces 保留小数位,默认2位
|
||||||
* @returns 单位 xB
|
* @returns 单位 xB
|
||||||
*/
|
*/
|
||||||
export function parseSizeFromFile(bytes: number, decimalPlaces: number = 2) {
|
export function parseSizeFromFile(
|
||||||
|
bytes: number,
|
||||||
|
decimalPlaces: number = 2
|
||||||
|
): string {
|
||||||
|
if (typeof bytes !== 'number' || isNaN(bytes) || bytes < 0) {
|
||||||
|
return `${bytes}`;
|
||||||
|
}
|
||||||
|
if (bytes === 0) return '0 B';
|
||||||
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (bytes >= 1024 && i < units.length - 1) {
|
while (bytes >= 1024 && i < units.length - 1) {
|
||||||
|
|||||||
@@ -27,14 +27,15 @@ export const regExpUserName = /^[A-Za-z0-9]{6,}$/;
|
|||||||
* 有效密码格式
|
* 有效密码格式
|
||||||
*
|
*
|
||||||
* 密码至少包含大小写字母、数字、特殊符号,且不少于6位
|
* 密码至少包含大小写字母、数字、特殊符号,且不少于6位
|
||||||
|
* /^(?![A-Za-z0-9]+$)(?![a-z0-9\W]+$)(?![A-Za-z\W]+$)(?![A-Z0-9\W]+$)[a-zA-Z0-9\W]{6,}$/
|
||||||
*/
|
*/
|
||||||
export const regExpPasswd = /^.{5,}$/;
|
export const regExpPasswd = /^.{6,}$/;
|
||||||
// /^(?![A-Za-z0-9]+$)(?![a-z0-9\W]+$)(?![A-Za-z\W]+$)(?![A-Z0-9\W]+$)[a-zA-Z0-9\W]{6,}$/;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 有效手机号格式
|
* 有效手机号格式
|
||||||
|
* /^1[3|4|5|6|7|8|9][0-9]\d{8}$/
|
||||||
*/
|
*/
|
||||||
export const regExpMobile = /^.{3,}$/; // /^1[3|4|5|6|7|8|9][0-9]\d{8}$/;
|
export const regExpMobile = /^.{3,}$/;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 有效邮箱格式
|
* 有效邮箱格式
|
||||||
@@ -46,8 +47,9 @@ export const regExpEmail =
|
|||||||
* 有效用户昵称格式
|
* 有效用户昵称格式
|
||||||
*
|
*
|
||||||
* 用户昵称只能包含字母、数字、中文和下划线,且不少于2位
|
* 用户昵称只能包含字母、数字、中文和下划线,且不少于2位
|
||||||
|
* /^[\w\u4e00-\u9fa5-]{2,}$/
|
||||||
*/
|
*/
|
||||||
export const regExpNick = /^.{2,}$/; // /^[\w\u4e00-\u9fa5-]{2,}$/;
|
export const regExpNick = /^.{2,}$/;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否为http(s)://开头
|
* 是否为http(s)://开头
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ function fnFinish() {
|
|||||||
updateUserPassword(state.form.oldPassword, state.form.confirmPassword)
|
updateUserPassword(state.form.oldPassword, state.form.confirmPassword)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
fnLogOut();
|
||||||
Modal.success({
|
Modal.success({
|
||||||
title: t('common.tipTitle'),
|
title: t('common.tipTitle'),
|
||||||
content: t('views.account.settings.submitOkTip', {
|
content: t('views.account.settings.submitOkTip', {
|
||||||
@@ -59,7 +60,7 @@ function fnFinish() {
|
|||||||
}),
|
}),
|
||||||
okText: t('views.account.settings.submitOk'),
|
okText: t('views.account.settings.submitOk'),
|
||||||
onOk() {
|
onOk() {
|
||||||
fnLogOut().finally(() => router.push({ name: 'Login' }));
|
router.push({ name: 'Login' });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ export const upfFlowData = ref<FDType>({
|
|||||||
|
|
||||||
/**UPF-流量数据 数据解析 */
|
/**UPF-流量数据 数据解析 */
|
||||||
export function upfFlowParse(data: Record<string, string>) {
|
export function upfFlowParse(data: Record<string, string>) {
|
||||||
upfFlowData.value.lineXTime.push(parseDateToStr(+data['timeGroup'], 'HH:mm:ss'));
|
upfFlowData.value.lineXTime.push(
|
||||||
|
parseDateToStr(+data['timeGroup'], 'HH:mm:ss')
|
||||||
|
);
|
||||||
const upN3 = parseSizeFromKbs(+data['UPF.03'], 5);
|
const upN3 = parseSizeFromKbs(+data['UPF.03'], 5);
|
||||||
upfFlowData.value.lineYUp.push(upN3[0]);
|
upfFlowData.value.lineYUp.push(upN3[0]);
|
||||||
const downN6 = parseSizeFromKbs(+data['UPF.06'], 5);
|
const downN6 = parseSizeFromKbs(+data['UPF.06'], 5);
|
||||||
|
|||||||
@@ -385,7 +385,7 @@ onBeforeUnmount(() => {
|
|||||||
class="item toRouter"
|
class="item toRouter"
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
:title="t('views.dashboard.overview.toRouter')"
|
||||||
>
|
>
|
||||||
<div @click="fnToRouter('Sub_2010')">
|
<div @click="fnToRouter('UdmSub_2001')">
|
||||||
<UserOutlined
|
<UserOutlined
|
||||||
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
|
style="color: #4096ff; margin-right: 8px; font-size: 1.1rem"
|
||||||
/>
|
/>
|
||||||
@@ -399,11 +399,7 @@ onBeforeUnmount(() => {
|
|||||||
</div>
|
</div>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<a-menu @click="fnSelectUDM">
|
<a-menu @click="fnSelectUDM">
|
||||||
<a-menu-item
|
<a-menu-item v-for="v in udmOtions" :key="v.value" :disabled="udmNeId === v.value">
|
||||||
v-for="v in udmOtions"
|
|
||||||
:key="v.value"
|
|
||||||
:disabled="udmNeId === v.value"
|
|
||||||
>
|
|
||||||
{{ v.label }}
|
{{ v.label }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
@@ -413,7 +409,7 @@ onBeforeUnmount(() => {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="item toRouter"
|
class="item toRouter"
|
||||||
@click="fnToRouter('Ims_2080')"
|
@click="fnToRouter('ImsSub_2004')"
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
:title="t('views.dashboard.overview.toRouter')"
|
||||||
style="margin: 0 12px"
|
style="margin: 0 12px"
|
||||||
v-perms:has="['dashboard:overview:imsUeNum']"
|
v-perms:has="['dashboard:overview:imsUeNum']"
|
||||||
@@ -428,7 +424,7 @@ onBeforeUnmount(() => {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="item toRouter"
|
class="item toRouter"
|
||||||
@click="fnToRouter('Ue_2081')"
|
@click="fnToRouter('SmfSub_2005')"
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
:title="t('views.dashboard.overview.toRouter')"
|
||||||
v-perms:has="['dashboard:overview:smfUeNum']"
|
v-perms:has="['dashboard:overview:smfUeNum']"
|
||||||
>
|
>
|
||||||
@@ -452,7 +448,7 @@ onBeforeUnmount(() => {
|
|||||||
<div class="data">
|
<div class="data">
|
||||||
<div
|
<div
|
||||||
class="item toRouter"
|
class="item toRouter"
|
||||||
@click="fnToRouter('BaseStation_2096', { neType: 'AMF' })"
|
@click="fnToRouter('BaseStation_2007', { neType: 'AMF' })"
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
:title="t('views.dashboard.overview.toRouter')"
|
||||||
>
|
>
|
||||||
<div style="align-items: flex-start">
|
<div style="align-items: flex-start">
|
||||||
@@ -466,7 +462,7 @@ onBeforeUnmount(() => {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="item toRouter"
|
class="item toRouter"
|
||||||
@click="fnToRouter('BaseStation_2096', { neType: 'AMF' })"
|
@click="fnToRouter('BaseStation_2007', { neType: 'AMF' })"
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
:title="t('views.dashboard.overview.toRouter')"
|
||||||
>
|
>
|
||||||
<div style="align-items: flex-start">
|
<div style="align-items: flex-start">
|
||||||
@@ -489,7 +485,7 @@ onBeforeUnmount(() => {
|
|||||||
<div class="data">
|
<div class="data">
|
||||||
<div
|
<div
|
||||||
class="item toRouter"
|
class="item toRouter"
|
||||||
@click="fnToRouter('BaseStation_2096', { neType: 'MME' })"
|
@click="fnToRouter('BaseStation_2007', { neType: 'MME' })"
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
:title="t('views.dashboard.overview.toRouter')"
|
||||||
>
|
>
|
||||||
<div style="align-items: flex-start">
|
<div style="align-items: flex-start">
|
||||||
@@ -503,7 +499,7 @@ onBeforeUnmount(() => {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="item toRouter"
|
class="item toRouter"
|
||||||
@click="fnToRouter('BaseStation_2096', { neType: 'MME' })"
|
@click="fnToRouter('BaseStation_2007', { neType: 'MME' })"
|
||||||
:title="t('views.dashboard.overview.toRouter')"
|
:title="t('views.dashboard.overview.toRouter')"
|
||||||
>
|
>
|
||||||
<div style="align-items: flex-start">
|
<div style="align-items: flex-start">
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
showPass,
|
showPass,
|
||||||
getPass,
|
getPass,
|
||||||
exportAll,
|
exportAll,
|
||||||
|
exportAlarm,
|
||||||
} from '@/api/faultManage/actAlarm';
|
} from '@/api/faultManage/actAlarm';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import useNeInfoStore from '@/store/modules/neinfo';
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
@@ -730,6 +731,38 @@ function fnModalCancel() {
|
|||||||
modalState.helpShowView = false;
|
modalState.helpShowView = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**列表导出 */
|
||||||
|
function fnExportList() {
|
||||||
|
if (modalState.confirmLoading) return;
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.faultManage.activeAlarm.exportTip'),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
const querys = toRaw(queryParams);
|
||||||
|
exportAlarm(querys)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
saveAs(res.data, `active_alarm_export_${Date.now()}.xlsx`);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**查询列表, pageNum初始页数 */
|
/**查询列表, pageNum初始页数 */
|
||||||
function fnGetList(pageNum?: number) {
|
function fnGetList(pageNum?: number) {
|
||||||
if (tableState.loading) return;
|
if (tableState.loading) return;
|
||||||
@@ -934,6 +967,11 @@ onMounted(() => {
|
|||||||
{{ t('views.faultManage.activeAlarm.syncMyself') }}
|
{{ t('views.faultManage.activeAlarm.syncMyself') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
|
|
||||||
|
<a-button type="primary" @click.prevent="fnShowSet()" v-if="false">
|
||||||
|
<template #icon> <SettingOutlined /> </template>
|
||||||
|
{{ t('views.faultManage.activeAlarm.disPlayFilfter') }}
|
||||||
|
</a-button>
|
||||||
|
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
danger
|
danger
|
||||||
@@ -946,19 +984,9 @@ onMounted(() => {
|
|||||||
{{ t('views.faultManage.activeAlarm.clear') }}
|
{{ t('views.faultManage.activeAlarm.clear') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
|
|
||||||
<a-button type="primary" @click.prevent="fnShowSet()" v-if="false">
|
<a-button type="dashed" @click.prevent="fnExportList()">
|
||||||
<template #icon> <SettingOutlined /> </template>
|
<template #icon><ExportOutlined /></template>
|
||||||
{{ t('views.faultManage.activeAlarm.disPlayFilfter') }}
|
{{ t('common.export') }}
|
||||||
</a-button>
|
|
||||||
|
|
||||||
<a-button
|
|
||||||
type="primary"
|
|
||||||
@click.prevent="fnExportAll()"
|
|
||||||
:disabled="tableState.data.length <= 0"
|
|
||||||
v-if="false"
|
|
||||||
>
|
|
||||||
<template #icon> <export-outlined /> </template>
|
|
||||||
{{ t('views.faultManage.activeAlarm.exportAll') }}
|
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import { SizeType } from 'ant-design-vue/es/config-provider';
|
|||||||
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
import { ColumnsType } from 'ant-design-vue/es/table';
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
import {
|
import {
|
||||||
listAct,
|
|
||||||
updateConfirm,
|
updateConfirm,
|
||||||
cancelConfirm,
|
cancelConfirm,
|
||||||
exportAll,
|
exportAll,
|
||||||
} from '@/api/faultManage/historyAlarm';
|
} from '@/api/faultManage/historyAlarm';
|
||||||
|
import { listAct, exportAlarm } from '@/api/faultManage/actAlarm';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import useDictStore from '@/store/modules/dict';
|
import useDictStore from '@/store/modules/dict';
|
||||||
import useNeInfoStore from '@/store/modules/neinfo';
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
@@ -59,9 +59,6 @@ let rangePickerPresets = ref([
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**表格字段列排序 */
|
|
||||||
let tableColumnsDnd = ref<ColumnsType>([]);
|
|
||||||
|
|
||||||
/**查询参数 */
|
/**查询参数 */
|
||||||
let queryParams = reactive({
|
let queryParams = reactive({
|
||||||
alarmStatus: 0,
|
alarmStatus: 0,
|
||||||
@@ -144,7 +141,7 @@ let tableState: TabeStateType = reactive({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**表格字段列 */
|
/**表格字段列 */
|
||||||
let tableColumns: ColumnsType = [
|
let tableColumns = ref<ColumnsType>([
|
||||||
{
|
{
|
||||||
title: t('views.faultManage.activeAlarm.alarmType'),
|
title: t('views.faultManage.activeAlarm.alarmType'),
|
||||||
dataIndex: 'alarmType',
|
dataIndex: 'alarmType',
|
||||||
@@ -241,7 +238,10 @@ let tableColumns: ColumnsType = [
|
|||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
];
|
]);
|
||||||
|
|
||||||
|
/**表格字段列排序 */
|
||||||
|
let tableColumnsDnd = ref<ColumnsType>([]);
|
||||||
|
|
||||||
/**表格分页器参数 */
|
/**表格分页器参数 */
|
||||||
let tablePagination = reactive({
|
let tablePagination = reactive({
|
||||||
@@ -466,7 +466,7 @@ function mapKeysWithReduce(data: any[], titleMapping: Record<string, string>) {
|
|||||||
function fnExportAll() {
|
function fnExportAll() {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: 'Tip',
|
title: 'Tip',
|
||||||
content: t('views.faultManage.historyAlarm.exportSure'),
|
content: t('views.faultManage.activeAlarm.exportSure'),
|
||||||
onOk() {
|
onOk() {
|
||||||
const key = 'exportAlarmHis';
|
const key = 'exportAlarmHis';
|
||||||
message.loading({ content: t('common.loading'), key });
|
message.loading({ content: t('common.loading'), key });
|
||||||
@@ -543,6 +543,38 @@ function fnModalCancel() {
|
|||||||
modalState.openByShowSet = false;
|
modalState.openByShowSet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**列表导出 */
|
||||||
|
function fnExportList() {
|
||||||
|
if (modalState.confirmLoading) return;
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.faultManage.activeAlarm.exportTip'),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
const querys = toRaw(queryParams);
|
||||||
|
exportAlarm(querys)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
saveAs(res.data, `history_alarm_export_${Date.now()}.xlsx`);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**查询列表, pageNum初始页数 */
|
/**查询列表, pageNum初始页数 */
|
||||||
function fnGetList(pageNum?: number) {
|
function fnGetList(pageNum?: number) {
|
||||||
if (tableState.loading) return;
|
if (tableState.loading) return;
|
||||||
@@ -712,11 +744,17 @@ onMounted(() => {
|
|||||||
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
<!-- 插槽-卡片左侧侧 -->
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
<template #title>
|
<template #title>
|
||||||
<a-space :size="8" align="center" v-if="false">
|
<a-space :size="8" align="center">
|
||||||
|
<a-button type="dashed" @click.prevent="fnExportList()">
|
||||||
|
<template #icon><ExportOutlined /></template>
|
||||||
|
{{ t('common.export') }}
|
||||||
|
</a-button>
|
||||||
|
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
@click.prevent="fnCancelConfirm()"
|
@click.prevent="fnCancelConfirm()"
|
||||||
:disabled="state.selectedRowKeys.length <= 0"
|
:disabled="state.selectedRowKeys.length <= 0"
|
||||||
|
v-if="false"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<CloseOutlined />
|
<CloseOutlined />
|
||||||
@@ -727,6 +765,7 @@ onMounted(() => {
|
|||||||
type="primary"
|
type="primary"
|
||||||
@click.prevent="fnExportAll()"
|
@click.prevent="fnExportAll()"
|
||||||
:disabled="tableState.data.length <= 0"
|
:disabled="tableState.data.length <= 0"
|
||||||
|
v-if="false"
|
||||||
>
|
>
|
||||||
<template #icon> <export-outlined /> </template>
|
<template #icon> <export-outlined /> </template>
|
||||||
{{ t('views.faultManage.activeAlarm.exportAll') }}
|
{{ t('views.faultManage.activeAlarm.exportAll') }}
|
||||||
@@ -796,28 +835,28 @@ onMounted(() => {
|
|||||||
:scroll="{ x: tableColumns.length * 150, y: 'calc(100vh - 480px)' }"
|
:scroll="{ x: tableColumns.length * 150, y: 'calc(100vh - 480px)' }"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'origSeverity'">
|
<template v-if="column?.key === 'origSeverity'">
|
||||||
<DictTag
|
<DictTag
|
||||||
:options="dict.activeAlarmSeverity"
|
:options="dict.activeAlarmSeverity"
|
||||||
:value="record.origSeverity"
|
:value="record.origSeverity"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'alarmType'">
|
<template v-if="column?.key === 'alarmType'">
|
||||||
<DictTag
|
<DictTag
|
||||||
:options="dict.activeAlarmType"
|
:options="dict.activeAlarmType"
|
||||||
:value="record.alarmType"
|
:value="record.alarmType"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'clearType'">
|
<template v-if="column?.key === 'clearType'">
|
||||||
<DictTag
|
<DictTag
|
||||||
:options="dict.activeClearType"
|
:options="dict.activeClearType"
|
||||||
:value="record.clearType"
|
:value="record.clearType"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'ackState'">
|
<template v-if="column?.key === 'ackState'">
|
||||||
<DictTag :options="dict.activeAckState" :value="record.ackState" />
|
<DictTag :options="dict.activeAckState" :value="record.ackState" />
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'id'">
|
<template v-if="column?.key === 'id'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template #title>{{ t('common.viewText') }}</template>
|
<template #title>{{ t('common.viewText') }}</template>
|
||||||
|
|||||||
@@ -1,38 +1,57 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, ref, onMounted, toRaw } from 'vue';
|
import { reactive, ref, toRaw } from 'vue';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { ProModal } from 'antdv-pro-modal';
|
|
||||||
import { SizeType } from 'ant-design-vue/es/config-provider';
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
import { ColumnsType } from 'ant-design-vue/es/table';
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
import { Form, Modal, message } from 'ant-design-vue/es';
|
import { Modal, message } from 'ant-design-vue/es';
|
||||||
|
import BackupModal from '@/views/ne/neConfigBackup/components/BackupModal.vue';
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
import {
|
|
||||||
getBakFile,
|
|
||||||
getBakFileList,
|
|
||||||
downFile,
|
|
||||||
delFile,
|
|
||||||
updateFTPInfo,
|
|
||||||
getFTPInfo,
|
|
||||||
putFTPInfo,
|
|
||||||
} from '@/api/logManage/exportFile';
|
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import saveAs from 'file-saver';
|
import saveAs from 'file-saver';
|
||||||
import { regExpIPv4 } from '@/utils/regular-utils';
|
import { delFile, getFile, listFile } from '@/api/tool/file';
|
||||||
|
import { parseSizeFromFile } from '@/utils/parse-utils';
|
||||||
|
import { pushBackupFTP } from '@/api/neData/backup';
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
/**网元参数 */
|
/**文件来源 */
|
||||||
let logSelect = ref<string[]>([]);
|
let sourceState = reactive({
|
||||||
|
/**文件列表 */
|
||||||
/**文件列表 */
|
list: [
|
||||||
let fileList = ref<any>([]);
|
{
|
||||||
|
value: '/log/operate_log',
|
||||||
|
label: t('views.logManage.exportFile.operateLog'),
|
||||||
|
path: '/usr/local/omc/backup',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '/cdr/ims_cdr',
|
||||||
|
label: t('views.logManage.exportFile.cdrIMS'),
|
||||||
|
path: '/usr/local/omc/backup',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '/cdr/smf_cdr',
|
||||||
|
label: t('views.logManage.exportFile.cdrSMF'),
|
||||||
|
path: '/usr/local/omc/backup',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '/cdr/smsc_cdr',
|
||||||
|
label: t('views.logManage.exportFile.cdrSMSC'),
|
||||||
|
path: '/usr/local/omc/backup',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '/cdr/sgwc_cdr',
|
||||||
|
label: t('views.logManage.exportFile.cdrSGWC'),
|
||||||
|
path: '/usr/local/omc/backup',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
/**选择value */
|
||||||
|
value: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
/**查询参数 */
|
/**查询参数 */
|
||||||
let queryParams = reactive({
|
let queryParams = reactive({
|
||||||
/**读取路径 */
|
/**读取路径 */
|
||||||
path: '',
|
path: '',
|
||||||
/**表名 */
|
|
||||||
tableName: '',
|
|
||||||
/**当前页数 */
|
/**当前页数 */
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
/**每页条数 */
|
/**每页条数 */
|
||||||
@@ -80,6 +99,9 @@ let tableColumns: ColumnsType = [
|
|||||||
title: t('views.logManage.neFile.size'),
|
title: t('views.logManage.neFile.size'),
|
||||||
dataIndex: 'size',
|
dataIndex: 'size',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
customRender(opt) {
|
||||||
|
return parseSizeFromFile(opt.value);
|
||||||
|
},
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -88,9 +110,9 @@ let tableColumns: ColumnsType = [
|
|||||||
align: 'left',
|
align: 'left',
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
if (!opt.value) return '';
|
if (!opt.value) return '';
|
||||||
return parseDateToStr(opt.value * 1000);
|
return parseDateToStr(opt.value);
|
||||||
},
|
},
|
||||||
width: 150,
|
width: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.logManage.neFile.fileName'),
|
title: t('views.logManage.neFile.fileName'),
|
||||||
@@ -135,10 +157,6 @@ let tablePagination = reactive({
|
|||||||
|
|
||||||
/**下载触发等待 */
|
/**下载触发等待 */
|
||||||
let downLoading = ref<boolean>(false);
|
let downLoading = ref<boolean>(false);
|
||||||
|
|
||||||
/**删除触发等待 */
|
|
||||||
let delLoading = ref<boolean>(false);
|
|
||||||
|
|
||||||
/**信息文件下载 */
|
/**信息文件下载 */
|
||||||
function fnDownloadFile(row: Record<string, any>) {
|
function fnDownloadFile(row: Record<string, any>) {
|
||||||
if (downLoading.value) return;
|
if (downLoading.value) return;
|
||||||
@@ -150,10 +168,7 @@ function fnDownloadFile(row: Record<string, any>) {
|
|||||||
onOk() {
|
onOk() {
|
||||||
downLoading.value = true;
|
downLoading.value = true;
|
||||||
const hide = message.loading(t('common.loading'), 0);
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
downFile({
|
getFile(queryParams.path, row.fileName)
|
||||||
path: queryParams.path,
|
|
||||||
fileName: row.fileName,
|
|
||||||
})
|
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
message.success({
|
message.success({
|
||||||
@@ -178,6 +193,9 @@ function fnDownloadFile(row: Record<string, any>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**删除触发等待 */
|
||||||
|
let delLoading = ref<boolean>(false);
|
||||||
|
/**信息文件删除 */
|
||||||
function fnRecordDelete(row: Record<string, any>) {
|
function fnRecordDelete(row: Record<string, any>) {
|
||||||
if (delLoading.value) return;
|
if (delLoading.value) return;
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
@@ -186,30 +204,27 @@ function fnRecordDelete(row: Record<string, any>) {
|
|||||||
fileName: row.fileName,
|
fileName: row.fileName,
|
||||||
}),
|
}),
|
||||||
onOk() {
|
onOk() {
|
||||||
const key = 'delFile';
|
|
||||||
delLoading.value = true;
|
delLoading.value = true;
|
||||||
message.loading({ content: t('common.loading'), key });
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
delFile({
|
delFile(queryParams.path, row.fileName)
|
||||||
fileName: row.fileName,
|
|
||||||
path: queryParams.path,
|
|
||||||
})
|
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
message.success({
|
message.success({
|
||||||
content: t('views.system.user.delSuss'),
|
content: t('common.msgSuccess', {
|
||||||
key,
|
msg: t('common.deleteText'),
|
||||||
|
}),
|
||||||
duration: 2,
|
duration: 2,
|
||||||
});
|
});
|
||||||
fnGetList();
|
fnGetList();
|
||||||
} else {
|
} else {
|
||||||
message.error({
|
message.error({
|
||||||
content: `${res.msg}`,
|
content: t('views.logManage.exportFile.deleteTipErr'),
|
||||||
key: key,
|
|
||||||
duration: 2,
|
duration: 2,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
|
hide();
|
||||||
delLoading.value = false;
|
delLoading.value = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -217,17 +232,18 @@ function fnRecordDelete(row: Record<string, any>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**网元类型选择对应修改 */
|
/**网元类型选择对应修改 */
|
||||||
function fnNeChange(keys: any, opt: any) {
|
function fnNeChange(_: any, opt: any) {
|
||||||
queryParams.tableName = keys;
|
queryParams.path = `${opt.path}${opt.value}`;
|
||||||
queryParams.path = opt.path;
|
ftpInfo.path = queryParams.path;
|
||||||
|
ftpInfo.tag = opt.value;
|
||||||
fnGetList(1);
|
fnGetList(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**查询备份信息列表, pageNum初始页数 */
|
/**查询备份信息列表, pageNum初始页数 */
|
||||||
function fnGetList(pageNum?: number) {
|
function fnGetList(pageNum?: number) {
|
||||||
if (queryParams.tableName === '') {
|
if (queryParams.path === '') {
|
||||||
message.warning({
|
message.warning({
|
||||||
content: t('views.logManage.exportFile.selectTip'),
|
content: t('views.logManage.exportFile.fileSourcePlease'),
|
||||||
duration: 2,
|
duration: 2,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -237,7 +253,7 @@ function fnGetList(pageNum?: number) {
|
|||||||
if (pageNum) {
|
if (pageNum) {
|
||||||
queryParams.pageNum = pageNum;
|
queryParams.pageNum = pageNum;
|
||||||
}
|
}
|
||||||
getBakFileList(toRaw(queryParams)).then(res => {
|
listFile(toRaw(queryParams)).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
const { total, rows } = res.data;
|
const { total, rows } = res.data;
|
||||||
tablePagination.total = total;
|
tablePagination.total = total;
|
||||||
@@ -259,147 +275,34 @@ function fnGetList(pageNum?: number) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
/**打开FTP配置窗口 */
|
||||||
getBakFile().then(res => {
|
const openFTPModal = ref<boolean>(false);
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
function fnFTPModalOpen() {
|
||||||
res.data.forEach((item: any) => {
|
openFTPModal.value = !openFTPModal.value;
|
||||||
fileList.value.push({
|
}
|
||||||
value: item.tableName,
|
|
||||||
label: item.tableDisplay,
|
|
||||||
path: item.filePath,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// .finally(() => {
|
|
||||||
// fnGetList();
|
|
||||||
// });
|
|
||||||
});
|
|
||||||
|
|
||||||
/**对象信息状态类型 */
|
type FTPInfoType = {
|
||||||
type ModalStateType = {
|
path: string;
|
||||||
/**新增框或修改框是否显示 */
|
tag: string;
|
||||||
openByEdit: boolean;
|
fileName: string;
|
||||||
/**标题 */
|
|
||||||
title: string;
|
|
||||||
/**表单数据 */
|
|
||||||
from: Record<string, any>;
|
|
||||||
/**确定按钮 loading */
|
|
||||||
confirmLoading: boolean;
|
|
||||||
};
|
};
|
||||||
|
const ftpInfo = reactive<FTPInfoType>({
|
||||||
/**FTP日志对象信息状态 */
|
path: '',
|
||||||
let modalState: ModalStateType = reactive({
|
tag: '',
|
||||||
openByEdit: false,
|
fileName: '',
|
||||||
title: '设置远程备份配置',
|
|
||||||
from: {
|
|
||||||
username: '',
|
|
||||||
password: '',
|
|
||||||
toIp: '',
|
|
||||||
toPort: 22,
|
|
||||||
enable: false,
|
|
||||||
dir: '',
|
|
||||||
},
|
|
||||||
confirmLoading: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**FTP日志对象信息内表单属性和校验规则 */
|
/**同步文件到FTP */
|
||||||
const modalStateFrom = Form.useForm(
|
function fnSyncFileToFTP(fileName: string) {
|
||||||
modalState.from,
|
ftpInfo.fileName = fileName;
|
||||||
reactive({
|
pushBackupFTP(toRaw(ftpInfo)).then(res => {
|
||||||
toIp: [
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
{
|
message.success(t('common.operateOk'), 3);
|
||||||
required: true,
|
|
||||||
pattern: regExpIPv4,
|
|
||||||
message: 'Please enter the service login IP',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
username: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
trigger: 'blur',
|
|
||||||
message: 'Please enter the service login user name',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
dir: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
trigger: 'blur',
|
|
||||||
message: 'Please enter the service address target file directory',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对话框弹出显示为 新增或者修改
|
|
||||||
* @param configId 参数编号id, 不传为新增
|
|
||||||
*/
|
|
||||||
function fnModalVisibleByEdit() {
|
|
||||||
if (modalState.confirmLoading) return;
|
|
||||||
const hide = message.loading(t('common.loading'), 0);
|
|
||||||
modalState.confirmLoading = true;
|
|
||||||
getFTPInfo().then(res => {
|
|
||||||
modalState.confirmLoading = false;
|
|
||||||
hide();
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
|
||||||
modalState.from = Object.assign(modalState.from, res.data);
|
|
||||||
modalState.title = 'Setting Remote Backup';
|
|
||||||
modalState.openByEdit = true;
|
|
||||||
} else {
|
} else {
|
||||||
message.error(res.msg, 3);
|
message.warning(res.msg, 3);
|
||||||
modalState.title = 'Setting Remote Backup';
|
|
||||||
modalState.openByEdit = false;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**FTP对象保存 */
|
|
||||||
function fnModalOk() {
|
|
||||||
modalStateFrom.validate().then(() => {
|
|
||||||
modalState.confirmLoading = true;
|
|
||||||
const from = toRaw(modalState.from);
|
|
||||||
updateFTPInfo(from)
|
|
||||||
.then(res => {
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
|
||||||
message.success(`Configuration saved successfully`, 3);
|
|
||||||
fnModalCancel();
|
|
||||||
} else {
|
|
||||||
message.warning(`Configuration save exception`, 3);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
modalState.confirmLoading = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对话框弹出关闭执行函数
|
|
||||||
* 进行表达规则校验
|
|
||||||
*/
|
|
||||||
function fnModalCancel() {
|
|
||||||
modalState.openByEdit = false;
|
|
||||||
modalStateFrom.resetFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步文件到FTP
|
|
||||||
* @param row
|
|
||||||
*/
|
|
||||||
function fnSyncFileToFTP(row: Record<string, any>) {
|
|
||||||
putFTPInfo(row.filePath, row.fileName)
|
|
||||||
.then(res => {
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
|
||||||
message.success(t('common.operateOk'), 3);
|
|
||||||
} else {
|
|
||||||
message.warning(res.msg, 3);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
modalState.confirmLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -410,12 +313,14 @@ function fnSyncFileToFTP(row: Record<string, any>) {
|
|||||||
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||||
<a-row :gutter="16" align="middle">
|
<a-row :gutter="16" align="middle">
|
||||||
<a-col>
|
<a-col>
|
||||||
<span>{{ t('views.logManage.exportFile.fileName') }}:</span>
|
<span>{{ t('views.logManage.exportFile.fileSource') }}:</span
|
||||||
|
>
|
||||||
<a-select
|
<a-select
|
||||||
v-model:value="logSelect"
|
v-model:value="sourceState.value"
|
||||||
:options="fileList"
|
:options="sourceState.list"
|
||||||
@change="fnNeChange"
|
@change="fnNeChange"
|
||||||
:allow-clear="false"
|
:allow-clear="false"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
style="width: 200px"
|
style="width: 200px"
|
||||||
/>
|
/>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -436,9 +341,11 @@ function fnSyncFileToFTP(row: Record<string, any>) {
|
|||||||
<!-- 插槽-卡片右侧 -->
|
<!-- 插槽-卡片右侧 -->
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
<a-tooltip>
|
<a-tooltip placement="topRight">
|
||||||
<template #title>Setting Remote Backup</template>
|
<template #title>
|
||||||
<a-button type="text" @click.prevent="fnModalVisibleByEdit()">
|
{{ t('views.ne.neConfigBackup.backupModal.title') }}
|
||||||
|
</template>
|
||||||
|
<a-button type="text" @click.prevent="fnFTPModalOpen()">
|
||||||
<template #icon><DeliveredProcedureOutlined /></template>
|
<template #icon><DeliveredProcedureOutlined /></template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
@@ -465,135 +372,45 @@ function fnSyncFileToFTP(row: Record<string, any>) {
|
|||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'fileName'">
|
<template v-if="column.key === 'fileName'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
<a-button
|
<a-tooltip placement="topRight" v-if="record.fileType === 'file'">
|
||||||
type="link"
|
<template #title>
|
||||||
:loading="downLoading"
|
{{ t('views.ne.neConfigBackup.backupModal.pushFileOper') }}
|
||||||
@click.prevent="fnSyncFileToFTP(record)"
|
</template>
|
||||||
v-if="record.fileType === 'file'"
|
<a-button
|
||||||
>
|
type="link"
|
||||||
<template #icon><CloudServerOutlined /></template>
|
@click.prevent="fnSyncFileToFTP(record.fileName)"
|
||||||
</a-button>
|
>
|
||||||
<a-button
|
<template #icon><CloudServerOutlined /></template>
|
||||||
type="link"
|
</a-button>
|
||||||
:loading="downLoading"
|
</a-tooltip>
|
||||||
@click.prevent="fnDownloadFile(record)"
|
<a-tooltip placement="topRight" v-if="record.fileType === 'file'">
|
||||||
v-if="record.fileType === 'file'"
|
<template #title>{{ t('common.downloadText') }}</template>
|
||||||
>
|
<a-button
|
||||||
<template #icon><DownloadOutlined /></template>
|
type="link"
|
||||||
</a-button>
|
:loading="downLoading"
|
||||||
<a-button
|
@click.prevent="fnDownloadFile(record)"
|
||||||
type="link"
|
>
|
||||||
:loading="delLoading"
|
<template #icon><DownloadOutlined /></template>
|
||||||
@click.prevent="fnRecordDelete(record)"
|
</a-button>
|
||||||
v-if="record.fileType === 'file'"
|
</a-tooltip>
|
||||||
>
|
<a-tooltip placement="topRight" v-if="record.fileType === 'file'">
|
||||||
<template #icon><DeleteOutlined /></template>
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
</a-button>
|
<a-button
|
||||||
|
type="link"
|
||||||
|
:loading="delLoading"
|
||||||
|
@click.prevent="fnRecordDelete(record)"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
<!-- 新增框或修改框 -->
|
<!-- FTP配置窗口 -->
|
||||||
<ProModal
|
<BackupModal v-model:open="openFTPModal"></BackupModal>
|
||||||
:drag="true"
|
|
||||||
:width="800"
|
|
||||||
:destroyOnClose="true"
|
|
||||||
:keyboard="false"
|
|
||||||
:mask-closable="false"
|
|
||||||
:open="modalState.openByEdit"
|
|
||||||
:title="modalState.title"
|
|
||||||
:confirm-loading="modalState.confirmLoading"
|
|
||||||
@ok="fnModalOk"
|
|
||||||
@cancel="fnModalCancel"
|
|
||||||
>
|
|
||||||
<a-form
|
|
||||||
name="modalStateFrom"
|
|
||||||
layout="horizontal"
|
|
||||||
:label-col="{ span: 6 }"
|
|
||||||
:label-wrap="true"
|
|
||||||
>
|
|
||||||
<a-form-item label="Enable" name="enable" :label-col="{ span: 3 }">
|
|
||||||
<a-switch
|
|
||||||
v-model:checked="modalState.from.enable"
|
|
||||||
:checked-children="t('common.switch.open')"
|
|
||||||
:un-checked-children="t('common.switch.shut')"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<template v-if="modalState.from.enable">
|
|
||||||
<a-row>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
label="Service IP"
|
|
||||||
name="toIp"
|
|
||||||
v-bind="modalStateFrom.validateInfos.toIp"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="modalState.from.toIp"
|
|
||||||
allow-clear
|
|
||||||
:placeholder="t('common.inputPlease')"
|
|
||||||
></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
label="Service Port"
|
|
||||||
name="toPort"
|
|
||||||
v-bind="modalStateFrom.validateInfos.toPort"
|
|
||||||
>
|
|
||||||
<a-input-number
|
|
||||||
v-model:value="modalState.from.toPort"
|
|
||||||
allow-clear
|
|
||||||
:placeholder="t('common.inputPlease')"
|
|
||||||
></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
<a-row>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
label="UserName"
|
|
||||||
name="username"
|
|
||||||
v-bind="modalStateFrom.validateInfos.username"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="modalState.from.username"
|
|
||||||
allow-clear
|
|
||||||
:placeholder="t('common.inputPlease')"
|
|
||||||
></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
label="Password"
|
|
||||||
name="password"
|
|
||||||
v-bind="modalStateFrom.validateInfos.password"
|
|
||||||
>
|
|
||||||
<a-input-password
|
|
||||||
v-model:value="modalState.from.password"
|
|
||||||
allow-clear
|
|
||||||
:placeholder="t('common.inputPlease')"
|
|
||||||
></a-input-password>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
<a-form-item
|
|
||||||
label="Save Dir"
|
|
||||||
name="dir"
|
|
||||||
v-bind="modalStateFrom.validateInfos.dir"
|
|
||||||
:label-col="{ span: 3 }"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="modalState.from.dir"
|
|
||||||
allow-clear
|
|
||||||
:placeholder="t('common.inputPlease')"
|
|
||||||
></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</template>
|
|
||||||
</a-form>
|
|
||||||
</ProModal>
|
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ let tableColumns: ColumnsType = reactive([
|
|||||||
title: t('views.logManage.mml.logTime'),
|
title: t('views.logManage.mml.logTime'),
|
||||||
dataIndex: 'logTime',
|
dataIndex: 'logTime',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 150,
|
width: 200,
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
if (!opt.value) return '';
|
if (!opt.value) return '';
|
||||||
return parseDateToStr(opt.value);
|
return parseDateToStr(opt.value);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { message } from 'ant-design-vue/es';
|
|||||||
import { reactive, onMounted, computed, toRaw } from 'vue';
|
import { reactive, onMounted, computed, toRaw } from 'vue';
|
||||||
import useUserStore from '@/store/modules/user';
|
import useUserStore from '@/store/modules/user';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import { getCaptchaImage } from '@/api/login';
|
import { getCaptchaImage } from '@/api/auth';
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import useLayoutStore from '@/store/modules/layout';
|
import useLayoutStore from '@/store/modules/layout';
|
||||||
@@ -75,18 +75,28 @@ function fnFinish() {
|
|||||||
function fnGetCaptcha() {
|
function fnGetCaptcha() {
|
||||||
if (state.captchaClick) return;
|
if (state.captchaClick) return;
|
||||||
state.captchaClick = true;
|
state.captchaClick = true;
|
||||||
getCaptchaImage().then(res => {
|
getCaptchaImage()
|
||||||
state.captchaClick = false;
|
.then(res => {
|
||||||
if (res.code != 1) {
|
if (res.code !== RESULT_CODE_SUCCESS) {
|
||||||
message.warning(`${res.msg}`, 3);
|
message.warning({
|
||||||
return;
|
content: `${res.msg}`,
|
||||||
}
|
duration: 3,
|
||||||
state.captcha.enabled = Boolean(res.captchaEnabled);
|
});
|
||||||
if (state.captcha.enabled) {
|
return;
|
||||||
state.captcha.codeImg = res.img;
|
}
|
||||||
state.from.uuid = res.uuid;
|
const { enabled, img, uuid } = res.data;
|
||||||
}
|
state.captcha.enabled = Boolean(enabled);
|
||||||
});
|
if (state.captcha.enabled) {
|
||||||
|
state.captcha.codeImg = img;
|
||||||
|
state.from.uuid = uuid;
|
||||||
|
}
|
||||||
|
if (res.data?.text) {
|
||||||
|
state.from.code = res.data.text;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
state.captchaClick = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否有背景地址
|
// 判断是否有背景地址
|
||||||
|
|||||||
@@ -181,9 +181,9 @@ function fnSendMML() {
|
|||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
let resultArr = res.data;
|
let resultArr = res.data;
|
||||||
for (let i = 0; i < resultArr.length; i++) {
|
for (let i = 0; i < resultArr.length; i++) {
|
||||||
const str = resultArr[i];
|
const str = resultArr[i] || '';
|
||||||
const logStr = str.replace(/(\r\n|\n)/g, '\n');
|
const logStr = str.replace(/(\r\n|\n)/g, '\n');
|
||||||
const cmdStr = cmdArr[i];
|
const cmdStr = cmdArr[i] || '';
|
||||||
state.mmlCmdLog += `${cmdStr}\n${logStr}\n`;
|
state.mmlCmdLog += `${cmdStr}\n${logStr}\n`;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -183,9 +183,9 @@ function fnSendMML() {
|
|||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
let resultArr = res.data;
|
let resultArr = res.data;
|
||||||
for (let i = 0; i < resultArr.length; i++) {
|
for (let i = 0; i < resultArr.length; i++) {
|
||||||
const str = resultArr[i];
|
const str = resultArr[i] || '';
|
||||||
const logStr = str.replace(/(\r\n|\n)/g, '\n');
|
const logStr = str.replace(/(\r\n|\n)/g, '\n');
|
||||||
const cmdStr = cmdArr[i];
|
const cmdStr = cmdArr[i] || '';
|
||||||
state.mmlCmdLog += `${cmdStr}\n${logStr}\n`;
|
state.mmlCmdLog += `${cmdStr}\n${logStr}\n`;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -183,9 +183,9 @@ function fnSendMML() {
|
|||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
let resultArr = res.data;
|
let resultArr = res.data;
|
||||||
for (let i = 0; i < resultArr.length; i++) {
|
for (let i = 0; i < resultArr.length; i++) {
|
||||||
const str = resultArr[i] || "";
|
const str = resultArr[i] || '';
|
||||||
const logStr = str.replace(/(\r\n|\n)/g, '\n');
|
const logStr = str.replace(/(\r\n|\n)/g, '\n');
|
||||||
const cmdStr = cmdArr[i] || "";
|
const cmdStr = cmdArr[i] || '';
|
||||||
state.mmlCmdLog += `${cmdStr}\n${logStr}\n`;
|
state.mmlCmdLog += `${cmdStr}\n${logStr}\n`;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1100,7 +1100,7 @@ onMounted(() => {
|
|||||||
<a-textarea
|
<a-textarea
|
||||||
v-model:value="modalState.from.targetParams"
|
v-model:value="modalState.from.targetParams"
|
||||||
:auto-size="{ minRows: 2, maxRows: 6 }"
|
:auto-size="{ minRows: 2, maxRows: 6 }"
|
||||||
:maxlength="400"
|
:maxlength="2000"
|
||||||
:placeholder="t('views.monitor.job.targetParamsPlease')"
|
:placeholder="t('views.monitor.job.targetParamsPlease')"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|||||||
@@ -118,8 +118,8 @@ let tableState: TabeStateType = reactive({
|
|||||||
let tableColumns: ColumnsType = [
|
let tableColumns: ColumnsType = [
|
||||||
{
|
{
|
||||||
title: t('common.rowId'),
|
title: t('common.rowId'),
|
||||||
dataIndex: 'jobLogId',
|
dataIndex: 'logId',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -132,7 +132,7 @@ let tableColumns: ColumnsType = [
|
|||||||
title: t('views.monitor.jobLog.jobGroup'),
|
title: t('views.monitor.jobLog.jobGroup'),
|
||||||
dataIndex: 'jobGroup',
|
dataIndex: 'jobGroup',
|
||||||
key: 'jobGroup',
|
key: 'jobGroup',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -142,17 +142,17 @@ let tableColumns: ColumnsType = [
|
|||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.monitor.jobLog.statusFlag'),
|
title: t('views.monitor.jobLog.status'),
|
||||||
dataIndex: 'statusFlag',
|
dataIndex: 'statusFlag',
|
||||||
key: 'statusFlag',
|
key: 'statusFlag',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.monitor.jobLog.createTime'),
|
title: t('views.monitor.jobLog.createTime'),
|
||||||
dataIndex: 'createTime',
|
dataIndex: 'createTime',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
width: 150,
|
width: 200,
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
if (+opt.value <= 0) return '';
|
if (+opt.value <= 0) return '';
|
||||||
return parseDateToStr(+opt.value);
|
return parseDateToStr(+opt.value);
|
||||||
@@ -170,7 +170,7 @@ let tableColumns: ColumnsType = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('common.operate'),
|
title: t('common.operate'),
|
||||||
key: 'jobLogId',
|
key: 'logId',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -229,7 +229,7 @@ let modalState: ModalStateType = reactive({
|
|||||||
open: false,
|
open: false,
|
||||||
title: '任务日志',
|
title: '任务日志',
|
||||||
from: {
|
from: {
|
||||||
jobLogId: undefined,
|
logId: undefined,
|
||||||
jobName: '',
|
jobName: '',
|
||||||
jobGroup: 'DEFAULT',
|
jobGroup: 'DEFAULT',
|
||||||
invokeTarget: '',
|
invokeTarget: '',
|
||||||
@@ -588,7 +588,7 @@ onMounted(() => {
|
|||||||
<!-- 表格列表 -->
|
<!-- 表格列表 -->
|
||||||
<a-table
|
<a-table
|
||||||
class="table"
|
class="table"
|
||||||
row-key="jobLogId"
|
row-key="logId"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
:loading="tableState.loading"
|
:loading="tableState.loading"
|
||||||
:data-source="tableState.data"
|
:data-source="tableState.data"
|
||||||
@@ -615,7 +615,7 @@ onMounted(() => {
|
|||||||
}}
|
}}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'jobLogId'">
|
<template v-if="column.key === 'logId'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template #title>{{ t('common.viewText') }}</template>
|
<template #title>{{ t('common.viewText') }}</template>
|
||||||
@@ -644,8 +644,8 @@ onMounted(() => {
|
|||||||
<a-form layout="horizontal" :label-col="{ span: 6 }" :label-wrap="true">
|
<a-form layout="horizontal" :label-col="{ span: 6 }" :label-wrap="true">
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item :label="t('common.rowId')" name="jobLogId">
|
<a-form-item :label="t('common.rowId')" name="logId">
|
||||||
{{ modalState.from.jobLogId }}
|
{{ modalState.from.logId }}
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
|||||||
230
src/views/ne/neConfigBackup/components/BackupModal.vue
Normal file
230
src/views/ne/neConfigBackup/components/BackupModal.vue
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, watch, toRaw } from 'vue';
|
||||||
|
import { ProModal } from 'antdv-pro-modal';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { Form, message } from 'ant-design-vue';
|
||||||
|
import { regExpIPv4 } from '@/utils/regular-utils';
|
||||||
|
import { getBackupFTP, updateBackupFTP } from '@/api/neData/backup';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
const { t } = useI18n();
|
||||||
|
const emit = defineEmits(['ok', 'cancel', 'update:open']);
|
||||||
|
const props = defineProps({
|
||||||
|
open: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type StateType = {
|
||||||
|
/**框是否显示 */
|
||||||
|
open: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**查看命令 */
|
||||||
|
form: Record<string, any>;
|
||||||
|
/**等待 */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let state: StateType = reactive({
|
||||||
|
open: false,
|
||||||
|
title: '设置远程备份配置',
|
||||||
|
form: {
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
toIp: '',
|
||||||
|
toPort: 22,
|
||||||
|
enable: false,
|
||||||
|
dir: '',
|
||||||
|
},
|
||||||
|
confirmLoading: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**FTP对象信息内表单属性和校验规则 */
|
||||||
|
const stateFrom = Form.useForm(
|
||||||
|
state.form,
|
||||||
|
reactive({
|
||||||
|
toIp: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
pattern: regExpIPv4,
|
||||||
|
message: t('views.ne.neConfigBackup.backupModal.toIpPleace'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
username: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
trigger: 'blur',
|
||||||
|
message: t('views.ne.neConfigBackup.backupModal.usernamePleace'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
dir: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
trigger: 'blur',
|
||||||
|
message: t('views.ne.neConfigBackup.backupModal.dirPleace'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**对象保存 */
|
||||||
|
function fnModalOk() {
|
||||||
|
stateFrom.validate().then(() => {
|
||||||
|
state.confirmLoading = true;
|
||||||
|
const from = toRaw(state.form);
|
||||||
|
updateBackupFTP(from)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success(t('common.operateOk'), 3);
|
||||||
|
fnModalCancel();
|
||||||
|
} else {
|
||||||
|
message.warning(t('common.operateErr'), 3);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
state.confirmLoading = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出关闭执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalCancel() {
|
||||||
|
stateFrom.resetFields();
|
||||||
|
state.open = false;
|
||||||
|
emit('cancel');
|
||||||
|
emit('update:open', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出显示为 新增或者修改
|
||||||
|
*/
|
||||||
|
function fnModalVisible() {
|
||||||
|
if (state.confirmLoading) return;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
state.confirmLoading = true;
|
||||||
|
getBackupFTP()
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
||||||
|
state.form = Object.assign(state.form, res.data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
state.confirmLoading = false;
|
||||||
|
hide();
|
||||||
|
state.title = t('views.ne.neConfigBackup.backupModal.title');
|
||||||
|
state.open = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**监听是否显示,初始数据 */
|
||||||
|
watch(
|
||||||
|
() => props.open,
|
||||||
|
val => {
|
||||||
|
if (val) {
|
||||||
|
fnModalVisible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ProModal
|
||||||
|
:drag="true"
|
||||||
|
:width="520"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
:keyboard="false"
|
||||||
|
:mask-closable="false"
|
||||||
|
:open="state.open"
|
||||||
|
:title="state.title"
|
||||||
|
:confirm-loading="state.confirmLoading"
|
||||||
|
@ok="fnModalOk"
|
||||||
|
@cancel="fnModalCancel"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
name="modalStateFTPFrom"
|
||||||
|
layout="horizontal"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neConfigBackup.backupModal.enable')"
|
||||||
|
name="enable"
|
||||||
|
>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="state.form.enable"
|
||||||
|
:checked-children="t('common.switch.open')"
|
||||||
|
:un-checked-children="t('common.switch.shut')"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<template v-if="state.form.enable">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neConfigBackup.backupModal.toIp')"
|
||||||
|
name="toIp"
|
||||||
|
v-bind="stateFrom.validateInfos.toIp"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="state.form.toIp"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neConfigBackup.backupModal.toPort')"
|
||||||
|
name="toPort"
|
||||||
|
v-bind="stateFrom.validateInfos.toPort"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="state.form.toPort"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neConfigBackup.backupModal.username')"
|
||||||
|
name="username"
|
||||||
|
v-bind="stateFrom.validateInfos.username"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="state.form.username"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neConfigBackup.backupModal.password')"
|
||||||
|
name="password"
|
||||||
|
v-bind="stateFrom.validateInfos.password"
|
||||||
|
>
|
||||||
|
<a-input-password
|
||||||
|
v-model:value="state.form.password"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input-password>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.ne.neConfigBackup.backupModal.dir')"
|
||||||
|
name="dir"
|
||||||
|
v-bind="stateFrom.validateInfos.dir"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="state.form.dir"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
</a-form>
|
||||||
|
</ProModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -5,22 +5,20 @@ import { ProModal } from 'antdv-pro-modal';
|
|||||||
import { Form, Modal, TableColumnsType, message } from 'ant-design-vue/es';
|
import { Form, Modal, TableColumnsType, message } from 'ant-design-vue/es';
|
||||||
import { SizeType } from 'ant-design-vue/es/config-provider';
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
|
import BackupModal from './components/BackupModal.vue';
|
||||||
import useNeInfoStore from '@/store/modules/neinfo';
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import useDictStore from '@/store/modules/dict';
|
import useDictStore from '@/store/modules/dict';
|
||||||
import { NE_TYPE_LIST } from '@/constants/ne-constants';
|
import { NE_TYPE_LIST } from '@/constants/ne-constants';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
import { regExpIPv4 } from '@/utils/regular-utils';
|
|
||||||
import {
|
import {
|
||||||
delNeConfigBackup,
|
delNeConfigBackup,
|
||||||
downNeConfigBackup,
|
downNeConfigBackup,
|
||||||
listNeConfigBackup,
|
listNeConfigBackup,
|
||||||
updateNeConfigBackup,
|
updateNeConfigBackup,
|
||||||
getFTPInfo,
|
|
||||||
putFTPInfo,
|
|
||||||
updateFTPInfo,
|
|
||||||
} from '@/api/ne/neConfigBackup';
|
} from '@/api/ne/neConfigBackup';
|
||||||
|
import { pushBackupFTP } from '@/api/neData/backup';
|
||||||
import saveAs from 'file-saver';
|
import saveAs from 'file-saver';
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { getDict } = useDictStore();
|
const { getDict } = useDictStore();
|
||||||
@@ -389,119 +387,26 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/**FTP日志对象信息状态 */
|
/**打开FTP配置窗口 */
|
||||||
let modalStateFTP: ModalStateType = reactive({
|
const openFTPModal = ref<boolean>(false);
|
||||||
openByEdit: false,
|
function fnFTPModalOpen() {
|
||||||
title: '设置远程备份配置',
|
openFTPModal.value = !openFTPModal.value;
|
||||||
from: {
|
}
|
||||||
username: '',
|
|
||||||
password: '',
|
|
||||||
toIp: '',
|
|
||||||
toPort: 22,
|
|
||||||
enable: false,
|
|
||||||
dir: '',
|
|
||||||
},
|
|
||||||
confirmLoading: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**FTP日志对象信息内表单属性和校验规则 */
|
/**同步文件到FTP */
|
||||||
const modalStateFTPFrom = Form.useForm(
|
function fnSyncFileToFTP(row: Record<string, any>) {
|
||||||
modalStateFTP.from,
|
pushBackupFTP({
|
||||||
reactive({
|
path: row.path.substring(0, row.path.lastIndexOf('/')),
|
||||||
toIp: [
|
fileName: row.name,
|
||||||
{
|
tag: 'ne_config',
|
||||||
required: true,
|
}).then(res => {
|
||||||
pattern: regExpIPv4,
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
message: 'Please enter the service login IP',
|
message.success(t('common.operateOk'), 3);
|
||||||
},
|
|
||||||
],
|
|
||||||
username: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
trigger: 'blur',
|
|
||||||
message: 'Please enter the service login user name',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
dir: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
trigger: 'blur',
|
|
||||||
message: 'Please enter the service address target file directory',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对话框弹出显示为 新增或者修改
|
|
||||||
* @param configId 参数编号id, 不传为新增
|
|
||||||
*/
|
|
||||||
function fnModalFTPVisibleByEdit() {
|
|
||||||
if (modalStateFTP.confirmLoading) return;
|
|
||||||
const hide = message.loading(t('common.loading'), 0);
|
|
||||||
modalStateFTP.confirmLoading = true;
|
|
||||||
getFTPInfo().then(res => {
|
|
||||||
modalStateFTP.confirmLoading = false;
|
|
||||||
hide();
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
|
||||||
modalStateFTP.from = Object.assign(modalStateFTP.from, res.data);
|
|
||||||
modalStateFTP.title = 'Setting Remote Backup';
|
|
||||||
modalStateFTP.openByEdit = true;
|
|
||||||
} else {
|
} else {
|
||||||
message.error(res.msg, 3);
|
message.warning(res.msg, 3);
|
||||||
modalStateFTP.title = 'Setting Remote Backup';
|
|
||||||
modalStateFTP.openByEdit = false;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**FTP对象保存 */
|
|
||||||
function fnModalFTPOk() {
|
|
||||||
modalStateFTPFrom.validate().then(() => {
|
|
||||||
modalStateFTP.confirmLoading = true;
|
|
||||||
const from = toRaw(modalStateFTP.from);
|
|
||||||
updateFTPInfo(from)
|
|
||||||
.then(res => {
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
|
||||||
message.success(`Configuration saved successfully`, 3);
|
|
||||||
fnModalFTPCancel();
|
|
||||||
} else {
|
|
||||||
message.warning(`Configuration save exception`, 3);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
modalStateFTP.confirmLoading = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对话框弹出关闭执行函数
|
|
||||||
* 进行表达规则校验
|
|
||||||
*/
|
|
||||||
function fnModalFTPCancel() {
|
|
||||||
modalStateFTP.openByEdit = false;
|
|
||||||
modalStateFTPFrom.resetFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步文件到FTP
|
|
||||||
* @param row
|
|
||||||
*/
|
|
||||||
function fnSyncFileToFTP(row: Record<string, any>) {
|
|
||||||
modalStateFTP.confirmLoading = true;
|
|
||||||
putFTPInfo(row.path)
|
|
||||||
.then(res => {
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
|
||||||
message.success(t('common.operateOk'), 3);
|
|
||||||
} else {
|
|
||||||
message.warning(res.msg, 3);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
modalStateFTP.confirmLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -572,8 +477,10 @@ function fnSyncFileToFTP(row: Record<string, any>) {
|
|||||||
<template #extra>
|
<template #extra>
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
<a-tooltip placement="topRight">
|
<a-tooltip placement="topRight">
|
||||||
<template #title>Setting Remote Backup</template>
|
<template #title>
|
||||||
<a-button type="text" @click.prevent="fnModalFTPVisibleByEdit()">
|
{{ t('views.ne.neConfigBackup.backupModal.title') }}
|
||||||
|
</template>
|
||||||
|
<a-button type="text" @click.prevent="fnFTPModalOpen()">
|
||||||
<template #icon><DeliveredProcedureOutlined /></template>
|
<template #icon><DeliveredProcedureOutlined /></template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
@@ -632,12 +539,10 @@ function fnSyncFileToFTP(row: Record<string, any>) {
|
|||||||
<template v-if="column.key === 'id'">
|
<template v-if="column.key === 'id'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
<a-tooltip placement="topRight">
|
<a-tooltip placement="topRight">
|
||||||
<template #title>Send Current File To Remote Backup</template>
|
<template #title>
|
||||||
<a-button
|
{{ t('views.ne.neConfigBackup.backupModal.pushFileOper') }}
|
||||||
type="link"
|
</template>
|
||||||
:loading="modalStateFTP.confirmLoading"
|
<a-button type="link" @click.prevent="fnSyncFileToFTP(record)">
|
||||||
@click.prevent="fnSyncFileToFTP(record)"
|
|
||||||
>
|
|
||||||
<template #icon><CloudServerOutlined /></template>
|
<template #icon><CloudServerOutlined /></template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
@@ -676,7 +581,7 @@ function fnSyncFileToFTP(row: Record<string, any>) {
|
|||||||
<!-- 新增框或修改框 -->
|
<!-- 新增框或修改框 -->
|
||||||
<ProModal
|
<ProModal
|
||||||
:drag="true"
|
:drag="true"
|
||||||
:width="512"
|
:width="520"
|
||||||
:destroyOnClose="true"
|
:destroyOnClose="true"
|
||||||
:keyboard="false"
|
:keyboard="false"
|
||||||
:mask-closable="false"
|
:mask-closable="false"
|
||||||
@@ -719,105 +624,8 @@ function fnSyncFileToFTP(row: Record<string, any>) {
|
|||||||
</a-form>
|
</a-form>
|
||||||
</ProModal>
|
</ProModal>
|
||||||
|
|
||||||
<!-- FTP -->
|
<!-- FTP配置窗口 -->
|
||||||
<ProModal
|
<BackupModal v-model:open="openFTPModal"></BackupModal>
|
||||||
:drag="true"
|
|
||||||
:width="800"
|
|
||||||
:destroyOnClose="true"
|
|
||||||
:keyboard="false"
|
|
||||||
:mask-closable="false"
|
|
||||||
:open="modalStateFTP.openByEdit"
|
|
||||||
:title="modalStateFTP.title"
|
|
||||||
:confirm-loading="modalStateFTP.confirmLoading"
|
|
||||||
@ok="fnModalFTPOk"
|
|
||||||
@cancel="fnModalFTPCancel"
|
|
||||||
>
|
|
||||||
<a-form
|
|
||||||
name="modalStateFTPFrom"
|
|
||||||
layout="horizontal"
|
|
||||||
:label-col="{ span: 6 }"
|
|
||||||
:label-wrap="true"
|
|
||||||
>
|
|
||||||
<a-form-item label="Enable" name="enable" :label-col="{ span: 3 }">
|
|
||||||
<a-switch
|
|
||||||
v-model:checked="modalStateFTP.from.enable"
|
|
||||||
:checked-children="t('common.switch.open')"
|
|
||||||
:un-checked-children="t('common.switch.shut')"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<template v-if="modalStateFTP.from.enable">
|
|
||||||
<a-row>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
label="Service IP"
|
|
||||||
name="toIp"
|
|
||||||
v-bind="modalStateFTPFrom.validateInfos.toIp"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="modalStateFTP.from.toIp"
|
|
||||||
allow-clear
|
|
||||||
:placeholder="t('common.inputPlease')"
|
|
||||||
></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
label="Service Port"
|
|
||||||
name="toPort"
|
|
||||||
v-bind="modalStateFTPFrom.validateInfos.toPort"
|
|
||||||
>
|
|
||||||
<a-input-number
|
|
||||||
v-model:value="modalStateFTP.from.toPort"
|
|
||||||
allow-clear
|
|
||||||
:placeholder="t('common.inputPlease')"
|
|
||||||
></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
<a-row>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
label="UserName"
|
|
||||||
name="username"
|
|
||||||
v-bind="modalStateFTPFrom.validateInfos.username"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="modalStateFTP.from.username"
|
|
||||||
allow-clear
|
|
||||||
:placeholder="t('common.inputPlease')"
|
|
||||||
></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
|
||||||
<a-form-item
|
|
||||||
label="Password"
|
|
||||||
name="password"
|
|
||||||
v-bind="modalStateFTPFrom.validateInfos.password"
|
|
||||||
>
|
|
||||||
<a-input-password
|
|
||||||
v-model:value="modalStateFTP.from.password"
|
|
||||||
allow-clear
|
|
||||||
:placeholder="t('common.inputPlease')"
|
|
||||||
></a-input-password>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
<a-form-item
|
|
||||||
label="Save Dir"
|
|
||||||
name="dir"
|
|
||||||
v-bind="modalStateFTPFrom.validateInfos.dir"
|
|
||||||
:label-col="{ span: 3 }"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="modalStateFTP.from.dir"
|
|
||||||
allow-clear
|
|
||||||
:placeholder="t('common.inputPlease')"
|
|
||||||
></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</template>
|
|
||||||
</a-form>
|
|
||||||
</ProModal>
|
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import useI18n from '@/hooks/useI18n';
|
|||||||
import ViewDrawer from './components/ViewDrawer.vue';
|
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';
|
||||||
|
import { parseSizeFromFile } from '@/utils/parse-utils';
|
||||||
const neInfoStore = useNeInfoStore();
|
const neInfoStore = useNeInfoStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -79,16 +80,19 @@ let tableColumns: ColumnsType = [
|
|||||||
dataIndex: 'size',
|
dataIndex: 'size',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 100,
|
width: 100,
|
||||||
|
customRender(opt) {
|
||||||
|
return parseSizeFromFile(opt.value);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.logManage.neFile.modifiedTime'),
|
title: t('views.logManage.neFile.modifiedTime'),
|
||||||
dataIndex: 'modifiedTime',
|
dataIndex: 'modifiedTime',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
width: 200,
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
if (!opt.value) return '';
|
if (!opt.value) return '';
|
||||||
return parseDateToStr(opt.value * 1000);
|
return parseDateToStr(opt.value * 1000);
|
||||||
},
|
},
|
||||||
width: 150,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.logManage.neFile.fileName'),
|
title: t('views.logManage.neFile.fileName'),
|
||||||
@@ -238,7 +242,7 @@ function fnGetList(pageNum?: number) {
|
|||||||
}
|
}
|
||||||
queryParams.path = nePathArr.value.join('/');
|
queryParams.path = nePathArr.value.join('/');
|
||||||
listNeFiles(toRaw(queryParams)).then(res => {
|
listNeFiles(toRaw(queryParams)).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
const { total, rows } = res.data;
|
const { total, rows } = res.data;
|
||||||
tablePagination.total = total;
|
tablePagination.total = total;
|
||||||
tableState.data = rows;
|
tableState.data = rows;
|
||||||
@@ -10,7 +10,6 @@ import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
|||||||
import { delNeHost, listNeHost } from '@/api/ne/neHost';
|
import { delNeHost, listNeHost } from '@/api/ne/neHost';
|
||||||
import useDictStore from '@/store/modules/dict';
|
import useDictStore from '@/store/modules/dict';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import { number } from 'echarts';
|
|
||||||
const { getDict } = useDictStore();
|
const { getDict } = useDictStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const EditModal = defineAsyncComponent(
|
const EditModal = defineAsyncComponent(
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { ProModal } from 'antdv-pro-modal';
|
|||||||
import { message, Form } from 'ant-design-vue/es';
|
import { message, Form } from 'ant-design-vue/es';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { getOAMFile, saveOAMFile } from '@/api/ne/neInfo';
|
import { getOAMFile, saveOAMFile, serviceNeAction } from '@/api/ne/neInfo';
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const emit = defineEmits(['ok', 'cancel', 'update:open']);
|
const emit = defineEmits(['ok', 'cancel', 'update:open']);
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -29,8 +29,8 @@ type ModalStateType = {
|
|||||||
openByEdit: boolean;
|
openByEdit: boolean;
|
||||||
/**标题 */
|
/**标题 */
|
||||||
title: string;
|
title: string;
|
||||||
/**是否同步 */
|
/**是否重启 */
|
||||||
sync: boolean;
|
restart: boolean;
|
||||||
/**表单数据 */
|
/**表单数据 */
|
||||||
from: Record<string, any>;
|
from: Record<string, any>;
|
||||||
/**确定按钮 loading */
|
/**确定按钮 loading */
|
||||||
@@ -41,7 +41,7 @@ type ModalStateType = {
|
|||||||
let modalState: ModalStateType = reactive({
|
let modalState: ModalStateType = reactive({
|
||||||
openByEdit: false,
|
openByEdit: false,
|
||||||
title: 'OAM Configuration',
|
title: 'OAM Configuration',
|
||||||
sync: true,
|
restart: false,
|
||||||
from: {
|
from: {
|
||||||
omcIP: '',
|
omcIP: '',
|
||||||
oamEnable: true,
|
oamEnable: true,
|
||||||
@@ -114,12 +114,19 @@ function fnModalOk() {
|
|||||||
neType: props.neType,
|
neType: props.neType,
|
||||||
neId: props.neId,
|
neId: props.neId,
|
||||||
content: from,
|
content: from,
|
||||||
sync: modalState.sync,
|
sync: true,
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
message.success(t('common.operateOk'), 3);
|
message.success(t('common.operateOk'), 3);
|
||||||
emit('ok');
|
emit('ok');
|
||||||
|
if (modalState.restart) {
|
||||||
|
serviceNeAction({
|
||||||
|
neType: props.neType,
|
||||||
|
neId: props.neId,
|
||||||
|
action: 'restart',
|
||||||
|
});
|
||||||
|
}
|
||||||
fnModalCancel();
|
fnModalCancel();
|
||||||
} else {
|
} else {
|
||||||
message.error({
|
message.error({
|
||||||
@@ -145,6 +152,7 @@ function fnModalOk() {
|
|||||||
function fnModalCancel() {
|
function fnModalCancel() {
|
||||||
modalState.openByEdit = false;
|
modalState.openByEdit = false;
|
||||||
modalState.confirmLoading = false;
|
modalState.confirmLoading = false;
|
||||||
|
modalState.restart = false;
|
||||||
modalStateFrom.resetFields();
|
modalStateFrom.resetFields();
|
||||||
emit('cancel');
|
emit('cancel');
|
||||||
emit('update:open', false);
|
emit('update:open', false);
|
||||||
@@ -183,15 +191,15 @@ watch(
|
|||||||
:labelWrap="true"
|
:labelWrap="true"
|
||||||
>
|
>
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.ne.neInfo.oam.sync')"
|
:label="t('views.ne.neInfo.oam.restart')"
|
||||||
name="sync"
|
name="restart"
|
||||||
:label-col="{ span: 6 }"
|
:label-col="{ span: 6 }"
|
||||||
:labelWrap="true"
|
:labelWrap="true"
|
||||||
>
|
>
|
||||||
<a-switch
|
<a-switch
|
||||||
:checked-children="t('common.switch.open')"
|
:checked-children="t('common.switch.open')"
|
||||||
:un-checked-children="t('common.switch.shut')"
|
:un-checked-children="t('common.switch.shut')"
|
||||||
v-model:checked="modalState.sync"
|
v-model:checked="modalState.restart"
|
||||||
></a-switch>
|
></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
|
|||||||
412
src/views/neData/backup-data/index.vue
Normal file
412
src/views/neData/backup-data/index.vue
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, toRaw } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
|
import { Modal, message } from 'ant-design-vue/es';
|
||||||
|
import BackupModal from '@/views/ne/neConfigBackup/components/BackupModal.vue';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import saveAs from 'file-saver';
|
||||||
|
import { delFile, getFile, listFile } from '@/api/tool/file';
|
||||||
|
import { parseSizeFromFile } from '@/utils/parse-utils';
|
||||||
|
import { pushBackupFTP } from '@/api/neData/backup';
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
/**文件来源 */
|
||||||
|
let sourceState = reactive({
|
||||||
|
/**文件列表 */
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
value: '/auth',
|
||||||
|
label: t('views.neData.backupData.auth'),
|
||||||
|
path: '/usr/local/omc/backup/udm_data',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '/sub',
|
||||||
|
label: t('views.neData.backupData.sub'),
|
||||||
|
path: '/usr/local/omc/backup/udm_data',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '/voip',
|
||||||
|
label: t('views.neData.backupData.voip'),
|
||||||
|
path: '/usr/local/omc/backup/udm_data',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '/volte',
|
||||||
|
label: t('views.neData.backupData.volte'),
|
||||||
|
path: '/usr/local/omc/backup/udm_data',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
/**选择value */
|
||||||
|
value: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**读取路径 */
|
||||||
|
path: '',
|
||||||
|
/**当前页数 */
|
||||||
|
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',
|
||||||
|
customRender(opt) {
|
||||||
|
return parseSizeFromFile(opt.value);
|
||||||
|
},
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.logManage.neFile.modifiedTime'),
|
||||||
|
dataIndex: 'modifiedTime',
|
||||||
|
align: 'left',
|
||||||
|
customRender(opt) {
|
||||||
|
if (!opt.value) return '';
|
||||||
|
return parseDateToStr(opt.value);
|
||||||
|
},
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
/**信息文件下载 */
|
||||||
|
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);
|
||||||
|
getFile(queryParams.path, 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;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**删除触发等待 */
|
||||||
|
let delLoading = ref<boolean>(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() {
|
||||||
|
delLoading.value = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
delFile(queryParams.path, row.fileName)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.msgSuccess', {
|
||||||
|
msg: t('common.deleteText'),
|
||||||
|
}),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
fnGetList();
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: t('views.logManage.exportFile.deleteTipErr'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
delLoading.value = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**网元类型选择对应修改 */
|
||||||
|
function fnNeChange(_: any, opt: any) {
|
||||||
|
queryParams.path = `${opt.path}${opt.value}`;
|
||||||
|
ftpInfo.path = queryParams.path;
|
||||||
|
ftpInfo.tag = opt.value;
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询备份信息列表, pageNum初始页数 */
|
||||||
|
function fnGetList(pageNum?: number) {
|
||||||
|
if (queryParams.path === '') {
|
||||||
|
message.warning({
|
||||||
|
content: t('views.logManage.exportFile.fileSourcePlease'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tableState.loading) return;
|
||||||
|
tableState.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
listFile(toRaw(queryParams)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
const { total, rows } = res.data;
|
||||||
|
tablePagination.total = total;
|
||||||
|
tableState.data = 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**打开FTP配置窗口 */
|
||||||
|
const openFTPModal = ref<boolean>(false);
|
||||||
|
function fnFTPModalOpen() {
|
||||||
|
openFTPModal.value = !openFTPModal.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
type FTPInfoType = {
|
||||||
|
path: string;
|
||||||
|
tag: string;
|
||||||
|
fileName: string;
|
||||||
|
};
|
||||||
|
const ftpInfo = reactive<FTPInfoType>({
|
||||||
|
path: '',
|
||||||
|
tag: '',
|
||||||
|
fileName: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
/**同步文件到FTP */
|
||||||
|
function fnSyncFileToFTP(fileName: string) {
|
||||||
|
ftpInfo.fileName = fileName;
|
||||||
|
pushBackupFTP(toRaw(ftpInfo)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success(t('common.operateOk'), 3);
|
||||||
|
} else {
|
||||||
|
message.warning(res.msg, 3);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||||
|
<a-row :gutter="16" align="middle">
|
||||||
|
<a-col>
|
||||||
|
<span>{{ t('views.logManage.exportFile.fileSource') }}:</span
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="sourceState.value"
|
||||||
|
:options="sourceState.list"
|
||||||
|
@change="fnNeChange"
|
||||||
|
:allow-clear="false"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<template v-if="queryParams.path">
|
||||||
|
<span>{{ t('views.logManage.neFile.nePath') }}:</span>
|
||||||
|
<a-col>
|
||||||
|
<a-breadcrumb>
|
||||||
|
<a-breadcrumb-item>
|
||||||
|
{{ queryParams.path }}
|
||||||
|
</a-breadcrumb-item>
|
||||||
|
</a-breadcrumb>
|
||||||
|
</a-col>
|
||||||
|
</template>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip placement="topRight">
|
||||||
|
<template #title>
|
||||||
|
{{ t('views.ne.neConfigBackup.backupModal.title') }}
|
||||||
|
</template>
|
||||||
|
<a-button type="text" @click.prevent="fnFTPModalOpen()">
|
||||||
|
<template #icon><DeliveredProcedureOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</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-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-tooltip placement="topRight" v-if="record.fileType === 'file'">
|
||||||
|
<template #title>
|
||||||
|
{{ t('views.ne.neConfigBackup.backupModal.pushFileOper') }}
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnSyncFileToFTP(record.fileName)"
|
||||||
|
>
|
||||||
|
<template #icon><CloudServerOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip placement="topRight" v-if="record.fileType === 'file'">
|
||||||
|
<template #title>{{ t('common.downloadText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
:loading="downLoading"
|
||||||
|
@click.prevent="fnDownloadFile(record)"
|
||||||
|
>
|
||||||
|
<template #icon><DownloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip placement="topRight" v-if="record.fileType === 'file'">
|
||||||
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
:loading="delLoading"
|
||||||
|
@click.prevent="fnRecordDelete(record)"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- FTP配置窗口 -->
|
||||||
|
<BackupModal v-model:open="openFTPModal"></BackupModal>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
||||||
@@ -910,6 +910,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<!-- 状态历史框 -->
|
<!-- 状态历史框 -->
|
||||||
<HistoryModal
|
<HistoryModal
|
||||||
|
v-if="neTypeAndId.length > 1"
|
||||||
v-model:open="modalState.openByHistory"
|
v-model:open="modalState.openByHistory"
|
||||||
:title="t('views.neData.baseStation.history')"
|
:title="t('views.neData.baseStation.history')"
|
||||||
:ne-type="neTypeAndId[0]"
|
:ne-type="neTypeAndId[0]"
|
||||||
@@ -545,9 +545,13 @@ function fnExportList(type: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**查询列表, pageNum初始页数 */
|
/**查询列表, pageNum初始页数 */
|
||||||
function fnGetList() {
|
function fnGetList(pageNum?: number) {
|
||||||
if (tableState.loading) return;
|
if (tableState.loading) return;
|
||||||
tableState.loading = true;
|
tableState.loading = true;
|
||||||
|
if (pageNum) {
|
||||||
|
queryParams.pageNum = pageNum;
|
||||||
|
tablePagination.current = pageNum;
|
||||||
|
}
|
||||||
listRules(toRaw(queryParams)).then(res => {
|
listRules(toRaw(queryParams)).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
// 取消勾选
|
// 取消勾选
|
||||||
@@ -705,6 +709,7 @@ onMounted(() => {
|
|||||||
v-model:value="queryParams.neId"
|
v-model:value="queryParams.neId"
|
||||||
:options="neOtions"
|
:options="neOtions"
|
||||||
:placeholder="t('common.selectPlease')"
|
:placeholder="t('common.selectPlease')"
|
||||||
|
@change="fnGetList(1)"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -831,7 +836,7 @@ onMounted(() => {
|
|||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<TableColumnsDnd
|
<TableColumnsDnd
|
||||||
cache-id="pcfData"
|
cache-id="pcfSubData"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
v-model:columns-dnd="tableColumnsDnd"
|
v-model:columns-dnd="tableColumnsDnd"
|
||||||
></TableColumnsDnd>
|
></TableColumnsDnd>
|
||||||
@@ -1190,7 +1195,6 @@ onMounted(() => {
|
|||||||
</a-button>
|
</a-button>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<a-textarea
|
<a-textarea
|
||||||
:disabled="true"
|
:disabled="true"
|
||||||
:hidden="!uploadImportState.msg"
|
:hidden="!uploadImportState.msg"
|
||||||
@@ -780,11 +780,12 @@ 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="6" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item :label="t('views.neUser.auth.neType')" name="neId ">
|
<a-form-item label="UDM" name="neId ">
|
||||||
<a-select
|
<a-select
|
||||||
v-model:value="queryParams.neId"
|
v-model:value="queryParams.neId"
|
||||||
:options="neOtions"
|
:options="neOtions"
|
||||||
:placeholder="t('common.selectPlease')"
|
:placeholder="t('common.selectPlease')"
|
||||||
|
:disabled="modalState.loadDataLoading"
|
||||||
@change="fnGetList(1)"
|
@change="fnGetList(1)"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -1268,7 +1269,6 @@ onMounted(() => {
|
|||||||
</a-button>
|
</a-button>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<a-input-password
|
<a-input-password
|
||||||
v-if="uploadImportState.from.typeVal === 'k4'"
|
v-if="uploadImportState.from.typeVal === 'k4'"
|
||||||
v-model:value="uploadImportState.from.typeData"
|
v-model:value="uploadImportState.from.typeData"
|
||||||
@@ -1282,11 +1282,12 @@ 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="6" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
<a-form-item :label="t('views.neUser.sub.neType')" name="neId ">
|
<a-form-item label="UDM" name="neId ">
|
||||||
<a-select
|
<a-select
|
||||||
v-model:value="queryParams.neId"
|
v-model:value="queryParams.neId"
|
||||||
:options="neOtions"
|
:options="neOtions"
|
||||||
:placeholder="t('common.selectPlease')"
|
:placeholder="t('common.selectPlease')"
|
||||||
|
:disabled="modalState.loadDataLoading"
|
||||||
@change="fnGetList(1)"
|
@change="fnGetList(1)"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -2284,7 +2285,6 @@ onMounted(() => {
|
|||||||
</a-button>
|
</a-button>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<a-alert
|
<a-alert
|
||||||
:message="uploadImportState.msg"
|
:message="uploadImportState.msg"
|
||||||
:type="uploadImportState.hasFail ? 'warning' : 'info'"
|
:type="uploadImportState.hasFail ? 'warning' : 'info'"
|
||||||
944
src/views/neData/udm-voip/index.vue
Normal file
944
src/views/neData/udm-voip/index.vue
Normal file
@@ -0,0 +1,944 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, onMounted, toRaw } from 'vue';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import { ProModal } from 'antdv-pro-modal';
|
||||||
|
import {
|
||||||
|
message,
|
||||||
|
Modal,
|
||||||
|
Form,
|
||||||
|
TableColumnsType,
|
||||||
|
notification,
|
||||||
|
} from 'ant-design-vue';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
|
import UploadModal from '@/components/UploadModal/index.vue';
|
||||||
|
import {
|
||||||
|
addUDMVOIP,
|
||||||
|
batchAddUDMVOIP,
|
||||||
|
batchDelUDMVOIP,
|
||||||
|
delUDMVOIP,
|
||||||
|
exportUDMVOIP,
|
||||||
|
importUDMVOIP,
|
||||||
|
listUDMVOIP,
|
||||||
|
resetUDMVOIP,
|
||||||
|
} from '@/api/neData/udm_voip';
|
||||||
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
import { uploadFileToNE } from '@/api/tool/file';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
/**网元参数 */
|
||||||
|
let neOtions = ref<Record<string, any>[]>([]);
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
/**网元ID */
|
||||||
|
neId: undefined,
|
||||||
|
/**用户名 */
|
||||||
|
username: '',
|
||||||
|
/**排序字段 */
|
||||||
|
sortField: 'username',
|
||||||
|
/**排序方式 */
|
||||||
|
sortOrder: 'asc',
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**查询参数重置 */
|
||||||
|
function fnQueryReset() {
|
||||||
|
queryParams = Object.assign(queryParams, {
|
||||||
|
username: '',
|
||||||
|
sortField: 'username',
|
||||||
|
sortOrder: 'asc',
|
||||||
|
});
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格状态类型 */
|
||||||
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**搜索栏 */
|
||||||
|
seached: boolean;
|
||||||
|
/**记录数据 */
|
||||||
|
data: object[];
|
||||||
|
/**勾选记录 */
|
||||||
|
selectedRowKeys: (string | number)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
size: 'small',
|
||||||
|
seached: true,
|
||||||
|
data: [],
|
||||||
|
selectedRowKeys: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
let tableColumns = ref<TableColumnsType>([
|
||||||
|
{
|
||||||
|
title: t('views.neData.udmVOIP.username'),
|
||||||
|
dataIndex: 'username',
|
||||||
|
sorter: true,
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 250,
|
||||||
|
minWidth: 100,
|
||||||
|
maxWidth: 300,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('common.operate'),
|
||||||
|
key: 'id',
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**表格字段列排序 */
|
||||||
|
let tableColumnsDnd = ref<TableColumnsType>([]);
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格分页、排序、筛选变化时触发操作, 排序方式,取值为 ascend descend */
|
||||||
|
function fnTableChange(pagination: any, filters: any, sorter: any, extra: any) {
|
||||||
|
const { field, order } = sorter;
|
||||||
|
if (order) {
|
||||||
|
queryParams.sortField = field;
|
||||||
|
queryParams.sortOrder = order.replace('end', '');
|
||||||
|
} else {
|
||||||
|
queryParams.sortOrder = 'asc';
|
||||||
|
}
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格紧凑型变更操作 */
|
||||||
|
function fnTableSize({ key }: MenuInfo) {
|
||||||
|
tableState.size = key as SizeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**表格多选 */
|
||||||
|
function fnTableSelectedRowKeys(keys: (string | number)[]) {
|
||||||
|
tableState.selectedRowKeys = keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框对象信息状态类型 */
|
||||||
|
type ModalStateType = {
|
||||||
|
/**新增框或修改框是否显示 */
|
||||||
|
openByEdit: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**表单数据 */
|
||||||
|
from: Record<string, any>;
|
||||||
|
/**是否批量操作 */
|
||||||
|
isBatch: boolean;
|
||||||
|
/**操作类型 */
|
||||||
|
type: 'delete' | 'add';
|
||||||
|
/**确定按钮 loading */
|
||||||
|
confirmLoading: boolean;
|
||||||
|
/**更新加载数据按钮 loading */
|
||||||
|
loadDataLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框对象信息状态 */
|
||||||
|
let modalState: ModalStateType = reactive({
|
||||||
|
openByEdit: false,
|
||||||
|
title: 'UDMVOIP',
|
||||||
|
from: {
|
||||||
|
num: 1,
|
||||||
|
username: undefined,
|
||||||
|
password: undefined,
|
||||||
|
},
|
||||||
|
isBatch: false,
|
||||||
|
type: 'add',
|
||||||
|
confirmLoading: false,
|
||||||
|
loadDataLoading: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**对话框内表单属性和校验规则 */
|
||||||
|
const modalStateFrom = Form.useForm(
|
||||||
|
modalState.from,
|
||||||
|
reactive({
|
||||||
|
num: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: t('views.neData.common.batchNum'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
username: [
|
||||||
|
{ required: true, message: t('views.neData.udmVOIP.usernamePlease') },
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{ required: true, message: t('views.neData.udmVOIP.passwordPlease') },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出显示为 新增或者修改
|
||||||
|
* @param noticeId 网元id, 不传为新增
|
||||||
|
*/
|
||||||
|
function fnModalVisibleByEdit(row?: Record<string, any>) {
|
||||||
|
modalState.isBatch = false;
|
||||||
|
if (!row) {
|
||||||
|
modalStateFrom.resetFields(); //重置表单
|
||||||
|
modalState.title = t('views.neData.udmVOIP.addTitle');
|
||||||
|
modalState.openByEdit = true;
|
||||||
|
modalState.type = 'add';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出确认执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalOk() {
|
||||||
|
const from = JSON.parse(JSON.stringify(modalState.from));
|
||||||
|
from.neId = queryParams.neId || '-';
|
||||||
|
from.username = `${from.username}`;
|
||||||
|
|
||||||
|
// 校验规则
|
||||||
|
let validateArr = ['username', 'password'];
|
||||||
|
if (modalState.isBatch) {
|
||||||
|
validateArr.push('num');
|
||||||
|
if (modalState.type === 'delete') {
|
||||||
|
validateArr = ['num', 'username'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modalStateFrom
|
||||||
|
.validate(validateArr)
|
||||||
|
.then(e => {
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
|
||||||
|
// 根据类型选择函数
|
||||||
|
let result: any = null;
|
||||||
|
if (modalState.isBatch) {
|
||||||
|
if (modalState.type === 'add') {
|
||||||
|
result = batchAddUDMVOIP(
|
||||||
|
from.neId,
|
||||||
|
{ username: from.username, password: from.password },
|
||||||
|
from.num
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (modalState.type === 'delete') {
|
||||||
|
result = batchDelUDMVOIP(from.neId, from.username, from.num);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (modalState.type === 'add') {
|
||||||
|
result = addUDMVOIP(from.neId, {
|
||||||
|
username: from.username,
|
||||||
|
password: from.password,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
.then((res: any) => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
fnModalCancel();
|
||||||
|
fnGetList();
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出关闭执行函数
|
||||||
|
* 进行表达规则校验
|
||||||
|
*/
|
||||||
|
function fnModalCancel() {
|
||||||
|
modalState.type = 'add';
|
||||||
|
modalState.isBatch = false;
|
||||||
|
modalState.openByEdit = false;
|
||||||
|
modalStateFrom.resetFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话框弹出显示为 批量操作
|
||||||
|
* @param type 类型
|
||||||
|
*/
|
||||||
|
function fnModalVisibleByBatch(type: 'delete' | 'add') {
|
||||||
|
modalStateFrom.resetFields(); //重置表单
|
||||||
|
modalState.isBatch = true;
|
||||||
|
modalState.type = type;
|
||||||
|
if (type === 'add') {
|
||||||
|
modalState.title = t('views.neData.common.batchAddText');
|
||||||
|
modalState.openByEdit = true;
|
||||||
|
}
|
||||||
|
if (type === 'delete') {
|
||||||
|
modalState.title = t('views.neData.common.batchDelText');
|
||||||
|
modalState.openByEdit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录删除
|
||||||
|
* @param username 网元编号ID
|
||||||
|
*/
|
||||||
|
function fnRecordDelete(username: string) {
|
||||||
|
const neID = queryParams.neId;
|
||||||
|
if (!neID) return;
|
||||||
|
let msg = username;
|
||||||
|
if (username === '0') {
|
||||||
|
msg = `${tableState.selectedRowKeys[0]}... ${tableState.selectedRowKeys.length}`;
|
||||||
|
username = tableState.selectedRowKeys.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: t('views.neData.udmVOIP.delTip', { num: msg }),
|
||||||
|
onOk() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
delUDMVOIP(neID, username)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success(t('common.operateOk'), 3);
|
||||||
|
fnGetList();
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**列表导出 */
|
||||||
|
function fnExportList(type: string) {
|
||||||
|
const neId = queryParams.neId;
|
||||||
|
if (!neId) return;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
exportUDMVOIP(Object.assign({ type: type }, queryParams))
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
saveAs(res.data, `UDM_VOIP_${neId}_${Date.now()}.${type}`);
|
||||||
|
} 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;
|
||||||
|
tablePagination.current = pageNum;
|
||||||
|
}
|
||||||
|
listUDMVOIP(toRaw(queryParams)).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
// 取消勾选
|
||||||
|
if (tableState.selectedRowKeys.length > 0) {
|
||||||
|
tableState.selectedRowKeys = [];
|
||||||
|
}
|
||||||
|
const { total, rows } = res.data;
|
||||||
|
tablePagination.total = total;
|
||||||
|
tableState.data = rows;
|
||||||
|
} else {
|
||||||
|
tableState.data = [];
|
||||||
|
}
|
||||||
|
tableState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**重新加载数据 */
|
||||||
|
function fnLoadData() {
|
||||||
|
const neId = queryParams.neId;
|
||||||
|
if (tableState.loading || !neId) return;
|
||||||
|
modalState.loadDataLoading = true;
|
||||||
|
tablePagination.total = 0;
|
||||||
|
tableState.data = [];
|
||||||
|
tableState.loading = true; // 表格loading
|
||||||
|
resetUDMVOIP(neId).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
const num = res.data;
|
||||||
|
const timerS = Math.ceil(+num / 800) + 3;
|
||||||
|
notification.success({
|
||||||
|
message: t('views.neData.common.loadData'),
|
||||||
|
description: t('views.neData.common.loadDataTip', {
|
||||||
|
num,
|
||||||
|
timer: timerS,
|
||||||
|
}),
|
||||||
|
duration: timerS,
|
||||||
|
});
|
||||||
|
// 延迟10s后关闭loading刷新列表
|
||||||
|
setTimeout(() => {
|
||||||
|
modalState.loadDataLoading = false;
|
||||||
|
tableState.loading = false; // 表格loading
|
||||||
|
fnQueryReset();
|
||||||
|
}, timerS * 1000);
|
||||||
|
} else {
|
||||||
|
modalState.loadDataLoading = false;
|
||||||
|
tableState.loading = false; // 表格loading
|
||||||
|
fnQueryReset();
|
||||||
|
message.error({
|
||||||
|
content: t('common.getInfoFail'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框表格信息导入对象信息状态类型 */
|
||||||
|
type ModalUploadImportStateType = {
|
||||||
|
/**是否显示 */
|
||||||
|
open: boolean;
|
||||||
|
/**标题 */
|
||||||
|
title: string;
|
||||||
|
/**是否上传中 */
|
||||||
|
loading: boolean;
|
||||||
|
/**上传结果信息 */
|
||||||
|
msg: string;
|
||||||
|
/**含失败信息 */
|
||||||
|
hasFail: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**对话框表格信息导入对象信息状态 */
|
||||||
|
let uploadImportState: ModalUploadImportStateType = reactive({
|
||||||
|
open: false,
|
||||||
|
title: t('components.UploadModal.uploadTitle'),
|
||||||
|
loading: false,
|
||||||
|
msg: '',
|
||||||
|
hasFail: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**对话框表格信息导入弹出窗口 */
|
||||||
|
function fnModalUploadImportOpen() {
|
||||||
|
uploadImportState.msg = '';
|
||||||
|
uploadImportState.hasFail = false;
|
||||||
|
uploadImportState.loading = false;
|
||||||
|
uploadImportState.open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框表格信息导入关闭窗口 */
|
||||||
|
function fnModalUploadImportClose() {
|
||||||
|
uploadImportState.open = false;
|
||||||
|
fnGetList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框表格信息导入上传 */
|
||||||
|
function fnModalUploadImportUpload(file: File) {
|
||||||
|
const neID = queryParams.neId;
|
||||||
|
if (!neID) {
|
||||||
|
return Promise.reject('Unknown network element');
|
||||||
|
}
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
uploadImportState.loading = true;
|
||||||
|
uploadFileToNE('UDM', neID, file, 5)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
return importUDMVOIP({
|
||||||
|
neId: neID,
|
||||||
|
uploadPath: res.data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (!res) return;
|
||||||
|
uploadImportState.msg = res.msg;
|
||||||
|
const regex = /fail num: (\d+)/;
|
||||||
|
const match = res.msg.match(regex);
|
||||||
|
if (match) {
|
||||||
|
const failNum = Number(match[1]);
|
||||||
|
uploadImportState.hasFail = failNum > 0;
|
||||||
|
} else {
|
||||||
|
uploadImportState.hasFail = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
uploadImportState.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**对话框表格信息导入模板 */
|
||||||
|
function fnModalDownloadImportTemplate() {
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
|
||||||
|
const baseUrl = import.meta.env.VITE_HISTORY_BASE_URL;
|
||||||
|
const templateUrl = `${
|
||||||
|
baseUrl.length === 1 && baseUrl.indexOf('/') === 0
|
||||||
|
? ''
|
||||||
|
: baseUrl.indexOf('/') === -1
|
||||||
|
? '/' + baseUrl
|
||||||
|
: baseUrl
|
||||||
|
}/neDataImput`;
|
||||||
|
saveAs(
|
||||||
|
`${templateUrl}/udm_voip_template.txt`,
|
||||||
|
`import_udmvoip_template_${Date.now()}.txt`
|
||||||
|
);
|
||||||
|
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 获取网元网元列表
|
||||||
|
useNeInfoStore()
|
||||||
|
.fnNelist()
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS && res.data?.length > 0) {
|
||||||
|
let arr: Record<string, any>[] = [];
|
||||||
|
res.data.forEach((v: any) => {
|
||||||
|
if (v.neType === 'UDM') {
|
||||||
|
arr.push({ value: v.neId, label: v.neName });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
neOtions.value = arr;
|
||||||
|
if (arr.length > 0) {
|
||||||
|
queryParams.neId = arr[0].value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// 获取列表数据
|
||||||
|
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="UDM" name="neId ">
|
||||||
|
<a-select
|
||||||
|
v-model:value="queryParams.neId"
|
||||||
|
:options="neOtions"
|
||||||
|
:placeholder="t('common.selectPlease')"
|
||||||
|
:disabled="modalState.loadDataLoading"
|
||||||
|
@change="fnGetList(1)"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.neData.udmVOIP.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="6" :md="12" :xs="24">
|
||||||
|
<a-form-item>
|
||||||
|
<a-space :size="8">
|
||||||
|
<a-button type="primary" @click.prevent="fnGetList()">
|
||||||
|
<template #icon>
|
||||||
|
<SearchOutlined />
|
||||||
|
</template>
|
||||||
|
{{ t('common.search') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="default" @click.prevent="fnQueryReset">
|
||||||
|
<template #icon>
|
||||||
|
<ClearOutlined />
|
||||||
|
</template>
|
||||||
|
{{ t('common.reset') }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
<template #title>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
|
||||||
|
<template #icon>
|
||||||
|
<PlusOutlined />
|
||||||
|
</template>
|
||||||
|
{{ t('common.addText') }}
|
||||||
|
</a-button>
|
||||||
|
|
||||||
|
<a-button
|
||||||
|
type="default"
|
||||||
|
danger
|
||||||
|
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||||
|
:loading="modalState.confirmLoading"
|
||||||
|
@click.prevent="fnRecordDelete('0')"
|
||||||
|
>
|
||||||
|
<template #icon><DeleteOutlined /></template>
|
||||||
|
{{ t('views.neData.common.checkDel') }}
|
||||||
|
</a-button>
|
||||||
|
|
||||||
|
<a-dropdown trigger="click">
|
||||||
|
<a-button>
|
||||||
|
{{ t('views.neData.common.batchOper') }}
|
||||||
|
<DownOutlined />
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu @click="({ key }:any) => fnModalVisibleByBatch(key)">
|
||||||
|
<a-menu-item key="add">
|
||||||
|
<PlusOutlined />
|
||||||
|
{{ t('views.neData.common.batchAddText') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="delete">
|
||||||
|
<DeleteOutlined />
|
||||||
|
{{ t('views.neData.common.batchDelText') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
|
||||||
|
<a-popconfirm
|
||||||
|
placement="topRight"
|
||||||
|
:title="t('views.neData.common.loadDataConfirm')"
|
||||||
|
:ok-text="t('common.ok')"
|
||||||
|
:cancel-text="t('common.cancel')"
|
||||||
|
:disabled="modalState.loadDataLoading"
|
||||||
|
@confirm="fnLoadData"
|
||||||
|
>
|
||||||
|
<a-button
|
||||||
|
type="dashed"
|
||||||
|
danger
|
||||||
|
:disabled="modalState.loadDataLoading"
|
||||||
|
:loading="modalState.loadDataLoading"
|
||||||
|
>
|
||||||
|
<template #icon><SyncOutlined /></template>
|
||||||
|
{{ t('views.neData.common.loadData') }}
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
|
||||||
|
<a-button type="dashed" @click.prevent="fnModalUploadImportOpen">
|
||||||
|
<template #icon><ImportOutlined /></template>
|
||||||
|
{{ t('common.import') }}
|
||||||
|
</a-button>
|
||||||
|
<a-popconfirm
|
||||||
|
placement="topRight"
|
||||||
|
:title="t('views.neData.udmVOIP.exportTip')"
|
||||||
|
ok-text="TXT"
|
||||||
|
ok-type="default"
|
||||||
|
@confirm="fnExportList('txt')"
|
||||||
|
>
|
||||||
|
<a-button type="dashed">
|
||||||
|
<template #icon><ExportOutlined /></template>
|
||||||
|
{{ t('common.export') }}
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip placement="topRight">
|
||||||
|
<template #title>
|
||||||
|
{{
|
||||||
|
tableState.seached
|
||||||
|
? t('common.switch.show')
|
||||||
|
: t('common.switch.hide')
|
||||||
|
}}
|
||||||
|
</template>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="tableState.seached"
|
||||||
|
:checked-children="t('common.searchBarText')"
|
||||||
|
:un-checked-children="t('common.searchBarText')"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
<TableColumnsDnd
|
||||||
|
cache-id="udmVOIPData"
|
||||||
|
:columns="tableColumns"
|
||||||
|
v-model:columns-dnd="tableColumnsDnd"
|
||||||
|
></TableColumnsDnd>
|
||||||
|
<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-tooltip placement="topRight">
|
||||||
|
<template #title>{{ t('common.sizeText') }}</template>
|
||||||
|
<a-dropdown placement="bottomRight" trigger="click">
|
||||||
|
<a-button type="text">
|
||||||
|
<template #icon><ColumnHeightOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu
|
||||||
|
:selected-keys="[tableState.size as string]"
|
||||||
|
@click="fnTableSize"
|
||||||
|
>
|
||||||
|
<a-menu-item key="default">
|
||||||
|
{{ t('common.size.default') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="middle">
|
||||||
|
{{ t('common.size.middle') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="small">
|
||||||
|
{{ t('common.size.small') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格列表 -->
|
||||||
|
<a-table
|
||||||
|
class="table"
|
||||||
|
row-key="username"
|
||||||
|
:columns="tableColumnsDnd"
|
||||||
|
:loading="tableState.loading"
|
||||||
|
:data-source="tableState.data"
|
||||||
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:scroll="{ y: 'calc(100vh - 480px)' }"
|
||||||
|
@change="fnTableChange"
|
||||||
|
@resizeColumn="(w:number, col:any) => (col.width = w)"
|
||||||
|
:row-selection="{
|
||||||
|
type: 'checkbox',
|
||||||
|
selectedRowKeys: tableState.selectedRowKeys,
|
||||||
|
onChange: fnTableSelectedRowKeys,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column?.key === 'id'">
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordDelete(record.username)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<DeleteOutlined />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 新增框或修改框 -->
|
||||||
|
<ProModal
|
||||||
|
:drag="true"
|
||||||
|
:width="520"
|
||||||
|
:destroyOnClose="true"
|
||||||
|
:keyboard="false"
|
||||||
|
:mask-closable="false"
|
||||||
|
:open="modalState.openByEdit"
|
||||||
|
:title="modalState.title"
|
||||||
|
:confirm-loading="modalState.confirmLoading"
|
||||||
|
@ok="fnModalOk"
|
||||||
|
@cancel="fnModalCancel"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
name="modalStateFrom"
|
||||||
|
layout="horizontal"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:labelWrap="true"
|
||||||
|
>
|
||||||
|
<!--批量删除-->
|
||||||
|
<template v-if="modalState.isBatch && modalState.type === 'delete'">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.neData.common.batchNum')"
|
||||||
|
name="num"
|
||||||
|
v-bind="modalStateFrom.validateInfos.num"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="modalState.from.num"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="1"
|
||||||
|
:max="500"
|
||||||
|
placeholder="<=500"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:label="
|
||||||
|
modalState.isBatch
|
||||||
|
? t('views.neData.udmVOIP.startUsername')
|
||||||
|
: t('views.neData.udmVOIP.username')
|
||||||
|
"
|
||||||
|
name="username"
|
||||||
|
v-bind="modalStateFrom.validateInfos.username"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="modalState.from.username"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="4"
|
||||||
|
:maxlangth="16"
|
||||||
|
:placeholder="t('views.neData.udmVOIP.username')"
|
||||||
|
>
|
||||||
|
</a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<!--批量数-->
|
||||||
|
<a-form-item
|
||||||
|
v-if="modalState.isBatch"
|
||||||
|
:label="t('views.neData.common.batchNum')"
|
||||||
|
name="num"
|
||||||
|
v-bind="modalStateFrom.validateInfos.num"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="modalState.from.num"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="1"
|
||||||
|
:max="500"
|
||||||
|
placeholder="<=500"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
:label="
|
||||||
|
modalState.isBatch
|
||||||
|
? t('views.neData.udmVOIP.startUsername')
|
||||||
|
: t('views.neData.udmVOIP.username')
|
||||||
|
"
|
||||||
|
name="username"
|
||||||
|
v-bind="modalStateFrom.validateInfos.username"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="modalState.from.username"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="4"
|
||||||
|
:maxlength="16"
|
||||||
|
:placeholder="t('views.neData.udmVOIP.username')"
|
||||||
|
>
|
||||||
|
</a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.neData.udmVOIP.password')"
|
||||||
|
name="password"
|
||||||
|
v-bind="modalStateFrom.validateInfos.password"
|
||||||
|
>
|
||||||
|
<a-input-password
|
||||||
|
v-model:value="modalState.from.password"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="4"
|
||||||
|
:max="64"
|
||||||
|
:placeholder="t('views.neData.udmVOIP.password')"
|
||||||
|
>
|
||||||
|
</a-input-password>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
</a-form>
|
||||||
|
</ProModal>
|
||||||
|
|
||||||
|
<!-- 上传导入表格数据文件框 -->
|
||||||
|
<UploadModal
|
||||||
|
:title="uploadImportState.title"
|
||||||
|
:loading="uploadImportState.loading"
|
||||||
|
@upload="fnModalUploadImportUpload"
|
||||||
|
@close="fnModalUploadImportClose"
|
||||||
|
v-model:open="uploadImportState.open"
|
||||||
|
:ext="['.txt']"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<a-row justify="space-between" align="middle">
|
||||||
|
<a-col :span="12"> </a-col>
|
||||||
|
<a-col>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
:title="t('views.neData.common.importTemplate')"
|
||||||
|
@click.prevent="fnModalDownloadImportTemplate"
|
||||||
|
>
|
||||||
|
{{ t('views.neData.common.importTemplate') }}
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-textarea
|
||||||
|
:disabled="true"
|
||||||
|
:hidden="!uploadImportState.msg"
|
||||||
|
:value="uploadImportState.msg"
|
||||||
|
:auto-size="{ minRows: 2, maxRows: 8 }"
|
||||||
|
style="background-color: transparent; color: rgba(0, 0, 0, 0.85)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</UploadModal>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table :deep(.ant-pagination) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
1125
src/views/neData/udm-volte/index.vue
Normal file
1125
src/views/neData/udm-volte/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
|||||||
import { GlobalFooter } from 'antdv-pro-layout';
|
import { GlobalFooter } from 'antdv-pro-layout';
|
||||||
import { Modal, message } from 'ant-design-vue/es';
|
import { Modal, message } from 'ant-design-vue/es';
|
||||||
import { computed, onMounted, reactive, toRaw } from 'vue';
|
import { computed, onMounted, reactive, toRaw } from 'vue';
|
||||||
import { register } from '@/api/login';
|
import { register } from '@/api/auth';
|
||||||
import { regExpPasswd, regExpUserName } from '@/utils/regular-utils';
|
import { regExpPasswd, regExpUserName } from '@/utils/regular-utils';
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ function fnUnlock() {
|
|||||||
hide();
|
hide();
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
message.success({
|
message.success({
|
||||||
content: t('views.system.log.login.unlockSure', {
|
content: t('views.system.log.login.unlockSuss', {
|
||||||
userName: username,
|
userName: username,
|
||||||
}),
|
}),
|
||||||
duration: 3,
|
duration: 3,
|
||||||
|
|||||||
@@ -2,9 +2,8 @@
|
|||||||
import { stepState, fnToStepName } from '../hooks/useStep';
|
import { stepState, fnToStepName } from '../hooks/useStep';
|
||||||
import { Modal } from 'ant-design-vue/es';
|
import { Modal } from 'ant-design-vue/es';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { removeToken } from '@/plugins/auth-token';
|
import { delAccessToken, delRefreshToken } from '@/plugins/auth-token';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import useAppStore from '@/store/modules/app';
|
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { bootloaderDone } from '@/api/system/quick-start/bootloader';
|
import { bootloaderDone } from '@/api/system/quick-start/bootloader';
|
||||||
@@ -56,8 +55,9 @@ function fnGuideDone() {
|
|||||||
bootloaderDone()
|
bootloaderDone()
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
removeToken();
|
|
||||||
// useAppStore().bootloader = false;
|
// useAppStore().bootloader = false;
|
||||||
|
delAccessToken();
|
||||||
|
delRefreshToken();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
import { onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { TOKEN_RESPONSE_FIELD } from '@/constants/token-constants';
|
import { getAccessToken, setAccessToken } from '@/plugins/auth-token';
|
||||||
import { getToken, setToken } from '@/plugins/auth-token';
|
|
||||||
import { bootloaderStart } from '@/api/system/quick-start/bootloader';
|
import { bootloaderStart } from '@/api/system/quick-start/bootloader';
|
||||||
import { fnToStepName } from '../hooks/useStep';
|
import { fnToStepName } from '../hooks/useStep';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
@@ -19,11 +18,10 @@ function fnChangeLocale(e: any) {
|
|||||||
|
|
||||||
/**引导开始 */
|
/**引导开始 */
|
||||||
function fnGuideStart() {
|
function fnGuideStart() {
|
||||||
if (getToken()) return;
|
if (getAccessToken()) return;
|
||||||
bootloaderStart().then(res => {
|
bootloaderStart().then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
||||||
const token = res.data[TOKEN_RESPONSE_FIELD];
|
setAccessToken(res.data.accessToken, res.data.expiresIn);
|
||||||
setToken(token);
|
|
||||||
} else {
|
} else {
|
||||||
router.push({ name: 'Login' });
|
router.push({ name: 'Login' });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,10 @@ import { hasPermissions } from '@/plugins/auth-user';
|
|||||||
import { MENU_PATH_INLINE } from '@/constants/menu-constants';
|
import { MENU_PATH_INLINE } from '@/constants/menu-constants';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import useUserStore from '@/store/modules/user';
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { getDict } = useDictStore();
|
const { getDict } = useDictStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const routePath = route.path;
|
const routePath = route.path;
|
||||||
@@ -732,7 +734,7 @@ function fnGetList(pageNum?: number) {
|
|||||||
queryParams.beginTime = queryRangePicker.value[0];
|
queryParams.beginTime = queryRangePicker.value[0];
|
||||||
queryParams.endTime = queryRangePicker.value[1];
|
queryParams.endTime = queryRangePicker.value[1];
|
||||||
listRole(toRaw(queryParams)).then(res => {
|
listRole(toRaw(queryParams)).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
// 取消勾选
|
// 取消勾选
|
||||||
if (tableState.selectedRowKeys.length > 0) {
|
if (tableState.selectedRowKeys.length > 0) {
|
||||||
tableState.selectedRowKeys = [];
|
tableState.selectedRowKeys = [];
|
||||||
@@ -941,6 +943,7 @@ onMounted(() => {
|
|||||||
v-if="
|
v-if="
|
||||||
dict.sysNormalDisable.length > 0 &&
|
dict.sysNormalDisable.length > 0 &&
|
||||||
record.roleId !== 1 &&
|
record.roleId !== 1 &&
|
||||||
|
!userStore.roles?.includes(record.roleKey) &&
|
||||||
hasPermissions(['system:role:edit'])
|
hasPermissions(['system:role:edit'])
|
||||||
"
|
"
|
||||||
v-model:checked="record.statusFlag"
|
v-model:checked="record.statusFlag"
|
||||||
@@ -969,7 +972,12 @@ onMounted(() => {
|
|||||||
<template #icon><ProfileOutlined /></template>
|
<template #icon><ProfileOutlined /></template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip v-if="record.roleId !== 1">
|
<a-tooltip
|
||||||
|
v-if="
|
||||||
|
record.roleId !== 1 &&
|
||||||
|
!userStore.roles?.includes(record.roleKey)
|
||||||
|
"
|
||||||
|
>
|
||||||
<template #title>{{ t('common.editText') }}</template>
|
<template #title>{{ t('common.editText') }}</template>
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
@@ -979,7 +987,12 @@ onMounted(() => {
|
|||||||
<template #icon><FormOutlined /></template>
|
<template #icon><FormOutlined /></template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip v-if="record.roleId !== 1">
|
<a-tooltip
|
||||||
|
v-if="
|
||||||
|
record.roleId !== 1 &&
|
||||||
|
!userStore.roles?.includes(record.roleKey)
|
||||||
|
"
|
||||||
|
>
|
||||||
<template #title>{{ t('common.deleteText') }}</template>
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
@@ -1001,7 +1014,7 @@ onMounted(() => {
|
|||||||
<template #icon><SecurityScanOutlined /></template>
|
<template #icon><SecurityScanOutlined /></template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip placement="topRight" v-if="record.roleId !== 1">
|
<a-tooltip placement="topRight" v-if="false">
|
||||||
<template #title>{{
|
<template #title>{{
|
||||||
t('views.system.role.distributeUser')
|
t('views.system.role.distributeUser')
|
||||||
}}</template>
|
}}</template>
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ const password = ref('');
|
|||||||
/**解锁 */
|
/**解锁 */
|
||||||
function handleUnlock() {
|
function handleUnlock() {
|
||||||
if (maskStore.lockPasswd === password.value) {
|
if (maskStore.lockPasswd === password.value) {
|
||||||
message.success(t('components.LockScreen.validSucc'), 3);
|
|
||||||
password.value = '';
|
password.value = '';
|
||||||
maskStore.handleMaskType('none');
|
maskStore.handleMaskType('none');
|
||||||
const redirectPath = route.query?.redirect || '/index';
|
const redirectPath = route.query?.redirect || '/index';
|
||||||
@@ -79,7 +78,7 @@ onMounted(() => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="lock-screen_login-from">
|
<div class="lock-screen_login-from">
|
||||||
<a-input-group compact>
|
<a-input-group compact v-if="maskStore.lockPasswd">
|
||||||
<a-input
|
<a-input
|
||||||
type="password"
|
type="password"
|
||||||
v-model:value="password"
|
v-model:value="password"
|
||||||
@@ -92,6 +91,10 @@ onMounted(() => {
|
|||||||
<LoginOutlined />
|
<LoginOutlined />
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
|
<a-button type="primary" block @click="handleUnlock" v-else>
|
||||||
|
{{ t('components.LockScreen.enter') }}
|
||||||
|
</a-button>
|
||||||
|
|
||||||
<a-button type="text" class="logout" @click="handleBackLogin">
|
<a-button type="text" class="logout" @click="handleBackLogin">
|
||||||
{{ t('components.LockScreen.backLogin') }}
|
{{ t('components.LockScreen.backLogin') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { Modal, message } from 'ant-design-vue/es';
|
|||||||
import { parseDateToStr } from '@/utils/date-utils';
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
import { getNeDirZip, getNeFile, listNeFiles } from '@/api/tool/neFile';
|
import { getNeDirZip, getNeFile, listNeFiles } from '@/api/tool/neFile';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import ViewDrawer from '@/views/logManage/neFile/components/ViewDrawer.vue';
|
import ViewDrawer from '@/views/ne/neFile/components/ViewDrawer.vue';
|
||||||
import useNeInfoStore from '@/store/modules/neinfo';
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
import useTabsStore from '@/store/modules/tabs';
|
import useTabsStore from '@/store/modules/tabs';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
|||||||
@@ -1,35 +1,32 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onBeforeUnmount, ref, watch } from 'vue';
|
import { onBeforeUnmount, onMounted, reactive, ref, toRaw } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { message, Modal } from 'ant-design-vue/es';
|
import { message, Modal } from 'ant-design-vue';
|
||||||
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
import DissectionTree from '../tshark/components/DissectionTree.vue';
|
import DissectionTree from '../tshark/components/DissectionTree.vue';
|
||||||
import DissectionDump from '../tshark/components/DissectionDump.vue';
|
import DissectionDump from '../tshark/components/DissectionDump.vue';
|
||||||
import PacketTable from '../tshark/components/PacketTable.vue';
|
|
||||||
import { usePCAP, NO_SELECTION } from '../tshark/hooks/usePCAP';
|
|
||||||
import {
|
import {
|
||||||
RESULT_CODE_ERROR,
|
RESULT_CODE_ERROR,
|
||||||
RESULT_CODE_SUCCESS,
|
RESULT_CODE_SUCCESS,
|
||||||
} from '@/constants/result-constants';
|
} from '@/constants/result-constants';
|
||||||
import { filePullTask } from '@/api/trace/task';
|
import { filePullTask, getTraceData, listTraceData } from '@/api/trace/task';
|
||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
import { scriptUrl as wkUrl } from '@/assets/js/wiregasm_worker';
|
||||||
|
import * as wkUtil from '@/plugins/wk-worker';
|
||||||
|
import * as wsUtil from '@/plugins/ws-websocket';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import useDictStore from '@/store/modules/dict';
|
||||||
import useTabsStore from '@/store/modules/tabs';
|
import useTabsStore from '@/store/modules/tabs';
|
||||||
import saveAs from 'file-saver';
|
import saveAs from 'file-saver';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
const { t } = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const tabsStore = useTabsStore();
|
const tabsStore = useTabsStore();
|
||||||
const ws = new WS();
|
const ws = new wsUtil.WS();
|
||||||
const { t } = useI18n();
|
const wk = new wkUtil.WK();
|
||||||
const {
|
|
||||||
state,
|
|
||||||
handleSelectedTreeEntry,
|
|
||||||
handleSelectedFindSelection,
|
|
||||||
handleSelectedFrame,
|
|
||||||
handleScrollBottom,
|
|
||||||
handleFilterFrames,
|
|
||||||
handleLoadFile,
|
|
||||||
} = usePCAP();
|
|
||||||
|
|
||||||
/**跟踪编号 */
|
/**跟踪编号 */
|
||||||
const traceId = ref<string>(route.query.traceId as string);
|
const traceId = ref<string>(route.query.traceId as string);
|
||||||
@@ -44,6 +41,17 @@ function fnClose() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**字典数据 */
|
||||||
|
let dict: {
|
||||||
|
/**跟踪消息类型 */
|
||||||
|
traceMsgType: DictType[];
|
||||||
|
/**跟踪消息方向 */
|
||||||
|
traceMsgDirect: DictType[];
|
||||||
|
} = reactive({
|
||||||
|
traceMsgType: [],
|
||||||
|
traceMsgDirect: [],
|
||||||
|
});
|
||||||
|
|
||||||
/**下载触发等待 */
|
/**下载触发等待 */
|
||||||
let downLoading = ref<boolean>(false);
|
let downLoading = ref<boolean>(false);
|
||||||
|
|
||||||
@@ -82,15 +90,266 @@ function fnDownloadFile() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**获取PCAP文件 */
|
// =========== 表格数据 ==============
|
||||||
function fnFilePCAP() {
|
/**表格状态类型 */
|
||||||
filePullTask(traceId.value).then(res => {
|
type TabeStateType = {
|
||||||
|
/**加载等待 */
|
||||||
|
loading: boolean;
|
||||||
|
/**紧凑型 */
|
||||||
|
size: SizeType;
|
||||||
|
/**记录数据 */
|
||||||
|
data: object[];
|
||||||
|
/**点击选择行 */
|
||||||
|
row: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**表格状态 */
|
||||||
|
let tableState: TabeStateType = reactive({
|
||||||
|
loading: false,
|
||||||
|
size: 'small',
|
||||||
|
data: [],
|
||||||
|
row: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格字段列 */
|
||||||
|
let tableColumns: ColumnsType = [
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.msgNe'),
|
||||||
|
dataIndex: 'msgNe',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.rowTime'),
|
||||||
|
dataIndex: 'timestamp',
|
||||||
|
align: 'left',
|
||||||
|
width: 250,
|
||||||
|
customRender(opt) {
|
||||||
|
if (!opt.value) return '';
|
||||||
|
const nanoseconds = opt.value; // 纳秒时间戳
|
||||||
|
const milliseconds = Math.floor(nanoseconds / 1000000);
|
||||||
|
const nanosecondsPart = (nanoseconds % 1000000)
|
||||||
|
.toString()
|
||||||
|
.padStart(6, '0');
|
||||||
|
|
||||||
|
return `${parseDateToStr(
|
||||||
|
milliseconds,
|
||||||
|
'YYYY-MM-DD HH:mm:ss.SSS'
|
||||||
|
)}.${nanosecondsPart}`;
|
||||||
|
},
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.protocolOrInterface'),
|
||||||
|
dataIndex: 'ifType',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.msgEvent'),
|
||||||
|
dataIndex: 'msgEvent',
|
||||||
|
align: 'left',
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.msgType'),
|
||||||
|
dataIndex: 'msgType',
|
||||||
|
key: 'msgType',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.msgDirect'),
|
||||||
|
dataIndex: 'msgDirect',
|
||||||
|
key: 'msgDirect',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.srcIp'),
|
||||||
|
dataIndex: 'srcAddr',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.dstIp'),
|
||||||
|
dataIndex: 'dstAddr',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**表格分页器参数 */
|
||||||
|
let tablePagination = reactive({
|
||||||
|
/**当前页数 */
|
||||||
|
current: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 10,
|
||||||
|
/**默认的每页条数 */
|
||||||
|
defaultPageSize: 10,
|
||||||
|
/**指定每页可以显示多少条 */
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**表格分页、排序、筛选变化时触发操作, 排序方式,取值为 ascend descend */
|
||||||
|
function fnTableChange(pagination: any, filters: any, sorter: any, extra: any) {
|
||||||
|
const { field, order } = sorter;
|
||||||
|
if (order) {
|
||||||
|
queryParams.sortBy = field;
|
||||||
|
queryParams.sortOrder = order.replace('end', '');
|
||||||
|
} else {
|
||||||
|
queryParams.sortOrder = 'asc';
|
||||||
|
}
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**查询参数 */
|
||||||
|
let queryParams = reactive({
|
||||||
|
traceId: traceId.value,
|
||||||
|
sortBy: 'timestamp',
|
||||||
|
sortOrder: 'asc',
|
||||||
|
/**当前页数 */
|
||||||
|
pageNum: 1,
|
||||||
|
/**每页条数 */
|
||||||
|
pageSize: 10,
|
||||||
|
});
|
||||||
|
/**查询备份信息列表, 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) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
handleLoadFile(res.data);
|
const { total, rows } = res.data;
|
||||||
|
tablePagination.total = total;
|
||||||
|
tableState.data = rows;
|
||||||
|
if (
|
||||||
|
tablePagination.total <=
|
||||||
|
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||||
|
queryParams.pageNum !== 1
|
||||||
|
) {
|
||||||
|
tableState.loading = false;
|
||||||
|
fnGetList(queryParams.pageNum - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
tableState.loading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看帧数据
|
||||||
|
* @param row 记录信息
|
||||||
|
*/
|
||||||
|
function fnVisible(row: Record<string, any>) {
|
||||||
|
// 选中行重复点击时显示隐藏
|
||||||
|
if (row.id === tableState.row.id && state.selectedFrame !== 0) {
|
||||||
|
state.selectedFrame = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getTraceData(row.id).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
Object.assign(tableState.row, res.data);
|
||||||
|
const blob = generatePCAP(res.data.timestamp / 1e3, res.data.rawMsg);
|
||||||
|
wk.send({ type: 'process', file: blob });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**生成PCAP-blob */
|
||||||
|
function generatePCAP(timestamp: number, base64Data: string): Blob {
|
||||||
|
// 1. 转换数据
|
||||||
|
const binaryString = atob(base64Data);
|
||||||
|
const len = binaryString.length;
|
||||||
|
const bytes = new Uint8Array(len);
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
bytes[i] = binaryString.charCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 创建PCAP文件头
|
||||||
|
const fileHeader = new Uint8Array([
|
||||||
|
0xd4,
|
||||||
|
0xc3,
|
||||||
|
0xb2,
|
||||||
|
0xa1, // magic_number (微秒级)
|
||||||
|
0x02,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00, // version_major(2) + version_minor(4)
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00, // thiszone (UTC)
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00, // sigfigs (固定0)
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x04,
|
||||||
|
0x00, // snaplen (1024)
|
||||||
|
0x71,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00, // network (LINKTYPE_LINUX_SLL)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 3. 构造Linux cooked头
|
||||||
|
const cookedHeader = new Uint8Array(16);
|
||||||
|
const view = new DataView(cookedHeader.buffer);
|
||||||
|
view.setUint16(0, 0x0000, false); // 数据包类型(主机→网络)
|
||||||
|
view.setUint16(2, 0x0304, false); // 地址类型(ARPHRD_ETHER)
|
||||||
|
view.setUint16(4, 0x0008, false); // 协议类型(ETH_P_IP)
|
||||||
|
view.setUint16(14, 0x0800, false); // 数据包类型(PACKET_HOST)
|
||||||
|
|
||||||
|
// 4. 合并链路层头与数据
|
||||||
|
const fullData = new Uint8Array(cookedHeader.length + bytes.length);
|
||||||
|
fullData.set(cookedHeader);
|
||||||
|
fullData.set(bytes, cookedHeader.length);
|
||||||
|
|
||||||
|
// 5. 生成时间戳
|
||||||
|
const date = new Date(timestamp);
|
||||||
|
const secs = Math.floor(date.getTime() / 1000);
|
||||||
|
const usecs = (date.getTime() % 1000) * 1000;
|
||||||
|
|
||||||
|
// 6. 构造PCAP报文头
|
||||||
|
const packetHeader = new Uint8Array(16);
|
||||||
|
const headerView = new DataView(packetHeader.buffer);
|
||||||
|
headerView.setUint32(0, secs, true); // 时间戳秒
|
||||||
|
headerView.setUint32(4, usecs, true); // 时间戳微秒
|
||||||
|
headerView.setUint32(8, fullData.length, true); // 捕获长度
|
||||||
|
headerView.setUint32(12, fullData.length, true); // 原始长度
|
||||||
|
|
||||||
|
// 7. 合并所有数据
|
||||||
|
const finalData = new Uint8Array(
|
||||||
|
fileHeader.length + packetHeader.length + fullData.length
|
||||||
|
);
|
||||||
|
finalData.set(fileHeader);
|
||||||
|
finalData.set(packetHeader, fileHeader.length);
|
||||||
|
finalData.set(fullData, fileHeader.length + packetHeader.length);
|
||||||
|
|
||||||
|
// 8. 文件Blob对象
|
||||||
|
return new Blob([finalData], { type: 'application/octet-stream' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========== WS ==============
|
||||||
/**接收数据后回调 */
|
/**接收数据后回调 */
|
||||||
function wsMessage(res: Record<string, any>) {
|
function wsMessage(res: Record<string, any>) {
|
||||||
const { code, requestId, data } = res;
|
const { code, requestId, data } = res;
|
||||||
@@ -101,7 +360,6 @@ function wsMessage(res: Record<string, any>) {
|
|||||||
|
|
||||||
// 建联时发送请求
|
// 建联时发送请求
|
||||||
if (!requestId && data.clientId) {
|
if (!requestId && data.clientId) {
|
||||||
fnFilePCAP();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,13 +368,16 @@ function wsMessage(res: Record<string, any>) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.groupId === `2_${traceId.value}`) {
|
if (data.groupId === `2_${traceId.value}`) {
|
||||||
fnFilePCAP();
|
// 第一页降序时实时添加记录
|
||||||
|
if (queryParams.pageNum === 1 && queryParams.sortOrder === 'desc') {
|
||||||
|
tableState.data.unshift(data);
|
||||||
|
}
|
||||||
|
tablePagination.total += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**建立WS连接 */
|
/**建立WS连接 */
|
||||||
function fnWS() {
|
function fnWS() {
|
||||||
const options: OptionsType = {
|
const options: wsUtil.OptionsType = {
|
||||||
url: '/ws',
|
url: '/ws',
|
||||||
params: {
|
params: {
|
||||||
/**订阅通道组
|
/**订阅通道组
|
||||||
@@ -135,15 +396,167 @@ function fnWS() {
|
|||||||
ws.connect(options);
|
ws.connect(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
// =========== WK ==============
|
||||||
() => state.initialized,
|
const NO_SELECTION = { id: '', idx: 0, start: 0, length: 0 };
|
||||||
v => {
|
|
||||||
v && fnWS();
|
type StateType = {
|
||||||
|
/**初始化 */
|
||||||
|
initialized: boolean;
|
||||||
|
/**当前选中的帧编号 */
|
||||||
|
selectedFrame: number;
|
||||||
|
/**当前选中的帧数据 */
|
||||||
|
packetFrame: { tree: any[]; data_sources: any[] };
|
||||||
|
/**pcap包帧数据 */
|
||||||
|
packetFrameTreeMap: Map<string, any> | null;
|
||||||
|
/**当前选中的帧数据 */
|
||||||
|
selectedTree: typeof NO_SELECTION;
|
||||||
|
/**选择帧的Dump数据标签 */
|
||||||
|
selectedDataSourceIndex: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = reactive<StateType>({
|
||||||
|
initialized: false,
|
||||||
|
selectedFrame: 0,
|
||||||
|
/**当前选中的帧数据 */
|
||||||
|
packetFrame: { tree: [], data_sources: [] },
|
||||||
|
packetFrameTreeMap: null, // 注意:Map 需要额外处理
|
||||||
|
selectedTree: NO_SELECTION, // NO_SELECTION 需要定义
|
||||||
|
/**选择帧的Dump数据标签 */
|
||||||
|
selectedDataSourceIndex: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**解析帧数据为简单结构 */
|
||||||
|
function parseFrameTree(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 = parseFrameTree(`${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 handleSelectedFindSelection(src_idx: number, pos: number) {
|
||||||
|
// console.log('fnSelectedFindSelection', pos);
|
||||||
|
if (state.packetFrameTreeMap == null) return;
|
||||||
|
// find the smallest one
|
||||||
|
let current = null;
|
||||||
|
for (let [k, pp] of state.packetFrameTreeMap) {
|
||||||
|
if (pp.idx !== src_idx) continue;
|
||||||
|
|
||||||
|
if (pos >= pp.start && pos <= pp.start + pp.length) {
|
||||||
|
if (
|
||||||
|
current != null &&
|
||||||
|
state.packetFrameTreeMap.get(current).length > pp.length
|
||||||
|
) {
|
||||||
|
current = k;
|
||||||
|
} else {
|
||||||
|
current = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current != null) {
|
||||||
|
state.selectedTree = state.packetFrameTreeMap.get(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**帧数据点击选中 */
|
||||||
|
function handleSelectedTree(e: any) {
|
||||||
|
state.selectedTree = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wkMessage(res: Record<string, any>) {
|
||||||
|
// console.log('wkMessage', res);
|
||||||
|
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;
|
||||||
|
fnGetList();
|
||||||
|
break;
|
||||||
|
case 'frames':
|
||||||
|
const { frames } = res.data;
|
||||||
|
|
||||||
|
// 有匹配的选择第一个
|
||||||
|
if (frames.length > 0) {
|
||||||
|
state.selectedFrame = frames[0].number;
|
||||||
|
wk.send({ type: 'select', number: state.selectedFrame });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'selected':
|
||||||
|
// 去掉两层
|
||||||
|
res.data.tree.shift();
|
||||||
|
res.data.tree.shift();
|
||||||
|
state.packetFrame = res.data;
|
||||||
|
state.packetFrameTreeMap = parseFrameTree('root', res.data);
|
||||||
|
state.selectedTree = NO_SELECTION;
|
||||||
|
state.selectedDataSourceIndex = 0;
|
||||||
|
break;
|
||||||
|
case 'processed':
|
||||||
|
// setStatus(`Error: non-zero return code (${e.data.code})`);
|
||||||
|
// 加载数据
|
||||||
|
wk.send({
|
||||||
|
type: 'frames',
|
||||||
|
filter: '',
|
||||||
|
skip: 0,
|
||||||
|
limit: 1,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn(res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**建立WK连接 */
|
||||||
|
function fnWK() {
|
||||||
|
const options: wkUtil.OptionsType = {
|
||||||
|
url: wkUrl,
|
||||||
|
onmessage: wkMessage,
|
||||||
|
onerror: (ev: any) => {
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
//建立连接
|
||||||
|
wk.connect(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始字典数据
|
||||||
|
Promise.allSettled([getDict('trace_msg_type'), getDict('trace_msg_direct')])
|
||||||
|
.then(resArr => {
|
||||||
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
dict.traceMsgType = resArr[0].value;
|
||||||
|
}
|
||||||
|
if (resArr[1].status === 'fulfilled') {
|
||||||
|
dict.traceMsgDirect = resArr[1].value;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
fnWK();
|
||||||
|
fnWS();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
ws.close();
|
wk.send({ type: 'close' }) && wk.close();
|
||||||
|
if (ws.state() <= WebSocket.OPEN) ws.close();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -154,8 +567,9 @@ onBeforeUnmount(() => {
|
|||||||
:loading="!state.initialized"
|
:loading="!state.initialized"
|
||||||
:body-style="{ padding: '12px' }"
|
:body-style="{ padding: '12px' }"
|
||||||
>
|
>
|
||||||
<div class="toolbar">
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
<a-space :size="8" class="toolbar-oper">
|
<template #title>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
<a-button type="default" @click.prevent="fnClose()">
|
<a-button type="default" @click.prevent="fnClose()">
|
||||||
<template #icon><CloseOutlined /></template>
|
<template #icon><CloseOutlined /></template>
|
||||||
{{ t('common.close') }}
|
{{ t('common.close') }}
|
||||||
@@ -173,89 +587,65 @@ onBeforeUnmount(() => {
|
|||||||
<strong>{{ traceId }}</strong>
|
<strong>{{ traceId }}</strong>
|
||||||
</span>
|
</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
<div class="toolbar-info">
|
<!-- 插槽-卡片右侧 -->
|
||||||
<a-tag color="green" v-show="!!state.currentFilter">
|
<template #extra>
|
||||||
{{ state.currentFilter }}
|
<a-tooltip>
|
||||||
</a-tag>
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
<span> Matched Frame: {{ state.totalFrames }} </span>
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
</div>
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
</a-button>
|
||||||
<!-- 包信息 -->
|
</a-tooltip>
|
||||||
<a-popover
|
</template>
|
||||||
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
|
<a-table
|
||||||
:columns="state.columns"
|
class="table"
|
||||||
:data="state.packetFrames"
|
row-key="id"
|
||||||
:selectedFrame="state.selectedFrame"
|
:columns="tableColumns"
|
||||||
:onSelectedFrame="handleSelectedFrame"
|
:loading="tableState.loading"
|
||||||
:onScrollBottom="handleScrollBottom"
|
:data-source="tableState.data"
|
||||||
></PacketTable>
|
:size="tableState.size"
|
||||||
|
:pagination="tablePagination"
|
||||||
<a-row>
|
:row-class-name="(record:any) => {
|
||||||
|
if (record.id === tableState.row.id) {
|
||||||
|
return 'table-striped-select';
|
||||||
|
}
|
||||||
|
return record.msgDirect === 0 ? 'table-striped-recv' : 'table-striped-send';
|
||||||
|
}"
|
||||||
|
:customRow="
|
||||||
|
record => {
|
||||||
|
return {
|
||||||
|
onClick: () => fnVisible(record),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"
|
||||||
|
@change="fnTableChange"
|
||||||
|
:scroll="{ x: tableColumns.length * 150, y: 'calc(100vh - 300px)' }"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'msgType'">
|
||||||
|
<DictTag :options="dict.traceMsgType" :value="record.msgType" />
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'msgDirect'">
|
||||||
|
<DictTag :options="dict.traceMsgDirect" :value="record.msgDirect" />
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<!-- 帧数据 -->
|
||||||
|
<a-row
|
||||||
|
:gutter="16"
|
||||||
|
style="border: 2px rgb(217 217 217) solid; border-radius: 6px"
|
||||||
|
v-show="state.selectedFrame == 1"
|
||||||
|
>
|
||||||
<a-col :lg="12" :md="12" :xs="24" class="tree">
|
<a-col :lg="12" :md="12" :xs="24" class="tree">
|
||||||
<!-- 帧数据 -->
|
<!-- 帧数据 -->
|
||||||
<DissectionTree
|
<DissectionTree
|
||||||
id="root"
|
id="root"
|
||||||
:select="handleSelectedTreeEntry"
|
:select="handleSelectedTree"
|
||||||
:selected="state.selectedTreeEntry"
|
:selected="state.selectedTree"
|
||||||
:tree="state.selectedPacket.tree"
|
:tree="state.packetFrame.tree"
|
||||||
/>
|
/>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="12" :md="12" :xs="24" class="dump">
|
<a-col :lg="12" :md="12" :xs="24" class="dump">
|
||||||
@@ -268,15 +658,15 @@ onBeforeUnmount(() => {
|
|||||||
<a-tab-pane
|
<a-tab-pane
|
||||||
:key="idx"
|
:key="idx"
|
||||||
:tab="v.name"
|
:tab="v.name"
|
||||||
v-for="(v, idx) in state.selectedPacket.data_sources"
|
v-for="(v, idx) in state.packetFrame.data_sources"
|
||||||
style="overflow: auto"
|
style="overflow: auto"
|
||||||
>
|
>
|
||||||
<DissectionDump
|
<DissectionDump
|
||||||
:base64="v.data"
|
:base64="v.data"
|
||||||
:select="(pos:number)=>handleSelectedFindSelection(idx, pos)"
|
:select="(pos:number)=>handleSelectedFindSelection(idx, pos)"
|
||||||
:selected="
|
:selected="
|
||||||
idx === state.selectedTreeEntry.idx
|
idx === state.selectedTree.idx
|
||||||
? state.selectedTreeEntry
|
? state.selectedTree
|
||||||
: NO_SELECTION
|
: NO_SELECTION
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
@@ -289,24 +679,20 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.toolbar {
|
.table :deep(.ant-pagination) {
|
||||||
display: flex;
|
padding: 0 24px;
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
}
|
||||||
.toolbar-info {
|
.table :deep(.table-striped-select) td {
|
||||||
flex: 1;
|
background-color: #c2c2c2;
|
||||||
text-align: right;
|
cursor: pointer;
|
||||||
padding-right: 8px;
|
|
||||||
}
|
}
|
||||||
|
.table :deep(.table-striped-recv) td {
|
||||||
.summary {
|
background-color: #a9e2ff;
|
||||||
display: flex;
|
cursor: pointer;
|
||||||
flex-direction: column;
|
|
||||||
}
|
}
|
||||||
.summary-item > span:first-child {
|
.table :deep(.table-striped-send) td {
|
||||||
font-weight: 600;
|
background-color: #bcfbb3;
|
||||||
margin-right: 6px;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tree {
|
.tree {
|
||||||
|
|||||||
@@ -1,24 +1,60 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted, toRaw } from 'vue';
|
import { reactive, onMounted, toRaw, ref } from 'vue';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { ProModal } from 'antdv-pro-modal';
|
import { ProModal } from 'antdv-pro-modal';
|
||||||
import { Modal } from 'ant-design-vue/es';
|
|
||||||
import { SizeType } from 'ant-design-vue/es/config-provider';
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
import { ColumnsType } from 'ant-design-vue/es/table';
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
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 useDictStore from '@/store/modules/dict';
|
||||||
|
import useTabsStore from '@/store/modules/tabs';
|
||||||
|
import { type Dayjs } from 'dayjs';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import { listTraceData } from '@/api/trace/analysis';
|
import { getTraceData, listTraceData } from '@/api/trace/task';
|
||||||
|
import { decode } from 'js-base64';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const { getDict } = useDictStore();
|
||||||
|
const tabsStore = useTabsStore();
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
/**跟踪编号 */
|
||||||
|
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 dict: {
|
||||||
|
/**跟踪消息类型 */
|
||||||
|
traceMsgType: DictType[];
|
||||||
|
/**跟踪消息方向 */
|
||||||
|
traceMsgDirect: DictType[];
|
||||||
|
} = reactive({
|
||||||
|
traceMsgType: [],
|
||||||
|
traceMsgDirect: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**开始结束时间 */
|
||||||
|
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>(undefined);
|
||||||
/**查询参数 */
|
/**查询参数 */
|
||||||
let queryParams = reactive({
|
let queryParams = reactive({
|
||||||
/**移动号 */
|
traceId: traceId.value,
|
||||||
imsi: '',
|
sortBy: 'timestamp',
|
||||||
/**移动号 */
|
sortOrder: 'asc',
|
||||||
msisdn: '',
|
/**开始时间 */
|
||||||
|
beginTime: undefined as undefined | number,
|
||||||
|
/**结束时间 */
|
||||||
|
endTime: undefined as undefined | number,
|
||||||
/**当前页数 */
|
/**当前页数 */
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
/**每页条数 */
|
/**每页条数 */
|
||||||
@@ -28,10 +64,10 @@ let queryParams = reactive({
|
|||||||
/**查询参数重置 */
|
/**查询参数重置 */
|
||||||
function fnQueryReset() {
|
function fnQueryReset() {
|
||||||
queryParams = Object.assign(queryParams, {
|
queryParams = Object.assign(queryParams, {
|
||||||
imsi: '',
|
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
});
|
});
|
||||||
|
queryRangePicker.value = undefined;
|
||||||
tablePagination.current = 1;
|
tablePagination.current = 1;
|
||||||
tablePagination.pageSize = 20;
|
tablePagination.pageSize = 20;
|
||||||
fnGetList();
|
fnGetList();
|
||||||
@@ -43,8 +79,6 @@ type TabeStateType = {
|
|||||||
loading: boolean;
|
loading: boolean;
|
||||||
/**紧凑型 */
|
/**紧凑型 */
|
||||||
size: SizeType;
|
size: SizeType;
|
||||||
/**搜索栏 */
|
|
||||||
seached: boolean;
|
|
||||||
/**记录数据 */
|
/**记录数据 */
|
||||||
data: object[];
|
data: object[];
|
||||||
};
|
};
|
||||||
@@ -52,66 +86,87 @@ type TabeStateType = {
|
|||||||
/**表格状态 */
|
/**表格状态 */
|
||||||
let tableState: TabeStateType = reactive({
|
let tableState: TabeStateType = reactive({
|
||||||
loading: false,
|
loading: false,
|
||||||
size: 'middle',
|
size: 'small',
|
||||||
seached: true,
|
|
||||||
data: [],
|
data: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
/**表格字段列 */
|
/**表格字段列 */
|
||||||
let tableColumns: ColumnsType = [
|
let tableColumns: ColumnsType = [
|
||||||
{
|
{
|
||||||
title: t('views.traceManage.analysis.trackTaskId'),
|
title: t('common.rowId'),
|
||||||
dataIndex: 'taskId',
|
dataIndex: 'id',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.traceManage.analysis.imsi'),
|
title: t('views.traceManage.task.msgNe'),
|
||||||
dataIndex: 'imsi',
|
dataIndex: 'msgNe',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.traceManage.analysis.msisdn'),
|
title: t('views.traceManage.task.rowTime'),
|
||||||
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',
|
dataIndex: 'timestamp',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
|
width: 250,
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
if (!opt.value) return '';
|
if (!opt.value) return '';
|
||||||
return parseDateToStr(opt.value);
|
const nanoseconds = opt.value; // 纳秒时间戳
|
||||||
|
const milliseconds = Math.floor(nanoseconds / 1000000);
|
||||||
|
const nanosecondsPart = (nanoseconds % 1000000)
|
||||||
|
.toString()
|
||||||
|
.padStart(6, '0');
|
||||||
|
|
||||||
|
return `${parseDateToStr(
|
||||||
|
milliseconds,
|
||||||
|
'YYYY-MM-DD HH:mm:ss.SSS'
|
||||||
|
)}.${nanosecondsPart}`;
|
||||||
},
|
},
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.protocolOrInterface'),
|
||||||
|
dataIndex: 'ifType',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.msgEvent'),
|
||||||
|
dataIndex: 'msgEvent',
|
||||||
|
align: 'left',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.msgType'),
|
||||||
|
dataIndex: 'msgType',
|
||||||
|
key: 'msgType',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.msgDirect'),
|
||||||
|
dataIndex: 'msgDirect',
|
||||||
|
key: 'msgDirect',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.srcIp'),
|
||||||
|
dataIndex: 'srcAddr',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.dstIp'),
|
||||||
|
dataIndex: 'dstAddr',
|
||||||
|
align: 'left',
|
||||||
|
width: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('common.operate'),
|
title: t('common.operate'),
|
||||||
key: 'id',
|
key: 'id',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -148,6 +203,18 @@ function fnTableSize({ key }: MenuInfo) {
|
|||||||
tableState.size = key as SizeType;
|
tableState.size = key as SizeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**表格分页、排序、筛选变化时触发操作, 排序方式,取值为 ascend descend */
|
||||||
|
function fnTableChange(pagination: any, filters: any, sorter: any, extra: any) {
|
||||||
|
const { field, order } = sorter;
|
||||||
|
if (order) {
|
||||||
|
queryParams.sortBy = field;
|
||||||
|
queryParams.sortOrder = order.replace('end', '');
|
||||||
|
} else {
|
||||||
|
queryParams.sortOrder = 'asc';
|
||||||
|
}
|
||||||
|
fnGetList(1);
|
||||||
|
}
|
||||||
|
|
||||||
/**查询备份信息列表, pageNum初始页数 */
|
/**查询备份信息列表, pageNum初始页数 */
|
||||||
function fnGetList(pageNum?: number) {
|
function fnGetList(pageNum?: number) {
|
||||||
if (tableState.loading) return;
|
if (tableState.loading) return;
|
||||||
@@ -155,6 +222,19 @@ function fnGetList(pageNum?: number) {
|
|||||||
if (pageNum) {
|
if (pageNum) {
|
||||||
queryParams.pageNum = pageNum;
|
queryParams.pageNum = pageNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 时间范围
|
||||||
|
if (
|
||||||
|
Array.isArray(queryRangePicker.value) &&
|
||||||
|
queryRangePicker.value.length > 0
|
||||||
|
) {
|
||||||
|
queryParams.beginTime = queryRangePicker.value[0].valueOf();
|
||||||
|
queryParams.endTime = queryRangePicker.value[1].valueOf();
|
||||||
|
} else {
|
||||||
|
queryParams.beginTime = undefined;
|
||||||
|
queryParams.endTime = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
listTraceData(toRaw(queryParams)).then(res => {
|
listTraceData(toRaw(queryParams)).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
const { total, rows } = res.data;
|
const { total, rows } = res.data;
|
||||||
@@ -199,24 +279,17 @@ let modalState: ModalStateType = reactive({
|
|||||||
* @param row 记录信息
|
* @param row 记录信息
|
||||||
*/
|
*/
|
||||||
function fnModalVisible(row: Record<string, any>) {
|
function fnModalVisible(row: Record<string, any>) {
|
||||||
// 进制转数据
|
getTraceData(row.id).then(res => {
|
||||||
const hexString = parseBase64Data(row.rawMsg);
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
const rawData = convertToReadableFormat(hexString);
|
Object.assign(modalState.from, res.data);
|
||||||
modalState.from.rawData = rawData;
|
// 进制转数据
|
||||||
// RAW解析HTML
|
const hexString = parseBase64Data(res.data.rawMsg);
|
||||||
// getTraceRawInfo(row.id).then(res => {
|
const rawData = convertToReadableFormat(hexString);
|
||||||
// if (res.code === RESULT_CODE_SUCCESS) {
|
modalState.from.rawData = rawData;
|
||||||
// const htmlString = rawDataHTMLScript(res.msg);
|
modalState.title = t('views.traceManage.task.taskInfo');
|
||||||
// modalState.from.rawDataHTML = htmlString;
|
modalState.open = true;
|
||||||
// modalState.from.downBtn = true;
|
}
|
||||||
// } else {
|
|
||||||
// modalState.from.rawDataHTML = t('views.traceManage.analysis.noData');
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
modalState.title = t('views.traceManage.analysis.taskTitle', {
|
|
||||||
num: row.imsi,
|
|
||||||
});
|
});
|
||||||
modalState.open = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -224,15 +297,13 @@ function fnModalVisible(row: Record<string, any>) {
|
|||||||
*/
|
*/
|
||||||
function fnModalVisibleClose() {
|
function fnModalVisibleClose() {
|
||||||
modalState.open = false;
|
modalState.open = false;
|
||||||
modalState.from.downBtn = false;
|
|
||||||
modalState.from.rawDataHTML = '';
|
|
||||||
modalState.from.rawData = '';
|
modalState.from.rawData = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将Base64编码解码为字节数组
|
// 将Base64编码解码为字节数组
|
||||||
function parseBase64Data(hexData: string) {
|
function parseBase64Data(base64Data: string) {
|
||||||
// 将Base64编码解码为字节数组
|
// 将Base64编码解码为字节数组
|
||||||
const byteString = atob(hexData);
|
const byteString = decode(base64Data);
|
||||||
const byteArray = new Uint8Array(byteString.length);
|
const byteArray = new Uint8Array(byteString.length);
|
||||||
for (let i = 0; i < byteString.length; i++) {
|
for (let i = 0; i < byteString.length; i++) {
|
||||||
byteArray[i] = byteString.charCodeAt(i);
|
byteArray[i] = byteString.charCodeAt(i);
|
||||||
@@ -252,7 +323,7 @@ function convertToReadableFormat(hexString: string) {
|
|||||||
let result = '';
|
let result = '';
|
||||||
let asciiResult = '';
|
let asciiResult = '';
|
||||||
let arr = [];
|
let arr = [];
|
||||||
let row = 100;
|
let row = 10000;
|
||||||
for (let i = 0; i < hexString.length; i += 2) {
|
for (let i = 0; i < hexString.length; i += 2) {
|
||||||
const hexChars = hexString.substring(i, i + 2);
|
const hexChars = hexString.substring(i, i + 2);
|
||||||
const decimal = parseInt(hexChars, 16);
|
const decimal = parseInt(hexChars, 16);
|
||||||
@@ -286,100 +357,46 @@ function convertToReadableFormat(hexString: string) {
|
|||||||
return arr;
|
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(() => {
|
onMounted(() => {
|
||||||
// 获取列表数据
|
// 初始字典数据
|
||||||
fnGetList();
|
Promise.allSettled([getDict('trace_msg_type'), getDict('trace_msg_direct')])
|
||||||
|
.then(resArr => {
|
||||||
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
dict.traceMsgType = resArr[0].value;
|
||||||
|
}
|
||||||
|
if (resArr[1].status === 'fulfilled') {
|
||||||
|
dict.traceMsgDirect = resArr[1].value;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// 获取列表数据
|
||||||
|
fnGetList();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<a-card
|
<a-card
|
||||||
v-show="tableState.seached"
|
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
||||||
>
|
>
|
||||||
<!-- 表格搜索栏 -->
|
<!-- 表格搜索栏 -->
|
||||||
<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-col :lg="8" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.traceManage.analysis.imsi')"
|
:label="t('views.traceManage.task.rowTime')"
|
||||||
name="imsi"
|
name="queryRangePicker"
|
||||||
>
|
>
|
||||||
<a-input
|
<a-range-picker
|
||||||
v-model:value="queryParams.imsi"
|
v-model:value="queryRangePicker"
|
||||||
:allow-clear="true"
|
:bordered="true"
|
||||||
:placeholder="t('views.traceManage.analysis.imsiPlease')"
|
:allow-clear="false"
|
||||||
></a-input>
|
style="width: 100%"
|
||||||
</a-form-item>
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
</a-col>
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
></a-range-picker>
|
||||||
<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-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="6" :md="12" :xs="24">
|
<a-col :lg="6" :md="12" :xs="24">
|
||||||
@@ -402,20 +419,22 @@ onMounted(() => {
|
|||||||
|
|
||||||
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||||
<!-- 插槽-卡片左侧侧 -->
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
<template #title> </template>
|
<template #title>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
|
<a-button type="default" @click.prevent="fnClose()">
|
||||||
|
<template #icon><CloseOutlined /></template>
|
||||||
|
{{ t('common.close') }}
|
||||||
|
</a-button>
|
||||||
|
<span>
|
||||||
|
{{ t('views.traceManage.task.traceId') }}:
|
||||||
|
<strong>{{ traceId }}</strong>
|
||||||
|
</span>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 插槽-卡片右侧 -->
|
<!-- 插槽-卡片右侧 -->
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<a-space :size="8" align="center">
|
<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>
|
<a-tooltip>
|
||||||
<template #title>{{ t('common.reloadText') }}</template>
|
<template #title>{{ t('common.reloadText') }}</template>
|
||||||
<a-button type="text" @click.prevent="fnGetList()">
|
<a-button type="text" @click.prevent="fnGetList()">
|
||||||
@@ -459,11 +478,18 @@ onMounted(() => {
|
|||||||
:size="tableState.size"
|
:size="tableState.size"
|
||||||
:pagination="tablePagination"
|
:pagination="tablePagination"
|
||||||
:scroll="{ x: true }"
|
:scroll="{ x: true }"
|
||||||
|
@change="fnTableChange"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'msgType'">
|
||||||
|
<DictTag :options="dict.traceMsgType" :value="record.msgType" />
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'msgDirect'">
|
||||||
|
<DictTag :options="dict.traceMsgDirect" :value="record.msgDirect" />
|
||||||
|
</template>
|
||||||
<template v-if="column.key === 'id'">
|
<template v-if="column.key === 'id'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
<a-tooltip>
|
<a-tooltip placement="topRight">
|
||||||
<template #title>{{ t('common.viewText') }}</template>
|
<template #title>{{ t('common.viewText') }}</template>
|
||||||
<a-button type="link" @click.prevent="fnModalVisible(record)">
|
<a-button type="link" @click.prevent="fnModalVisible(record)">
|
||||||
<template #icon><ProfileOutlined /></template>
|
<template #icon><ProfileOutlined /></template>
|
||||||
@@ -482,31 +508,111 @@ onMounted(() => {
|
|||||||
:title="modalState.title"
|
:title="modalState.title"
|
||||||
:open="modalState.open"
|
:open="modalState.open"
|
||||||
@cancel="fnModalVisibleClose"
|
@cancel="fnModalVisibleClose"
|
||||||
|
:footer="false"
|
||||||
>
|
>
|
||||||
<div class="raw-title">
|
<a-form
|
||||||
{{ t('views.traceManage.analysis.signalData') }}
|
name="modalStateFrom"
|
||||||
</div>
|
layout="horizontal"
|
||||||
<a-row class="raw" v-for="v in modalState.from.rawData" :key="v.row">
|
:label-col="{ span: 8 }"
|
||||||
<a-col class="num" :span="2">{{ v.row }}</a-col>
|
:label-wrap="true"
|
||||||
<a-col class="code" :span="12">{{ v.code }}</a-col>
|
>
|
||||||
<a-col class="txt" :span="10">{{ v.asciiText }}</a-col>
|
<a-row>
|
||||||
</a-row>
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-divider />
|
<a-form-item
|
||||||
<!-- <div class="raw-title">
|
:label="t('views.traceManage.task.msgType')"
|
||||||
{{ t('views.traceManage.analysis.signalDetail') }}
|
name="msgType"
|
||||||
<a-button
|
>
|
||||||
type="dashed"
|
<DictTag
|
||||||
size="small"
|
:options="dict.traceMsgType"
|
||||||
@click.prevent="fnDownloadFile"
|
:value="modalState.from.msgType"
|
||||||
v-if="modalState.from.downBtn"
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.traceManage.task.msgDirect')"
|
||||||
|
name="msgDirect"
|
||||||
|
>
|
||||||
|
<DictTag
|
||||||
|
:options="dict.traceMsgDirect"
|
||||||
|
:value="modalState.from.msgDirect"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.traceManage.task.srcIp')"
|
||||||
|
name="srcAddr"
|
||||||
|
>
|
||||||
|
{{ modalState.from.srcAddr }}
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.traceManage.task.dstIp')"
|
||||||
|
name="dstAddr"
|
||||||
|
>
|
||||||
|
{{ modalState.from.dstAddr }}
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.traceManage.task.msgNe')"
|
||||||
|
name="msgNe"
|
||||||
|
>
|
||||||
|
{{ modalState.from.msgNe }}
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.traceManage.task.msgEvent')"
|
||||||
|
name="msgEvent"
|
||||||
|
>
|
||||||
|
{{ modalState.from.msgEvent }}
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
v-if="modalState.from.imsi"
|
||||||
|
:label="t('views.traceManage.task.imsi')"
|
||||||
|
name="imsi"
|
||||||
|
:label-col="{ span: 4 }"
|
||||||
>
|
>
|
||||||
<template #icon>
|
{{ modalState.from.imsi }}
|
||||||
<DownloadOutlined />
|
</a-form-item>
|
||||||
</template>
|
|
||||||
{{ t('views.traceManage.analysis.taskDownText') }}
|
<a-form-item
|
||||||
</a-button>
|
v-if="modalState.from.ifType"
|
||||||
</div> -->
|
:label="t('views.traceManage.task.protocolOrInterface')"
|
||||||
<!-- <div class="raw-html" v-html="modalState.from.rawDataHTML"></div> -->
|
name="ifType"
|
||||||
|
:label-col="{ span: 4 }"
|
||||||
|
>
|
||||||
|
{{ modalState.from.ifType }}
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
:label="t('views.traceManage.task.msgLen')"
|
||||||
|
name="length"
|
||||||
|
:label-col="{ span: 4 }"
|
||||||
|
>
|
||||||
|
{{ modalState.from.length }}
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item>
|
||||||
|
<a-row class="raw" 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-form-item>
|
||||||
|
</a-form>
|
||||||
</ProModal>
|
</ProModal>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
@@ -517,24 +623,15 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.raw {
|
.raw {
|
||||||
&-title {
|
|
||||||
color: #000000d9;
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: 1.8;
|
|
||||||
}
|
|
||||||
.num {
|
.num {
|
||||||
background-color: #e5e5e5;
|
background-color: #f0f0f0;
|
||||||
}
|
}
|
||||||
.code {
|
.code {
|
||||||
background-color: #e7e6ff;
|
background-color: #0078d4;
|
||||||
|
color: #fff;
|
||||||
}
|
}
|
||||||
.txt {
|
.txt {
|
||||||
background-color: #ffe3e5;
|
background-color: #d9d9d9;
|
||||||
}
|
|
||||||
|
|
||||||
&-html {
|
|
||||||
max-height: 300px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
updateTraceTask,
|
updateTraceTask,
|
||||||
} from '@/api/trace/task';
|
} from '@/api/trace/task';
|
||||||
import useDictStore from '@/store/modules/dict';
|
import useDictStore from '@/store/modules/dict';
|
||||||
import { regExpIPv4, regExpPort } from '@/utils/regular-utils';
|
import { regExpIPv4 } from '@/utils/regular-utils';
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import { parseObjHumpToLine } from '@/utils/parse-utils';
|
import { parseObjHumpToLine } from '@/utils/parse-utils';
|
||||||
const neInfoStore = useNeInfoStore();
|
const neInfoStore = useNeInfoStore();
|
||||||
@@ -33,8 +33,11 @@ const route = useRoute();
|
|||||||
let dict: {
|
let dict: {
|
||||||
/**跟踪类型 */
|
/**跟踪类型 */
|
||||||
traceType: DictType[];
|
traceType: DictType[];
|
||||||
|
/**跟踪接口 */
|
||||||
|
traceInterfaces: DictType[];
|
||||||
} = reactive({
|
} = reactive({
|
||||||
traceType: [],
|
traceType: [],
|
||||||
|
traceInterfaces: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
/**网元类型_多neId */
|
/**网元类型_多neId */
|
||||||
@@ -98,40 +101,28 @@ let tableState: TabeStateType = reactive({
|
|||||||
|
|
||||||
/**表格字段列 */
|
/**表格字段列 */
|
||||||
let tableColumns: ColumnsType = [
|
let tableColumns: ColumnsType = [
|
||||||
{
|
|
||||||
title: t('views.ne.common.neType'),
|
|
||||||
dataIndex: 'neType',
|
|
||||||
align: 'left',
|
|
||||||
sorter: {
|
|
||||||
compare: (a, b) => 1,
|
|
||||||
multiple: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.ne.common.neId'),
|
|
||||||
dataIndex: 'neId',
|
|
||||||
align: 'left',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: t('views.traceManage.task.traceId'),
|
title: t('views.traceManage.task.traceId'),
|
||||||
dataIndex: 'traceId',
|
dataIndex: 'traceId',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.traceManage.task.trackType'),
|
title: t('views.traceManage.task.trackType'),
|
||||||
dataIndex: 'traceType',
|
dataIndex: 'traceType',
|
||||||
key: 'traceType',
|
key: 'traceType',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.traceManage.task.startTime'),
|
title: t('views.traceManage.task.startTime'),
|
||||||
dataIndex: 'startTime',
|
dataIndex: 'startTime',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
width: 200,
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
if (!opt.value) return '';
|
if (!opt.value) return '';
|
||||||
return parseDateToStr(opt.value);
|
return parseDateToStr(opt.value);
|
||||||
},
|
},
|
||||||
sorter: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.traceManage.task.endTime'),
|
title: t('views.traceManage.task.endTime'),
|
||||||
@@ -141,6 +132,7 @@ let tableColumns: ColumnsType = [
|
|||||||
if (!opt.value) return '';
|
if (!opt.value) return '';
|
||||||
return parseDateToStr(opt.value);
|
return parseDateToStr(opt.value);
|
||||||
},
|
},
|
||||||
|
width: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('common.operate'),
|
title: t('common.operate'),
|
||||||
@@ -274,18 +266,15 @@ type ModalStateType = {
|
|||||||
/**标题 */
|
/**标题 */
|
||||||
title: string;
|
title: string;
|
||||||
/**网元类型设备对象 */
|
/**网元类型设备对象 */
|
||||||
neType: string[];
|
neType: any[] | undefined;
|
||||||
/**网元类型设备对象接口 */
|
/**网元类型设备对象接口 */
|
||||||
neTypeInterface: Record<string, any>[];
|
neTypeInterface: string[];
|
||||||
/**网元类型设备对象接口选择 */
|
|
||||||
neTypeInterfaceSelect: string[];
|
|
||||||
/**任务开始结束时间 */
|
/**任务开始结束时间 */
|
||||||
timeRangePicker: [string, string];
|
timeRangePicker: [Dayjs, Dayjs] | undefined;
|
||||||
/**表单数据 */
|
/**表单数据 */
|
||||||
from: {
|
from: {
|
||||||
id?: string;
|
id?: string;
|
||||||
neType: string;
|
neList: string; // 网元列表 neType_neId 例如 UDM_001,AMF_001
|
||||||
neId: string;
|
|
||||||
/**1-Interface,2-Device,3-User */
|
/**1-Interface,2-Device,3-User */
|
||||||
traceType: string;
|
traceType: string;
|
||||||
startTime?: number;
|
startTime?: number;
|
||||||
@@ -312,13 +301,11 @@ let modalState: ModalStateType = reactive({
|
|||||||
title: '',
|
title: '',
|
||||||
neType: [],
|
neType: [],
|
||||||
neTypeInterface: [],
|
neTypeInterface: [],
|
||||||
neTypeInterfaceSelect: [],
|
timeRangePicker: undefined,
|
||||||
timeRangePicker: ['', ''],
|
|
||||||
from: {
|
from: {
|
||||||
id: undefined,
|
id: undefined,
|
||||||
neType: '',
|
neList: '',
|
||||||
neId: '',
|
traceId: undefined,
|
||||||
traceId: '',
|
|
||||||
traceType: '3',
|
traceType: '3',
|
||||||
startTime: undefined,
|
startTime: undefined,
|
||||||
endTime: undefined,
|
endTime: undefined,
|
||||||
@@ -330,8 +317,8 @@ let modalState: ModalStateType = reactive({
|
|||||||
dstIp: '',
|
dstIp: '',
|
||||||
signalPort: undefined,
|
signalPort: undefined,
|
||||||
/**3用户跟踪 */
|
/**3用户跟踪 */
|
||||||
imsi: '',
|
imsi: undefined,
|
||||||
msisdn: '',
|
// msisdn: undefined,
|
||||||
},
|
},
|
||||||
confirmLoading: false,
|
confirmLoading: false,
|
||||||
});
|
});
|
||||||
@@ -346,7 +333,7 @@ const modalStateFrom = Form.useForm(
|
|||||||
message: t('views.traceManage.task.trackTypePlease'),
|
message: t('views.traceManage.task.trackTypePlease'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
neId: [
|
neList: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: t('views.ne.common.neTypePlease'),
|
message: t('views.ne.common.neTypePlease'),
|
||||||
@@ -393,43 +380,48 @@ const modalStateFrom = Form.useForm(
|
|||||||
message: t('views.traceManage.task.dstIpPlease'),
|
message: t('views.traceManage.task.dstIpPlease'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
signalPort: [
|
|
||||||
{
|
|
||||||
required: false,
|
|
||||||
pattern: regExpPort,
|
|
||||||
message: t('views.traceManage.task.signalPortPlease'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
/**网元类型选择对应修改 */
|
/**网元类型选择对应修改 */
|
||||||
function fnNeChange(_: any, item: any) {
|
function fnNeChange(p: any, c: any) {
|
||||||
modalState.from.neType = item[1].neType;
|
let neList: string[] = [];
|
||||||
modalState.from.neId = item[1].neId;
|
for (let i = 0; i < p.length; i++) {
|
||||||
// 网元信令接口可选列表
|
const v = p[i];
|
||||||
modalState.from.interfaces = '';
|
if (v.length === 1) {
|
||||||
modalState.neTypeInterfaceSelect = [];
|
c[i][0].children.forEach((item: any) => {
|
||||||
fnSelectInterfaceInit(item[1].neType);
|
neList.push(`${item.neType}_${item.neId}`);
|
||||||
}
|
});
|
||||||
|
} else if (v.length === 2) {
|
||||||
/**跟踪类型选择对应修改 */
|
neList.push(`${v[0]}_${v[1]}`);
|
||||||
function fnTraceTypeChange(v: any, _: any) {
|
}
|
||||||
// 网元信令接口可选列表
|
}
|
||||||
if (v === '1' && modalState.from.neType) {
|
if (neList.length > 0) {
|
||||||
modalState.from.interfaces = '';
|
modalState.from.neList = neList.join(',');
|
||||||
modalState.neTypeInterfaceSelect = [];
|
} else {
|
||||||
fnSelectInterfaceInit(modalState.from.neType);
|
modalState.from.neList = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**开始结束时间选择对应修改 */
|
/**开始结束时间选择对应修改 */
|
||||||
function fnRangePickerChange(item: any, _: any) {
|
function fnRangePickerChange(item: any, _: any) {
|
||||||
modalState.from.startTime = +item[0];
|
if (!item || item.length !== 2) {
|
||||||
modalState.from.endTime = +item[1];
|
modalState.from.startTime = undefined;
|
||||||
|
modalState.from.endTime = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 获取当前时间
|
||||||
|
const now = dayjs();
|
||||||
|
// 如果开始时间小于当前时间,则设置为当前时间
|
||||||
|
const startTime = item[0].isBefore(now) ? now : item[0];
|
||||||
|
const endTime = item[1].isBefore(now) ? now : item[1];
|
||||||
|
modalState.timeRangePicker = [startTime, endTime];
|
||||||
|
|
||||||
|
modalState.from.startTime = startTime.valueOf();
|
||||||
|
modalState.from.endTime = endTime.valueOf();
|
||||||
}
|
}
|
||||||
function fnRangePickerDisabledDate(current: Dayjs) {
|
function fnRangePickerDisabledDate(current: Dayjs) {
|
||||||
return current && current < dayjs().subtract(1, 'day').endOf('day');
|
return current && current < dayjs().startOf('day');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**信令接口选择对应修改 */
|
/**信令接口选择对应修改 */
|
||||||
@@ -437,19 +429,6 @@ function fnSelectInterface(s: any, _: any) {
|
|||||||
modalState.from.interfaces = s.join(',');
|
modalState.from.interfaces = s.join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**信令接口选择初始 */
|
|
||||||
function fnSelectInterfaceInit(neType: string) {
|
|
||||||
const interfaces = neInfoStore.traceInterfaceList;
|
|
||||||
modalState.neTypeInterface = interfaces
|
|
||||||
.filter(i => i.neType === neType)
|
|
||||||
.map(i => {
|
|
||||||
return {
|
|
||||||
value: i.interface,
|
|
||||||
label: i.interface,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对话框弹出显示为 新增或者修改
|
* 对话框弹出显示为 新增或者修改
|
||||||
* @param noticeId 网元id, 不传为新增
|
* @param noticeId 网元id, 不传为新增
|
||||||
@@ -467,20 +446,45 @@ function fnModalOpenByEdit(id?: string) {
|
|||||||
modalState.confirmLoading = false;
|
modalState.confirmLoading = false;
|
||||||
hide();
|
hide();
|
||||||
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
if (res.code === RESULT_CODE_SUCCESS && res.data) {
|
||||||
modalState.neType = [res.data.neType, res.data.neId];
|
// 回显网元类型
|
||||||
modalState.timeRangePicker = [res.data.startTime, res.data.endTime];
|
const neType: any[] = [];
|
||||||
modalState.from = Object.assign(modalState.from, res.data);
|
const neList = res.data.neList.split(',');
|
||||||
// 接口
|
const neListMap: any = {};
|
||||||
if (res.data.traceType === 'Interface') {
|
for (const v of neList) {
|
||||||
if (
|
const item: string[] = v.split('_');
|
||||||
res.data.interfaces.length > 4 &&
|
if (!neListMap[item[0]]) {
|
||||||
res.data.interfaces.includes('[')
|
neListMap[item[0]] = [];
|
||||||
) {
|
|
||||||
modalState.neTypeInterfaceSelect = JSON.parse(res.data.interfaces);
|
|
||||||
}
|
}
|
||||||
fnSelectInterfaceInit(res.data.neType);
|
neListMap[item[0]].push(item[1]);
|
||||||
}
|
}
|
||||||
modalState.title = t('views.traceManage.task.editTask');
|
for (const op of neCascaderOptions.value) {
|
||||||
|
const arr = neListMap[op.value];
|
||||||
|
if (!arr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const all = op.children.every((c: any) => {
|
||||||
|
return arr.includes(c.neId);
|
||||||
|
});
|
||||||
|
if (all) {
|
||||||
|
neType.push([op.value]);
|
||||||
|
} else {
|
||||||
|
arr.forEach((v: string) => {
|
||||||
|
neType.push([op.value, v]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modalState.neType = neType;
|
||||||
|
// 回显时间
|
||||||
|
modalState.timeRangePicker = [
|
||||||
|
dayjs(res.data.startTime),
|
||||||
|
dayjs(res.data.endTime),
|
||||||
|
];
|
||||||
|
// 回显接口
|
||||||
|
if (res.data.traceType === '1') {
|
||||||
|
modalState.neTypeInterface = res.data.interfaces.split(',');
|
||||||
|
}
|
||||||
|
modalState.from = Object.assign(modalState.from, res.data);
|
||||||
|
modalState.title = t('views.traceManage.task.viewTask');
|
||||||
modalState.openByEdit = true;
|
modalState.openByEdit = true;
|
||||||
} else {
|
} else {
|
||||||
message.error(t('views.traceManage.task.errorTaskInfo'), 3);
|
message.error(t('views.traceManage.task.errorTaskInfo'), 3);
|
||||||
@@ -495,15 +499,15 @@ function fnModalOpenByEdit(id?: string) {
|
|||||||
*/
|
*/
|
||||||
function fnModalOk() {
|
function fnModalOk() {
|
||||||
const from = toRaw(modalState.from);
|
const from = toRaw(modalState.from);
|
||||||
let valids = ['traceType', 'neId', 'endTime'];
|
let valids = ['traceType', 'neList', 'endTime'];
|
||||||
if (from.traceType === '1') {
|
if (from.traceType === '1') {
|
||||||
valids = valids.concat(['interfaces']);
|
valids = valids.concat(['interfaces']);
|
||||||
}
|
}
|
||||||
if (from.traceType === '2') {
|
if (from.traceType === '2') {
|
||||||
valids = valids.concat(['srcIp', 'dstIp', 'signalPort']);
|
valids = valids.concat(['srcIp', 'dstIp']);
|
||||||
}
|
}
|
||||||
if (from.traceType === '3') {
|
if (from.traceType === '3') {
|
||||||
valids = valids.concat(['imsi', 'msisdn']);
|
valids = valids.concat(['imsi']);
|
||||||
}
|
}
|
||||||
|
|
||||||
modalStateFrom
|
modalStateFrom
|
||||||
@@ -546,29 +550,31 @@ function fnModalCancel() {
|
|||||||
modalState.openByEdit = false;
|
modalState.openByEdit = false;
|
||||||
modalState.confirmLoading = false;
|
modalState.confirmLoading = false;
|
||||||
modalStateFrom.resetFields();
|
modalStateFrom.resetFields();
|
||||||
modalState.timeRangePicker = ['', ''];
|
|
||||||
modalState.neTypeInterfaceSelect = [];
|
|
||||||
modalState.neType = [];
|
modalState.neType = [];
|
||||||
modalState.neTypeInterface = [];
|
modalState.neTypeInterface = [];
|
||||||
|
modalState.timeRangePicker = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**跳转PCAP文件详情页面 */
|
/**跳转内嵌详情页面 */
|
||||||
function fnRecordPCAPView(row: Record<string, any>) {
|
function fnRecordView(traceId: any, type: 'analyze' | 'data') {
|
||||||
router.push({
|
router.push({
|
||||||
path: `${route.path}${MENU_PATH_INLINE}/analyze`,
|
path: `${route.path}${MENU_PATH_INLINE}/${type}`,
|
||||||
query: {
|
query: { traceId },
|
||||||
traceId: row.traceId,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 初始字典数据
|
// 初始字典数据
|
||||||
Promise.allSettled([getDict('trace_type')]).then(resArr => {
|
Promise.allSettled([getDict('trace_type'), getDict('trace_interfaces')]).then(
|
||||||
if (resArr[0].status === 'fulfilled') {
|
resArr => {
|
||||||
dict.traceType = resArr[0].value;
|
if (resArr[0].status === 'fulfilled') {
|
||||||
|
dict.traceType = resArr[0].value;
|
||||||
|
}
|
||||||
|
if (resArr[1].status === 'fulfilled') {
|
||||||
|
dict.traceInterfaces = resArr[1].value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
// 获取网元网元列表
|
// 获取网元网元列表
|
||||||
useNeInfoStore()
|
useNeInfoStore()
|
||||||
@@ -579,13 +585,13 @@ onMounted(() => {
|
|||||||
// 过滤不可用的网元
|
// 过滤不可用的网元
|
||||||
neCascaderOptions.value = neInfoStore.getNeSelectOtions.filter(
|
neCascaderOptions.value = neInfoStore.getNeSelectOtions.filter(
|
||||||
(item: any) => {
|
(item: any) => {
|
||||||
return ['UDM'].includes(item.value);
|
return ['AMF', 'AUSF', 'SMF', 'UDM', 'PCF'].includes(item.value);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (neCascaderOptions.value.length === 0) {
|
if (neCascaderOptions.value.length === 0) {
|
||||||
message.warning({
|
message.warning({
|
||||||
content: t('common.noData'),
|
content: t('common.noData'),
|
||||||
duration: 2,
|
duration: 3,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -593,13 +599,11 @@ onMounted(() => {
|
|||||||
} else {
|
} else {
|
||||||
message.warning({
|
message.warning({
|
||||||
content: t('common.noData'),
|
content: t('common.noData'),
|
||||||
duration: 2,
|
duration: 3,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
// 获取跟踪接口列表
|
|
||||||
neInfoStore.fnNeTraceInterface();
|
|
||||||
// 获取列表数据
|
// 获取列表数据
|
||||||
fnGetList();
|
fnGetList();
|
||||||
});
|
});
|
||||||
@@ -765,21 +769,40 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'id'">
|
<template v-if="column.key === 'id'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
|
<div v-perms:has="['traceManage:task:data']">
|
||||||
|
<a-tooltip placement="topRight">
|
||||||
|
<template #title>
|
||||||
|
{{ t('views.traceManage.task.dataView') }}
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordView(record.traceId, 'data')"
|
||||||
|
>
|
||||||
|
<template #icon><ContainerOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<div v-perms:has="['traceManage:task:analyze']">
|
||||||
|
<a-tooltip placement="topRight">
|
||||||
|
<template #title>
|
||||||
|
{{ t('views.traceManage.task.pcapView') }}
|
||||||
|
</template>
|
||||||
|
<a-button
|
||||||
|
type="link"
|
||||||
|
@click.prevent="fnRecordView(record.traceId, 'analyze')"
|
||||||
|
>
|
||||||
|
<template #icon><BarsOutlined /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a-tooltip placement="topRight">
|
<a-tooltip placement="topRight">
|
||||||
<template #title>{{ t('common.editText') }}</template>
|
<template #title>{{ t('common.viewText') }}</template>
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
@click.prevent="fnModalOpenByEdit(record.id)"
|
@click.prevent="fnModalOpenByEdit(record.id)"
|
||||||
>
|
>
|
||||||
<template #icon><FormOutlined /></template>
|
<template #icon><ProfileOutlined /></template>
|
||||||
</a-button>
|
|
||||||
</a-tooltip>
|
|
||||||
<a-tooltip placement="topRight">
|
|
||||||
<template #title>{{
|
|
||||||
t('views.traceManage.task.pcapView')
|
|
||||||
}}</template>
|
|
||||||
<a-button type="link" @click.prevent="fnRecordPCAPView(record)">
|
|
||||||
<template #icon><ContainerOutlined /></template>
|
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip placement="topRight">
|
<a-tooltip placement="topRight">
|
||||||
@@ -810,12 +833,14 @@ onMounted(() => {
|
|||||||
:confirm-loading="modalState.confirmLoading"
|
:confirm-loading="modalState.confirmLoading"
|
||||||
@ok="fnModalOk"
|
@ok="fnModalOk"
|
||||||
@cancel="fnModalCancel"
|
@cancel="fnModalCancel"
|
||||||
|
:footer="modalState.from.id ? null : undefined"
|
||||||
>
|
>
|
||||||
<a-form
|
<a-form
|
||||||
name="modalStateFrom"
|
name="modalStateFrom"
|
||||||
layout="horizontal"
|
layout="horizontal"
|
||||||
:label-col="{ span: 4 }"
|
:label-col="{ span: 4 }"
|
||||||
:label-wrap="true"
|
:label-wrap="true"
|
||||||
|
:disabled="!!modalState.from.id"
|
||||||
>
|
>
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
@@ -823,12 +848,13 @@ onMounted(() => {
|
|||||||
:label="t('views.ne.common.neType')"
|
:label="t('views.ne.common.neType')"
|
||||||
:label-col="{ span: 8 }"
|
:label-col="{ span: 8 }"
|
||||||
name="neType"
|
name="neType"
|
||||||
v-bind="modalStateFrom.validateInfos.neId"
|
v-bind="modalStateFrom.validateInfos.neList"
|
||||||
>
|
>
|
||||||
<a-cascader
|
<a-cascader
|
||||||
v-model:value="modalState.neType"
|
v-model:value="modalState.neType"
|
||||||
:options="neCascaderOptions"
|
:options="neCascaderOptions"
|
||||||
@change="fnNeChange"
|
@change="fnNeChange"
|
||||||
|
multiple
|
||||||
:allow-clear="false"
|
:allow-clear="false"
|
||||||
:placeholder="t('views.ne.common.neTypePlease')"
|
:placeholder="t('views.ne.common.neTypePlease')"
|
||||||
/>
|
/>
|
||||||
@@ -844,7 +870,6 @@ onMounted(() => {
|
|||||||
<a-select
|
<a-select
|
||||||
v-model:value="modalState.from.traceType"
|
v-model:value="modalState.from.traceType"
|
||||||
:options="dict.traceType"
|
:options="dict.traceType"
|
||||||
@change="fnTraceTypeChange"
|
|
||||||
:allow-clear="false"
|
:allow-clear="false"
|
||||||
:placeholder="t('views.traceManage.task.trackTypePlease')"
|
:placeholder="t('views.traceManage.task.trackTypePlease')"
|
||||||
>
|
>
|
||||||
@@ -861,11 +886,9 @@ onMounted(() => {
|
|||||||
<a-range-picker
|
<a-range-picker
|
||||||
v-model:value="modalState.timeRangePicker"
|
v-model:value="modalState.timeRangePicker"
|
||||||
@change="fnRangePickerChange"
|
@change="fnRangePickerChange"
|
||||||
allow-clear
|
|
||||||
bordered
|
bordered
|
||||||
:show-time="{ format: 'HH:mm:ss' }"
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
value-format="x"
|
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
:disabled-date="fnRangePickerDisabledDate"
|
:disabled-date="fnRangePickerDisabledDate"
|
||||||
:placeholder="[
|
:placeholder="[
|
||||||
@@ -894,8 +917,8 @@ onMounted(() => {
|
|||||||
<a-select
|
<a-select
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
:placeholder="t('views.traceManage.task.interfacesPlease')"
|
:placeholder="t('views.traceManage.task.interfacesPlease')"
|
||||||
v-model:value="modalState.neTypeInterfaceSelect"
|
v-model:value="modalState.neTypeInterface"
|
||||||
:options="modalState.neTypeInterface"
|
:options="dict.traceInterfaces"
|
||||||
@change="fnSelectInterface"
|
@change="fnSelectInterface"
|
||||||
>
|
>
|
||||||
</a-select>
|
</a-select>
|
||||||
@@ -904,26 +927,6 @@ onMounted(() => {
|
|||||||
|
|
||||||
<!-- 设备跟踪 -->
|
<!-- 设备跟踪 -->
|
||||||
<template v-if="modalState.from.traceType === '2'">
|
<template v-if="modalState.from.traceType === '2'">
|
||||||
<a-form-item
|
|
||||||
:label="t('views.traceManage.task.signalPort')"
|
|
||||||
name="signalPort"
|
|
||||||
v-bind="modalStateFrom.validateInfos.signalPort"
|
|
||||||
>
|
|
||||||
<a-input-number
|
|
||||||
v-model:value="modalState.from.signalPort"
|
|
||||||
style="width: 100%"
|
|
||||||
:placeholder="t('views.traceManage.task.signalPortPlease')"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<a-tooltip placement="topLeft">
|
|
||||||
<template #title>
|
|
||||||
<div>{{ t('views.traceManage.task.signalPortTip') }}</div>
|
|
||||||
</template>
|
|
||||||
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
|
|
||||||
</a-tooltip>
|
|
||||||
</template>
|
|
||||||
</a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
@@ -1000,7 +1003,7 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item
|
<!-- <a-form-item
|
||||||
:label="t('views.traceManage.task.msisdn')"
|
:label="t('views.traceManage.task.msisdn')"
|
||||||
name="msisdn"
|
name="msisdn"
|
||||||
v-bind="modalStateFrom.validateInfos.msisdn"
|
v-bind="modalStateFrom.validateInfos.msisdn"
|
||||||
@@ -1019,7 +1022,7 @@ onMounted(() => {
|
|||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-form-item>
|
</a-form-item> -->
|
||||||
</template>
|
</template>
|
||||||
</a-form>
|
</a-form>
|
||||||
</ProModal>
|
</ProModal>
|
||||||
|
|||||||
@@ -238,15 +238,13 @@ watchEffect(() => {
|
|||||||
.thead-item:nth-child(3),
|
.thead-item:nth-child(3),
|
||||||
.tbody-item:nth-child(3),
|
.tbody-item:nth-child(3),
|
||||||
.thead-item:nth-child(4),
|
.thead-item:nth-child(4),
|
||||||
.tbody-item:nth-child(4) {
|
.tbody-item:nth-child(4),
|
||||||
|
.thead-item:nth-child(5),
|
||||||
|
.tbody-item:nth-child(5) {
|
||||||
flex-basis: 8rem;
|
flex-basis: 8rem;
|
||||||
width: 8rem;
|
width: 8rem;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
text-wrap: nowrap;
|
||||||
.thead-item:nth-child(5),
|
|
||||||
.tbody-item:nth-child(5) {
|
|
||||||
flex-basis: 7rem;
|
|
||||||
width: 7rem;
|
|
||||||
}
|
}
|
||||||
.thead-item:nth-child(6),
|
.thead-item:nth-child(6),
|
||||||
.tbody-item:nth-child(6) {
|
.tbody-item:nth-child(6) {
|
||||||
@@ -270,6 +268,7 @@ watchEffect(() => {
|
|||||||
.tbody-item:nth-child(2)::-webkit-scrollbar,
|
.tbody-item:nth-child(2)::-webkit-scrollbar,
|
||||||
.tbody-item:nth-child(3)::-webkit-scrollbar,
|
.tbody-item:nth-child(3)::-webkit-scrollbar,
|
||||||
.tbody-item:nth-child(4)::-webkit-scrollbar,
|
.tbody-item:nth-child(4)::-webkit-scrollbar,
|
||||||
|
.tbody-item:nth-child(5)::-webkit-scrollbar,
|
||||||
.tbody-item:nth-child(7)::-webkit-scrollbar {
|
.tbody-item:nth-child(7)::-webkit-scrollbar {
|
||||||
width: 4px; /* 设置滚动条宽度 */
|
width: 4px; /* 设置滚动条宽度 */
|
||||||
height: 4px;
|
height: 4px;
|
||||||
@@ -278,6 +277,7 @@ watchEffect(() => {
|
|||||||
.tbody-item:nth-child(2)::-webkit-scrollbar-track,
|
.tbody-item:nth-child(2)::-webkit-scrollbar-track,
|
||||||
.tbody-item:nth-child(3)::-webkit-scrollbar-track,
|
.tbody-item:nth-child(3)::-webkit-scrollbar-track,
|
||||||
.tbody-item:nth-child(4)::-webkit-scrollbar-track,
|
.tbody-item:nth-child(4)::-webkit-scrollbar-track,
|
||||||
|
.tbody-item:nth-child(5)::-webkit-scrollbar-track,
|
||||||
.tbody-item:nth-child(7)::-webkit-scrollbar-track {
|
.tbody-item:nth-child(7)::-webkit-scrollbar-track {
|
||||||
background-color: #f0f0f0; /* 设置滚动条轨道背景颜色 */
|
background-color: #f0f0f0; /* 设置滚动条轨道背景颜色 */
|
||||||
}
|
}
|
||||||
@@ -285,6 +285,7 @@ watchEffect(() => {
|
|||||||
.tbody-item:nth-child(2)::-webkit-scrollbar-thumb,
|
.tbody-item:nth-child(2)::-webkit-scrollbar-thumb,
|
||||||
.tbody-item:nth-child(3)::-webkit-scrollbar-thumb,
|
.tbody-item:nth-child(3)::-webkit-scrollbar-thumb,
|
||||||
.tbody-item:nth-child(4)::-webkit-scrollbar-thumb,
|
.tbody-item:nth-child(4)::-webkit-scrollbar-thumb,
|
||||||
|
.tbody-item:nth-child(5)::-webkit-scrollbar-thumb,
|
||||||
.tbody-item:nth-child(7)::-webkit-scrollbar-thumb {
|
.tbody-item:nth-child(7)::-webkit-scrollbar-thumb {
|
||||||
background-color: #bfbfbf; /* 设置滚动条滑块颜色 */
|
background-color: #bfbfbf; /* 设置滚动条滑块颜色 */
|
||||||
}
|
}
|
||||||
@@ -292,6 +293,7 @@ watchEffect(() => {
|
|||||||
.tbody-item:nth-child(2)::-webkit-scrollbar-thumb:hover,
|
.tbody-item:nth-child(2)::-webkit-scrollbar-thumb:hover,
|
||||||
.tbody-item:nth-child(3)::-webkit-scrollbar-thumb:hover,
|
.tbody-item:nth-child(3)::-webkit-scrollbar-thumb:hover,
|
||||||
.tbody-item:nth-child(4)::-webkit-scrollbar-thumb:hover,
|
.tbody-item:nth-child(4)::-webkit-scrollbar-thumb:hover,
|
||||||
|
.tbody-item:nth-child(5)::-webkit-scrollbar-thumb:hover,
|
||||||
.tbody-item:nth-child(7)::-webkit-scrollbar-thumb:hover {
|
.tbody-item:nth-child(7)::-webkit-scrollbar-thumb:hover {
|
||||||
background-color: #1890ff; /* 设置鼠标悬停时滚动条滑块颜色 */
|
background-color: #1890ff; /* 设置鼠标悬停时滚动条滑块颜色 */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ type StateType = {
|
|||||||
/**当前选中的帧编号 */
|
/**当前选中的帧编号 */
|
||||||
selectedFrame: number;
|
selectedFrame: number;
|
||||||
/**当前选中的帧数据 */
|
/**当前选中的帧数据 */
|
||||||
selectedPacket: { tree: any[]; data_sources: any[] };
|
packetFrame: { tree: any[]; data_sources: any[] };
|
||||||
/**pcap包帧数据 */
|
/**pcap包帧数据 */
|
||||||
packetFrameData: Map<string, any> | null;
|
packetFrameTreeMap: Map<string, any> | null;
|
||||||
/**当前选中的帧数据-空占位 */
|
/**当前选中的帧数据 */
|
||||||
selectedTreeEntry: typeof NO_SELECTION;
|
selectedTree: typeof NO_SELECTION;
|
||||||
/**选择帧的Dump数据标签 */
|
/**选择帧的Dump数据标签 */
|
||||||
selectedDataSourceIndex: number;
|
selectedDataSourceIndex: number;
|
||||||
/**处理完成状态 */
|
/**处理完成状态 */
|
||||||
@@ -69,11 +69,11 @@ export function usePCAP() {
|
|||||||
filter: '',
|
filter: '',
|
||||||
filterError: null,
|
filterError: null,
|
||||||
currentFilter: '',
|
currentFilter: '',
|
||||||
selectedFrame: 1,
|
selectedFrame: 0,
|
||||||
/**当前选中的帧数据 */
|
/**当前选中的帧数据 */
|
||||||
selectedPacket: { tree: [], data_sources: [] },
|
packetFrame: { tree: [], data_sources: [] },
|
||||||
packetFrameData: null, // 注意:Map 需要额外处理
|
packetFrameTreeMap: null, // 注意:Map 需要额外处理
|
||||||
selectedTreeEntry: NO_SELECTION, // NO_SELECTION 需要定义
|
selectedTree: NO_SELECTION, // NO_SELECTION 需要定义
|
||||||
/**选择帧的Dump数据标签 */
|
/**选择帧的Dump数据标签 */
|
||||||
selectedDataSourceIndex: 0,
|
selectedDataSourceIndex: 0,
|
||||||
/**处理完成状态 */
|
/**处理完成状态 */
|
||||||
@@ -91,9 +91,9 @@ export function usePCAP() {
|
|||||||
state.nextPageNum = 1;
|
state.nextPageNum = 1;
|
||||||
// 选择帧的数据
|
// 选择帧的数据
|
||||||
state.selectedFrame = 0;
|
state.selectedFrame = 0;
|
||||||
state.selectedPacket = { tree: [], data_sources: [] };
|
state.packetFrame = { tree: [], data_sources: [] };
|
||||||
state.packetFrameData = null;
|
state.packetFrameTreeMap = null;
|
||||||
state.selectedTreeEntry = NO_SELECTION;
|
state.selectedTree = NO_SELECTION;
|
||||||
state.selectedDataSourceIndex = 0;
|
state.selectedDataSourceIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,23 +121,23 @@ export function usePCAP() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**帧数据点击选中 */
|
/**帧数据点击选中 */
|
||||||
function handleSelectedTreeEntry(e: any) {
|
function handleSelectedTree(e: any) {
|
||||||
console.log('fnSelectedTreeEntry', e);
|
// console.log('fnSelectedTree', e);
|
||||||
state.selectedTreeEntry = e;
|
state.selectedTree = e;
|
||||||
}
|
}
|
||||||
/**报文数据点击选中 */
|
/**报文数据点击选中 */
|
||||||
function handleSelectedFindSelection(src_idx: number, pos: number) {
|
function handleSelectedFindSelection(src_idx: number, pos: number) {
|
||||||
console.log('fnSelectedFindSelection', pos);
|
// console.log('fnSelectedFindSelection', pos);
|
||||||
if (state.packetFrameData == null) return;
|
if (state.packetFrameTreeMap == null) return;
|
||||||
// find the smallest one
|
// find the smallest one
|
||||||
let current = null;
|
let current = null;
|
||||||
for (let [k, pp] of state.packetFrameData) {
|
for (let [k, pp] of state.packetFrameTreeMap) {
|
||||||
if (pp.idx !== src_idx) continue;
|
if (pp.idx !== src_idx) continue;
|
||||||
|
|
||||||
if (pos >= pp.start && pos <= pp.start + pp.length) {
|
if (pos >= pp.start && pos <= pp.start + pp.length) {
|
||||||
if (
|
if (
|
||||||
current != null &&
|
current != null &&
|
||||||
state.packetFrameData.get(current).length > pp.length
|
state.packetFrameTreeMap.get(current).length > pp.length
|
||||||
) {
|
) {
|
||||||
current = k;
|
current = k;
|
||||||
} else {
|
} else {
|
||||||
@@ -146,19 +146,19 @@ export function usePCAP() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (current != null) {
|
if (current != null) {
|
||||||
state.selectedTreeEntry = state.packetFrameData.get(current);
|
state.selectedTree = state.packetFrameTreeMap.get(current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**包数据表点击选中 */
|
/**包数据表点击选中 */
|
||||||
function handleSelectedFrame(no: number) {
|
function handleSelectedFrame(no: number) {
|
||||||
console.log('fnSelectedFrame', no, state.totalFrames);
|
// console.log('fnSelectedFrame', no, state.totalFrames);
|
||||||
state.selectedFrame = no;
|
state.selectedFrame = no;
|
||||||
wk.send({ type: 'select', number: state.selectedFrame });
|
wk.send({ type: 'select', number: state.selectedFrame });
|
||||||
}
|
}
|
||||||
/**包数据表滚动底部加载 */
|
/**包数据表滚动底部加载 */
|
||||||
function handleScrollBottom() {
|
function handleScrollBottom() {
|
||||||
const totalFetched = state.packetFrames.length;
|
const totalFetched = state.packetFrames.length;
|
||||||
console.log('fnScrollBottom', totalFetched);
|
// console.log('fnScrollBottom', totalFetched);
|
||||||
if (!state.nextPageLoad && totalFetched < state.totalFrames) {
|
if (!state.nextPageLoad && totalFetched < state.totalFrames) {
|
||||||
state.nextPageLoad = true;
|
state.nextPageLoad = true;
|
||||||
state.nextPageNum++;
|
state.nextPageNum++;
|
||||||
@@ -167,7 +167,7 @@ export function usePCAP() {
|
|||||||
}
|
}
|
||||||
/**包数据表过滤 */
|
/**包数据表过滤 */
|
||||||
function handleFilterFrames() {
|
function handleFilterFrames() {
|
||||||
console.log('fnFilterFinish', state.filter);
|
// console.log('fnFilterFinish', state.filter);
|
||||||
wk.send({ type: 'check-filter', filter: state.filter });
|
wk.send({ type: 'check-filter', filter: state.filter });
|
||||||
}
|
}
|
||||||
/**包数据表加载 */
|
/**包数据表加载 */
|
||||||
@@ -254,9 +254,9 @@ export function usePCAP() {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'selected':
|
case 'selected':
|
||||||
state.selectedPacket = res.data;
|
state.packetFrame = res.data;
|
||||||
state.packetFrameData = parseFrameData('root', res.data);
|
state.packetFrameTreeMap = parseFrameData('root', res.data);
|
||||||
state.selectedTreeEntry = NO_SELECTION;
|
state.selectedTree = NO_SELECTION;
|
||||||
state.selectedDataSourceIndex = 0;
|
state.selectedDataSourceIndex = 0;
|
||||||
break;
|
break;
|
||||||
case 'processed':
|
case 'processed':
|
||||||
@@ -306,7 +306,7 @@ export function usePCAP() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
state,
|
state,
|
||||||
handleSelectedTreeEntry,
|
handleSelectedTree,
|
||||||
handleSelectedFindSelection,
|
handleSelectedFindSelection,
|
||||||
handleSelectedFrame,
|
handleSelectedFrame,
|
||||||
handleScrollBottom,
|
handleScrollBottom,
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ import DissectionDump from './components/DissectionDump.vue';
|
|||||||
import PacketTable from './components/PacketTable.vue';
|
import PacketTable from './components/PacketTable.vue';
|
||||||
import { usePCAP, NO_SELECTION } from './hooks/usePCAP';
|
import { usePCAP, NO_SELECTION } from './hooks/usePCAP';
|
||||||
import { parseSizeFromFile } from '@/utils/parse-utils';
|
import { parseSizeFromFile } from '@/utils/parse-utils';
|
||||||
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const {
|
const {
|
||||||
state,
|
state,
|
||||||
handleSelectedTreeEntry,
|
handleSelectedTree,
|
||||||
handleSelectedFindSelection,
|
handleSelectedFindSelection,
|
||||||
handleSelectedFrame,
|
handleSelectedFrame,
|
||||||
handleScrollBottom,
|
handleScrollBottom,
|
||||||
@@ -49,8 +50,9 @@ function fnUpload(up: UploadRequestOption) {
|
|||||||
:loading="!state.initialized"
|
:loading="!state.initialized"
|
||||||
:body-style="{ padding: '12px' }"
|
:body-style="{ padding: '12px' }"
|
||||||
>
|
>
|
||||||
<div class="toolbar">
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
<a-space :size="8" class="toolbar-oper">
|
<template #title>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
<a-upload
|
<a-upload
|
||||||
name="file"
|
name="file"
|
||||||
list-type="picture"
|
list-type="picture"
|
||||||
@@ -64,61 +66,94 @@ function fnUpload(up: UploadRequestOption) {
|
|||||||
</a-upload>
|
</a-upload>
|
||||||
<a-button @click="handleLoadExample()">Example</a-button>
|
<a-button @click="handleLoadExample()">Example</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
|
</template>
|
||||||
<div class="toolbar-info">
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="center">
|
||||||
<a-tag color="green" v-show="!!state.currentFilter">
|
<a-tag color="green" v-show="!!state.currentFilter">
|
||||||
{{ state.currentFilter }}
|
{{ state.currentFilter }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
<span> Matched Frame: {{ state.totalFrames }} </span>
|
<span> Matched Frame: {{ state.totalFrames }} </span>
|
||||||
</div>
|
<!-- 包信息 -->
|
||||||
|
<a-popover
|
||||||
<!-- 包信息 -->
|
trigger="click"
|
||||||
<a-popover
|
placement="bottomLeft"
|
||||||
trigger="click"
|
v-if="state.summary.filename"
|
||||||
placement="bottomLeft"
|
>
|
||||||
v-if="state.summary.filename"
|
<template #content>
|
||||||
>
|
<div class="summary">
|
||||||
<template #content>
|
<div class="summary-item">
|
||||||
<div class="summary">
|
<span>Type:</span>
|
||||||
<div class="summary-item">
|
<span>{{ state.summary.file_type }}</span>
|
||||||
<span>Type:</span>
|
</div>
|
||||||
<span>{{ state.summary.file_type }}</span>
|
<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>Start Time:</span>
|
||||||
|
<span>
|
||||||
|
{{
|
||||||
|
parseDateToStr(
|
||||||
|
state.summary.start_time * 1000,
|
||||||
|
'YYYY-MM-DD HH:mm:ss.SSS'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span>Stop Time:</span>
|
||||||
|
<span>
|
||||||
|
{{
|
||||||
|
parseDateToStr(
|
||||||
|
state.summary.stop_time * 1000,
|
||||||
|
'YYYY-MM-DD HH:mm:ss.SSS'
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span>Duration:</span>
|
||||||
|
<span>{{ Math.round(state.summary.elapsed_time) }}s</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-item">
|
</template>
|
||||||
<span>Size:</span>
|
<InfoCircleOutlined />
|
||||||
<span>{{ parseSizeFromFile(state.summary.file_length) }}</span>
|
</a-popover>
|
||||||
</div>
|
</a-space>
|
||||||
<div class="summary-item">
|
</template>
|
||||||
<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-group compact>
|
||||||
<a-input
|
<a-auto-complete
|
||||||
v-model:value="state.filter"
|
v-model:value="state.filter"
|
||||||
placeholder="display filter, example: tcp"
|
:options="[
|
||||||
:allow-clear="true"
|
{ value: 'http || tcp.port == 33030 || http2' },
|
||||||
|
{ value: 'ip.src== 172.17.0.19 && ip.dst == 172.17.0.77' },
|
||||||
|
{ value: 'sip || ngap' },
|
||||||
|
]"
|
||||||
style="width: calc(100% - 100px)"
|
style="width: calc(100% - 100px)"
|
||||||
@pressEnter="handleFilterFrames"
|
:allow-clear="true"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<a-input
|
||||||
<FilterOutlined />
|
placeholder="display filter, example: tcp"
|
||||||
</template>
|
@pressEnter="handleFilterFrames"
|
||||||
</a-input>
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<FilterOutlined />
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-auto-complete>
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
html-type="submit"
|
html-type="submit"
|
||||||
@@ -143,14 +178,18 @@ function fnUpload(up: UploadRequestOption) {
|
|||||||
:onScrollBottom="handleScrollBottom"
|
:onScrollBottom="handleScrollBottom"
|
||||||
></PacketTable>
|
></PacketTable>
|
||||||
|
|
||||||
<a-row>
|
<a-row
|
||||||
|
:gutter="16"
|
||||||
|
style="border: 2px rgb(217 217 217) solid; border-radius: 6px"
|
||||||
|
v-show="state.selectedFrame > 0"
|
||||||
|
>
|
||||||
<a-col :lg="12" :md="12" :xs="24" class="tree">
|
<a-col :lg="12" :md="12" :xs="24" class="tree">
|
||||||
<!-- 帧数据 -->
|
<!-- 帧数据 -->
|
||||||
<DissectionTree
|
<DissectionTree
|
||||||
id="root"
|
id="root"
|
||||||
:select="handleSelectedTreeEntry"
|
:select="handleSelectedTree"
|
||||||
:selected="state.selectedTreeEntry"
|
:selected="state.selectedTree"
|
||||||
:tree="state.selectedPacket.tree"
|
:tree="state.packetFrame.tree"
|
||||||
/>
|
/>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="12" :md="12" :xs="24" class="dump">
|
<a-col :lg="12" :md="12" :xs="24" class="dump">
|
||||||
@@ -163,15 +202,15 @@ function fnUpload(up: UploadRequestOption) {
|
|||||||
<a-tab-pane
|
<a-tab-pane
|
||||||
:key="idx"
|
:key="idx"
|
||||||
:tab="v.name"
|
:tab="v.name"
|
||||||
v-for="(v, idx) in state.selectedPacket.data_sources"
|
v-for="(v, idx) in state.packetFrame.data_sources"
|
||||||
style="overflow: auto"
|
style="overflow: auto"
|
||||||
>
|
>
|
||||||
<DissectionDump
|
<DissectionDump
|
||||||
:base64="v.data"
|
:base64="v.data"
|
||||||
:select="(pos:number)=>handleSelectedFindSelection(idx, pos)"
|
:select="(pos:number)=>handleSelectedFindSelection(idx, pos)"
|
||||||
:selected="
|
:selected="
|
||||||
idx === state.selectedTreeEntry.idx
|
idx === state.selectedTree.idx
|
||||||
? state.selectedTreeEntry
|
? state.selectedTree
|
||||||
: NO_SELECTION
|
: NO_SELECTION
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
@@ -184,17 +223,6 @@ function fnUpload(up: UploadRequestOption) {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.toolbar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
.toolbar-info {
|
|
||||||
flex: 1;
|
|
||||||
text-align: right;
|
|
||||||
padding-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.summary {
|
.summary {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onBeforeUnmount, onMounted, reactive, ref } from 'vue';
|
import { onBeforeUnmount, onMounted, reactive, ref, toRaw } from 'vue';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { message, Modal } from 'ant-design-vue/es';
|
import { message, Modal } from 'ant-design-vue/es';
|
||||||
import DissectionTree from '../tshark/components/DissectionTree.vue';
|
import DissectionTree from '../tshark/components/DissectionTree.vue';
|
||||||
import DissectionDump from '../tshark/components/DissectionDump.vue';
|
import DissectionDump from '../tshark/components/DissectionDump.vue';
|
||||||
import PacketTable from '../tshark/components/PacketTable.vue';
|
|
||||||
import {
|
import {
|
||||||
RESULT_CODE_ERROR,
|
RESULT_CODE_ERROR,
|
||||||
RESULT_CODE_SUCCESS,
|
RESULT_CODE_SUCCESS,
|
||||||
} from '@/constants/result-constants';
|
} from '@/constants/result-constants';
|
||||||
import { filePullTask } from '@/api/trace/task';
|
import { scriptUrl as wkUrl } from '@/assets/js/wiregasm_worker';
|
||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
import * as wkUtil from '@/plugins/wk-worker';
|
||||||
|
import * as wsUtil from '@/plugins/ws-websocket';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import saveAs from 'file-saver';
|
import saveAs from 'file-saver';
|
||||||
import {
|
import {
|
||||||
@@ -19,34 +19,20 @@ import {
|
|||||||
packetStop,
|
packetStop,
|
||||||
packetFilter,
|
packetFilter,
|
||||||
packetKeep,
|
packetKeep,
|
||||||
|
packetPCAPFile,
|
||||||
} from '@/api/trace/packet';
|
} from '@/api/trace/packet';
|
||||||
const ws = new WS();
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
|
const ws = new wsUtil.WS();
|
||||||
|
const wk = new wkUtil.WK();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
// =========== WK ==============
|
||||||
const NO_SELECTION = { id: '', idx: 0, start: 0, length: 0 };
|
const NO_SELECTION = { id: '', idx: 0, start: 0, length: 0 };
|
||||||
|
|
||||||
type StateType = {
|
type StateType = {
|
||||||
/**网卡设备列表 */
|
|
||||||
devices: { id: string; label: string; children: any[] }[];
|
|
||||||
/**初始化 */
|
/**初始化 */
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
/**保活调度器 */
|
|
||||||
keepTimer: any;
|
|
||||||
/**任务 */
|
|
||||||
task: {
|
|
||||||
taskNo: string;
|
|
||||||
device: string;
|
|
||||||
filter: string;
|
|
||||||
outputPCAP: boolean;
|
|
||||||
};
|
|
||||||
/**字段 */
|
|
||||||
columns: string[];
|
|
||||||
|
|
||||||
/**过滤条件 */
|
|
||||||
filter: string;
|
|
||||||
/**过滤条件错误信息 */
|
|
||||||
filterError: string | null;
|
|
||||||
|
|
||||||
/**当前选中的帧编号 */
|
/**当前选中的帧编号 */
|
||||||
selectedFrame: number;
|
selectedFrame: number;
|
||||||
/**当前选中的帧数据 */
|
/**当前选中的帧数据 */
|
||||||
@@ -57,63 +43,19 @@ type StateType = {
|
|||||||
selectedTree: typeof NO_SELECTION;
|
selectedTree: typeof NO_SELECTION;
|
||||||
/**选择帧的Dump数据标签 */
|
/**选择帧的Dump数据标签 */
|
||||||
selectedDataSourceIndex: number;
|
selectedDataSourceIndex: number;
|
||||||
|
|
||||||
/**包总数 */
|
|
||||||
totalPackets: number;
|
|
||||||
/**包数据 */
|
|
||||||
packetList: any[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const state = reactive<StateType>({
|
const state = reactive<StateType>({
|
||||||
devices: [],
|
|
||||||
initialized: false,
|
initialized: false,
|
||||||
keepTimer: null,
|
selectedFrame: 0,
|
||||||
task: {
|
|
||||||
taskNo: 'laYlTbq',
|
|
||||||
device: '192.168.5.58',
|
|
||||||
filter: 'tcp and (port 33030 or 8080)',
|
|
||||||
outputPCAP: false,
|
|
||||||
},
|
|
||||||
columns: [
|
|
||||||
'No.',
|
|
||||||
'Time',
|
|
||||||
'Source',
|
|
||||||
'Destination',
|
|
||||||
'Protocol',
|
|
||||||
'Length',
|
|
||||||
'Info',
|
|
||||||
],
|
|
||||||
filter: 'tcp and (port 33030 or 8080)',
|
|
||||||
filterError: null,
|
|
||||||
selectedFrame: 1,
|
|
||||||
/**当前选中的帧数据 */
|
/**当前选中的帧数据 */
|
||||||
packetFrame: { tree: [], data_sources: [] },
|
packetFrame: { tree: [], data_sources: [] },
|
||||||
packetFrameTreeMap: null, // 注意:Map 需要额外处理
|
packetFrameTreeMap: null, // 注意:Map 需要额外处理
|
||||||
selectedTree: NO_SELECTION, // NO_SELECTION 需要定义
|
selectedTree: NO_SELECTION, // NO_SELECTION 需要定义
|
||||||
/**选择帧的Dump数据标签 */
|
/**选择帧的Dump数据标签 */
|
||||||
selectedDataSourceIndex: 0,
|
selectedDataSourceIndex: 0,
|
||||||
// 包数据
|
|
||||||
totalPackets: 0,
|
|
||||||
packetList: [],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**清除帧数据和报文信息状态 */
|
|
||||||
function fnReset() {
|
|
||||||
state.initialized = false;
|
|
||||||
// 选择帧的数据
|
|
||||||
state.selectedFrame = 0;
|
|
||||||
state.packetFrame = { tree: [], data_sources: [] };
|
|
||||||
state.packetFrameTreeMap = null;
|
|
||||||
state.selectedTree = NO_SELECTION;
|
|
||||||
state.selectedDataSourceIndex = 0;
|
|
||||||
// 过滤条件
|
|
||||||
state.filter = 'tcp and (port 33030 or 8080)';
|
|
||||||
state.filterError = null;
|
|
||||||
// 包数据
|
|
||||||
state.totalPackets = 0;
|
|
||||||
state.packetList = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**解析帧数据为简单结构 */
|
/**解析帧数据为简单结构 */
|
||||||
function parseFrameTree(id: string, node: Record<string, any>) {
|
function parseFrameTree(id: string, node: Record<string, any>) {
|
||||||
let map = new Map();
|
let map = new Map();
|
||||||
@@ -136,15 +78,9 @@ function parseFrameTree(id: string, node: Record<string, any>) {
|
|||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**帧数据点击选中 */
|
|
||||||
function handleSelectedTreeEntry(e: any) {
|
|
||||||
console.log('fnSelectedTreeEntry', e);
|
|
||||||
state.selectedTree = e;
|
|
||||||
}
|
|
||||||
/**报文数据点击选中 */
|
/**报文数据点击选中 */
|
||||||
function handleSelectedFindSelection(src_idx: number, pos: number) {
|
function handleSelectedFindSelection(src_idx: number, pos: number) {
|
||||||
console.log('fnSelectedFindSelection', pos);
|
// console.log('fnSelectedFindSelection', pos);
|
||||||
if (state.packetFrameTreeMap == null) return;
|
if (state.packetFrameTreeMap == null) return;
|
||||||
// find the smallest one
|
// find the smallest one
|
||||||
let current = null;
|
let current = null;
|
||||||
@@ -166,83 +102,274 @@ function handleSelectedFindSelection(src_idx: number, pos: number) {
|
|||||||
state.selectedTree = state.packetFrameTreeMap.get(current);
|
state.selectedTree = state.packetFrameTreeMap.get(current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**帧数据点击选中 */
|
||||||
|
function handleSelectedTree(e: any) {
|
||||||
|
state.selectedTree = e;
|
||||||
|
}
|
||||||
|
|
||||||
/**包数据表点击选中 */
|
/**接收数据后回调 */
|
||||||
function handleSelectedFrame(num: number) {
|
function wkMessage(res: Record<string, any>) {
|
||||||
console.log('fnSelectedFrame', num, state.totalPackets);
|
// console.log('wkMessage', res);
|
||||||
const packet = state.packetList.find((v: any) => v.number === num);
|
switch (res.type) {
|
||||||
if (!packet) return;
|
case 'status':
|
||||||
const packetFrame = packet.frame;
|
console.info(res.status);
|
||||||
state.selectedFrame = packet.number;
|
break;
|
||||||
state.packetFrame = packetFrame;
|
case 'error':
|
||||||
state.packetFrameTreeMap = parseFrameTree('root', packetFrame);
|
console.warn(res.error);
|
||||||
state.selectedTree = NO_SELECTION;
|
break;
|
||||||
state.selectedDataSourceIndex = 0;
|
case 'init':
|
||||||
|
wk.send({ type: 'columns' });
|
||||||
|
state.initialized = true;
|
||||||
|
break;
|
||||||
|
case 'frames':
|
||||||
|
const { frames } = res.data;
|
||||||
|
|
||||||
|
// 有匹配的选择第一个
|
||||||
|
if (frames.length > 0) {
|
||||||
|
state.selectedFrame = frames[0].number;
|
||||||
|
wk.send({ type: 'select', number: state.selectedFrame });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'selected':
|
||||||
|
// 首行修改帧编号
|
||||||
|
const fristFrame = res.data.tree[0];
|
||||||
|
res.data.tree[0].label = fristFrame.label.replace(
|
||||||
|
'Frame 1:',
|
||||||
|
`Frame ${tableState.selectedNumber}:`
|
||||||
|
);
|
||||||
|
const item = fristFrame.tree.find(
|
||||||
|
(item: any) => item.label === 'Frame Number: 1'
|
||||||
|
);
|
||||||
|
if (item) {
|
||||||
|
item.label = `Frame Number: ${tableState.selectedNumber}:`;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.packetFrame = res.data;
|
||||||
|
state.packetFrameTreeMap = parseFrameTree('root', res.data);
|
||||||
|
state.selectedTree = NO_SELECTION;
|
||||||
|
state.selectedDataSourceIndex = 0;
|
||||||
|
break;
|
||||||
|
case 'processed':
|
||||||
|
// setStatus(`Error: non-zero return code (${e.data.code})`);
|
||||||
|
// 加载数据
|
||||||
|
wk.send({
|
||||||
|
type: 'frames',
|
||||||
|
filter: '',
|
||||||
|
skip: 0,
|
||||||
|
limit: 1,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn(res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/**包数据表滚动底部加载 */
|
/**建立WK连接 */
|
||||||
function handleScrollBottom(index: any) {
|
function fnWK() {
|
||||||
console.log('handleScrollBottom', index);
|
const options: wkUtil.OptionsType = {
|
||||||
|
url: wkUrl,
|
||||||
|
onmessage: wkMessage,
|
||||||
|
onerror: (ev: any) => {
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
//建立连接
|
||||||
|
wk.connect(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =========== WS ==============
|
||||||
|
/**接收数据后回调 */
|
||||||
|
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) {
|
||||||
|
tableState.data = [];
|
||||||
|
tableState.total = 0;
|
||||||
|
taskState.keepTimer = setInterval(() => {
|
||||||
|
if (ws.state() != WebSocket.OPEN) {
|
||||||
|
clearInterval(taskState.keepTimer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
packetKeep(taskState.task.taskNo, 120);
|
||||||
|
}, 90 * 1000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅组信息
|
||||||
|
if (!data?.groupId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data.groupId === `4_${taskState.task.taskNo}`) {
|
||||||
|
const packetData = data.data;
|
||||||
|
tableState.total = packetData.number;
|
||||||
|
tableState.data.unshift(packetData);
|
||||||
|
if (tableState.data.length > 100) {
|
||||||
|
tableState.data.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**建立WS连接 */
|
||||||
|
function fnWS() {
|
||||||
|
const options: wsUtil.OptionsType = {
|
||||||
|
url: '/ws',
|
||||||
|
params: {
|
||||||
|
/**订阅通道组
|
||||||
|
*
|
||||||
|
* 信令跟踪Packet (GroupID:4_taskNo)
|
||||||
|
*/
|
||||||
|
subGroupID: `4_${taskState.task.taskNo}`,
|
||||||
|
},
|
||||||
|
heartTimer: 30 * 1000,
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: (ev: any) => {
|
||||||
|
// 接收数据后回调
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
//建立连接
|
||||||
|
ws.connect(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========== 任务 ==============
|
||||||
|
type TaskStateType = {
|
||||||
|
/**网卡设备列表 */
|
||||||
|
devices: { id: string; label: string; children: any[] }[];
|
||||||
|
|
||||||
|
/**任务 */
|
||||||
|
task: {
|
||||||
|
taskNo: string;
|
||||||
|
device: string;
|
||||||
|
filter: string;
|
||||||
|
outputPCAP: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**过滤条件 */
|
||||||
|
filter: string;
|
||||||
|
/**过滤条件错误信息 */
|
||||||
|
filterError: string | null;
|
||||||
|
|
||||||
|
/**保活调度器 */
|
||||||
|
keepTimer: any;
|
||||||
|
|
||||||
|
/**停止标记 */
|
||||||
|
stop: boolean;
|
||||||
|
stopOutputPCAP: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const taskState = reactive<TaskStateType>({
|
||||||
|
devices: [],
|
||||||
|
|
||||||
|
task: {
|
||||||
|
taskNo: '',
|
||||||
|
device: '192.168.5.58',
|
||||||
|
filter: '',
|
||||||
|
outputPCAP: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
filter: '',
|
||||||
|
filterError: null,
|
||||||
|
|
||||||
|
keepTimer: null,
|
||||||
|
|
||||||
|
stop: false,
|
||||||
|
stopOutputPCAP: false,
|
||||||
|
});
|
||||||
|
|
||||||
/**开始跟踪 */
|
/**开始跟踪 */
|
||||||
function fnStart() {
|
function fnStart() {
|
||||||
// state.task.taskNo = 'laYlTbq';
|
if (taskState.keepTimer) return;
|
||||||
state.task.taskNo = Number(Date.now()).toString(16);
|
taskState.keepTimer = true;
|
||||||
state.task.outputPCAP = false;
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
packetStart(state.task).then(res => {
|
// taskState.task.taskNo = 'laYlTbq';
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
taskState.task.taskNo = Number(Date.now()).toString(16);
|
||||||
fnReset();
|
taskState.task.filter = taskState.filter;
|
||||||
fnWS();
|
packetStart(toRaw(taskState.task))
|
||||||
} else {
|
.then(res => {
|
||||||
message.error(t('common.operateErr'), 3);
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
}
|
// 清空选择帧的数据
|
||||||
});
|
state.selectedFrame = 0;
|
||||||
|
state.packetFrame = { tree: [], data_sources: [] };
|
||||||
|
state.packetFrameTreeMap = null;
|
||||||
|
state.selectedTree = NO_SELECTION;
|
||||||
|
state.selectedDataSourceIndex = 0;
|
||||||
|
// 开启
|
||||||
|
if (!state.initialized) {
|
||||||
|
fnWK();
|
||||||
|
}
|
||||||
|
clearInterval(taskState.keepTimer);
|
||||||
|
taskState.keepTimer = null;
|
||||||
|
fnWS();
|
||||||
|
// 记录状态
|
||||||
|
taskState.stop = false;
|
||||||
|
taskState.stopOutputPCAP = taskState.task.outputPCAP;
|
||||||
|
} else {
|
||||||
|
message.error(t('common.operateErr'), 3);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**停止跟踪 */
|
/**停止跟踪 */
|
||||||
function fnStop() {
|
function fnStop() {
|
||||||
packetStop(state.task.taskNo).then(res => {
|
if (typeof taskState.keepTimer !== 'number') return;
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
packetStop(taskState.task.taskNo)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code !== RESULT_CODE_SUCCESS) {
|
||||||
|
message.warning(res.msg, 3);
|
||||||
|
}
|
||||||
|
// 关闭监听
|
||||||
ws.close();
|
ws.close();
|
||||||
state.initialized = false;
|
clearInterval(taskState.keepTimer);
|
||||||
state.filter = '';
|
taskState.keepTimer = null;
|
||||||
state.filterError = null;
|
|
||||||
} else {
|
taskState.filter = '';
|
||||||
message.warning(res.msg, 3);
|
taskState.filterError = null;
|
||||||
}
|
|
||||||
});
|
taskState.stop = true;
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**跟踪数据表过滤 */
|
/**跟踪数据表过滤 */
|
||||||
function handleFilterFrames() {
|
function handleFilterFrames() {
|
||||||
packetFilter(state.task.taskNo, state.filter).then(res => {
|
packetFilter(taskState.task.taskNo, taskState.filter).then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
state.task.filter = state.filter;
|
taskState.task.filter = taskState.filter;
|
||||||
|
taskState.filterError = null;
|
||||||
} else {
|
} else {
|
||||||
state.filterError = res.msg;
|
taskState.filterError = res.msg;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**开始跟踪 */
|
/**选择网卡 */
|
||||||
function fnDevice(v: string) {
|
function fnDevice(v: string) {
|
||||||
state.task.device = v;
|
taskState.task.device = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**下载触发等待 */
|
/**下载触发等待 */
|
||||||
let downLoading = ref<boolean>(false);
|
let downLoading = ref<boolean>(false);
|
||||||
|
|
||||||
/**信息文件下载 */
|
/**信息文件下载 */
|
||||||
function fnDownloadPCAP() {
|
function fnDownloadPCAP() {
|
||||||
if (downLoading.value) return;
|
if (downLoading.value) return;
|
||||||
const fileName = `trace_packet_${state.task.taskNo}.pcap`;
|
const fileName = `packet_${taskState.task.taskNo}.pcap`;
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: t('common.tipTitle'),
|
title: t('common.tipTitle'),
|
||||||
content: t('views.logManage.neFile.downTip', { fileName }),
|
content: t('views.logManage.neFile.downTip', { fileName }),
|
||||||
onOk() {
|
onOk() {
|
||||||
downLoading.value = true;
|
downLoading.value = true;
|
||||||
const hide = message.loading(t('common.loading'), 0);
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
filePullTask(state.task.taskNo)
|
packetPCAPFile(taskState.task.taskNo)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
message.success({
|
message.success({
|
||||||
@@ -267,90 +394,203 @@ function fnDownloadPCAP() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**接收数据后回调 */
|
// =========== 表格数据 ==============
|
||||||
function wsMessage(res: Record<string, any>) {
|
/**表格状态类型 */
|
||||||
const { code, requestId, data } = res;
|
type TabeStateType = {
|
||||||
if (code === RESULT_CODE_ERROR) {
|
/**加载等待 */
|
||||||
console.warn(res.msg);
|
loading: boolean;
|
||||||
return;
|
/**记录数据 */
|
||||||
}
|
data: Record<string, any>[];
|
||||||
|
/**总记录数 */
|
||||||
|
total: number;
|
||||||
|
/**选择帧编号 */
|
||||||
|
selectedNumber: number;
|
||||||
|
};
|
||||||
|
|
||||||
// 建联时发送请求
|
/**表格状态 */
|
||||||
if (!requestId && data.clientId) {
|
let tableState: TabeStateType = reactive({
|
||||||
state.initialized = true;
|
loading: false,
|
||||||
state.keepTimer = setInterval(() => {
|
data: [],
|
||||||
packetKeep(state.task.taskNo, 120);
|
total: 0,
|
||||||
}, 90 * 1000);
|
selectedNumber: 0,
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// 订阅组信息
|
/**表格字段列 */
|
||||||
if (!data?.groupId) {
|
let tableColumns = ref<ColumnsType>([
|
||||||
|
{
|
||||||
|
title: 'Number',
|
||||||
|
dataIndex: 'number',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Time',
|
||||||
|
dataIndex: 'time',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
customRender(opt) {
|
||||||
|
if (!opt.value) return '';
|
||||||
|
const nanoseconds = opt.value; // 纳秒时间戳
|
||||||
|
const milliseconds = Math.floor(nanoseconds / 1000000);
|
||||||
|
const nanosecondsPart = (nanoseconds % 1000000)
|
||||||
|
.toString()
|
||||||
|
.padStart(6, '0');
|
||||||
|
|
||||||
|
return `${parseDateToStr(
|
||||||
|
milliseconds,
|
||||||
|
'HH:mm:ss.SSS'
|
||||||
|
)}.${nanosecondsPart}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Source',
|
||||||
|
dataIndex: 'source',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Destination',
|
||||||
|
dataIndex: 'destination',
|
||||||
|
align: 'left',
|
||||||
|
width: 150,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Protocol',
|
||||||
|
dataIndex: 'protocol',
|
||||||
|
key: 'protocol',
|
||||||
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'length',
|
||||||
|
dataIndex: 'length',
|
||||||
|
align: 'right',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Info',
|
||||||
|
dataIndex: 'info',
|
||||||
|
align: 'left',
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看帧数据
|
||||||
|
* @param row 记录信息
|
||||||
|
*/
|
||||||
|
function fnVisible(row: Record<string, any>) {
|
||||||
|
// 选中行重复点击时显示隐藏
|
||||||
|
if (row.id === tableState.selectedNumber && state.selectedFrame !== 0) {
|
||||||
|
state.selectedFrame = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.groupId === `4_${state.task.taskNo}`) {
|
tableState.selectedNumber = row.number;
|
||||||
const packetData = data.data;
|
const blob = generatePCAP(row.time / 1e3, row.data);
|
||||||
state.totalPackets = packetData.number;
|
wk.send({ type: 'process', file: blob });
|
||||||
state.packetList.push(packetData);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
/**生成PCAP-blob */
|
||||||
|
function generatePCAP(timestamp: number, base64Data: string): Blob {
|
||||||
|
// 1. 转换数据
|
||||||
|
const binaryString = atob(base64Data);
|
||||||
|
const len = binaryString.length;
|
||||||
|
const bytes = new Uint8Array(len);
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
bytes[i] = binaryString.charCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
/**建立WS连接 */
|
// 2. 创建PCAP文件头
|
||||||
function fnWS() {
|
const fileHeader = new Uint8Array([
|
||||||
const options: OptionsType = {
|
0xd4,
|
||||||
url: '/ws',
|
0xc3,
|
||||||
params: {
|
0xb2,
|
||||||
/**订阅通道组
|
0xa1, // magic_number (微秒级)
|
||||||
*
|
0x02,
|
||||||
* 信令跟踪Packet (GroupID:4_taskNo)
|
0x00, // version_major (2)
|
||||||
*/
|
0x04,
|
||||||
subGroupID: `4_${state.task.taskNo}`,
|
0x00, // version_minor (4)
|
||||||
},
|
0x00,
|
||||||
onmessage: wsMessage,
|
0x00,
|
||||||
onerror: (ev: any) => {
|
0x00,
|
||||||
// 接收数据后回调
|
0x00, // thiszone (UTC)
|
||||||
console.error(ev);
|
0x00,
|
||||||
},
|
0x00,
|
||||||
};
|
0x00,
|
||||||
//建立连接
|
0x00, // sigfigs (固定0)
|
||||||
ws.connect(options);
|
0xff,
|
||||||
|
0xff,
|
||||||
|
0x00,
|
||||||
|
0x00, // snaplen (最大捕获长度)
|
||||||
|
0x01,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00, // network (LINKTYPE_ETHERNET)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 3. 生成时间戳
|
||||||
|
const date = new Date(timestamp);
|
||||||
|
const secs = Math.floor(date.getTime() / 1000);
|
||||||
|
const usecs = (date.getTime() % 1000) * 1000;
|
||||||
|
|
||||||
|
// 4. 构造PCAP报文头
|
||||||
|
const packetHeader = new Uint8Array(16);
|
||||||
|
const headerView = new DataView(packetHeader.buffer);
|
||||||
|
headerView.setUint32(0, secs, true); // 时间戳秒
|
||||||
|
headerView.setUint32(4, usecs, true); // 时间戳微秒
|
||||||
|
headerView.setUint32(8, bytes.length, true); // 捕获长度
|
||||||
|
headerView.setUint32(12, bytes.length, true); // 原始长度
|
||||||
|
|
||||||
|
// 5. 合并所有数据
|
||||||
|
const finalData = new Uint8Array(
|
||||||
|
fileHeader.length + packetHeader.length + bytes.length
|
||||||
|
);
|
||||||
|
finalData.set(fileHeader);
|
||||||
|
finalData.set(packetHeader, fileHeader.length);
|
||||||
|
finalData.set(bytes, fileHeader.length + packetHeader.length);
|
||||||
|
|
||||||
|
// 6. 文件Blob对象
|
||||||
|
return new Blob([finalData], { type: 'application/octet-stream' });
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
packetDevices().then(res => {
|
packetDevices().then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
state.devices = res.data;
|
taskState.devices = res.data;
|
||||||
if (res.data.length === 0) return;
|
if (res.data.length === 0) return;
|
||||||
state.task.device = res.data[0].id;
|
taskState.task.device = res.data[0].id;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
fnWK();
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
clearInterval(state.keepTimer);
|
clearInterval(taskState.keepTimer);
|
||||||
state.keepTimer = null;
|
wk.send({ type: 'close' }) && wk.close();
|
||||||
if (ws.state() === WebSocket.OPEN) ws.close();
|
if (ws.state() <= WebSocket.OPEN) ws.close();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<a-card :bordered="false" :body-style="{ padding: '12px' }">
|
<a-card :bordered="false" :body-style="{ padding: '12px' }">
|
||||||
<div class="toolbar">
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
<a-space :size="8" class="toolbar-oper">
|
<template #title>
|
||||||
|
<a-space :size="8" align="end">
|
||||||
<a-dropdown-button
|
<a-dropdown-button
|
||||||
type="primary"
|
type="primary"
|
||||||
:disabled="state.initialized"
|
|
||||||
@click="fnStart"
|
@click="fnStart"
|
||||||
|
:disabled="!taskState.stop && taskState.task.taskNo !== ''"
|
||||||
>
|
>
|
||||||
<PlayCircleOutlined />
|
<PlayCircleOutlined />
|
||||||
Start Trace
|
Start Trace
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<a-menu
|
<a-menu
|
||||||
@click="({ key }:any) => fnDevice(key)"
|
@click="({ key }:any) => fnDevice(key)"
|
||||||
:selectedKeys="[state.task.device]"
|
:selectedKeys="[taskState.task.device]"
|
||||||
>
|
>
|
||||||
<a-menu-item v-for="v in state.devices" :key="v.id">
|
<a-menu-item v-for="v in taskState.devices" :key="v.id">
|
||||||
<a-popover placement="rightTop" trigger="hover">
|
<a-popover placement="rightTop" trigger="hover">
|
||||||
<template #content>
|
<template #content>
|
||||||
<div v-for="c in v.children">{{ c.id }}</div>
|
<div v-for="c in v.children">{{ c.id }}</div>
|
||||||
@@ -366,79 +606,130 @@ onBeforeUnmount(() => {
|
|||||||
<template #icon><DownOutlined /></template>
|
<template #icon><DownOutlined /></template>
|
||||||
</a-dropdown-button>
|
</a-dropdown-button>
|
||||||
|
|
||||||
<a-button danger @click.prevent="fnStop()" v-if="state.initialized">
|
<a-button
|
||||||
|
danger
|
||||||
|
@click.prevent="fnStop()"
|
||||||
|
:disabled="taskState.stop || taskState.task.taskNo === ''"
|
||||||
|
>
|
||||||
<template #icon><CloseCircleOutlined /></template>
|
<template #icon><CloseCircleOutlined /></template>
|
||||||
Stop Trace
|
Stop Trace
|
||||||
</a-button>
|
</a-button>
|
||||||
|
|
||||||
|
<a-checkbox
|
||||||
|
v-model:checked="taskState.task.outputPCAP"
|
||||||
|
:disabled="!taskState.stop && taskState.task.taskNo !== ''"
|
||||||
|
>
|
||||||
|
Output PCAP
|
||||||
|
</a-checkbox>
|
||||||
|
|
||||||
|
<a-tag color="processing" v-if="taskState.task.filter !== ''">
|
||||||
|
{{ taskState.task.filter }}
|
||||||
|
</a-tag>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 插槽-卡片右侧 -->
|
||||||
|
<template #extra>
|
||||||
|
<a-space :size="8" align="end">
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
:loading="downLoading"
|
:loading="downLoading"
|
||||||
@click.prevent="fnDownloadPCAP()"
|
@click.prevent="fnDownloadPCAP()"
|
||||||
v-if="state.task.outputPCAP"
|
v-if="taskState.stop && taskState.stopOutputPCAP"
|
||||||
>
|
>
|
||||||
<template #icon><DownloadOutlined /></template>
|
<template #icon><DownloadOutlined /></template>
|
||||||
{{ t('common.downloadText') }}
|
{{ t('common.downloadText') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-tag
|
|
||||||
color="green"
|
|
||||||
v-show="!!state.task.filter && state.initialized"
|
|
||||||
>
|
|
||||||
{{ state.task.filter }}
|
|
||||||
</a-tag>
|
|
||||||
</a-space>
|
|
||||||
|
|
||||||
<a-space :size="8" class="toolbar-info" v-show="state.initialized">
|
|
||||||
<span>
|
<span>
|
||||||
{{ t('views.traceManage.task.traceId') }}:
|
Packets:
|
||||||
<strong>{{ state.task.taskNo }}</strong>
|
<strong>{{ tableState.total }}</strong>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
Task No:
|
||||||
|
<strong>{{ taskState.task.taskNo }}</strong>
|
||||||
</span>
|
</span>
|
||||||
<span> Packets: {{ state.totalPackets }} </span>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<!-- 包数据表过滤 -->
|
<!-- 包数据表过滤 -->
|
||||||
<a-input-group compact v-show="state.initialized">
|
<a-input-group compact>
|
||||||
<a-input
|
<a-auto-complete
|
||||||
v-model:value="state.filter"
|
v-model:value="taskState.filter"
|
||||||
placeholder="display filter, example: tcp"
|
:options="[
|
||||||
:allow-clear="true"
|
{ value: 'tcp and port 33030 and greater 100' },
|
||||||
|
{
|
||||||
|
value:
|
||||||
|
'(src 192.168.5.58 and dst port 33030) or (src 192.168.9.59 and dst port 33030)',
|
||||||
|
},
|
||||||
|
{ value: 'src host 192.168.5.58 and dst host 192.168.5.58' },
|
||||||
|
{ value: 'host 192.168.5.58 and greater 100 and less 2500' },
|
||||||
|
]"
|
||||||
style="width: calc(100% - 100px)"
|
style="width: calc(100% - 100px)"
|
||||||
@pressEnter="handleFilterFrames"
|
:allow-clear="true"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<a-input
|
||||||
<FilterOutlined />
|
placeholder="BPF Basic Filter, example: tcp"
|
||||||
</template>
|
@pressEnter="handleFilterFrames"
|
||||||
</a-input>
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<FilterOutlined />
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-auto-complete>
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
html-type="submit"
|
html-type="submit"
|
||||||
style="width: 100px"
|
style="width: 100px"
|
||||||
@click="handleFilterFrames"
|
@click="handleFilterFrames"
|
||||||
|
:disabled="taskState.task.taskNo === '' || taskState.stop"
|
||||||
>
|
>
|
||||||
Filter
|
Filter
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
<a-alert
|
<a-alert
|
||||||
:message="state.filterError"
|
:message="taskState.filterError"
|
||||||
type="error"
|
type="error"
|
||||||
v-if="state.filterError != null"
|
v-if="taskState.filterError != null"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 包数据表 -->
|
<!-- 包数据表 -->
|
||||||
<PacketTable
|
<a-table
|
||||||
:columns="state.columns"
|
class="table"
|
||||||
:data="state.packetList"
|
row-key="number"
|
||||||
:selectedFrame="state.selectedFrame"
|
:columns="tableColumns"
|
||||||
:onSelectedFrame="handleSelectedFrame"
|
:loading="tableState.loading"
|
||||||
:onScrollBottom="handleScrollBottom"
|
:data-source="tableState.data"
|
||||||
></PacketTable>
|
size="small"
|
||||||
|
:pagination="false"
|
||||||
<a-row>
|
:row-class-name="(record:any) => {
|
||||||
|
return `table-striped-${record.protocol}`
|
||||||
|
}"
|
||||||
|
:customRow="
|
||||||
|
record => {
|
||||||
|
return {
|
||||||
|
onClick: () => fnVisible(record),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"
|
||||||
|
:scroll="{ x: tableColumns.length * 150, y: '400px' }"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.key === 'protocol'">
|
||||||
|
<!-- <DictTag :options="dict.traceMsgType" :value="record.msgType" /> -->
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<!-- 帧数据 -->
|
||||||
|
<a-row
|
||||||
|
:gutter="16"
|
||||||
|
style="border: 2px rgb(217 217 217) solid; border-radius: 6px"
|
||||||
|
v-show="state.selectedFrame == 1"
|
||||||
|
>
|
||||||
<a-col :lg="12" :md="12" :xs="24" class="tree">
|
<a-col :lg="12" :md="12" :xs="24" class="tree">
|
||||||
<!-- 帧数据 -->
|
<!-- 帧数据 -->
|
||||||
<DissectionTree
|
<DissectionTree
|
||||||
id="root"
|
id="root"
|
||||||
:select="handleSelectedTreeEntry"
|
:select="handleSelectedTree"
|
||||||
:selected="state.selectedTree"
|
:selected="state.selectedTree"
|
||||||
:tree="state.packetFrame.tree"
|
:tree="state.packetFrame.tree"
|
||||||
/>
|
/>
|
||||||
@@ -474,28 +765,6 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.toolbar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
.toolbar-oper {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.toolbar-info {
|
|
||||||
text-align: right;
|
|
||||||
padding-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.summary {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.summary-item > span:first-child {
|
|
||||||
font-weight: 600;
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree {
|
.tree {
|
||||||
font-size: 0.8125rem;
|
font-size: 0.8125rem;
|
||||||
line-height: 1.5rem;
|
line-height: 1.5rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user