模型配置自定义表单
This commit is contained in:
@@ -3,5 +3,5 @@ ENV = 'development'
|
|||||||
|
|
||||||
# 统一后端服务地址前缀(网关服务名:admin-go)
|
# 统一后端服务地址前缀(网关服务名:admin-go)
|
||||||
# 开发环境走本地代理,避免 CORS
|
# 开发环境走本地代理,避免 CORS
|
||||||
VITE_API_URL = 'http://116.204.74.41:8000'
|
# VITE_API_URL = 'http://116.204.74.41:8000'
|
||||||
# VITE_API_URL = 'http://192.168.3.30:8000'
|
VITE_API_URL = 'http://192.168.3.30:8000'
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="system-edit-module-container">
|
<div class="system-edit-module-container">
|
||||||
<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="900px">
|
<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="900px">
|
||||||
<el-form
|
<el-form
|
||||||
@@ -213,13 +213,126 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<!-- 自定义字段配置弹窗 -->
|
<!-- 自定义字段配置弹窗 -->
|
||||||
<el-dialog v-model="showFormDialog" title="配置表单字段" width="600px" :close-on-click-modal="false">
|
<el-dialog v-model="showFormDialog" title="配置表单字段" width="900px" :close-on-click-modal="false">
|
||||||
<div class="form-config-container">
|
<div class="form-config-container">
|
||||||
<div v-for="(field, index) in state.formFields" :key="index" class="form-field-item">
|
<div v-for="(field, index) in state.formFields" :key="index" class="form-field-item">
|
||||||
<el-input v-model="field.key" placeholder="请输入字段名 (Key)" style="width: 40%" clearable></el-input>
|
<!-- 删除按钮 - 右上角 -->
|
||||||
<span class="separator">=</span>
|
<el-button type="danger" link size="small" @click="removeFormField(index)" class="delete-btn">删除</el-button>
|
||||||
<el-input v-model="field.value" placeholder="请输入字段值 (Value)" style="width: 40%" clearable></el-input>
|
|
||||||
<el-button type="danger" link @click="removeFormField(index)">删除</el-button>
|
<!-- 第一行 -->
|
||||||
|
<div class="field-row">
|
||||||
|
<div class="field-col field-col-sm">
|
||||||
|
<label class="field-label required">字段描述</label>
|
||||||
|
<el-input v-model="field.label" placeholder="字段显示名称" clearable style="width: 100%"></el-input>
|
||||||
|
</div>
|
||||||
|
<div class="field-col field-col-sm">
|
||||||
|
<label class="field-label required">字段名称</label>
|
||||||
|
<el-input v-model="field.key" placeholder="字段Key,如 user_name" clearable style="width: 100%"></el-input>
|
||||||
|
</div>
|
||||||
|
<div class="field-col field-col-sm">
|
||||||
|
<label class="field-label required">字段类型</label>
|
||||||
|
<el-select v-model="field.type" placeholder="选择字段类型" clearable style="width: 100%" @change="onFieldTypeChange(index)">
|
||||||
|
<el-option label="字符串" value="string"></el-option>
|
||||||
|
<el-option label="数字" value="number"></el-option>
|
||||||
|
<el-option label="下拉菜单" value="select"></el-option>
|
||||||
|
<el-option label="单选按钮" value="radio"></el-option>
|
||||||
|
<el-option label="附件上传" value="file"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="field-col field-col-sm">
|
||||||
|
<label class="field-label">默认值</label>
|
||||||
|
<el-input v-model="field.defaultValue" placeholder="默认值" clearable style="width: 100%"></el-input>
|
||||||
|
</div>
|
||||||
|
<div class="field-col field-col-sm">
|
||||||
|
<label class="field-label">是否必填</label>
|
||||||
|
<div class="switch-wrapper">
|
||||||
|
<el-switch v-model="field.required"></el-switch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 第二行:类型特定配置 -->
|
||||||
|
<div class="field-row">
|
||||||
|
<!-- 字符串额外配置 -->
|
||||||
|
<template v-if="field.type === 'string'">
|
||||||
|
<div class="field-col field-col-sm">
|
||||||
|
<label class="field-label">最大长度</label>
|
||||||
|
<el-input-number v-model="field.maxLength" :min="0" :max="10000" placeholder="最大长度" style="width: 100%"></el-input-number>
|
||||||
|
</div>
|
||||||
|
<div class="field-col"></div>
|
||||||
|
<div class="field-col"></div>
|
||||||
|
<div class="field-col"></div>
|
||||||
|
<div class="field-col"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 数字额外配置 -->
|
||||||
|
<template v-if="field.type === 'number'">
|
||||||
|
<div class="field-col field-col-sm">
|
||||||
|
<label class="field-label">数字类型</label>
|
||||||
|
<el-select v-model="field.numberType" placeholder="选择数字类型" clearable style="width: 100%">
|
||||||
|
<el-option label="任意数字" value="any"></el-option>
|
||||||
|
<el-option label="整数" value="integer"></el-option>
|
||||||
|
<el-option label="浮点数(小数)" value="float"></el-option>
|
||||||
|
<el-option label="正整数" value="positive-int"></el-option>
|
||||||
|
<el-option label="正浮点数" value="positive-float"></el-option>
|
||||||
|
<el-option label="负整数" value="negative-int"></el-option>
|
||||||
|
<el-option label="负浮点数" value="negative-float"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="field-col field-col-sm">
|
||||||
|
<label class="field-label">最小值</label>
|
||||||
|
<el-input-number v-model="field.min" placeholder="最小值" style="width: 100%"></el-input-number>
|
||||||
|
</div>
|
||||||
|
<div class="field-col field-col-sm">
|
||||||
|
<label class="field-label">最大值</label>
|
||||||
|
<el-input-number v-model="field.max" placeholder="最大值" style="width: 100%"></el-input-number>
|
||||||
|
</div>
|
||||||
|
<div class="field-col"></div>
|
||||||
|
<div class="field-col"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 附件上传额外配置 -->
|
||||||
|
<template v-if="field.type === 'file'">
|
||||||
|
<div class="field-col field-col-sm">
|
||||||
|
<label class="field-label">最大文件(MB)</label>
|
||||||
|
<el-input-number v-model="field.maxSize" :min="1" :max="1000" style="width: 100%"></el-input-number>
|
||||||
|
</div>
|
||||||
|
<div class="field-col field-col-sm">
|
||||||
|
<label class="field-label">最大上传数量</label>
|
||||||
|
<el-input-number v-model="field.maxCount" :min="1" :max="100" :default-value="1" style="width: 100%"></el-input-number>
|
||||||
|
</div>
|
||||||
|
<div class="field-col field-col-md">
|
||||||
|
<label class="field-label">允许格式</label>
|
||||||
|
<el-input v-model="field.allowedTypes" placeholder=".jpg,.png,.pdf" clearable style="width: 100%"></el-input>
|
||||||
|
</div>
|
||||||
|
<div class="field-col"></div>
|
||||||
|
<div class="field-col"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 下拉框和单选不需要第二行额外配置 -->
|
||||||
|
<template v-if="field.type === 'select' || field.type === 'radio'">
|
||||||
|
<div class="field-col"></div>
|
||||||
|
<div class="field-col"></div>
|
||||||
|
<div class="field-col"></div>
|
||||||
|
<div class="field-col"></div>
|
||||||
|
<div class="field-col"></div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 下拉菜单/单选框 选项配置 -->
|
||||||
|
<div v-if="field.type === 'select' || field.type === 'radio'" class="options-row">
|
||||||
|
<div class="options-label">选项配置</div>
|
||||||
|
<div v-for="(opt, optIndex) in field.options" :key="optIndex" class="option-item">
|
||||||
|
<div class="option-col">
|
||||||
|
<el-input v-model="opt.label" placeholder="显示文本" style="width: 100%" clearable></el-input>
|
||||||
|
</div>
|
||||||
|
<span class="separator">:</span>
|
||||||
|
<div class="option-col">
|
||||||
|
<el-input v-model="opt.value" placeholder="值" style="width: 100%" clearable></el-input>
|
||||||
|
</div>
|
||||||
|
<el-button type="danger" link size="small" @click="removeFieldOption(field, optIndex)">删除</el-button>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" link size="small" @click="addFieldOption(field)">+ 添加选项</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-button type="primary" link @click="addFormField" style="margin-top: 10px">+ 添加字段</el-button>
|
<el-button type="primary" link @click="addFormField" style="margin-top: 10px">+ 添加字段</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -426,6 +539,33 @@ import {
|
|||||||
|
|
||||||
export type ModelTypeOption = { id: number | string; label: string };
|
export type ModelTypeOption = { id: number | string; label: string };
|
||||||
|
|
||||||
|
// 定义自定义字段选项类型
|
||||||
|
export interface FormFieldOption {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义自定义字段类型
|
||||||
|
export interface FormField {
|
||||||
|
label: string; // 字段描述
|
||||||
|
key: string; // 字段名称
|
||||||
|
type: 'string' | 'number' | 'select' | 'radio' | 'file'; // 字段类型
|
||||||
|
defaultValue: string; // 默认值
|
||||||
|
required: boolean; // 是否必填
|
||||||
|
// 字符串配置
|
||||||
|
maxLength?: number; // 最大长度
|
||||||
|
// 数字配置
|
||||||
|
min?: number; // 最小值
|
||||||
|
max?: number; // 最大值
|
||||||
|
numberType?: 'any' | 'integer' | 'float' | 'positive-int' | 'positive-float' | 'negative-int' | 'negative-float'; // 数字子类型
|
||||||
|
// 文件上传配置
|
||||||
|
maxSize?: number; // 最大文件大小(MB)
|
||||||
|
maxCount?: number; // 最大上传数量
|
||||||
|
allowedTypes?: string; // 允许的文件格式,逗号分隔
|
||||||
|
// 下拉框/单选框配置
|
||||||
|
options?: FormFieldOption[]; // 选项列表
|
||||||
|
}
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
modelTypes?: ModelTypeOption[];
|
modelTypes?: ModelTypeOption[];
|
||||||
@@ -492,6 +632,7 @@ const queryResponseTypeOptions = [
|
|||||||
{ label: '等候回调', value: 'callback' },
|
{ label: '等候回调', value: 'callback' },
|
||||||
{ label: '主动拉取', value: 'pull' },
|
{ label: '主动拉取', value: 'pull' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
ruleForm: {
|
ruleForm: {
|
||||||
id: '',
|
id: '',
|
||||||
@@ -680,7 +821,7 @@ const state = reactive({
|
|||||||
},
|
},
|
||||||
showAdvanced: false,
|
showAdvanced: false,
|
||||||
headers: [] as Array<{ key: string; value: string }>,
|
headers: [] as Array<{ key: string; value: string }>,
|
||||||
formFields: [] as Array<{ key: string; value: string }>,
|
formFields: [] as Array<FormField>,
|
||||||
requestMappingFields: [] as Array<{ key: string; value: string }>,
|
requestMappingFields: [] as Array<{ key: string; value: string }>,
|
||||||
responseMappingFields: [] as Array<{ key: string; value: string; isMainBody?: boolean; isTokenField?: boolean }>,
|
responseMappingFields: [] as Array<{ key: string; value: string; isMainBody?: boolean; isTokenField?: boolean }>,
|
||||||
extendMappingFields: [] as Array<{ key: string; value: string }>,
|
extendMappingFields: [] as Array<{ key: string; value: string }>,
|
||||||
@@ -688,6 +829,25 @@ const state = reactive({
|
|||||||
mainBodyIndex: -1, // 记录哪一行被设置为返回主体
|
mainBodyIndex: -1, // 记录哪一行被设置为返回主体
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 创建空字段
|
||||||
|
const createEmptyFormField = (): FormField => {
|
||||||
|
return {
|
||||||
|
label: '',
|
||||||
|
key: '',
|
||||||
|
type: 'string',
|
||||||
|
defaultValue: '',
|
||||||
|
required: false,
|
||||||
|
maxLength: undefined,
|
||||||
|
min: undefined,
|
||||||
|
max: undefined,
|
||||||
|
numberType: 'any',
|
||||||
|
maxSize: 10,
|
||||||
|
maxCount: 1,
|
||||||
|
allowedTypes: '',
|
||||||
|
options: [{ label: '', value: '' }],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// 将数组转换为对象
|
// 将数组转换为对象
|
||||||
const fieldsToObject = (fields: Array<{ key: string; value: string }>) => {
|
const fieldsToObject = (fields: Array<{ key: string; value: string }>) => {
|
||||||
const obj: Record<string, string> = {};
|
const obj: Record<string, string> = {};
|
||||||
@@ -729,7 +889,7 @@ const parseFieldsUnified = (raw: unknown): Array<{ key: string; value: string }>
|
|||||||
const fields: Array<{ key: string; value: string }> = [];
|
const fields: Array<{ key: string; value: string }> = [];
|
||||||
Object.keys(raw as Record<string, unknown>).forEach((key) => {
|
Object.keys(raw as Record<string, unknown>).forEach((key) => {
|
||||||
let v = (raw as Record<string, unknown>)[key];
|
let v = (raw as Record<string, unknown>)[key];
|
||||||
// 处理 { key: { value: "value" } } 格式(后端可能返回这种结构)
|
// 处理 { key: { value: value } } 格式(后端可能返回这种结构)
|
||||||
if (v && typeof v === 'object' && !Array.isArray(v) && 'value' in v) {
|
if (v && typeof v === 'object' && !Array.isArray(v) && 'value' in v) {
|
||||||
v = (v as { value: unknown }).value;
|
v = (v as { value: unknown }).value;
|
||||||
}
|
}
|
||||||
@@ -882,7 +1042,7 @@ const confirmHeaders = () => {
|
|||||||
};
|
};
|
||||||
// 添加表单字段
|
// 添加表单字段
|
||||||
const addFormField = () => {
|
const addFormField = () => {
|
||||||
state.formFields.push({ key: '', value: '' });
|
state.formFields.push(createEmptyFormField());
|
||||||
};
|
};
|
||||||
|
|
||||||
// 删除表单字段
|
// 删除表单字段
|
||||||
@@ -890,6 +1050,29 @@ const removeFormField = (index: number) => {
|
|||||||
state.formFields.splice(index, 1);
|
state.formFields.splice(index, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 字段类型变化时初始化默认配置
|
||||||
|
const onFieldTypeChange = (index: number) => {
|
||||||
|
const field = state.formFields[index];
|
||||||
|
if ((field.type === 'select' || field.type === 'radio') && !field.options) {
|
||||||
|
field.options = [{ label: '', value: '' }];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加选项
|
||||||
|
const addFieldOption = (field: FormField) => {
|
||||||
|
if (!field.options) {
|
||||||
|
field.options = [];
|
||||||
|
}
|
||||||
|
field.options.push({ label: '', value: '' });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除选项
|
||||||
|
const removeFieldOption = (field: FormField, optIndex: number) => {
|
||||||
|
if (field.options) {
|
||||||
|
field.options.splice(optIndex, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 确认表单字段配置
|
// 确认表单字段配置
|
||||||
const confirmFormFields = () => {
|
const confirmFormFields = () => {
|
||||||
showFormDialog.value = false;
|
showFormDialog.value = false;
|
||||||
@@ -959,12 +1142,36 @@ const setPullMainBody = (index: number) => {
|
|||||||
|
|
||||||
const ensureKeyValueRows = (rows: Array<{ key: string; value: string }>) => (rows.length ? rows : [{ key: '', value: '' }]);
|
const ensureKeyValueRows = (rows: Array<{ key: string; value: string }>) => (rows.length ? rows : [{ key: '', value: '' }]);
|
||||||
|
|
||||||
const ensureResponseMappingRows = (rows: Array<{ key: string; value: string; isMainBody?: boolean; isTokenField?: boolean }>) => {
|
// 解析旧格式的form数据兼容到新格式
|
||||||
if (!rows.length) return [{ key: '', value: '', isMainBody: false, isTokenField: false }];
|
const parseFormFieldsUnified = (raw: unknown): Array<FormField> => {
|
||||||
return rows.map((row) => ({ ...row, isMainBody: row.isMainBody || false, isTokenField: row.isTokenField || false }));
|
if (!raw) return [createEmptyFormField()];
|
||||||
|
|
||||||
|
// 如果已经是新格式数组(每个元素有 type 字段),直接返回
|
||||||
|
if (Array.isArray(raw)) {
|
||||||
|
if (raw.length === 0) return [createEmptyFormField()];
|
||||||
|
// 确保新增字段有默认值
|
||||||
|
return (raw as any[]).map(item => {
|
||||||
|
const field = createEmptyFormField();
|
||||||
|
return { ...field, ...item };
|
||||||
|
}) as FormField[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 旧格式对象 { key: value } -> 转换为新格式数组
|
||||||
|
if (typeof raw === 'object') {
|
||||||
|
const fields: FormField[] = [];
|
||||||
|
Object.entries(raw as Record<string, unknown>).forEach(([key, value]) => {
|
||||||
|
const field = createEmptyFormField();
|
||||||
|
field.key = key;
|
||||||
|
field.label = key;
|
||||||
|
field.defaultValue = String(value ?? '').trim();
|
||||||
|
fields.push(field);
|
||||||
|
});
|
||||||
|
return fields.length ? fields : [createEmptyFormField()];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [createEmptyFormField()];
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 从 getModel 返回的 data 中取出单条模型对象 */
|
|
||||||
const unwrapModelDetailPayload = (data: unknown): Record<string, unknown> | null => {
|
const unwrapModelDetailPayload = (data: unknown): Record<string, unknown> | null => {
|
||||||
if (data == null) return null;
|
if (data == null) return null;
|
||||||
if (Array.isArray(data)) return null;
|
if (Array.isArray(data)) return null;
|
||||||
@@ -1018,9 +1225,9 @@ const fillFormFromDetailRow = (row: Record<string, unknown>) => {
|
|||||||
tokenConfig: '{}',
|
tokenConfig: '{}',
|
||||||
};
|
};
|
||||||
state.headers = ensureKeyValueRows(parseHeaders(String(row.headMsg || '')));
|
state.headers = ensureKeyValueRows(parseHeaders(String(row.headMsg || '')));
|
||||||
state.formFields = ensureKeyValueRows(parseFieldsUnified(row.form));
|
state.formFields = parseFormFieldsUnified(row.form);
|
||||||
state.requestMappingFields = ensureKeyValueRows(parseRequestMappingFields(row.requestMapping));
|
state.requestMappingFields = ensureKeyValueRows(parseRequestMappingFields(row.requestMapping));
|
||||||
state.responseMappingFields = ensureResponseMappingRows(parseResponseMappingFields(row.responseMapping));
|
state.responseMappingFields = ensureKeyValueRows(parseResponseMappingFields(row.responseMapping));
|
||||||
state.extendMappingFields = ensureKeyValueRows(parseFieldsUnified(row.extendMapping));
|
state.extendMappingFields = ensureKeyValueRows(parseFieldsUnified(row.extendMapping));
|
||||||
state.tokenConfigFields = ensureKeyValueRows(parseFieldsUnified(row.tokenConfig));
|
state.tokenConfigFields = ensureKeyValueRows(parseFieldsUnified(row.tokenConfig));
|
||||||
|
|
||||||
@@ -1135,7 +1342,7 @@ const openDialog = async (type: string, row?: Record<string, unknown>) => {
|
|||||||
tokenConfig: '{}',
|
tokenConfig: '{}',
|
||||||
};
|
};
|
||||||
state.headers = [{ key: '', value: '' }];
|
state.headers = [{ key: '', value: '' }];
|
||||||
state.formFields = [{ key: '', value: '' }];
|
state.formFields = [createEmptyFormField()];
|
||||||
state.requestMappingFields = [{ key: '', value: '' }];
|
state.requestMappingFields = [{ key: '', value: '' }];
|
||||||
state.responseMappingFields = [{ key: '', value: '', isMainBody: false, isTokenField: false }];
|
state.responseMappingFields = [{ key: '', value: '', isMainBody: false, isTokenField: false }];
|
||||||
state.mainBodyIndex = -1;
|
state.mainBodyIndex = -1;
|
||||||
@@ -1201,6 +1408,27 @@ const onSubmit = () => {
|
|||||||
const responseTokenField =
|
const responseTokenField =
|
||||||
state.responseMappingFields.find((f) => f.isTokenField)?.key?.trim() || String(state.ruleForm.responseTokenField || '').trim();
|
state.responseMappingFields.find((f) => f.isTokenField)?.key?.trim() || String(state.ruleForm.responseTokenField || '').trim();
|
||||||
|
|
||||||
|
// 过滤掉空key的自定义字段
|
||||||
|
const processedFormFields = state.formFields
|
||||||
|
.filter((f) => String(f.key || '').trim() !== '')
|
||||||
|
.map((f) => ({
|
||||||
|
label: f.label?.trim() || f.key,
|
||||||
|
key: String(f.key).trim(),
|
||||||
|
type: f.type,
|
||||||
|
defaultValue: f.defaultValue || '',
|
||||||
|
required: Boolean(f.required),
|
||||||
|
maxLength: f.type === 'string' && f.maxLength ? f.maxLength : undefined,
|
||||||
|
min: f.type === 'number' && f.min !== undefined ? f.min : undefined,
|
||||||
|
max: f.type === 'number' && f.max !== undefined ? f.max : undefined,
|
||||||
|
numberType: f.type === 'number' ? f.numberType || 'any' : undefined,
|
||||||
|
maxSize: f.type === 'file' && f.maxSize ? f.maxSize : undefined,
|
||||||
|
maxCount: f.type === 'file' && f.maxCount ? f.maxCount : undefined,
|
||||||
|
allowedTypes: f.type === 'file' && f.allowedTypes ? f.allowedTypes.trim() : undefined,
|
||||||
|
options: (f.type === 'select' || f.type === 'radio') && f.options
|
||||||
|
? f.options.filter(opt => String(opt.label || '').trim() !== '' || String(opt.value || '').trim() !== '')
|
||||||
|
: undefined,
|
||||||
|
}));
|
||||||
|
|
||||||
const submitData: CreateModelParams = {
|
const submitData: CreateModelParams = {
|
||||||
modelName: state.ruleForm.modelName,
|
modelName: state.ruleForm.modelName,
|
||||||
modelType: state.ruleForm.modelType as number | string,
|
modelType: state.ruleForm.modelType as number | string,
|
||||||
@@ -1213,9 +1441,7 @@ const onSubmit = () => {
|
|||||||
isChatModel: state.ruleForm.isChatModel,
|
isChatModel: state.ruleForm.isChatModel,
|
||||||
// 确保 API 密钥只在 isPrivate=1 时提交
|
// 确保 API 密钥只在 isPrivate=1 时提交
|
||||||
apiKey: state.ruleForm.isPrivate === 1 ? String(state.ruleForm.apiKey ?? '').trim() : '',
|
apiKey: state.ruleForm.isPrivate === 1 ? String(state.ruleForm.apiKey ?? '').trim() : '',
|
||||||
form: state.formFields
|
form: processedFormFields,
|
||||||
.filter((f) => String(f.key || '').trim() !== '')
|
|
||||||
.map((f) => ({ key: String(f.key).trim(), value: String(f.value ?? '') })),
|
|
||||||
requestMapping,
|
requestMapping,
|
||||||
responseMapping,
|
responseMapping,
|
||||||
responseBody,
|
responseBody,
|
||||||
@@ -1276,15 +1502,89 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.form-config-container {
|
.form-config-container {
|
||||||
max-height: 400px;
|
max-height: 550px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
padding: 5px 0;
|
||||||
|
|
||||||
.form-field-item {
|
.form-field-item {
|
||||||
display: flex;
|
border: 1px solid #e4e7ed;
|
||||||
align-items: center;
|
border-radius: 6px;
|
||||||
gap: 10px;
|
padding: 16px 16px 12px 16px;
|
||||||
margin-bottom: 12px;
|
padding-right: 80px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
background-color: #fafafa;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.delete-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.field-col {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
&.field-col-sm {
|
||||||
|
flex: 0 0 140px;
|
||||||
|
}
|
||||||
|
&.field-col-md {
|
||||||
|
flex: 0 0 200px;
|
||||||
|
}
|
||||||
|
&.field-col-xs {
|
||||||
|
flex: 0 0 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #606266;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
line-height: 1.2;
|
||||||
|
|
||||||
|
&.required::before {
|
||||||
|
content: '*';
|
||||||
|
color: #f56c6c;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-wrapper {
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.options-row {
|
||||||
|
margin-top: 8px;
|
||||||
|
padding-top: 16px;
|
||||||
|
border-top: 1px dashed #e4e7ed;
|
||||||
|
|
||||||
|
.options-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.option-col {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 180px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ml5 {
|
.ml5 {
|
||||||
|
|||||||
Reference in New Issue
Block a user