mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-02-03 05:18:52 +08:00
feat: Implement process management and network data handling (#11270)
- Introduced ProcessStore for managing process and network data via WebSocket. - Enhanced TableSearch component to synchronize search parameters with props. - Updated network and process views to utilize ProcessStore for data fetching and state management. - Improved data filtering and sorting in network and process views. - Added WebSocket connection management with polling for real-time updates.
This commit is contained in:
parent
38985671c6
commit
38ab0d9ca0
5 changed files with 441 additions and 174 deletions
|
|
@ -15,7 +15,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
defineOptions({ name: 'TableSearch' });
|
||||
|
||||
const emit = defineEmits(['search', 'update:searchName']);
|
||||
|
|
@ -26,8 +26,22 @@ const props = defineProps({
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
searchName: {
|
||||
type: [String, Number],
|
||||
default: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.searchName,
|
||||
(newVal) => {
|
||||
if (searchInfo.value !== newVal) {
|
||||
searchInfo.value = newVal;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const search = () => {
|
||||
emit('update:searchName', searchInfo.value);
|
||||
emit('search');
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ import GlobalStore from './modules/global';
|
|||
import MenuStore from './modules/menu';
|
||||
import TabsStore from './modules/tabs';
|
||||
import TerminalStore from './modules/terminal';
|
||||
import ProcessStore from './modules/process';
|
||||
|
||||
const pinia = createPinia();
|
||||
pinia.use(piniaPluginPersistedstate);
|
||||
|
||||
export { GlobalStore, MenuStore, TabsStore, TerminalStore };
|
||||
export { GlobalStore, MenuStore, TabsStore, TerminalStore, ProcessStore };
|
||||
|
||||
export default pinia;
|
||||
|
|
|
|||
320
frontend/src/store/modules/process.ts
Normal file
320
frontend/src/store/modules/process.ts
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { ref, reactive } from 'vue';
|
||||
|
||||
export interface PsSearch {
|
||||
type: 'ps';
|
||||
pid: number | undefined;
|
||||
username: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface NetSearch {
|
||||
type: 'net';
|
||||
processID: number | undefined;
|
||||
processName: string;
|
||||
port: number | undefined;
|
||||
}
|
||||
|
||||
export const ProcessStore = defineStore('ProcessStore', () => {
|
||||
let websocket: WebSocket | null = null;
|
||||
let pollingTimer: ReturnType<typeof setInterval> | null = null;
|
||||
let disconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
let connectionRefCount = 0;
|
||||
|
||||
const isConnected = ref(false);
|
||||
const isConnecting = ref(false);
|
||||
|
||||
const psData = ref<any[]>([]);
|
||||
const psLoading = ref(false);
|
||||
const psSearch = reactive<PsSearch>({
|
||||
type: 'ps',
|
||||
pid: undefined,
|
||||
username: '',
|
||||
name: '',
|
||||
});
|
||||
|
||||
const netData = ref<any[]>([]);
|
||||
const netLoading = ref(false);
|
||||
const netSearch = reactive<NetSearch>({
|
||||
type: 'net',
|
||||
processID: undefined,
|
||||
processName: '',
|
||||
port: undefined,
|
||||
});
|
||||
|
||||
let pendingRequestType: 'ps' | 'net' | null = null;
|
||||
|
||||
let queuedRequestType: 'ps' | 'net' | null = null;
|
||||
|
||||
const isPsFetching = ref(false);
|
||||
const isNetFetching = ref(false);
|
||||
|
||||
const activePollingType = ref<'ps' | 'net' | null>(null);
|
||||
|
||||
const isWsOpen = () => {
|
||||
return websocket && websocket.readyState === WebSocket.OPEN;
|
||||
};
|
||||
|
||||
const onOpen = () => {
|
||||
isConnected.value = true;
|
||||
isConnecting.value = false;
|
||||
};
|
||||
|
||||
const doSendMessage = (type: 'ps' | 'net') => {
|
||||
pendingRequestType = type;
|
||||
|
||||
if (type === 'ps') {
|
||||
isPsFetching.value = true;
|
||||
psLoading.value = psData.value.length === 0;
|
||||
|
||||
const searchParams = { ...psSearch };
|
||||
if (typeof searchParams.pid === 'string') {
|
||||
searchParams.pid = Number(searchParams.pid);
|
||||
}
|
||||
websocket!.send(JSON.stringify(searchParams));
|
||||
} else {
|
||||
isNetFetching.value = true;
|
||||
netLoading.value = netData.value.length === 0;
|
||||
|
||||
const searchParams = { ...netSearch };
|
||||
if (typeof searchParams.processID === 'string') {
|
||||
searchParams.processID = Number(searchParams.processID);
|
||||
}
|
||||
if (typeof searchParams.port === 'string') {
|
||||
searchParams.port = Number(searchParams.port);
|
||||
}
|
||||
websocket!.send(JSON.stringify(searchParams));
|
||||
}
|
||||
};
|
||||
|
||||
const onMessage = (event: MessageEvent) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
const responseType = pendingRequestType;
|
||||
|
||||
if (pendingRequestType === 'ps') {
|
||||
isPsFetching.value = false;
|
||||
} else if (pendingRequestType === 'net') {
|
||||
isNetFetching.value = false;
|
||||
}
|
||||
pendingRequestType = null;
|
||||
|
||||
if (responseType === activePollingType.value) {
|
||||
if (responseType === 'ps') {
|
||||
psData.value = data || [];
|
||||
psLoading.value = false;
|
||||
} else if (responseType === 'net') {
|
||||
netData.value = data || [];
|
||||
netLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (queuedRequestType && isWsOpen()) {
|
||||
const typeToSend = queuedRequestType;
|
||||
queuedRequestType = null;
|
||||
doSendMessage(typeToSend);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to parse WebSocket message:', e);
|
||||
}
|
||||
};
|
||||
|
||||
const onError = () => {
|
||||
console.error('WebSocket error');
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
isConnected.value = false;
|
||||
isConnecting.value = false;
|
||||
websocket = null;
|
||||
};
|
||||
|
||||
const initWebSocket = (currentNode: string) => {
|
||||
if (websocket || isConnecting.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
isConnecting.value = true;
|
||||
|
||||
const href = window.location.href;
|
||||
const protocol = href.split('//')[0] === 'http:' ? 'ws' : 'wss';
|
||||
const ipLocal = href.split('//')[1].split('/')[0];
|
||||
|
||||
websocket = new WebSocket(`${protocol}://${ipLocal}/api/v2/process/ws?operateNode=${currentNode}`);
|
||||
websocket.onopen = onOpen;
|
||||
websocket.onmessage = onMessage;
|
||||
websocket.onerror = onError;
|
||||
websocket.onclose = onClose;
|
||||
};
|
||||
|
||||
const closeWebSocket = () => {
|
||||
stopPolling();
|
||||
|
||||
if (websocket) {
|
||||
websocket.close();
|
||||
websocket = null;
|
||||
}
|
||||
|
||||
isConnected.value = false;
|
||||
isConnecting.value = false;
|
||||
};
|
||||
|
||||
const connect = (currentNode: string) => {
|
||||
if (disconnectTimer) {
|
||||
clearTimeout(disconnectTimer);
|
||||
disconnectTimer = null;
|
||||
}
|
||||
|
||||
connectionRefCount++;
|
||||
|
||||
if (!websocket && !isConnecting.value) {
|
||||
initWebSocket(currentNode);
|
||||
}
|
||||
};
|
||||
|
||||
const disconnect = () => {
|
||||
connectionRefCount = Math.max(0, connectionRefCount - 1);
|
||||
|
||||
if (connectionRefCount === 0) {
|
||||
disconnectTimer = setTimeout(() => {
|
||||
if (connectionRefCount === 0) {
|
||||
closeWebSocket();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
|
||||
const sendPsMessage = () => {
|
||||
if (!isWsOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingRequestType !== null) {
|
||||
queuedRequestType = 'ps';
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPsFetching.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
doSendMessage('ps');
|
||||
};
|
||||
|
||||
const sendNetMessage = () => {
|
||||
if (!isWsOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingRequestType !== null) {
|
||||
queuedRequestType = 'net';
|
||||
return;
|
||||
}
|
||||
|
||||
if (isNetFetching.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
doSendMessage('net');
|
||||
};
|
||||
|
||||
const startPolling = (type: 'ps' | 'net', interval = 3000, initialDelay = 0) => {
|
||||
stopPolling();
|
||||
activePollingType.value = type;
|
||||
|
||||
const sendInitial = () => {
|
||||
if (type === 'ps') {
|
||||
sendPsMessage();
|
||||
} else {
|
||||
sendNetMessage();
|
||||
}
|
||||
};
|
||||
|
||||
const scheduleInitialFetch = () => {
|
||||
if (initialDelay > 0) {
|
||||
setTimeout(sendInitial, initialDelay);
|
||||
} else {
|
||||
sendInitial();
|
||||
}
|
||||
};
|
||||
|
||||
if (isWsOpen()) {
|
||||
scheduleInitialFetch();
|
||||
} else {
|
||||
const checkConnection = setInterval(() => {
|
||||
if (isWsOpen()) {
|
||||
clearInterval(checkConnection);
|
||||
scheduleInitialFetch();
|
||||
}
|
||||
}, 100);
|
||||
setTimeout(() => clearInterval(checkConnection), 5000);
|
||||
}
|
||||
|
||||
pollingTimer = setInterval(() => {
|
||||
if (type === 'ps') {
|
||||
sendPsMessage();
|
||||
} else {
|
||||
sendNetMessage();
|
||||
}
|
||||
}, interval);
|
||||
};
|
||||
|
||||
const stopPolling = () => {
|
||||
if (pollingTimer) {
|
||||
clearInterval(pollingTimer);
|
||||
pollingTimer = null;
|
||||
}
|
||||
activePollingType.value = null;
|
||||
};
|
||||
|
||||
const updatePsSearch = (params: Partial<Omit<PsSearch, 'type'>>) => {
|
||||
Object.assign(psSearch, params);
|
||||
};
|
||||
|
||||
const updateNetSearch = (params: Partial<Omit<NetSearch, 'type'>>) => {
|
||||
Object.assign(netSearch, params);
|
||||
};
|
||||
|
||||
const resetPsSearch = () => {
|
||||
psSearch.pid = undefined;
|
||||
psSearch.username = '';
|
||||
psSearch.name = '';
|
||||
};
|
||||
|
||||
const resetNetSearch = () => {
|
||||
netSearch.processID = undefined;
|
||||
netSearch.processName = '';
|
||||
netSearch.port = undefined;
|
||||
};
|
||||
|
||||
return {
|
||||
isConnected,
|
||||
isConnecting,
|
||||
psData,
|
||||
psLoading,
|
||||
psSearch,
|
||||
netData,
|
||||
netLoading,
|
||||
netSearch,
|
||||
isPsFetching,
|
||||
isNetFetching,
|
||||
activePollingType,
|
||||
|
||||
isWsOpen,
|
||||
connect,
|
||||
disconnect,
|
||||
initWebSocket,
|
||||
closeWebSocket,
|
||||
sendPsMessage,
|
||||
sendNetMessage,
|
||||
startPolling,
|
||||
stopPolling,
|
||||
updatePsSearch,
|
||||
updateNetSearch,
|
||||
resetPsSearch,
|
||||
resetNetSearch,
|
||||
};
|
||||
});
|
||||
|
||||
export default ProcessStore;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<FireRouter />
|
||||
<LayoutContent :title="$t('menu.network', 2)" v-loading="loading">
|
||||
<LayoutContent :title="$t('menu.network', 2)" v-loading="processStore.netLoading">
|
||||
<template #rightToolBar>
|
||||
<div class="w-full flex justify-end items-center gap-5">
|
||||
<el-select
|
||||
|
|
@ -25,17 +25,17 @@
|
|||
<TableSearch
|
||||
@search="search()"
|
||||
:placeholder="$t('process.pid')"
|
||||
v-model:searchName="netSearch.processID"
|
||||
v-model:searchName="processStore.netSearch.processID"
|
||||
/>
|
||||
<TableSearch
|
||||
@search="search()"
|
||||
:placeholder="$t('process.processName')"
|
||||
v-model:searchName="netSearch.processName"
|
||||
v-model:searchName="processStore.netSearch.processName"
|
||||
/>
|
||||
<TableSearch
|
||||
@search="search()"
|
||||
:placeholder="$t('commons.table.port')"
|
||||
v-model:searchName="netSearch.port"
|
||||
v-model:searchName="processStore.netSearch.port"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -62,9 +62,10 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import FireRouter from '@/views/host/process/index.vue';
|
||||
import { ref, onMounted, onUnmounted, reactive, watch } from 'vue';
|
||||
import { GlobalStore } from '@/store';
|
||||
import { SortBy, TableV2SortOrder } from 'element-plus';
|
||||
import { ref, onMounted, onUnmounted, watch, h } from 'vue';
|
||||
import { GlobalStore, ProcessStore } from '@/store';
|
||||
import { SortBy, TableV2SortOrder, ElIcon } from 'element-plus';
|
||||
import { Filter } from '@element-plus/icons-vue';
|
||||
import i18n from '@/lang';
|
||||
|
||||
const statusOptions = [
|
||||
|
|
@ -76,24 +77,29 @@ const statusOptions = [
|
|||
];
|
||||
|
||||
const globalStore = GlobalStore();
|
||||
const processStore = ProcessStore();
|
||||
|
||||
const netSearch = reactive({
|
||||
type: 'net',
|
||||
processID: undefined,
|
||||
processName: '',
|
||||
port: undefined,
|
||||
});
|
||||
const quickSearchName = (name: string) => {
|
||||
processStore.netSearch.processID = undefined;
|
||||
processStore.netSearch.processName = name;
|
||||
processStore.netSearch.port = undefined;
|
||||
search();
|
||||
};
|
||||
|
||||
const quickSearchPort = (port: number) => {
|
||||
processStore.netSearch.processID = undefined;
|
||||
processStore.netSearch.processName = '';
|
||||
processStore.netSearch.port = port;
|
||||
search();
|
||||
};
|
||||
|
||||
let processSocket = ref(null) as unknown as WebSocket;
|
||||
const data = ref<any[]>([]);
|
||||
const oldData = ref<any[]>([]);
|
||||
const loading = ref(false);
|
||||
|
||||
const sortState = ref<SortBy>({
|
||||
key: 'PID',
|
||||
order: TableV2SortOrder.ASC,
|
||||
});
|
||||
const filters = ref<string[]>([]);
|
||||
const filters = ref<string[]>(['LISTEN', 'ESTABLISHED']);
|
||||
|
||||
const sortByNum = (a: any, b: any, prop: string): number => {
|
||||
const aVal = parseFloat(a[prop]) || 0;
|
||||
|
|
@ -121,6 +127,23 @@ const columns = ref([
|
|||
title: i18n.global.t('process.processName'),
|
||||
dataKey: 'name',
|
||||
width: 300,
|
||||
cellRenderer: ({ rowData }) => {
|
||||
return h('div', { class: 'flex items-center gap-1' }, [
|
||||
h('span', { class: 'truncate', title: rowData.name }, rowData.name),
|
||||
h(
|
||||
ElIcon,
|
||||
{
|
||||
class: 'cursor-pointer hover:text-primary ml-1 flex-shrink-0',
|
||||
size: 14,
|
||||
onClick: (e: Event) => {
|
||||
e.stopPropagation();
|
||||
quickSearchName(rowData.name);
|
||||
},
|
||||
},
|
||||
() => h(Filter),
|
||||
),
|
||||
]);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'localaddr',
|
||||
|
|
@ -129,7 +152,25 @@ const columns = ref([
|
|||
width: 350,
|
||||
cellRenderer: ({ rowData }) => {
|
||||
const addr = rowData.localaddr;
|
||||
return addr?.ip ? `${addr.ip}${addr.port > 0 ? ':' + addr.port : ''}` : '';
|
||||
const addrStr = addr?.ip ? `${addr.ip}${addr.port > 0 ? ':' + addr.port : ''}` : '';
|
||||
const hasPort = addr?.port > 0;
|
||||
return h('div', { class: 'flex items-center gap-1' }, [
|
||||
h('span', {}, addrStr),
|
||||
hasPort
|
||||
? h(
|
||||
ElIcon,
|
||||
{
|
||||
class: 'cursor-pointer hover:text-primary ml-1',
|
||||
size: 12,
|
||||
onClick: (e: Event) => {
|
||||
e.stopPropagation();
|
||||
quickSearchPort(addr.port);
|
||||
},
|
||||
},
|
||||
() => h(Filter),
|
||||
)
|
||||
: null,
|
||||
]);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -152,9 +193,12 @@ const columns = ref([
|
|||
]);
|
||||
|
||||
watch(
|
||||
[sortState, oldData, filters],
|
||||
[sortState, () => processStore.netData, filters],
|
||||
([newState, newData, newFilters]) => {
|
||||
if (!newData?.length) return;
|
||||
if (!newData?.length) {
|
||||
data.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
let filtered = newData;
|
||||
if (newFilters.length > 0) {
|
||||
|
|
@ -187,70 +231,18 @@ const changeSort = ({ key, order }) => {
|
|||
sortState.value = { key, order };
|
||||
};
|
||||
|
||||
const filterByStatus = () => {
|
||||
if (filters.value.length > 0) {
|
||||
return oldData.value.filter((row) => filters.value.includes(row.status));
|
||||
}
|
||||
return oldData.value;
|
||||
};
|
||||
|
||||
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();
|
||||
if (data.value == null) {
|
||||
data.value = [];
|
||||
}
|
||||
loading.value = false;
|
||||
};
|
||||
const onerror = () => {};
|
||||
const onClose = () => {};
|
||||
|
||||
const initProcess = () => {
|
||||
let href = window.location.href;
|
||||
let protocol = href.split('//')[0] === 'http:' ? 'ws' : 'wss';
|
||||
let ipLocal = href.split('//')[1].split('/')[0];
|
||||
let currentNode = globalStore.currentNode;
|
||||
processSocket = new WebSocket(`${protocol}://${ipLocal}/api/v2/process/ws?operateNode=${currentNode}`);
|
||||
processSocket.onopen = onOpenProcess;
|
||||
processSocket.onmessage = onMessage;
|
||||
processSocket.onerror = onerror;
|
||||
processSocket.onclose = onClose;
|
||||
|
||||
search();
|
||||
sendMsg();
|
||||
};
|
||||
|
||||
const sendMsg = () => {
|
||||
setInterval(() => {
|
||||
search();
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
const search = () => {
|
||||
if (isWsOpen()) {
|
||||
if (typeof netSearch.processID === 'string') {
|
||||
netSearch.processID = Number(netSearch.processID);
|
||||
}
|
||||
if (typeof netSearch.port === 'string') {
|
||||
netSearch.port = Number(netSearch.port);
|
||||
}
|
||||
processSocket.send(JSON.stringify(netSearch));
|
||||
}
|
||||
processStore.sendNetMessage();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initProcess();
|
||||
processStore.connect(globalStore.currentNode);
|
||||
const initialDelay = processStore.netData.length > 0 ? 500 : 0;
|
||||
processStore.startPolling('net', 3000, initialDelay);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
closeSocket();
|
||||
processStore.stopPolling();
|
||||
processStore.disconnect();
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<FireRouter />
|
||||
<LayoutContent :title="$t('menu.process', 2)" v-loading="loading">
|
||||
<LayoutContent :title="$t('menu.process', 2)" v-loading="processStore.psLoading">
|
||||
<template #rightToolBar>
|
||||
<div class="w-full flex justify-end items-center gap-5">
|
||||
<el-select
|
||||
|
|
@ -25,17 +25,17 @@
|
|||
<TableSearch
|
||||
@search="search()"
|
||||
:placeholder="$t('process.pid')"
|
||||
v-model:searchName="processSearch.pid"
|
||||
v-model:searchName="processStore.psSearch.pid"
|
||||
/>
|
||||
<TableSearch
|
||||
@search="search()"
|
||||
:placeholder="$t('commons.table.name')"
|
||||
v-model:searchName="processSearch.name"
|
||||
v-model:searchName="processStore.psSearch.name"
|
||||
/>
|
||||
<TableSearch
|
||||
@search="search()"
|
||||
:placeholder="$t('commons.table.user')"
|
||||
v-model:searchName="processSearch.username"
|
||||
v-model:searchName="processStore.psSearch.username"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -64,13 +64,15 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import FireRouter from '@/views/host/process/index.vue';
|
||||
import { ref, onMounted, onUnmounted, reactive } from 'vue';
|
||||
import { ref, onMounted, onUnmounted, computed, watch, h } from 'vue';
|
||||
import ProcessDetail from './detail/index.vue';
|
||||
import i18n from '@/lang';
|
||||
import { stopProcess } from '@/api/modules/process';
|
||||
import { GlobalStore } from '@/store';
|
||||
import { GlobalStore, ProcessStore } from '@/store';
|
||||
import { SortBy, TableV2SortOrder, ElButton } from 'element-plus';
|
||||
|
||||
const globalStore = GlobalStore();
|
||||
const processStore = ProcessStore();
|
||||
|
||||
const statusOptions = computed(() => [
|
||||
{ text: i18n.global.t('process.running'), value: 'running' },
|
||||
|
|
@ -82,25 +84,15 @@ const statusOptions = computed(() => [
|
|||
{ text: i18n.global.t('process.zombie'), value: 'zombie' },
|
||||
]);
|
||||
|
||||
const processSearch = reactive({
|
||||
type: 'ps',
|
||||
pid: undefined,
|
||||
username: '',
|
||||
name: '',
|
||||
});
|
||||
const opRef = ref();
|
||||
const sortState = ref<SortBy>({
|
||||
key: 'PID',
|
||||
order: TableV2SortOrder.ASC,
|
||||
});
|
||||
|
||||
let processSocket = ref(null) as unknown as WebSocket;
|
||||
const data = ref([]);
|
||||
const loading = ref(false);
|
||||
const oldData = ref([]);
|
||||
const data = ref<any[]>([]);
|
||||
const detailRef = ref();
|
||||
const isGetData = ref(true);
|
||||
const filters = ref([]);
|
||||
const filters = ref<string[]>([]);
|
||||
|
||||
const sortByNum = (a: any, b: any, prop: string): number => {
|
||||
const aVal = parseFloat(a[prop]) || 0;
|
||||
|
|
@ -217,23 +209,32 @@ const columns = ref([
|
|||
]);
|
||||
|
||||
watch(
|
||||
[sortState, oldData],
|
||||
([newState, newData]) => {
|
||||
if (!newData?.length) return;
|
||||
[sortState, () => processStore.psData, filters],
|
||||
([newState, newData, newFilters]) => {
|
||||
if (!newData?.length) {
|
||||
data.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
let filtered = newData;
|
||||
if (newFilters.length > 0) {
|
||||
filtered = filtered.filter((re: any) => newFilters.includes(re.status));
|
||||
}
|
||||
|
||||
const { key, order } = newState ?? {};
|
||||
if (!key || !order) {
|
||||
data.value = filterByStatus();
|
||||
data.value = filtered;
|
||||
return;
|
||||
}
|
||||
|
||||
const currCol = columns.value.find((c) => c.key === key);
|
||||
if (!currCol) return;
|
||||
if (!currCol) {
|
||||
data.value = filtered;
|
||||
return;
|
||||
}
|
||||
|
||||
const currSortMethod = currCol.sortMethod ?? sortByNum;
|
||||
const filteredData = filterByStatus();
|
||||
|
||||
data.value = filteredData.slice(0).sort((a, b) => {
|
||||
data.value = filtered.slice(0).sort((a, b) => {
|
||||
const res = (currSortMethod as any)(a, b, currCol.dataKey);
|
||||
return order === TableV2SortOrder.ASC ? res : 0 - res;
|
||||
});
|
||||
|
|
@ -250,72 +251,8 @@ const changeSort = ({ key, order }) => {
|
|||
sortState.value = { key, order };
|
||||
};
|
||||
|
||||
const isWsOpen = () => {
|
||||
const readyState = processSocket && processSocket.readyState;
|
||||
return readyState === 1;
|
||||
};
|
||||
const closeSocket = () => {
|
||||
if (isWsOpen()) {
|
||||
processSocket && processSocket.close();
|
||||
}
|
||||
};
|
||||
|
||||
const onOpenProcess = () => {
|
||||
loading.value = true;
|
||||
isGetData.value = true;
|
||||
processSocket.send(JSON.stringify(processSearch));
|
||||
};
|
||||
const onMessage = (message: any) => {
|
||||
isGetData.value = false;
|
||||
oldData.value = JSON.parse(message.data);
|
||||
data.value = filterByStatus();
|
||||
if (data.value == null) {
|
||||
data.value = [];
|
||||
}
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
const filterByStatus = () => {
|
||||
if (filters.value.length > 0) {
|
||||
const newData = oldData.value.filter((re: any) => {
|
||||
return (filters.value as string[]).indexOf(re.status) > -1;
|
||||
});
|
||||
return newData;
|
||||
} else {
|
||||
return oldData.value;
|
||||
}
|
||||
};
|
||||
|
||||
const onerror = () => {};
|
||||
const onClose = () => {};
|
||||
|
||||
const initProcess = () => {
|
||||
let href = window.location.href;
|
||||
let protocol = href.split('//')[0] === 'http:' ? 'ws' : 'wss';
|
||||
let ipLocal = href.split('//')[1].split('/')[0];
|
||||
let currentNode = globalStore.currentNode;
|
||||
processSocket = new WebSocket(`${protocol}://${ipLocal}/api/v2/process/ws?operateNode=${currentNode}`);
|
||||
processSocket.onopen = onOpenProcess;
|
||||
processSocket.onmessage = onMessage;
|
||||
processSocket.onerror = onerror;
|
||||
processSocket.onclose = onClose;
|
||||
sendMsg();
|
||||
};
|
||||
|
||||
const sendMsg = () => {
|
||||
setInterval(() => {
|
||||
search();
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
const search = () => {
|
||||
if (isWsOpen() && !isGetData.value) {
|
||||
isGetData.value = true;
|
||||
if (typeof processSearch.pid === 'string') {
|
||||
processSearch.pid = Number(processSearch.pid);
|
||||
}
|
||||
processSocket.send(JSON.stringify(processSearch));
|
||||
}
|
||||
processStore.sendPsMessage();
|
||||
};
|
||||
|
||||
const stop = async (row: any) => {
|
||||
|
|
@ -333,10 +270,13 @@ const stop = async (row: any) => {
|
|||
};
|
||||
|
||||
onMounted(() => {
|
||||
initProcess();
|
||||
processStore.connect(globalStore.currentNode);
|
||||
const initialDelay = processStore.psData.length > 0 ? 500 : 0;
|
||||
processStore.startPolling('ps', 3000, initialDelay);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
closeSocket();
|
||||
processStore.stopPolling();
|
||||
processStore.disconnect();
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue