feat(sj_map_reduce): 执行批次新增日志按钮,并优化日志显示
This commit is contained in:
parent
090763eec9
commit
61cc8252a7
12
public/iconify/hugeicons.json
Normal file
12
public/iconify/hugeicons.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"prefix": "hugeicons",
|
||||||
|
"lastModified": 1715671861,
|
||||||
|
"aliases": {},
|
||||||
|
"width": 24,
|
||||||
|
"height": 24,
|
||||||
|
"icons": {
|
||||||
|
"share-01": {
|
||||||
|
"body": "<g fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\" color=\"currentColor\"><path d=\"m20.393 8.032l-1.747-1.517c-1.259-1.093-1.752-1.681-2.434-1.474c-.85.26-.57 1.895-.57 2.447c-1.321 0-2.695-.101-3.998.11c-4.3.7-5.644 3.759-5.644 7.055c1.217-.747 2.433-1.578 3.895-1.924c1.826-.432 3.865-.226 5.747-.226c0 .552-.28 2.188.57 2.447c.772.236 1.175-.38 2.434-1.473l1.747-1.517C21.464 11.03 22 10.564 22 9.996s-.536-1.034-1.607-1.964\"/><path d=\"M10.568 3c-3.86.007-5.882.102-7.174 1.39C2 5.783 2 8.022 2 12.5s0 6.717 1.394 8.109C4.788 22 7.032 22 11.52 22s6.73 0 8.125-1.391C20.615 19.64 20.91 18.26 21 16\"/></g>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { NCollapse, NCollapseItem } from 'naive-ui';
|
import { NCollapse, NCollapseItem } from 'naive-ui';
|
||||||
import { defineComponent, watch } from 'vue';
|
import { defineComponent, watch } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
|
import { useLogStore } from '@/store/modules/log';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'LogDrawer'
|
name: 'LogDrawer'
|
||||||
@ -66,7 +68,12 @@ watch(
|
|||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const store = useLogStore();
|
||||||
|
|
||||||
const onUpdateShow = (value: boolean) => {
|
const onUpdateShow = (value: boolean) => {
|
||||||
|
if (!value) {
|
||||||
|
store.clear();
|
||||||
|
}
|
||||||
emit('update:show', value);
|
emit('update:show', value);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -81,6 +88,14 @@ function timestampToDate(timestamp: string): string {
|
|||||||
const seconds = date.getSeconds().toString().length === 1 ? `0${date.getSeconds()}` : date.getSeconds().toString();
|
const seconds = date.getSeconds().toString().length === 1 ? `0${date.getSeconds()}` : date.getSeconds().toString();
|
||||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${date.getMilliseconds()}`;
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${date.getMilliseconds()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
function openNewTab() {
|
||||||
|
const url = router.resolve('/log');
|
||||||
|
store.setData(props.modelValue);
|
||||||
|
window.open(url.href);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -91,7 +106,13 @@ function timestampToDate(timestamp: string): string {
|
|||||||
display-directive="if"
|
display-directive="if"
|
||||||
@update:show="onUpdateShow"
|
@update:show="onUpdateShow"
|
||||||
>
|
>
|
||||||
<NDrawerContent :title="title" closable>
|
<NDrawerContent closable>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex-center">
|
||||||
|
<span>{{ title }}</span>
|
||||||
|
<ButtonIcon icon="hugeicons:share-01" tooltip-content="在新标签页打开" class="ml-3px" @click="openNewTab" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<div class="snail-log bg-#fafafc p-16px dark:bg-#000">
|
<div class="snail-log bg-#fafafc p-16px dark:bg-#000">
|
||||||
<div class="snail-log-scrollbar">
|
<div class="snail-log-scrollbar">
|
||||||
<code>
|
<code>
|
||||||
|
@ -302,6 +302,7 @@ const local: App.I18n.Schema = {
|
|||||||
404: 'Page Not Found',
|
404: 'Page Not Found',
|
||||||
500: 'Server Error',
|
500: 'Server Error',
|
||||||
'iframe-page': 'Iframe',
|
'iframe-page': 'Iframe',
|
||||||
|
log: 'Log',
|
||||||
home: 'Home',
|
home: 'Home',
|
||||||
about: 'About',
|
about: 'About',
|
||||||
pods: 'Online Machine',
|
pods: 'Online Machine',
|
||||||
|
@ -302,6 +302,7 @@ const local: App.I18n.Schema = {
|
|||||||
404: '页面不存在',
|
404: '页面不存在',
|
||||||
500: '服务器错误',
|
500: '服务器错误',
|
||||||
'iframe-page': '外链页面',
|
'iframe-page': '外链页面',
|
||||||
|
log: '日志',
|
||||||
home: '首页',
|
home: '首页',
|
||||||
about: '关于',
|
about: '关于',
|
||||||
pods: '在线机器',
|
pods: '在线机器',
|
||||||
|
@ -19,6 +19,7 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
|
|||||||
404: () => import("@/views/_builtin/404/index.vue"),
|
404: () => import("@/views/_builtin/404/index.vue"),
|
||||||
500: () => import("@/views/_builtin/500/index.vue"),
|
500: () => import("@/views/_builtin/500/index.vue"),
|
||||||
"iframe-page": () => import("@/views/_builtin/iframe-page/[url].vue"),
|
"iframe-page": () => import("@/views/_builtin/iframe-page/[url].vue"),
|
||||||
|
log: () => import("@/views/_builtin/log/index.vue"),
|
||||||
login: () => import("@/views/_builtin/login/index.vue"),
|
login: () => import("@/views/_builtin/login/index.vue"),
|
||||||
about: () => import("@/views/about/index.vue"),
|
about: () => import("@/views/about/index.vue"),
|
||||||
group: () => import("@/views/group/index.vue"),
|
group: () => import("@/views/group/index.vue"),
|
||||||
|
@ -120,6 +120,17 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'log',
|
||||||
|
path: '/log',
|
||||||
|
component: 'layout.blank$view.log',
|
||||||
|
meta: {
|
||||||
|
title: 'log',
|
||||||
|
i18nKey: 'route.log',
|
||||||
|
icon: 'carbon:batch-job',
|
||||||
|
hideInMenu: true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'login',
|
name: 'login',
|
||||||
path: '/login/:module(pwd-login)?',
|
path: '/login/:module(pwd-login)?',
|
||||||
|
40
src/store/modules/log/index.ts
Normal file
40
src/store/modules/log/index.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
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');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLogStg() {
|
||||||
|
return localStg.get('log');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
taskName,
|
||||||
|
taskBatchId,
|
||||||
|
data,
|
||||||
|
clear,
|
||||||
|
setTaskInfo,
|
||||||
|
setData,
|
||||||
|
getLogStg
|
||||||
|
};
|
||||||
|
});
|
5
src/typings/storage.d.ts
vendored
5
src/typings/storage.d.ts
vendored
@ -44,5 +44,10 @@ declare namespace StorageType {
|
|||||||
layout: UnionKey.ThemeLayoutMode;
|
layout: UnionKey.ThemeLayoutMode;
|
||||||
siderCollapse: boolean;
|
siderCollapse: boolean;
|
||||||
};
|
};
|
||||||
|
log: {
|
||||||
|
taskName: string;
|
||||||
|
taskBatchId: string;
|
||||||
|
data: Api.JobLog.JobMessage[];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
26
src/views/_builtin/log/index.vue
Normal file
26
src/views/_builtin/log/index.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onUnmounted, ref } from 'vue';
|
||||||
|
import { useLogStore } from '@/store/modules/log';
|
||||||
|
|
||||||
|
const store = useLogStore();
|
||||||
|
|
||||||
|
const data = ref(store.getLogStg());
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
store.clear();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NCard
|
||||||
|
:title="`${$t('page.log.title')} ------ ${$t('page.jobBatch.jobName')}: ${data?.taskName}, ${$t('common.batchList')} ID: ${data?.taskBatchId}`"
|
||||||
|
:bordered="false"
|
||||||
|
size="small"
|
||||||
|
class="h-full sm:flex-1-hidden card-wrapper"
|
||||||
|
header-class="view-card-header"
|
||||||
|
>
|
||||||
|
<LogDrawer :model-value="data?.data" :drawer="false" />
|
||||||
|
</NCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
@ -20,6 +20,7 @@ const route = useRoute();
|
|||||||
const detailData = ref<Api.JobBatch.JobBatch | null>();
|
const detailData = ref<Api.JobBatch.JobBatch | null>();
|
||||||
/** 详情页可见状态 */
|
/** 详情页可见状态 */
|
||||||
const { bool: detailVisible, setTrue: openDetail } = useBoolean(false);
|
const { bool: detailVisible, setTrue: openDetail } = useBoolean(false);
|
||||||
|
const { bool: detailLog, setBool: setDetailLog } = useBoolean(false);
|
||||||
|
|
||||||
const { columnChecks, columns, data, getData, loading, mobilePagination, searchParams, resetSearchParams } = useTable({
|
const { columnChecks, columns, data, getData, loading, mobilePagination, searchParams, resetSearchParams } = useTable({
|
||||||
apiFn: fetchGetJobBatchList,
|
apiFn: fetchGetJobBatchList,
|
||||||
@ -55,6 +56,7 @@ const { columnChecks, columns, data, getData, loading, mobilePagination, searchP
|
|||||||
render: row => {
|
render: row => {
|
||||||
function showDetailDrawer() {
|
function showDetailDrawer() {
|
||||||
detailData.value = row;
|
detailData.value = row;
|
||||||
|
setDetailLog(false);
|
||||||
openDetail();
|
openDetail();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,6 +154,9 @@ const { columnChecks, columns, data, getData, loading, mobilePagination, searchP
|
|||||||
width: 130,
|
width: 130,
|
||||||
render: row => (
|
render: row => (
|
||||||
<div class="flex-center gap-8px">
|
<div class="flex-center gap-8px">
|
||||||
|
<NButton type="primary" text ghost size="small" onClick={handleLog}>
|
||||||
|
{$t('common.log')}
|
||||||
|
</NButton>
|
||||||
{row.taskBatchStatus === 1 || row.taskBatchStatus === 2 ? (
|
{row.taskBatchStatus === 1 || row.taskBatchStatus === 2 ? (
|
||||||
<NPopconfirm onPositiveClick={() => handleStopJob(row.id!)}>
|
<NPopconfirm onPositiveClick={() => handleStopJob(row.id!)}>
|
||||||
{{
|
{{
|
||||||
@ -186,6 +191,11 @@ const { columnChecks, columns, data, getData, loading, mobilePagination, searchP
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function handleLog() {
|
||||||
|
setDetailLog(true);
|
||||||
|
openDetail();
|
||||||
|
}
|
||||||
|
|
||||||
async function handleRetryJob(id: string) {
|
async function handleRetryJob(id: string) {
|
||||||
const { error } = await fetchJobBatchRetry(id);
|
const { error } = await fetchJobBatchRetry(id);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
@ -269,7 +279,12 @@ initParams();
|
|||||||
class="sm:h-full"
|
class="sm:h-full"
|
||||||
/>
|
/>
|
||||||
</NCard>
|
</NCard>
|
||||||
<JobBatchDetailDrawer v-if="detailVisible" v-model:visible="detailVisible" :row-data="detailData" />
|
<JobBatchDetailDrawer
|
||||||
|
v-if="detailVisible"
|
||||||
|
v-model:visible="detailVisible"
|
||||||
|
v-model:log="detailLog"
|
||||||
|
:row-data="detailData"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import { executorTypeRecord, operationReasonRecord, taskBatchStatusRecord } from
|
|||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
import { tagColor } from '@/utils/common';
|
import { tagColor } from '@/utils/common';
|
||||||
import { fetchJobLogList } from '@/service/api/log';
|
import { fetchJobLogList } from '@/service/api/log';
|
||||||
|
import { useLogStore } from '@/store/modules/log';
|
||||||
import JobTaskListTable from './job-task-list-table.vue';
|
import JobTaskListTable from './job-task-list-table.vue';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@ -13,20 +14,24 @@ defineOptions({
|
|||||||
interface Props {
|
interface Props {
|
||||||
/** row data */
|
/** row data */
|
||||||
rowData?: Api.JobBatch.JobBatch | null;
|
rowData?: Api.JobBatch.JobBatch | null;
|
||||||
|
log?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
defineProps<Props>();
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
log: false,
|
||||||
|
rowData: null
|
||||||
|
});
|
||||||
|
|
||||||
const visible = defineModel<boolean>('visible', {
|
const visible = defineModel<boolean>('visible', {
|
||||||
default: false
|
default: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const taskData = ref<Api.Job.JobTask>();
|
const taskData = ref<Api.Job.JobTask>();
|
||||||
const logShow = defineModel<boolean>('logShow', {
|
const logShow = ref(false);
|
||||||
default: false
|
const store = useLogStore();
|
||||||
});
|
|
||||||
|
|
||||||
async function openLog(row: Api.Job.JobTask) {
|
async function openLog(row: Api.Job.JobTask) {
|
||||||
|
store.setTaskInfo(props.rowData!.jobName, row.taskBatchId);
|
||||||
logShow.value = true;
|
logShow.value = true;
|
||||||
taskData.value = row;
|
taskData.value = row;
|
||||||
await getLogList();
|
await getLogList();
|
||||||
@ -77,7 +82,7 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<DetailDrawer v-model="visible" :title="$t('page.jobBatch.detail')" :width="['50%', '90%']">
|
<DetailDrawer v-model="visible" :title="$t('page.jobBatch.detail')" :width="['50%', '90%']">
|
||||||
<NTabs type="segment" animated>
|
<NTabs type="segment" animated :default-value="log ? 1 : 0">
|
||||||
<NTabPane :name="0" :tab="$t('page.log.info')">
|
<NTabPane :name="0" :tab="$t('page.log.info')">
|
||||||
<NDescriptions label-placement="top" bordered :column="2">
|
<NDescriptions label-placement="top" bordered :column="2">
|
||||||
<NDescriptionsItem :label="$t('page.jobBatch.groupName')">{{ rowData?.groupName }}</NDescriptionsItem>
|
<NDescriptionsItem :label="$t('page.jobBatch.groupName')">{{ rowData?.groupName }}</NDescriptionsItem>
|
||||||
|
@ -48,7 +48,7 @@ const { columns, data, loading, mobilePagination } = useTable({
|
|||||||
key: 'id',
|
key: 'id',
|
||||||
title: $t('page.jobBatch.jobTask.id'),
|
title: $t('page.jobBatch.jobTask.id'),
|
||||||
align: 'left',
|
align: 'left',
|
||||||
minWidth: 100,
|
width: 100,
|
||||||
ellipsis: {
|
ellipsis: {
|
||||||
tooltip: true
|
tooltip: true
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user