|
@@ -4,7 +4,7 @@ import { useRoute, useRouter } from 'vue-router'
|
|
|
import { VueFlow, useVueFlow } from '@vue-flow/core'
|
|
import { VueFlow, useVueFlow } from '@vue-flow/core'
|
|
|
import { Background } from '@vue-flow/background'
|
|
import { Background } from '@vue-flow/background'
|
|
|
import {
|
|
import {
|
|
|
- NInput, NSelect, NButton, NIcon, NCheckbox, useMessage
|
|
|
|
|
|
|
+ NInput, NSelect, NButton, NIcon, NCheckbox, NModal, useMessage
|
|
|
} from 'naive-ui'
|
|
} from 'naive-ui'
|
|
|
import {
|
|
import {
|
|
|
ArrowBackOutline, SaveOutline, ChatbubbleEllipsesOutline,
|
|
ArrowBackOutline, SaveOutline, ChatbubbleEllipsesOutline,
|
|
@@ -32,7 +32,7 @@ import ConditionNode from '../../components/workflow/nodes/ConditionNode.vue'
|
|
|
import SmartActionNode from '../../components/workflow/nodes/SmartActionNode.vue'
|
|
import SmartActionNode from '../../components/workflow/nodes/SmartActionNode.vue'
|
|
|
import VirtualGroupNode from '../../components/workflow/nodes/VirtualGroupNode.vue'
|
|
import VirtualGroupNode from '../../components/workflow/nodes/VirtualGroupNode.vue'
|
|
|
import CodeEditor from '../../components/skill/CodeEditor.vue'
|
|
import CodeEditor from '../../components/skill/CodeEditor.vue'
|
|
|
-import { inferNewEdge, refreshMappingsForNode, getNodeOutputs, getReachablePredecessors, IO_TYPES } from '../../utils/ioInference'
|
|
|
|
|
|
|
+import { inferNewEdge, refreshMappingsForNode, getNodeOutputs, getNodeInputs, getReachablePredecessors, getReachableSuccessors, IO_TYPES } from '../../utils/ioInference'
|
|
|
import { useVersionHistory } from '../../composables/useVersionHistory'
|
|
import { useVersionHistory } from '../../composables/useVersionHistory'
|
|
|
import { useWorkflowRunner } from '../../composables/useWorkflowRunner'
|
|
import { useWorkflowRunner } from '../../composables/useWorkflowRunner'
|
|
|
import { useNodeFields } from '../../composables/useNodeFields'
|
|
import { useNodeFields } from '../../composables/useNodeFields'
|
|
@@ -280,7 +280,14 @@ function getDefaultData(type) {
|
|
|
allGraphSources: false,
|
|
allGraphSources: false,
|
|
|
graphSourceIds: [],
|
|
graphSourceIds: [],
|
|
|
knowledgeBaseId: null,
|
|
knowledgeBaseId: null,
|
|
|
- topK: 5
|
|
|
|
|
|
|
+ topK: 5,
|
|
|
|
|
+ inputs: [],
|
|
|
|
|
+ outputs: [
|
|
|
|
|
+ { name: 'evidences', label: '检索证据列表', type: 'array', description: '命中的知识片段集合', mapping: null },
|
|
|
|
|
+ { name: 'evidenceCount', label: '证据数量', type: 'number', description: '命中证据条数', mapping: null },
|
|
|
|
|
+ { name: 'sourceType', label: '来源类型', type: 'string', description: 'DOCUMENT / STRUCTURED_DATA / GRAPH / HYBRID', mapping: null },
|
|
|
|
|
+ { name: 'diagnostics', label: '诊断信息', type: 'object', description: '检索过程的诊断信息', mapping: null }
|
|
|
|
|
+ ]
|
|
|
}
|
|
}
|
|
|
case 'condition': return { nodeName: '', label: '条件分支', conditions: [{ type: 'IF', expression: '' }] }
|
|
case 'condition': return { nodeName: '', label: '条件分支', conditions: [{ type: 'IF', expression: '' }] }
|
|
|
case 'output': return { nodeName: '', label: '输出', fields: [{ name: 'result', label: '输出结果', type: 'string', description: '' }] }
|
|
case 'output': return { nodeName: '', label: '输出', fields: [{ name: 'result', label: '输出结果', type: 'string', description: '' }] }
|
|
@@ -444,6 +451,112 @@ function clearInputMapping(input) {
|
|
|
onFieldChange('inputs', [...selectedData.value.inputs])
|
|
onFieldChange('inputs', [...selectedData.value.inputs])
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// ========== 字段引入对话框:从前驱/后继节点引入字段 ==========
|
|
|
|
|
+const importDialog = reactive({
|
|
|
|
|
+ open: false,
|
|
|
|
|
+ mode: null, // 'predecessor' | 'successor'
|
|
|
|
|
+ candidates: []
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 前驱输出候选项:用于"前置数据"面板的引入按钮
|
|
|
|
|
+const predecessorOutputCandidates = computed(() => {
|
|
|
|
|
+ if (!selectedNode.value) return []
|
|
|
|
|
+ const predecessors = getReachablePredecessors(selectedNode.value.id, getNodes.value, getEdges.value)
|
|
|
|
|
+ const result = []
|
|
|
|
|
+ for (const src of predecessors) {
|
|
|
|
|
+ for (const out of getNodeOutputs(src)) {
|
|
|
|
|
+ result.push({
|
|
|
|
|
+ nodeId: src.id,
|
|
|
|
|
+ nodeName: src.data?.nodeName || src.data?.label || src.id,
|
|
|
|
|
+ fieldName: out.name,
|
|
|
|
|
+ fieldLabel: out.label || out.name,
|
|
|
|
|
+ fieldType: out.type || 'string',
|
|
|
|
|
+ fieldDescription: out.description || ''
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return result
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 后继输入候选项:用于"输出变量"面板的引入按钮
|
|
|
|
|
+const successorInputCandidates = computed(() => {
|
|
|
|
|
+ if (!selectedNode.value) return []
|
|
|
|
|
+ const successors = getReachableSuccessors(selectedNode.value.id, getNodes.value, getEdges.value)
|
|
|
|
|
+ const result = []
|
|
|
|
|
+ for (const dst of successors) {
|
|
|
|
|
+ for (const inp of getNodeInputs(dst)) {
|
|
|
|
|
+ result.push({
|
|
|
|
|
+ nodeId: dst.id,
|
|
|
|
|
+ nodeName: dst.data?.nodeName || dst.data?.label || dst.id,
|
|
|
|
|
+ fieldName: inp.name,
|
|
|
|
|
+ fieldLabel: inp.label || inp.name,
|
|
|
|
|
+ fieldType: inp.type || 'string',
|
|
|
|
|
+ fieldDescription: inp.description || ''
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return result
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+function openImportDialog(mode) {
|
|
|
|
|
+ importDialog.mode = mode
|
|
|
|
|
+ importDialog.candidates = mode === 'predecessor' ? predecessorOutputCandidates.value : successorInputCandidates.value
|
|
|
|
|
+ importDialog.open = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 确认引入:从前驱输出导入到 inputs,或从后继输入导入到 outputs
|
|
|
|
|
+function confirmImportField(candidate) {
|
|
|
|
|
+ if (!selectedNode.value) return
|
|
|
|
|
+ if (importDialog.mode === 'predecessor') {
|
|
|
|
|
+ const newInput = {
|
|
|
|
|
+ name: candidate.fieldName,
|
|
|
|
|
+ label: candidate.fieldLabel || candidate.fieldName,
|
|
|
|
|
+ type: candidate.fieldType || 'string',
|
|
|
|
|
+ description: candidate.fieldDescription || '',
|
|
|
|
|
+ mapping: {
|
|
|
|
|
+ sourceNodeId: candidate.nodeId,
|
|
|
|
|
+ sourceField: candidate.fieldName,
|
|
|
|
|
+ sourcePath: null,
|
|
|
|
|
+ autoMapped: false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ const inputs = [...(selectedData.value?.inputs || []), newInput]
|
|
|
|
|
+ vfUpdateNode(selectedNode.value.id, { inputs })
|
|
|
|
|
+ } else if (importDialog.mode === 'successor') {
|
|
|
|
|
+ const newOutput = {
|
|
|
|
|
+ name: candidate.fieldName,
|
|
|
|
|
+ label: candidate.fieldLabel || candidate.fieldName,
|
|
|
|
|
+ type: candidate.fieldType || 'string',
|
|
|
|
|
+ description: candidate.fieldDescription || ''
|
|
|
|
|
+ }
|
|
|
|
|
+ const outputs = [...(selectedData.value?.outputs || []), newOutput]
|
|
|
|
|
+ vfUpdateNode(selectedNode.value.id, { outputs })
|
|
|
|
|
+ // 同步设置后继节点对应 input 的 mapping,指向本节点该输出字段
|
|
|
|
|
+ const successorNode = getNodes.value.find(n => n.id === candidate.nodeId)
|
|
|
|
|
+ if (successorNode) {
|
|
|
|
|
+ const succInputs = (successorNode.data?.inputs || []).map(inp => {
|
|
|
|
|
+ if (inp.name === candidate.fieldName) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ ...inp,
|
|
|
|
|
+ mapping: {
|
|
|
|
|
+ sourceNodeId: selectedNode.value.id,
|
|
|
|
|
+ sourceField: candidate.fieldName,
|
|
|
|
|
+ sourcePath: null,
|
|
|
|
|
+ autoMapped: false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return inp
|
|
|
|
|
+ })
|
|
|
|
|
+ vfUpdateNode(candidate.nodeId, { inputs: succInputs })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 刷新当前节点关联的边映射,并同步 selectedNode 引用
|
|
|
|
|
+ applyEdgeMappingUpdates(refreshMappingsForNode(selectedNode.value.id, getNodes.value, getEdges.value))
|
|
|
|
|
+ selectedNode.value = getNodes.value.find(n => n.id === selectedNode.value.id) || selectedNode.value
|
|
|
|
|
+ importDialog.open = false
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// ========== 节点名称编辑(含重名校验) ==========
|
|
// ========== 节点名称编辑(含重名校验) ==========
|
|
|
function updateNodeName(value) {
|
|
function updateNodeName(value) {
|
|
|
const trimmed = (value || '').trim()
|
|
const trimmed = (value || '').trim()
|
|
@@ -579,8 +692,7 @@ const {
|
|
|
addListField, removeListField,
|
|
addListField, removeListField,
|
|
|
addVariable, removeVariable,
|
|
addVariable, removeVariable,
|
|
|
addOutputField, removeOutputField,
|
|
addOutputField, removeOutputField,
|
|
|
- addElif, removeElif, onConditionChange,
|
|
|
|
|
- syncLlmFieldsFromTemplate
|
|
|
|
|
|
|
+ addElif, removeElif, onConditionChange
|
|
|
} = useNodeFields({
|
|
} = useNodeFields({
|
|
|
selectedNode,
|
|
selectedNode,
|
|
|
selectedData,
|
|
selectedData,
|
|
@@ -1254,7 +1366,7 @@ function applyGraphData(graphData) {
|
|
|
<div class="prop-header">
|
|
<div class="prop-header">
|
|
|
<span class="prop-label">前置数据</span>
|
|
<span class="prop-label">前置数据</span>
|
|
|
<div style="display:flex;gap:4px">
|
|
<div style="display:flex;gap:4px">
|
|
|
- <button class="text-btn" @click="syncLlmFieldsFromTemplate('inputs')">从模板同步</button>
|
|
|
|
|
|
|
+ <button class="text-btn" @click="openImportDialog('predecessor')">引入</button>
|
|
|
<button class="text-btn" @click="addLlmField('inputs')">+ 添加</button>
|
|
<button class="text-btn" @click="addLlmField('inputs')">+ 添加</button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -1262,19 +1374,21 @@ function applyGraphData(graphData) {
|
|
|
<div class="var-form-row">
|
|
<div class="var-form-row">
|
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
|
@update:value="nv => { v.name = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
@update:value="nv => { v.name = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
- <n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
|
|
- @update:value="nv => { v.type = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="var-form-row">
|
|
|
|
|
<n-input :value="v.label" placeholder="中文名" size="small"
|
|
<n-input :value="v.label" placeholder="中文名" size="small"
|
|
|
@update:value="nv => { v.label = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
@update:value="nv => { v.label = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
- <button class="text-btn danger" @click="removeLlmField('inputs', i)">删除</button>
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="var-form-row">
|
|
|
|
|
+ <n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
|
|
+ @update:value="nv => { v.type = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
|
|
+ <n-input :value="v.description" placeholder="描述" size="small"
|
|
|
|
|
+ @update:value="nv => { v.description = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
</div>
|
|
</div>
|
|
|
<div class="var-form-row mapping-row">
|
|
<div class="var-form-row mapping-row">
|
|
|
<n-select :value="currentMappingValue(v)"
|
|
<n-select :value="currentMappingValue(v)"
|
|
|
:options="[{ label: '自动推断', value: null }, ...inputMappingOptions]" size="small"
|
|
:options="[{ label: '自动推断', value: null }, ...inputMappingOptions]" size="small"
|
|
|
- clearable placeholder="关联方式:自动推断"
|
|
|
|
|
|
|
+ clearable placeholder="关联方式:自动推断" style="flex:1"
|
|
|
@update:value="nv => updateInputMapping(v, nv)" />
|
|
@update:value="nv => updateInputMapping(v, nv)" />
|
|
|
|
|
+ <button class="text-btn danger" @click="removeLlmField('inputs', i)">删除</button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div v-if="!selectedData?.inputs?.length" class="empty-hint">可用 {{ 变量名 }} 引用,或手动添加</div>
|
|
<div v-if="!selectedData?.inputs?.length" class="empty-hint">可用 {{ 变量名 }} 引用,或手动添加</div>
|
|
@@ -1283,18 +1397,25 @@ function applyGraphData(graphData) {
|
|
|
<div class="prop-section">
|
|
<div class="prop-section">
|
|
|
<div class="prop-header">
|
|
<div class="prop-header">
|
|
|
<span class="prop-label">输出变量</span>
|
|
<span class="prop-label">输出变量</span>
|
|
|
- <button class="text-btn" @click="addLlmField('outputs')">+ 添加</button>
|
|
|
|
|
|
|
+ <div style="display:flex;gap:4px">
|
|
|
|
|
+ <button class="text-btn" @click="openImportDialog('successor')">引入</button>
|
|
|
|
|
+ <button class="text-btn" @click="addLlmField('outputs')">+ 添加</button>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
<div v-for="(v, i) in selectedData?.outputs || []" :key="'out-' + i" class="var-form">
|
|
<div v-for="(v, i) in selectedData?.outputs || []" :key="'out-' + i" class="var-form">
|
|
|
<div class="var-form-row">
|
|
<div class="var-form-row">
|
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
|
@update:value="nv => { v.name = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
@update:value="nv => { v.name = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ <n-input :value="v.label" placeholder="中文名" size="small"
|
|
|
|
|
+ @update:value="nv => { v.label = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="var-form-row">
|
|
|
<n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
<n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
@update:value="nv => { v.type = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
@update:value="nv => { v.type = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ <n-input :value="v.description" placeholder="描述" size="small"
|
|
|
|
|
+ @update:value="nv => { v.description = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="var-form-row">
|
|
|
|
|
- <n-input :value="v.label" placeholder="中文名" size="small"
|
|
|
|
|
- @update:value="nv => { v.label = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
|
|
+ <div class="var-form-row" style="justify-content:flex-end">
|
|
|
<button class="text-btn danger" @click="removeLlmField('outputs', i)">删除</button>
|
|
<button class="text-btn danger" @click="removeLlmField('outputs', i)">删除</button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -1325,25 +1446,30 @@ function applyGraphData(graphData) {
|
|
|
<div class="prop-section">
|
|
<div class="prop-section">
|
|
|
<div class="prop-header">
|
|
<div class="prop-header">
|
|
|
<span class="prop-label">前置数据</span>
|
|
<span class="prop-label">前置数据</span>
|
|
|
- <button class="text-btn" @click="addAgentField('inputs')">+ 添加</button>
|
|
|
|
|
|
|
+ <div style="display:flex;gap:4px">
|
|
|
|
|
+ <button class="text-btn" @click="openImportDialog('predecessor')">引入</button>
|
|
|
|
|
+ <button class="text-btn" @click="addAgentField('inputs')">+ 添加</button>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
<div v-for="(v, i) in selectedData?.inputs || []" :key="'in-' + i" class="var-form">
|
|
<div v-for="(v, i) in selectedData?.inputs || []" :key="'in-' + i" class="var-form">
|
|
|
<div class="var-form-row">
|
|
<div class="var-form-row">
|
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
|
@update:value="nv => { v.name = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
@update:value="nv => { v.name = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
- <n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
|
|
- @update:value="nv => { v.type = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="var-form-row">
|
|
|
|
|
<n-input :value="v.label" placeholder="中文名" size="small"
|
|
<n-input :value="v.label" placeholder="中文名" size="small"
|
|
|
@update:value="nv => { v.label = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
@update:value="nv => { v.label = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
- <button class="text-btn danger" @click="removeAgentField('inputs', i)">删除</button>
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="var-form-row">
|
|
|
|
|
+ <n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
|
|
+ @update:value="nv => { v.type = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
|
|
+ <n-input :value="v.description" placeholder="描述" size="small"
|
|
|
|
|
+ @update:value="nv => { v.description = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
</div>
|
|
</div>
|
|
|
<div class="var-form-row mapping-row">
|
|
<div class="var-form-row mapping-row">
|
|
|
<n-select :value="currentMappingValue(v)"
|
|
<n-select :value="currentMappingValue(v)"
|
|
|
:options="[{ label: '自动推断', value: null }, ...inputMappingOptions]" size="small"
|
|
:options="[{ label: '自动推断', value: null }, ...inputMappingOptions]" size="small"
|
|
|
- clearable placeholder="关联方式:自动推断"
|
|
|
|
|
|
|
+ clearable placeholder="关联方式:自动推断" style="flex:1"
|
|
|
@update:value="nv => updateInputMapping(v, nv)" />
|
|
@update:value="nv => updateInputMapping(v, nv)" />
|
|
|
|
|
+ <button class="text-btn danger" @click="removeAgentField('inputs', i)">删除</button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div v-if="!selectedData?.inputs?.length" class="empty-hint">选择技能后自动填充</div>
|
|
<div v-if="!selectedData?.inputs?.length" class="empty-hint">选择技能后自动填充</div>
|
|
@@ -1352,18 +1478,25 @@ function applyGraphData(graphData) {
|
|
|
<div class="prop-section">
|
|
<div class="prop-section">
|
|
|
<div class="prop-header">
|
|
<div class="prop-header">
|
|
|
<span class="prop-label">输出变量</span>
|
|
<span class="prop-label">输出变量</span>
|
|
|
- <button class="text-btn" @click="addAgentField('outputs')">+ 添加</button>
|
|
|
|
|
|
|
+ <div style="display:flex;gap:4px">
|
|
|
|
|
+ <button class="text-btn" @click="openImportDialog('successor')">引入</button>
|
|
|
|
|
+ <button class="text-btn" @click="addAgentField('outputs')">+ 添加</button>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
<div v-for="(v, i) in selectedData?.outputs || []" :key="'out-' + i" class="var-form">
|
|
<div v-for="(v, i) in selectedData?.outputs || []" :key="'out-' + i" class="var-form">
|
|
|
<div class="var-form-row">
|
|
<div class="var-form-row">
|
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
|
@update:value="nv => { v.name = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
@update:value="nv => { v.name = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ <n-input :value="v.label" placeholder="中文名" size="small"
|
|
|
|
|
+ @update:value="nv => { v.label = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="var-form-row">
|
|
|
<n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
<n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
@update:value="nv => { v.type = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
@update:value="nv => { v.type = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ <n-input :value="v.description" placeholder="描述" size="small"
|
|
|
|
|
+ @update:value="nv => { v.description = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="var-form-row">
|
|
|
|
|
- <n-input :value="v.label" placeholder="中文名" size="small"
|
|
|
|
|
- @update:value="nv => { v.label = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
|
|
+ <div class="var-form-row" style="justify-content:flex-end">
|
|
|
<button class="text-btn danger" @click="removeAgentField('outputs', i)">删除</button>
|
|
<button class="text-btn danger" @click="removeAgentField('outputs', i)">删除</button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -1394,25 +1527,30 @@ function applyGraphData(graphData) {
|
|
|
<div class="prop-section">
|
|
<div class="prop-section">
|
|
|
<div class="prop-header">
|
|
<div class="prop-header">
|
|
|
<span class="prop-label">前置数据</span>
|
|
<span class="prop-label">前置数据</span>
|
|
|
- <button class="text-btn" @click="addAgentField('inputs')">+ 添加</button>
|
|
|
|
|
|
|
+ <div style="display:flex;gap:4px">
|
|
|
|
|
+ <button class="text-btn" @click="openImportDialog('predecessor')">引入</button>
|
|
|
|
|
+ <button class="text-btn" @click="addAgentField('inputs')">+ 添加</button>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
<div v-for="(v, i) in selectedData?.inputs || []" :key="'in-' + i" class="var-form">
|
|
<div v-for="(v, i) in selectedData?.inputs || []" :key="'in-' + i" class="var-form">
|
|
|
<div class="var-form-row">
|
|
<div class="var-form-row">
|
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
|
@update:value="nv => { v.name = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
@update:value="nv => { v.name = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
- <n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
|
|
- @update:value="nv => { v.type = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="var-form-row">
|
|
|
|
|
<n-input :value="v.label" placeholder="中文名" size="small"
|
|
<n-input :value="v.label" placeholder="中文名" size="small"
|
|
|
@update:value="nv => { v.label = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
@update:value="nv => { v.label = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
- <button class="text-btn danger" @click="removeAgentField('inputs', i)">删除</button>
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="var-form-row">
|
|
|
|
|
+ <n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
|
|
+ @update:value="nv => { v.type = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
|
|
+ <n-input :value="v.description" placeholder="描述" size="small"
|
|
|
|
|
+ @update:value="nv => { v.description = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
</div>
|
|
</div>
|
|
|
<div class="var-form-row mapping-row">
|
|
<div class="var-form-row mapping-row">
|
|
|
<n-select :value="currentMappingValue(v)"
|
|
<n-select :value="currentMappingValue(v)"
|
|
|
:options="[{ label: '自动推断', value: null }, ...inputMappingOptions]" size="small"
|
|
:options="[{ label: '自动推断', value: null }, ...inputMappingOptions]" size="small"
|
|
|
- clearable placeholder="关联方式:自动推断"
|
|
|
|
|
|
|
+ clearable placeholder="关联方式:自动推断" style="flex:1"
|
|
|
@update:value="nv => updateInputMapping(v, nv)" />
|
|
@update:value="nv => updateInputMapping(v, nv)" />
|
|
|
|
|
+ <button class="text-btn danger" @click="removeAgentField('inputs', i)">删除</button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div v-if="!selectedData?.inputs?.length" class="empty-hint">选择技能后自动填充</div>
|
|
<div v-if="!selectedData?.inputs?.length" class="empty-hint">选择技能后自动填充</div>
|
|
@@ -1421,18 +1559,25 @@ function applyGraphData(graphData) {
|
|
|
<div class="prop-section">
|
|
<div class="prop-section">
|
|
|
<div class="prop-header">
|
|
<div class="prop-header">
|
|
|
<span class="prop-label">输出变量</span>
|
|
<span class="prop-label">输出变量</span>
|
|
|
- <button class="text-btn" @click="addAgentField('outputs')">+ 添加</button>
|
|
|
|
|
|
|
+ <div style="display:flex;gap:4px">
|
|
|
|
|
+ <button class="text-btn" @click="openImportDialog('successor')">引入</button>
|
|
|
|
|
+ <button class="text-btn" @click="addAgentField('outputs')">+ 添加</button>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
<div v-for="(v, i) in selectedData?.outputs || []" :key="'out-' + i" class="var-form">
|
|
<div v-for="(v, i) in selectedData?.outputs || []" :key="'out-' + i" class="var-form">
|
|
|
<div class="var-form-row">
|
|
<div class="var-form-row">
|
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
|
@update:value="nv => { v.name = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
@update:value="nv => { v.name = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ <n-input :value="v.label" placeholder="中文名" size="small"
|
|
|
|
|
+ @update:value="nv => { v.label = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="var-form-row">
|
|
|
<n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
<n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
@update:value="nv => { v.type = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
@update:value="nv => { v.type = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ <n-input :value="v.description" placeholder="描述" size="small"
|
|
|
|
|
+ @update:value="nv => { v.description = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="var-form-row">
|
|
|
|
|
- <n-input :value="v.label" placeholder="中文名" size="small"
|
|
|
|
|
- @update:value="nv => { v.label = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
|
|
+ <div class="var-form-row" style="justify-content:flex-end">
|
|
|
<button class="text-btn danger" @click="removeAgentField('outputs', i)">删除</button>
|
|
<button class="text-btn danger" @click="removeAgentField('outputs', i)">删除</button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -1470,25 +1615,30 @@ function applyGraphData(graphData) {
|
|
|
<div class="prop-section">
|
|
<div class="prop-section">
|
|
|
<div class="prop-header">
|
|
<div class="prop-header">
|
|
|
<span class="prop-label">前置数据</span>
|
|
<span class="prop-label">前置数据</span>
|
|
|
- <button class="text-btn" @click="addAgentField('inputs')">+ 添加</button>
|
|
|
|
|
|
|
+ <div style="display:flex;gap:4px">
|
|
|
|
|
+ <button class="text-btn" @click="openImportDialog('predecessor')">引入</button>
|
|
|
|
|
+ <button class="text-btn" @click="addAgentField('inputs')">+ 添加</button>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
<div v-for="(v, i) in selectedData?.inputs || []" :key="'in-' + i" class="var-form">
|
|
<div v-for="(v, i) in selectedData?.inputs || []" :key="'in-' + i" class="var-form">
|
|
|
<div class="var-form-row">
|
|
<div class="var-form-row">
|
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
|
@update:value="nv => { v.name = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
@update:value="nv => { v.name = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
- <n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
|
|
- @update:value="nv => { v.type = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="var-form-row">
|
|
|
|
|
<n-input :value="v.label" placeholder="中文名" size="small"
|
|
<n-input :value="v.label" placeholder="中文名" size="small"
|
|
|
@update:value="nv => { v.label = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
@update:value="nv => { v.label = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
- <button class="text-btn danger" @click="removeAgentField('inputs', i)">删除</button>
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="var-form-row">
|
|
|
|
|
+ <n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
|
|
+ @update:value="nv => { v.type = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
|
|
+ <n-input :value="v.description" placeholder="描述" size="small"
|
|
|
|
|
+ @update:value="nv => { v.description = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
</div>
|
|
</div>
|
|
|
<div class="var-form-row mapping-row">
|
|
<div class="var-form-row mapping-row">
|
|
|
<n-select :value="currentMappingValue(v)"
|
|
<n-select :value="currentMappingValue(v)"
|
|
|
:options="[{ label: '自动推断', value: null }, ...inputMappingOptions]" size="small"
|
|
:options="[{ label: '自动推断', value: null }, ...inputMappingOptions]" size="small"
|
|
|
- clearable placeholder="关联方式:自动推断"
|
|
|
|
|
|
|
+ clearable placeholder="关联方式:自动推断" style="flex:1"
|
|
|
@update:value="nv => updateInputMapping(v, nv)" />
|
|
@update:value="nv => updateInputMapping(v, nv)" />
|
|
|
|
|
+ <button class="text-btn danger" @click="removeAgentField('inputs', i)">删除</button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div v-if="!selectedData?.inputs?.length" class="empty-hint">可用 {{ 变量名 }} 引用,或手动添加</div>
|
|
<div v-if="!selectedData?.inputs?.length" class="empty-hint">可用 {{ 变量名 }} 引用,或手动添加</div>
|
|
@@ -1497,18 +1647,25 @@ function applyGraphData(graphData) {
|
|
|
<div class="prop-section">
|
|
<div class="prop-section">
|
|
|
<div class="prop-header">
|
|
<div class="prop-header">
|
|
|
<span class="prop-label">输出变量</span>
|
|
<span class="prop-label">输出变量</span>
|
|
|
- <button class="text-btn" @click="addAgentField('outputs')">+ 添加</button>
|
|
|
|
|
|
|
+ <div style="display:flex;gap:4px">
|
|
|
|
|
+ <button class="text-btn" @click="openImportDialog('successor')">引入</button>
|
|
|
|
|
+ <button class="text-btn" @click="addAgentField('outputs')">+ 添加</button>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
<div v-for="(v, i) in selectedData?.outputs || []" :key="'out-' + i" class="var-form">
|
|
<div v-for="(v, i) in selectedData?.outputs || []" :key="'out-' + i" class="var-form">
|
|
|
<div class="var-form-row">
|
|
<div class="var-form-row">
|
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
|
@update:value="nv => { v.name = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
@update:value="nv => { v.name = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ <n-input :value="v.label" placeholder="中文名" size="small"
|
|
|
|
|
+ @update:value="nv => { v.label = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="var-form-row">
|
|
|
<n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
<n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
@update:value="nv => { v.type = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
@update:value="nv => { v.type = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ <n-input :value="v.description" placeholder="描述" size="small"
|
|
|
|
|
+ @update:value="nv => { v.description = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="var-form-row">
|
|
|
|
|
- <n-input :value="v.label" placeholder="中文名" size="small"
|
|
|
|
|
- @update:value="nv => { v.label = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
|
|
+ <div class="var-form-row" style="justify-content:flex-end">
|
|
|
<button class="text-btn danger" @click="removeAgentField('outputs', i)">删除</button>
|
|
<button class="text-btn danger" @click="removeAgentField('outputs', i)">删除</button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -1611,6 +1768,66 @@ function applyGraphData(graphData) {
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
+ <!-- 前置数据 -->
|
|
|
|
|
+ <div class="prop-section">
|
|
|
|
|
+ <div class="prop-header">
|
|
|
|
|
+ <span class="prop-label">前置数据</span>
|
|
|
|
|
+ <div style="display:flex;gap:4px">
|
|
|
|
|
+ <button class="text-btn" @click="openImportDialog('predecessor')">引入</button>
|
|
|
|
|
+ <button class="text-btn" @click="addListField('inputs')">+ 添加</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-for="(v, i) in selectedData?.inputs || []" :key="'in-' + i" class="var-form">
|
|
|
|
|
+ <div class="var-form-row">
|
|
|
|
|
+ <n-input :value="v.name" placeholder="变量名" size="small"
|
|
|
|
|
+ @update:value="nv => { v.name = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
|
|
+ <n-input :value="v.label" placeholder="中文名" size="small"
|
|
|
|
|
+ @update:value="nv => { v.label = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="var-form-row">
|
|
|
|
|
+ <n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
|
|
+ @update:value="nv => { v.type = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
|
|
+ <n-input :value="v.description" placeholder="描述" size="small"
|
|
|
|
|
+ @update:value="nv => { v.description = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="var-form-row mapping-row">
|
|
|
|
|
+ <n-select :value="currentMappingValue(v)"
|
|
|
|
|
+ :options="[{ label: '自动推断', value: null }, ...inputMappingOptions]" size="small"
|
|
|
|
|
+ clearable placeholder="关联方式:自动推断" style="flex:1"
|
|
|
|
|
+ @update:value="nv => updateInputMapping(v, nv)" />
|
|
|
|
|
+ <button class="text-btn danger" @click="removeListField('inputs', i)">删除</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-if="!selectedData?.inputs?.length" class="empty-hint">可用 {{ 变量名 }} 引用,或手动添加</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- 输出变量 -->
|
|
|
|
|
+ <div class="prop-section">
|
|
|
|
|
+ <div class="prop-header">
|
|
|
|
|
+ <span class="prop-label">输出变量</span>
|
|
|
|
|
+ <div style="display:flex;gap:4px">
|
|
|
|
|
+ <button class="text-btn" @click="openImportDialog('successor')">引入</button>
|
|
|
|
|
+ <button class="text-btn" @click="addListField('outputs')">+ 添加</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-for="(v, i) in selectedData?.outputs || []" :key="'out-' + i" class="var-form">
|
|
|
|
|
+ <div class="var-form-row">
|
|
|
|
|
+ <n-input :value="v.name" placeholder="变量名" size="small"
|
|
|
|
|
+ @update:value="nv => { v.name = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ <n-input :value="v.label" placeholder="中文名" size="small"
|
|
|
|
|
+ @update:value="nv => { v.label = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="var-form-row">
|
|
|
|
|
+ <n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
|
|
+ @update:value="nv => { v.type = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ <n-input :value="v.description" placeholder="描述" size="small"
|
|
|
|
|
+ @update:value="nv => { v.description = nv; onFieldChange('outputs', [...selectedData.outputs]) }" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="var-form-row" style="justify-content:flex-end">
|
|
|
|
|
+ <button class="text-btn danger" @click="removeListField('outputs', i)">删除</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-if="!selectedData?.outputs?.length" class="empty-hint">默认输出 evidences / evidenceCount / sourceType / diagnostics</div>
|
|
|
|
|
+ </div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<!-- 条件分支节点 -->
|
|
<!-- 条件分支节点 -->
|
|
@@ -1632,25 +1849,30 @@ function applyGraphData(graphData) {
|
|
|
<div class="prop-section">
|
|
<div class="prop-section">
|
|
|
<div class="prop-header">
|
|
<div class="prop-header">
|
|
|
<span class="prop-label">前置数据</span>
|
|
<span class="prop-label">前置数据</span>
|
|
|
- <button class="text-btn" @click="addConditionInput">+ 添加</button>
|
|
|
|
|
|
|
+ <div style="display:flex;gap:4px">
|
|
|
|
|
+ <button class="text-btn" @click="openImportDialog('predecessor')">引入</button>
|
|
|
|
|
+ <button class="text-btn" @click="addConditionInput">+ 添加</button>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
<div v-for="(v, i) in selectedData?.inputs || []" :key="i" class="var-form">
|
|
<div v-for="(v, i) in selectedData?.inputs || []" :key="i" class="var-form">
|
|
|
<div class="var-form-row">
|
|
<div class="var-form-row">
|
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
<n-input :value="v.name" placeholder="变量名" size="small"
|
|
|
@update:value="nv => { v.name = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
@update:value="nv => { v.name = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
- <n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
|
|
- @update:value="nv => { v.type = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="var-form-row">
|
|
|
|
|
<n-input :value="v.label" placeholder="中文名" size="small"
|
|
<n-input :value="v.label" placeholder="中文名" size="small"
|
|
|
@update:value="nv => { v.label = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
@update:value="nv => { v.label = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
- <button class="text-btn danger" @click="removeConditionInput(i)">删除</button>
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="var-form-row">
|
|
|
|
|
+ <n-select :value="v.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
|
|
+ @update:value="nv => { v.type = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
|
|
+ <n-input :value="v.description" placeholder="描述" size="small"
|
|
|
|
|
+ @update:value="nv => { v.description = nv; onFieldChange('inputs', [...selectedData.inputs]) }" />
|
|
|
</div>
|
|
</div>
|
|
|
<div class="var-form-row mapping-row">
|
|
<div class="var-form-row mapping-row">
|
|
|
<n-select :value="currentMappingValue(v)"
|
|
<n-select :value="currentMappingValue(v)"
|
|
|
:options="[{ label: '自动推断', value: null }, ...inputMappingOptions]" size="small"
|
|
:options="[{ label: '自动推断', value: null }, ...inputMappingOptions]" size="small"
|
|
|
- clearable placeholder="关联方式:自动推断"
|
|
|
|
|
|
|
+ clearable placeholder="关联方式:自动推断" style="flex:1"
|
|
|
@update:value="nv => updateInputMapping(v, nv)" />
|
|
@update:value="nv => updateInputMapping(v, nv)" />
|
|
|
|
|
+ <button class="text-btn danger" @click="removeConditionInput(i)">删除</button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div v-if="!selectedData?.inputs?.length" class="empty-hint">连接上游节点后自动推断,或手动添加</div>
|
|
<div v-if="!selectedData?.inputs?.length" class="empty-hint">连接上游节点后自动推断,或手动添加</div>
|
|
@@ -1669,12 +1891,16 @@ function applyGraphData(graphData) {
|
|
|
<div class="var-form-row">
|
|
<div class="var-form-row">
|
|
|
<n-input :value="f.name" placeholder="字段名" size="small"
|
|
<n-input :value="f.name" placeholder="字段名" size="small"
|
|
|
@update:value="nv => { f.name = nv; onFieldChange('fields', [...selectedData.fields]) }" />
|
|
@update:value="nv => { f.name = nv; onFieldChange('fields', [...selectedData.fields]) }" />
|
|
|
|
|
+ <n-input :value="f.label" placeholder="中文名" size="small"
|
|
|
|
|
+ @update:value="nv => { f.label = nv; onFieldChange('fields', [...selectedData.fields]) }" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="var-form-row">
|
|
|
<n-select :value="f.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
<n-select :value="f.type" :options="IO_TYPES" size="small" style="width:100px"
|
|
|
@update:value="nv => { f.type = nv; onFieldChange('fields', [...selectedData.fields]) }" />
|
|
@update:value="nv => { f.type = nv; onFieldChange('fields', [...selectedData.fields]) }" />
|
|
|
|
|
+ <n-input :value="f.description" placeholder="描述" size="small"
|
|
|
|
|
+ @update:value="nv => { f.description = nv; onFieldChange('fields', [...selectedData.fields]) }" />
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="var-form-row">
|
|
|
|
|
- <n-input :value="f.label" placeholder="中文名" size="small"
|
|
|
|
|
- @update:value="nv => { f.label = nv; onFieldChange('fields', [...selectedData.fields]) }" />
|
|
|
|
|
|
|
+ <div class="var-form-row" style="justify-content:flex-end">
|
|
|
<button class="text-btn danger" @click="removeOutputField(i)">删除</button>
|
|
<button class="text-btn danger" @click="removeOutputField(i)">删除</button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -1938,6 +2164,27 @@ function applyGraphData(graphData) {
|
|
|
</div>
|
|
</div>
|
|
|
</Teleport>
|
|
</Teleport>
|
|
|
|
|
|
|
|
|
|
+ <!-- 字段引入对话框:从前驱输出或后继输入中引入字段 -->
|
|
|
|
|
+ <n-modal v-model:show="importDialog.open" preset="card" style="max-width:560px"
|
|
|
|
|
+ :title="importDialog.mode === 'predecessor' ? '从前驱节点引入字段' : '向后继节点引入字段'">
|
|
|
|
|
+ <div class="import-candidate-list">
|
|
|
|
|
+ <div v-if="!importDialog.candidates.length" class="empty-hint">暂无可引入字段</div>
|
|
|
|
|
+ <div v-for="(c, i) in importDialog.candidates" :key="i" class="import-candidate"
|
|
|
|
|
+ @click="confirmImportField(c)">
|
|
|
|
|
+ <div class="candidate-main">
|
|
|
|
|
+ <span class="candidate-node">{{ c.nodeName }}</span>
|
|
|
|
|
+ <span class="candidate-sep">.</span>
|
|
|
|
|
+ <span class="candidate-field">{{ c.fieldName }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="candidate-meta">
|
|
|
|
|
+ <span class="candidate-type">{{ c.fieldType }}</span>
|
|
|
|
|
+ <span v-if="c.fieldLabel && c.fieldLabel !== c.fieldName" class="candidate-label">{{ c.fieldLabel }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-if="c.fieldDescription" class="candidate-desc">{{ c.fieldDescription }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </n-modal>
|
|
|
|
|
+
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
@@ -2724,6 +2971,74 @@ function applyGraphData(graphData) {
|
|
|
font-style: italic;
|
|
font-style: italic;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/* 引入对话框候选列表 */
|
|
|
|
|
+.import-candidate-list {
|
|
|
|
|
+ max-height: 360px;
|
|
|
|
|
+ overflow-y: auto;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 6px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.import-candidate {
|
|
|
|
|
+ padding: 8px 10px;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.04);
|
|
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.06);
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ transition: background 0.15s, border-color 0.15s;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.import-candidate:hover {
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.08);
|
|
|
|
|
+ border-color: rgba(255, 255, 255, 0.14);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.import-candidate .candidate-main {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: var(--text-primary, #eee);
|
|
|
|
|
+ margin-bottom: 2px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.import-candidate .candidate-node {
|
|
|
|
|
+ color: var(--text-secondary, #aaa);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.import-candidate .candidate-sep {
|
|
|
|
|
+ color: var(--text-tertiary, #666);
|
|
|
|
|
+ margin: 0 2px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.import-candidate .candidate-field {
|
|
|
|
|
+ font-family: 'Consolas', 'Monaco', monospace;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.import-candidate .candidate-meta {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ margin-bottom: 2px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.import-candidate .candidate-type {
|
|
|
|
|
+ padding: 1px 6px;
|
|
|
|
|
+ border-radius: 3px;
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.06);
|
|
|
|
|
+ color: var(--text-secondary, #aaa);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.import-candidate .candidate-label {
|
|
|
|
|
+ color: var(--text-tertiary, #888);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.import-candidate .candidate-desc {
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ color: var(--text-tertiary, #777);
|
|
|
|
|
+ line-height: 1.4;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.kr-checkbox-row {
|
|
.kr-checkbox-row {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|