From 2fc5db07454d318f38bf748ad7eba48b05b9b7ff Mon Sep 17 00:00:00 2001 From: baiqi Date: Wed, 3 Jul 2024 18:29:57 +0800 Subject: [PATCH] =?UTF-8?q?feat(json-schema):=20ms-json-schema=E7=BB=84?= =?UTF-8?q?=E4=BB=B6-json=E9=A2=84=E8=A7=88=E6=8E=A5=E5=8F=A3&json?= =?UTF-8?q?=E5=AF=BC=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/api/modules/api-test/management.ts | 8 ++ .../src/api/requrls/api-test/management.ts | 1 + .../business/ms-params-input/index.vue | 6 +- .../components/pure/ms-json-schema/index.vue | 81 +++++++++++---- .../pure/ms-json-schema/locale/zh-CN.ts | 1 + .../components/pure/ms-json-schema/types.ts | 2 +- .../components/pure/ms-json-schema/utils.ts | 99 ++++++++++++++++++- .../management/components/moduleTree.vue | 2 +- 8 files changed, 172 insertions(+), 28 deletions(-) diff --git a/frontend/src/api/modules/api-test/management.ts b/frontend/src/api/modules/api-test/management.ts index 0e87a1da8f..7edc5efe47 100644 --- a/frontend/src/api/modules/api-test/management.ts +++ b/frontend/src/api/modules/api-test/management.ts @@ -1,3 +1,5 @@ +import type { JsonSchema } from '@/components/pure/ms-json-schema/types'; + import MSR from '@/api/http/index'; import { AddCaseUrl, @@ -19,6 +21,7 @@ import { BatchUpdateDefinitionUrl, CasePageUrl, CheckDefinitionScheduleUrl, + ConvertJsonSchemaToJsonUrl, CopyMockUrl, DebugCaseUrl, DebugDefinitionUrl, @@ -298,6 +301,11 @@ export function getDefinitionReference(data: DefinitionReferencePageParams) { return MSR.post({ url: DefinitionReferenceUrl, data }); } +// 将json-schema转换为 json 数据 +export function convertJsonSchemaToJson(data: JsonSchema) { + return MSR.post({ url: ConvertJsonSchemaToJsonUrl, data }); +} + /** * Mock */ diff --git a/frontend/src/api/requrls/api-test/management.ts b/frontend/src/api/requrls/api-test/management.ts index 6011907133..39fa65afa0 100644 --- a/frontend/src/api/requrls/api-test/management.ts +++ b/frontend/src/api/requrls/api-test/management.ts @@ -35,6 +35,7 @@ export const OperationHistoryUrl = '/api/definition/operation-history'; // 接 export const SaveOperationHistoryUrl = '/api/definition/operation-history/save'; // 接口定义-另存变更历史为指定版本 export const RecoverOperationHistoryUrl = '/api/definition/operation-history/recover'; // 接口定义-变更历史恢复 export const DefinitionReferenceUrl = '/api/definition/get-reference'; // 获取接口引用关系 +export const ConvertJsonSchemaToJsonUrl = '/api/definition/preview'; // 将json-schema转换为 json 数据 /** * Mock diff --git a/frontend/src/components/business/ms-params-input/index.vue b/frontend/src/components/business/ms-params-input/index.vue index 93a23e289b..f08145d80b 100644 --- a/frontend/src/components/business/ms-params-input/index.vue +++ b/frontend/src/components/business/ms-params-input/index.vue @@ -363,7 +363,11 @@ }); const disabledPopover = computed(() => { - return !innerValue.value || innerValue.value.trim() === '' || isFocusAutoComplete.value; + return ( + !innerValue.value || + (typeof innerValue.value === 'string' && innerValue.value.trim() === '') || + isFocusAutoComplete.value + ); }); const paramSettingVisible = ref(false); diff --git a/frontend/src/components/pure/ms-json-schema/index.vue b/frontend/src/components/pure/ms-json-schema/index.vue index c4feb4eef1..9f6834bff3 100644 --- a/frontend/src/components/pure/ms-json-schema/index.vue +++ b/frontend/src/components/pure/ms-json-schema/index.vue @@ -421,7 +421,7 @@
{{ t('ms.json.schema.preview') }}
@@ -485,13 +494,14 @@ import MsParamsInput from '@/components/business/ms-params-input/index.vue'; import MsQuickInput from '@/components/business/ms-quick-input/index.vue'; + import { convertJsonSchemaToJson } from '@/api/modules/api-test/management'; import { useI18n } from '@/hooks/useI18n'; import { getGenerateId, traverseTree } from '@/utils'; import { TableKeyEnum } from '@/enums/tableEnum'; - import { JsonSchemaTableItem } from './types'; - import { convertToJsonSchema } from './utils'; + import { JsonSchema, JsonSchemaTableItem } from './types'; + import { parseJsonToJsonSchemaTableItem, tableItemToJsonSchema } from './utils'; const { t } = useI18n(); @@ -828,38 +838,52 @@ const batchAddDrawerVisible = ref(false); const batchAddValue = ref(''); + const batchAddType = ref<'json' | 'schema'>('json'); function batchAdd() { batchAddDrawerVisible.value = true; } function applyBatchAdd() { + if (batchAddType.value === 'json') { + const res = parseJsonToJsonSchemaTableItem(batchAddValue.value); + if (res.result.length > 0) { + data.value = res.result; + selectedKeys.value = res.ids; + } + } batchAddDrawerVisible.value = false; } const settingDrawerVisible = ref(false); const activeRecord = ref({}); - const activePreviewValue = ref(''); + const previewShowType = ref<'json' | 'schema'>('json'); + const activePreviewJsonValue = ref(''); + const activePreviewJsonSchemaValue = ref(''); + const activePreviewValue = computed(() => { + return previewShowType.value === 'json' ? activePreviewJsonValue.value : activePreviewJsonSchemaValue.value; + }); - function openSetting(record: JsonSchemaTableItem) { + async function openSetting(record: JsonSchemaTableItem) { // 浅拷贝,以保留 parent 和 children 的引用 activeRecord.value = { ...record, }; try { - activePreviewValue.value = JSON.stringify(convertToJsonSchema(record, record.id === 'root')); + const schema = tableItemToJsonSchema(record, record.id === 'root'); + activePreviewJsonSchemaValue.value = JSON.stringify(schema); } catch (error) { // eslint-disable-next-line no-console console.log(error); - activePreviewValue.value = t('ms.json.schema.convertFailed'); + activePreviewJsonSchemaValue.value = t('ms.json.schema.convertFailed'); } finally { settingDrawerVisible.value = true; } } function handleSettingFormChange() { - activePreviewValue.value = JSON.stringify( - convertToJsonSchema(activeRecord.value, activeRecord.value.id === 'root') + activePreviewJsonSchemaValue.value = JSON.stringify( + tableItemToJsonSchema(activeRecord.value, activeRecord.value.id === 'root') ); } @@ -901,9 +925,24 @@ } const previewDrawerVisible = ref(false); - function previewSchema() { + const previewDrawerLoading = ref(false); + + async function previewSchema() { previewDrawerVisible.value = true; - activePreviewValue.value = JSON.stringify(convertToJsonSchema(data.value[0])); + try { + previewDrawerLoading.value = true; + const schema = tableItemToJsonSchema(data.value[0]); + const res = await convertJsonSchemaToJson(schema as JsonSchema); + activePreviewJsonValue.value = JSON.stringify(res); + 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'); + } finally { + previewDrawerLoading.value = false; + } } defineExpose({ diff --git a/frontend/src/components/pure/ms-json-schema/locale/zh-CN.ts b/frontend/src/components/pure/ms-json-schema/locale/zh-CN.ts index d21c5ae5c7..aacb216c39 100644 --- a/frontend/src/components/pure/ms-json-schema/locale/zh-CN.ts +++ b/frontend/src/components/pure/ms-json-schema/locale/zh-CN.ts @@ -21,4 +21,5 @@ export default { 'ms.json.schema.convertFailed': '数据转换失败,请重试', 'ms.json.schema.minItems': '最小元素数量', 'ms.json.schema.maxItems': '最大元素数量', + 'ms.json.schema.illegalJsonConvertFailed': '转换失败,请检查输入的json 结构是否合法', }; diff --git a/frontend/src/components/pure/ms-json-schema/types.ts b/frontend/src/components/pure/ms-json-schema/types.ts index 8014050ae5..d6c5935410 100644 --- a/frontend/src/components/pure/ms-json-schema/types.ts +++ b/frontend/src/components/pure/ms-json-schema/types.ts @@ -17,7 +17,7 @@ export interface JsonSchemaCommon { } // json-schema 表格组件的表格项 export interface JsonSchemaTableItem extends JsonSchemaCommon { - required?: string[]; // 是否必填 + required?: boolean; // 是否必填 children?: JsonSchemaTableItem[]; parent?: JsonSchemaTableItem; // 父级 enumValues?: string; // 参数值的枚举 diff --git a/frontend/src/components/pure/ms-json-schema/utils.ts b/frontend/src/components/pure/ms-json-schema/utils.ts index 3cd8ca2fdd..8f86b47dbb 100644 --- a/frontend/src/components/pure/ms-json-schema/utils.ts +++ b/frontend/src/components/pure/ms-json-schema/utils.ts @@ -1,3 +1,8 @@ +import { Message } from '@arco-design/web-vue'; + +import { useI18n } from '@/hooks/useI18n'; +import { getGenerateId } from '@/utils'; + import type { JsonSchema, JsonSchemaItem, JsonSchemaTableItem } from './types'; /** @@ -5,7 +10,7 @@ import type { JsonSchema, JsonSchemaItem, JsonSchemaTableItem } from './types'; * @param schemaItem 表格组件项 * @param isRoot 是否为根节点 */ -export function convertToJsonSchema( +export function tableItemToJsonSchema( schemaItem: JsonSchemaTableItem, isRoot: boolean = true ): JsonSchema | JsonSchemaItem { @@ -41,7 +46,7 @@ export function convertToJsonSchema( required: [], }; schemaItem.children.forEach((child) => { - const childSchema = convertToJsonSchema(child, false); + const childSchema = tableItemToJsonSchema(child, false); schema.properties![child.title] = childSchema as JsonSchemaItem; if (child.required) { schema.required!.push(child.title); @@ -54,11 +59,97 @@ export function convertToJsonSchema( schema = { type: 'array', enable: schemaItem.enable, - items: schemaItem.children.map((child) => convertToJsonSchema(child, false) as JsonSchemaItem), + items: schemaItem.children.map((child) => tableItemToJsonSchema(child, false) as JsonSchemaItem), }; } } return schema; } -export default {}; + +/** + * 创建 json-schema 表格组件的表格项 + * @param key 对象/数组/普通子项的 key名 + * @param value 对象/数组/普通子项的值 + * @param parent 父级 + */ +function createItem(key: string, value: any, parent?: JsonSchemaTableItem): JsonSchemaTableItem { + let exampleValue; // 默认情况下,example 值为 undefined + const itemType = Array.isArray(value) ? 'array' : typeof value; + + // 如果值不是对象或数组,则直接将值作为 example + if (itemType !== 'object' && itemType !== 'array') { + exampleValue = typeof value === 'boolean' ? value.toString() : value; + } + + return { + id: getGenerateId(), + title: key, + type: itemType, + description: '', + enable: true, + required: true, + defaultValue: '', + example: exampleValue, // 仅当值不是对象或数组时,才赋予 example 值 + parent, + }; +} +/** + * 将 json 转换为 json-schema 表格组件的表格项 + * @param json json 字符串或对象或数组 + * @param parent 父级 + */ +export function parseJsonToJsonSchemaTableItem( + json: string | object | Array, + parent?: JsonSchemaTableItem +): { result: JsonSchemaTableItem[]; ids: Array } { + if (typeof json === 'string') { + // 尝试将 json 字符串转换为对象 + try { + json = JSON.parse(json); + } catch (error) { + const { t } = useI18n(); + Message.warning(t('ms.json.schema.illegalJsonConvertFailed')); + return { result: [], ids: [] }; + } + } + if (!parent) { + // 创建根节点 + const rootItem: JsonSchemaTableItem = { + id: 'root', + title: 'root', + type: Array.isArray(json) ? 'array' : 'object', + description: '', + enable: true, + required: true, + example: '', + defaultValue: '', + }; + const children = parseJsonToJsonSchemaTableItem(json, rootItem); + rootItem.children = children.result; + children.ids.push(rootItem.id); + return { result: [rootItem], ids: children.ids }; + } + + const items: JsonSchemaTableItem[] = []; + const type = Array.isArray(json) ? 'array' : 'object'; + const ids: Array = []; + + if (type === 'object' || type === 'array') { + // 遍历对象或数组 + 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); + item.children = children.result; + ids.push(...children.ids); + } else { + item.example = typeof value === 'boolean' ? value.toString() : value; + } + items.push(item); + ids.push(item.id); + }); + } + + return { result: items, ids }; +} diff --git a/frontend/src/views/api-test/management/components/moduleTree.vue b/frontend/src/views/api-test/management/components/moduleTree.vue index 21d6ce649d..0f1ffbe9cc 100644 --- a/frontend/src/views/api-test/management/components/moduleTree.vue +++ b/frontend/src/views/api-test/management/components/moduleTree.vue @@ -276,7 +276,7 @@ }; } return { - height: 'calc(100vh - 298px)', + height: 'calc(100vh - 250px)', threshold: 200, fixedSize: true, buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题