feat(sj_map_reduce): 日志新增刷新频率支持

This commit is contained in:
xlsea 2024-06-26 15:27:43 +08:00
parent dd00ff5be0
commit c366c22881
9 changed files with 383 additions and 396 deletions

View File

@ -0,0 +1,10 @@
{
"prefix": "nonicons",
"lastModified": 1702313196,
"aliases": {},
"icons": {
"loading-16": {
"body": "<path fill=\"currentColor\" fill-rule=\"evenodd\" d=\"M7.706.29c-.222.072-.35.2-.412.409c-.035.117-.041.389-.041 1.809c0 1.881-.002 1.857.19 2.049c.257.256.857.256 1.114 0c.192-.192.19-.168.19-2.049c0-1.82-.003-1.852-.151-2.028C8.472.333 8.339.284 8.04.276a1.705 1.705 0 0 0-.334.014M2.753 2.266c-.158.072-.391.3-.472.462a.605.605 0 0 0-.012.525c.074.165 2.398 2.497 2.581 2.59c.259.133.525.068.793-.194c.264-.258.334-.538.2-.799c-.093-.183-2.425-2.507-2.59-2.581a.638.638 0 0 0-.5-.003m10.1.016c-.123.057-.333.254-1.335 1.259c-.921.923-1.202 1.221-1.247 1.319a.617.617 0 0 0 .001.518c.07.15.3.386.455.467c.157.082.39.081.553-.002c.167-.086 2.477-2.396 2.563-2.563a.648.648 0 0 0 .003-.551a1.26 1.26 0 0 0-.454-.446a.569.569 0 0 0-.539-.001M.699 7.292c-.295.093-.441.328-.441.707c.001.387.145.619.44.707c.118.035.381.041 1.81.041c1.489 0 1.688-.005 1.81-.045a.602.602 0 0 0 .384-.384c.086-.265.043-.641-.094-.827a.723.723 0 0 0-.191-.148l-.137-.076l-1.733-.006c-1.395-.004-1.756.002-1.848.031m11.046-.014a.757.757 0 0 0-.353.214c-.137.185-.18.561-.094.826c.058.18.204.326.384.384c.122.04.321.045 1.81.045c1.429 0 1.692-.006 1.81-.041c.295-.088.439-.32.44-.707c0-.385-.147-.616-.452-.708c-.103-.031-.426-.037-1.794-.035c-.918.002-1.706.012-1.751.022m-6.892 3.004c-.123.057-.333.254-1.335 1.259c-.921.923-1.202 1.221-1.247 1.319a.617.617 0 0 0 .001.518c.07.15.3.386.455.467c.157.082.39.081.553-.002c.167-.086 2.477-2.396 2.563-2.563a.648.648 0 0 0 .003-.551a1.26 1.26 0 0 0-.454-.446a.569.569 0 0 0-.539-.001m5.9-.016c-.158.072-.391.3-.472.462a.605.605 0 0 0-.012.525c.074.165 2.398 2.497 2.581 2.59c.259.133.525.068.793-.194c.264-.258.334-.538.2-.799c-.093-.183-2.425-2.507-2.59-2.581a.638.638 0 0 0-.5-.003m-3.008 1.011a.768.768 0 0 0-.353.215c-.138.186-.139.199-.139 1.997c0 1.432.006 1.695.041 1.813c.088.295.321.439.706.439c.385 0 .618-.144.706-.439c.062-.212.061-3.427-.002-3.612a.528.528 0 0 0-.284-.344c-.11-.06-.174-.075-.363-.082a1.537 1.537 0 0 0-.312.013\"/>"
}
}
}

12
public/iconify/solar.json Normal file
View File

@ -0,0 +1,12 @@
{
"prefix": "solar",
"lastModified": 1702314058,
"aliases": {},
"width": 24,
"height": 24,
"icons": {
"refresh-outline": {
"body": "<path fill=\"currentColor\" fill-rule=\"evenodd\" d=\"M2.93 11.2c.072-4.96 4.146-8.95 9.149-8.95a9.158 9.158 0 0 1 7.814 4.357a.75.75 0 0 1-1.277.786a7.658 7.658 0 0 0-6.537-3.643c-4.185 0-7.575 3.328-7.648 7.448l.4-.397a.75.75 0 0 1 1.057 1.065l-1.68 1.666a.75.75 0 0 1-1.056 0l-1.68-1.666A.75.75 0 1 1 2.528 10.8zm16.856-.733a.75.75 0 0 1 1.055 0l1.686 1.666a.75.75 0 1 1-1.054 1.067l-.41-.405c-.07 4.965-4.161 8.955-9.18 8.955a9.197 9.197 0 0 1-7.842-4.356a.75.75 0 1 1 1.277-.788a7.697 7.697 0 0 0 6.565 3.644c4.206 0 7.61-3.333 7.68-7.453l-.408.403a.75.75 0 1 1-1.055-1.067z\" clip-rule=\"evenodd\"/>"
}
}
}

View File

@ -1,9 +1,9 @@
<script setup lang="tsx">
import { NCollapse, NCollapseItem } from 'naive-ui';
import { defineComponent, watch } from 'vue';
import { NButton, NCard, NCollapse, NCollapseItem, NDivider, NDropdown, NEmpty, NSpin, NVirtualList } from 'naive-ui';
import { defineComponent, onBeforeUnmount, ref, watch } from 'vue';
import { useRouter } from 'vue-router';
import { $t } from '@/locales';
import { useLogStore } from '@/store/modules/log';
import { fetchJobLogList, fetchRetryLogList } from '@/service/api/log';
import ButtonIcon from '@/components/custom/button-icon.vue';
defineOptions({
name: 'LogDrawer'
@ -13,13 +13,17 @@ interface Props {
title?: string;
show?: boolean;
drawer?: boolean;
type?: 'job' | 'retry';
taskData?: Api.Job.JobTask | Api.RetryLog.RetryLog | Api.RetryTask.RetryTask;
modelValue?: Api.JobLog.JobMessage[];
}
const props = withDefaults(defineProps<Props>(), {
title: $t('page.log.title'),
title: undefined,
show: false,
drawer: true,
type: 'job',
taskData: undefined,
modelValue: () => []
});
@ -32,48 +36,95 @@ const visible = defineModel<boolean>('visible', {
default: true
});
const ThrowableComponent = defineComponent({
props: {
throwable: {
type: String,
default: ''
}
},
setup(thProps) {
return () => {
if (!thProps.throwable) {
return <></>;
}
const firstLine = thProps.throwable.match(/^.+/m);
if (!firstLine) {
return <></>;
}
const restOfText = thProps.throwable.replace(/^.+(\n|$)/m, '');
return (
<NCollapse>
<NCollapseItem title={firstLine[0]} name="1">
{`${restOfText}`}
</NCollapseItem>
</NCollapse>
);
};
const syncTime = ref(1);
const logList = ref<Api.JobLog.JobMessage[]>([]);
const interval = ref<NodeJS.Timeout>();
const controller = new AbortController();
const finished = ref<boolean>(false);
let startId = '0';
let fromIndex: number = 0;
const stopLog = () => {
finished.value = true;
controller.abort();
clearTimeout(interval.value);
interval.value = undefined;
};
async function getLogList() {
let logData = null;
let logError;
if (props.type === 'job') {
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
});
logData = data;
logError = error;
}
if (props.type === 'retry') {
const taskData = props.taskData! as Api.RetryLog.RetryLog | Api.RetryTask.RetryTask;
const { data, error } = await fetchRetryLogList({
groupName: taskData.groupName,
uniqueId: taskData.uniqueId!,
startId,
fromIndex,
size: 50
});
logData = data;
logError = error;
}
if (!logError && logData) {
finished.value = logData.finished;
startId = logData.nextStartId;
fromIndex = logData.fromIndex;
if (logData.message) {
logList.value.push(...logData.message);
logList.value.sort((a, b) => Number.parseInt(a.time_stamp, 10) - Number.parseInt(b.time_stamp, 10));
}
if (!finished.value) {
clearTimeout(interval.value);
interval.value = setTimeout(getLogList, syncTime.value * 1000);
}
} else {
stopLog();
}
}
onBeforeUnmount(() => {
stopLog();
});
watch(
() => props.show,
val => {
async val => {
visible.value = val;
if (val) {
if (props.modelValue) {
logList.value = props.modelValue;
}
}
if ((val || !props.drawer) && props.type && props.taskData) {
finished.value = false;
await getLogList();
}
if (!val && props.drawer) {
stopLog();
}
},
{ immediate: true }
);
const store = useLogStore();
const onUpdateShow = (value: boolean) => {
if (!value) {
store.clear();
}
emit('update:show', value);
};
@ -92,10 +143,119 @@ function timestampToDate(timestamp: string): string {
const router = useRouter();
function openNewTab() {
const url = router.resolve('/log');
store.setData(props.modelValue);
let query;
if (props.type === 'job') {
query = {
type: props.type,
taskBatchId: (props.taskData as Api.Job.JobTask).taskBatchId,
jobId: (props.taskData as Api.Job.JobTask).jobId,
taskId: (props.taskData as Api.Job.JobTask).id
};
}
if (props.type === 'retry') {
query = {
type: props.type,
groupName: (props.taskData as Api.RetryLog.RetryLog | Api.RetryTask.RetryTask).groupName,
uniqueId: (props.taskData as Api.RetryLog.RetryLog | Api.RetryTask.RetryTask).uniqueId
};
}
const url = router.resolve({ path: '/log', query });
window.open(url.href);
}
const handleSyncSelect = async (time: number) => {
if (time === -1) {
finished.value = false;
await getLogList();
}
if (time === 0) {
stopLog();
return;
}
syncTime.value = time;
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({
setup() {
if (finished.value && logList.value.length === 0) {
return () => <NEmpty class="h-full flex-center" size="huge" />;
}
const throwableComponent = (throwable: string) => {
if (!throwable) {
return <></>;
}
const firstLine = throwable.match(/^.+/m);
if (!firstLine) {
return <></>;
}
const restOfText = throwable.replace(/^.+(\n|$)/m, '');
return (
<NCollapse>
<NCollapseItem title={firstLine[0]} name="1">
{`${restOfText}`}
</NCollapseItem>
</NCollapse>
);
};
return () => (
<code class="snail-log">
<NVirtualList class="virtual-list" itemSize={65} items={logList.value}>
{{
default: ({ item: message }: { item: Api.JobLog.JobMessage }) => (
<pre>
<span class="log-hljs-time inline-block">{timestampToDate(message.time_stamp)}</span>
<span class={`log-hljs-level-${message.level} ml-12px mr-12px inline-block`}>{`${message.level}`}</span>
<span class="log-hljs-thread mr-12px inline-block">{`[${message.host}:${message.port}]`}</span>
<span class="log-hljs-thread mr-12px inline-block">{`[${message.thread}]`}</span>
<span class="log-hljs-location">{`${message.location}: \n`}</span> -
<span class="pl-6px">{`${message.message}`}</span>
{throwableComponent(message.throwable)}
<NDivider />
</pre>
)
}}
</NVirtualList>
</code>
);
}
});
</script>
<template>
@ -103,63 +263,132 @@ function openNewTab() {
<NDrawerContent closable>
<template #header>
<div class="flex-center">
<span>{{ title }}</span>
<ButtonIcon icon="hugeicons:share-01" tooltip-content="在新标签页打开" class="ml-3px" @click="openNewTab" />
<NTooltip v-if="finished">
<template #trigger>
<icon-material-symbols:check-circle class="text-20px color-success" />
</template>
日志加载完成
</NTooltip>
<NTooltip v-else>
<template #trigger>
<NSpin size="small">
<template #icon>
<icon-nonicons:loading-16 />
</template>
</NSpin>
</template>
日志正在加载
</NTooltip>
<span class="ml-6px">{{ title }}</span>
<ButtonIcon icon="hugeicons:share-01" tooltip-content="在新标签页打开" class="ml-6px" @click="openNewTab" />
<NDropdown trigger="hover" :options="syncOptions" @select="handleSyncSelect">
<NTooltip>
<template #trigger>
<NButton quaternary class="ml-3px" @click="handleSyncSelect(-1)">
<template #icon>
<div class="flex-center gap-8px">
<icon-solar:refresh-outline class="text-18px" />
</div>
</template>
</NButton>
</template>
自动刷新频率
</NTooltip>
</NDropdown>
</div>
</template>
<div class="snail-log bg-#fafafc p-16px dark:bg-#000">
<div class="snail-log-scrollbar">
<code>
<NVirtualList class="virtual-list" :item-size="42" :items="modelValue">
<template #default="{ item: message, index }">
<pre><NDivider v-if="index !== 0" /><span class="log-hljs-time inline-block">{{timestampToDate(message.time_stamp)}}</span><span :class="`log-hljs-level-${message.level}`" class="ml-12px mr-12px inline-block">{{`${message.level}`}}</span><span class="log-hljs-thread mr-12px inline-block">{{ `[${message.host}:${message.port}]` }}</span><span class="log-hljs-thread mr-12px inline-block">{{`[${message.thread}]`}}</span><span class="log-hljs-location">{{`${message.location}: \n`}}</span> -<span class="pl-6px">{{`${message.message}`}}</span><ThrowableComponent :throwable="message.throwable" /></pre>
</template>
</NVirtualList>
</code>
</div>
</div>
<SnailLogComponent />
</NDrawerContent>
</NDrawer>
<div v-if="!drawer" class="snail-log">
<div class="snail-log-scrollbar">
<code>
<NVirtualList class="virtual-list" :item-size="42" :items="modelValue">
<template #default="{ item: message, index }">
<pre><NDivider v-if="index !== 0" /><span class="log-hljs-time inline-block">{{timestampToDate(message.time_stamp)}}</span><span :class="`log-hljs-level-${message.level}`" class="ml-12px mr-12px inline-block">{{`${message.level}`}}</span><span class="log-hljs-thread mr-12px inline-block">{{ `[${message.host}:${message.port}]` }}</span><span class="log-hljs-thread mr-12px inline-block">{{`[${message.thread}]`}}</span><span class="log-hljs-location">{{`${message.location}: \n`}}</span> -<span class="pl-6px">{{`${message.message}`}}</span><ThrowableComponent :throwable="message.throwable" /></pre>
<NCard v-else :bordered="false" :title="title" size="small" class="h-full sm:flex-1-hidden card-wrapper">
<template #header-extra>
<div class="flex items-center">
<NDropdown trigger="hover" :options="syncOptions" @select="handleSyncSelect">
<NTooltip>
<template #trigger>
<NButton quaternary class="ml-3px" @click="handleSyncSelect(-1)">
<template #icon>
<div class="flex-center gap-8px">
<icon-solar:refresh-outline class="text-18px" />
</div>
</template>
</NButton>
</template>
自动刷新频率
</NTooltip>
</NDropdown>
<NTooltip v-if="finished">
<template #trigger>
<icon-material-symbols:check-circle class="text-20px color-success" />
</template>
</NVirtualList>
</code>
</div>
</div>
日志加载完成
</NTooltip>
<NTooltip v-else>
<template #trigger>
<NSpin size="small">
<template #icon>
<icon-nonicons:loading-16 />
</template>
</NSpin>
</template>
日志正在加载
</NTooltip>
</div>
</template>
<SnailLogComponent />
</NCard>
</template>
<style scoped lang="scss">
<style lang="scss">
.snail-log {
width: 100%;
height: 100%;
padding: 0;
.virtual-list {
max-height: calc(100vh - 101px);
}
&-scrollbar {
padding: 0;
height: 100%;
width: 100%;
overflow: auto;
@include scrollbar();
.n-divider:not(.n-divider--vertical) {
margin-top: 6px;
margin-bottom: 6px;
}
.n-divider:not(.n-divider--vertical) {
margin-top: 6px;
margin-bottom: 6px;
pre {
white-space: pre-wrap;
word-break: break-word;
margin: 0;
font-size: 16px;
color: #333639;
}
.log-hljs {
&-time {
color: #2db7f5;
}
pre {
white-space: pre-wrap;
word-break: break-word;
margin: 0;
font-size: 16px;
color: #333639;
&-level {
&-DEBUG {
color: #2647cc;
}
&-INFO {
color: #5c962c;
}
&-WARN {
color: #da9816;
}
&-ERROR {
color: #dc3f41;
}
}
&-thread {
color: #00a3a3;
}
&-location {
color: #a771bf;
}
}
}
@ -173,36 +402,13 @@ function openNewTab() {
}
}
}
</style>
.log-hljs {
&-time {
color: #2db7f5;
}
&-level {
&-DEBUG {
color: #2647cc;
}
&-INFO {
color: #5c962c;
}
&-WARN {
color: #da9816;
}
&-ERROR {
color: #dc3f41;
}
}
&-thread {
color: #00a3a3;
}
&-location {
color: #a771bf;
}
<style scoped>
:deep(.n-spin) {
height: 18px !important;
width: 18px !important;
font-size: 18px !important;
margin-right: 6px;
}
</style>

View File

@ -1,7 +1,5 @@
<script setup lang="tsx">
import { onBeforeUnmount, ref, watch } from 'vue';
import { $t } from '@/locales';
import { fetchJobLogList } from '@/service/api/log';
defineOptions({
name: 'FlowLogDrawer'
@ -12,138 +10,17 @@ interface Props {
taskData: Workflow.JobTaskType;
}
const props = withDefaults(defineProps<Props>(), {
title: $t('workflow.node.log.title'),
modelValue: () => []
withDefaults(defineProps<Props>(), {
title: $t('workflow.node.log.title')
});
const show = defineModel<boolean>('show', {
default: true
});
watch(
() => show.value,
val => {
if (val) {
getLogList();
}
},
{ immediate: true }
);
const logList = ref<Api.JobLog.JobMessage[]>([]);
const interval = ref<NodeJS.Timeout>();
const controller = new AbortController();
const finished = ref<boolean>(false);
let startId = '0';
let fromIndex: number = 0;
async function getLogList() {
const { data: logData, error } = await fetchJobLogList({
taskBatchId: props.taskData.taskBatchId!,
jobId: props.taskData.jobId!,
taskId: props.taskData.id!,
startId,
fromIndex,
size: 50
});
if (!error) {
finished.value = logData.finished;
startId = logData.nextStartId;
fromIndex = logData.fromIndex;
if (logData.message) {
logList.value.push(...logData.message);
logList.value.sort((a, b) => Number.parseInt(a.time_stamp, 10) - Number.parseInt(b.time_stamp, 10));
}
if (!finished.value) {
clearTimeout(interval.value);
interval.value = setTimeout(getLogList, 1000);
}
}
}
const stopLog = () => {
finished.value = true;
controller.abort();
clearTimeout(interval.value);
interval.value = undefined;
};
onBeforeUnmount(() => {
stopLog();
});
</script>
<template>
<LogDrawer v-model="logList" v-model:show="show" :title="title" />
<LogDrawer v-model:show="show" :title="title" :task-data="taskData as any" />
</template>
<style scoped lang="scss">
.snail-log {
width: 100%;
height: 100%;
&-scrollbar {
padding: 0;
height: 100%;
width: 100%;
overflow: auto;
@include scrollbar();
.n-divider:not(.n-divider--vertical) {
margin-top: 6px;
margin-bottom: 6px;
}
pre {
white-space: pre-wrap;
word-break: break-word;
margin: 0;
font-size: 16px;
color: #333639;
}
}
}
.dark {
.snail-log {
background-color: #1e1f22;
pre {
color: #ffffffe6;
}
}
}
.log-hljs {
&-time {
color: #2db7f5;
}
&-level {
&-DEBUG {
color: #2647cc;
}
&-INFO {
color: #5c962c;
}
&-WARN {
color: #da9816;
}
&-ERROR {
color: #dc3f41;
}
}
&-thread {
color: #00a3a3;
}
&-location {
color: #a771bf;
}
}
</style>
<style scoped></style>

View File

@ -1,35 +0,0 @@
import { defineStore } from 'pinia';
import { ref } from 'vue';
import { localStg } from '@/utils/storage';
export const useLogStore = defineStore('log', () => {
const taskName = ref<string>();
const taskBatchId = ref<string>();
const data = ref<Api.JobLog.JobMessage[]>([]);
function setTaskInfo(name: string, id: string) {
taskName.value = name;
taskBatchId.value = id;
}
function setData(value: Api.JobLog.JobMessage[]) {
data.value = value;
localStg.set('log', { taskName: taskName.value!, taskBatchId: taskBatchId.value!, data: data.value });
}
function clear() {
taskName.value = undefined;
taskBatchId.value = undefined;
data.value = [];
localStg.remove('log');
}
return {
taskName,
taskBatchId,
data,
clear,
setTaskInfo,
setData
};
});

View File

@ -1,38 +1,53 @@
<script setup lang="ts">
import { onUnmounted, ref } from 'vue';
import { useLogStore } from '@/store/modules/log';
import { useRoute } from 'vue-router';
import { computed, ref } from 'vue';
import { useRouterPush } from '@/hooks/common/router';
import { localStg } from '@/utils/storage';
import { $t } from '@/locales';
const store = useLogStore();
const data = ref(localStg.get('log'));
const route = useRoute();
const { routerPushByKey } = useRouterPush();
const type = ref<'job' | 'retry'>(route.query.type as 'job' | 'retry');
const taskData = ref();
const { taskBatchId, jobId, taskId, groupName, uniqueId } = route.query as { [key: string]: string };
function init() {
if (!data.value) {
if (!['job', 'retry'].includes(type.value)) {
routerPushByKey('404');
}
if (type.value === 'job') {
taskData.value = { taskBatchId, jobId, id: taskId };
}
if (type.value === 'retry') {
taskData.value = { groupName, uniqueId };
}
}
init();
onUnmounted(() => {
store.clear();
const title = computed(() => {
if (type.value === 'job') {
return `${$t('common.systemTaskType.job') + $t('page.log.title')} ------ JobId: ${jobId}, TaskId: ${taskId}, TaskBatchId: ${taskBatchId}`;
}
if (type.value === 'retry') {
return `${$t('common.systemTaskType.retry') + $t('page.log.title')} ------ ${$t('page.retryLog.groupName')}: ${groupName}, ${$t('page.retryLog.UniqueId')}: ${uniqueId}`;
}
return $t('page.log.title');
});
</script>
<template>
<NCard :bordered="false" size="small" class="h-full sm:flex-1-hidden card-wrapper" header-class="view-card-header">
<template #header>
<span v-if="data?.taskName">
{{
`${$t('page.log.title')} ------ ${$t('page.jobBatch.jobName')}: ${data?.taskName}, ${$t('common.batchList')} ID: ${data?.taskBatchId}`
}}
</span>
</template>
<LogDrawer :model-value="data?.data" :drawer="false" />
</NCard>
<div>
<LogDrawer :drawer="false" :title="title" :type="type" :task-data="taskData" />
</div>
</template>
<style scoped></style>
<style scoped>
:deep(.virtual-list) {
max-height: calc(100vh - 66px);
}
</style>

View File

@ -1,10 +1,8 @@
<script setup lang="tsx">
import { onBeforeUnmount, ref } from 'vue';
import { ref } from 'vue';
import { executorTypeRecord, operationReasonRecord, taskBatchStatusRecord } from '@/constants/business';
import { $t } from '@/locales';
import { tagColor } from '@/utils/common';
import { fetchJobLogList } from '@/service/api/log';
import { useLogStore } from '@/store/modules/log';
import JobTaskListTable from './job-task-list-table.vue';
defineOptions({
@ -17,7 +15,7 @@ interface Props {
log?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
withDefaults(defineProps<Props>(), {
log: false,
rowData: null
});
@ -28,56 +26,11 @@ const visible = defineModel<boolean>('visible', {
const taskData = ref<Api.Job.JobTask>();
const logShow = ref(false);
const store = useLogStore();
async function openLog(row: Api.Job.JobTask) {
store.setTaskInfo(props.rowData?.jobName || '', row.taskBatchId);
logShow.value = true;
taskData.value = row;
await getLogList();
}
const logList = ref<Api.JobLog.JobMessage[]>([]);
const interval = ref<NodeJS.Timeout>();
const controller = new AbortController();
const finished = ref<boolean>(false);
let startId = '0';
let fromIndex: number = 0;
async function getLogList() {
const { data: logData, error } = await fetchJobLogList({
taskBatchId: taskData.value!.taskBatchId,
jobId: taskData.value!.jobId,
taskId: taskData.value!.id,
startId,
fromIndex,
size: 50
});
if (!error) {
finished.value = logData.finished;
startId = logData.nextStartId;
fromIndex = logData.fromIndex;
if (logData.message) {
logList.value.push(...logData.message);
logList.value.sort((a, b) => Number.parseInt(a.time_stamp, 10) - Number.parseInt(b.time_stamp, 10));
}
if (!finished.value) {
clearTimeout(interval.value);
interval.value = setTimeout(getLogList, 1000);
}
}
}
const stopLog = () => {
finished.value = true;
controller.abort();
clearTimeout(interval.value);
interval.value = undefined;
};
onBeforeUnmount(() => {
stopLog();
});
</script>
<template>
@ -114,7 +67,7 @@ onBeforeUnmount(() => {
</NTabPane>
</NTabs>
</DetailDrawer>
<LogDrawer v-model="logList" v-model:show="logShow" :title="$t('page.log.title')" />
<LogDrawer v-model:show="logShow" :title="$t('page.log.title')" :task-data="taskData" />
</template>
<style scoped></style>

View File

@ -1,9 +1,7 @@
<script setup lang="ts">
import { onBeforeUnmount, ref } from 'vue';
import { $t } from '@/locales';
import { tagColor } from '@/utils/common';
import { retryTaskStatusTypeRecord, retryTaskTypeRecord } from '@/constants/business';
import { fetchRetryLogList } from '@/service/api/log';
defineOptions({
name: 'SceneDetailDrawer'
@ -14,63 +12,16 @@ interface Props {
rowData?: Api.RetryLog.RetryLog | null;
}
const props = defineProps<Props>();
defineProps<Props>();
const visible = defineModel<boolean>('visible', {
default: false
});
const logList = ref<Api.JobLog.JobMessage[]>([]);
const interval = ref<NodeJS.Timeout>();
const controller = new AbortController();
const finished = ref<boolean>(false);
let startId = '0';
let fromIndex: number = 0;
async function getLogList() {
const { data: logData, error } = await fetchRetryLogList({
groupName: props.rowData!.groupName,
uniqueId: props.rowData!.uniqueId,
startId,
fromIndex,
size: 50
});
if (!error) {
finished.value = logData.finished;
startId = logData.nextStartId;
fromIndex = logData.fromIndex;
if (logData.message) {
logList.value.push(...logData.message);
logList.value.sort((a, b) => Number.parseInt(a.time_stamp, 10) - Number.parseInt(b.time_stamp, 10));
}
if (!finished.value) {
clearTimeout(interval.value);
interval.value = setTimeout(getLogList, 1000);
}
}
}
const handleUpdateTab = async (value: number) => {
if (value === 1 && logList.value.length === 0) {
await getLogList();
}
};
const stopLog = () => {
finished.value = true;
controller.abort();
clearTimeout(interval.value);
interval.value = undefined;
};
onBeforeUnmount(() => {
stopLog();
});
</script>
<template>
<DetailDrawer v-model="visible" :title="$t('page.retryLog.detail')" :width="['50%', '90%']">
<NTabs type="segment" animated @update:value="handleUpdateTab">
<NTabs type="segment" animated>
<NTabPane :name="0" :tab="$t('page.log.info')">
<NDescriptions label-placement="top" bordered :column="2">
<NDescriptionsItem :label="$t('page.retryLog.UniqueId')" :span="2">
@ -102,8 +53,7 @@ onBeforeUnmount(() => {
</NDescriptions>
</NTabPane>
<NTabPane :name="1" :tab="$t('page.log.title')" display-directive="if">
<LogDrawer v-if="logList.length > 0" v-model="logList" :drawer="false" />
<NEmpty v-else class="h-full" />
<LogDrawer :drawer="false" type="retry" :task-data="rowData!" />
</NTabPane>
</NTabs>
</DetailDrawer>

View File

@ -109,8 +109,7 @@ onBeforeUnmount(() => {
</NDescriptions>
</NTabPane>
<NTabPane :name="1" :tab="$t('page.log.title')" display-directive="if">
<LogDrawer v-if="logList.length > 0" v-model="logList" :drawer="false" />
<NEmpty v-else class="h-full" />
<LogDrawer :drawer="false" type="retry" :task-data="rowData!" />
</NTabPane>
</NTabs>
</OperateDrawer>