fix: 展示 JavaScript 代码节点执行错误
- 配置脚本执行超时并保留可定位的 JavaScript 异常信息 - 在试运行步骤和终态结果中展示并展开失败节点
This commit is contained in:
@@ -37,11 +37,15 @@ watch(
|
||||
() => props.initSignal,
|
||||
() => {
|
||||
result.value = '';
|
||||
success.value = false;
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => props.pollingData,
|
||||
(newVal) => {
|
||||
if (!newVal) {
|
||||
return;
|
||||
}
|
||||
if (newVal.status === 20) {
|
||||
ElMessage.success($t('message.success'));
|
||||
result.value = newVal.result;
|
||||
@@ -83,7 +87,7 @@ function getResult(res: any) {
|
||||
</template>
|
||||
</ElRow>
|
||||
</div>
|
||||
<div v-if="finalNode.type !== 'endNode' && !success">
|
||||
<div v-if="!success && result">
|
||||
<ShowJson :value="result" />
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
CircleCloseFilled,
|
||||
SuccessFilled,
|
||||
VideoPause,
|
||||
WarningFilled,
|
||||
} from '@element-plus/icons-vue';
|
||||
import {
|
||||
ElAlert,
|
||||
@@ -35,7 +34,6 @@ const props = defineProps<WorkflowStepsProps>();
|
||||
const emit = defineEmits(['resume']);
|
||||
const nodes = ref<any[]>([]);
|
||||
const nodeStatusMap = ref<Record<string, any>>({});
|
||||
const isChainError = ref(false);
|
||||
const activeNames = ref<string[]>([]);
|
||||
const userControlledExpansion = ref(false);
|
||||
const confirmParams = ref<any>({});
|
||||
@@ -91,13 +89,14 @@ watch(
|
||||
return;
|
||||
}
|
||||
const currentNodes = newVal.nodes || {};
|
||||
if (newVal.status === 21) {
|
||||
isChainError.value = true;
|
||||
chainErrMsg.value = newVal.message;
|
||||
}
|
||||
chainErrMsg.value = newVal.status === 21 ? newVal.message : '';
|
||||
if (![20, 21].includes(newVal.status)) {
|
||||
confirmBtnLoading.value = false;
|
||||
}
|
||||
let autoExpandNodeId: string | undefined;
|
||||
const failedNodeId = Object.keys(currentNodes).find(
|
||||
(nodeId) => Number(currentNodes[nodeId]?.status) === 21,
|
||||
);
|
||||
for (const nodeId in currentNodes) {
|
||||
const previousNodeState = nodeStatusMap.value[nodeId];
|
||||
const currentNodeState = currentNodes[nodeId];
|
||||
@@ -113,7 +112,13 @@ watch(
|
||||
previousStatus !== currentStatus &&
|
||||
shouldAutoExpandStatus(currentStatus)
|
||||
) {
|
||||
activeNames.value = [nodeId];
|
||||
autoExpandNodeId = nodeId;
|
||||
}
|
||||
}
|
||||
if (!userControlledExpansion.value && !props.expandAll) {
|
||||
const nextActiveNodeId = failedNodeId || autoExpandNodeId;
|
||||
if (nextActiveNodeId) {
|
||||
activeNames.value = [nextActiveNodeId];
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -123,7 +128,6 @@ watch(
|
||||
() => props.initSignal,
|
||||
() => {
|
||||
nodeStatusMap.value = {};
|
||||
isChainError.value = false;
|
||||
confirmBtnLoading.value = false;
|
||||
chainErrMsg.value = '';
|
||||
userControlledExpansion.value = false;
|
||||
@@ -191,7 +195,13 @@ function handleConfirm(node: any) {
|
||||
<template>
|
||||
<div>
|
||||
<div class="mb-1">
|
||||
<ElAlert v-if="chainErrMsg" :title="chainErrMsg" type="error" />
|
||||
<ElAlert
|
||||
v-if="chainErrMsg"
|
||||
:closable="false"
|
||||
show-icon
|
||||
:title="chainErrMsg"
|
||||
type="error"
|
||||
/>
|
||||
</div>
|
||||
<ElCollapse
|
||||
v-model="activeNames"
|
||||
@@ -210,31 +220,16 @@ function handleConfirm(node: any) {
|
||||
{{ node.label }}
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<ElIcon
|
||||
v-if="node.status === 20 && !isChainError"
|
||||
color="green"
|
||||
size="20"
|
||||
>
|
||||
<ElIcon v-if="node.status === 20" color="green" size="20">
|
||||
<SuccessFilled />
|
||||
</ElIcon>
|
||||
<div v-if="node.status === 1" class="spinner"></div>
|
||||
<ElIcon
|
||||
v-if="node.status === 21 && !isChainError"
|
||||
color="red"
|
||||
size="20"
|
||||
>
|
||||
<ElIcon v-if="node.status === 21" color="red" size="20">
|
||||
<CircleCloseFilled />
|
||||
</ElIcon>
|
||||
<ElIcon
|
||||
v-if="node.status === 5 && !isChainError"
|
||||
color="orange"
|
||||
size="20"
|
||||
>
|
||||
<ElIcon v-if="node.status === 5" color="orange" size="20">
|
||||
<VideoPause />
|
||||
</ElIcon>
|
||||
<ElIcon v-if="isChainError" color="orange" size="20">
|
||||
<WarningFilled />
|
||||
</ElIcon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import ExecResult from '../ExecResult.vue';
|
||||
|
||||
vi.mock('#/locales', () => ({
|
||||
$t: (key: string) => key,
|
||||
}));
|
||||
|
||||
describe('execResult', () => {
|
||||
it('结束节点执行失败时展示工作流错误信息', async () => {
|
||||
const wrapper = mount(ExecResult, {
|
||||
props: {
|
||||
initSignal: false,
|
||||
nodeJson: [
|
||||
{
|
||||
original: {
|
||||
data: {
|
||||
outputDefs: [],
|
||||
},
|
||||
type: 'endNode',
|
||||
},
|
||||
},
|
||||
],
|
||||
pollingData: undefined,
|
||||
workflowId: 'workflow-1',
|
||||
},
|
||||
global: {
|
||||
stubs: {
|
||||
ShowJson: {
|
||||
props: ['value'],
|
||||
template: '<div data-test="show-json">{{ value }}</div>',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await wrapper.setProps({
|
||||
pollingData: {
|
||||
message: 'JavaScript 执行失败(第 3 行,第 5 列):boom',
|
||||
status: 21,
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.get('[data-test="show-json"]').text()).toContain(
|
||||
'JavaScript 执行失败(第 3 行,第 5 列):boom',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -134,4 +134,29 @@ describe('workflowSteps', () => {
|
||||
});
|
||||
expect(getCollapseItems(wrapper)[0]?.classes()).toContain('is-active');
|
||||
});
|
||||
|
||||
it('工作流失败时优先展开失败节点并展示错误信息', async () => {
|
||||
const wrapper = mountWorkflowSteps();
|
||||
|
||||
await wrapper.setProps({
|
||||
pollingData: {
|
||||
message: 'JavaScript 语法错误(第 2 行,第 3 列):Unexpected token',
|
||||
nodes: {
|
||||
'node-a': { result: { text: 'A 完成' }, status: 20 },
|
||||
'node-b': {
|
||||
message:
|
||||
'JavaScript 语法错误(第 2 行,第 3 列):Unexpected token',
|
||||
status: 21,
|
||||
},
|
||||
},
|
||||
status: 21,
|
||||
},
|
||||
});
|
||||
|
||||
expect(getCollapseItems(wrapper)[0]?.classes()).not.toContain('is-active');
|
||||
expect(getCollapseItems(wrapper)[1]?.classes()).toContain('is-active');
|
||||
expect(wrapper.text()).toContain(
|
||||
'JavaScript 语法错误(第 2 行,第 3 列):Unexpected token',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user