重构工作流绘制
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
<template>
|
||||
<div class="config-panel">
|
||||
<h3>节点配置</h3>
|
||||
<div v-if="selectedNode">
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="节点名称">
|
||||
<el-input :model-value="selectedNode.data.label" @update:model-value="updateNodeLabel" />
|
||||
</el-form-item>
|
||||
<el-form-item label="节点类型">
|
||||
<el-tag>{{ selectedNode.data.nodeCode }}</el-tag>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 模型选择 -->
|
||||
<el-form-item v-if="nodeConfig?.modelConfig && nodeConfig.modelConfig.length > 0" label="选择模型">
|
||||
<el-button type="primary" @click="emit('openModelSelector')" style="width: 100%">选择模型</el-button>
|
||||
<div v-if="selectedNode.data.modelConfig?.modelName" class="selected-tag">
|
||||
<el-tag type="success" size="large" closable @close="emit('removeModel')">
|
||||
{{ selectedNode.data.modelConfig.modelName }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 技能选择 -->
|
||||
<el-form-item v-if="nodeConfig?.skillOption" label="选择技能">
|
||||
<el-button type="primary" @click="emit('openSkillSelector')" style="width: 100%">选择技能</el-button>
|
||||
<div v-if="selectedNode.data.skillName" class="selected-tag">
|
||||
<el-tag type="success" size="large" closable @close="emit('removeSkill')">
|
||||
{{ selectedNode.data.skillName }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 动态表单字段 -->
|
||||
<template v-if="nodeConfig?.formConfig && nodeConfig.formConfig.length > 0">
|
||||
<el-divider content-position="left">节点参数</el-divider>
|
||||
<el-form-item v-for="field in nodeConfig.formConfig" :key="field.field" :label="field.label" :required="field.required">
|
||||
<el-input
|
||||
v-if="field.type === 'input' || field.type === 'string'"
|
||||
:model-value="getFieldValue(field.field)"
|
||||
@update:model-value="updateFieldValue(field.field, $event)"
|
||||
:placeholder="field.required ? '必填' : '选填'"
|
||||
/>
|
||||
<el-input-number
|
||||
v-else-if="field.type === 'number' || field.type === 'inputNumber'"
|
||||
:model-value="getFieldValue(field.field)"
|
||||
@update:model-value="updateFieldValue(field.field, $event)"
|
||||
class="w100"
|
||||
/>
|
||||
<el-input
|
||||
v-else-if="field.type === 'textarea'"
|
||||
:model-value="getFieldValue(field.field)"
|
||||
@update:model-value="updateFieldValue(field.field, $event)"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
:placeholder="field.required ? '必填' : '选填'"
|
||||
/>
|
||||
<el-switch
|
||||
v-else-if="field.type === 'switch'"
|
||||
:model-value="getFieldValue(field.field)"
|
||||
@update:model-value="updateFieldValue(field.field, $event)"
|
||||
/>
|
||||
<el-select
|
||||
v-else-if="field.type === 'select' && field.options"
|
||||
:model-value="getFieldValue(field.field)"
|
||||
@update:model-value="updateFieldValue(field.field, $event)"
|
||||
:placeholder="field.required ? '必填' : '选填'"
|
||||
class="w100"
|
||||
>
|
||||
<el-option v-for="opt in field.options" :key="opt.value" :label="opt.label" :value="opt.value" />
|
||||
</el-select>
|
||||
<el-input
|
||||
v-else
|
||||
:model-value="getFieldValue(field.field)"
|
||||
@update:model-value="updateFieldValue(field.field, $event)"
|
||||
:placeholder="field.required ? '必填' : '选填'"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-form>
|
||||
|
||||
<!-- 上级参数管理 -->
|
||||
<InputSourceManager
|
||||
v-if="selectedNode.data.nodeCode !== '__start__'"
|
||||
:selected-node="selectedNode"
|
||||
:nodes="allNodes"
|
||||
:edges="allEdges"
|
||||
@remove-field="(nodeId: string, fieldName: string) => emit('removeField', nodeId, fieldName)"
|
||||
@toggle-output="(nodeId: string, enabled: boolean) => emit('toggleOutput', nodeId, enabled)"
|
||||
@add-param="(paramValue: string) => emit('addParamByValue', paramValue)"
|
||||
/>
|
||||
</div>
|
||||
<el-empty v-else description="请选择一个节点" :image-size="100" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Node } from '@vue-flow/core';
|
||||
import InputSourceManager from './InputSourceManager.vue';
|
||||
|
||||
interface NodeData {
|
||||
label: string;
|
||||
nodeCode: string;
|
||||
inputSource: Array<{ nodeId: string; field: string[]; quoteOutput?: boolean }> | null;
|
||||
formConfig?: any[];
|
||||
modelConfig?: any;
|
||||
skillName?: string;
|
||||
}
|
||||
|
||||
interface ParamRef {
|
||||
id: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface NodeConfig {
|
||||
formConfig: any[];
|
||||
modelConfig: any[];
|
||||
skillOption: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
selectedNode: Node<NodeData> | null;
|
||||
availableParams: ParamRef[];
|
||||
nodeConfig: NodeConfig | null;
|
||||
allNodes: Node<NodeData>[];
|
||||
allEdges: any[];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:selectedNode', node: Node<NodeData>): void;
|
||||
(e: 'addParam', param: ParamRef): void;
|
||||
(e: 'openModelSelector'): void;
|
||||
(e: 'removeModel'): void;
|
||||
(e: 'openSkillSelector'): void;
|
||||
(e: 'removeSkill'): void;
|
||||
(e: 'removeField', nodeId: string, fieldName: string): void;
|
||||
(e: 'toggleOutput', nodeId: string, enabled: boolean): void;
|
||||
(e: 'addParamByValue', paramValue: string): void;
|
||||
}>();
|
||||
|
||||
const updateNodeLabel = (newLabel: string) => {
|
||||
if (!props.selectedNode) return;
|
||||
const updatedNode = {
|
||||
...props.selectedNode,
|
||||
data: {
|
||||
...props.selectedNode.data,
|
||||
label: newLabel,
|
||||
},
|
||||
};
|
||||
emit('update:selectedNode', updatedNode);
|
||||
};
|
||||
|
||||
const getFieldValue = (fieldName: string) => {
|
||||
if (!props.selectedNode?.data.formConfig) return '';
|
||||
const field = props.selectedNode.data.formConfig.find((f: any) => f.field === fieldName);
|
||||
return field?.value ?? '';
|
||||
};
|
||||
|
||||
const updateFieldValue = (fieldName: string, value: any) => {
|
||||
if (!props.selectedNode) return;
|
||||
|
||||
const formConfig = props.selectedNode.data.formConfig || [];
|
||||
const existingIndex = formConfig.findIndex((f: any) => f.field === fieldName);
|
||||
|
||||
let updatedFormConfig;
|
||||
if (existingIndex >= 0) {
|
||||
updatedFormConfig = [...formConfig];
|
||||
updatedFormConfig[existingIndex] = { ...updatedFormConfig[existingIndex], value };
|
||||
} else {
|
||||
const fieldDef = props.nodeConfig?.formConfig.find((f) => f.field === fieldName);
|
||||
if (fieldDef) {
|
||||
updatedFormConfig = [...formConfig, { ...fieldDef, value }];
|
||||
} else {
|
||||
updatedFormConfig = formConfig;
|
||||
}
|
||||
}
|
||||
|
||||
const updatedNode = {
|
||||
...props.selectedNode,
|
||||
data: {
|
||||
...props.selectedNode.data,
|
||||
formConfig: updatedFormConfig,
|
||||
},
|
||||
};
|
||||
emit('update:selectedNode', updatedNode);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.config-panel {
|
||||
background: #ffffff;
|
||||
border-radius: 10px;
|
||||
padding: 16px;
|
||||
overflow-y: auto;
|
||||
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
|
||||
border: 1px solid #e6eaf0;
|
||||
|
||||
h3 {
|
||||
margin: 0 0 16px 0;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #334155;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
:deep(.el-form) {
|
||||
.el-form-item {
|
||||
margin-bottom: 14px;
|
||||
|
||||
.el-form-item__label {
|
||||
font-weight: 600;
|
||||
color: #475569;
|
||||
margin-bottom: 6px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.el-input__wrapper {
|
||||
border-radius: 6px;
|
||||
box-shadow: none;
|
||||
border: 1px solid #dbe3ee;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: #b8c4d6;
|
||||
}
|
||||
|
||||
&.is-focus {
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.12);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.param-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 8px;
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: #cbd5e1;
|
||||
background: #f1f5f9;
|
||||
}
|
||||
}
|
||||
|
||||
.selected-tag {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.w100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.el-empty) {
|
||||
padding: 32px 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user