Trim legacy workflow node runtime metadata
This commit is contained in:
-19
@@ -474,25 +474,6 @@ const availableNodes = computed(() => {
|
|||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'text_to_sql': {
|
|
||||||
const textToSqlOutputVar =
|
|
||||||
node.data.output_variable || 'text_to_sql_result';
|
|
||||||
variables = [
|
|
||||||
{
|
|
||||||
key: `${textToSqlOutputVar}_sql`,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.generatedSqlStatement',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: `${textToSqlOutputVar}_thought`,
|
|
||||||
label: $t(
|
|
||||||
'ai-platform.workflow.editor.variableSelector.sqlThought',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
default: {
|
||||||
// 其他未知节点类型,尝试从 output_variable 获取
|
// 其他未知节点类型,尝试从 output_variable 获取
|
||||||
if (node.data.output_variable) {
|
if (node.data.output_variable) {
|
||||||
|
|||||||
@@ -10,8 +10,13 @@ import { ElTooltip } from 'element-plus';
|
|||||||
|
|
||||||
import NodeResultCard from '../components/NodeResultCard.vue';
|
import NodeResultCard from '../components/NodeResultCard.vue';
|
||||||
|
|
||||||
|
type NodeColorConfig = {
|
||||||
|
headerBg: string;
|
||||||
|
iconBg: string;
|
||||||
|
iconColor: string;
|
||||||
|
};
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
// 新增:完整的执行结果(用于节点下方展示)
|
|
||||||
executionResult?: NodeExecutionResult;
|
executionResult?: NodeExecutionResult;
|
||||||
executionStatus?: {
|
executionStatus?: {
|
||||||
elapsed_time?: number;
|
elapsed_time?: number;
|
||||||
@@ -29,27 +34,11 @@ const props = defineProps<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const addButtonRef = ref<HTMLElement | null>(null);
|
const addButtonRef = ref<HTMLElement | null>(null);
|
||||||
|
const onNodeAddClick = inject<
|
||||||
|
(nodeId: string, position: { x: number; y: number }) => void
|
||||||
|
>('onNodeAddClick');
|
||||||
|
|
||||||
// 从父组件注入的添加节点函数
|
const nodeColorConfigs: Record<string, NodeColorConfig> = {
|
||||||
const onNodeAddClick =
|
|
||||||
inject<(nodeId: string, position: { x: number; y: number }) => void>(
|
|
||||||
'onNodeAddClick',
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleClick = () => {
|
|
||||||
const rect = addButtonRef.value?.getBoundingClientRect();
|
|
||||||
if (rect && onNodeAddClick && props.nodeId) {
|
|
||||||
onNodeAddClick(props.nodeId, { x: rect.right + 10, y: rect.top });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 节点类型配色 - 支持 dark 模式,使用 Tailwind dark: 前缀
|
|
||||||
const nodeColorConfig = computed(() => {
|
|
||||||
const configs: Record<
|
|
||||||
string,
|
|
||||||
{ headerBg: string; iconBg: string; iconColor: string }
|
|
||||||
> = {
|
|
||||||
// 流程控制
|
|
||||||
start: {
|
start: {
|
||||||
headerBg:
|
headerBg:
|
||||||
'bg-gradient-to-r from-emerald-50 to-green-50 dark:from-emerald-950 dark:to-green-950',
|
'bg-gradient-to-r from-emerald-50 to-green-50 dark:from-emerald-950 dark:to-green-950',
|
||||||
@@ -80,8 +69,6 @@ const nodeColorConfig = computed(() => {
|
|||||||
iconBg: 'bg-slate-300 dark:bg-slate-700',
|
iconBg: 'bg-slate-300 dark:bg-slate-700',
|
||||||
iconColor: 'text-slate-600 dark:text-slate-400',
|
iconColor: 'text-slate-600 dark:text-slate-400',
|
||||||
},
|
},
|
||||||
|
|
||||||
// AI 相关
|
|
||||||
llm: {
|
llm: {
|
||||||
headerBg:
|
headerBg:
|
||||||
'bg-gradient-to-r from-violet-50 to-purple-50 dark:from-violet-950 dark:to-purple-950',
|
'bg-gradient-to-r from-violet-50 to-purple-50 dark:from-violet-950 dark:to-purple-950',
|
||||||
@@ -94,8 +81,6 @@ const nodeColorConfig = computed(() => {
|
|||||||
iconBg: 'bg-fuchsia-200 dark:bg-fuchsia-800',
|
iconBg: 'bg-fuchsia-200 dark:bg-fuchsia-800',
|
||||||
iconColor: 'text-fuchsia-600 dark:text-fuchsia-400',
|
iconColor: 'text-fuchsia-600 dark:text-fuchsia-400',
|
||||||
},
|
},
|
||||||
|
|
||||||
// 代码和模板
|
|
||||||
code: {
|
code: {
|
||||||
headerBg:
|
headerBg:
|
||||||
'bg-gradient-to-r from-orange-50 to-amber-50 dark:from-orange-950 dark:to-amber-950',
|
'bg-gradient-to-r from-orange-50 to-amber-50 dark:from-orange-950 dark:to-amber-950',
|
||||||
@@ -108,56 +93,12 @@ const nodeColorConfig = computed(() => {
|
|||||||
iconBg: 'bg-teal-200 dark:bg-teal-800',
|
iconBg: 'bg-teal-200 dark:bg-teal-800',
|
||||||
iconColor: 'text-teal-600 dark:text-teal-400',
|
iconColor: 'text-teal-600 dark:text-teal-400',
|
||||||
},
|
},
|
||||||
|
|
||||||
// HTTP
|
|
||||||
http: {
|
http: {
|
||||||
headerBg:
|
headerBg:
|
||||||
'bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-950 dark:to-indigo-950',
|
'bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-950 dark:to-indigo-950',
|
||||||
iconBg: 'bg-blue-200 dark:bg-blue-800',
|
iconBg: 'bg-blue-200 dark:bg-blue-800',
|
||||||
iconColor: 'text-blue-600 dark:text-blue-400',
|
iconColor: 'text-blue-600 dark:text-blue-400',
|
||||||
},
|
},
|
||||||
|
|
||||||
// 数据库
|
|
||||||
db_query: {
|
|
||||||
headerBg:
|
|
||||||
'bg-gradient-to-r from-indigo-50 to-blue-50 dark:from-indigo-950 dark:to-blue-950',
|
|
||||||
iconBg: 'bg-indigo-200 dark:bg-indigo-800',
|
|
||||||
iconColor: 'text-indigo-600 dark:text-indigo-400',
|
|
||||||
},
|
|
||||||
db_insert: {
|
|
||||||
headerBg:
|
|
||||||
'bg-gradient-to-r from-green-50 to-emerald-50 dark:from-green-950 dark:to-emerald-950',
|
|
||||||
iconBg: 'bg-green-200 dark:bg-green-800',
|
|
||||||
iconColor: 'text-green-600 dark:text-green-400',
|
|
||||||
},
|
|
||||||
db_update: {
|
|
||||||
headerBg:
|
|
||||||
'bg-gradient-to-r from-yellow-50 to-amber-50 dark:from-yellow-950 dark:to-amber-950',
|
|
||||||
iconBg: 'bg-yellow-200 dark:bg-yellow-800',
|
|
||||||
iconColor: 'text-yellow-600 dark:text-yellow-400',
|
|
||||||
},
|
|
||||||
db_delete: {
|
|
||||||
headerBg:
|
|
||||||
'bg-gradient-to-r from-red-50 to-rose-50 dark:from-red-950 dark:to-rose-950',
|
|
||||||
iconBg: 'bg-red-200 dark:bg-red-800',
|
|
||||||
iconColor: 'text-red-600 dark:text-red-400',
|
|
||||||
},
|
|
||||||
db_sql: {
|
|
||||||
headerBg:
|
|
||||||
'bg-gradient-to-r from-purple-50 to-violet-50 dark:from-purple-950 dark:to-violet-950',
|
|
||||||
iconBg: 'bg-purple-200 dark:bg-purple-800',
|
|
||||||
iconColor: 'text-purple-600 dark:text-purple-400',
|
|
||||||
},
|
|
||||||
|
|
||||||
// Text-to-SQL
|
|
||||||
text_to_sql: {
|
|
||||||
headerBg:
|
|
||||||
'bg-gradient-to-r from-cyan-50 to-teal-50 dark:from-cyan-950 dark:to-teal-950',
|
|
||||||
iconBg: 'bg-cyan-200 dark:bg-cyan-800',
|
|
||||||
iconColor: 'text-cyan-600 dark:text-cyan-400',
|
|
||||||
},
|
|
||||||
|
|
||||||
// 对话流
|
|
||||||
question: {
|
question: {
|
||||||
headerBg:
|
headerBg:
|
||||||
'bg-gradient-to-r from-sky-50 to-blue-50 dark:from-sky-950 dark:to-blue-950',
|
'bg-gradient-to-r from-sky-50 to-blue-50 dark:from-sky-950 dark:to-blue-950',
|
||||||
@@ -182,51 +123,46 @@ const nodeColorConfig = computed(() => {
|
|||||||
iconBg: 'bg-orange-200 dark:bg-orange-800',
|
iconBg: 'bg-orange-200 dark:bg-orange-800',
|
||||||
iconColor: 'text-orange-600 dark:text-orange-400',
|
iconColor: 'text-orange-600 dark:text-orange-400',
|
||||||
},
|
},
|
||||||
|
loop: {
|
||||||
// Snowflake
|
|
||||||
snowflake_cortex_llm: {
|
|
||||||
headerBg:
|
|
||||||
'bg-gradient-to-r from-cyan-50 to-blue-50 dark:from-cyan-950 dark:to-blue-950',
|
|
||||||
iconBg: 'bg-cyan-200 dark:bg-cyan-800',
|
|
||||||
iconColor: 'text-cyan-600 dark:text-cyan-400',
|
|
||||||
},
|
|
||||||
snowflake_cortex_analyst: {
|
|
||||||
headerBg:
|
headerBg:
|
||||||
'bg-gradient-to-r from-blue-50 to-cyan-50 dark:from-blue-950 dark:to-cyan-950',
|
'bg-gradient-to-r from-blue-50 to-cyan-50 dark:from-blue-950 dark:to-cyan-950',
|
||||||
iconBg: 'bg-blue-200 dark:bg-blue-800',
|
iconBg: 'bg-blue-200 dark:bg-blue-800',
|
||||||
iconColor: 'text-blue-600 dark:text-blue-400',
|
iconColor: 'text-blue-600 dark:text-blue-400',
|
||||||
},
|
},
|
||||||
|
|
||||||
// 子流程
|
|
||||||
subflow: {
|
subflow: {
|
||||||
headerBg:
|
headerBg:
|
||||||
'bg-gradient-to-r from-indigo-50 to-purple-50 dark:from-indigo-950 dark:to-purple-950',
|
'bg-gradient-to-r from-indigo-50 to-purple-50 dark:from-indigo-950 dark:to-purple-950',
|
||||||
iconBg: 'bg-indigo-200 dark:bg-indigo-800',
|
iconBg: 'bg-indigo-200 dark:bg-indigo-800',
|
||||||
iconColor: 'text-indigo-600 dark:text-indigo-400',
|
iconColor: 'text-indigo-600 dark:text-indigo-400',
|
||||||
},
|
},
|
||||||
|
|
||||||
// 知识库
|
|
||||||
knowledge_retrieval: {
|
knowledge_retrieval: {
|
||||||
headerBg:
|
headerBg:
|
||||||
'bg-gradient-to-r from-teal-50 to-cyan-50 dark:from-teal-950 dark:to-cyan-950',
|
'bg-gradient-to-r from-teal-50 to-cyan-50 dark:from-teal-950 dark:to-cyan-950',
|
||||||
iconBg: 'bg-teal-200 dark:bg-teal-800',
|
iconBg: 'bg-teal-200 dark:bg-teal-800',
|
||||||
iconColor: 'text-teal-600 dark:text-teal-400',
|
iconColor: 'text-teal-600 dark:text-teal-400',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
const defaultNodeColorConfig: NodeColorConfig = {
|
||||||
configs[props.nodeType || ''] || {
|
|
||||||
headerBg: 'bg-gradient-to-r from-card to-muted',
|
headerBg: 'bg-gradient-to-r from-card to-muted',
|
||||||
iconBg: 'bg-card border border-border',
|
iconBg: 'bg-card border border-border',
|
||||||
iconColor: 'text-primary',
|
iconColor: 'text-primary',
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
const rect = addButtonRef.value?.getBoundingClientRect();
|
||||||
|
if (rect && onNodeAddClick && props.nodeId) {
|
||||||
|
onNodeAddClick(props.nodeId, { x: rect.right + 10, y: rect.top });
|
||||||
}
|
}
|
||||||
);
|
};
|
||||||
});
|
|
||||||
|
const nodeColorConfig = computed(
|
||||||
|
() => nodeColorConfigs[props.nodeType || ''] || defaultNodeColorConfig,
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<!-- 节点主体 -->
|
|
||||||
<div
|
<div
|
||||||
class="bg-card group relative w-[280px] max-w-[280px] rounded-xl border shadow-sm transition-all duration-200 hover:shadow-md"
|
class="bg-card group relative w-[280px] max-w-[280px] rounded-xl border shadow-sm transition-all duration-200 hover:shadow-md"
|
||||||
:class="[
|
:class="[
|
||||||
@@ -243,7 +179,6 @@ const nodeColorConfig = computed(() => {
|
|||||||
!selected && !executionStatus?.status ? 'border-border' : '',
|
!selected && !executionStatus?.status ? 'border-border' : '',
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<!-- Status Badge (Absolute Top Right) -->
|
|
||||||
<div
|
<div
|
||||||
v-if="executionStatus"
|
v-if="executionStatus"
|
||||||
class="absolute -top-3 right-4 z-20 flex items-center gap-1 rounded-full border px-2 py-0.5 text-[10px] shadow-sm"
|
class="absolute -top-3 right-4 z-20 flex items-center gap-1 rounded-full border px-2 py-0.5 text-[10px] shadow-sm"
|
||||||
@@ -280,10 +215,11 @@ const nodeColorConfig = computed(() => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<span v-if="executionStatus.status === 'running'">Running...</span>
|
<span v-if="executionStatus.status === 'running'">Running...</span>
|
||||||
<span v-else-if="executionStatus.elapsed_time !== undefined">{{ executionStatus.elapsed_time }}ms</span>
|
<span v-else-if="executionStatus.elapsed_time !== undefined">
|
||||||
|
{{ executionStatus.elapsed_time }}ms
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Header -->
|
|
||||||
<div
|
<div
|
||||||
class="border-border/50 flex items-center gap-3 rounded-t-xl border-b px-4 py-3"
|
class="border-border/50 flex items-center gap-3 rounded-t-xl border-b px-4 py-3"
|
||||||
:class="nodeColorConfig.headerBg"
|
:class="nodeColorConfig.headerBg"
|
||||||
@@ -302,7 +238,6 @@ const nodeColorConfig = computed(() => {
|
|||||||
<slot name="desc"></slot>
|
<slot name="desc"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Token Usage -->
|
|
||||||
<div
|
<div
|
||||||
v-if="executionStatus?.tokens_used"
|
v-if="executionStatus?.tokens_used"
|
||||||
class="text-muted-foreground bg-muted border-border flex items-center rounded border px-1.5 py-0.5 text-[10px]"
|
class="text-muted-foreground bg-muted border-border flex items-center rounded border px-1.5 py-0.5 text-[10px]"
|
||||||
@@ -314,40 +249,34 @@ const nodeColorConfig = computed(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Body -->
|
|
||||||
<div class="text-muted-foreground min-w-0 space-y-2 overflow-hidden p-4 text-xs">
|
<div class="text-muted-foreground min-w-0 space-y-2 overflow-hidden p-4 text-xs">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Input Handles (Left) -->
|
|
||||||
<div v-if="inputs && inputs.length > 0">
|
<div v-if="inputs && inputs.length > 0">
|
||||||
<Handle
|
<Handle
|
||||||
v-for="(input, index) in inputs"
|
v-for="(input, index) in inputs"
|
||||||
:key="input.id"
|
|
||||||
type="target"
|
|
||||||
:position="Position.Left"
|
|
||||||
:id="input.id"
|
:id="input.id"
|
||||||
|
:key="input.id"
|
||||||
class="!bg-primary !z-20 !h-3 !w-3 !border-2 !border-white transition-transform hover:scale-125"
|
class="!bg-primary !z-20 !h-3 !w-3 !border-2 !border-white transition-transform hover:scale-125"
|
||||||
|
:position="Position.Left"
|
||||||
:style="{ top: `${((index + 1) * 100) / (inputs.length + 1)}%` }"
|
:style="{ top: `${((index + 1) * 100) / (inputs.length + 1)}%` }"
|
||||||
|
type="target"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- Default single input if not specified but needed logic can be handled in parent -->
|
|
||||||
|
|
||||||
<!-- Output Handles (Right) -->
|
|
||||||
<div v-if="outputs && outputs.length > 0">
|
<div v-if="outputs && outputs.length > 0">
|
||||||
<Handle
|
<Handle
|
||||||
v-for="(output, index) in outputs"
|
v-for="(output, index) in outputs"
|
||||||
:key="output.id"
|
|
||||||
type="source"
|
|
||||||
:position="Position.Right"
|
|
||||||
:id="output.id"
|
:id="output.id"
|
||||||
|
:key="output.id"
|
||||||
class="!bg-primary !z-20 !h-3 !w-3 !border-2 !border-white transition-transform hover:scale-125"
|
class="!bg-primary !z-20 !h-3 !w-3 !border-2 !border-white transition-transform hover:scale-125"
|
||||||
|
:position="Position.Right"
|
||||||
:style="{ top: `${((index + 1) * 100) / (outputs.length + 1)}%` }"
|
:style="{ top: `${((index + 1) * 100) / (outputs.length + 1)}%` }"
|
||||||
|
type="source"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 悬停时显示的添加按钮 - 放在节点更右侧,不覆盖 Handle -->
|
|
||||||
<!-- 分支类型节点(condition/intent/parallel/choice)不显示通用添加按钮,需要从具体分支 Handle 拖线 -->
|
|
||||||
<div
|
<div
|
||||||
v-if="
|
v-if="
|
||||||
nodeType !== 'end' &&
|
nodeType !== 'end' &&
|
||||||
@@ -357,25 +286,23 @@ const nodeColorConfig = computed(() => {
|
|||||||
"
|
"
|
||||||
ref="addButtonRef"
|
ref="addButtonRef"
|
||||||
class="bg-primary hover:bg-primary/90 absolute -right-8 top-1/2 flex size-6 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full text-white opacity-0 shadow-md transition-opacity hover:scale-110 group-hover:opacity-100"
|
class="bg-primary hover:bg-primary/90 absolute -right-8 top-1/2 flex size-6 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full text-white opacity-0 shadow-md transition-opacity hover:scale-110 group-hover:opacity-100"
|
||||||
title="{{ $t('ai-platform.workflow.nodes.common.addNextNode') }}"
|
:title="$t('ai-platform.workflow.nodes.common.addNextNode')"
|
||||||
@click.stop="handleClick"
|
@click.stop="handleClick"
|
||||||
>
|
>
|
||||||
<Plus class="pointer-events-none size-3.5" />
|
<Plus class="pointer-events-none size-3.5" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 节点下方的执行结果卡片(独立显示,不在节点内部) -->
|
|
||||||
<NodeResultCard
|
<NodeResultCard
|
||||||
v-if="executionResult"
|
v-if="executionResult"
|
||||||
:result="executionResult"
|
|
||||||
:node-type="nodeType"
|
:node-type="nodeType"
|
||||||
|
:result="executionResult"
|
||||||
class="mt-3"
|
class="mt-3"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 自定义 Handle 样式 */
|
|
||||||
:deep(.vue-flow__handle) {
|
:deep(.vue-flow__handle) {
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
@@ -383,7 +310,6 @@ const nodeColorConfig = computed(() => {
|
|||||||
background: hsl(var(--primary));
|
background: hsl(var(--primary));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 运行中边框呼吸动画 */
|
|
||||||
@keyframes pulse-border {
|
@keyframes pulse-border {
|
||||||
0%,
|
0%,
|
||||||
100% {
|
100% {
|
||||||
|
|||||||
Reference in New Issue
Block a user