feat: 新增批次详情卡片

This commit is contained in:
xlsea 2024-05-30 11:03:10 +08:00
parent 2a913e4edd
commit e82682b9c8
17 changed files with 912 additions and 6 deletions

View File

@ -10,6 +10,36 @@ export function fetchJobList(groupName: string) {
});
}
export function fetchJobLogList(params?: FlowApi.JobLog.JobLogSearchParams) {
return request<FlowApi.JobLog.JobLogList>({
url: '/job/log/list',
method: 'get',
params
});
}
export function fetchJobDetail(id: string) {
return request<Flow.JobTaskType>({
url: `/job/${id}`,
method: 'get'
});
}
export function fetchBatchDetail(id: string) {
return request<Flow.JobBatchType>({
url: `/job/batch/${id}`,
method: 'get'
});
}
export function fetchTaskList(params: { groupName: string; taskBatchId: string; page?: number; pageSize?: number }) {
return request<Flow.JobBatchPage>({
url: '/job/task/list',
method: 'get',
params
});
}
export function fetchNodeRetry(nodeId: string, taskBatchId: string) {
return request<null>({
url: `/workflow/node/retry/${nodeId}/${taskBatchId}`,
@ -60,3 +90,10 @@ export function fetchWorkflowBatchInfo(id: string) {
method: 'get'
});
}
export function fetchWorkflowNodeRetry(id: string, workflowNodeId: number) {
return request<null>({
url: `/workflow/node/retry/${workflowNodeId}/${id}`,
method: 'get'
});
}

View File

@ -0,0 +1,376 @@
<script setup lang="tsx">
import { nextTick, ref, useSlots, watch } from 'vue';
import type { DataTableColumn } from 'naive-ui';
import { NButton, NTag } from 'naive-ui';
import { useFlowStore } from '../stores';
import { fetchBatchDetail, fetchJobDetail, fetchTaskList, fetchWorkflowNodeRetry } from '../api';
import { executorTypeRecord, operationReasonRecord, taskBatchStatusRecord } from '../constants/business';
import { $t } from '../locales';
import LogDrawer from './log-drawer.vue';
defineOptions({
name: 'DetailCard'
});
interface Props {
id?: string;
ids?: string[];
show?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
show: false,
ids: () => []
});
interface Emits {
(e: 'update:show', show: boolean): void;
}
const emit = defineEmits<Emits>();
const slots = useSlots();
const store = useFlowStore();
const visible = ref(false);
const logOpen = ref(false);
const spinning = ref(false);
const loading = ref(false);
const currentIndex = ref(1);
const jobData = ref<Flow.JobTaskType>({});
const dataSource = ref<Flow.JobBatchType[]>([]);
const pagination = ref({
page: 1,
pageCount: 0,
pageSize: 10,
showSizePicker: true,
pageSizes: [10, 15, 20, 25, 30],
onUpdatePage: async (page: number) => {
pagination.value.page = page;
const id = props.ids[currentIndex.value - 1];
getBatchDetail(id);
getRows(id, page);
},
onUpdatePageSize: async (pageSize: number) => {
pagination.value.pageSize = pageSize;
const id = props.ids[currentIndex.value - 1];
getBatchDetail(id);
getRows(id, pagination.value.page);
}
});
watch(
() => props.show,
val => {
visible.value = val;
if (val) {
onLoad();
}
},
{ immediate: true }
);
const onUpdateShow = () => {
emit('update:show', false);
};
async function getDetail(id: string) {
spinning.value = true;
const { data, error } = await fetchJobDetail(id);
if (!error) {
jobData.value = data;
spinning.value = false;
}
}
async function getBatchDetail(id: string) {
spinning.value = true;
const { data, error } = await fetchBatchDetail(id);
if (!error) {
jobData.value = data;
spinning.value = false;
}
}
async function getRows(id: string, page: number = 1) {
loading.value = true;
const { data, error } = await fetchTaskList({
groupName: store.groupName!,
taskBatchId: id ?? '0',
page,
pageSize: pagination.value.pageSize
});
if (!error) {
pagination.value.pageCount = data.total;
dataSource.value = data.data;
loading.value = false;
}
}
const idList = ref<string[]>([]);
function onLoad() {
idList.value = props.ids;
nextTick(() => {
if (props.ids.length > 0) {
getBatchDetail(props.ids[0]);
getRows(props.ids[0]);
} else if (props.id) {
idList.value = [jobData.value.taskBatchId!];
getDetail(props.id);
getRows(jobData.value.taskBatchId!);
}
});
}
const record = ref<Flow.JobTaskType>({});
const getLogRows = (task: Flow.JobTaskType) => {
record.value = task;
logOpen.value = true;
};
const retry = async (item: Flow.JobTaskType) => {
const { error } = await fetchWorkflowNodeRetry(store.id!, item.workflowNodeId!);
if (!error) {
window.$message?.success('执行重试成功');
}
};
const isRetry = (taskBatchStatus: number) => {
return taskBatchStatus === 4 || taskBatchStatus === 5 || taskBatchStatus === 6;
};
type ThemeColor = 'default' | 'error' | 'primary' | 'info' | 'success' | 'warning';
const columns = ref<DataTableColumn<Flow.JobBatchType>[]>([
{
key: 'index',
title: '日志',
align: 'center',
width: 64,
render: row => {
return (
<NButton type="info" text onClick={() => getLogRows(row)}>
<span class="w-28px ws-break-spaces">{`查看\n日志`}</span>
</NButton>
);
}
},
{
key: 'id',
title: $t('snail.jobBatch.jobTask.id'),
align: 'left',
minWidth: 64
},
{
key: 'groupName',
title: $t('snail.jobBatch.jobTask.groupName'),
align: 'left',
minWidth: 120
},
{
key: 'taskStatus',
title: $t('snail.jobBatch.jobTask.taskStatus'),
align: 'left',
minWidth: 80,
render: row => {
if (row.taskStatus === null) {
return undefined;
}
const label = $t(taskBatchStatusRecord[row.taskStatus!]);
const tagMap: Record<number, ThemeColor> = {
1: 'info',
2: 'info',
3: 'info',
4: 'error',
5: 'error',
6: 'error'
};
return <NTag type={tagMap[row.taskStatus!]}>{label}</NTag>;
}
},
{
key: 'clientInfo',
title: $t('snail.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('snail.jobBatch.jobTask.argsStr'),
align: 'left',
minWidth: 120
},
{
key: 'resultMessage',
title: $t('snail.jobBatch.jobTask.resultMessage'),
align: 'left',
minWidth: 120
},
{
key: 'retryCount',
title: $t('snail.jobBatch.jobTask.retryCount'),
align: 'left',
minWidth: 64
},
{
key: 'createDt',
title: $t('snail.jobBatch.jobTask.createDt'),
align: 'left',
minWidth: 120
}
]);
function tagColor(tagIndex: number) {
const tagMap: Record<number, ThemeColor> = {
0: 'error',
1: 'info',
2: 'success',
3: 'warning',
4: 'primary'
};
if (tagIndex === null || tagIndex < 0) {
return tagMap[1];
}
return tagMap[tagIndex % 5];
}
const onUpdatePage = (page: number) => {
currentIndex.value = page;
const id = props.ids[page - 1];
getBatchDetail(id);
getRows(id, page);
};
</script>
<template>
<NDrawer v-model:show="visible" :width="800" display-directive="if" @update:show="onUpdateShow">
<NDrawerContent title="任务批次详情" closable>
<NTabs v-if="idList && idList.length > 0" v-model:value="currentIndex" type="segment" animated>
<NTabPane v-for="(item, index) in idList" :key="index" :name="index + 1" :tab="item">
<NDescriptions label-placement="top" bordered :column="2">
<NDescriptionsItem :label="$t('snail.jobBatch.groupName')">{{ jobData?.groupName }}</NDescriptionsItem>
<NDescriptionsItem :label="$t('snail.jobBatch.jobName')">{{ jobData?.jobName }}</NDescriptionsItem>
<NDescriptionsItem :label="$t('snail.jobBatch.taskBatchStatus')">
<NTag v-if="jobData.taskBatchStatus" :type="tagColor(jobData?.taskBatchStatus!)">
{{ $t(taskBatchStatusRecord[jobData?.taskBatchStatus]) }}
</NTag>
</NDescriptionsItem>
<NDescriptionsItem :label="$t('snail.jobBatch.executionAt')">
{{ jobData?.executionAt }}
</NDescriptionsItem>
<NDescriptionsItem :label="$t('snail.jobBatch.operationReason')">
<NTag v-if="jobData.operationReason" :type="tagColor(jobData?.operationReason!)">
{{ $t(operationReasonRecord[jobData.operationReason]) }}
</NTag>
</NDescriptionsItem>
<NDescriptionsItem v-if="!slots.default" :label="$t('snail.jobBatch.executorType')">
<NTag v-if="jobData.executorType" :type="tagColor(jobData?.executorType!)">
{{ $t(executorTypeRecord[jobData?.executorType!]) }}
</NTag>
</NDescriptionsItem>
<NDescriptionsItem :label="$t('snail.jobBatch.executorInfo')" :span="2">
{{ jobData?.executorInfo }}
</NDescriptionsItem>
<NDescriptionsItem :label="$t('snail.jobBatch.createDt')" :span="2">
{{ jobData?.createDt }}
</NDescriptionsItem>
</NDescriptions>
<slot></slot>
<NCard
:bordered="false"
size="small"
class="sm:flex-1-hidden card-wrapper"
:content-style="{ padding: 0 }"
:header-style="{ padding: 0 }"
>
<template #header>
<div class="header-border"><span class="pl-12px">任务项列表</span></div>
</template>
<template #header-extra>
<NTooltip trigger="hover">
<template #trigger>
<NButton text @click="getRows(item)">
<icon-ant-design:sync-outlined class="mr-16px text-20px font-bold" />
</NButton>
</template>
刷新
</NTooltip>
<NTooltip v-if="isRetry(jobData.taskBatchStatus!)" trigger="hover">
<template #trigger>
<NButton text>
<icon-ant-design:redo-outlined class="text-20px font-bold" @click="retry(jobData)" />
</NButton>
</template>
重试
</NTooltip>
</template>
<NDataTable
:columns="columns"
:data="dataSource"
:loading="loading"
:scroll="{ x: 1200 }"
remote
:row-key="row => row.id"
:pagination="pagination"
class="sm:h-full"
/>
</NCard>
</NTabPane>
</NTabs>
<template v-if="ids && ids.length > 1" #footer>
<NPagination
v-model:page="currentIndex"
class="text-center"
:page-size="1"
:page-count="ids.length"
@update:page="onUpdatePage"
/>
</template>
</NDrawerContent>
</NDrawer>
<LogDrawer v-model:show="logOpen" title="日志详情" :task-data="record" />
</template>
<style scoped lang="scss">
.empty {
display: flex;
justify-content: center;
align-items: center;
height: calc(100% - 88px);
}
.header-border {
margin: 20px 0;
border-left: #1366ff 5px solid;
font-size: medium;
font-weight: bold;
}
:deep(.n-tabs-nav) {
display: none;
}
:deep(.n-tab-pane) {
padding-top: 0 !important;
}
</style>

View File

@ -0,0 +1,213 @@
<script setup lang="tsx">
import { NCollapse, NCollapseItem } from 'naive-ui';
import { defineComponent, onBeforeUnmount, ref, watch } from 'vue';
import { $t } from '../locales';
import { fetchJobLogList } from '../api';
defineOptions({
name: 'LogDrawer'
});
interface Props {
title?: string;
show?: boolean;
taskData: Flow.JobTaskType;
}
const props = withDefaults(defineProps<Props>(), {
title: $t('node.log.title'),
show: false,
modelValue: () => []
});
interface Emits {
(e: 'update:show', show: boolean): void;
}
const emit = defineEmits<Emits>();
const visible = defineModel<boolean>('visible', {
default: true
});
const ThrowableComponent = defineComponent({
props: {
throwable: String
},
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>
);
};
}
});
watch(
() => props.show,
val => {
visible.value = val;
if (val) {
getLogList();
}
},
{ immediate: true }
);
const onUpdateShow = (value: boolean) => {
emit('update:show', value);
};
const logList = ref<FlowApi.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();
});
function timestampToDate(timestamp: string): string {
const date = new Date(Number.parseInt(timestamp.toString(), 10));
const year = date.getFullYear();
const month =
(date.getMonth() + 1).toString().length === 1 ? `0${date.getMonth() + 1}` : (date.getMonth() + 1).toString();
const day = date.getDate().toString().length === 1 ? `0${date.getDate()}` : date.getDate().toString();
const hours = date.getHours().toString().length === 1 ? `0${date.getHours()}` : date.getHours().toString();
const minutes = date.getMinutes().toString().length === 1 ? `0${date.getMinutes()}` : date.getMinutes().toString();
const seconds = date.getSeconds().toString().length === 1 ? `0${date.getSeconds()}` : date.getSeconds().toString();
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${date.getMilliseconds()}`;
}
</script>
<template>
<NDrawer v-model:show="visible" width="100%" display-directive="if" @update:show="onUpdateShow">
<NDrawerContent :title="title" closable>
<div class="snail-log bg-#fafafc p-16px dark:bg-#000">
<div class="snail-log-scrollbar">
<code>
<pre
v-for="(message, index) in logList"
:key="index"
><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>
</code>
</div>
</div>
</NDrawerContent>
</NDrawer>
</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>

View File

@ -113,3 +113,28 @@ export const taskBatchStatusRecord: Record<Flow.TaskBatchStatus, FlowI18n.I18nKe
};
export const taskBatchStatusOptions = transformRecordToOption(taskBatchStatusRecord);
export const operationReasonRecord: Record<Flow.OperationReason, FlowI18n.I18nKey> = {
0: 'snail.enum.jobOperationReason.none',
1: 'snail.enum.jobOperationReason.taskExecutionTimeout',
2: 'snail.enum.jobOperationReason.notClient',
3: 'snail.enum.jobOperationReason.closed',
4: 'snail.enum.jobOperationReason.discard',
5: 'snail.enum.jobOperationReason.overlay',
6: 'snail.enum.jobOperationReason.notExecutionTask',
7: 'snail.enum.jobOperationReason.taskExecutionError',
8: 'snail.enum.jobOperationReason.mannerStop',
9: 'snail.enum.jobOperationReason.workflowConditionNodeExecutionError',
10: 'snail.enum.jobOperationReason.jobTaskInterrupted',
11: 'snail.enum.jobOperationReason.workflowCallbackNodeExecutionError',
12: 'snail.enum.jobOperationReason.workflowNodeNoRequired',
13: 'snail.enum.jobOperationReason.workflowNodeClosedSkipExecution',
14: 'snail.enum.jobOperationReason.workflowDecisionFailed'
};
export const operationReasonOptions = transformRecordToOption(operationReasonRecord);
export const executorTypeRecord: Record<Flow.ExecutorType, FlowI18n.I18nKey> = {
1: 'snail.enum.executorType.java'
};
export const executorTypeRecordOptions = transformRecordToOption(executorTypeRecord);

View File

@ -7,6 +7,7 @@ const local: FlowI18n.Schema = {
retry: 'Retry',
ignore: 'Ignore',
stop: 'Stop',
refresh: 'Refresh',
form: {
groupName: 'Please select group',
workflowTip: 'Please configure workflow',
@ -14,6 +15,26 @@ const local: FlowI18n.Schema = {
stopMessage: 'Stop mission successful',
taskTip: 'Please select task'
},
jobBatch: {
groupName: 'Group name',
jobName: 'Job name',
executorInfo: 'Executor Name',
executorType: 'Executor type',
executionAt: 'Start execution time',
taskBatchStatus: 'Task Batch Status',
operationReason: 'Reason for operation',
createDt: 'Create time',
jobTask: {
id: 'ID',
groupName: 'Group name',
taskStatus: 'Status',
clientInfo: 'Client address',
argsStr: 'Argument string',
resultMessage: 'Result message',
retryCount: 'Number of retries',
createDt: 'Create time'
}
},
enum: {
failStrategy: {
skip: 'Skip',
@ -41,6 +62,26 @@ const local: FlowI18n.Schema = {
triggerType: {
time: 'Fixed Time',
cron: 'CRON Expressions'
},
jobOperationReason: {
none: 'None',
taskExecutionTimeout: 'Task execution timeout',
notClient: 'No client',
closed: 'Job closed',
discard: 'Job discard',
overlay: 'Job overlapped',
notExecutionTask: 'No execution task',
taskExecutionError: 'Execution error',
mannerStop: 'Manual stop',
workflowConditionNodeExecutionError: 'Condition node execution error',
jobTaskInterrupted: 'Job interrupted',
workflowCallbackNodeExecutionError: 'Callback node execution error',
workflowNodeNoRequired: 'No process required',
workflowNodeClosedSkipExecution: 'Node closed, skip execution',
workflowDecisionFailed: 'Workflow decision failed'
},
executorType: {
java: 'Java'
}
}
},
@ -79,7 +120,10 @@ const local: FlowI18n.Schema = {
webhookTip: 'Please configure callback notifications'
}
},
endNode: 'End Node'
endNode: 'End Node',
log: {
title: 'Log Detail'
}
}
};

View File

@ -7,6 +7,7 @@ const local: FlowI18n.Schema = {
retry: '重试',
ignore: '忽略',
stop: '停止',
refresh: '刷新',
form: {
groupName: '请选择组',
workflowTip: '请配置工作流',
@ -14,6 +15,26 @@ const local: FlowI18n.Schema = {
stopMessage: '停止任务成功',
taskTip: '请选择任务'
},
jobBatch: {
groupName: '组名称',
jobName: '任务名称',
executorInfo: '执行器名称',
executorType: '执行器类型',
executionAt: '开始执行时间',
taskBatchStatus: '状态',
operationReason: '操作原因',
createDt: '创建时间',
jobTask: {
id: 'ID',
groupName: '组名称',
taskStatus: '状态',
clientInfo: '地址',
argsStr: '参数',
resultMessage: '结果',
retryCount: '重试次数',
createDt: '开始执行时间'
}
},
enum: {
failStrategy: {
skip: '跳过',
@ -41,6 +62,26 @@ const local: FlowI18n.Schema = {
triggerType: {
time: '固定时间',
cron: 'CRON 表达式'
},
jobOperationReason: {
none: '无',
taskExecutionTimeout: '任务执行超时',
notClient: '无客户端节点',
closed: '任务已关闭',
discard: '任务丢弃',
overlay: '任务被覆盖',
notExecutionTask: '无可执行任务项',
taskExecutionError: '任务执行期间发生非预期异常',
mannerStop: '手动停止',
workflowConditionNodeExecutionError: '条件节点执行异常',
jobTaskInterrupted: '任务中断',
workflowCallbackNodeExecutionError: '回调节点执行异常',
workflowNodeNoRequired: '无需处理',
workflowNodeClosedSkipExecution: '节点关闭跳过执行',
workflowDecisionFailed: '判定未通过'
},
executorType: {
java: 'Java'
}
}
},
@ -77,7 +118,10 @@ const local: FlowI18n.Schema = {
webhookTip: '请配置回调通知'
}
},
endNode: '流程结束'
endNode: '流程结束',
log: {
title: '日志详情'
}
}
};

View File

@ -7,6 +7,7 @@ import { $t } from '../locales';
import { failStrategyRecord, taskBatchStatusEnum } from '../constants/business';
import TaskDrawer from '../drawer/task-drawer.vue';
import TaskDetail from '../detail/task-detail.vue';
import DetailCard from '../components/detail-card.vue';
import AddNode from './add-node.vue';
defineOptions({
@ -271,7 +272,7 @@ const isStop = (taskBatchStatus: number) => {
v-model:len="nodeConfig.conditionNodes!.length"
@save="save"
/>
<!-- <DetailCard v-if="store.type !== 0 && cardDrawer" :id="detailId" v-model:open="cardDrawer" :ids="detailIds" /> -->
<DetailCard v-if="store.type !== 0" :id="detailId" v-model:show="cardDrawer" :ids="detailIds" />
</div>
</template>

View File

@ -5,4 +5,48 @@ declare namespace FlowApi {
id: string;
jobName: string;
};
/**
* namespace JobLog
*
* backend api module: "JobLog"
*/
namespace JobLog {
type JobLevel = 'INFO' | 'WARN' | 'ERROR' | 'DEBUG';
type JobLogSearchParams = {
taskBatchId: string;
jobId: string;
taskId: string;
} & LogSearchParams;
type RetryLogSearchParams = {
groupName: string;
uniqueId: string;
} & LogSearchParams;
type LogSearchParams = {
startId: string;
fromIndex: number;
size: number;
};
type JobLogList = {
finished: boolean;
fromIndex: number;
message: JobMessage[];
nextStartId: string;
};
type JobMessage = {
level: JobLevel;
host: string;
port: string;
location: string;
message: string;
thread: string;
['time_stamp']: string;
throwable: string;
};
}
}

View File

@ -7,6 +7,8 @@ declare namespace Flow {
type ContentType = 1 | 2;
type TriggerType = 2 | 3;
type WorkFlowNodeStatus = 0 | 1;
type ExecutorType = 1;
type OperationReason = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14;
/** 组 */
export type NodeDataType = {
@ -121,4 +123,80 @@ declare namespace Flow {
/** 图标 */
icon: string;
};
/** 定时任务详情 */
export type JobTaskType = {
/** 定时任务 ID */
id?: string;
/** 组名称 */
groupName?: string;
/** 任务信息 ID */
jobId?: string;
/** 任务名称 */
jobName?: string;
/** 节点名称 */
nodeName?: string;
/** 任务实例 ID */
taskBatchId?: string;
/** 状态 */
jobStatus?: number;
/** 状态 */
taskBatchStatus?: Flow.TaskBatchStatus;
/** 执行器类型 */
executorType?: ExecutorType;
/** 操作原因 */
operationReason?: OperationReason;
/** 开始执行时间 */
executionAt?: string;
/** 执行器名称 */
executorInfo?: string;
/** 创建时间 */
createDt?: string;
/** 工作流节点ID */
workflowNodeId?: number;
/** 工作流任务批次ID */
workflowTaskBatchId?: number;
};
/** 任务项列表 */
export type JobBatchType = {
/** ID */
id?: string;
/** 任务 ID */
jobId?: string;
/** 组名称 */
groupName?: string;
/** 地址 */
clientInfo?: string;
/** 参数 */
argsStr?: string;
/** 结果 */
resultMessage?: string;
/** 重试次数 */
retryCount?: string;
/** 开始执行时间 */
createDt?: string;
/** 任务批次 ID */
taskBatchId?: string;
/** 任务状态 ID */
taskStatus?: TaskBatchStatus;
};
export type JobBatchPage = {
total: number;
data: JobTaskType[];
};
/** 任务日志 */
export type JobLogType = {};
/** Tag */
export type JobTagType = {
[key: number | string]: {
/** 名称 */
name: string;
/** 颜色 */
color: string;
};
};
}

View File

@ -9,6 +9,7 @@ declare namespace FlowI18n {
retry: string;
ignore: string;
stop: string;
refresh: string;
failStrategy: string;
form: {
groupName: string;
@ -17,6 +18,26 @@ declare namespace FlowI18n {
stopMessage: string;
taskTip: string;
};
jobBatch: {
groupName: string;
jobName: string;
executorInfo: string;
executorType: string;
executionAt: string;
taskBatchStatus: string;
operationReason: string;
createDt: string;
jobTask: {
id: string;
groupName: string;
taskStatus: string;
clientInfo: string;
argsStr: string;
resultMessage: string;
retryCount: string;
createDt: string;
};
};
enum: {
failStrategy: {
skip: string;
@ -45,6 +66,26 @@ declare namespace FlowI18n {
time: string;
cron: string;
};
jobOperationReason: {
none: string;
taskExecutionTimeout: string;
notClient: string;
closed: string;
discard: string;
overlay: string;
notExecutionTask: string;
taskExecutionError: string;
mannerStop: string;
workflowConditionNodeExecutionError: string;
jobTaskInterrupted: string;
workflowCallbackNodeExecutionError: string;
workflowNodeNoRequired: string;
workflowNodeClosedSkipExecution: string;
workflowDecisionFailed: string;
};
executorType: {
java: string;
};
};
};
node: {
@ -81,6 +122,9 @@ declare namespace FlowI18n {
};
};
endNode: string;
log: {
title: string;
};
};
};

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref, watch } from 'vue';
import NodeWrap from './components/node-wrap.vue';
import StartNode from './components/start-node.vue';
import NodeWrap from './node/node-wrap.vue';
import StartNode from './node/start-node.vue';
import { $t } from './locales';
defineOptions({

View File

@ -5,7 +5,7 @@ import { localStg } from '@/utils/storage';
import systemLogo from '@/assets/svg-icon/logo.svg?raw';
export function setupLoading() {
const themeColor = localStg.get('themeColor') || '#22aae3';
const themeColor = localStg.get('themeColor') || '#1366FF';
const { r, g, b } = getRgb(themeColor);