322 lines
10 KiB
Vue
322 lines
10 KiB
Vue
<script setup lang="ts">
|
|
import { nextTick, ref, watch } from 'vue';
|
|
import { useMessage } from 'naive-ui';
|
|
import { fetchNodeRetry, fetchNodeStop } from '../api';
|
|
import { useFlowStore } from '../stores';
|
|
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({
|
|
name: 'TaskNode'
|
|
});
|
|
|
|
interface Props {
|
|
modelValue?: Flow.NodeModelType;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
disabled: false,
|
|
modelValue: () => ({})
|
|
});
|
|
|
|
interface Emits {
|
|
(e: 'update:modelValue', modelValue: Flow.NodeModelType): void;
|
|
}
|
|
|
|
const emit = defineEmits<Emits>();
|
|
|
|
const store = useFlowStore();
|
|
const message = useMessage();
|
|
const nodeConfig = ref<Flow.NodeModelType>({});
|
|
|
|
watch(
|
|
() => props.modelValue,
|
|
val => {
|
|
nodeConfig.value = val;
|
|
},
|
|
{ immediate: true, deep: true }
|
|
);
|
|
|
|
const addTerm = () => {
|
|
const len = nodeConfig.value.conditionNodes!.length + 1;
|
|
nodeConfig.value.conditionNodes?.push({
|
|
nodeName: `${$t('node.task.name')} ${len}`,
|
|
priorityLevel: len,
|
|
failStrategy: 1,
|
|
workflowNodeStatus: 1,
|
|
jobTask: {
|
|
jobId: undefined
|
|
}
|
|
});
|
|
emit('update:modelValue', nodeConfig.value);
|
|
};
|
|
|
|
const reData = (data: Flow.NodeModelType, addData: Flow.NodeModelType) => {
|
|
if (!data.childNode) {
|
|
data.childNode = addData;
|
|
} else {
|
|
reData(data.childNode, addData);
|
|
}
|
|
};
|
|
|
|
const delTerm = (currentIndex: number) => {
|
|
if (nodeConfig.value.conditionNodes!.length === 1) {
|
|
if (nodeConfig.value.childNode) {
|
|
if (nodeConfig.value.conditionNodes![0].childNode) {
|
|
reData(nodeConfig.value.conditionNodes![0].childNode, nodeConfig.value.childNode);
|
|
} else {
|
|
nodeConfig.value.conditionNodes![0].childNode = nodeConfig.value.childNode;
|
|
}
|
|
}
|
|
nextTick(() => {
|
|
emit('update:modelValue', nodeConfig.value.conditionNodes![0].childNode!);
|
|
});
|
|
} else {
|
|
nodeConfig.value.conditionNodes?.splice(currentIndex, 1);
|
|
}
|
|
};
|
|
|
|
const arrTransfer = (index: number, type: number = 1) => {
|
|
nodeConfig.value.conditionNodes![index] = nodeConfig.value.conditionNodes!.splice(
|
|
index + type,
|
|
1,
|
|
nodeConfig.value.conditionNodes![index]
|
|
)[0];
|
|
nodeConfig.value.conditionNodes?.map((item, i) => (item.priorityLevel = i + 1));
|
|
emit('update:modelValue', nodeConfig.value);
|
|
};
|
|
|
|
const index = ref<number>(0);
|
|
const drawer = ref<boolean>(false);
|
|
const form = ref<Flow.ConditionNodeType>({});
|
|
|
|
const save = (val: Flow.ConditionNodeType) => {
|
|
const oldLevel = nodeConfig.value.conditionNodes![index.value].priorityLevel;
|
|
const newLevel = val.priorityLevel;
|
|
nodeConfig.value.conditionNodes![index.value] = val;
|
|
if (oldLevel !== newLevel) {
|
|
arrTransfer(index.value, newLevel! - oldLevel!);
|
|
}
|
|
emit('update:modelValue', nodeConfig.value);
|
|
};
|
|
|
|
const show = (currentIndex: number) => {
|
|
if (!props.disabled) {
|
|
index.value = currentIndex;
|
|
form.value = JSON.parse(JSON.stringify(nodeConfig.value.conditionNodes![currentIndex]));
|
|
drawer.value = true;
|
|
}
|
|
};
|
|
|
|
const getClass = (item: Flow.ConditionNodeType) => {
|
|
if (props.disabled) {
|
|
if (store.type === 2) {
|
|
return `node-error node-error-${
|
|
item.taskBatchStatus && taskBatchStatusEnum[item.taskBatchStatus]
|
|
? taskBatchStatusEnum[item.taskBatchStatus].name
|
|
: 'default'
|
|
}`;
|
|
}
|
|
return 'node-error';
|
|
}
|
|
return 'auto-judge-def auto-judge-hover';
|
|
};
|
|
|
|
const detailId = ref<string>();
|
|
const detailIds = ref<string[]>();
|
|
const cardDrawer = ref(false);
|
|
const detailDrawer = ref<boolean[]>([]);
|
|
|
|
const showDetail = (node: Flow.ConditionNodeType, detailIndex: number) => {
|
|
detailIds.value = [];
|
|
if (store.type === 2) {
|
|
node.jobBatchList
|
|
?.sort((a, b) => a.taskBatchStatus - b.taskBatchStatus)
|
|
.forEach(item => {
|
|
if (item.id) {
|
|
detailIds.value?.push(item.id);
|
|
} else if (item.jobId) {
|
|
detailId.value = item.jobId.toString();
|
|
}
|
|
});
|
|
if (node.jobTask?.jobId) {
|
|
detailId.value = node.jobTask?.jobId.toString();
|
|
}
|
|
cardDrawer.value = true;
|
|
} else if (store.type === 1) {
|
|
detailDrawer.value[detailIndex] = true;
|
|
} else {
|
|
show(detailIndex);
|
|
}
|
|
};
|
|
|
|
const retry = async (node: Flow.ConditionNodeType) => {
|
|
const { error } = await fetchNodeRetry(node.id!, store.id!);
|
|
if (!error) {
|
|
message.success($t('snail.form.executeMessage'));
|
|
}
|
|
};
|
|
|
|
const stop = async (node: Flow.ConditionNodeType) => {
|
|
const { error } = await fetchNodeStop(node.id!, store.id!);
|
|
if (!error) {
|
|
message.success($t('snail.form.stopMessage'));
|
|
}
|
|
};
|
|
|
|
const isRetry = (taskBatchStatus: number) => {
|
|
return taskBatchStatus === 4 || taskBatchStatus === 5 || taskBatchStatus === 6;
|
|
};
|
|
|
|
const isStop = (taskBatchStatus: number) => {
|
|
return taskBatchStatus === 1 || taskBatchStatus === 2;
|
|
};
|
|
|
|
const isShow = (taskBatchStatus: number) => {
|
|
return isRetry(taskBatchStatus!) || isStop(taskBatchStatus!);
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="node-wrap">
|
|
<div class="branch-box">
|
|
<NButton v-if="!disabled" class="add-branch" strong type="success" @click="addTerm">
|
|
{{ $t('node.task.add') }}
|
|
</NButton>
|
|
<div v-for="(item, i) in nodeConfig.conditionNodes" :key="i" class="col-box">
|
|
<div class="condition-node">
|
|
<div class="condition-node-box">
|
|
<NPopover :disabled="store.type !== 2 || !isShow(item.taskBatchStatus!)">
|
|
<div class="popover">
|
|
<NButton v-if="isRetry(item.taskBatchStatus!)" text @click="retry(item!)">
|
|
<span class="popover-item">
|
|
<icon-ant-design:redo-outlined class="mb-3px text-24px font-bold" />
|
|
{{ $t('snail.retry') }}
|
|
</span>
|
|
</NButton>
|
|
<NDivider v-if="isStop(item.taskBatchStatus!) && isRetry(item.taskBatchStatus!)" vertical />
|
|
<NButton v-if="isStop(item.taskBatchStatus!)" text class="popover-item" @click="stop(item!)">
|
|
<icon-ant-design:stop-outlined />
|
|
<span>{{ $t('snail.stop') }}</span>
|
|
</NButton>
|
|
</div>
|
|
<template #trigger>
|
|
<div class="auto-judge cursor-pointer" :class="getClass(item)" @click="showDetail(item!, i)">
|
|
<div v-if="i != 0" class="sort-left" @click.stop="arrTransfer(i, -1)">
|
|
<icon-ant-design:left-outlined />
|
|
</div>
|
|
<div class="title">
|
|
<span class="text color-#3296fa">
|
|
<NBadge processing dot :color="item.workflowNodeStatus === 1 ? '#52c41a' : '#ff4d4f'" />
|
|
{{ item.nodeName }}
|
|
</span>
|
|
<span class="priority-title">{{ $t('node.priority') }}{{ item.priorityLevel }}</span>
|
|
<icon-ant-design:close-outlined v-if="!disabled" class="close" @click.stop="delTerm(i)" />
|
|
</div>
|
|
<div class="content min-h-72px">
|
|
<div v-if="!item.jobTask?.jobId" class="placeholder">{{ $t('snail.form.taskTip') }}</div>
|
|
<template v-if="item.jobTask?.jobId">
|
|
<div>
|
|
<span class="content_label">{{ $t('snail.taskName') }}: </span>
|
|
<NEllipsis class="max-w-123px">
|
|
{{ `${item.jobTask?.jobName}(${item.jobTask?.jobId})` }}
|
|
</NEllipsis>
|
|
</div>
|
|
<div>
|
|
<span class="content_label">{{ $t('snail.failStrategy') }}: </span>
|
|
{{ $t(failStrategyRecord[item.failStrategy!]) }}
|
|
</div>
|
|
<div>.........</div>
|
|
</template>
|
|
</div>
|
|
<div
|
|
v-if="i != nodeConfig.conditionNodes!.length - 1"
|
|
class="sort-right"
|
|
@click.stop="arrTransfer(i)"
|
|
>
|
|
<icon-ant-design:right-outlined />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</NPopover>
|
|
<NTooltip v-if="store.type === 2 && item.taskBatchStatus">
|
|
<template #trigger>
|
|
<div
|
|
class="task-error-tip text-24px"
|
|
:style="{ color: taskBatchStatusEnum[item.taskBatchStatus!].color }"
|
|
>
|
|
<SvgIcon :icon="taskBatchStatusEnum[item.taskBatchStatus!].icon" />
|
|
</div>
|
|
</template>
|
|
{{ taskBatchStatusEnum[item.taskBatchStatus!].title }}
|
|
</NTooltip>
|
|
<AddNode v-model="item.childNode!" :disabled="disabled"></AddNode>
|
|
</div>
|
|
</div>
|
|
<slot v-if="item.childNode" :node="item"></slot>
|
|
<div v-if="i == 0" class="top-left-cover-line"></div>
|
|
<div v-if="i == 0" class="bottom-left-cover-line"></div>
|
|
<div v-if="i == nodeConfig.conditionNodes!.length - 1" class="top-right-cover-line"></div>
|
|
<div v-if="i == nodeConfig.conditionNodes!.length - 1" class="bottom-right-cover-line"></div>
|
|
|
|
<TaskDetail
|
|
v-if="store.type !== 0"
|
|
v-model:open="detailDrawer[i]"
|
|
:model-value="nodeConfig.conditionNodes![i]"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<AddNode v-if="nodeConfig.conditionNodes!.length > 1" v-model="nodeConfig.childNode!" :disabled="disabled" />
|
|
|
|
<TaskDrawer
|
|
v-if="store.type === 0"
|
|
v-model:open="drawer"
|
|
v-model="form"
|
|
v-model:len="nodeConfig.conditionNodes!.length"
|
|
@save="save"
|
|
/>
|
|
<DetailCard v-if="store.type !== 0" :id="detailId" v-model:show="cardDrawer" :ids="detailIds" />
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.task-error-tip {
|
|
cursor: default;
|
|
position: absolute;
|
|
top: 63px;
|
|
left: 291px;
|
|
font-size: 24px;
|
|
}
|
|
|
|
.popover {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-around;
|
|
|
|
.popover-item {
|
|
height: 42px;
|
|
display: flex;
|
|
font-size: 13px;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-direction: column;
|
|
text-align: center;
|
|
|
|
span {
|
|
margin-inline-start: 0;
|
|
}
|
|
|
|
.anticon {
|
|
font-size: 22px;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
}
|
|
}
|
|
</style>
|