feat: PCAP文件解析功能页面优化
This commit is contained in:
@@ -31,11 +31,11 @@ type StateType = {
|
||||
/**当前选中的帧编号 */
|
||||
selectedFrame: number;
|
||||
/**当前选中的帧数据 */
|
||||
selectedPacket: { tree: any[]; data_sources: any[] };
|
||||
packetFrame: { tree: any[]; data_sources: any[] };
|
||||
/**pcap包帧数据 */
|
||||
packetFrameData: Map<string, any> | null;
|
||||
/**当前选中的帧数据-空占位 */
|
||||
selectedTreeEntry: typeof NO_SELECTION;
|
||||
packetFrameTreeMap: Map<string, any> | null;
|
||||
/**当前选中的帧数据 */
|
||||
selectedTree: typeof NO_SELECTION;
|
||||
/**选择帧的Dump数据标签 */
|
||||
selectedDataSourceIndex: number;
|
||||
/**处理完成状态 */
|
||||
@@ -69,11 +69,11 @@ export function usePCAP() {
|
||||
filter: '',
|
||||
filterError: null,
|
||||
currentFilter: '',
|
||||
selectedFrame: 1,
|
||||
selectedFrame: 0,
|
||||
/**当前选中的帧数据 */
|
||||
selectedPacket: { tree: [], data_sources: [] },
|
||||
packetFrameData: null, // 注意:Map 需要额外处理
|
||||
selectedTreeEntry: NO_SELECTION, // NO_SELECTION 需要定义
|
||||
packetFrame: { tree: [], data_sources: [] },
|
||||
packetFrameTreeMap: null, // 注意:Map 需要额外处理
|
||||
selectedTree: NO_SELECTION, // NO_SELECTION 需要定义
|
||||
/**选择帧的Dump数据标签 */
|
||||
selectedDataSourceIndex: 0,
|
||||
/**处理完成状态 */
|
||||
@@ -91,9 +91,9 @@ export function usePCAP() {
|
||||
state.nextPageNum = 1;
|
||||
// 选择帧的数据
|
||||
state.selectedFrame = 0;
|
||||
state.selectedPacket = { tree: [], data_sources: [] };
|
||||
state.packetFrameData = null;
|
||||
state.selectedTreeEntry = NO_SELECTION;
|
||||
state.packetFrame = { tree: [], data_sources: [] };
|
||||
state.packetFrameTreeMap = null;
|
||||
state.selectedTree = NO_SELECTION;
|
||||
state.selectedDataSourceIndex = 0;
|
||||
}
|
||||
|
||||
@@ -121,23 +121,23 @@ export function usePCAP() {
|
||||
}
|
||||
|
||||
/**帧数据点击选中 */
|
||||
function handleSelectedTreeEntry(e: any) {
|
||||
console.log('fnSelectedTreeEntry', e);
|
||||
state.selectedTreeEntry = e;
|
||||
function handleSelectedTree(e: any) {
|
||||
// console.log('fnSelectedTree', e);
|
||||
state.selectedTree = e;
|
||||
}
|
||||
/**报文数据点击选中 */
|
||||
function handleSelectedFindSelection(src_idx: number, pos: number) {
|
||||
console.log('fnSelectedFindSelection', pos);
|
||||
if (state.packetFrameData == null) return;
|
||||
// console.log('fnSelectedFindSelection', pos);
|
||||
if (state.packetFrameTreeMap == null) return;
|
||||
// find the smallest one
|
||||
let current = null;
|
||||
for (let [k, pp] of state.packetFrameData) {
|
||||
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.packetFrameData.get(current).length > pp.length
|
||||
state.packetFrameTreeMap.get(current).length > pp.length
|
||||
) {
|
||||
current = k;
|
||||
} else {
|
||||
@@ -146,19 +146,19 @@ export function usePCAP() {
|
||||
}
|
||||
}
|
||||
if (current != null) {
|
||||
state.selectedTreeEntry = state.packetFrameData.get(current);
|
||||
state.selectedTree = state.packetFrameTreeMap.get(current);
|
||||
}
|
||||
}
|
||||
/**包数据表点击选中 */
|
||||
function handleSelectedFrame(no: number) {
|
||||
console.log('fnSelectedFrame', no, state.totalFrames);
|
||||
// console.log('fnSelectedFrame', no, state.totalFrames);
|
||||
state.selectedFrame = no;
|
||||
wk.send({ type: 'select', number: state.selectedFrame });
|
||||
}
|
||||
/**包数据表滚动底部加载 */
|
||||
function handleScrollBottom() {
|
||||
const totalFetched = state.packetFrames.length;
|
||||
console.log('fnScrollBottom', totalFetched);
|
||||
// console.log('fnScrollBottom', totalFetched);
|
||||
if (!state.nextPageLoad && totalFetched < state.totalFrames) {
|
||||
state.nextPageLoad = true;
|
||||
state.nextPageNum++;
|
||||
@@ -167,7 +167,7 @@ export function usePCAP() {
|
||||
}
|
||||
/**包数据表过滤 */
|
||||
function handleFilterFrames() {
|
||||
console.log('fnFilterFinish', state.filter);
|
||||
// console.log('fnFilterFinish', state.filter);
|
||||
wk.send({ type: 'check-filter', filter: state.filter });
|
||||
}
|
||||
/**包数据表加载 */
|
||||
@@ -254,9 +254,9 @@ export function usePCAP() {
|
||||
}
|
||||
break;
|
||||
case 'selected':
|
||||
state.selectedPacket = res.data;
|
||||
state.packetFrameData = parseFrameData('root', res.data);
|
||||
state.selectedTreeEntry = NO_SELECTION;
|
||||
state.packetFrame = res.data;
|
||||
state.packetFrameTreeMap = parseFrameData('root', res.data);
|
||||
state.selectedTree = NO_SELECTION;
|
||||
state.selectedDataSourceIndex = 0;
|
||||
break;
|
||||
case 'processed':
|
||||
@@ -306,7 +306,7 @@ export function usePCAP() {
|
||||
|
||||
return {
|
||||
state,
|
||||
handleSelectedTreeEntry,
|
||||
handleSelectedTree,
|
||||
handleSelectedFindSelection,
|
||||
handleSelectedFrame,
|
||||
handleScrollBottom,
|
||||
|
||||
@@ -8,11 +8,12 @@ import DissectionDump from './components/DissectionDump.vue';
|
||||
import PacketTable from './components/PacketTable.vue';
|
||||
import { usePCAP, NO_SELECTION } from './hooks/usePCAP';
|
||||
import { parseSizeFromFile } from '@/utils/parse-utils';
|
||||
import { parseDateToStr } from '@/utils/date-utils';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
const { t } = useI18n();
|
||||
const {
|
||||
state,
|
||||
handleSelectedTreeEntry,
|
||||
handleSelectedTree,
|
||||
handleSelectedFindSelection,
|
||||
handleSelectedFrame,
|
||||
handleScrollBottom,
|
||||
@@ -49,8 +50,9 @@ function fnUpload(up: UploadRequestOption) {
|
||||
:loading="!state.initialized"
|
||||
:body-style="{ padding: '12px' }"
|
||||
>
|
||||
<div class="toolbar">
|
||||
<a-space :size="8" class="toolbar-oper">
|
||||
<!-- 插槽-卡片左侧侧 -->
|
||||
<template #title>
|
||||
<a-space :size="8" align="center">
|
||||
<a-upload
|
||||
name="file"
|
||||
list-type="picture"
|
||||
@@ -64,61 +66,94 @@ function fnUpload(up: UploadRequestOption) {
|
||||
</a-upload>
|
||||
<a-button @click="handleLoadExample()">Example</a-button>
|
||||
</a-space>
|
||||
|
||||
<div class="toolbar-info">
|
||||
</template>
|
||||
<!-- 插槽-卡片右侧 -->
|
||||
<template #extra>
|
||||
<a-space :size="8" align="center">
|
||||
<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>
|
||||
<!-- 包信息 -->
|
||||
<a-popover
|
||||
trigger="click"
|
||||
placement="bottomLeft"
|
||||
v-if="state.summary.filename"
|
||||
>
|
||||
<template #content>
|
||||
<div class="summary">
|
||||
<div class="summary-item">
|
||||
<span>Type:</span>
|
||||
<span>{{ state.summary.file_type }}</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span>Size:</span>
|
||||
<span>{{
|
||||
parseSizeFromFile(state.summary.file_length)
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span>Encapsulation:</span>
|
||||
<span>{{ state.summary.file_encap_type }}</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span>Packets:</span>
|
||||
<span>{{ state.summary.packet_count }}</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span>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 class="summary-item">
|
||||
<span>Size:</span>
|
||||
<span>{{ parseSizeFromFile(state.summary.file_length) }}</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span>Encapsulation:</span>
|
||||
<span>{{ state.summary.file_encap_type }}</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span>Packets:</span>
|
||||
<span>{{ state.summary.packet_count }}</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span>Duration:</span>
|
||||
<span>{{ Math.round(state.summary.elapsed_time) }}s</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<InfoCircleOutlined />
|
||||
</a-popover>
|
||||
</div>
|
||||
</template>
|
||||
<InfoCircleOutlined />
|
||||
</a-popover>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<!-- 包数据表过滤 -->
|
||||
<a-input-group compact>
|
||||
<a-input
|
||||
<a-auto-complete
|
||||
v-model:value="state.filter"
|
||||
placeholder="display filter, example: tcp"
|
||||
:allow-clear="true"
|
||||
:options="[
|
||||
{ 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)"
|
||||
@pressEnter="handleFilterFrames"
|
||||
:allow-clear="true"
|
||||
>
|
||||
<template #prefix>
|
||||
<FilterOutlined />
|
||||
</template>
|
||||
</a-input>
|
||||
<a-input
|
||||
placeholder="display filter, example: tcp"
|
||||
@pressEnter="handleFilterFrames"
|
||||
>
|
||||
<template #prefix>
|
||||
<FilterOutlined />
|
||||
</template>
|
||||
</a-input>
|
||||
</a-auto-complete>
|
||||
<a-button
|
||||
type="primary"
|
||||
html-type="submit"
|
||||
@@ -143,14 +178,18 @@ function fnUpload(up: UploadRequestOption) {
|
||||
:onScrollBottom="handleScrollBottom"
|
||||
></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">
|
||||
<!-- 帧数据 -->
|
||||
<DissectionTree
|
||||
id="root"
|
||||
:select="handleSelectedTreeEntry"
|
||||
:selected="state.selectedTreeEntry"
|
||||
:tree="state.selectedPacket.tree"
|
||||
:select="handleSelectedTree"
|
||||
:selected="state.selectedTree"
|
||||
:tree="state.packetFrame.tree"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :lg="12" :md="12" :xs="24" class="dump">
|
||||
@@ -163,15 +202,15 @@ function fnUpload(up: UploadRequestOption) {
|
||||
<a-tab-pane
|
||||
:key="idx"
|
||||
:tab="v.name"
|
||||
v-for="(v, idx) in state.selectedPacket.data_sources"
|
||||
v-for="(v, idx) in state.packetFrame.data_sources"
|
||||
style="overflow: auto"
|
||||
>
|
||||
<DissectionDump
|
||||
:base64="v.data"
|
||||
:select="(pos:number)=>handleSelectedFindSelection(idx, pos)"
|
||||
:selected="
|
||||
idx === state.selectedTreeEntry.idx
|
||||
? state.selectedTreeEntry
|
||||
idx === state.selectedTree.idx
|
||||
? state.selectedTree
|
||||
: NO_SELECTION
|
||||
"
|
||||
/>
|
||||
@@ -184,17 +223,6 @@ function fnUpload(up: UploadRequestOption) {
|
||||
</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;
|
||||
|
||||
Reference in New Issue
Block a user