714 lines
20 KiB
Vue
714 lines
20 KiB
Vue
<script lang="ts" setup>
|
||
import { onMounted, reactive } from 'vue';
|
||
import { useRoute, useRouter } from 'vue-router';
|
||
import { message, Modal } from 'ant-design-vue/lib';
|
||
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||
import { PageContainer } from 'antdv-pro-layout';
|
||
import { dumpStart, dumpStop, dumpDownload, traceUPF } from '@/api/trace/pcap';
|
||
import { listAllNeInfo } from '@/api/ne/neInfo';
|
||
import { getNeFile } from '@/api/tool/neFile';
|
||
import saveAs from 'file-saver';
|
||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||
import useI18n from '@/hooks/useI18n';
|
||
import { MENU_PATH_INLINE } from '@/constants/menu-constants';
|
||
const router = useRouter();
|
||
const route = useRoute();
|
||
const { t } = useI18n();
|
||
|
||
/**对话框对象信息状态类型 */
|
||
type ModalStateType = {
|
||
/**表单数据 */
|
||
from: Record<
|
||
string,
|
||
{
|
||
loading: boolean;
|
||
/**网元名 */
|
||
title: string;
|
||
/**命令 */
|
||
cmdStart: string;
|
||
/**upf标准版需要停止命令,一般空字符 */
|
||
cmdStop: string;
|
||
/**任务编号 */
|
||
taskCode: string;
|
||
/**任务日志,upf标准版为空字符串 */
|
||
logMsg: string;
|
||
/**提交表单参数 */
|
||
data: {
|
||
neType: string;
|
||
neId: string;
|
||
cmd?: string;
|
||
};
|
||
}
|
||
>;
|
||
/**tcpdump命令组 */
|
||
cmdOptions: {
|
||
/**命令名称 */
|
||
label: string;
|
||
/**命令选中值 */
|
||
value: string;
|
||
/**开始命令 */
|
||
start: string;
|
||
/**停止命令 */
|
||
stop: string;
|
||
}[];
|
||
/**UPF命令组 */
|
||
cmdOptionsUPF: {
|
||
/**命令名称 */
|
||
label: string;
|
||
/**命令选中值 */
|
||
value: string;
|
||
/**开始命令 */
|
||
start: string;
|
||
/**停止命令 */
|
||
stop: string;
|
||
}[];
|
||
/**详情框是否显示 */
|
||
visibleByView: boolean;
|
||
/**详情框内容 */
|
||
logMsg: string;
|
||
};
|
||
|
||
/**对话框对象信息状态 */
|
||
let modalState: ModalStateType = reactive({
|
||
from: {},
|
||
cmdOptions: [
|
||
{
|
||
label: t('views.traceManage.pcap.execCmd'),
|
||
value: 'any',
|
||
start: '-n -v -s 0',
|
||
stop: '',
|
||
},
|
||
{
|
||
label: t('views.traceManage.pcap.execCmd2'),
|
||
value: 'any2',
|
||
start: 'sctp or tcp port 3030 or 8088',
|
||
stop: '',
|
||
},
|
||
{
|
||
label: t('views.traceManage.pcap.execCmd3'),
|
||
value: 'any3',
|
||
start: '-n -s 0 -v -G 10 -W 7',
|
||
stop: '',
|
||
},
|
||
],
|
||
cmdOptionsUPF: [
|
||
{
|
||
label: t('views.traceManage.pcap.execUPFCmdA'),
|
||
value: 'pcap trace',
|
||
start: 'pcap trace rx tx max 100000 intfc any',
|
||
stop: 'pcap trace rx tx off',
|
||
},
|
||
{
|
||
label: t('views.traceManage.pcap.execUPFCmdB'),
|
||
value: 'pcap dispatch',
|
||
start: 'pcap dispatch trace on max 100000',
|
||
stop: 'pcap dispatch trace off',
|
||
},
|
||
],
|
||
visibleByView: false,
|
||
logMsg: '',
|
||
});
|
||
|
||
/**表格状态类型 */
|
||
type TabeStateType = {
|
||
/**加载等待 */
|
||
loading: boolean;
|
||
/**记录数据 */
|
||
data: object[];
|
||
/**勾选记录 */
|
||
selectedRowKeys: (string | number)[];
|
||
};
|
||
|
||
/**表格状态 */
|
||
let tableState: TabeStateType = reactive({
|
||
loading: false,
|
||
data: [],
|
||
selectedRowKeys: [],
|
||
});
|
||
|
||
/**表格字段列 */
|
||
let tableColumns: ColumnsType = [
|
||
{
|
||
title: t('views.ne.common.neType'),
|
||
dataIndex: 'neType',
|
||
align: 'left',
|
||
width: 100,
|
||
},
|
||
{
|
||
title: t('views.ne.common.neId'),
|
||
dataIndex: 'neId',
|
||
align: 'left',
|
||
width: 100,
|
||
},
|
||
{
|
||
title: t('views.ne.common.neName'),
|
||
dataIndex: 'neName',
|
||
align: 'left',
|
||
width: 100,
|
||
},
|
||
{
|
||
title: t('views.ne.common.ipAddr'),
|
||
dataIndex: 'ip',
|
||
align: 'left',
|
||
width: 150,
|
||
},
|
||
{
|
||
title: t('views.traceManage.pcap.cmd'),
|
||
key: 'cmd',
|
||
dataIndex: 'serverState',
|
||
align: 'left',
|
||
width: 350,
|
||
},
|
||
{
|
||
title: t('common.operate'),
|
||
key: 'id',
|
||
align: 'left',
|
||
},
|
||
];
|
||
|
||
/**表格多选 */
|
||
function fnTableSelectedRowKeys(keys: (string | number)[]) {
|
||
tableState.selectedRowKeys = keys;
|
||
}
|
||
|
||
/**查询全部网元数据列表 */
|
||
function fnGetList() {
|
||
if (tableState.loading) return;
|
||
tableState.loading = true;
|
||
listAllNeInfo({
|
||
bandStatus: false,
|
||
}).then(res => {
|
||
if (
|
||
res.code === RESULT_CODE_SUCCESS &&
|
||
Array.isArray(res.data) &&
|
||
res.data.length > 0
|
||
) {
|
||
tableState.data = res.data;
|
||
// 初始网元参数表单
|
||
if (tableState.data.length > 0) {
|
||
const { start, stop } = modalState.cmdOptions[0];
|
||
for (const item of res.data) {
|
||
modalState.from[item.id] = {
|
||
loading: false,
|
||
title: item.neName,
|
||
cmdStart: start,
|
||
cmdStop: stop,
|
||
taskCode: '',
|
||
logMsg: '',
|
||
data: {
|
||
neType: item.neType,
|
||
neId: item.neId,
|
||
},
|
||
};
|
||
}
|
||
}
|
||
} else {
|
||
message.warning({
|
||
content: t('common.noData'),
|
||
duration: 2,
|
||
});
|
||
}
|
||
tableState.loading = false;
|
||
});
|
||
}
|
||
|
||
/**抓包命令选择 */
|
||
function fnSelectCmd(id: any, option: any) {
|
||
modalState.from[id].cmdStart = option.start;
|
||
modalState.from[id].cmdStop = option.stop;
|
||
// 重置任务
|
||
modalState.from[id].taskCode = '';
|
||
modalState.from[id].logMsg = '';
|
||
}
|
||
|
||
/**
|
||
* 开始
|
||
* @param row 行记录
|
||
*/
|
||
function fnRecordStart(row?: Record<string, any>) {
|
||
let neIDs: string[] = [];
|
||
if (row) {
|
||
neIDs = [`${row.id}`];
|
||
} else {
|
||
row = {
|
||
neName: t('views.traceManage.pcap.textSelect'),
|
||
};
|
||
neIDs = tableState.selectedRowKeys.map(s => `${s}`);
|
||
}
|
||
|
||
Modal.confirm({
|
||
title: t('common.tipTitle'),
|
||
content: t('views.traceManage.pcap.startTip', { title: row.neName }),
|
||
onOk() {
|
||
const hide = message.loading(t('common.loading'), 0);
|
||
const fromArr = neIDs.map(id => modalState.from[id]);
|
||
const reqArr = fromArr.map(from => {
|
||
const data = Object.assign({ cmd: from.cmdStart }, from.data);
|
||
if (from.data.neType === 'UPF' && from.cmdStart.startsWith('pcap')) {
|
||
return traceUPF(data);
|
||
}
|
||
return dumpStart(data);
|
||
});
|
||
Promise.allSettled(reqArr)
|
||
.then(resArr => {
|
||
resArr.forEach((res, idx) => {
|
||
const title = fromArr[idx].title;
|
||
if (res.status === 'fulfilled') {
|
||
const resV = res.value;
|
||
if (resV.code === RESULT_CODE_SUCCESS) {
|
||
if (!fromArr[idx].cmdStop) {
|
||
fromArr[idx].taskCode = resV.data;
|
||
}
|
||
fromArr[idx].loading = true;
|
||
message.success({
|
||
content: t('views.traceManage.pcap.startOk', { title }),
|
||
duration: 3,
|
||
});
|
||
} else {
|
||
message.warning({
|
||
content: `${resV.msg}`,
|
||
duration: 3,
|
||
});
|
||
}
|
||
} else {
|
||
message.error({
|
||
content: t('views.traceManage.pcap.startErr', { title }),
|
||
duration: 3,
|
||
});
|
||
}
|
||
});
|
||
})
|
||
.finally(() => {
|
||
hide();
|
||
});
|
||
},
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 停止
|
||
* @param row 行记录
|
||
*/
|
||
function fnRecordStop(row?: Record<string, any>) {
|
||
let neIDs: string[] = [];
|
||
if (row) {
|
||
neIDs = [`${row.id}`];
|
||
} else {
|
||
row = {
|
||
neName: t('views.traceManage.pcap.textSelect'),
|
||
};
|
||
neIDs = tableState.selectedRowKeys.map(s => `${s}`);
|
||
}
|
||
|
||
Modal.confirm({
|
||
title: t('common.tipTitle'),
|
||
content: t('views.traceManage.pcap.stopTip', { title: row.neName }),
|
||
onOk() {
|
||
const fromArr = neIDs.map(id => modalState.from[id]);
|
||
const reqArr: any = [];
|
||
for (const from of fromArr) {
|
||
if (from.data.neType === 'UPF' && from.cmdStart.startsWith('pcap')) {
|
||
reqArr.push(
|
||
traceUPF(Object.assign({ cmd: from.cmdStop }, from.data))
|
||
);
|
||
} else {
|
||
const taskCode = from.taskCode;
|
||
if (!taskCode) {
|
||
message.warning({
|
||
content: t('views.traceManage.pcap.stopNotRun', {
|
||
title: from.title,
|
||
}),
|
||
duration: 3,
|
||
});
|
||
continue;
|
||
}
|
||
reqArr.push(
|
||
dumpStop(Object.assign({ taskCode: from.taskCode }, from.data))
|
||
);
|
||
}
|
||
}
|
||
if (reqArr.length === 0) return;
|
||
|
||
const hide = message.loading(t('common.loading'), 0);
|
||
Promise.allSettled(reqArr)
|
||
.then(resArr => {
|
||
resArr.forEach((res, idx) => {
|
||
const title = fromArr[idx].title;
|
||
|
||
if (res.status === 'fulfilled') {
|
||
const resV = res.value;
|
||
fromArr[idx].loading = false;
|
||
fromArr[idx].logMsg = '';
|
||
if (fromArr[idx].cmdStop) {
|
||
fromArr[idx].taskCode = '';
|
||
}
|
||
|
||
if (resV.code === RESULT_CODE_SUCCESS) {
|
||
if (fromArr[idx].cmdStop) {
|
||
fromArr[idx].taskCode = resV.data;
|
||
} else {
|
||
fromArr[idx].logMsg = resV.msg;
|
||
}
|
||
message.success({
|
||
content: t('views.traceManage.pcap.stopOk', { title }),
|
||
duration: 3,
|
||
});
|
||
} else if (resV.msg.indexOf('not run') > 0) {
|
||
message.warning({
|
||
content: t('views.traceManage.pcap.stopNotRun', { title }),
|
||
duration: 3,
|
||
});
|
||
} else {
|
||
message.warning({
|
||
content: `${resV.msg}`,
|
||
duration: 3,
|
||
});
|
||
}
|
||
} else {
|
||
message.error({
|
||
content: t('views.traceManage.pcap.stopErr', { title }),
|
||
duration: 3,
|
||
});
|
||
}
|
||
});
|
||
})
|
||
.finally(() => {
|
||
hide();
|
||
});
|
||
},
|
||
});
|
||
}
|
||
|
||
/**下载PCAP文件 */
|
||
function fnDownPCAP(row?: Record<string, any>) {
|
||
let neIDs: string[] = [];
|
||
if (row) {
|
||
neIDs = [`${row.id}`];
|
||
} else {
|
||
row = {
|
||
neName: t('views.traceManage.pcap.textSelect'),
|
||
};
|
||
neIDs = tableState.selectedRowKeys.map(s => `${s}`);
|
||
}
|
||
|
||
Modal.confirm({
|
||
title: t('common.tipTitle'),
|
||
content: t('views.traceManage.pcap.downTip', { title: row.neName }),
|
||
onOk() {
|
||
const fromArr = neIDs.map(id => modalState.from[id]);
|
||
const reqArr: any = [];
|
||
for (const from of fromArr) {
|
||
const taskCode = from.taskCode;
|
||
if (!taskCode) {
|
||
message.warning({
|
||
content: t('views.traceManage.pcap.stopNotRun', {
|
||
title: from.title,
|
||
}),
|
||
duration: 3,
|
||
});
|
||
continue;
|
||
}
|
||
if (from.data.neType === 'UPF' && taskCode.startsWith('/tmp')) {
|
||
const fileName = taskCode.substring(taskCode.lastIndexOf('/') + 1);
|
||
reqArr.push(
|
||
getNeFile(
|
||
Object.assign(
|
||
{ path: '/tmp', fileName, delTemp: true },
|
||
from.data
|
||
)
|
||
)
|
||
);
|
||
} else {
|
||
reqArr.push(
|
||
dumpDownload(
|
||
Object.assign({ taskCode: taskCode, delTemp: true }, from.data)
|
||
)
|
||
);
|
||
}
|
||
}
|
||
if (reqArr.length === 0) return;
|
||
|
||
const hide = message.loading(t('common.loading'), 0);
|
||
Promise.allSettled(reqArr)
|
||
.then(resArr => {
|
||
resArr.forEach((res, idx) => {
|
||
const title = fromArr[idx].title;
|
||
const taskCode = fromArr[idx].taskCode;
|
||
|
||
if (res.status === 'fulfilled') {
|
||
const resV = res.value;
|
||
if (resV.code === RESULT_CODE_SUCCESS) {
|
||
message.success({
|
||
content: t('views.traceManage.pcap.downOk', { title }),
|
||
duration: 3,
|
||
});
|
||
// 文件名
|
||
if (taskCode.startsWith('/tmp')) {
|
||
saveAs(resV.data, `${title}_${Date.now()}.pcap`);
|
||
} else {
|
||
saveAs(resV.data, `${title}_${Date.now()}.zip`);
|
||
}
|
||
} else {
|
||
message.warning({
|
||
content: `${resV.msg}`,
|
||
duration: 3,
|
||
});
|
||
}
|
||
} else {
|
||
message.error({
|
||
content: t('views.traceManage.pcap.downErr', { title }),
|
||
duration: 3,
|
||
});
|
||
}
|
||
});
|
||
})
|
||
.finally(() => {
|
||
hide();
|
||
});
|
||
},
|
||
});
|
||
}
|
||
|
||
/**批量操作 */
|
||
function fnBatchOper(key: string) {
|
||
switch (key) {
|
||
case 'start':
|
||
fnRecordStart();
|
||
break;
|
||
case 'stop':
|
||
fnRecordStop();
|
||
break;
|
||
case 'down':
|
||
fnDownPCAP();
|
||
break;
|
||
default:
|
||
console.warn('undefined batch oper', key);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 对话框弹出显示为 查看
|
||
* @param dictId 编号id
|
||
*/
|
||
function fnModalVisibleByVive(id: string | number) {
|
||
const from = modalState.from[id];
|
||
if (!from) return;
|
||
modalState.visibleByView = true;
|
||
modalState.logMsg = from.logMsg;
|
||
}
|
||
|
||
/**
|
||
* 对话框弹出关闭执行函数
|
||
* 进行表达规则校验
|
||
*/
|
||
function fnModalCancel() {
|
||
modalState.visibleByView = false;
|
||
modalState.logMsg = '';
|
||
}
|
||
|
||
/**跳转文件数据页面 */
|
||
function fnFileView(row?: Record<string, any>) {
|
||
let query = undefined;
|
||
if (row) {
|
||
const from = modalState.from[row.id];
|
||
query = {
|
||
neId: from.data.neId,
|
||
neType: from.data.neType,
|
||
};
|
||
}
|
||
router.push({
|
||
path: `${route.path}${MENU_PATH_INLINE}/file`,
|
||
query: query,
|
||
});
|
||
}
|
||
|
||
onMounted(() => {
|
||
// 获取网元列表
|
||
fnGetList();
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<PageContainer>
|
||
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||
<!-- 插槽-卡片左侧侧 -->
|
||
<template #title>
|
||
<a-space :size="8" align="center">
|
||
<a-button @click="fnFileView()">
|
||
<FileSearchOutlined />
|
||
{{ t('views.traceManage.pcap.fileView') }}
|
||
</a-button>
|
||
|
||
<a-dropdown trigger="click">
|
||
<a-button :disabled="tableState.selectedRowKeys.length <= 0">
|
||
{{ t('views.traceManage.pcap.batchOper') }}
|
||
<DownOutlined />
|
||
</a-button>
|
||
<template #overlay>
|
||
<a-menu @click="({ key }:any) => fnBatchOper(key)">
|
||
<a-menu-item key="start">
|
||
<PlayCircleOutlined />
|
||
{{ t('views.traceManage.pcap.batchStartText') }}
|
||
</a-menu-item>
|
||
<a-menu-item key="stop">
|
||
<StopOutlined />
|
||
{{ t('views.traceManage.pcap.batchStopText') }}
|
||
</a-menu-item>
|
||
<a-menu-item key="down">
|
||
<DownloadOutlined />
|
||
{{ t('views.traceManage.pcap.batchDownText') }}
|
||
</a-menu-item>
|
||
</a-menu>
|
||
</template>
|
||
</a-dropdown>
|
||
</a-space>
|
||
</template>
|
||
|
||
<!-- 插槽-卡片右侧 -->
|
||
<template #extra>
|
||
<a-space :size="8" align="center">
|
||
<a-tooltip>
|
||
<template #title>{{ t('common.reloadText') }}</template>
|
||
<a-button type="text" @click.prevent="fnGetList()">
|
||
<template #icon><ReloadOutlined /></template>
|
||
</a-button>
|
||
</a-tooltip>
|
||
</a-space>
|
||
</template>
|
||
|
||
<!-- 表格列表 -->
|
||
<a-table
|
||
class="table"
|
||
row-key="id"
|
||
size="small"
|
||
:columns="tableColumns"
|
||
:loading="tableState.loading"
|
||
:data-source="tableState.data"
|
||
:pagination="false"
|
||
:scroll="{ x: tableColumns.length * 170 }"
|
||
:row-selection="{
|
||
type: 'checkbox',
|
||
selectedRowKeys: tableState.selectedRowKeys,
|
||
onChange: fnTableSelectedRowKeys,
|
||
}"
|
||
>
|
||
<template #bodyCell="{ column, record }">
|
||
<template v-if="column.key === 'cmd'">
|
||
<a-auto-complete
|
||
v-model:value="modalState.from[record.id].cmdStart"
|
||
:options="
|
||
record.neType === 'UPF'
|
||
? modalState.cmdOptions.concat(modalState.cmdOptionsUPF)
|
||
: modalState.cmdOptions
|
||
"
|
||
:placeholder="t('views.traceManage.pcap.capArgPlease')"
|
||
:disabled="modalState.from[record.id].loading"
|
||
allow-clear
|
||
@select="(_: any, option: any) => fnSelectCmd(record.id, option)"
|
||
style="width: 100%"
|
||
/>
|
||
</template>
|
||
<template v-if="column.key === 'id'">
|
||
<a-space :size="8" align="start" direction="horizontal">
|
||
<a-tooltip placement="topRight">
|
||
<template #title>
|
||
<div>{{ t('views.traceManage.pcap.textStart') }}</div>
|
||
</template>
|
||
<a-button
|
||
type="primary"
|
||
size="small"
|
||
:disabled="modalState.from[record.id].loading"
|
||
@click.prevent="fnRecordStart(record)"
|
||
>
|
||
<template #icon><PlayCircleOutlined /> </template>
|
||
</a-button>
|
||
</a-tooltip>
|
||
<a-tooltip
|
||
placement="topRight"
|
||
v-if="
|
||
modalState.from[record.id].loading ||
|
||
modalState.from[record.id].cmdStop
|
||
"
|
||
>
|
||
<template #title>
|
||
<div>{{ t('views.traceManage.pcap.textStop') }}</div>
|
||
</template>
|
||
<a-button
|
||
type="default"
|
||
danger
|
||
size="small"
|
||
@click.prevent="fnRecordStop(record)"
|
||
>
|
||
<template #icon><StopOutlined /> </template>
|
||
</a-button>
|
||
</a-tooltip>
|
||
|
||
<a-tooltip
|
||
placement="topRight"
|
||
v-if="
|
||
!modalState.from[record.id].loading &&
|
||
!!modalState.from[record.id].logMsg
|
||
"
|
||
>
|
||
<template #title>
|
||
<div>{{ t('views.traceManage.pcap.textLog') }}</div>
|
||
</template>
|
||
<a-button
|
||
type="primary"
|
||
ghost
|
||
size="small"
|
||
@click.prevent="fnModalVisibleByVive(record.id)"
|
||
>
|
||
<template #icon><FileTextOutlined /> </template>
|
||
</a-button>
|
||
</a-tooltip>
|
||
<a-tooltip
|
||
placement="topRight"
|
||
v-if="
|
||
!modalState.from[record.id].loading &&
|
||
!!modalState.from[record.id].taskCode
|
||
"
|
||
>
|
||
<template #title>
|
||
<div>{{ t('views.traceManage.pcap.textDown') }}</div>
|
||
</template>
|
||
<a-button
|
||
type="primary"
|
||
ghost
|
||
size="small"
|
||
@click.prevent="fnDownPCAP(record)"
|
||
>
|
||
<template #icon><DownloadOutlined /> </template>
|
||
</a-button>
|
||
</a-tooltip>
|
||
</a-space>
|
||
</template>
|
||
</template>
|
||
</a-table>
|
||
</a-card>
|
||
|
||
<!-- 日志信息框 -->
|
||
<ProModal
|
||
:drag="true"
|
||
:width="800"
|
||
:visible="modalState.visibleByView"
|
||
:footer="false"
|
||
:maskClosable="false"
|
||
:keyboard="false"
|
||
:body-style="{ padding: '12px' }"
|
||
:title="t('views.traceManage.pcap.textLogMsg')"
|
||
@cancel="fnModalCancel"
|
||
>
|
||
<a-textarea
|
||
v-model:value="modalState.logMsg"
|
||
:auto-size="{ minRows: 2, maxRows: 18 }"
|
||
:disabled="true"
|
||
style="color: rgba(0, 0, 0, 0.85)"
|
||
/>
|
||
</ProModal>
|
||
</PageContainer>
|
||
</template>
|
||
|
||
<style lang="less" scoped></style>
|