feat: 增强模型配置与创建表单功能
- 在 NodeLibraryFormItem 和 WorkflowItem 接口中添加了新的字段,如 value、options、expand 和 outputParams,以支持更复杂的表单配置。 - 新增 getOperatorList 函数以获取服务商列表,并在 editModule.vue 中集成了运营商选择功能。 - 更新了模型创建和编辑逻辑,支持 tokenConfig 的 JSON 格式配置,确保更灵活的模型设置。 - 优化了文件上传处理,增加了上传状态管理,提升用户体验。
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<template>
|
||||
<template>
|
||||
<div class="creation-page" :class="{ 'creation-mode': isCreationMode }">
|
||||
<!-- 左侧面板:工作空间/当前选中元素 Tab切换 -->
|
||||
<div class="panel left">
|
||||
@@ -301,7 +301,7 @@
|
||||
<div class="simple-form-scroll">
|
||||
<el-form label-position="top" class="simple-creation-form creation-form-grid">
|
||||
<template v-if="currentWorkflowForCreation?.nodeInputParams">
|
||||
<template v-for="node in currentWorkflowForCreation.nodeInputParams" :key="node.id">
|
||||
<template v-for="node in currentWorkflowForCreation.nodeInputParams">
|
||||
<template v-if="node.nodeCode !== '__start__' && node.formConfig && node.formConfig.length > 0">
|
||||
<el-form-item
|
||||
v-for="field in getCreationVisibleFields(node)"
|
||||
@@ -352,7 +352,7 @@
|
||||
:accept="getCreationFileAccept(field)"
|
||||
:on-change="(file: any) => handleCreationFieldUpload(node, field, file)"
|
||||
>
|
||||
<el-button size="small" type="primary" :disabled="isFromWorkspace">选择文件</el-button>
|
||||
<el-button size="small" type="primary" :loading="isCreationFieldUploading(node, field)" :disabled="isFromWorkspace || isCreationFieldUploading(node, field)">{{ isCreationFieldUploading(node, field) ? '上传中...' : '选择文件' }}</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
<div class="creation-upload-tags">
|
||||
@@ -703,7 +703,7 @@
|
||||
class="http-body-switch"
|
||||
@change="(val: boolean) => handleHttpBodyShowInFormChange(fieldValue, val)"
|
||||
/>
|
||||
<el-button type="danger" :icon="Delete" circle @click="deleteHttpBodyField(fieldKey)" />
|
||||
<el-button type="danger" :icon="Delete" circle @click="deleteHttpBodyField(String(fieldKey))" />
|
||||
</div>
|
||||
|
||||
<div v-if="!fieldValue.showInForm" class="http-body-value-row">
|
||||
@@ -769,7 +769,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { Document, Plus, Paperclip, MagicStick, Promotion, Check, Setting, Search, CircleCheck, VideoPause, Delete } from '@element-plus/icons-vue';
|
||||
import { Plus, Paperclip, MagicStick, Promotion, Check, Setting, Search, CircleCheck, VideoPause, Delete } from '@element-plus/icons-vue';
|
||||
import LogicFlow from '@logicflow/core';
|
||||
import { Control, SelectionSelect } from '@logicflow/extension';
|
||||
import '@logicflow/core/dist/index.css';
|
||||
@@ -823,7 +823,19 @@ const treeLoading = ref(false);
|
||||
const treeNodes = ref<TreeNode[]>([]);
|
||||
const imgAddressPrefix = ref('');
|
||||
const selectedElement = ref<SelectedState | null>(null);
|
||||
const customFields = ref<Array<{ label: string; value: string; type: string; required: boolean }>>([]);
|
||||
const customFields = ref<
|
||||
Array<{
|
||||
label: string;
|
||||
value: string;
|
||||
type: string;
|
||||
required: boolean;
|
||||
fileList?: Array<{ name: string; url: string }>;
|
||||
uploadKey?: number;
|
||||
fileTypes?: string;
|
||||
maxFileSize?: number;
|
||||
maxFileCount?: number;
|
||||
}>
|
||||
>([]);
|
||||
const selectedParentParam = ref('');
|
||||
const selectedModel = ref('');
|
||||
const showSkillSelector = ref(false);
|
||||
@@ -984,15 +996,18 @@ const currentNodeSkillOption = computed(() => {
|
||||
});
|
||||
// 获取当前节点的模型类型
|
||||
const currentNodeModelType = computed(() => {
|
||||
let modelType = 1; // 默认为推理模型
|
||||
nodeLibraryGroups.value.forEach((group) => {
|
||||
(group.items || []).forEach((item) => {
|
||||
if (item.nodeCode === formState.nodeCode) {
|
||||
modelType = item.modelType || 1;
|
||||
const currentNodeCode = String(formState.nodeCode || '').trim();
|
||||
if (!currentNodeCode) return 0;
|
||||
|
||||
for (const group of nodeLibraryGroups.value) {
|
||||
for (const item of group.items || []) {
|
||||
if (item.nodeCode === currentNodeCode) {
|
||||
const mt = Number(item.modelType ?? 0);
|
||||
return Number.isNaN(mt) ? 0 : mt;
|
||||
}
|
||||
});
|
||||
});
|
||||
return modelType;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
// 获取当前选中模型的表单字段
|
||||
const currentModelForm = computed<NodeLibraryFormItem[]>(() => {
|
||||
@@ -1375,7 +1390,7 @@ const fetchChatModelList = async () => {
|
||||
const res: any = await getModelModuleList({
|
||||
pageNum: chatModelPagination.pageNum,
|
||||
pageSize: chatModelPagination.pageSize,
|
||||
modelType: 1, // 传递 modelType=1 给后端,获取推理模型
|
||||
modelType: 100, // 传递 modelType=100 固定获取对话模型
|
||||
modelName: chatModelSearchKeyword.value || undefined,
|
||||
enabled: 1,
|
||||
});
|
||||
@@ -1540,7 +1555,14 @@ const useWorkflow = async (workflow: WorkflowItem) => {
|
||||
creationFormValues[fieldKey] = Boolean(field.value);
|
||||
} else {
|
||||
// 其他类型:保持原值或空字符串
|
||||
creationFormValues[fieldKey] = field.value || '';
|
||||
creationFormValues[fieldKey] =
|
||||
field.type === 'upload' || field.type === 'uploadMultiple' || field.type === 'fileUpload'
|
||||
? Array.isArray(field.value)
|
||||
? field.value
|
||||
: field.value
|
||||
? [field.value]
|
||||
: []
|
||||
: field.value || '';
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1698,6 +1720,11 @@ const getCreationVisibleFields = (node: any) => {
|
||||
return result;
|
||||
};
|
||||
const creationFieldFiles = reactive<Record<string, Array<{ name: string; url: string }>>>({});
|
||||
const creationFieldUploading = reactive<Record<string, boolean>>({});
|
||||
const isCreationFieldUploading = (node: any, field: any) => {
|
||||
const key = getCreationFieldKey(node, field);
|
||||
return creationFieldUploading[key] === true;
|
||||
};
|
||||
const getCreationFieldFiles = (node: any, field: any) => {
|
||||
const key = getCreationFieldKey(node, field);
|
||||
return creationFieldFiles[key] || [];
|
||||
@@ -1728,6 +1755,8 @@ const getCreationFileRuleText = (field: any) => {
|
||||
};
|
||||
const handleCreationFieldUpload = async (node: any, field: any, file: any) => {
|
||||
const key = getCreationFieldKey(node, field);
|
||||
if (creationFieldUploading[key]) return;
|
||||
creationFieldUploading[key] = true;
|
||||
try {
|
||||
const fc = getCreationFieldConstraint(field);
|
||||
const raw = file?.raw;
|
||||
@@ -1770,8 +1799,10 @@ const handleCreationFieldUpload = async (node: any, field: any, file: any) => {
|
||||
creationFieldFiles[key].push({ name: file.name, url: fileUrl });
|
||||
creationFormValues[key] = creationFieldFiles[key].map((f) => f.url);
|
||||
ElMessage.success('文件上传成功');
|
||||
} catch (error: any) {
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.message || '文件上传失败');
|
||||
} finally {
|
||||
creationFieldUploading[key] = false;
|
||||
}
|
||||
};
|
||||
const removeCreationFieldFile = (node: any, field: any, fileIdx: number) => {
|
||||
@@ -1911,7 +1942,18 @@ const sendMessage = async () => {
|
||||
const userVal = creationFormValues[bodyFieldKey];
|
||||
bodyValue[bodyKey] = {
|
||||
...bodyItem,
|
||||
value: userVal !== undefined ? userVal : bodyItem.value,
|
||||
value:
|
||||
bodyItem.fieldType === 'fileUpload'
|
||||
? Array.isArray(userVal !== undefined ? userVal : bodyItem.value)
|
||||
? (userVal !== undefined ? userVal : bodyItem.value)
|
||||
: userVal !== undefined
|
||||
? [userVal]
|
||||
: bodyItem.value
|
||||
? [bodyItem.value]
|
||||
: []
|
||||
: userVal !== undefined
|
||||
? userVal
|
||||
: bodyItem.value,
|
||||
};
|
||||
});
|
||||
return {
|
||||
@@ -2020,7 +2062,7 @@ const handleGlobalMouseUp = () => {
|
||||
isDraggingMiddleSplitter.value = false;
|
||||
};
|
||||
// 根据字段类型返回CSS类名
|
||||
const getFieldClass = (type: string) => {
|
||||
const _getFieldClass = (type: string) => {
|
||||
if (type === 'textarea') return 'form-item-full';
|
||||
if (type === 'number' || type === 'switch') return 'form-item-small';
|
||||
return 'form-item-medium';
|
||||
@@ -2087,7 +2129,14 @@ const handleTreeNodeClick = async (data: TreeNode) => {
|
||||
} else if (field.type === 'switch') {
|
||||
creationFormValues[fieldKey] = Boolean(field.value);
|
||||
} else {
|
||||
creationFormValues[fieldKey] = field.value || '';
|
||||
creationFormValues[fieldKey] =
|
||||
field.type === 'upload' || field.type === 'uploadMultiple' || field.type === 'fileUpload'
|
||||
? Array.isArray(field.value)
|
||||
? field.value
|
||||
: field.value
|
||||
? [field.value]
|
||||
: []
|
||||
: field.value || '';
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2164,7 +2213,14 @@ const handleTreeNodeClick = async (data: TreeNode) => {
|
||||
} else if (field.type === 'switch') {
|
||||
creationFormValues[fieldKey] = Boolean(field.value);
|
||||
} else {
|
||||
creationFormValues[fieldKey] = field.value || '';
|
||||
creationFormValues[fieldKey] =
|
||||
field.type === 'upload' || field.type === 'uploadMultiple' || field.type === 'fileUpload'
|
||||
? Array.isArray(field.value)
|
||||
? field.value
|
||||
: field.value
|
||||
? [field.value]
|
||||
: []
|
||||
: field.value || '';
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2288,7 +2344,7 @@ const removeCustomField = (index: number) => {
|
||||
customFields.value.splice(index, 1);
|
||||
};
|
||||
// 获取自定义字段的文件列表
|
||||
const getCustomFieldFileList = (index: number) => {
|
||||
const _getCustomFieldFileList = (index: number) => {
|
||||
const field = customFields.value[index];
|
||||
if (!field || !field.fileList) return [];
|
||||
return field.fileList;
|
||||
@@ -2752,7 +2808,7 @@ const handleFieldUpload = async (field: string, file: any, type: string) => {
|
||||
ElMessage.success('文件上传成功');
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.message || '文件上传失败');
|
||||
console.error('Upload error:', error);
|
||||
// 上传失败日志已省略
|
||||
// 上传失败时,递增 uploadKey 来重置上传组件
|
||||
fieldUploadKeys[field] = (fieldUploadKeys[field] || 0) + 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user