feat(1.5.0)-beta1): 完成日志接入ws

This commit is contained in:
opensnail 2025-04-01 22:10:01 +08:00
parent 447cd61215
commit 2408463d7c
3 changed files with 8 additions and 180 deletions

2
.env
View File

@ -6,7 +6,7 @@ VITE_APP_TITLE=Snail Job
VITE_APP_DESC=A flexible, reliable, and fast platform for distributed task retry and distributed task scheduling. VITE_APP_DESC=A flexible, reliable, and fast platform for distributed task retry and distributed task scheduling.
VITE_APP_VERSION=1.4.0 VITE_APP_VERSION=1.5.0-beta1
VITE_APP_DEFAULT_TOKEN=SJ_Wyz3dmsdbDOkDujOTSSoBjGQP1BMsVnj VITE_APP_DEFAULT_TOKEN=SJ_Wyz3dmsdbDOkDujOTSSoBjGQP1BMsVnj

View File

@ -1,11 +1,9 @@
<script setup lang="tsx"> <script setup lang="tsx">
import { import {
NButton,
NCard, NCard,
NCollapse, NCollapse,
NCollapseItem, NCollapseItem,
NDivider, NDivider,
NDropdown,
NEmpty, NEmpty,
NScrollbar, NScrollbar,
NSpin, NSpin,
@ -42,14 +40,11 @@ const visible = defineModel<boolean>('show', {
default: false default: false
}); });
const isAutoScroll = ref(false);
const isFullscreen = ref(true); const isFullscreen = ref(true);
const expandedNames = ref<string[]>([]); const expandedNames = ref<string[]>([]);
const virtualListInst = ref<VirtualListInst>(); const virtualListInst = ref<VirtualListInst>();
const syncTime = ref(1);
const logList = ref<Api.JobLog.JobMessage[]>([]); const logList = ref<Api.JobLog.JobMessage[]>([]);
const interval = ref<NodeJS.Timeout>(); const interval = ref<NodeJS.Timeout>();
const controller = new AbortController();
const finished = ref<boolean>(true); const finished = ref<boolean>(true);
const pauseLog = () => { const pauseLog = () => {
@ -59,13 +54,18 @@ const pauseLog = () => {
}; };
const stopLog = () => { const stopLog = () => {
if (!finished.value) controller.abort();
pauseLog(); pauseLog();
logList.value = []; logList.value = [];
}; };
async function getLogList() { async function getLogList() {
if (terminalSocket.value) {
terminalSocket.value.close();
}
if (props.type === 'job') { if (props.type === 'job') {
const newSocket = initWebSocket('JOB_LOG_SCENE');
terminalSocket.value = newSocket ?? undefined;
terminalSocket!.value!.onopen = () => { terminalSocket!.value!.onopen = () => {
const taskData = props.taskData! as Api.Job.JobTask; const taskData = props.taskData! as Api.Job.JobTask;
const msg = { const msg = {
@ -86,74 +86,9 @@ async function getLogList() {
virtualListInst.value?.scrollTo({ position: 'bottom', debounce: false }); virtualListInst.value?.scrollTo({ position: 'bottom', debounce: false });
}); });
} }
// logList.value
// .sort((a, b) => Number.parseInt(a.time_stamp, 10) - Number.parseInt(b.time_stamp, 10))
// .forEach((item, index) => (item.index = index));
// logData.value.push(`${event.data}\n`);
}; };
// if (props.type === 'retry') {
// const taskData = props.taskData! as Api.RetryTask.RetryTask;
// const { data, error } = await fetchRetryLogList({
// groupName: taskData.groupName,
// retryTaskId: taskData.id!,
// startId,
// fromIndex,
// size: 50
// });
// logData = data;
// logError = error;
// }
// if (!logError && logData) {
// //
// finished.value = syncTime.value === 0;
// logList.value.push(logData);
// logList.value
// .sort((a, b) => Number.parseInt(a.time_stamp, 10) - Number.parseInt(b.time_stamp, 10))
// .forEach((item, index) => (item.index = index));
// nextTick(() => {
// if (isAutoScroll.value) virtualListInst.value?.scrollTo({ position: 'bottom', debounce: true });
// });
// if (!finished.value && syncTime.value !== 0) {
// interval.value = setTimeout(getLogList, syncTime.value * 1000);
// }
//
// if (finished.value && syncTime.value !== 0) {
// setTimeout(() => {
// watchFinished();
// }, 5 * 1000);
// }
// } else if (logError?.code !== 'ERR_CANCELED') {
// stopLog();
// }
} }
// async function watchFinished() {
// clearTimeout(interval.value);
// if (props.type === 'job' && syncTime.value !== 0) {
// const taskData = props.taskData! as Api.Job.JobTask;
// const { data, error } = await fetchJobLogList(
// {
// taskBatchId: taskData.taskBatchId,
// jobId: taskData.jobId,
// taskId: taskData.id,
// startId,
// fromIndex,
// size: 50
// },
// controller
// );
// if (!error && data) {
// if (data.finished) {
// interval.value = setTimeout(watchFinished, 5 * 1000);
// return;
// }
// await getLogList();
// }
// }
// }
onBeforeUnmount(() => { onBeforeUnmount(() => {
stopLog(); stopLog();
}); });
@ -164,11 +99,6 @@ watch(
if (val) { if (val) {
if (props.modelValue) { if (props.modelValue) {
logList.value = [...props.modelValue]; logList.value = [...props.modelValue];
if (terminalSocket.value) {
terminalSocket.value.close();
}
const newSocket = initWebSocket('JOB_LOG_SCENE');
terminalSocket.value = newSocket ?? undefined;
} }
} else { } else {
terminalSocket?.value?.close(); terminalSocket?.value?.close();
@ -176,7 +106,6 @@ watch(
if ((val || !props.drawer) && props.type && props.taskData) { if ((val || !props.drawer) && props.type && props.taskData) {
finished.value = false; finished.value = false;
// controller = new AbortController();
await getLogList(); await getLogList();
} }
@ -223,57 +152,6 @@ function openNewTab() {
window.open(url.href); window.open(url.href);
} }
const handleSyncSelect = async (time: number) => {
if (time === -1) {
if (finished.value) {
finished.value = false;
await getLogList();
}
return;
}
syncTime.value = time;
if (time === 0) {
pauseLog();
return;
}
finished.value = false;
await getLogList();
};
const syncOptions = ref([
{
label: 'Off',
key: 0
},
{
label: 'Auto(1s)',
key: 1
},
{
label: '5s',
key: 5
},
{
label: '10s',
key: 10
},
{
label: '30s',
key: 30
},
{
label: '1m',
key: 60
},
{
label: '5m',
key: 300
}
]);
const SnailLogComponent = defineComponent({ const SnailLogComponent = defineComponent({
setup() { setup() {
if (finished.value && logList.value.length === 0) { if (finished.value && logList.value.length === 0) {
@ -336,7 +214,6 @@ const SnailLogComponent = defineComponent({
ref={virtualListInst} ref={virtualListInst}
class="virtual-list" class="virtual-list"
itemSize={85} itemSize={85}
itemResizable
paddingBottom={16} paddingBottom={16}
items={logList.value} items={logList.value}
scrollbarProps={{ xScrollable: true }} scrollbarProps={{ xScrollable: true }}
@ -397,32 +274,8 @@ const SnailLogComponent = defineComponent({
日志正在加载 日志正在加载
</NTooltip> </NTooltip>
<span class="ml-6px">{{ title }}</span> <span class="ml-6px">{{ title }}</span>
<NDropdown trigger="hover" :options="syncOptions" width="trigger" @select="handleSyncSelect">
<NTooltip placement="right">
<template #trigger>
<NButton dashed class="ml-16px w-136px" @click="handleSyncSelect(-1)">
<template #icon>
<div class="flex-center gap-8px">
<icon-solar:refresh-outline class="text-18px" />
{{ syncOptions.filter(item => item.key === syncTime)[0].label }}
<SvgIcon icon="material-symbols:expand-more-rounded" />
</div>
</template>
</NButton>
</template>
自动刷新频率
</NTooltip>
</NDropdown>
</div> </div>
<div class="flex-center"> <div class="flex-center">
<ButtonIcon
size="tiny"
:tooltip-content="isAutoScroll ? '关闭自动滚动' : '开启自动滚动'"
@click="() => (isAutoScroll = !isAutoScroll)"
>
<icon-streamline:synchronize-disable v-if="isAutoScroll" />
<icon-streamline:interface-arrows-vertical-scroll-point-move-scroll-vertical v-else />
</ButtonIcon>
<ButtonIcon <ButtonIcon
size="tiny" size="tiny"
icon="hugeicons:share-01" icon="hugeicons:share-01"
@ -451,31 +304,6 @@ const SnailLogComponent = defineComponent({
<NCard v-else :bordered="false" :title="title" size="small" class="h-full sm:flex-1-hidden card-wrapper"> <NCard v-else :bordered="false" :title="title" size="small" class="h-full sm:flex-1-hidden card-wrapper">
<template #header-extra> <template #header-extra>
<div class="flex items-center"> <div class="flex items-center">
<NDropdown trigger="hover" :options="syncOptions" width="trigger" @select="handleSyncSelect">
<NTooltip placement="right">
<template #trigger>
<NButton dashed class="mx-12px w-136px" @click="handleSyncSelect(-1)">
<template #icon>
<div class="flex-center gap-8px">
<icon-solar:refresh-outline class="text-18px" />
{{ syncOptions.filter(item => item.key === syncTime)[0].label }}
<SvgIcon icon="material-symbols:expand-more-rounded" />
</div>
</template>
</NButton>
</template>
自动刷新频率
</NTooltip>
</NDropdown>
<ButtonIcon
size="tiny"
class="mr-12px"
:tooltip-content="isAutoScroll ? '关闭自动滚动' : '开启自动滚动'"
@click="() => (isAutoScroll = !isAutoScroll)"
>
<icon-streamline:synchronize-disable v-if="isAutoScroll" />
<icon-streamline:interface-arrows-vertical-scroll-point-move-scroll-vertical v-else />
</ButtonIcon>
<NTooltip v-if="finished"> <NTooltip v-if="finished">
<template #trigger> <template #trigger>
<icon-material-symbols:check-circle class="text-20px color-success" /> <icon-material-symbols:check-circle class="text-20px color-success" />

View File

@ -42,7 +42,7 @@ const title = computed(() => {
<template> <template>
<div class="h-full"> <div class="h-full">
<LogDrawer :drawer="false" :title="title" :type="type" :task-data="taskData" /> <LogDrawerWs :drawer="false" :title="title" :type="type" :task-data="taskData" />
</div> </div>
</template> </template>