feat: 任务批次新增日志详情
This commit is contained in:
parent
790e4a71be
commit
713c643544
101
src/components/common/detail-drawer.vue
Normal file
101
src/components/common/detail-drawer.vue
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, nextTick, onUnmounted, reactive, ref } from 'vue';
|
||||||
|
import { useAppStore } from '@/store/modules/app';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'DetailDrawer'
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title?: string;
|
||||||
|
width?: [string, string];
|
||||||
|
}
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:modelValue', modelValue: boolean): void;
|
||||||
|
}
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
const model = defineModel<boolean>({ default: false });
|
||||||
|
|
||||||
|
const slots = defineSlots();
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
const state = reactive({ width: 0 });
|
||||||
|
const isFullscreen = ref(false);
|
||||||
|
const drawerWidth = computed(() => {
|
||||||
|
if (props.width) {
|
||||||
|
return isFullscreen.value ? props.width[1] : props.width[0];
|
||||||
|
}
|
||||||
|
const maxMinWidth = 360;
|
||||||
|
const maxMaxWidth = 600;
|
||||||
|
if (appStore.isMobile) {
|
||||||
|
return state.width * 0.9 >= maxMinWidth ? `${maxMinWidth}px` : '90%';
|
||||||
|
}
|
||||||
|
let minWidth = state.width * 0.3 >= maxMinWidth ? `${maxMinWidth}px` : '30%';
|
||||||
|
minWidth = state.width <= 420 ? '90%' : minWidth;
|
||||||
|
let maxWidth = state.width * 0.5 >= maxMaxWidth ? `${maxMaxWidth}px` : '50%';
|
||||||
|
maxWidth = state.width <= 420 ? '90%' : maxWidth;
|
||||||
|
return isFullscreen.value ? maxWidth : minWidth;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getState = () => {
|
||||||
|
state.width = document.documentElement.clientWidth;
|
||||||
|
};
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
getState();
|
||||||
|
window.addEventListener('resize', getState);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
// 移除监听事件
|
||||||
|
window.removeEventListener('resize', getState);
|
||||||
|
});
|
||||||
|
|
||||||
|
const onUpdateShow = (value: boolean) => {
|
||||||
|
emit('update:modelValue', value);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NDrawer v-model:show="model" display-directive="if" :width="drawerWidth" @update:show="onUpdateShow">
|
||||||
|
<NDrawerContent :title="props.title" :native-scrollbar="false" closable header-class="operate-dawer-header">
|
||||||
|
<template #header>
|
||||||
|
{{ props.title }}
|
||||||
|
<div
|
||||||
|
v-if="!appStore.isMobile && (!props.width || (props.width && props.width[0] !== props.width[1]))"
|
||||||
|
quaternary
|
||||||
|
class="fullscreen text-18px color-#6a6a6a"
|
||||||
|
@click="isFullscreen = !isFullscreen"
|
||||||
|
>
|
||||||
|
<icon-material-symbols:close-fullscreen-rounded v-if="isFullscreen" />
|
||||||
|
<icon-material-symbols:open-in-full-rounded v-else />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<slot></slot>
|
||||||
|
<template v-if="slots.footer" #footer>
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</template>
|
||||||
|
</NDrawerContent>
|
||||||
|
</NDrawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.fullscreen {
|
||||||
|
height: 22px;
|
||||||
|
width: 22px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullscreen:hover {
|
||||||
|
background-color: #e8e8e8;
|
||||||
|
color: #696969;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,6 +1,7 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { NCollapse, NCollapseItem } from 'naive-ui';
|
import { NCollapse, NCollapseItem } from 'naive-ui';
|
||||||
import { defineComponent, ref, watch } from 'vue';
|
import { defineComponent, watch } from 'vue';
|
||||||
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'LogDrawer'
|
name: 'LogDrawer'
|
||||||
@ -9,11 +10,13 @@ defineOptions({
|
|||||||
interface Props {
|
interface Props {
|
||||||
title?: string;
|
title?: string;
|
||||||
show?: boolean;
|
show?: boolean;
|
||||||
|
modelValue?: Api.JobLog.JobMessage[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
title: '日志详情',
|
title: $t('page.log.title'),
|
||||||
show: false
|
show: false,
|
||||||
|
modelValue: () => []
|
||||||
});
|
});
|
||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
@ -25,38 +28,6 @@ const visible = defineModel<boolean>('visible', {
|
|||||||
default: true
|
default: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const messageList = ref([
|
|
||||||
{
|
|
||||||
time_stamp: '1712021845601',
|
|
||||||
level: 'ERROR',
|
|
||||||
port: '8018',
|
|
||||||
throwable:
|
|
||||||
'java.lang.ArithmeticException: / by zero\n\tat com.example.easy.retry.service.impl.RemoteRetryServiceImpl.remoteSync(RemoteRetryServiceImpl.java:46)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:568)\n\tat org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)\n\tat org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:756)\n\tat com.aizuda.easy.retry.client.core.intercepter.EasyRetryInterceptor.invoke(EasyRetryInterceptor.java:92)\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)\n\tat org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:756)\n\tat org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)\n\tat com.example.easy.retry.service.impl.RemoteRetryServiceImpl$$SpringCGLIB$$0.remoteSync(<generated>)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:568)\n\tat org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:281)\n\tat com.aizuda.easy.retry.client.core.strategy.ExecutorAnnotationMethod.doExecute(ExecutorAnnotationMethod.java:28)\n\tat com.aizuda.easy.retry.client.core.executor.AbstractRetryExecutor.doExecute(AbstractRetryExecutor.java:32)\n\tat com.aizuda.easy.retry.client.core.executor.AbstractRetryExecutor.execute(AbstractRetryExecutor.java:23)\n\tat com.aizuda.easy.retry.client.core.strategy.RemoteRetryStrategies.lambda$doGetCallable$2(RemoteRetryStrategies.java:89)\n\tat com.github.rholder.retry.AttemptTimeLimiters$NoAttemptTimeLimit.call(AttemptTimeLimiters.java:78)\n\tat com.github.rholder.retry.Retryer.call(Retryer.java:160)\n\tat com.aizuda.easy.retry.client.core.executor.GuavaRetryExecutor.call(GuavaRetryExecutor.java:56)\n\tat com.aizuda.easy.retry.client.core.strategy.AbstractRetryStrategies.openRetry(AbstractRetryStrategies.java:88)\n\tat com.aizuda.easy.retry.client.core.client.RetryEndPoint.dispatch(RetryEndPoint.java:99)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)',
|
|
||||||
host: '127.0.0.1',
|
|
||||||
location: 'com.aizuda.easy.retry.client.core.client.RetryEndPoint.dispatch(RetryEndPoint.java:124)',
|
|
||||||
thread: 'http-nio-8018-exec-5',
|
|
||||||
message: 'remote retry complete. count:[1] '
|
|
||||||
},
|
|
||||||
{
|
|
||||||
time_stamp: '1712021875512',
|
|
||||||
level: 'INFO',
|
|
||||||
port: '8018',
|
|
||||||
host: '127.0.0.1',
|
|
||||||
location: 'com.aizuda.easy.retry.client.core.client.RetryEndPoint.dispatch(RetryEndPoint.java:117)',
|
|
||||||
thread: 'http-nio-8018-exec-2',
|
|
||||||
message: 'remote retry complete. count:[4] result:[null]'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
time_stamp: '1712021875512',
|
|
||||||
level: 'ERROR',
|
|
||||||
port: '8018',
|
|
||||||
host: '127.0.0.1',
|
|
||||||
location: 'com.aizuda.easy.retry.client.core.client.RetryEndPoint.dispatch(RetryEndPoint.java:124)',
|
|
||||||
thread: 'http-nio-8018-exec-2',
|
|
||||||
message: 'remote retry complete. count:[4] '
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
const ThrowableComponent = defineComponent({
|
const ThrowableComponent = defineComponent({
|
||||||
props: {
|
props: {
|
||||||
throwable: String
|
throwable: String
|
||||||
@ -109,12 +80,12 @@ function timestampToDate(timestamp: string): string {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NDrawer v-model:show="visible" width="100%" display-directive="if" @update:show="onUpdateShow">
|
<NDrawer v-model:show="visible" width="100%" display-directive="if" @update:show="onUpdateShow">
|
||||||
<NDrawerContent title="日志详情" closable>
|
<NDrawerContent :title="title" closable>
|
||||||
<div class="snail-log">
|
<div class="snail-log">
|
||||||
<div class="snail-log-scrollbar">
|
<div class="snail-log-scrollbar">
|
||||||
<code>
|
<code>
|
||||||
<pre
|
<pre
|
||||||
v-for="(message, index) in messageList"
|
v-for="(message, index) in modelValue"
|
||||||
:key="index"
|
:key="index"
|
||||||
><NDivider v-if="index !== 0" /><span class="log-hljs-time">{{timestampToDate(message.time_stamp)}}</span><span :class="`log-hljs-level-${message.level}`">{{`\t${message.level}\t`}}</span><span class="log-hljs-thread">{{`[${message.thread}]\t`}}</span><span class="log-hljs-location">{{`${message.location}: \n`}}</span> -<span class="pl-6px">{{`${message.message}\n`}}</span><ThrowableComponent :throwable="message.throwable" /></pre>
|
><NDivider v-if="index !== 0" /><span class="log-hljs-time">{{timestampToDate(message.time_stamp)}}</span><span :class="`log-hljs-level-${message.level}`">{{`\t${message.level}\t`}}</span><span class="log-hljs-thread">{{`[${message.thread}]\t`}}</span><span class="log-hljs-location">{{`${message.location}: \n`}}</span> -<span class="pl-6px">{{`${message.message}\n`}}</span><ThrowableComponent :throwable="message.throwable" /></pre>
|
||||||
</code>
|
</code>
|
||||||
|
@ -976,7 +976,17 @@ const local: App.I18n.Schema = {
|
|||||||
jobName: 'Please enter job name',
|
jobName: 'Please enter job name',
|
||||||
taskBatchStatus: 'Please enter state'
|
taskBatchStatus: 'Please enter state'
|
||||||
},
|
},
|
||||||
detail: 'Job Batch Detail'
|
detail: 'Job Batch Detail',
|
||||||
|
jobTask: {
|
||||||
|
title: 'JobTask 列表',
|
||||||
|
id: 'ID',
|
||||||
|
groupName: '组名称',
|
||||||
|
clientInfo: '地址',
|
||||||
|
argsStr: '参数',
|
||||||
|
resultMessage: '结果',
|
||||||
|
retryCount: '重试次数',
|
||||||
|
createDt: '开始执行时间'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
userManager: {
|
userManager: {
|
||||||
title: 'UserCenter List',
|
title: 'UserCenter List',
|
||||||
@ -1001,6 +1011,11 @@ const local: App.I18n.Schema = {
|
|||||||
user: 'User',
|
user: 'User',
|
||||||
admin: 'Admin'
|
admin: 'Admin'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
log: {
|
||||||
|
title: 'Log Detail',
|
||||||
|
view: 'View Log',
|
||||||
|
info: 'Info'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
|
@ -971,7 +971,17 @@ const local: App.I18n.Schema = {
|
|||||||
jobName: '请输入任务名称',
|
jobName: '请输入任务名称',
|
||||||
taskBatchStatus: '请输入状态'
|
taskBatchStatus: '请输入状态'
|
||||||
},
|
},
|
||||||
detail: '执行批次详情'
|
detail: '执行批次详情',
|
||||||
|
jobTask: {
|
||||||
|
title: 'JobTask 列表',
|
||||||
|
id: 'ID',
|
||||||
|
groupName: '组名称',
|
||||||
|
clientInfo: '地址',
|
||||||
|
argsStr: '参数',
|
||||||
|
resultMessage: '结果',
|
||||||
|
retryCount: '重试次数',
|
||||||
|
createDt: '开始执行时间'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
userManager: {
|
userManager: {
|
||||||
title: '用户列表',
|
title: '用户列表',
|
||||||
@ -996,6 +1006,11 @@ const local: App.I18n.Schema = {
|
|||||||
user: '普通用户',
|
user: '普通用户',
|
||||||
admin: '管理员'
|
admin: '管理员'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
log: {
|
||||||
|
title: '日志详情',
|
||||||
|
view: '查看日志',
|
||||||
|
info: '基本信息'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
|
@ -18,6 +18,15 @@ export function fetchGetJobList(params?: Api.Job.JobSearchParams) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** get Job Task list */
|
||||||
|
export function fetchGetJobTaskList(params?: Api.Job.jobTaskSearchParams) {
|
||||||
|
return request<Api.Job.JobTaskList>({
|
||||||
|
url: '/job/task/list',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** add Job */
|
/** add Job */
|
||||||
export function fetchAddJob(data: Api.Job.Job) {
|
export function fetchAddJob(data: Api.Job.Job) {
|
||||||
return request<boolean>({
|
return request<boolean>({
|
||||||
|
19
src/service/api/log.ts
Normal file
19
src/service/api/log.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { request } from '../request';
|
||||||
|
|
||||||
|
/** get Job Log List */
|
||||||
|
export function fetchJobLogList(params?: Api.JobLog.JobLogSearchParams) {
|
||||||
|
return request<Api.JobLog.JobLogList>({
|
||||||
|
url: '/job/log/list',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get Retry Log List */
|
||||||
|
export function fetchRetryLogList(params?: Api.JobLog.JobLogSearchParams) {
|
||||||
|
return request<Api.JobLog.JobLogList>({
|
||||||
|
url: '/retry-task-log/message/list',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
64
src/typings/api.d.ts
vendored
64
src/typings/api.d.ts
vendored
@ -986,6 +986,36 @@ declare namespace Api {
|
|||||||
jobId?: number;
|
jobId?: number;
|
||||||
keywords?: string;
|
keywords?: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
/** jobTask */
|
||||||
|
type JobTask = Common.CommonRecord<{
|
||||||
|
/** ID */
|
||||||
|
id: string;
|
||||||
|
/** 任务 ID */
|
||||||
|
jobId: string;
|
||||||
|
/** 组名称 */
|
||||||
|
groupName: string;
|
||||||
|
/** 地址 */
|
||||||
|
clientInfo: string;
|
||||||
|
/** 参数 */
|
||||||
|
argsStr: string;
|
||||||
|
/** 结果 */
|
||||||
|
resultMessage: string;
|
||||||
|
/** 重试次数 */
|
||||||
|
retryCount: string;
|
||||||
|
/** 开始执行时间 */
|
||||||
|
createDt: string;
|
||||||
|
/** 任务批次 ID */
|
||||||
|
taskBatchId: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
/** jobTask search params */
|
||||||
|
type jobTaskSearchParams = CommonType.RecordNullable<
|
||||||
|
Pick<Api.Job.JobTask, 'groupName' | 'taskBatchId'> & CommonSearchParams & { startId: number; fromIndex: number }
|
||||||
|
>;
|
||||||
|
|
||||||
|
/** jobTask list */
|
||||||
|
type JobTaskList = Common.PaginatingQueryRecord<JobTask>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1159,4 +1189,38 @@ declare namespace Api {
|
|||||||
/** 1、user 2、admin */
|
/** 1、user 2、admin */
|
||||||
type Role = 1 | 2;
|
type Role = 1 | 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* namespace JobLog
|
||||||
|
*
|
||||||
|
* backend api module: "JobLog"
|
||||||
|
*/
|
||||||
|
namespace JobLog {
|
||||||
|
type JobLevel = 'INFO' | 'WARN' | 'ERROR' | 'DEBUG';
|
||||||
|
|
||||||
|
type JobLogSearchParams = {
|
||||||
|
taskBatchId: string;
|
||||||
|
jobId: string;
|
||||||
|
taskId: string;
|
||||||
|
startId: string;
|
||||||
|
fromIndex: number;
|
||||||
|
size: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type JobLogList = {
|
||||||
|
finished: boolean;
|
||||||
|
fromIndex: number;
|
||||||
|
message: JobMessage[];
|
||||||
|
nextStartId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type JobMessage = {
|
||||||
|
level: JobLevel;
|
||||||
|
location: string;
|
||||||
|
message: string;
|
||||||
|
thread: string;
|
||||||
|
['time_stamp']: string;
|
||||||
|
throwable: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
15
src/typings/app.d.ts
vendored
15
src/typings/app.d.ts
vendored
@ -1119,6 +1119,16 @@ declare namespace App {
|
|||||||
taskBatchStatus: string;
|
taskBatchStatus: string;
|
||||||
};
|
};
|
||||||
detail: string;
|
detail: string;
|
||||||
|
jobTask: {
|
||||||
|
title: string;
|
||||||
|
id: string;
|
||||||
|
groupName: string;
|
||||||
|
clientInfo: string;
|
||||||
|
argsStr: string;
|
||||||
|
resultMessage: string;
|
||||||
|
retryCount: string;
|
||||||
|
createDt: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
userManager: {
|
userManager: {
|
||||||
title: string;
|
title: string;
|
||||||
@ -1144,6 +1154,11 @@ declare namespace App {
|
|||||||
admin: string;
|
admin: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
log: {
|
||||||
|
title: string;
|
||||||
|
view: string;
|
||||||
|
info: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
form: {
|
form: {
|
||||||
required: string;
|
required: string;
|
||||||
|
@ -151,7 +151,7 @@ watch(
|
|||||||
class="sm:h-full"
|
class="sm:h-full"
|
||||||
/>
|
/>
|
||||||
</NCard>
|
</NCard>
|
||||||
<JobBatchDetailDrawer v-model:visible="detailVisible" :row-data="detailData" />
|
<JobBatchDetailDrawer v-if="detailVisible" v-model:visible="detailVisible" :row-data="detailData" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="tsx">
|
||||||
import { watch } from 'vue';
|
import { NButton } from 'naive-ui';
|
||||||
|
import { onBeforeUnmount, ref } from 'vue';
|
||||||
import { executorTypeRecord, operationReasonRecord, taskBatchStatusRecord } from '@/constants/business';
|
import { executorTypeRecord, operationReasonRecord, taskBatchStatusRecord } from '@/constants/business';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
// import { fetchGetJobBatchDetail } from '@/service/api';
|
// import { fetchGetJobBatchDetail } from '@/service/api';
|
||||||
import { tagColor } from '@/utils/common';
|
import { tagColor } from '@/utils/common';
|
||||||
|
import { useTable } from '@/hooks/common/table';
|
||||||
|
import { fetchGetJobTaskList } from '@/service/api';
|
||||||
|
import { fetchJobLogList } from '@/service/api/log';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'JobBatchDetailDrawer'
|
name: 'JobBatchDetailDrawer'
|
||||||
@ -13,50 +17,187 @@ interface Props {
|
|||||||
/** row data */
|
/** row data */
|
||||||
rowData?: Api.JobBatch.JobBatch | null;
|
rowData?: Api.JobBatch.JobBatch | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
|
|
||||||
|
const taskData = ref<Api.Job.JobTask>();
|
||||||
const visible = defineModel<boolean>('visible', {
|
const visible = defineModel<boolean>('visible', {
|
||||||
default: false
|
default: false
|
||||||
});
|
});
|
||||||
|
const logShow = defineModel<boolean>('logShow', {
|
||||||
|
default: false
|
||||||
|
});
|
||||||
|
|
||||||
watch(
|
const { columns, data, loading, mobilePagination } = useTable({
|
||||||
() => visible.value,
|
apiFn: fetchGetJobTaskList,
|
||||||
async val => {
|
apiParams: {
|
||||||
if (val === true) {
|
page: 1,
|
||||||
console.log(props.rowData?.id);
|
size: 10,
|
||||||
}
|
groupName: props.rowData?.groupName,
|
||||||
|
taskBatchId: props.rowData?.id,
|
||||||
|
startId: 0,
|
||||||
|
fromIndex: 0
|
||||||
|
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
|
||||||
|
// the value can not be undefined, otherwise the property in Form will not be reactive
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
columns: () => [
|
||||||
);
|
{
|
||||||
|
key: 'index',
|
||||||
|
title: $t('common.index'),
|
||||||
|
align: 'center',
|
||||||
|
width: 64,
|
||||||
|
render: row => {
|
||||||
|
async function openLog() {
|
||||||
|
logShow.value = true;
|
||||||
|
taskData.value = row;
|
||||||
|
await getLogList();
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<NButton type="info" text onClick={openLog}>
|
||||||
|
<span class="w-28px ws-break-spaces">{$t('page.log.view')}</span>
|
||||||
|
</NButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'id',
|
||||||
|
title: $t('page.jobBatch.jobTask.id'),
|
||||||
|
align: 'left',
|
||||||
|
minWidth: 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'groupName',
|
||||||
|
title: $t('page.jobBatch.jobTask.groupName'),
|
||||||
|
align: 'left',
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'clientInfo',
|
||||||
|
title: $t('page.jobBatch.jobTask.clientInfo'),
|
||||||
|
align: 'left',
|
||||||
|
minWidth: 120,
|
||||||
|
render: row => {
|
||||||
|
if (row.clientInfo) {
|
||||||
|
const parts = row.clientInfo?.split('@');
|
||||||
|
const result = parts.length > 1 ? parts[1] : '';
|
||||||
|
return <div>{result}</div>;
|
||||||
|
}
|
||||||
|
return <div>{row.clientInfo}</div>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'argsStr',
|
||||||
|
title: $t('page.jobBatch.jobTask.argsStr'),
|
||||||
|
align: 'left',
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'resultMessage',
|
||||||
|
title: $t('page.jobBatch.jobTask.resultMessage'),
|
||||||
|
align: 'left',
|
||||||
|
minWidth: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'retryCount',
|
||||||
|
title: $t('page.jobBatch.jobTask.retryCount'),
|
||||||
|
align: 'left',
|
||||||
|
minWidth: 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'createDt',
|
||||||
|
title: $t('page.jobBatch.jobTask.createDt'),
|
||||||
|
align: 'left',
|
||||||
|
minWidth: 120
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<OperateDrawer v-model="visible" :title="$t('page.jobBatch.detail')">
|
<DetailDrawer v-model="visible" :title="$t('page.jobBatch.detail')" :width="['50%', '90%']">
|
||||||
<NDescriptions label-placement="top" bordered :column="2">
|
<NTabs type="segment" animated>
|
||||||
<NDescriptionsItem :label="$t('page.jobBatch.groupName')">{{ rowData?.groupName }}</NDescriptionsItem>
|
<NTabPane :name="0" :tab="$t('page.log.info')">
|
||||||
<NDescriptionsItem :label="$t('page.jobBatch.jobName')">{{ rowData?.jobName }}</NDescriptionsItem>
|
<NDescriptions label-placement="top" bordered :column="2">
|
||||||
<NDescriptionsItem :label="$t('page.jobBatch.taskBatchStatus')">
|
<NDescriptionsItem :label="$t('page.jobBatch.groupName')">{{ rowData?.groupName }}</NDescriptionsItem>
|
||||||
<NTag :type="tagColor(rowData?.taskBatchStatus!)">
|
<NDescriptionsItem :label="$t('page.jobBatch.jobName')">{{ rowData?.jobName }}</NDescriptionsItem>
|
||||||
{{ $t(taskBatchStatusRecord[rowData?.taskBatchStatus!]) }}
|
<NDescriptionsItem :label="$t('page.jobBatch.taskBatchStatus')">
|
||||||
</NTag>
|
<NTag :type="tagColor(rowData?.taskBatchStatus!)">
|
||||||
</NDescriptionsItem>
|
{{ $t(taskBatchStatusRecord[rowData?.taskBatchStatus!]) }}
|
||||||
<NDescriptionsItem :label="$t('page.jobBatch.executionAt')">{{ rowData?.executionAt }}</NDescriptionsItem>
|
</NTag>
|
||||||
<NDescriptionsItem :label="$t('page.jobBatch.operationReason')">
|
</NDescriptionsItem>
|
||||||
<NTag :type="tagColor(rowData?.operationReason!)">
|
<NDescriptionsItem :label="$t('page.jobBatch.executionAt')">{{ rowData?.executionAt }}</NDescriptionsItem>
|
||||||
{{ $t(operationReasonRecord[rowData?.operationReason!]) }}
|
<NDescriptionsItem :label="$t('page.jobBatch.operationReason')">
|
||||||
</NTag>
|
<NTag :type="tagColor(rowData?.operationReason!)">
|
||||||
</NDescriptionsItem>
|
{{ $t(operationReasonRecord[rowData?.operationReason!]) }}
|
||||||
<NDescriptionsItem :label="$t('page.jobBatch.executorType')">
|
</NTag>
|
||||||
<NTag :type="tagColor(rowData?.executorType!)">
|
</NDescriptionsItem>
|
||||||
{{ $t(executorTypeRecord[rowData?.executorType!]) }}
|
<NDescriptionsItem :label="$t('page.jobBatch.executorType')">
|
||||||
</NTag>
|
<NTag :type="tagColor(rowData?.executorType!)">
|
||||||
</NDescriptionsItem>
|
{{ $t(executorTypeRecord[rowData?.executorType!]) }}
|
||||||
<NDescriptionsItem :label="$t('page.jobBatch.executorInfo')" :span="2">
|
</NTag>
|
||||||
{{ rowData?.executorInfo }}
|
</NDescriptionsItem>
|
||||||
</NDescriptionsItem>
|
<NDescriptionsItem :label="$t('page.jobBatch.executorInfo')" :span="2">
|
||||||
<NDescriptionsItem :label="$t('common.createDt')" :span="2">{{ rowData?.createDt }}</NDescriptionsItem>
|
{{ rowData?.executorInfo }}
|
||||||
</NDescriptions>
|
</NDescriptionsItem>
|
||||||
</OperateDrawer>
|
<NDescriptionsItem :label="$t('common.createDt')" :span="2">{{ rowData?.createDt }}</NDescriptionsItem>
|
||||||
|
</NDescriptions>
|
||||||
|
</NTabPane>
|
||||||
|
<NTabPane :name="1" :tab="$t('page.log.title')" display-directive="if">
|
||||||
|
<NDataTable
|
||||||
|
:columns="columns"
|
||||||
|
:data="data"
|
||||||
|
:loading="loading"
|
||||||
|
remote
|
||||||
|
:row-key="row => row.id"
|
||||||
|
:pagination="mobilePagination"
|
||||||
|
class="sm:h-full"
|
||||||
|
/>
|
||||||
|
</NTabPane>
|
||||||
|
</NTabs>
|
||||||
|
<LogDrawer v-model="logList" v-model:show="logShow" title="日志" />
|
||||||
|
</DetailDrawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
Loading…
Reference in New Issue
Block a user