mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-01-12 18:15:30 +08:00
perf: Optimize Process-Network Page Performance (#10456)
Refs https://github.com/1Panel-dev/1Panel/issues/6900
This commit is contained in:
parent
657b2ddc8a
commit
563ea985a4
2 changed files with 170 additions and 131 deletions
|
|
@ -44,7 +44,7 @@
|
|||
"js-base64": "^3.7.7",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"md-editor-v3": "^2.11.3",
|
||||
"monaco-editor": "^0.50.0",
|
||||
"monaco-editor": "^0.53.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^1.6.1",
|
||||
|
|
|
|||
|
|
@ -3,77 +3,58 @@
|
|||
<FireRouter />
|
||||
<LayoutContent :title="$t('menu.network', 2)" v-loading="loading">
|
||||
<template #rightToolBar>
|
||||
<div class="w-full">
|
||||
<el-form-item class="float-right">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<TableSearch
|
||||
@search="search()"
|
||||
:placeholder="$t('process.pid')"
|
||||
v-model:searchName="netSearch.processID"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<TableSearch
|
||||
@search="search()"
|
||||
:placeholder="$t('process.processName')"
|
||||
v-model:searchName="netSearch.processName"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<TableSearch
|
||||
@search="search()"
|
||||
:placeholder="$t('commons.table.port')"
|
||||
v-model:searchName="netSearch.port"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
<div class="w-full flex justify-end items-center gap-5">
|
||||
<el-select
|
||||
v-model="filters"
|
||||
:placeholder="$t('commons.table.status')"
|
||||
clearable
|
||||
multiple
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
:max-collapse-tags="2"
|
||||
@change="search()"
|
||||
class="p-w-400"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in statusOptions"
|
||||
:key="item.value"
|
||||
:label="item.text"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<TableSearch
|
||||
@search="search()"
|
||||
:placeholder="$t('process.pid')"
|
||||
v-model:searchName="netSearch.processID"
|
||||
/>
|
||||
<TableSearch
|
||||
@search="search()"
|
||||
:placeholder="$t('process.processName')"
|
||||
v-model:searchName="netSearch.processName"
|
||||
/>
|
||||
<TableSearch
|
||||
@search="search()"
|
||||
:placeholder="$t('commons.table.port')"
|
||||
v-model:searchName="netSearch.port"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #main>
|
||||
<ComplexTable
|
||||
:data="data"
|
||||
@sort-change="changeSort"
|
||||
@filter-change="changeFilter"
|
||||
ref="tableRef"
|
||||
:heightDiff="220"
|
||||
>
|
||||
<el-table-column :label="$t('commons.table.type')" fix prop="type"></el-table-column>
|
||||
<el-table-column :label="'PID'" fix prop="PID" max-width="60px" sortable></el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('process.processName')"
|
||||
fix
|
||||
prop="name"
|
||||
min-width="120px"
|
||||
></el-table-column>
|
||||
<el-table-column prop="localaddr" :label="$t('process.laddr')">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.localaddr.ip }}</span>
|
||||
<span v-if="row.localaddr.port > 0">:{{ row.localaddr.port }}</span>
|
||||
<div class="!h-[900px]">
|
||||
<el-auto-resizer>
|
||||
<template #default="{ height, width }">
|
||||
<el-table-v2
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:width="width"
|
||||
:height="height"
|
||||
:sort-by="sortState"
|
||||
@column-sort="changeSort"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remoteaddr" :label="$t('process.raddr')">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.remoteaddr.ip }}</span>
|
||||
<span v-if="row.remoteaddr.port > 0">:{{ row.remoteaddr.port }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="status"
|
||||
column-key="status"
|
||||
:label="$t('commons.table.status')"
|
||||
:filters="[
|
||||
{ text: 'LISTEN', value: 'LISTEN' },
|
||||
{ text: 'ESTABLISHED', value: 'ESTABLISHED' },
|
||||
{ text: 'TIME_WAIT', value: 'TIME_WAIT' },
|
||||
{ text: 'CLOSE_WAIT', value: 'CLOSE_WAIT' },
|
||||
{ text: 'NONE', value: 'NONE' },
|
||||
]"
|
||||
:filter-method="filterStatus"
|
||||
:filtered-value="sortConfig.filters"
|
||||
></el-table-column>
|
||||
</ComplexTable>
|
||||
</el-auto-resizer>
|
||||
</div>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
</div>
|
||||
|
|
@ -81,20 +62,20 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import FireRouter from '@/views/host/process/index.vue';
|
||||
import { ref, onMounted, onUnmounted, nextTick, reactive } from 'vue';
|
||||
import { ref, onMounted, onUnmounted, reactive, watch } from 'vue';
|
||||
import { GlobalStore } from '@/store';
|
||||
const globalStore = GlobalStore();
|
||||
import { SortBy, TableV2SortOrder } from 'element-plus';
|
||||
import i18n from '@/lang';
|
||||
|
||||
interface SortStatus {
|
||||
prop: '';
|
||||
order: '';
|
||||
filters: [];
|
||||
}
|
||||
const sortConfig: SortStatus = {
|
||||
prop: '',
|
||||
order: '',
|
||||
filters: [],
|
||||
};
|
||||
const statusOptions = [
|
||||
{ text: 'LISTEN', value: 'LISTEN' },
|
||||
{ text: 'ESTABLISHED', value: 'ESTABLISHED' },
|
||||
{ text: 'TIME_WAIT', value: 'TIME_WAIT' },
|
||||
{ text: 'CLOSE_WAIT', value: 'CLOSE_WAIT' },
|
||||
{ text: 'NONE', value: 'NONE' },
|
||||
];
|
||||
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
const netSearch = reactive({
|
||||
type: 'net',
|
||||
|
|
@ -104,70 +85,129 @@ const netSearch = reactive({
|
|||
});
|
||||
|
||||
let processSocket = ref(null) as unknown as WebSocket;
|
||||
const data = ref([]);
|
||||
const data = ref<any[]>([]);
|
||||
const oldData = ref<any[]>([]);
|
||||
const loading = ref(false);
|
||||
const tableRef = ref();
|
||||
const oldData = ref([]);
|
||||
|
||||
const changeSort = ({ prop, order }) => {
|
||||
sortConfig.prop = prop;
|
||||
sortConfig.order = order;
|
||||
const sortState = ref<SortBy>({
|
||||
key: 'PID',
|
||||
order: TableV2SortOrder.ASC,
|
||||
});
|
||||
const filters = ref<string[]>([]);
|
||||
|
||||
const sortByNum = (a: any, b: any, prop: string): number => {
|
||||
const aVal = parseFloat(a[prop]) || 0;
|
||||
const bVal = parseFloat(b[prop]) || 0;
|
||||
return aVal - bVal;
|
||||
};
|
||||
|
||||
const changeFilter = (filters: any) => {
|
||||
if (filters.status && filters.status.length > 0) {
|
||||
sortConfig.filters = filters.status;
|
||||
data.value = filterByStatus();
|
||||
sortTable();
|
||||
} else {
|
||||
data.value = oldData.value;
|
||||
sortConfig.filters = [];
|
||||
sortTable();
|
||||
}
|
||||
};
|
||||
const columns = ref([
|
||||
{
|
||||
key: 'type',
|
||||
title: i18n.global.t('commons.table.type'),
|
||||
dataKey: 'type',
|
||||
width: 220,
|
||||
},
|
||||
{
|
||||
key: 'PID',
|
||||
title: 'PID',
|
||||
dataKey: 'PID',
|
||||
width: 220,
|
||||
sortable: true,
|
||||
sortMethod: sortByNum,
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
title: i18n.global.t('process.processName'),
|
||||
dataKey: 'name',
|
||||
width: 300,
|
||||
},
|
||||
{
|
||||
key: 'localaddr',
|
||||
title: i18n.global.t('process.laddr'),
|
||||
dataKey: 'localaddr',
|
||||
width: 350,
|
||||
cellRenderer: ({ rowData }) => {
|
||||
const addr = rowData.localaddr;
|
||||
return addr?.ip ? `${addr.ip}${addr.port > 0 ? ':' + addr.port : ''}` : '';
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'remoteaddr',
|
||||
title: i18n.global.t('process.raddr'),
|
||||
dataKey: 'remoteaddr',
|
||||
width: 350,
|
||||
cellRenderer: ({ rowData }) => {
|
||||
const addr = rowData.remoteaddr;
|
||||
return addr?.ip ? `${addr.ip}${addr.port > 0 ? ':' + addr.port : ''}` : '';
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
title: i18n.global.t('commons.table.status'),
|
||||
dataKey: 'status',
|
||||
width: 380,
|
||||
cellRenderer: ({ rowData }) => rowData.status,
|
||||
},
|
||||
]);
|
||||
|
||||
const isWsOpen = () => {
|
||||
const readyState = processSocket && processSocket.readyState;
|
||||
return readyState === 1;
|
||||
};
|
||||
const closeSocket = () => {
|
||||
if (isWsOpen()) {
|
||||
processSocket && processSocket.close();
|
||||
}
|
||||
};
|
||||
watch(
|
||||
[sortState, oldData, filters],
|
||||
([newState, newData, newFilters]) => {
|
||||
if (!newData?.length) return;
|
||||
|
||||
const filterStatus = (value: string, row: any) => {
|
||||
return row.status === value;
|
||||
};
|
||||
let filtered = newData;
|
||||
if (newFilters.length > 0) {
|
||||
filtered = filtered.filter((row) => newFilters.includes(row.status));
|
||||
}
|
||||
|
||||
const onOpenProcess = () => {};
|
||||
const onMessage = (message: any) => {
|
||||
let result: any[] = JSON.parse(message.data);
|
||||
oldData.value = result;
|
||||
data.value = filterByStatus();
|
||||
sortTable();
|
||||
loading.value = false;
|
||||
const { key, order } = newState ?? {};
|
||||
if (!key || !order) {
|
||||
data.value = filtered;
|
||||
return;
|
||||
}
|
||||
|
||||
const currCol = columns.value.find((c) => c.key === key);
|
||||
if (!currCol) {
|
||||
data.value = filtered;
|
||||
return;
|
||||
}
|
||||
|
||||
const sortMethod = currCol.sortMethod ?? sortByNum;
|
||||
data.value = filtered.slice().sort((a, b) => {
|
||||
const res = (sortMethod as any)(a, b, currCol.dataKey);
|
||||
return order === TableV2SortOrder.ASC ? res : -res;
|
||||
});
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const changeSort = ({ key, order }) => {
|
||||
if (!order) order = TableV2SortOrder.ASC;
|
||||
sortState.value = { key, order };
|
||||
};
|
||||
|
||||
const filterByStatus = () => {
|
||||
if (sortConfig.filters.length > 0) {
|
||||
const newData = oldData.value.filter((re: any) => {
|
||||
return (sortConfig.filters as string[]).indexOf(re.status) > -1;
|
||||
});
|
||||
return newData;
|
||||
} else {
|
||||
return oldData.value;
|
||||
if (filters.value.length > 0) {
|
||||
return oldData.value.filter((row) => filters.value.includes(row.status));
|
||||
}
|
||||
return oldData.value;
|
||||
};
|
||||
|
||||
const sortTable = () => {
|
||||
if (sortConfig.prop != '' && sortConfig.order != '') {
|
||||
nextTick(() => {
|
||||
tableRef.value?.sort(sortConfig.prop, sortConfig.order);
|
||||
});
|
||||
}
|
||||
const isWsOpen = () => processSocket && processSocket.readyState === 1;
|
||||
const closeSocket = () => {
|
||||
if (isWsOpen()) processSocket.close();
|
||||
};
|
||||
|
||||
const onOpenProcess = () => {
|
||||
loading.value = true;
|
||||
processSocket.send(JSON.stringify(netSearch));
|
||||
};
|
||||
const onMessage = (message: any) => {
|
||||
oldData.value = JSON.parse(message.data);
|
||||
data.value = filterByStatus();
|
||||
loading.value = false;
|
||||
};
|
||||
const onerror = () => {};
|
||||
const onClose = () => {};
|
||||
|
||||
|
|
@ -181,7 +221,7 @@ const initProcess = () => {
|
|||
processSocket.onmessage = onMessage;
|
||||
processSocket.onerror = onerror;
|
||||
processSocket.onclose = onClose;
|
||||
loading.value = true;
|
||||
|
||||
search();
|
||||
sendMsg();
|
||||
};
|
||||
|
|
@ -207,7 +247,6 @@ const search = () => {
|
|||
onMounted(() => {
|
||||
initProcess();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
closeSocket();
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue