feat: 增强模型配置与创建表单功能

- 在 NodeLibraryFormItem 和 WorkflowItem 接口中添加了新的字段,如 value、options、expand 和 outputParams,以支持更复杂的表单配置。
- 新增 getOperatorList 函数以获取服务商列表,并在 editModule.vue 中集成了运营商选择功能。
- 更新了模型创建和编辑逻辑,支持 tokenConfig 的 JSON 格式配置,确保更灵活的模型设置。
- 优化了文件上传处理,增加了上传状态管理,提升用户体验。
This commit is contained in:
2026-05-22 13:22:45 +08:00
parent 8b5eedb308
commit e32b01337a
6 changed files with 221 additions and 59 deletions

View File

@@ -23,6 +23,13 @@
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="运营商名称" prop="operatorName" required>
<el-select v-model="state.ruleForm.operatorName" placeholder="请选择运营商" clearable style="width: 100%">
<el-option v-for="item in operatorNameOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="模型服务地址" prop="baseUrl">
<el-input v-model="state.ruleForm.baseUrl" placeholder="请输入模型服务地址" clearable></el-input>
@@ -150,6 +157,17 @@
<el-input v-model="state.ruleForm.tokenMapping" placeholder="请输入Token映射" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="Token计算配置" prop="tokenConfig">
<el-input
v-model="state.ruleForm.tokenConfig"
type="textarea"
:rows="4"
placeholder="请输入 JSON 对象,例如:{promptRate: 1, completionRate: 1}"
clearable
></el-input>
</el-form-item>
</el-col>
</el-row>
</el-collapse-transition>
</el-form>
@@ -243,11 +261,17 @@
</template>
<script setup lang="ts" name="systemEditModule">
import { reactive, ref, computed } from 'vue';
import { reactive, ref, computed, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { ArrowUp, ArrowDown } from '@element-plus/icons-vue';
import { addModelModule, updateModelModule, getModelModuleDetail, type ModelFormEntry } from '/@/api/settings/modelConfig/modelModule/index';
import {
addModelModule,
updateModelModule,
getModelModuleDetail,
getOperatorList,
type ModelFormEntry,
} from '/@/api/settings/modelConfig/modelModule/index';
export type ModelTypeOption = { id: number | string; label: string };
@@ -264,6 +288,20 @@ const props = withDefaults(
const modelTypeOptions = computed(() => props.modelTypes);
const operatorNameOptions = ref<Array<{ label: string; value: string }>>([]);
const loadOperatorOptions = async () => {
try {
const res: any = await getOperatorList();
const list = res?.data?.list;
operatorNameOptions.value = Array.isArray(list)
? list.filter((item: unknown) => typeof item === 'string' && item.trim() !== '').map((name: string) => ({ label: name, value: name }))
: [];
} catch {
operatorNameOptions.value = [];
}
};
const typeOptionValue = (id: number | string): number | string => {
const n = Number(id);
return Number.isNaN(n) ? id : n;
@@ -280,6 +318,7 @@ const state = reactive({
id: '',
modelName: '',
modelType: null as number | string | null,
operatorName: '',
baseUrl: '',
httpMethod: 'POST',
headMsg: '',
@@ -296,6 +335,7 @@ const state = reactive({
autoCleanSeconds: 300,
remark: '',
tokenMapping: '',
tokenConfig: '{}',
},
rules: {
modelName: [{ required: true, message: '请输入模型名称', trigger: 'blur' }],
@@ -313,6 +353,7 @@ const state = reactive({
],
baseUrl: [{ required: true, message: '请输入模型服务地址', trigger: 'blur' }],
httpMethod: [{ required: true, message: '请选择请求方式', trigger: 'change' }],
operatorName: [{ required: true, message: '请选择运营商名称', trigger: 'change' }],
apiKey: [
{
validator: (_rule: unknown, value: unknown, callback: (e?: Error) => void) => {
@@ -339,6 +380,27 @@ const state = reactive({
queueLimit: [{ required: true, message: '请输入排队队列上限', trigger: 'blur' }],
timeoutSeconds: [{ required: true, message: '请输入请求超时时间', trigger: 'blur' }],
expectedSeconds: [{ required: true, message: '请输入预计执行时间', trigger: 'blur' }],
tokenConfig: [
{
validator: (_rule: unknown, value: unknown, callback: (e?: Error) => void) => {
if (value === undefined || value === null || String(value).trim() === '') {
callback();
return;
}
try {
const parsed = JSON.parse(String(value));
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
callback();
return;
}
callback(new Error('Token计算配置必须为 JSON 对象'));
} catch {
callback(new Error('Token计算配置 JSON 格式不正确'));
}
},
trigger: 'blur',
},
],
},
dialog: {
isShowDialog: false,
@@ -402,12 +464,6 @@ const parseResponseMappingFields = (mapping: unknown) => {
return Object.entries(mapping).map(([key, value]) => ({ key, value: String(value) }));
};
const buildFormArray = (): ModelFormEntry[] => {
return state.formFields
.filter((field) => field.key?.trim() && field.value?.trim())
.map((field) => ({ key: field.key.trim(), value: field.value.trim() }));
};
const parseKeyValueString = (raw: string) => {
if (!raw) return [];
const headers: Array<{ key: string; value: string }> = [];
@@ -551,6 +607,7 @@ const fillFormFromDetailRow = (row: Record<string, unknown>) => {
id: row.id as string,
modelName: String(row.modelName ?? ''),
modelType: row.modelType !== undefined && row.modelType !== null ? typeOptionValue(row.modelType as number | string) : null,
operatorName: String(row.operatorName ?? ''),
baseUrl: String(row.baseUrl ?? ''),
httpMethod: String(row.httpMethod || 'POST'),
headMsg: String(row.headMsg || ''),
@@ -567,6 +624,10 @@ const fillFormFromDetailRow = (row: Record<string, unknown>) => {
autoCleanSeconds: Number(row.autoCleanSeconds ?? 300),
remark: String(row.remark || ''),
tokenMapping: String(row.tokenMapping || ''),
tokenConfig:
typeof row.tokenConfig === 'string'
? row.tokenConfig
: JSON.stringify((row.tokenConfig as Record<string, unknown>) || {}, null, 2),
};
state.headers = ensureKeyValueRows(parseHeaders(String(row.headMsg || '')));
state.formFields = ensureKeyValueRows(parseFormFields(row.form));
@@ -628,6 +689,7 @@ const openDialog = async (type: string, row?: Record<string, unknown>) => {
id: '',
modelName: '',
modelType: null,
operatorName: '',
baseUrl: '',
httpMethod: 'POST',
headMsg: '',
@@ -644,6 +706,7 @@ const openDialog = async (type: string, row?: Record<string, unknown>) => {
autoCleanSeconds: 300,
remark: '',
tokenMapping: '',
tokenConfig: '{}',
};
state.headers = [{ key: '', value: '' }];
state.formFields = [{ key: '', value: '' }];
@@ -682,6 +745,7 @@ const onSubmit = () => {
const submitData = {
modelName: state.ruleForm.modelName,
modelType: state.ruleForm.modelType as number | string,
operatorName: state.ruleForm.operatorName,
baseUrl: state.ruleForm.baseUrl,
httpMethod: state.ruleForm.httpMethod || 'POST',
headMsg: state.ruleForm.headMsg,
@@ -702,6 +766,7 @@ const onSubmit = () => {
autoCleanSeconds: state.ruleForm.autoCleanSeconds,
remark: state.ruleForm.remark || '',
tokenMapping: state.ruleForm.tokenMapping || '',
tokenConfig: parseJsonObjectField(state.ruleForm.tokenConfig || '{}', {}),
};
if (state.dialog.type === 'edit') {
@@ -725,6 +790,10 @@ const onSubmit = () => {
defineExpose({
openDialog,
});
onMounted(() => {
loadOperatorOptions();
});
</script>
<style scoped lang="scss">
@@ -773,7 +842,3 @@ defineExpose({
color: #606266;
}
</style>