1. 上游节点输出引用(核心功能)

- 新增 upstreamNodes computed:遍历当前选中节点的前驱链路,仅 model / http / form 三类节点被识别为有输出的节点
  - 新增 getNodeOutputFields,按节点类型提取可引用字段:
    - form → formConfig 的字段
    - http → response schema 的叶子字段
    - model → modelResponseBodyMapping(模型返回参数)的 key
    - 开始节点 → runFormFields(运行表单字段),从而让下游节点能引用"用户填的运行表单"作为输出

  2. 开始节点运行表单字段聚合
  - syncRunFormFields + 深监听:开始节点 formConfig 变化时自动聚合字段,存为 runFormFields,作为开始节点自身可被下游引用的输出

  3. 模型返回参数
  - handleModelConfirm 用 stripReadonlyFields 清理 requestBodyMapping 冗余字段,同时保存 modelResponseBodyMapping 供下游引用
  - 保存时 collectExposedFields 提取对外暴露字段存 modelFormFields

  4. 提示词 / 反向提示词
  - 保存 prompt、negativePrompt、runFormFields 到节点 DSL

  5. 其他
  - nodeTypes 用 markRaw 包裹 FlowNode,修复 Vue 警告
  - buildNodeFormConfigFromDsl 的 responseType 匹配改为兼容 key/value 两种取值
This commit is contained in:
2026-08-12 17:26:58 +08:00
parent 4e1cd284e9
commit 0e6381d2b3
9 changed files with 1661 additions and 50 deletions
@@ -0,0 +1,91 @@
<template>
<div class="model-params-form">
<div v-if="!hasParams" class="mpf-empty">
<el-empty description="该模型无请求参数配置" :image-size="80" />
</div>
<div v-else>
<div class="mpf-count">
{{ fieldCount }} 个参数<el-divider direction="vertical" />点击字段名旁图标查看说明
</div>
<div class="mpf-fields">
<ModelField
v-for="(def, key) in localParams"
:key="key"
:field-def="def"
:path="key"
:upstream-nodes="upstreamNodes"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch, nextTick } from 'vue';
import ModelField from './ModelField.vue';
import { deepClone, normalizeModelParams, stripReadonlyFields } from './modelParamUtils';
// 前驱节点的可引用输出字段(供「引用上级节点输出」)
interface UpstreamNodeInfo {
id: string;
label: string;
nodeCode: string;
outputFields: { field: string; label: string }[];
}
const props = defineProps<{ modelRequestParams: Record<string, any> | null; upstreamNodes?: UpstreamNodeInfo[] }>();
const emit = defineEmits<{ 'update:modelRequestParams': [Record<string, any> | null] }>();
const localParams = ref<Record<string, any>>({});
let internalSync = false; // 由 props 同步触发,避免 emit 循环
// props → 本地:深拷贝 + defaultValue 预填,避免直接改动父级引用
watch(
() => props.modelRequestParams,
(val) => {
internalSync = true;
// 剔除 isForm=false 的只读字段:不显示、不参与编辑与保存
localParams.value = stripReadonlyFields(normalizeModelParams(deepClone(val || {})));
nextTick(() => {
internalSync = false;
});
},
{ immediate: true, deep: true }
);
// 本地 → 父级:就地修改的叶子 value 会触发深度 watch
watch(
localParams,
(val) => {
if (internalSync) return;
emit('update:modelRequestParams', deepClone(val));
},
{ deep: true }
);
const hasParams = computed(() => Object.keys(localParams.value).length > 0);
const fieldCount = computed(() => Object.keys(localParams.value).length);
</script>
<style scoped lang="scss">
.model-params-form {
width: 100%;
.mpf-empty {
padding: 8px 0;
}
.mpf-count {
font-size: 12px;
color: #94a3b8;
margin-bottom: 10px;
line-height: 1.4;
}
.mpf-fields {
max-height: 420px;
overflow-y: auto;
padding-right: 4px;
}
}
</style>