模型配置自定义表单
This commit is contained in:
@@ -3,5 +3,5 @@ ENV = 'development'
|
||||
|
||||
# 统一后端服务地址前缀(网关服务名:admin-go)
|
||||
# 开发环境走本地代理,避免 CORS
|
||||
VITE_API_URL = 'http://116.204.74.41:8000'
|
||||
# VITE_API_URL = 'http://192.168.3.30:8000'
|
||||
# VITE_API_URL = 'http://116.204.74.41:8000'
|
||||
VITE_API_URL = 'http://192.168.3.30:8000'
|
||||
@@ -1,4 +1,4 @@
|
||||
<template>
|
||||
<template>
|
||||
<div class="system-edit-module-container">
|
||||
<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="900px">
|
||||
<el-form
|
||||
@@ -213,13 +213,126 @@
|
||||
</template>
|
||||
</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 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-input v-model="field.value" placeholder="请输入字段值 (Value)" style="width: 40%" clearable></el-input>
|
||||
<el-button type="danger" link @click="removeFormField(index)">删除</el-button>
|
||||
<!-- 删除按钮 - 右上角 -->
|
||||
<el-button type="danger" link size="small" @click="removeFormField(index)" class="delete-btn">删除</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>
|
||||
<el-button type="primary" link @click="addFormField" style="margin-top: 10px">+ 添加字段</el-button>
|
||||
</div>
|
||||
@@ -426,6 +539,33 @@ import {
|
||||
|
||||
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(
|
||||
defineProps<{
|
||||
modelTypes?: ModelTypeOption[];
|
||||
@@ -492,6 +632,7 @@ const queryResponseTypeOptions = [
|
||||
{ label: '等候回调', value: 'callback' },
|
||||
{ label: '主动拉取', value: 'pull' },
|
||||
];
|
||||
|
||||
const state = reactive({
|
||||
ruleForm: {
|
||||
id: '',
|
||||
@@ -680,7 +821,7 @@ const state = reactive({
|
||||
},
|
||||
showAdvanced: false,
|
||||
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 }>,
|
||||
responseMappingFields: [] as Array<{ key: string; value: string; isMainBody?: boolean; isTokenField?: boolean }>,
|
||||
extendMappingFields: [] as Array<{ key: string; value: string }>,
|
||||
@@ -688,6 +829,25 @@ const state = reactive({
|
||||
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 obj: Record<string, string> = {};
|
||||
@@ -703,7 +863,7 @@ const parseHeaders = (headMsg: string) => parseKeyValueString(headMsg);
|
||||
// 统一的字段解析函数:支持数组、对象、JSON字符串
|
||||
const parseFieldsUnified = (raw: unknown): Array<{ key: string; value: string }> => {
|
||||
if (!raw) return [];
|
||||
|
||||
|
||||
// 如果是字符串,尝试解析为JSON
|
||||
if (typeof raw === 'string') {
|
||||
try {
|
||||
@@ -713,7 +873,7 @@ const parseFieldsUnified = (raw: unknown): Array<{ key: string; value: string }>
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果是数组格式 [{ key, value }]
|
||||
if (Array.isArray(raw)) {
|
||||
return (raw as ModelFormEntry[])
|
||||
@@ -723,13 +883,13 @@ const parseFieldsUnified = (raw: unknown): Array<{ key: string; value: string }>
|
||||
value: String(item.value ?? '').trim(),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
// 如果是对象格式 { key: value } 或者 { key: { value: value } }
|
||||
if (typeof raw === 'object') {
|
||||
const fields: Array<{ key: string; value: string }> = [];
|
||||
Object.keys(raw as Record<string, unknown>).forEach((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) {
|
||||
v = (v as { value: unknown }).value;
|
||||
}
|
||||
@@ -738,7 +898,7 @@ const parseFieldsUnified = (raw: unknown): Array<{ key: string; value: string }>
|
||||
});
|
||||
return fields;
|
||||
}
|
||||
|
||||
|
||||
return [];
|
||||
};
|
||||
// 解析 requestMapping 对象为数组
|
||||
@@ -882,7 +1042,7 @@ const confirmHeaders = () => {
|
||||
};
|
||||
// 添加表单字段
|
||||
const addFormField = () => {
|
||||
state.formFields.push({ key: '', value: '' });
|
||||
state.formFields.push(createEmptyFormField());
|
||||
};
|
||||
|
||||
// 删除表单字段
|
||||
@@ -890,6 +1050,29 @@ const removeFormField = (index: number) => {
|
||||
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 = () => {
|
||||
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 ensureResponseMappingRows = (rows: Array<{ key: string; value: string; isMainBody?: boolean; isTokenField?: boolean }>) => {
|
||||
if (!rows.length) return [{ key: '', value: '', isMainBody: false, isTokenField: false }];
|
||||
return rows.map((row) => ({ ...row, isMainBody: row.isMainBody || false, isTokenField: row.isTokenField || false }));
|
||||
// 解析旧格式的form数据兼容到新格式
|
||||
const parseFormFieldsUnified = (raw: unknown): Array<FormField> => {
|
||||
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 => {
|
||||
if (data == null) return null;
|
||||
if (Array.isArray(data)) return null;
|
||||
@@ -1018,9 +1225,9 @@ const fillFormFromDetailRow = (row: Record<string, unknown>) => {
|
||||
tokenConfig: '{}',
|
||||
};
|
||||
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.responseMappingFields = ensureResponseMappingRows(parseResponseMappingFields(row.responseMapping));
|
||||
state.responseMappingFields = ensureKeyValueRows(parseResponseMappingFields(row.responseMapping));
|
||||
state.extendMappingFields = ensureKeyValueRows(parseFieldsUnified(row.extendMapping));
|
||||
state.tokenConfigFields = ensureKeyValueRows(parseFieldsUnified(row.tokenConfig));
|
||||
|
||||
@@ -1135,7 +1342,7 @@ const openDialog = async (type: string, row?: Record<string, unknown>) => {
|
||||
tokenConfig: '{}',
|
||||
};
|
||||
state.headers = [{ key: '', value: '' }];
|
||||
state.formFields = [{ key: '', value: '' }];
|
||||
state.formFields = [createEmptyFormField()];
|
||||
state.requestMappingFields = [{ key: '', value: '' }];
|
||||
state.responseMappingFields = [{ key: '', value: '', isMainBody: false, isTokenField: false }];
|
||||
state.mainBodyIndex = -1;
|
||||
@@ -1174,33 +1381,54 @@ const onSubmit = () => {
|
||||
if (state.ruleForm.queryResponseType === 'pull') {
|
||||
await editModuleFormRef.value?.validateField?.('queryPullConfig');
|
||||
}
|
||||
|
||||
|
||||
// 验证响应映射(如果有配置)
|
||||
const validResponseFields = state.responseMappingFields.filter((x) => String(x.key || '').trim() !== '');
|
||||
if (validResponseFields.length > 0) {
|
||||
await editModuleFormRef.value?.validateField?.('responseMapping');
|
||||
}
|
||||
|
||||
|
||||
// 验证请求映射(如果有配置)
|
||||
const validRequestFields = state.requestMappingFields.filter((x) => String(x.key || '').trim() !== '');
|
||||
if (validRequestFields.length > 0) {
|
||||
await editModuleFormRef.value?.validateField?.('requestMapping');
|
||||
}
|
||||
|
||||
|
||||
state.ruleForm.headMsg = stringifyHeaders();
|
||||
|
||||
|
||||
// 过滤掉空键名的字段
|
||||
const requestMapping = fieldsToObject(state.requestMappingFields.filter((f) => String(f.key || '').trim() !== ''));
|
||||
const responseMapping = fieldsToObject(state.responseMappingFields.filter((f) => String(f.key || '').trim() !== ''));
|
||||
|
||||
|
||||
// 获取被设置为返回主体的字段 {key: value}
|
||||
const responseBodyField = state.responseMappingFields.find((f) => f.isMainBody && String(f.key || '').trim() !== '');
|
||||
const responseBody = responseBodyField ? { [responseBodyField.key.trim()]: responseBodyField.value } : {};
|
||||
|
||||
|
||||
// 获取计费字段(可选)
|
||||
const responseTokenField =
|
||||
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 = {
|
||||
modelName: state.ruleForm.modelName,
|
||||
modelType: state.ruleForm.modelType as number | string,
|
||||
@@ -1213,9 +1441,7 @@ const onSubmit = () => {
|
||||
isChatModel: state.ruleForm.isChatModel,
|
||||
// 确保 API 密钥只在 isPrivate=1 时提交
|
||||
apiKey: state.ruleForm.isPrivate === 1 ? String(state.ruleForm.apiKey ?? '').trim() : '',
|
||||
form: state.formFields
|
||||
.filter((f) => String(f.key || '').trim() !== '')
|
||||
.map((f) => ({ key: String(f.key).trim(), value: String(f.value ?? '') })),
|
||||
form: processedFormFields,
|
||||
requestMapping,
|
||||
responseMapping,
|
||||
responseBody,
|
||||
@@ -1276,15 +1502,89 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.form-config-container {
|
||||
max-height: 400px;
|
||||
max-height: 550px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
padding: 5px 0;
|
||||
|
||||
.form-field-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 12px;
|
||||
.form-field-item {
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 6px;
|
||||
padding: 16px 16px 12px 16px;
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user