feat(json-schema): ms-json-schema组件-细节交互&预览
This commit is contained in:
parent
cc26849807
commit
ce391aac3a
|
@ -603,7 +603,7 @@
|
||||||
showDrag: true,
|
showDrag: true,
|
||||||
columnSelectorDisabled: true,
|
columnSelectorDisabled: true,
|
||||||
addLineDisabled: true,
|
addLineDisabled: true,
|
||||||
typeOptions: [
|
options: [
|
||||||
{ label: 'object', value: 'object' },
|
{ label: 'object', value: 'object' },
|
||||||
{ label: 'array', value: 'array' },
|
{ label: 'array', value: 'array' },
|
||||||
{ label: 'string', value: 'string' },
|
{ label: 'string', value: 'string' },
|
||||||
|
|
|
@ -828,7 +828,7 @@
|
||||||
* @param fullJson 脑图导出的完整数据
|
* @param fullJson 脑图导出的完整数据
|
||||||
* @param callback 保存成功回调
|
* @param callback 保存成功回调
|
||||||
*/
|
*/
|
||||||
async function handleMinderSave(fullJson: MinderJson, callback: () => void) {
|
async function handleMinderSave(fullJson: MinderJson, callback: (refersh: boolean) => void) {
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
await saveCaseMinder(makeMinderParams(fullJson));
|
await saveCaseMinder(makeMinderParams(fullJson));
|
||||||
|
@ -836,7 +836,7 @@
|
||||||
Message.success(t('common.saveSuccess'));
|
Message.success(t('common.saveSuccess'));
|
||||||
resetMinderParams();
|
resetMinderParams();
|
||||||
emit('save');
|
emit('save');
|
||||||
callback();
|
callback(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|
|
@ -1,61 +1,63 @@
|
||||||
<template>
|
<template>
|
||||||
<a-popover position="tl" :disabled="!modelValue || modelValue.trim() === ''" class="ms-params-input-popover">
|
<div>
|
||||||
<template #content>
|
<a-popover position="tl" :disabled="!modelValue || modelValue.trim() === ''" class="ms-params-input-popover">
|
||||||
<div v-if="props.title" class="ms-params-popover-title">
|
<template #content>
|
||||||
{{ props.title }}
|
<div v-if="props.title" class="ms-params-popover-title">
|
||||||
</div>
|
{{ props.title }}
|
||||||
<div class="ms-params-popover-value">
|
</div>
|
||||||
{{ modelValue }}
|
<div class="ms-params-popover-value">
|
||||||
</div>
|
{{ props.popoverTitle || modelValue }}
|
||||||
</template>
|
</div>
|
||||||
<a-input
|
</template>
|
||||||
v-if="props.type === 'input'"
|
<a-input
|
||||||
ref="inputRef"
|
v-if="props.type === 'input'"
|
||||||
v-model:model-value="modelValue"
|
ref="inputRef"
|
||||||
:class="props.class"
|
v-model:model-value="modelValue"
|
||||||
:disabled="props.disabled"
|
:class="props.class"
|
||||||
:size="props.size"
|
:disabled="props.disabled"
|
||||||
:max-length="props.maxLength"
|
:size="props.size"
|
||||||
:placeholder="props.placeholder"
|
:max-length="props.maxLength"
|
||||||
:trigger-props="{ contentClass: 'ms-form-table-input-trigger' }"
|
:placeholder="props.placeholder"
|
||||||
:allow-clear="props.allowClear"
|
:trigger-props="{ contentClass: 'ms-form-table-input-trigger' }"
|
||||||
@input="(val) => emit('input', val)"
|
:allow-clear="props.allowClear"
|
||||||
@change="(val) => emit('change', val)"
|
@input="(val) => emit('input', val)"
|
||||||
/>
|
@change="(val) => emit('change', val)"
|
||||||
<a-textarea
|
/>
|
||||||
v-else
|
<a-textarea
|
||||||
ref="inputRef"
|
v-else
|
||||||
v-model:model-value="modelValue"
|
ref="inputRef"
|
||||||
:class="props.class"
|
v-model:model-value="modelValue"
|
||||||
:disabled="props.disabled"
|
:class="props.class"
|
||||||
:size="props.size"
|
:disabled="props.disabled"
|
||||||
:placeholder="props.placeholder"
|
:size="props.size"
|
||||||
:max-length="props.maxLength"
|
:placeholder="props.placeholder"
|
||||||
:auto-size="{ minRows: 1, maxRows: 1 }"
|
:max-length="props.maxLength"
|
||||||
:allow-clear="props.allowClear"
|
:auto-size="{ minRows: 1, maxRows: 1 }"
|
||||||
@input="(val) => emit('input', val)"
|
:allow-clear="props.allowClear"
|
||||||
@change="(val) => emit('change', val)"
|
@input="(val) => emit('input', val)"
|
||||||
/>
|
@change="(val) => emit('change', val)"
|
||||||
</a-popover>
|
/>
|
||||||
<a-modal
|
</a-popover>
|
||||||
v-model:visible="showQuickInput"
|
<a-modal
|
||||||
:title="props.title"
|
v-model:visible="showQuickInput"
|
||||||
:ok-text="t('common.save')"
|
:title="props.title"
|
||||||
:ok-button-props="{ disabled: !quickInputValue || quickInputValue.trim() === '' }"
|
:ok-text="t('common.save')"
|
||||||
class="ms-modal-form"
|
:ok-button-props="{ disabled: !quickInputValue || quickInputValue.trim() === '' }"
|
||||||
body-class="!p-0"
|
class="ms-modal-form"
|
||||||
:width="480"
|
body-class="!p-0"
|
||||||
title-align="start"
|
:width="480"
|
||||||
@ok="applyQuickInputDesc"
|
title-align="start"
|
||||||
@close="clearQuickInputDesc"
|
@ok="applyQuickInputDesc"
|
||||||
>
|
@close="clearQuickInputDesc"
|
||||||
<a-textarea
|
>
|
||||||
v-model:model-value="quickInputValue"
|
<a-textarea
|
||||||
:placeholder="props.placeholder"
|
v-model:model-value="quickInputValue"
|
||||||
:auto-size="{ minRows: 2 }"
|
:placeholder="props.placeholder"
|
||||||
:max-length="1000"
|
:auto-size="{ minRows: 2 }"
|
||||||
></a-textarea>
|
:max-length="1000"
|
||||||
</a-modal>
|
/>
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -67,6 +69,7 @@
|
||||||
defineProps<{
|
defineProps<{
|
||||||
type?: 'input' | 'textarea';
|
type?: 'input' | 'textarea';
|
||||||
title?: string;
|
title?: string;
|
||||||
|
popoverTitle?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
size?: 'small' | 'large' | 'medium' | 'mini';
|
size?: 'small' | 'large' | 'medium' | 'mini';
|
||||||
|
@ -85,7 +88,6 @@
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'input', val: string): void;
|
(e: 'input', val: string): void;
|
||||||
(e: 'change', val: string): void;
|
(e: 'change', val: string): void;
|
||||||
(e: 'dblclick'): void;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
|
@ -305,6 +305,7 @@
|
||||||
theme: currentTheme.value,
|
theme: currentTheme.value,
|
||||||
lineNumbersMinChars: 3,
|
lineNumbersMinChars: 3,
|
||||||
lineDecorationsWidth: 0,
|
lineDecorationsWidth: 0,
|
||||||
|
tabSize: 2,
|
||||||
});
|
});
|
||||||
|
|
||||||
editor.getModel()?.setEOL(monaco.editor.EndOfLineSequence.LF); // 设置换行符
|
editor.getModel()?.setEOL(monaco.editor.EndOfLineSequence.LF); // 设置换行符
|
||||||
|
|
|
@ -59,8 +59,9 @@
|
||||||
<div>*</div>
|
<div>*</div>
|
||||||
</MsButton>
|
</MsButton>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
<div v-if="item.isNull && item.isNull(record)" class="ms-form-table-td-text">-</div>
|
||||||
<a-input
|
<a-input
|
||||||
v-if="item.inputType === 'input'"
|
v-else-if="item.inputType === 'input'"
|
||||||
v-model:model-value="record[item.dataIndex as string]"
|
v-model:model-value="record[item.dataIndex as string]"
|
||||||
:placeholder="t(item.locale)"
|
:placeholder="t(item.locale)"
|
||||||
class="ms-form-table-input"
|
class="ms-form-table-input"
|
||||||
|
@ -71,7 +72,7 @@
|
||||||
<a-select
|
<a-select
|
||||||
v-else-if="item.inputType === 'select'"
|
v-else-if="item.inputType === 'select'"
|
||||||
v-model:model-value="record[item.dataIndex as string]"
|
v-model:model-value="record[item.dataIndex as string]"
|
||||||
:options="item.typeOptions || []"
|
:options="item.options || []"
|
||||||
class="ms-form-table-input w-full"
|
class="ms-form-table-input w-full"
|
||||||
:size="item.size || 'medium'"
|
:size="item.size || 'medium'"
|
||||||
@change="() => handleFormChange(record, rowIndex, item)"
|
@change="() => handleFormChange(record, rowIndex, item)"
|
||||||
|
@ -249,6 +250,7 @@
|
||||||
step?: number;
|
step?: number;
|
||||||
precision?: number;
|
precision?: number;
|
||||||
valueFormat?: (record: Record<string, any>) => string; // 展示值格式化,仅在inputType为text时生效
|
valueFormat?: (record: Record<string, any>) => string; // 展示值格式化,仅在inputType为text时生效
|
||||||
|
isNull?: (record: Record<string, any>) => boolean; // 需要判断是否为空,为空展示‘-’,不展示表单或文本
|
||||||
[key: string]: any; // 扩展属性
|
[key: string]: any; // 扩展属性
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,6 +514,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.ms-form-table-td-text {
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
.arco-table-col-fixed-right {
|
.arco-table-col-fixed-right {
|
||||||
.arco-table-cell {
|
.arco-table-cell {
|
||||||
padding: 0 8px !important;
|
padding: 0 8px !important;
|
||||||
|
|
|
@ -16,15 +16,19 @@
|
||||||
:scroll="{ x: 'max-content' }"
|
:scroll="{ x: 'max-content' }"
|
||||||
show-setting
|
show-setting
|
||||||
class="ms-json-schema"
|
class="ms-json-schema"
|
||||||
@select="handleSelect"
|
@select="
|
||||||
|
(rowKeys: (string | number)[], rowKey: string | number, record: Record<string, any>) =>
|
||||||
|
handleSelect(rowKeys, rowKey, record as JsonSchemaTableItem)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<template #batchAddTitle>
|
<template #batchAddTitle>
|
||||||
<MsButton type="text" size="mini" class="!mr-0" @click="batchAdd">
|
<MsButton type="text" size="mini" class="!mr-0" @click="batchAdd">
|
||||||
{{ t('apiTestDebug.batchAdd') }}
|
{{ t('apiTestDebug.batchAdd') }}
|
||||||
</MsButton>
|
</MsButton>
|
||||||
</template>
|
</template>
|
||||||
<template #title="{ record, columnConfig }">
|
<template #title="{ record, columnConfig, rowIndex }">
|
||||||
<span v-if="record.title === 'root'" class="px-[8px]">root</span>
|
<span v-if="record.id === 'root'" class="px-[8px]">root</span>
|
||||||
|
<span v-else-if="record.parent?.type === 'array'" class="px-[8px]">{{ rowIndex }}</span>
|
||||||
<a-popover
|
<a-popover
|
||||||
v-else
|
v-else
|
||||||
position="tl"
|
position="tl"
|
||||||
|
@ -73,8 +77,9 @@
|
||||||
@change="handleTypeChange(record)"
|
@change="handleTypeChange(record)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #value="{ record }">
|
<template #example="{ record }">
|
||||||
<MsParamsInput v-model:value="record.value" size="medium" @dblclick="() => quickInputParams(record)" />
|
<div v-if="['object', 'array', 'null'].includes(record.type)" class="ms-form-table-td-text">-</div>
|
||||||
|
<MsParamsInput v-else v-model:value="record.example" size="medium" @dblclick="() => quickInputParams(record)" />
|
||||||
</template>
|
</template>
|
||||||
<template #minLength="{ record }">
|
<template #minLength="{ record }">
|
||||||
<a-input-number
|
<a-input-number
|
||||||
|
@ -85,7 +90,7 @@
|
||||||
:precision="0"
|
:precision="0"
|
||||||
size="medium"
|
size="medium"
|
||||||
/>
|
/>
|
||||||
<div v-else class="ms-json-schema-td-text">-</div>
|
<div v-else class="ms-form-table-td-text">-</div>
|
||||||
</template>
|
</template>
|
||||||
<template #maxLength="{ record }">
|
<template #maxLength="{ record }">
|
||||||
<a-input-number
|
<a-input-number
|
||||||
|
@ -96,7 +101,7 @@
|
||||||
:precision="0"
|
:precision="0"
|
||||||
size="medium"
|
size="medium"
|
||||||
/>
|
/>
|
||||||
<div v-else class="ms-json-schema-td-text">-</div>
|
<div v-else class="ms-form-table-td-text">-</div>
|
||||||
</template>
|
</template>
|
||||||
<template #minimum="{ record }">
|
<template #minimum="{ record }">
|
||||||
<a-input-number
|
<a-input-number
|
||||||
|
@ -113,7 +118,7 @@
|
||||||
:step="1"
|
:step="1"
|
||||||
:precision="0"
|
:precision="0"
|
||||||
/>
|
/>
|
||||||
<div v-else class="ms-json-schema-td-text">-</div>
|
<div v-else class="ms-form-table-td-text">-</div>
|
||||||
</template>
|
</template>
|
||||||
<template #maximum="{ record }">
|
<template #maximum="{ record }">
|
||||||
<a-input-number
|
<a-input-number
|
||||||
|
@ -130,7 +135,31 @@
|
||||||
:step="1"
|
:step="1"
|
||||||
:precision="0"
|
:precision="0"
|
||||||
/>
|
/>
|
||||||
<div v-else class="ms-json-schema-td-text">-</div>
|
<div v-else class="ms-form-table-td-text">-</div>
|
||||||
|
</template>
|
||||||
|
<template #minItems="{ record }">
|
||||||
|
<a-input-number
|
||||||
|
v-if="record.type === 'array'"
|
||||||
|
v-model:model-value="record.minItems"
|
||||||
|
class="ms-form-table-input-number"
|
||||||
|
size="medium"
|
||||||
|
:min="0"
|
||||||
|
:step="1"
|
||||||
|
:precision="0"
|
||||||
|
/>
|
||||||
|
<div v-else class="ms-form-table-td-text">-</div>
|
||||||
|
</template>
|
||||||
|
<template #maxItems="{ record }">
|
||||||
|
<a-input-number
|
||||||
|
v-if="record.type === 'array'"
|
||||||
|
v-model:model-value="record.maxItems"
|
||||||
|
class="ms-form-table-input-number"
|
||||||
|
size="medium"
|
||||||
|
:min="0"
|
||||||
|
:step="1"
|
||||||
|
:precision="0"
|
||||||
|
/>
|
||||||
|
<div v-else class="ms-form-table-td-text">-</div>
|
||||||
</template>
|
</template>
|
||||||
<template #defaultValue="{ record }">
|
<template #defaultValue="{ record }">
|
||||||
<a-input-number
|
<a-input-number
|
||||||
|
@ -163,7 +192,7 @@
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
<div v-else-if="['object', 'array', 'null'].includes(record.type)" class="ms-json-schema-td-text"> - </div>
|
<div v-else-if="['object', 'array', 'null'].includes(record.type)" class="ms-form-table-td-text"> - </div>
|
||||||
<a-input
|
<a-input
|
||||||
v-else
|
v-else
|
||||||
v-model:model-value="record.defaultValue"
|
v-model:model-value="record.defaultValue"
|
||||||
|
@ -171,6 +200,18 @@
|
||||||
class="ms-form-table-input"
|
class="ms-form-table-input"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
<template #enumValues="{ record }">
|
||||||
|
<div v-if="['object', 'array', 'null', 'boolean'].includes(record.type)" class="ms-form-table-td-text">-</div>
|
||||||
|
<MsQuickInput
|
||||||
|
v-else
|
||||||
|
v-model:model-value="record.enumValues"
|
||||||
|
:title="t('ms.json.schema.enum')"
|
||||||
|
:popover-title="JSON.stringify(record.enumValues.split('\n'))"
|
||||||
|
class="ms-form-table-input"
|
||||||
|
type="textarea"
|
||||||
|
>
|
||||||
|
</MsQuickInput>
|
||||||
|
</template>
|
||||||
<template #action="{ record, rowIndex }">
|
<template #action="{ record, rowIndex }">
|
||||||
<div class="flex w-full items-center gap-[8px]">
|
<div class="flex w-full items-center gap-[8px]">
|
||||||
<a-tooltip :content="t('common.advancedSettings')">
|
<a-tooltip :content="t('common.advancedSettings')">
|
||||||
|
@ -233,14 +274,19 @@
|
||||||
asterisk-position="end"
|
asterisk-position="end"
|
||||||
>
|
>
|
||||||
<a-input
|
<a-input
|
||||||
v-model:model-value="activeRecord.name"
|
v-model:model-value="activeRecord.title"
|
||||||
:max-length="255"
|
:max-length="255"
|
||||||
:placeholder="t('common.pleaseInput')"
|
:placeholder="t('common.pleaseInput')"
|
||||||
:disabled="activeRecord.id === 'root'"
|
:disabled="activeRecord.id === 'root'"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :label="t('common.desc')">
|
<a-form-item :label="t('common.desc')">
|
||||||
<a-textarea v-model:model-value="activeRecord.description" :placeholder="t('common.pleaseInput')" />
|
<a-textarea
|
||||||
|
v-model:model-value="activeRecord.description"
|
||||||
|
:placeholder="t('common.pleaseInput')"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<template v-if="!['object', 'array', 'null'].includes(activeRecord.type)">
|
<template v-if="!['object', 'array', 'null'].includes(activeRecord.type)">
|
||||||
<div class="flex items-center justify-between gap-[24px]">
|
<div class="flex items-center justify-between gap-[24px]">
|
||||||
|
@ -252,6 +298,7 @@
|
||||||
:min="0"
|
:min="0"
|
||||||
:step="1"
|
:step="1"
|
||||||
:precision="0"
|
:precision="0"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :label="t('ms.json.schema.maxLength')" class="w-[144px]">
|
<a-form-item :label="t('ms.json.schema.maxLength')" class="w-[144px]">
|
||||||
|
@ -261,6 +308,7 @@
|
||||||
:min="0"
|
:min="0"
|
||||||
:step="1"
|
:step="1"
|
||||||
:precision="0"
|
:precision="0"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
@ -272,8 +320,14 @@
|
||||||
mode="button"
|
mode="button"
|
||||||
:step="1"
|
:step="1"
|
||||||
:precision="0"
|
:precision="0"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
|
/>
|
||||||
|
<a-input-number
|
||||||
|
v-else
|
||||||
|
v-model:model-value="activeRecord.minimum"
|
||||||
|
mode="button"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
/>
|
/>
|
||||||
<a-input-number v-else v-model:model-value="activeRecord.minimum" mode="button" />
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :label="t('ms.json.schema.maximum')" class="w-[144px]">
|
<a-form-item :label="t('ms.json.schema.maximum')" class="w-[144px]">
|
||||||
<a-input-number
|
<a-input-number
|
||||||
|
@ -282,8 +336,14 @@
|
||||||
mode="button"
|
mode="button"
|
||||||
:step="1"
|
:step="1"
|
||||||
:precision="0"
|
:precision="0"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
|
/>
|
||||||
|
<a-input-number
|
||||||
|
v-else
|
||||||
|
v-model:model-value="activeRecord.maximum"
|
||||||
|
mode="button"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
/>
|
/>
|
||||||
<a-input-number v-else v-model:model-value="activeRecord.maximum" mode="button" />
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
<a-form-item :label="t('ms.json.schema.default')" class="flex-1">
|
<a-form-item :label="t('ms.json.schema.default')" class="flex-1">
|
||||||
|
@ -292,6 +352,7 @@
|
||||||
v-model:model-value="activeRecord.defaultValue"
|
v-model:model-value="activeRecord.defaultValue"
|
||||||
mode="button"
|
mode="button"
|
||||||
:placeholder="t('common.pleaseInput')"
|
:placeholder="t('common.pleaseInput')"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
/>
|
/>
|
||||||
<a-input-number
|
<a-input-number
|
||||||
v-else-if="activeRecord.type === 'integer'"
|
v-else-if="activeRecord.type === 'integer'"
|
||||||
|
@ -300,33 +361,71 @@
|
||||||
:placeholder="t('common.pleaseInput')"
|
:placeholder="t('common.pleaseInput')"
|
||||||
:step="1"
|
:step="1"
|
||||||
:precision="0"
|
:precision="0"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
|
/>
|
||||||
|
<a-input
|
||||||
|
v-else
|
||||||
|
v-model:model-value="activeRecord.defaultValue"
|
||||||
|
:placeholder="t('common.pleaseInput')"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
/>
|
/>
|
||||||
<a-input v-else v-model:model-value="activeRecord.defaultValue" :placeholder="t('common.pleaseInput')" />
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="activeRecord.type !== 'boolean'">
|
<template v-if="activeRecord.type !== 'boolean'">
|
||||||
<a-form-item :label="t('ms.json.schema.enum')">
|
<a-form-item :label="t('ms.json.schema.enum')">
|
||||||
<a-textarea v-model:model-value="activeRecord.enum" :placeholder="t('ms.json.schema.enumPlaceholder')" />
|
<a-textarea
|
||||||
|
v-model:model-value="activeRecord.enumValues"
|
||||||
|
:placeholder="t('ms.json.schema.enumPlaceholder')"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :label="t('ms.json.schema.regex')">
|
<a-form-item :label="t('ms.json.schema.regex')">
|
||||||
<a-input v-model:model-value="activeRecord.regex" :placeholder="t('ms.json.schema.regexPlaceholder')" />
|
<a-input
|
||||||
|
v-model:model-value="activeRecord.regex"
|
||||||
|
:placeholder="t('ms.json.schema.regexPlaceholder', { reg: '/<title(.*?)</title>' })"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :label="t('ms.json.schema.format')">
|
<a-form-item :label="t('ms.json.schema.format')">
|
||||||
<a-select
|
<a-select
|
||||||
v-model:model-value="activeRecord.format"
|
v-model:model-value="activeRecord.format"
|
||||||
:placeholder="t('common.pleaseSelect')"
|
:placeholder="t('common.pleaseSelect')"
|
||||||
:options="formatOptions"
|
:options="formatOptions"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
<div v-if="activeRecord.type === 'array'" class="flex items-center gap-[24px]">
|
||||||
|
<a-form-item :label="t('ms.json.schema.minItems')" class="w-[144px]">
|
||||||
|
<a-input-number
|
||||||
|
v-model:model-value="activeRecord.minItems"
|
||||||
|
mode="button"
|
||||||
|
:min="0"
|
||||||
|
:step="1"
|
||||||
|
:precision="0"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :label="t('ms.json.schema.maxItems')" class="w-[144px]">
|
||||||
|
<a-input-number
|
||||||
|
v-model:model-value="activeRecord.maxItems"
|
||||||
|
mode="button"
|
||||||
|
:min="0"
|
||||||
|
:step="1"
|
||||||
|
:precision="0"
|
||||||
|
@change="handleSettingFormChange"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-[8px]">{{ t('ms.json.schema.preview') }}</div>
|
<div class="mb-[8px]">{{ t('ms.json.schema.preview') }}</div>
|
||||||
<MsCodeEditor
|
<MsCodeEditor
|
||||||
v-model:model-value="activePreviewValue"
|
v-model:model-value="activePreviewValue"
|
||||||
theme="vs"
|
theme="vs"
|
||||||
height="300px"
|
height="500px"
|
||||||
:show-full-screen="false"
|
:show-full-screen="false"
|
||||||
|
:language="LanguageEnum.JSON"
|
||||||
read-only
|
read-only
|
||||||
>
|
>
|
||||||
</MsCodeEditor>
|
</MsCodeEditor>
|
||||||
|
@ -362,6 +461,16 @@
|
||||||
</template>
|
</template>
|
||||||
</MsCodeEditor>
|
</MsCodeEditor>
|
||||||
</MsDrawer>
|
</MsDrawer>
|
||||||
|
<MsDrawer v-model:visible="previewDrawerVisible" :width="600" :title="t('common.preview')" :footer="false">
|
||||||
|
<MsCodeEditor
|
||||||
|
v-model:model-value="activePreviewValue"
|
||||||
|
theme="vs"
|
||||||
|
height="100%"
|
||||||
|
:language="LanguageEnum.JSON"
|
||||||
|
:show-full-screen="false"
|
||||||
|
read-only
|
||||||
|
/>
|
||||||
|
</MsDrawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -374,21 +483,23 @@
|
||||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||||
import MsFormTable, { FormTableColumn } from '@/components/pure/ms-form-table/index.vue';
|
import MsFormTable, { FormTableColumn } from '@/components/pure/ms-form-table/index.vue';
|
||||||
import MsParamsInput from '@/components/business/ms-params-input/index.vue';
|
import MsParamsInput from '@/components/business/ms-params-input/index.vue';
|
||||||
|
import MsQuickInput from '@/components/business/ms-quick-input/index.vue';
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { getGenerateId, traverseTree } from '@/utils';
|
import { getGenerateId, traverseTree } from '@/utils';
|
||||||
|
|
||||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
|
|
||||||
import { JsonSchemaItem } from './types';
|
import { JsonSchemaTableItem } from './types';
|
||||||
|
import { convertToJsonSchema } from './utils';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const defaultItem: JsonSchemaItem = {
|
const defaultItem: JsonSchemaTableItem = {
|
||||||
id: '',
|
id: '',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: '',
|
title: '',
|
||||||
value: '',
|
example: '',
|
||||||
description: '',
|
description: '',
|
||||||
enable: true,
|
enable: true,
|
||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
|
@ -401,13 +512,13 @@
|
||||||
format: undefined,
|
format: undefined,
|
||||||
children: undefined,
|
children: undefined,
|
||||||
};
|
};
|
||||||
const data = defineModel<JsonSchemaItem[]>('data', {
|
const data = defineModel<JsonSchemaTableItem[]>('data', {
|
||||||
default: () => [
|
default: () => [
|
||||||
{
|
{
|
||||||
id: 'root',
|
id: 'root',
|
||||||
title: 'root',
|
title: 'root',
|
||||||
type: 'object',
|
type: 'object',
|
||||||
value: '',
|
example: '',
|
||||||
description: '',
|
description: '',
|
||||||
enable: true,
|
enable: true,
|
||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
|
@ -508,8 +619,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('ms.json.schema.value'),
|
title: t('ms.json.schema.value'),
|
||||||
dataIndex: 'value',
|
dataIndex: 'example',
|
||||||
slotName: 'value',
|
slotName: 'example',
|
||||||
addLineDisabled: true,
|
addLineDisabled: true,
|
||||||
columnSelectorDisabled: true,
|
columnSelectorDisabled: true,
|
||||||
},
|
},
|
||||||
|
@ -565,6 +676,30 @@
|
||||||
showInTable: false,
|
showInTable: false,
|
||||||
width: 120,
|
width: 120,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('ms.json.schema.minItems'),
|
||||||
|
dataIndex: 'minItems',
|
||||||
|
slotName: 'minItems',
|
||||||
|
inputType: 'inputNumber',
|
||||||
|
size: 'medium',
|
||||||
|
min: 0,
|
||||||
|
precision: 0,
|
||||||
|
addLineDisabled: true,
|
||||||
|
showInTable: false,
|
||||||
|
width: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('ms.json.schema.maxItems'),
|
||||||
|
dataIndex: 'maxItems',
|
||||||
|
slotName: 'maxItems',
|
||||||
|
inputType: 'inputNumber',
|
||||||
|
size: 'medium',
|
||||||
|
min: 0,
|
||||||
|
precision: 0,
|
||||||
|
addLineDisabled: true,
|
||||||
|
showInTable: false,
|
||||||
|
width: 120,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('ms.json.schema.default'),
|
title: t('ms.json.schema.default'),
|
||||||
dataIndex: 'defaultValue',
|
dataIndex: 'defaultValue',
|
||||||
|
@ -576,8 +711,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('ms.json.schema.enum'),
|
title: t('ms.json.schema.enum'),
|
||||||
dataIndex: 'enum',
|
dataIndex: 'enumValues',
|
||||||
slotName: 'enum',
|
slotName: 'enumValues',
|
||||||
inputType: 'textarea',
|
inputType: 'textarea',
|
||||||
size: 'medium',
|
size: 'medium',
|
||||||
addLineDisabled: true,
|
addLineDisabled: true,
|
||||||
|
@ -591,6 +726,7 @@
|
||||||
size: 'medium',
|
size: 'medium',
|
||||||
addLineDisabled: true,
|
addLineDisabled: true,
|
||||||
showInTable: false,
|
showInTable: false,
|
||||||
|
isNull: (record) => ['object', 'array', 'null', 'boolean'].includes(record.type),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('ms.json.schema.format'),
|
title: t('ms.json.schema.format'),
|
||||||
|
@ -601,6 +737,7 @@
|
||||||
options: formatOptions,
|
options: formatOptions,
|
||||||
addLineDisabled: true,
|
addLineDisabled: true,
|
||||||
showInTable: false,
|
showInTable: false,
|
||||||
|
isNull: (record) => ['object', 'array', 'null', 'boolean'].includes(record.type),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
|
@ -617,8 +754,8 @@
|
||||||
/**
|
/**
|
||||||
* 获取类型选项,根节点只能是 object 或 array
|
* 获取类型选项,根节点只能是 object 或 array
|
||||||
*/
|
*/
|
||||||
function getTypeOptions(record: Record<string, any>) {
|
function getTypeOptions(record: JsonSchemaTableItem) {
|
||||||
if (record.name === 'root') {
|
if (record.id === 'root') {
|
||||||
return typeOptions.filter((item) => ['object', 'array'].includes(item.value as string));
|
return typeOptions.filter((item) => ['object', 'array'].includes(item.value as string));
|
||||||
}
|
}
|
||||||
return typeOptions;
|
return typeOptions;
|
||||||
|
@ -627,7 +764,7 @@
|
||||||
/**
|
/**
|
||||||
* 处理类型变化
|
* 处理类型变化
|
||||||
*/
|
*/
|
||||||
function handleTypeChange(record: Record<string, any>) {
|
function handleTypeChange(record: JsonSchemaTableItem) {
|
||||||
if (record.type === 'object' || record.type === 'array') {
|
if (record.type === 'object' || record.type === 'array') {
|
||||||
if (!record.children) {
|
if (!record.children) {
|
||||||
// 没有子节点,初始化
|
// 没有子节点,初始化
|
||||||
|
@ -641,7 +778,7 @@
|
||||||
/**
|
/**
|
||||||
* 添加子节点
|
* 添加子节点
|
||||||
*/
|
*/
|
||||||
function addChild(record: Record<string, any>) {
|
function addChild(record: JsonSchemaTableItem) {
|
||||||
if (!record.children) {
|
if (!record.children) {
|
||||||
record.children = [];
|
record.children = [];
|
||||||
}
|
}
|
||||||
|
@ -664,8 +801,8 @@
|
||||||
/**
|
/**
|
||||||
* 删除行
|
* 删除行
|
||||||
*/
|
*/
|
||||||
function deleteLine(record: Record<string, any>, rowIndex: number) {
|
function deleteLine(record: JsonSchemaTableItem, rowIndex: number) {
|
||||||
if (record.parent) {
|
if (record.parent?.children) {
|
||||||
record.parent.children.splice(rowIndex, 1);
|
record.parent.children.splice(rowIndex, 1);
|
||||||
} else {
|
} else {
|
||||||
data.value.splice(rowIndex, 1);
|
data.value.splice(rowIndex, 1);
|
||||||
|
@ -675,11 +812,11 @@
|
||||||
/**
|
/**
|
||||||
* 行选择处理
|
* 行选择处理
|
||||||
*/
|
*/
|
||||||
function handleSelect(rowKeys: (string | number)[], rowKey: string | number, record: Record<string, any>) {
|
function handleSelect(rowKeys: (string | number)[], rowKey: string | number, record: JsonSchemaTableItem) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (record.enable && record.children && record.children.length > 0) {
|
if (record.enable && record.children && record.children.length > 0) {
|
||||||
// 选中父节点时,选中子孙节点
|
// 选中父节点时,选中子孙节点
|
||||||
traverseTree(record.children, (item: Record<string, any>) => {
|
traverseTree<JsonSchemaTableItem>(record.children, (item) => {
|
||||||
item.enable = true;
|
item.enable = true;
|
||||||
if (!selectedKeys.value.includes(item.id)) {
|
if (!selectedKeys.value.includes(item.id)) {
|
||||||
selectedKeys.value.push(item.id);
|
selectedKeys.value.push(item.id);
|
||||||
|
@ -704,12 +841,26 @@
|
||||||
const activeRecord = ref<any>({});
|
const activeRecord = ref<any>({});
|
||||||
const activePreviewValue = ref('');
|
const activePreviewValue = ref('');
|
||||||
|
|
||||||
function openSetting(record: Record<string, any>) {
|
function openSetting(record: JsonSchemaTableItem) {
|
||||||
// 浅拷贝,以保留 parent 和 children 的引用
|
// 浅拷贝,以保留 parent 和 children 的引用
|
||||||
activeRecord.value = {
|
activeRecord.value = {
|
||||||
...record,
|
...record,
|
||||||
};
|
};
|
||||||
settingDrawerVisible.value = true;
|
try {
|
||||||
|
activePreviewValue.value = JSON.stringify(convertToJsonSchema(record, record.id === 'root'));
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
activePreviewValue.value = t('ms.json.schema.convertFailed');
|
||||||
|
} finally {
|
||||||
|
settingDrawerVisible.value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSettingFormChange() {
|
||||||
|
activePreviewValue.value = JSON.stringify(
|
||||||
|
convertToJsonSchema(activeRecord.value, activeRecord.value.id === 'root')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -717,12 +868,12 @@
|
||||||
*/
|
*/
|
||||||
function applySetting() {
|
function applySetting() {
|
||||||
if (activeRecord.value.id === 'root') {
|
if (activeRecord.value.id === 'root') {
|
||||||
data.value[0] = activeRecord.value;
|
data.value = [{ ...activeRecord.value }];
|
||||||
} else {
|
} else {
|
||||||
const brothers = activeRecord.value.parent?.children || [];
|
const brothers = activeRecord.value.parent?.children || [];
|
||||||
const index = brothers.findIndex((item: any) => item.id === activeRecord.value.id);
|
const index = brothers.findIndex((item: any) => item.id === activeRecord.value.id);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
brothers.splice(index, 1, activeRecord.value);
|
brothers.splice(index, 1, { ...activeRecord.value });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
settingDrawerVisible.value = false;
|
settingDrawerVisible.value = false;
|
||||||
|
@ -748,13 +899,20 @@
|
||||||
showQuickInputParam.value = false;
|
showQuickInputParam.value = false;
|
||||||
clearQuickInputParam();
|
clearQuickInputParam();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const previewDrawerVisible = ref(false);
|
||||||
|
function previewSchema() {
|
||||||
|
previewDrawerVisible.value = true;
|
||||||
|
activePreviewValue.value = JSON.stringify(convertToJsonSchema(data.value[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
previewSchema,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.ms-json-schema {
|
.ms-json-schema {
|
||||||
.ms-json-schema-td-text {
|
|
||||||
padding: 0 8px;
|
|
||||||
}
|
|
||||||
.ms-json-schema-icon-button {
|
.ms-json-schema-icon-button {
|
||||||
@apply !mr-0;
|
@apply !mr-0;
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
|
@ -13,9 +13,12 @@ export default {
|
||||||
'ms.json.schema.enum': '枚举值',
|
'ms.json.schema.enum': '枚举值',
|
||||||
'ms.json.schema.enumPlaceholder': '1 行 1 个枚举值',
|
'ms.json.schema.enumPlaceholder': '1 行 1 个枚举值',
|
||||||
'ms.json.schema.regex': '正则表达式',
|
'ms.json.schema.regex': '正则表达式',
|
||||||
'ms.json.schema.regexPlaceholder': '如 /<title(.*?)</title>',
|
'ms.json.schema.regexPlaceholder': '如 {reg}',
|
||||||
'ms.json.schema.format': '格式化',
|
'ms.json.schema.format': '格式化',
|
||||||
'ms.json.schema.preview': '预览',
|
'ms.json.schema.preview': '预览',
|
||||||
'ms.json.schema.batchAdd': '批量添加',
|
'ms.json.schema.batchAdd': '批量添加',
|
||||||
'ms.json.schema.batchAddTip': '书写格式:"键":"值",如"nama":"natural"',
|
'ms.json.schema.batchAddTip': '书写格式:"键":"值",如"nama":"natural"',
|
||||||
|
'ms.json.schema.convertFailed': '数据转换失败,请重试',
|
||||||
|
'ms.json.schema.minItems': '最小元素数量',
|
||||||
|
'ms.json.schema.maxItems': '最大元素数量',
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,22 +1,38 @@
|
||||||
export interface JsonSchemaItem {
|
export interface JsonSchemaCommon {
|
||||||
id: string;
|
id: string;
|
||||||
title: string; // 参数名称
|
title: string; // 参数名称
|
||||||
type: string; // 参数类型
|
type: string; // 参数类型
|
||||||
description: string; // 参数描述
|
description: string; // 参数描述
|
||||||
enable: boolean; // 是否启用
|
enable: boolean; // 是否启用
|
||||||
value: string; // 参数值
|
example: string; // 参数值
|
||||||
defaultValue: string | number | boolean; // 默认值
|
defaultValue: string | number | boolean; // 默认值
|
||||||
example?: Record<string, any>;
|
|
||||||
items?: string; // 子级,当 type 为array 时,使用该值
|
|
||||||
properties?: Record<string, any>; // 子级,当 type 为object 时,使用该值
|
|
||||||
required?: string[]; // 必填参数 这里的值是参数的title
|
|
||||||
pattern?: string; // 正则表达式
|
pattern?: string; // 正则表达式
|
||||||
maxLength?: number;
|
maxLength?: number;
|
||||||
minLength?: number;
|
minLength?: number;
|
||||||
minimum?: number;
|
minimum?: number;
|
||||||
maximum?: number;
|
maximum?: number;
|
||||||
|
minItems?: number;
|
||||||
|
maxItems?: number;
|
||||||
format?: string; // 格式化
|
format?: string; // 格式化
|
||||||
enumValues?: string; // 参数值的枚举
|
}
|
||||||
// 前端渲染字段
|
// json-schema 表格组件的表格项
|
||||||
children?: JsonSchemaItem[];
|
export interface JsonSchemaTableItem extends JsonSchemaCommon {
|
||||||
|
required?: string[]; // 是否必填
|
||||||
|
children?: JsonSchemaTableItem[];
|
||||||
|
parent?: JsonSchemaTableItem; // 父级
|
||||||
|
enumValues?: string; // 参数值的枚举
|
||||||
|
}
|
||||||
|
// json-schema 规范的结构子项(表格组件转换后的结构)
|
||||||
|
export interface JsonSchemaItem extends JsonSchemaCommon {
|
||||||
|
items?: JsonSchemaItem[]; // 子级,当 type 为array 时,使用该值
|
||||||
|
properties?: Record<string, JsonSchemaItem>; // 子级,当 type 为object 时,使用该值
|
||||||
|
required?: string[]; // 必填的字段名
|
||||||
|
enumValues?: string[]; // 参数值的枚举
|
||||||
|
}
|
||||||
|
// json-schema 规范的结构(表格组件转换后的结构)
|
||||||
|
export interface JsonSchema {
|
||||||
|
type: string;
|
||||||
|
properties?: Record<string, JsonSchemaItem>;
|
||||||
|
items?: JsonSchemaItem[];
|
||||||
|
required?: string[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import type { JsonSchema, JsonSchemaItem, JsonSchemaTableItem } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 json-schema 表格组件的表格项转换为 json-schema 规范的结构
|
||||||
|
* @param schemaItem 表格组件项
|
||||||
|
* @param isRoot 是否为根节点
|
||||||
|
*/
|
||||||
|
export function convertToJsonSchema(
|
||||||
|
schemaItem: JsonSchemaTableItem,
|
||||||
|
isRoot: boolean = true
|
||||||
|
): JsonSchema | JsonSchemaItem {
|
||||||
|
let schema: JsonSchema | JsonSchemaItem = { type: schemaItem.type };
|
||||||
|
|
||||||
|
// 对于 null 类型,只设置 type 和 enable 属性
|
||||||
|
if (schemaItem.type === 'null') {
|
||||||
|
return {
|
||||||
|
type: 'null',
|
||||||
|
enable: schemaItem.enable,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isRoot) {
|
||||||
|
// 使用解构赋值和剩余参数来拷贝对象,同时排除 children、required、parent、id、title 属性
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const { children, required, parent, id, title, ...copiedObject } = schemaItem;
|
||||||
|
// 使用\n分割enumValues字符串,得到枚举值数组
|
||||||
|
const enumArray = (copiedObject.enumValues?.split('\n') || []).filter((value) => value.trim() !== '');
|
||||||
|
schema = {
|
||||||
|
...schema,
|
||||||
|
...copiedObject,
|
||||||
|
enumValues: enumArray.length > 0 ? enumArray : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schemaItem.children && schemaItem.children.length > 0) {
|
||||||
|
if (schemaItem.type === 'object') {
|
||||||
|
schema = {
|
||||||
|
type: 'object',
|
||||||
|
enable: schemaItem.enable,
|
||||||
|
properties: {},
|
||||||
|
required: [],
|
||||||
|
};
|
||||||
|
schemaItem.children.forEach((child) => {
|
||||||
|
const childSchema = convertToJsonSchema(child, false);
|
||||||
|
schema.properties![child.title] = childSchema as JsonSchemaItem;
|
||||||
|
if (child.required) {
|
||||||
|
schema.required!.push(child.title);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (schema.required!.length === 0) {
|
||||||
|
delete schema.required;
|
||||||
|
}
|
||||||
|
} else if (schemaItem.type === 'array') {
|
||||||
|
schema = {
|
||||||
|
type: 'array',
|
||||||
|
enable: schemaItem.enable,
|
||||||
|
items: schemaItem.children.map((child) => convertToJsonSchema(child, false) as JsonSchemaItem),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
export default {};
|
|
@ -203,7 +203,7 @@
|
||||||
data = cloneDeep(fullJson);
|
data = cloneDeep(fullJson);
|
||||||
importJson.value = fullJson;
|
importJson.value = fullJson;
|
||||||
}
|
}
|
||||||
emit('save', data, () => {
|
emit('save', data, (refresh = false) => {
|
||||||
importJson.value.root.children = mapTree<MinderJsonNode>(
|
importJson.value.root.children = mapTree<MinderJsonNode>(
|
||||||
importJson.value.root.children || [],
|
importJson.value.root.children || [],
|
||||||
(node, path, level) => ({
|
(node, path, level) => ({
|
||||||
|
@ -216,12 +216,14 @@
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
// if (innerImportJson.value.treePath?.length > 1) {
|
if (refresh) {
|
||||||
// switchNode(innerImportJson.value.root.data);
|
if (innerImportJson.value.treePath?.length > 1) {
|
||||||
// } else {
|
switchNode(innerImportJson.value.root.data);
|
||||||
// innerImportJson.value = importJson.value;
|
} else {
|
||||||
// window.minder.importJson(importJson.value);
|
innerImportJson.value = importJson.value;
|
||||||
// }
|
window.minder.importJson(importJson.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
minderStore.setMinderUnsaved(false);
|
minderStore.setMinderUnsaved(false);
|
||||||
floatMenuVisible.value = false;
|
floatMenuVisible.value = false;
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,6 +7,60 @@ import { Recordable } from '#/global';
|
||||||
export default function useLocalForage() {
|
export default function useLocalForage() {
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测并序列化函数
|
||||||
|
* @param val 要存储的值
|
||||||
|
*/
|
||||||
|
const serializeValue = (val: any): any => {
|
||||||
|
if (typeof val === 'function') {
|
||||||
|
return `function:${val.toString()}`;
|
||||||
|
}
|
||||||
|
if (val && typeof val === 'object' && !Array.isArray(val)) {
|
||||||
|
const newVal = { ...val };
|
||||||
|
Object.keys(newVal).forEach((key) => {
|
||||||
|
newVal[key] = serializeValue(newVal[key]);
|
||||||
|
});
|
||||||
|
return newVal;
|
||||||
|
}
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
return val.map((item) => serializeValue(item));
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deserializeFunction = (funcStr: string) => {
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line no-eval
|
||||||
|
const func = eval(`${funcStr}`);
|
||||||
|
return func;
|
||||||
|
} catch (e) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反序列化值,将特殊格式字符串转换回函数
|
||||||
|
* @param val 从存储中读取的值
|
||||||
|
*/
|
||||||
|
const deserializeValue = <T>(val: any): T | null => {
|
||||||
|
if (typeof val === 'string' && val.startsWith('function:')) {
|
||||||
|
return deserializeFunction(val.slice(9)) as T;
|
||||||
|
}
|
||||||
|
if (val && typeof val === 'object' && !Array.isArray(val)) {
|
||||||
|
const newVal = { ...val };
|
||||||
|
Object.keys(newVal).forEach((key) => {
|
||||||
|
newVal[key] = deserializeValue(newVal[key]);
|
||||||
|
});
|
||||||
|
return newVal as T;
|
||||||
|
}
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
return val.map((item) => deserializeValue(item) as T) as T;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 读取本地存储的数据
|
* 读取本地存储的数据
|
||||||
* @param key 唯一 key
|
* @param key 唯一 key
|
||||||
|
@ -19,7 +73,7 @@ export default function useLocalForage() {
|
||||||
if (!res) {
|
if (!res) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return res;
|
return deserializeValue<T>(res);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
@ -36,7 +90,7 @@ export default function useLocalForage() {
|
||||||
const setItem = async (key: string, val: string | number | boolean | Recordable, notIsolatedByProject = false) => {
|
const setItem = async (key: string, val: string | number | boolean | Recordable, notIsolatedByProject = false) => {
|
||||||
try {
|
try {
|
||||||
const itemKey = notIsolatedByProject ? key : `${appStore.currentProjectId}-${key}`;
|
const itemKey = notIsolatedByProject ? key : `${appStore.currentProjectId}-${key}`;
|
||||||
await localforage.setItem(itemKey, val);
|
await localforage.setItem(itemKey, serializeValue(val));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
|
|
@ -835,7 +835,7 @@ if (!result){
|
||||||
title: 'apiTestDebug.paramType',
|
title: 'apiTestDebug.paramType',
|
||||||
dataIndex: 'variableType',
|
dataIndex: 'variableType',
|
||||||
slotName: 'variableType',
|
slotName: 'variableType',
|
||||||
typeOptions: extractTypeOptions.map((item) => {
|
options: extractTypeOptions.map((item) => {
|
||||||
return {
|
return {
|
||||||
label: t(item.label),
|
label: t(item.label),
|
||||||
value: item.value,
|
value: item.value,
|
||||||
|
@ -847,7 +847,7 @@ if (!result){
|
||||||
title: 'apiTestDebug.mode',
|
title: 'apiTestDebug.mode',
|
||||||
dataIndex: 'extractType',
|
dataIndex: 'extractType',
|
||||||
slotName: 'extractType',
|
slotName: 'extractType',
|
||||||
typeOptions: [
|
options: [
|
||||||
{
|
{
|
||||||
label: 'JSONPath',
|
label: 'JSONPath',
|
||||||
value: RequestExtractExpressionEnum.JSON_PATH,
|
value: RequestExtractExpressionEnum.JSON_PATH,
|
||||||
|
@ -867,7 +867,7 @@ if (!result){
|
||||||
title: 'apiTestDebug.range',
|
title: 'apiTestDebug.range',
|
||||||
dataIndex: 'extractScope',
|
dataIndex: 'extractScope',
|
||||||
slotName: 'extractScope',
|
slotName: 'extractScope',
|
||||||
typeOptions: [
|
options: [
|
||||||
{
|
{
|
||||||
label: 'Body',
|
label: 'Body',
|
||||||
value: RequestExtractScope.BODY,
|
value: RequestExtractScope.BODY,
|
||||||
|
|
|
@ -167,7 +167,7 @@
|
||||||
<a-select
|
<a-select
|
||||||
v-model:model-value="record.paramType"
|
v-model:model-value="record.paramType"
|
||||||
:disabled="props.disabledExceptParam"
|
:disabled="props.disabledExceptParam"
|
||||||
:options="columnConfig.typeOptions || []"
|
:options="columnConfig.options || []"
|
||||||
class="ms-form-table-input w-full"
|
class="ms-form-table-input w-full"
|
||||||
@change="(val) => handleTypeChange(val, record, rowIndex, columnConfig.addLineDisabled)"
|
@change="(val) => handleTypeChange(val, record, rowIndex, columnConfig.addLineDisabled)"
|
||||||
/>
|
/>
|
||||||
|
@ -177,7 +177,7 @@
|
||||||
<a-select
|
<a-select
|
||||||
v-model:model-value="record.extractType"
|
v-model:model-value="record.extractType"
|
||||||
:disabled="props.disabledExceptParam"
|
:disabled="props.disabledExceptParam"
|
||||||
:options="columnConfig.typeOptions || []"
|
:options="columnConfig.options || []"
|
||||||
class="ms-form-table-input w-[110px]"
|
class="ms-form-table-input w-[110px]"
|
||||||
@change="() => addTableLine(rowIndex)"
|
@change="() => addTableLine(rowIndex)"
|
||||||
/>
|
/>
|
||||||
|
@ -187,7 +187,7 @@
|
||||||
<a-select
|
<a-select
|
||||||
v-model:model-value="record.variableType"
|
v-model:model-value="record.variableType"
|
||||||
:disabled="props.disabledExceptParam"
|
:disabled="props.disabledExceptParam"
|
||||||
:options="columnConfig.typeOptions || []"
|
:options="columnConfig.options || []"
|
||||||
class="ms-form-table-input w-[110px]"
|
class="ms-form-table-input w-[110px]"
|
||||||
@change="() => addTableLine(rowIndex)"
|
@change="() => addTableLine(rowIndex)"
|
||||||
/>
|
/>
|
||||||
|
@ -197,7 +197,7 @@
|
||||||
<a-select
|
<a-select
|
||||||
v-model:model-value="record.extractScope"
|
v-model:model-value="record.extractScope"
|
||||||
:disabled="props.disabledExceptParam || record.extractType !== RequestExtractExpressionEnum.REGEX"
|
:disabled="props.disabledExceptParam || record.extractType !== RequestExtractExpressionEnum.REGEX"
|
||||||
:options="columnConfig.typeOptions || []"
|
:options="columnConfig.options || []"
|
||||||
class="ms-form-table-input w-[180px]"
|
class="ms-form-table-input w-[180px]"
|
||||||
@change="() => addTableLine(rowIndex)"
|
@change="() => addTableLine(rowIndex)"
|
||||||
/>
|
/>
|
||||||
|
@ -211,7 +211,7 @@
|
||||||
<a-select
|
<a-select
|
||||||
v-model:model-value="record.scope"
|
v-model:model-value="record.scope"
|
||||||
:disabled="props.disabledExceptParam"
|
:disabled="props.disabledExceptParam"
|
||||||
:options="columnConfig.typeOptions || []"
|
:options="columnConfig.options || []"
|
||||||
class="ms-form-table-input w-[180px]"
|
class="ms-form-table-input w-[180px]"
|
||||||
@change="(val) => handleScopeChange(val, record, rowIndex, columnConfig.addLineDisabled)"
|
@change="(val) => handleScopeChange(val, record, rowIndex, columnConfig.addLineDisabled)"
|
||||||
/>
|
/>
|
||||||
|
@ -625,7 +625,7 @@
|
||||||
isAutoComplete?: boolean; // 用于 key 列区分是否是请求/响应头联想输入
|
isAutoComplete?: boolean; // 用于 key 列区分是否是请求/响应头联想输入
|
||||||
isNormal?: boolean; // 用于 value 列区分是普通输入框还是 MsParamsInput
|
isNormal?: boolean; // 用于 value 列区分是普通输入框还是 MsParamsInput
|
||||||
hasRequired?: boolean; // 用于 type 列区分是否有 required 星号
|
hasRequired?: boolean; // 用于 type 列区分是否有 required 星号
|
||||||
typeOptions?: { label: string; value: string }[]; // 用于 type 列选择器选项
|
options?: { label: string; value: string }[]; // 用于 type 列选择器选项
|
||||||
typeTitleTooltip?: string | string[]; // 用于 type 表头列展示的 tooltip
|
typeTitleTooltip?: string | string[]; // 用于 type 表头列展示的 tooltip
|
||||||
hasDisable?: boolean; // 用于 operation 列区分是否有 enable 开关
|
hasDisable?: boolean; // 用于 operation 列区分是否有 enable 开关
|
||||||
moreAction?: ActionsItem[]; // 用于 operation 列更多操作按钮配置
|
moreAction?: ActionsItem[]; // 用于 operation 列更多操作按钮配置
|
||||||
|
@ -847,7 +847,7 @@
|
||||||
// 最后一行的更改才会触发添加新一行
|
// 最后一行的更改才会触发添加新一行
|
||||||
const id = getGenerateId();
|
const id = getGenerateId();
|
||||||
const lastLineData = paramsData.value[rowIndex]; // 上一行数据
|
const lastLineData = paramsData.value[rowIndex]; // 上一行数据
|
||||||
const selectColumnKeys = props.columns.filter((e) => e.typeOptions).map((e) => e.dataIndex); // 找到下拉框选项的列
|
const selectColumnKeys = props.columns.filter((e) => e.options).map((e) => e.dataIndex); // 找到下拉框选项的列
|
||||||
const nextLine = {
|
const nextLine = {
|
||||||
id,
|
id,
|
||||||
enable: true, // 是否勾选
|
enable: true, // 是否勾选
|
||||||
|
|
|
@ -86,22 +86,36 @@
|
||||||
</div> -->
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="h-[calc(100%-34px)]">
|
<div v-else class="h-[calc(100%-34px)]">
|
||||||
<div class="mb-[8px] flex items-center gap-[8px]">
|
<div class="mb-[8px] flex items-center justify-between">
|
||||||
<MsButton
|
<div class="flex items-center gap-[8px]">
|
||||||
type="text"
|
<MsButton
|
||||||
class="!mr-0"
|
type="text"
|
||||||
:class="jsonType === 'Schema' ? 'font-medium !text-[rgb(var(--primary-5))]' : '!text-[var(--color-text-4)]'"
|
class="!mr-0"
|
||||||
@click="jsonType = 'Schema'"
|
:class="jsonType === 'Schema' ? 'font-medium !text-[rgb(var(--primary-5))]' : '!text-[var(--color-text-4)]'"
|
||||||
>Schema</MsButton
|
@click="jsonType = 'Schema'"
|
||||||
>
|
>Schema</MsButton
|
||||||
<a-divider :margin="0" direction="vertical"></a-divider>
|
>
|
||||||
<MsButton
|
<a-divider :margin="0" direction="vertical"></a-divider>
|
||||||
type="text"
|
<MsButton
|
||||||
class="!mr-0"
|
type="text"
|
||||||
:class="jsonType === 'Json' ? 'font-medium !text-[rgb(var(--primary-5))]' : '!text-[var(--color-text-4)]'"
|
class="!mr-0"
|
||||||
@click="jsonType = 'Json'"
|
:class="jsonType === 'Json' ? 'font-medium !text-[rgb(var(--primary-5))]' : '!text-[var(--color-text-4)]'"
|
||||||
>Json</MsButton
|
@click="jsonType = 'Json'"
|
||||||
|
>Json</MsButton
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<a-button
|
||||||
|
v-show="jsonType === 'Schema'"
|
||||||
|
type="outline"
|
||||||
|
class="arco-btn-outline--secondary px-[8px]"
|
||||||
|
size="small"
|
||||||
|
@click="previewJsonSchema"
|
||||||
>
|
>
|
||||||
|
<div class="flex items-center gap-[8px]">
|
||||||
|
<icon-eye />
|
||||||
|
{{ t('common.preview') }}
|
||||||
|
</div>
|
||||||
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
<MsCodeEditor
|
<MsCodeEditor
|
||||||
v-if="jsonType === 'Json'"
|
v-if="jsonType === 'Json'"
|
||||||
|
@ -116,7 +130,7 @@
|
||||||
is-adaptive
|
is-adaptive
|
||||||
>
|
>
|
||||||
</MsCodeEditor>
|
</MsCodeEditor>
|
||||||
<MsJsonSchema v-else />
|
<MsJsonSchema v-else ref="jsonSchemaRef" />
|
||||||
</div>
|
</div>
|
||||||
<batchAddKeyVal
|
<batchAddKeyVal
|
||||||
v-if="showParamTable"
|
v-if="showParamTable"
|
||||||
|
@ -222,7 +236,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const typeOptions = computed(() => {
|
const options = computed(() => {
|
||||||
const fullOptions = Object.values(RequestParamsType).map((val) => ({
|
const fullOptions = Object.values(RequestParamsType).map((val) => ({
|
||||||
label: val,
|
label: val,
|
||||||
value: val,
|
value: val,
|
||||||
|
@ -246,7 +260,7 @@
|
||||||
dataIndex: 'paramType',
|
dataIndex: 'paramType',
|
||||||
slotName: 'paramType',
|
slotName: 'paramType',
|
||||||
hasRequired: true,
|
hasRequired: true,
|
||||||
typeOptions: typeOptions.value,
|
options: options.value,
|
||||||
width: 130,
|
width: 130,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -307,6 +321,14 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
const jsonType = ref<'Schema' | 'Json'>('Schema');
|
const jsonType = ref<'Schema' | 'Json'>('Schema');
|
||||||
|
const jsonSchemaRef = ref<InstanceType<typeof MsJsonSchema>>();
|
||||||
|
|
||||||
|
function previewJsonSchema() {
|
||||||
|
if (jsonType.value === 'Schema') {
|
||||||
|
jsonSchemaRef.value?.previewSchema();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 当前显示的代码
|
// 当前显示的代码
|
||||||
const currentBodyCode = computed({
|
const currentBodyCode = computed({
|
||||||
get() {
|
get() {
|
||||||
|
|
|
@ -73,7 +73,7 @@
|
||||||
dataIndex: 'paramType',
|
dataIndex: 'paramType',
|
||||||
slotName: 'paramType',
|
slotName: 'paramType',
|
||||||
hasRequired: true,
|
hasRequired: true,
|
||||||
typeOptions: Object.values(RequestParamsType)
|
options: Object.values(RequestParamsType)
|
||||||
.filter((val) => ![RequestParamsType.JSON, RequestParamsType.FILE].includes(val))
|
.filter((val) => ![RequestParamsType.JSON, RequestParamsType.FILE].includes(val))
|
||||||
.map((val) => ({
|
.map((val) => ({
|
||||||
label: val,
|
label: val,
|
||||||
|
|
|
@ -74,7 +74,7 @@
|
||||||
dataIndex: 'paramType',
|
dataIndex: 'paramType',
|
||||||
slotName: 'paramType',
|
slotName: 'paramType',
|
||||||
hasRequired: true,
|
hasRequired: true,
|
||||||
typeOptions: Object.values(RequestParamsType)
|
options: Object.values(RequestParamsType)
|
||||||
.filter((val) => ![RequestParamsType.JSON, RequestParamsType.FILE].includes(val as RequestParamsType))
|
.filter((val) => ![RequestParamsType.JSON, RequestParamsType.FILE].includes(val as RequestParamsType))
|
||||||
.map((val) => ({
|
.map((val) => ({
|
||||||
label: val,
|
label: val,
|
||||||
|
|
|
@ -146,7 +146,7 @@
|
||||||
title: 'apiScenario.params.csvScoped',
|
title: 'apiScenario.params.csvScoped',
|
||||||
dataIndex: 'scope',
|
dataIndex: 'scope',
|
||||||
slotName: 'scope',
|
slotName: 'scope',
|
||||||
typeOptions: [
|
options: [
|
||||||
{
|
{
|
||||||
label: t('apiScenario.scenario'),
|
label: t('apiScenario.scenario'),
|
||||||
value: 'SCENARIO',
|
value: 'SCENARIO',
|
||||||
|
|
|
@ -90,7 +90,7 @@
|
||||||
title: 'apiScenario.params.type',
|
title: 'apiScenario.params.type',
|
||||||
dataIndex: 'paramType',
|
dataIndex: 'paramType',
|
||||||
slotName: 'paramType',
|
slotName: 'paramType',
|
||||||
typeOptions: [
|
options: [
|
||||||
{
|
{
|
||||||
label: t('common.constant'),
|
label: t('common.constant'),
|
||||||
value: 'CONSTANT',
|
value: 'CONSTANT',
|
||||||
|
|
|
@ -90,7 +90,7 @@
|
||||||
showDrag: true,
|
showDrag: true,
|
||||||
hasRequired: false,
|
hasRequired: false,
|
||||||
columnSelectorDisabled: true,
|
columnSelectorDisabled: true,
|
||||||
typeOptions: [
|
options: [
|
||||||
{
|
{
|
||||||
label: t('common.constant'),
|
label: t('common.constant'),
|
||||||
value: 'CONSTANT',
|
value: 'CONSTANT',
|
||||||
|
|
Loading…
Reference in New Issue