feat: JSON编辑器增强 - Token映射下拉、字段选项key:value、序列化始终包装type

1. 模型配置弹窗: Token映射三个字段由输入框改为下拉菜单
   - 同步/流式从响应映射取key, 异步从回调响应映射取key
   - 切换返回类型时清空Token映射, 无映射配置时禁用并提示
2. JSON编辑器序列化: 所有节点始终输出{type, value/attrs}格式
   - 容器(object/array)始终{type, attrs}, 标量始终{type, value}
3. 字段配置增强:
   - 对象类型新增"循环生成"开关
   - 下拉控件选项列表改为key:value双输入框
4. 类型注释更新
This commit is contained in:
2026-07-15 16:33:58 +08:00
parent a6eaf57898
commit 5742608b97
5 changed files with 78 additions and 21 deletions
@@ -128,17 +128,23 @@
<el-row :gutter="20">
<el-col :xs="24" :sm="8" :md="8" :lg="8" :xl="8" class="mb20">
<el-form-item label="输入 Token 路径">
<el-input v-model="formData.tokenMapping.promptTokens" placeholder="如 data.usage.prompt_tokens" clearable />
<el-select v-model="formData.tokenMapping.promptTokens" :disabled="!hasMappingConfig" :placeholder="mappingPlaceholder" clearable style="width: 100%">
<el-option v-for="key in tokenMappingOptions" :key="key" :label="key" :value="key" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="8" :md="8" :lg="8" :xl="8" class="mb20">
<el-form-item label="输出 Token 路径">
<el-input v-model="formData.tokenMapping.completionTokens" placeholder="如 data.usage.completion_tokens" clearable />
<el-select v-model="formData.tokenMapping.completionTokens" :disabled="!hasMappingConfig" :placeholder="mappingPlaceholder" clearable style="width: 100%">
<el-option v-for="key in tokenMappingOptions" :key="key" :label="key" :value="key" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="8" :md="8" :lg="8" :xl="8" class="mb20">
<el-form-item label="总 Token 路径">
<el-input v-model="formData.tokenMapping.totalTokens" placeholder="如 data.usage.total_tokens" clearable />
<el-select v-model="formData.tokenMapping.totalTokens" :disabled="!hasMappingConfig" :placeholder="mappingPlaceholder" clearable style="width: 100%">
<el-option v-for="key in tokenMappingOptions" :key="key" :label="key" :value="key" />
</el-select>
</el-form-item>
</el-col>
</el-row>
@@ -379,6 +385,27 @@ watch(isReasoningModel, (val) => {
if (!val) formData.chatModel = false;
});
/** 根据返回类型获取 Token 映射下拉选项(响应映射的 key 列表) */
const tokenMappingOptions = computed(() => {
const source = formData.responseType === 2
? formData.asyncTaskMapping.responseMapping
: formData.responseMapping;
return source ? Object.keys(source) : [];
});
/** 是否有可用的映射配置 */
const hasMappingConfig = computed(() => tokenMappingOptions.value.length > 0);
/** 下拉占位文本 */
const mappingPlaceholder = computed(() =>
hasMappingConfig.value ? '请选择' : '请先配置响应映射'
);
/** 切换返回类型时清空 token mapping */
watch(() => formData.responseType, () => {
formData.tokenMapping = { ...defaultTokenMapping };
});
/** JSON 编辑器弹窗状态 */
const jsonEditorVisible = ref(false);
const jsonEditorData = ref<Record<string, unknown>>({});
@@ -547,10 +574,9 @@ const handleSubmit = async () => {
let asyncTaskMapping: AsyncTaskMapping | undefined;
let lastFrame: string | undefined;
if (formData.responseType === 3) {
const hasTokenConfig = Object.values(formData.tokenMapping).some((v) => v);
tokenMapping = hasTokenConfig ? { ...formData.tokenMapping } : undefined;
}
// 所有返回类型共用 Token 映射(存的是响应映射中的 key)
const hasTokenConfig = Object.values(formData.tokenMapping).some((v) => v);
tokenMapping = hasTokenConfig ? { ...formData.tokenMapping } : undefined;
if (formData.responseType === 2) {
const atm = formData.asyncTaskMapping;