feat(json-schema): ms-json-schema组件Done

This commit is contained in:
baiqi 2024-07-04 15:42:32 +08:00 committed by 刘瑞斌
parent 9b97fe0664
commit 37a7993785
15 changed files with 483 additions and 165 deletions

View File

@ -72,7 +72,7 @@
<style lang="less">
/** 面包屑 **/
.arco-breadcrumb-item {
@apply cursor-pointer;
@apply cursor-pointer break-keep;
color: var(--color-text-4);
&:hover {

View File

@ -527,7 +527,7 @@
if (node.children && node.children.length > 0) {
waitingRenderNodes = waitingRenderNodes.concat(node.children);
}
if (total > list.length * current) {
if (total > 100 * (current + 1)) {
//
const moreNode = window.minder.createNode(
{

View File

@ -1,5 +1,5 @@
<template>
<div>
<div class="flex w-full">
<a-popover position="tl" :disabled="!modelValue || modelValue.trim() === ''" class="ms-params-input-popover">
<template #content>
<div v-if="props.title" class="ms-params-popover-title">
@ -38,26 +38,26 @@
@change="(val) => emit('change', val)"
/>
</a-popover>
<a-modal
v-model:visible="showQuickInput"
:title="props.title"
:ok-text="t('common.save')"
:ok-button-props="{ disabled: !quickInputValue || quickInputValue.trim() === '' }"
class="ms-modal-form"
body-class="!p-0"
:width="480"
title-align="start"
@ok="applyQuickInputDesc"
@close="clearQuickInputDesc"
>
<a-textarea
v-model:model-value="quickInputValue"
:placeholder="props.placeholder"
:auto-size="{ minRows: 2 }"
:max-length="1000"
/>
</a-modal>
</div>
<a-modal
v-model:visible="showQuickInput"
:title="props.title"
:ok-text="t('common.save')"
:ok-button-props="{ disabled: !quickInputValue || quickInputValue.trim() === '' }"
class="ms-modal-form"
body-class="!p-0"
:width="480"
title-align="start"
@ok="applyQuickInputDesc"
@close="clearQuickInputDesc"
>
<a-textarea
v-model:model-value="quickInputValue"
:placeholder="props.placeholder"
:auto-size="{ minRows: 2 }"
:max-length="1000"
/>
</a-modal>
</template>
<script setup lang="ts">

View File

@ -461,8 +461,14 @@
</template>
</MsCodeEditor>
</MsDrawer>
<MsDrawer v-model:visible="previewDrawerVisible" :width="600" :title="t('common.preview')" :footer="false">
<a-spin class="block" :loading="previewDrawerLoading">
<MsDrawer
v-model:visible="previewDrawerVisible"
:width="600"
:title="t('common.preview')"
:footer="false"
@close="previewShowType = 'json'"
>
<a-spin class="block h-full w-full" :loading="previewDrawerLoading">
<MsCodeEditor
v-model:model-value="activePreviewValue"
theme="vs"
@ -500,8 +506,12 @@
import { TableKeyEnum } from '@/enums/tableEnum';
import { JsonSchema, JsonSchemaTableItem } from './types';
import { parseJsonToJsonSchemaTableItem, tableItemToJsonSchema } from './utils';
import { JsonSchema, JsonSchemaItem, JsonSchemaTableItem } from './types';
import {
parseJsonToJsonSchemaTableData,
parseSchemaToJsonSchemaTableData,
parseTableDataToJsonSchema,
} from './utils';
const { t } = useI18n();
@ -523,29 +533,43 @@
children: undefined,
};
const data = defineModel<JsonSchemaTableItem[]>('data', {
default: () => [
{
id: 'root',
title: 'root',
type: 'object',
example: '',
description: '',
enable: true,
defaultValue: '',
maximum: undefined,
minimum: undefined,
maxLength: undefined,
minLength: undefined,
enumValues: '',
pattern: undefined,
format: undefined,
required: false,
children: [],
},
],
default: () => [],
});
const expandKeys = defineModel<string[]>('expandKeys', {
default: () => ['root'],
});
const selectedKeys = defineModel<string[]>('selectedKeys', {
default: () => ['root'],
});
//
watchEffect(() => {
if (data.value.length === 0) {
data.value = [
{
id: 'root',
title: 'root',
type: 'object',
example: '',
description: '',
enable: true,
defaultValue: '',
maximum: undefined,
minimum: undefined,
maxLength: undefined,
minLength: undefined,
enumValues: '',
pattern: undefined,
format: undefined,
required: false,
children: [],
},
];
if (selectedKeys.value.length === 0) {
selectedKeys.value = ['root'];
}
}
});
const expandKeys = ref<string[]>(['root']);
const selectedKeys = ref<string[]>(['root']);
const typeOptions: SelectOptionData[] = [
{
@ -845,14 +869,22 @@
}
function applyBatchAdd() {
if (batchAddType.value === 'json') {
const res = parseJsonToJsonSchemaTableItem(batchAddValue.value);
try {
let res: { result: JsonSchemaTableItem[]; ids: Array<string> } = { result: [], ids: [] };
if (batchAddType.value === 'json') {
res = parseJsonToJsonSchemaTableData(batchAddValue.value);
} else {
res = parseSchemaToJsonSchemaTableData(batchAddValue.value);
}
if (res.result.length > 0) {
data.value = res.result;
selectedKeys.value = res.ids;
}
batchAddDrawerVisible.value = false;
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
batchAddDrawerVisible.value = false;
}
const settingDrawerVisible = ref(false);
@ -870,7 +902,7 @@
...record,
};
try {
const schema = tableItemToJsonSchema(record, record.id === 'root');
const schema = parseTableDataToJsonSchema(record, record.id === 'root');
activePreviewJsonSchemaValue.value = JSON.stringify(schema);
} catch (error) {
// eslint-disable-next-line no-console
@ -883,7 +915,7 @@
function handleSettingFormChange() {
activePreviewJsonSchemaValue.value = JSON.stringify(
tableItemToJsonSchema(activeRecord.value, activeRecord.value.id === 'root')
parseTableDataToJsonSchema(activeRecord.value, activeRecord.value.id === 'root')
);
}
@ -927,19 +959,32 @@
const previewDrawerVisible = ref(false);
const previewDrawerLoading = ref(false);
/**
* 预览 schema
*/
async function previewSchema() {
previewDrawerVisible.value = true;
previewDrawerLoading.value = true;
let schema: JsonSchema | JsonSchemaItem | undefined;
try {
previewDrawerLoading.value = true;
const schema = tableItemToJsonSchema(data.value[0]);
const res = await convertJsonSchemaToJson(schema as JsonSchema);
activePreviewJsonValue.value = JSON.stringify(res);
// json schema
schema = parseTableDataToJsonSchema(data.value[0] as JsonSchemaTableItem);
activePreviewJsonSchemaValue.value = JSON.stringify(schema);
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
activePreviewJsonValue.value = t('ms.json.schema.convertFailed');
activePreviewJsonSchemaValue.value = t('ms.json.schema.convertFailed');
previewDrawerLoading.value = false;
return;
}
try {
// json schema json
const res = await convertJsonSchemaToJson(schema as JsonSchema);
activePreviewJsonValue.value = JSON.stringify(res);
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
activePreviewJsonValue.value = t('ms.json.schema.convertFailed');
} finally {
previewDrawerLoading.value = false;
}

View File

@ -0,0 +1,25 @@
export default {
'ms.json.schema.name': 'Parameter Name',
'ms.json.schema.nameNotNull': 'Parameter Name cannot be empty',
'ms.json.schema.type': 'Type',
'ms.json.schema.value': 'Parameter Value',
'ms.json.schema.addChild': 'Add Child Field',
'ms.json.schema.advancedSettings': 'Advanced Settings',
'ms.json.schema.minLength': 'Minimum Length',
'ms.json.schema.maxLength': 'Maximum Length',
'ms.json.schema.minimum': 'Minimum Value',
'ms.json.schema.maximum': 'Maximum Value',
'ms.json.schema.default': 'Default Value',
'ms.json.schema.enum': 'Enumeration',
'ms.json.schema.enumPlaceholder': '1 line, 1 enumeration value',
'ms.json.schema.regex': 'Regular Expression',
'ms.json.schema.regexPlaceholder': 'e.g. {reg}',
'ms.json.schema.format': 'Format',
'ms.json.schema.preview': 'Preview',
'ms.json.schema.batchAdd': 'Batch Add',
'ms.json.schema.batchAddTip': 'Write in the format: "key":"value", e.g. "name":"natural"',
'ms.json.schema.convertFailed': 'Data conversion failed, please try again',
'ms.json.schema.minItems': 'Minimum number of items',
'ms.json.schema.maxItems': 'Maximum number of items',
'ms.json.schema.illegalJsonConvertFailed': 'Conversion failed, please check if the input JSON structure is valid',
};

View File

@ -2,7 +2,7 @@ export interface JsonSchemaCommon {
id: string;
title: string; // 参数名称
type: string; // 参数类型
description: string; // 参数描述
description?: string; // 参数描述
enable: boolean; // 是否启用
example: string; // 参数值
defaultValue: string | number | boolean; // 默认值
@ -35,4 +35,5 @@ export interface JsonSchema {
properties?: Record<string, JsonSchemaItem>;
items?: JsonSchemaItem[];
required?: string[];
description?: string;
}

View File

@ -10,61 +10,70 @@ import type { JsonSchema, JsonSchemaItem, JsonSchemaTableItem } from './types';
* @param schemaItem
* @param isRoot
*/
export function tableItemToJsonSchema(
export function parseTableDataToJsonSchema(
schemaItem: JsonSchemaTableItem,
isRoot: boolean = true
): JsonSchema | JsonSchemaItem {
let schema: JsonSchema | JsonSchemaItem = { type: schemaItem.type };
): JsonSchema | JsonSchemaItem | undefined {
try {
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',
// 对于 null 类型,只设置 type 和 enable 属性
if (schemaItem.type === 'null') {
return {
type: 'null',
enable: schemaItem.enable,
properties: {},
required: [],
};
schemaItem.children.forEach((child) => {
const childSchema = tableItemToJsonSchema(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) => tableItemToJsonSchema(child, false) as JsonSchemaItem),
};
}
}
return schema;
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 = parseTableDataToJsonSchema(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) => parseTableDataToJsonSchema(child, false) as JsonSchemaItem),
};
}
}
return schema;
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
return undefined;
}
}
/**
@ -94,12 +103,13 @@ function createItem(key: string, value: any, parent?: JsonSchemaTableItem): Json
parent,
};
}
/**
* json json-schema
* json json-schema
* @param json json
* @param parent
*/
export function parseJsonToJsonSchemaTableItem(
export function parseJsonToJsonSchemaTableData(
json: string | object | Array<any>,
parent?: JsonSchemaTableItem
): { result: JsonSchemaTableItem[]; ids: Array<string> } {
@ -125,7 +135,7 @@ export function parseJsonToJsonSchemaTableItem(
example: '',
defaultValue: '',
};
const children = parseJsonToJsonSchemaTableItem(json, rootItem);
const children = parseJsonToJsonSchemaTableData(json, rootItem);
rootItem.children = children.result;
children.ids.push(rootItem.id);
return { result: [rootItem], ids: children.ids };
@ -140,7 +150,7 @@ export function parseJsonToJsonSchemaTableItem(
Object.entries(json).forEach(([key, value]) => {
const item: JsonSchemaTableItem = createItem(key, value, parent);
if (typeof value === 'object' || Array.isArray(value)) {
const children = parseJsonToJsonSchemaTableItem(value, item);
const children = parseJsonToJsonSchemaTableData(value, item);
item.children = children.result;
ids.push(...children.ids);
} else {
@ -153,3 +163,76 @@ export function parseJsonToJsonSchemaTableItem(
return { result: items, ids };
}
/**
* json-schema json-schema
* @param schema json-schema /
*/
export function parseSchemaToJsonSchemaTableData(schema: string | JsonSchema): {
result: JsonSchemaTableItem[];
ids: Array<string>;
} {
const ids: Array<string> = ['root'];
if (typeof schema === 'string') {
// 尝试将 json 字符串转换为对象
try {
schema = JSON.parse(schema);
} catch (error) {
const { t } = useI18n();
Message.warning(t('ms.json.schema.illegalJsonConvertFailed'));
return { result: [], ids: [] };
}
}
const parseNode = (
node: JsonSchema | JsonSchemaItem,
parent?: JsonSchemaTableItem,
requiredFields?: string[]
): JsonSchemaTableItem => {
let item: JsonSchemaTableItem;
if (!parent) {
// 根节点
item = {
id: 'root',
title: 'root',
type: node.type,
description: node.description,
enable: true,
required: true,
example: '',
defaultValue: '',
};
} else {
// 子孙节点
// 剔除不需要的属性 properties、items
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { properties, items, ...copiedObj } = node;
const itemId = getGenerateId();
item = {
...(copiedObj as JsonSchemaItem),
id: itemId,
required: requiredFields?.includes((node as JsonSchemaItem).title),
enumValues: (node as JsonSchemaItem).enumValues?.join('\n'),
parent,
};
if ((node as JsonSchemaItem).enable === true || (node as JsonSchemaItem).enable === undefined) {
// 如果enable为true或者undefined则设置为选中
ids.push(itemId);
}
}
// 检查当前节点的required属性是否存在且为数组类型然后传递当前节点的required属性给子节点判断是否必填
const newRequiredFields = Array.isArray(node.required) ? node.required : undefined;
if ((node.type === 'object' && node.properties) || (node.type === 'array' && node.items)) {
const children = node.type === 'object' ? node.properties : node.items;
item.children = Object.entries(children || []).map(([key, childNode]) =>
parseNode({ ...childNode, title: key }, item, newRequiredFields)
);
}
return item;
};
const result = [parseNode(schema as JsonSchema)];
return { result, ids };
}

View File

@ -1,5 +1,5 @@
import { Language } from '@/components/pure/ms-code-editor/types';
import type { JsonSchemaItem } from '@/components/pure/ms-json-schema/types';
import type { JsonSchema, JsonSchemaTableItem } from '@/components/pure/ms-json-schema/types';
import {
type FullResponseAssertionType,
@ -124,8 +124,10 @@ export interface ExecuteBinaryBody {
export interface ExecuteJsonBody {
enableJsonSchema?: boolean;
enableTransition?: boolean;
jsonSchema?: JsonSchemaItem[];
jsonSchema?: JsonSchema;
jsonValue: string;
// 前端渲染字段
jsonSchemaTableData?: JsonSchemaTableItem[];
}
// 执行请求配置
export interface ExecuteOtherConfig {

View File

@ -89,8 +89,9 @@ export const defaultResponseItem: ResponseDefinition = {
bodyType: ResponseBodyFormat.JSON,
jsonBody: {
jsonValue: '',
enableJsonSchema: false,
enableJsonSchema: true,
enableTransition: false,
jsonSchemaTableData: [],
},
xmlBody: {
value: '',
@ -117,6 +118,8 @@ export const defaultBodyParams: ExecuteBody = {
},
jsonBody: {
jsonValue: '',
enableJsonSchema: true,
jsonSchemaTableData: [],
},
xmlBody: { value: '' },
rawBody: { value: '' },
@ -312,6 +315,8 @@ export const mockDefaultParams: MockParams = {
},
jsonBody: {
jsonValue: '',
enableJsonSchema: true,
jsonSchemaTableData: [],
},
xmlBody: { value: '' },
rawBody: { value: '' },
@ -331,8 +336,9 @@ export const mockDefaultParams: MockParams = {
bodyType: ResponseBodyFormat.JSON,
jsonBody: {
jsonValue: '',
enableJsonSchema: false,
enableJsonSchema: true,
enableTransition: false,
jsonSchemaTableData: [],
},
xmlBody: {
value: '',

View File

@ -85,27 +85,35 @@
</a-tooltip>
</div> -->
</div>
<div v-else class="h-[calc(100%-34px)]">
<a-spin v-else :loading="bodyLoading" class="block h-[calc(100%-34px)]">
<div class="mb-[8px] flex items-center justify-between">
<div class="flex items-center gap-[8px]">
<MsButton
type="text"
class="!mr-0"
:class="jsonType === 'Schema' ? 'font-medium !text-[rgb(var(--primary-5))]' : '!text-[var(--color-text-4)]'"
@click="jsonType = 'Schema'"
:class="
innerParams.jsonBody.enableJsonSchema
? 'font-medium !text-[rgb(var(--primary-5))]'
: '!text-[var(--color-text-4)]'
"
@click="innerParams.jsonBody.enableJsonSchema = true"
>Schema</MsButton
>
<a-divider :margin="0" direction="vertical"></a-divider>
<MsButton
type="text"
class="!mr-0"
:class="jsonType === 'Json' ? 'font-medium !text-[rgb(var(--primary-5))]' : '!text-[var(--color-text-4)]'"
@click="jsonType = 'Json'"
:class="
!innerParams.jsonBody.enableJsonSchema
? 'font-medium !text-[rgb(var(--primary-5))]'
: '!text-[var(--color-text-4)]'
"
@click="innerParams.jsonBody.enableJsonSchema = false"
>Json</MsButton
>
</div>
<a-button
v-show="jsonType === 'Schema'"
v-show="innerParams.jsonBody.enableJsonSchema"
type="outline"
class="arco-btn-outline--secondary px-[8px]"
size="small"
@ -117,8 +125,14 @@
</div>
</a-button>
</div>
<MsJsonSchema
v-if="innerParams.jsonBody.enableJsonSchema"
ref="jsonSchemaRef"
v-model:data="innerParams.jsonBody.jsonSchemaTableData"
v-model:selectedKeys="selectedKeys"
/>
<MsCodeEditor
v-if="jsonType === 'Json'"
v-else
v-model:model-value="currentBodyCode"
:read-only="props.disabledExceptParam"
theme="vs"
@ -129,9 +143,13 @@
:language="currentCodeLanguage"
is-adaptive
>
<template #rightTitle>
<a-button type="outline" class="arco-btn-outline--secondary p-[0_8px]" size="mini" @click="autoMakeJson">
<div class="text-[var(--color-text-1)]">{{ t('apiTestManagement.autoMake') }}</div>
</a-button>
</template>
</MsCodeEditor>
<MsJsonSchema v-else ref="jsonSchemaRef" />
</div>
</a-spin>
<batchAddKeyVal
v-if="showParamTable"
v-model:visible="batchAddKeyValVisible"
@ -143,17 +161,19 @@
</template>
<script setup lang="ts">
import { TableColumnData } from '@arco-design/web-vue';
import { Message, TableColumnData } from '@arco-design/web-vue';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
import { LanguageEnum } from '@/components/pure/ms-code-editor/types';
import MsJsonSchema from '@/components/pure/ms-json-schema/index.vue';
import { parseSchemaToJsonSchemaTableData, parseTableDataToJsonSchema } from '@/components/pure/ms-json-schema/utils';
import { MsFileItem } from '@/components/pure/ms-upload/types';
import MsAddAttachment from '@/components/business/ms-add-attachment/index.vue';
import batchAddKeyVal from '@/views/api-test/components/batchAddKeyVal.vue';
import paramTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
import { convertJsonSchemaToJson } from '@/api/modules/api-test/management';
import { requestBodyTypeMap } from '@/config/apiTest';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
@ -186,6 +206,9 @@
const innerParams = defineModel<ExecuteBody>('params', {
required: true,
});
const selectedKeys = ref<string[]>([]);
const bodyLoading = ref(false);
const batchAddKeyValVisible = ref(false);
const fileList = ref<MsFileItem[]>([]);
@ -202,6 +225,17 @@
}
);
watchEffect(() => {
if (innerParams.value.jsonBody.jsonSchema) {
const { result, ids } = parseSchemaToJsonSchemaTableData(innerParams.value.jsonBody.jsonSchema);
innerParams.value.jsonBody.jsonSchemaTableData = result;
selectedKeys.value = ids;
} else {
innerParams.value.jsonBody.jsonSchemaTableData = [];
selectedKeys.value = [];
}
});
async function handleFileChange(files: MsFileItem[], file?: MsFileItem) {
try {
if (file?.local && file.file && props.uploadTempFileApi) {
@ -320,15 +354,42 @@
},
});
const jsonType = ref<'Schema' | 'Json'>('Schema');
const jsonSchemaRef = ref<InstanceType<typeof MsJsonSchema>>();
function previewJsonSchema() {
if (jsonType.value === 'Schema') {
if (innerParams.value.jsonBody.enableJsonSchema) {
jsonSchemaRef.value?.previewSchema();
}
}
/**
* 自动转换json schema为json
*/
async function autoMakeJson() {
if (!innerParams.value.jsonBody.enableJsonSchema) {
try {
bodyLoading.value = true;
let schema = innerParams.value.jsonBody.jsonSchema;
if (!schema && innerParams.value.jsonBody.jsonSchemaTableData) {
// jsonSchema json schema
schema = parseTableDataToJsonSchema(innerParams.value.jsonBody.jsonSchemaTableData[0]);
}
if (schema) {
// json schema json
const res = await convertJsonSchemaToJson(schema);
innerParams.value.jsonBody.jsonValue = JSON.stringify(res);
} else {
Message.warning(t('apiTestManagement.pleaseInputJsonSchema'));
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
bodyLoading.value = false;
}
}
}
//
const currentBodyCode = computed({
get() {

View File

@ -432,6 +432,7 @@
import { TabItem } from '@/components/pure/ms-editable-tab/types';
import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import { parseTableDataToJsonSchema } from '@/components/pure/ms-json-schema/utils';
import MsTab from '@/components/pure/ms-tab/index.vue';
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
import assertion from '@/components/business/ms-assertion/index.vue';
@ -990,7 +991,7 @@
*/
function makeRequestParams(executeType?: 'localExec' | 'serverExec') {
const isExecute = executeType === 'localExec' || executeType === 'serverExec';
const { formDataBody, wwwFormBody } = requestVModel.value.body;
const { formDataBody, wwwFormBody, jsonBody } = requestVModel.value.body;
const polymorphicName = protocolOptions.value.find(
(e) => e.value === requestVModel.value.protocol
)?.polymorphicName; //
@ -1023,6 +1024,13 @@
wwwFormBody: {
formValues: realWwwFormBodyValues,
},
jsonBody: {
jsonValue: jsonBody.jsonValue,
enableJsonSchema: jsonBody.enableJsonSchema,
jsonSchema: jsonBody.jsonSchemaTableData
? parseTableDataToJsonSchema(jsonBody.jsonSchemaTableData[0])
: undefined,
},
},
headers: filterKeyValParams(requestVModel.value.headers, defaultHeaderParamsItem, isExecute).validParams,
method: requestVModel.value.method,
@ -1058,6 +1066,16 @@
response: requestVModel.value.responseDefinition?.map((e) => ({
...e,
headers: filterKeyValParams(e.headers, defaultKeyValueParamItem, isExecute).validParams,
body: {
...e.body,
jsonBody: {
jsonValue: e.body.jsonBody.jsonValue,
enableJsonSchema: jsonBody.enableJsonSchema,
jsonSchema: e.body.jsonBody.jsonSchemaTableData
? parseTableDataToJsonSchema(e.body.jsonBody.jsonSchemaTableData[0])
: undefined,
},
},
})),
};
} else {
@ -1172,9 +1190,6 @@
() => requestVModel.value.id,
async () => {
isSwitchingContent.value = true; //
nextTick(() => {
isSwitchingContent.value = false; //
});
if (requestVModel.value.protocol !== 'HTTP') {
requestVModel.value.activeTab = RequestComposition.PLUGIN;
if (protocolOptions.value.length === 0) {
@ -1206,6 +1221,9 @@
requestVModel.value.executeLoading = false;
delete temporaryResponseMap[props.request.reportId];
}
nextTick(() => {
isSwitchingContent.value = false; //
});
},
{
immediate: true,

View File

@ -96,20 +96,48 @@
{{ ResponseBodyFormat[item].toLowerCase() }}
</a-radio>
</a-radio-group>
<!-- <div v-if="activeResponse.body.bodyType === ResponseBodyFormat.JSON" class="ml-auto flex items-center">
<a-radio-group
v-model:model-value="activeResponse.body.jsonBody.enableJsonSchema"
size="mini"
@change="emit('change')"
<div
v-if="activeResponse.body.bodyType === ResponseBodyFormat.JSON"
class="ml-auto flex items-center gap-[8px]"
>
<MsButton
type="text"
class="!mr-0"
:class="
activeResponse.body.jsonBody.enableJsonSchema
? 'font-medium !text-[rgb(var(--primary-5))]'
: '!text-[var(--color-text-4)]'
"
@click="activeResponse.body.jsonBody.enableJsonSchema = true"
>
<a-radio :value="false">Json</a-radio>
<a-radio class="mr-0" :value="true"> Json Schema </a-radio>
</a-radio-group>
<div class="flex items-center gap-[8px]">
<a-switch v-model:model-value="activeResponse.body.jsonBody.enableTransition" size="small" type="line" />
{{ t('apiTestManagement.dynamicConversion') }}
</div>
</div> -->
Schema
</MsButton>
<a-divider :margin="0" direction="vertical"></a-divider>
<MsButton
type="text"
class="!mr-0"
:class="
!activeResponse.body.jsonBody.enableJsonSchema
? 'font-medium !text-[rgb(var(--primary-5))]'
: '!text-[var(--color-text-4)]'
"
@click="activeResponse.body.jsonBody.enableJsonSchema = false"
>
Json
</MsButton>
<a-button
v-show="activeResponse.body.jsonBody.enableJsonSchema"
type="outline"
class="arco-btn-outline--secondary ml-[16px] px-[8px]"
size="small"
@click="previewJsonSchema"
>
<div class="flex items-center gap-[8px]">
<icon-eye />
{{ t('common.preview') }}
</div>
</a-button>
</div>
</div>
<div
v-if="
@ -118,12 +146,14 @@
)
"
>
<!-- <MsJsonSchema
<MsJsonSchema
v-if="activeResponse.body.jsonBody.enableJsonSchema"
:data="activeResponse.body.jsonBody.jsonSchema"
:columns="jsonSchemaColumns"
/> -->
ref="jsonSchemaRef"
v-model:data="activeResponse.body.jsonBody.jsonSchemaTableData"
v-model:selectedKeys="selectedKeys"
/>
<MsCodeEditor
v-else
ref="responseEditorRef"
v-model:model-value="currentBodyCode"
:language="currentCodeLanguage"
@ -134,6 +164,11 @@
:show-charset-change="false"
show-code-format
>
<template #rightTitle>
<a-button type="outline" class="arco-btn-outline--secondary p-[0_8px]" size="mini" @click="autoMakeJson">
<div class="text-[var(--color-text-1)]">{{ t('apiTestManagement.autoMake') }}</div>
</a-button>
</template>
</MsCodeEditor>
</div>
<div v-else>
@ -194,14 +229,16 @@
</template>
<script setup lang="ts">
import { Message } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
import { LanguageEnum } from '@/components/pure/ms-code-editor/types';
import MsEditableTab from '@/components/pure/ms-editable-tab/index.vue';
import { TabItem } from '@/components/pure/ms-editable-tab/types';
// import { FormTableColumn } from '@/components/pure/ms-form-table/index.vue';
// import MsJsonSchema from '@/components/pure/ms-json-schema/index.vue';
import MsJsonSchema from '@/components/pure/ms-json-schema/index.vue';
import { parseSchemaToJsonSchemaTableData, parseTableDataToJsonSchema } from '@/components/pure/ms-json-schema/utils';
import MsMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import { MsFileItem } from '@/components/pure/ms-upload/types';
@ -209,6 +246,7 @@
import paramTable, { ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
import popConfirm from '@/views/api-test/components/popConfirm.vue';
import { convertJsonSchemaToJson } from '@/api/modules/api-test/management';
import { responseHeaderOption } from '@/config/apiTest';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
@ -350,20 +388,54 @@
emit('change');
}
// const jsonSchemaColumns: FormTableColumn[] = [
// {
// title: 'apiTestManagement.paramName',
// dataIndex: 'key',
// slotName: 'key',
// inputType: 'input',
// },
// {
// title: 'apiTestManagement.paramVal',
// dataIndex: 'value',
// slotName: 'value',
// inputType: 'input',
// },
// ];
const jsonSchemaRef = ref<InstanceType<typeof MsJsonSchema>>();
const bodyLoading = ref(false);
const selectedKeys = ref<string[]>([]);
watchEffect(() => {
if (activeResponse.value.body.jsonBody.jsonSchema) {
const { result, ids } = parseSchemaToJsonSchemaTableData(activeResponse.value.body.jsonBody.jsonSchema);
activeResponse.value.body.jsonBody.jsonSchemaTableData = result;
selectedKeys.value = ids;
} else {
activeResponse.value.body.jsonBody.jsonSchemaTableData = [];
selectedKeys.value = [];
}
});
function previewJsonSchema() {
if (activeResponse.value.body.jsonBody.enableJsonSchema) {
jsonSchemaRef.value?.previewSchema();
}
}
/**
* 自动转换json schema为json
*/
async function autoMakeJson() {
if (!activeResponse.value.body.jsonBody.enableJsonSchema) {
try {
bodyLoading.value = true;
let schema = activeResponse.value.body.jsonBody.jsonSchema;
if (!schema && activeResponse.value.body.jsonBody.jsonSchemaTableData) {
// jsonSchema json schema
schema = parseTableDataToJsonSchema(activeResponse.value.body.jsonBody.jsonSchemaTableData[0]);
}
if (schema) {
// json schema json
const res = await convertJsonSchemaToJson(schema);
activeResponse.value.body.jsonBody.jsonValue = JSON.stringify(res);
} else {
Message.warning(t('apiTestManagement.pleaseInputJsonSchema'));
}
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
bodyLoading.value = false;
}
}
}
//
const currentBodyCode = computed({

View File

@ -183,8 +183,9 @@
if (!item.body.jsonBody) {
item.body.jsonBody = {
jsonValue: '',
enableJsonSchema: false,
enableJsonSchema: true,
enableTransition: false,
jsonSchemaTableData: [],
};
if (!item.body.xmlBody) {
item.body.xmlBody = {

View File

@ -166,6 +166,8 @@ export default {
'apiTestManagement.regex': 'Regular Expression',
'apiTestManagement.caseTotal': 'Case total',
'apiTestManagement.requestTypeTip': 'Note: Batch request type changes apply only to HTTP requests.',
'apiTestManagement.autoMake': 'Auto Generate',
'apiTestManagement.pleaseInputJsonSchema': 'Please enter Schema first before automatically generating it.',
'case.execute.selectEnv': 'Select Environment',
'case.execute.defaultEnv': 'Default Environment',
'case.execute.newEnv': 'New Environment',

View File

@ -159,6 +159,8 @@ export default {
'apiTestManagement.regex': '正则表达式',
'apiTestManagement.caseTotal': '用例数',
'apiTestManagement.requestTypeTip': '注批量修改请求类型仅对HTTP协议的请求生效',
'apiTestManagement.autoMake': '自动生成',
'apiTestManagement.pleaseInputJsonSchema': '请先输入 Schema 后再进行自动生成',
'case.execute.selectEnv': '环境选择',
'case.execute.defaultEnv': '默认环境',
'case.execute.newEnv': '新环境',