feat: 跟踪任务查看pcap内容信息
This commit is contained in:
@@ -65,6 +65,21 @@ export async function delTraceTask(ids: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跟踪任务文件
|
||||||
|
* @param query 对象
|
||||||
|
* @returns object
|
||||||
|
*/
|
||||||
|
export function filePullTask(traceId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/trace/task/filePull',
|
||||||
|
method: 'get',
|
||||||
|
params: { traceId },
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取网元跟踪接口列表
|
* 获取网元跟踪接口列表
|
||||||
* @returns object
|
* @returns object
|
||||||
|
|||||||
@@ -1126,9 +1126,7 @@ export default {
|
|||||||
stopNotRun: "{title} not running",
|
stopNotRun: "{title} not running",
|
||||||
},
|
},
|
||||||
task: {
|
task: {
|
||||||
neTypePlease: 'Query network element type',
|
traceId: 'Tracing No',
|
||||||
neType: 'NE Type',
|
|
||||||
neID: 'NE ID',
|
|
||||||
trackType: 'Tracing Type',
|
trackType: 'Tracing Type',
|
||||||
trackTypePlease: 'Please select a tracing type',
|
trackTypePlease: 'Please select a tracing type',
|
||||||
creater: 'Created by',
|
creater: 'Created by',
|
||||||
@@ -1165,6 +1163,7 @@ export default {
|
|||||||
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",
|
||||||
traceFile: "Tracking File",
|
traceFile: "Tracking File",
|
||||||
errMsg: "Error Message",
|
errMsg: "Error Message",
|
||||||
imsiORmsisdn: "imsi or msisdn is null, cannot start task",
|
imsiORmsisdn: "imsi or msisdn is null, cannot start task",
|
||||||
|
|||||||
@@ -1126,9 +1126,7 @@ export default {
|
|||||||
stopNotRun: "{title} 任务未运行",
|
stopNotRun: "{title} 任务未运行",
|
||||||
},
|
},
|
||||||
task: {
|
task: {
|
||||||
neTypePlease: '请选择网元类型',
|
traceId: '跟踪编号',
|
||||||
neType: '网元类型',
|
|
||||||
neID: '网元内部标识',
|
|
||||||
trackType: '跟踪类型',
|
trackType: '跟踪类型',
|
||||||
trackTypePlease: '请选择跟踪类型',
|
trackTypePlease: '请选择跟踪类型',
|
||||||
creater: '创建人',
|
creater: '创建人',
|
||||||
@@ -1165,6 +1163,7 @@ export default {
|
|||||||
delTaskTip: '确认删除记录编号为 {id} 的数据项?',
|
delTaskTip: '确认删除记录编号为 {id} 的数据项?',
|
||||||
stopTask: '成功停止任务 {id}',
|
stopTask: '成功停止任务 {id}',
|
||||||
stopTaskTip: '确认停止记录编号为 {id} 的任务?',
|
stopTaskTip: '确认停止记录编号为 {id} 的任务?',
|
||||||
|
pcapView: "跟踪数据分析",
|
||||||
traceFile: "跟踪文件",
|
traceFile: "跟踪文件",
|
||||||
errMsg: "错误信息",
|
errMsg: "错误信息",
|
||||||
imsiORmsisdn: "imsi 或 msisdn 是空值,不能开始任务",
|
imsiORmsisdn: "imsi 或 msisdn 是空值,不能开始任务",
|
||||||
|
|||||||
287
src/views/traceManage/task/analyze.vue
Normal file
287
src/views/traceManage/task/analyze.vue
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onBeforeUnmount, ref, watch } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
|
import DissectionTree from '../tshark/components/DissectionTree.vue';
|
||||||
|
import DissectionDump from '../tshark/components/DissectionDump.vue';
|
||||||
|
import PacketTable from '../tshark/components/PacketTable.vue';
|
||||||
|
import { usePCAP, NO_SELECTION } from '../tshark/hooks/usePCAP';
|
||||||
|
import {
|
||||||
|
RESULT_CODE_ERROR,
|
||||||
|
RESULT_CODE_SUCCESS,
|
||||||
|
} from '@/constants/result-constants';
|
||||||
|
import { filePullTask } from '@/api/trace/task';
|
||||||
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import useTabsStore from '@/store/modules/tabs';
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const tabsStore = useTabsStore();
|
||||||
|
const ws = new WS();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const {
|
||||||
|
state,
|
||||||
|
handleSelectedTreeEntry,
|
||||||
|
handleSelectedFindSelection,
|
||||||
|
handleSelectedFrame,
|
||||||
|
handleScrollBottom,
|
||||||
|
handleFilterFrames,
|
||||||
|
handleLoadFile,
|
||||||
|
} = usePCAP();
|
||||||
|
|
||||||
|
/**跟踪编号 */
|
||||||
|
const traceId = ref<string>(route.query.traceId as string);
|
||||||
|
|
||||||
|
/**关闭跳转 */
|
||||||
|
function fnClose() {
|
||||||
|
const to = tabsStore.tabClose(route.path);
|
||||||
|
if (to) {
|
||||||
|
router.push(to);
|
||||||
|
} else {
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**获取PCAP文件 */
|
||||||
|
function fnFilePCAP() {
|
||||||
|
filePullTask(traceId.value).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
handleLoadFile(res.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**接收数据后回调 */
|
||||||
|
function wsMessage(res: Record<string, any>) {
|
||||||
|
const { code, requestId, data } = res;
|
||||||
|
if (code === RESULT_CODE_ERROR) {
|
||||||
|
console.warn(res.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 建联时发送请求
|
||||||
|
if (!requestId && data.clientId) {
|
||||||
|
fnFilePCAP();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 订阅组信息
|
||||||
|
if (!data?.groupId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data.groupId === `2_${traceId.value}`) {
|
||||||
|
fnFilePCAP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**建立WS连接 */
|
||||||
|
function fnWS() {
|
||||||
|
const options: OptionsType = {
|
||||||
|
url: '/ws',
|
||||||
|
params: {
|
||||||
|
/**订阅通道组
|
||||||
|
*
|
||||||
|
* 跟踪任务PCAP文件 (GroupID:2_traceId)
|
||||||
|
*/
|
||||||
|
subGroupID: `2_${traceId.value}`,
|
||||||
|
},
|
||||||
|
onmessage: wsMessage,
|
||||||
|
onerror: (ev: any) => {
|
||||||
|
// 接收数据后回调
|
||||||
|
console.error(ev);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
//建立连接
|
||||||
|
ws.connect(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => state.initialized,
|
||||||
|
v => {
|
||||||
|
v && fnWS();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
ws.close();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<PageContainer>
|
||||||
|
<a-card
|
||||||
|
:bordered="false"
|
||||||
|
:loading="!state.initialized"
|
||||||
|
:body-style="{ padding: '12px' }"
|
||||||
|
>
|
||||||
|
<div class="toolbar">
|
||||||
|
<a-space :size="8" class="toolbar-oper">
|
||||||
|
<a-button type="default" @click.prevent="fnClose()">
|
||||||
|
<template #icon><CloseOutlined /></template>
|
||||||
|
{{ t('common.close') }}
|
||||||
|
</a-button>
|
||||||
|
<span>
|
||||||
|
跟踪编号:
|
||||||
|
<strong>{{ traceId }}</strong>
|
||||||
|
</span>
|
||||||
|
</a-space>
|
||||||
|
|
||||||
|
<div class="toolbar-info">
|
||||||
|
<a-tag color="green" v-show="!!state.currentFilter">
|
||||||
|
{{ state.currentFilter }}
|
||||||
|
</a-tag>
|
||||||
|
<span> Matched Frame: {{ state.totalFrames }} </span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 包信息 -->
|
||||||
|
<a-popover
|
||||||
|
trigger="click"
|
||||||
|
placement="bottomLeft"
|
||||||
|
v-if="state.summary.filename"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="summary">
|
||||||
|
<div class="summary-item">
|
||||||
|
<span>Type:</span>
|
||||||
|
<span>{{ state.summary.file_type }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span>Encapsulation:</span>
|
||||||
|
<span>{{ state.summary.file_encap_type }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span>Packets:</span>
|
||||||
|
<span>{{ state.summary.packet_count }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-item">
|
||||||
|
<span>Duration:</span>
|
||||||
|
<span>{{ Math.round(state.summary.elapsed_time) }}s</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</a-popover>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 包数据表过滤 -->
|
||||||
|
<a-input-group compact>
|
||||||
|
<a-input
|
||||||
|
v-model:value="state.filter"
|
||||||
|
placeholder="display filter, example: tcp"
|
||||||
|
:allow-clear="true"
|
||||||
|
style="width: calc(100% - 100px)"
|
||||||
|
@pressEnter="handleFilterFrames"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<FilterOutlined />
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
html-type="submit"
|
||||||
|
style="width: 100px"
|
||||||
|
@click="handleFilterFrames"
|
||||||
|
>
|
||||||
|
Filter
|
||||||
|
</a-button>
|
||||||
|
</a-input-group>
|
||||||
|
<a-alert
|
||||||
|
:message="state.filterError"
|
||||||
|
type="error"
|
||||||
|
v-if="state.filterError != null"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 包数据表 -->
|
||||||
|
<PacketTable
|
||||||
|
:columns="state.columns"
|
||||||
|
:data="state.packetFrames"
|
||||||
|
:selectedFrame="state.selectedFrame"
|
||||||
|
:onSelectedFrame="handleSelectedFrame"
|
||||||
|
:onScrollBottom="handleScrollBottom"
|
||||||
|
></PacketTable>
|
||||||
|
|
||||||
|
<a-row :gutter="20">
|
||||||
|
<a-col :lg="12" :md="12" :xs="24" class="tree">
|
||||||
|
<!-- 帧数据 -->
|
||||||
|
<DissectionTree
|
||||||
|
id="root"
|
||||||
|
:select="handleSelectedTreeEntry"
|
||||||
|
:selected="state.selectedTreeEntry"
|
||||||
|
:tree="state.selectedPacket.tree"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="24" class="dump">
|
||||||
|
<!-- 报文数据 -->
|
||||||
|
<a-tabs
|
||||||
|
v-model:activeKey="state.selectedDataSourceIndex"
|
||||||
|
:tab-bar-gutter="16"
|
||||||
|
:tab-bar-style="{ marginBottom: '8px' }"
|
||||||
|
>
|
||||||
|
<a-tab-pane
|
||||||
|
:key="idx"
|
||||||
|
:tab="v.name"
|
||||||
|
v-for="(v, idx) in state.selectedPacket.data_sources"
|
||||||
|
style="overflow: auto"
|
||||||
|
>
|
||||||
|
<DissectionDump
|
||||||
|
:base64="v.data"
|
||||||
|
:select="(pos:number)=>handleSelectedFindSelection(idx, pos)"
|
||||||
|
:selected="
|
||||||
|
idx === state.selectedTreeEntry.idx
|
||||||
|
? state.selectedTreeEntry
|
||||||
|
: NO_SELECTION
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.toolbar-info {
|
||||||
|
flex: 1;
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.summary-item > span:first-child {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
padding-bottom: 0.75rem;
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-y: auto;
|
||||||
|
user-select: none;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.tree > ul.tree {
|
||||||
|
min-height: 15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dump {
|
||||||
|
padding-bottom: 0.75rem;
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.dump .ant-tabs-tabpane {
|
||||||
|
min-height: calc(15rem - 56px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted, toRaw, ref } from 'vue';
|
import { reactive, onMounted, toRaw, ref } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { Form, message, Modal } from 'ant-design-vue/lib';
|
import { Form, message, Modal } from 'ant-design-vue/lib';
|
||||||
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
import { SizeType } from 'ant-design-vue/lib/config-provider';
|
||||||
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
|
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
|
||||||
import { ColumnsType } from 'ant-design-vue/lib/table';
|
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
|
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 useNeInfoStore from '@/store/modules/neinfo';
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
@@ -23,6 +25,8 @@ import { parseObjHumpToLine } from '@/utils/parse-utils';
|
|||||||
const neInfoStore = useNeInfoStore();
|
const neInfoStore = useNeInfoStore();
|
||||||
const { getDict } = useDictStore();
|
const { getDict } = useDictStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
/**字典数据 */
|
/**字典数据 */
|
||||||
let dict: {
|
let dict: {
|
||||||
@@ -94,34 +98,34 @@ let tableState: TabeStateType = reactive({
|
|||||||
/**表格字段列 */
|
/**表格字段列 */
|
||||||
let tableColumns: ColumnsType = [
|
let tableColumns: ColumnsType = [
|
||||||
{
|
{
|
||||||
title: t('common.rowId'),
|
title: t('views.ne.common.neType'),
|
||||||
dataIndex: 'id',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('views.traceManage.task.neType'),
|
|
||||||
dataIndex: 'neType',
|
dataIndex: 'neType',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
sorter: {
|
sorter: {
|
||||||
compare: (a, b) => 1,
|
compare: (a, b) => 1,
|
||||||
multiple: 1,
|
multiple: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.traceManage.task.neID'),
|
title: t('views.ne.common.neId'),
|
||||||
dataIndex: 'neId',
|
dataIndex: 'neId',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.traceManage.task.traceId'),
|
||||||
|
dataIndex: 'traceId',
|
||||||
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.traceManage.task.trackType'),
|
title: t('views.traceManage.task.trackType'),
|
||||||
dataIndex: 'traceType',
|
dataIndex: 'traceType',
|
||||||
key: 'traceType',
|
key: 'traceType',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.traceManage.task.startTime'),
|
title: t('views.traceManage.task.startTime'),
|
||||||
dataIndex: 'startTime',
|
dataIndex: 'startTime',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
if (!opt.value) return '';
|
if (!opt.value) return '';
|
||||||
return parseDateToStr(opt.value);
|
return parseDateToStr(opt.value);
|
||||||
@@ -131,7 +135,7 @@ let tableColumns: ColumnsType = [
|
|||||||
{
|
{
|
||||||
title: t('views.traceManage.task.endTime'),
|
title: t('views.traceManage.task.endTime'),
|
||||||
dataIndex: 'endTime',
|
dataIndex: 'endTime',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
if (!opt.value) return '';
|
if (!opt.value) return '';
|
||||||
return parseDateToStr(opt.value);
|
return parseDateToStr(opt.value);
|
||||||
@@ -140,7 +144,7 @@ let tableColumns: ColumnsType = [
|
|||||||
{
|
{
|
||||||
title: t('common.operate'),
|
title: t('common.operate'),
|
||||||
key: 'id',
|
key: 'id',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -312,6 +316,7 @@ let modalState: ModalStateType = reactive({
|
|||||||
id: '',
|
id: '',
|
||||||
neType: '',
|
neType: '',
|
||||||
neId: '',
|
neId: '',
|
||||||
|
traceId: '',
|
||||||
traceType: '3',
|
traceType: '3',
|
||||||
startTime: undefined,
|
startTime: undefined,
|
||||||
endTime: undefined,
|
endTime: undefined,
|
||||||
@@ -545,6 +550,16 @@ function fnModalCancel() {
|
|||||||
modalState.neTypeInterface = [];
|
modalState.neTypeInterface = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**跳转PCAP文件详情页面 */
|
||||||
|
function fnRecordPCAPView(row: Record<string, any>) {
|
||||||
|
router.push({
|
||||||
|
path: `${route.path}${MENU_PATH_INLINE}/analyze`,
|
||||||
|
query: {
|
||||||
|
traceId: row.traceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 初始字典数据
|
// 初始字典数据
|
||||||
Promise.allSettled([getDict('trace_type')]).then(resArr => {
|
Promise.allSettled([getDict('trace_type')]).then(resArr => {
|
||||||
@@ -600,15 +615,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
|
<a-form-item :label="t('views.ne.common.neType')" name="neType ">
|
||||||
:label="t('views.traceManage.task.neType')"
|
|
||||||
name="neType "
|
|
||||||
>
|
|
||||||
<a-auto-complete
|
<a-auto-complete
|
||||||
v-model:value="queryParams.neType"
|
v-model:value="queryParams.neType"
|
||||||
:options="neCascaderOptions"
|
:options="neCascaderOptions"
|
||||||
allow-clear
|
allow-clear
|
||||||
:placeholder="t('views.traceManage.task.neTypePlease')"
|
:placeholder="t('views.ne.common.neTypePlease')"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -751,7 +763,7 @@ 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">
|
||||||
<a-tooltip>
|
<a-tooltip placement="topRight">
|
||||||
<template #title>{{ t('common.editText') }}</template>
|
<template #title>{{ t('common.editText') }}</template>
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
@@ -760,7 +772,15 @@ onMounted(() => {
|
|||||||
<template #icon><FormOutlined /></template>
|
<template #icon><FormOutlined /></template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<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-tooltip>
|
||||||
|
<a-tooltip placement="topRight">
|
||||||
<template #title>{{ t('common.deleteText') }}</template>
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
|
|||||||
Reference in New Issue
Block a user