fix: 【Api配置】修复api响应参数类型展示错误,接口文档添加注释
This commit is contained in:
parent
08c8eda133
commit
bda3e6a4c7
|
@ -24,7 +24,12 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="api-card" v-if="requestCard.codeText !== undefined">
|
<div class="api-card" v-if="requestCard.codeText !== undefined">
|
||||||
<h5>请求示例</h5>
|
<h5>请求示例</h5>
|
||||||
<JsonViewer :value="requestCard.codeText" copyable />
|
<Monaco
|
||||||
|
:tips="requestCard.tips"
|
||||||
|
:codeText="requestCard.codeText"
|
||||||
|
:loading="loading"
|
||||||
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="api-card" v-if="requestCard.tableData.length">
|
<div class="api-card" v-if="requestCard.tableData.length">
|
||||||
<h5>请求参数</h5>
|
<h5>请求参数</h5>
|
||||||
|
@ -37,10 +42,7 @@
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
<template #required="slotProps">
|
<template #required="slotProps">
|
||||||
<span>{{ Boolean(slotProps.required) + '' }}</span>
|
<span :style="{ color: Boolean(slotProps.required) ? '#f81d22' : ''}">{{ Boolean(slotProps.required) + '' }}</span>
|
||||||
</template>
|
|
||||||
<template #type="slotProps">
|
|
||||||
<span>{{ slotProps?.schema.type }}</span>
|
|
||||||
</template>
|
</template>
|
||||||
</j-pro-table>
|
</j-pro-table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -80,18 +82,23 @@
|
||||||
</j-pro-table>
|
</j-pro-table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<JsonViewer :value="respParamsCard.codeText" copyable />
|
<!-- <JsonViewer :value="respParamsCard.codeText" copyable />-->
|
||||||
|
<Monaco
|
||||||
|
:tips="respParamsCard.tips"
|
||||||
|
:codeText="respParamsCard.codeText"
|
||||||
|
:loading="loading"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts" name="APIDoes">
|
||||||
import { JsonViewer } from 'vue3-json-viewer';
|
|
||||||
import 'vue3-json-viewer/dist/index.css';
|
|
||||||
import type { apiDetailsType } from '../typing';
|
import type { apiDetailsType } from '../typing';
|
||||||
import InputCard from './InputCard.vue';
|
import InputCard from './InputCard.vue';
|
||||||
import { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import { findData, getCodeText, dealNoRef } from '../utils';
|
import { findData, getCodeText, dealNoRef } from '../utils';
|
||||||
|
import {randomString} from "@/utils/utils";
|
||||||
|
import Monaco from './monaco.vue'
|
||||||
|
|
||||||
type cardType = {
|
type cardType = {
|
||||||
columns: object[];
|
columns: object[];
|
||||||
|
@ -112,38 +119,49 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { selectApi } = toRefs(props);
|
const { selectApi } = toRefs(props);
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
const requestCard = reactive<cardType>({
|
const requestCard = reactive<cardType>({
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
title: '参数名',
|
title: '参数名',
|
||||||
dataIndex: 'name',
|
dataIndex: 'paramsName',
|
||||||
key: 'name',
|
key: 'paramsName',
|
||||||
|
width: 320
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '参数说明',
|
title: '参数说明',
|
||||||
dataIndex: 'description',
|
dataIndex: 'desc',
|
||||||
key: 'description',
|
key: 'desc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '请求类型',
|
title: '请求类型',
|
||||||
dataIndex: 'in',
|
dataIndex: 'in',
|
||||||
key: 'in',
|
key: 'in',
|
||||||
|
width: 80
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '是否必须',
|
title: '是否必须',
|
||||||
dataIndex: 'required',
|
dataIndex: 'required',
|
||||||
key: 'required',
|
key: 'required',
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
|
width: 80
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '参数类型',
|
title: '参数类型',
|
||||||
|
dataIndex: 'paramsType',
|
||||||
|
key: 'paramsType',
|
||||||
|
width: 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'schema',
|
||||||
dataIndex: 'type',
|
dataIndex: 'type',
|
||||||
key: 'type',
|
key: 'type',
|
||||||
scopedSlots: true,
|
width: 200
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
tableData: [],
|
tableData: [],
|
||||||
|
tips: [],
|
||||||
codeText: undefined,
|
codeText: undefined,
|
||||||
getData: () => {
|
getData: () => {
|
||||||
if (!props.selectApi.requestBody)
|
if (!props.selectApi.requestBody)
|
||||||
|
@ -159,27 +177,30 @@ const requestCard = reactive<cardType>({
|
||||||
const schemaName = _ref?.split('/').pop();
|
const schemaName = _ref?.split('/').pop();
|
||||||
const type = schema.type || '';
|
const type = schema.type || '';
|
||||||
const tableData = findData(props.schemas, schemaName);
|
const tableData = findData(props.schemas, schemaName);
|
||||||
requestCard.codeText =
|
// requestCard.codeText =
|
||||||
type === 'array'
|
// type === 'array'
|
||||||
? [getCodeText(props.schemas, tableData, 3)]
|
// ? [getCodeText(props.schemas, tableData, 3)]
|
||||||
: getCodeText(props.schemas, tableData, 3);
|
// : getCodeText(props.schemas, tableData, 3);
|
||||||
|
const { codeText, codeTips } = getCodeText(props.schemas, tableData, 3)
|
||||||
|
requestCard.codeText = JSON.stringify(codeText)
|
||||||
|
requestCard.tips = codeTips
|
||||||
requestCard.tableData = [
|
requestCard.tableData = [
|
||||||
{
|
{
|
||||||
name: schemaName[0].toLowerCase() + schemaName.substring(1),
|
paramsName: schemaName[0].toLowerCase() + schemaName.substring(1),
|
||||||
description: schemaName,
|
desc: schemaName,
|
||||||
in: 'body',
|
in: 'body',
|
||||||
|
id: randomString(),
|
||||||
required: true,
|
required: true,
|
||||||
schema: { type: type || schemaName },
|
paramsType: type || schemaName,
|
||||||
children: tableData.map((item) => ({
|
type: type || schemaName,
|
||||||
name: item.paramsName,
|
children: tableData,
|
||||||
description: item.desc,
|
|
||||||
required: false,
|
|
||||||
schema: { type: item.paramsType },
|
|
||||||
})),
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
// console.log(requestCard,'requestCard')
|
// console.log(requestCard,'requestCard')
|
||||||
}
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = true
|
||||||
|
}, 1000)
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const responseStatusCard = reactive<cardType>({
|
const responseStatusCard = reactive<cardType>({
|
||||||
|
@ -189,6 +210,7 @@ const responseStatusCard = reactive<cardType>({
|
||||||
title: '状态码',
|
title: '状态码',
|
||||||
dataIndex: 'code',
|
dataIndex: 'code',
|
||||||
key: 'code',
|
key: 'code',
|
||||||
|
width: 200
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '说明',
|
title: '说明',
|
||||||
|
@ -199,6 +221,7 @@ const responseStatusCard = reactive<cardType>({
|
||||||
title: 'schema',
|
title: 'schema',
|
||||||
dataIndex: 'schema',
|
dataIndex: 'schema',
|
||||||
key: 'schema',
|
key: 'schema',
|
||||||
|
width: 200
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
tableData: [],
|
tableData: [],
|
||||||
|
@ -231,6 +254,7 @@ const respParamsCard = reactive<cardType>({
|
||||||
{
|
{
|
||||||
title: '参数名称',
|
title: '参数名称',
|
||||||
dataIndex: 'paramsName',
|
dataIndex: 'paramsName',
|
||||||
|
width: 320
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '参数说明',
|
title: '参数说明',
|
||||||
|
@ -239,9 +263,16 @@ const respParamsCard = reactive<cardType>({
|
||||||
{
|
{
|
||||||
title: '类型',
|
title: '类型',
|
||||||
dataIndex: 'paramsType',
|
dataIndex: 'paramsType',
|
||||||
|
width: 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'schema',
|
||||||
|
dataIndex: 'schema',
|
||||||
|
width: 200
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
tableData: [],
|
tableData: [],
|
||||||
|
tips: [],
|
||||||
codeText: '',
|
codeText: '',
|
||||||
getData: (code: string) => {
|
getData: (code: string) => {
|
||||||
const schemaName = responseStatusCard.tableData.find(
|
const schemaName = responseStatusCard.tableData.find(
|
||||||
|
@ -249,11 +280,22 @@ const respParamsCard = reactive<cardType>({
|
||||||
)?.schema;
|
)?.schema;
|
||||||
|
|
||||||
const tableData = findData(props.schemas, schemaName);
|
const tableData = findData(props.schemas, schemaName);
|
||||||
respParamsCard.codeText = getCodeText(props.schemas, tableData, 3);
|
const { codeText, codeTips } = getCodeText(props.schemas, tableData, 3)
|
||||||
|
respParamsCard.codeText = JSON.stringify(codeText)
|
||||||
|
respParamsCard.tips = codeTips
|
||||||
respParamsCard.tableData = tableData;
|
respParamsCard.tableData = tableData;
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = true
|
||||||
|
}, 1000)
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
minimap: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const getContent = (data: any) => {
|
const getContent = (data: any) => {
|
||||||
if (data && data.content) {
|
if (data && data.content) {
|
||||||
return Object.keys(data.content || {})[0];
|
return Object.keys(data.content || {})[0];
|
||||||
|
@ -263,17 +305,18 @@ const getContent = (data: any) => {
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
requestCard.getData();
|
requestCard.getData();
|
||||||
responseStatusCard.getData();
|
responseStatusCard.getData();
|
||||||
watch(
|
});
|
||||||
() => props.selectApi,
|
|
||||||
() => {
|
|
||||||
requestCard.getData();
|
|
||||||
responseStatusCard.getData();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
watch([() => responseStatusCard.activeKey, () => props.selectApi], (n) => {
|
watch(
|
||||||
n[0] && respParamsCard.getData(n[0]);
|
() => props.selectApi,
|
||||||
});
|
() => {
|
||||||
|
requestCard.getData();
|
||||||
|
responseStatusCard.getData();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
watch([() => responseStatusCard.activeKey, () => props.selectApi], (n) => {
|
||||||
|
n[0] && respParamsCard.getData(n[0]);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="input">
|
<div class="input">
|
||||||
<InputCard :value="props.selectApi.method" />
|
<InputCard :value="props.selectApi.method" />
|
||||||
<j-input :value="props.selectApi?.url" disabled />
|
<j-input :value="props.selectApi?.url" disabled />
|
||||||
<span class="send" @click="send">发送</span>
|
<j-button type="primary" @click="send" :loading="loading">发送</j-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -107,14 +107,31 @@
|
||||||
<AIcon type="PlusOutlined" />新增
|
<AIcon type="PlusOutlined" />新增
|
||||||
</j-button>
|
</j-button>
|
||||||
</div>
|
</div>
|
||||||
<j-monaco-editor
|
<a-select v-model:value="bodyType" @change="handleChangeBodyType">
|
||||||
|
<a-select-option value="json">Json</a-select-option>
|
||||||
|
<a-select-option value="text">Text</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<template
|
||||||
v-if="showRequestBody"
|
v-if="showRequestBody"
|
||||||
ref="editorRef"
|
>
|
||||||
language="json"
|
<j-monaco-editor
|
||||||
style="height: 100% ; min-height: 200px;"
|
v-if="bodyType === 'json'"
|
||||||
theme="vs"
|
ref="editorRef"
|
||||||
v-model:modelValue="requestBody.code"
|
language="json"
|
||||||
/>
|
style="height: 100% ; min-height: 200px;"
|
||||||
|
theme="vs"
|
||||||
|
v-model:modelValue="requestBody.code"
|
||||||
|
/>
|
||||||
|
<j-monaco-editor
|
||||||
|
v-else
|
||||||
|
ref="editorRef"
|
||||||
|
language="text"
|
||||||
|
style="height: 100% ; min-height: 200px;"
|
||||||
|
theme="vs"
|
||||||
|
v-model:modelValue="requestBody.code"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="api-card">
|
<div class="api-card">
|
||||||
|
@ -145,6 +162,8 @@ const editorRef = ref();
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
const method = ref()
|
const method = ref()
|
||||||
const showRequestBody = ref(!!props.selectApi?.requestBody)
|
const showRequestBody = ref(!!props.selectApi?.requestBody)
|
||||||
|
const bodyType = ref('text');
|
||||||
|
const loading = ref(false);
|
||||||
const requestBody = reactive({
|
const requestBody = reactive({
|
||||||
tableColumns: [
|
tableColumns: [
|
||||||
{
|
{
|
||||||
|
@ -208,6 +227,11 @@ const init = () => {
|
||||||
};
|
};
|
||||||
init();
|
init();
|
||||||
|
|
||||||
|
const handleChangeBodyType = () => {
|
||||||
|
console.log(editorRef.value)
|
||||||
|
editorRef.value?.setModelLanguage(editorRef.value.getModel(), bodyType.value);
|
||||||
|
}
|
||||||
|
|
||||||
const send = () => {
|
const send = () => {
|
||||||
if (paramsTable.value.length)
|
if (paramsTable.value.length)
|
||||||
formRef.value &&
|
formRef.value &&
|
||||||
|
@ -240,16 +264,22 @@ const _send = () => {
|
||||||
...urlParams,
|
...urlParams,
|
||||||
};
|
};
|
||||||
}else{
|
}else{
|
||||||
params = JSON.parse(requestBody.code || '{}')
|
if(bodyType.value == 'text') {
|
||||||
|
params = requestBody.code
|
||||||
|
} else {
|
||||||
|
params = JSON.parse(requestBody.code || '{}')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
loading.value = true;
|
||||||
server[methodObj[methodName]](url, params).then((resp: any) => {
|
server[methodObj[methodName]](url, params, {}, bodyType.value === 'text' ? {headers: {'Content-Type': 'text/plain'}} : {}).then((resp: any) => {
|
||||||
// 如果用户没填写参数且有body的情况下,给用户展示请求示例
|
// 如果用户没填写参数且有body的情况下,给用户展示请求示例
|
||||||
if (Object.keys(params).length === 0 && refStr.value) {
|
if (Object.keys(params).length === 0 && refStr.value) {
|
||||||
requestBody.code = JSON.stringify(getDefaultParams());
|
requestBody.code = JSON.stringify(getDefaultParams());
|
||||||
editorRef.value?.editorFormat();
|
editorRef.value?.editorFormat();
|
||||||
}
|
}
|
||||||
responsesContent.value = resp;
|
responsesContent.value = resp;
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:dataSource="_tableData"
|
:dataSource="_tableData"
|
||||||
:rowSelection="props.mode !== 'home' ? rowSelection : undefined"
|
:rowSelection="props.mode !== 'home' ? rowSelection : undefined"
|
||||||
|
:style="{padding: 0}"
|
||||||
noPagination
|
noPagination
|
||||||
model="TABLE"
|
model="TABLE"
|
||||||
>
|
>
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
<template>
|
||||||
|
<div class="api-example">
|
||||||
|
<j-monaco-editor
|
||||||
|
v-if="loading"
|
||||||
|
language="json"
|
||||||
|
:model-value="codeText"
|
||||||
|
:init="init"
|
||||||
|
/>
|
||||||
|
<div class="api-example-tips" :style="{ transform: `translateY(${scrollTop}px)`}" v-if="loading">
|
||||||
|
<div class="tips-line" v-for="tip in tips">
|
||||||
|
<span v-if="tip">//{{tip}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="ApiMonaco">
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
tips: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
codeText: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const scrollTop = ref(0)
|
||||||
|
const init = (editor) => {
|
||||||
|
editor.onDidScrollChange(e => {
|
||||||
|
scrollTop.value = 0 - e.scrollTop
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.api-example {
|
||||||
|
height: 350px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.api-example-tips {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 19px;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
color: #608b4e;
|
||||||
|
|
||||||
|
.tips-line {
|
||||||
|
height: 19px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,76 +1,77 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="api-page-container">
|
<div class="api-page-container">
|
||||||
<div class="top">
|
<div class="top">
|
||||||
<slot name="top" />
|
<slot name="top" />
|
||||||
</div>
|
|
||||||
<j-row class="content" :style="{padding:'24px'}" >
|
|
||||||
<j-col
|
|
||||||
:span="24"
|
|
||||||
v-if="props.showTitle"
|
|
||||||
style="font-size: 16px;margin-bottom: 48px;"
|
|
||||||
>
|
|
||||||
API文档
|
|
||||||
</j-col>
|
|
||||||
<j-col :span="5" class="tree-content">
|
|
||||||
<LeftTree
|
|
||||||
@select="treeSelect"
|
|
||||||
:mode="props.mode"
|
|
||||||
:has-home="props.hasHome"
|
|
||||||
:code="props.code"
|
|
||||||
/>
|
|
||||||
</j-col>
|
|
||||||
<j-col :span="19">
|
|
||||||
<HomePage v-show="showHome" />
|
|
||||||
<div class="url-page" v-show="!showHome">
|
|
||||||
<ChooseApi
|
|
||||||
v-show="!selectedApi.url"
|
|
||||||
v-model:click-api="selectedApi"
|
|
||||||
v-model:selectedRowKeys="selectedKeys"
|
|
||||||
v-model:changedApis="changedApis"
|
|
||||||
:table-data="tableData"
|
|
||||||
:source-keys="selectSourceKeys"
|
|
||||||
:mode="props.mode"
|
|
||||||
@refresh="getSelectKeys"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="api-details"
|
|
||||||
v-if="selectedApi.url && tableData.length > 0"
|
|
||||||
>
|
|
||||||
<j-button
|
|
||||||
@click="selectedApi = initSelectedApi"
|
|
||||||
style="margin-bottom: 24px"
|
|
||||||
>返回</j-button
|
|
||||||
>
|
|
||||||
<j-tabs v-model:activeKey="activeKey" type="card">
|
|
||||||
<j-tab-pane key="does" tab="文档">
|
|
||||||
<ApiDoes
|
|
||||||
:select-api="selectedApi"
|
|
||||||
:schemas="schemas"
|
|
||||||
/>
|
|
||||||
</j-tab-pane>
|
|
||||||
<j-tab-pane key="test" tab="调试">
|
|
||||||
<ApiTest
|
|
||||||
:select-api="selectedApi"
|
|
||||||
:schemas="schemas"
|
|
||||||
/>
|
|
||||||
</j-tab-pane>
|
|
||||||
</j-tabs>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</j-col>
|
|
||||||
</j-row>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="api-page-content" :style="styles">
|
||||||
|
<div
|
||||||
|
v-if="props.showTitle"
|
||||||
|
style="font-size: 16px;margin-bottom: 48px;"
|
||||||
|
>
|
||||||
|
API文档
|
||||||
|
</div>
|
||||||
|
<div class="api-page-body">
|
||||||
|
<div class="tree-content">
|
||||||
|
<LeftTree
|
||||||
|
@select="treeSelect"
|
||||||
|
:mode="props.mode"
|
||||||
|
:has-home="props.hasHome"
|
||||||
|
:code="props.code"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="api-page-detail">
|
||||||
|
<HomePage v-show="showHome" />
|
||||||
|
<div class="url-page" v-show="!showHome">
|
||||||
|
<ChooseApi
|
||||||
|
v-show="!selectedApi.url"
|
||||||
|
v-model:click-api="selectedApi"
|
||||||
|
v-model:selectedRowKeys="selectedKeys"
|
||||||
|
v-model:changedApis="changedApis"
|
||||||
|
:table-data="tableData"
|
||||||
|
:source-keys="selectSourceKeys"
|
||||||
|
:mode="props.mode"
|
||||||
|
@refresh="getSelectKeys"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="api-details"
|
||||||
|
v-if="selectedApi.url && tableData.length > 0"
|
||||||
|
>
|
||||||
|
<j-button
|
||||||
|
@click="selectedApi = initSelectedApi"
|
||||||
|
style="margin-bottom: 24px; width: 80px">返回</j-button>
|
||||||
|
<div class="api-details-tabs">
|
||||||
|
<j-tabs v-model:activeKey="activeKey" type="card">
|
||||||
|
<j-tab-pane key="does" tab="文档">
|
||||||
|
<ApiDoes
|
||||||
|
:select-api="selectedApi"
|
||||||
|
:schemas="schemas"
|
||||||
|
/>
|
||||||
|
</j-tab-pane>
|
||||||
|
<j-tab-pane key="test" tab="调试">
|
||||||
|
<ApiTest
|
||||||
|
:select-api="selectedApi"
|
||||||
|
:schemas="schemas"
|
||||||
|
/>
|
||||||
|
</j-tab-pane>
|
||||||
|
</j-tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="apiPage">
|
<script setup lang="ts" name="apiPage">
|
||||||
import HomePage from './components/HomePage.vue';
|
import HomePage from './components/HomePage.vue';
|
||||||
import { getApiGranted_api, apiOperations_api } from '@/api/system/apiPage';
|
import { getApiGranted_api, apiOperations_api } from '@/api/system/apiPage';
|
||||||
import type {
|
import type {
|
||||||
treeNodeTpye,
|
treeNodeTpye,
|
||||||
apiObjType,
|
apiObjType,
|
||||||
apiDetailsType,
|
apiDetailsType,
|
||||||
modeType,
|
modeType,
|
||||||
} from './typing';
|
} from './typing';
|
||||||
import LeftTree from './components/LeftTree.vue';
|
import LeftTree from './components/LeftTree.vue';
|
||||||
import ChooseApi from './components/ChooseApi.vue';
|
import ChooseApi from './components/ChooseApi.vue';
|
||||||
|
@ -81,46 +82,52 @@ import { useDepartmentStore } from '@/store/department';
|
||||||
const department = useDepartmentStore();
|
const department = useDepartmentStore();
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
mode: modeType;
|
mode: modeType;
|
||||||
showTitle?: boolean;
|
showTitle?: boolean;
|
||||||
hasHome?: boolean;
|
hasHome?: boolean;
|
||||||
code?: string;
|
code?: string;
|
||||||
}>();
|
}>();
|
||||||
const showHome = ref<boolean>(Boolean(props.hasHome)); // 是否展示home页面
|
const showHome = ref<boolean>(Boolean(props.hasHome)); // 是否展示home页面
|
||||||
const tableData = ref([]);
|
const tableData = ref([]);
|
||||||
|
|
||||||
|
const styles = computed(() => {
|
||||||
|
return {
|
||||||
|
padding: props.mode === 'api' ? 0 : '24px'
|
||||||
|
}
|
||||||
|
})
|
||||||
const treeSelect = (node: treeNodeTpye, nodeSchemas: object = {}) => {
|
const treeSelect = (node: treeNodeTpye, nodeSchemas: object = {}) => {
|
||||||
if (node.key === 'home') return (showHome.value = true);
|
if (node.key === 'home') return (showHome.value = true);
|
||||||
schemas.value = nodeSchemas;
|
schemas.value = nodeSchemas;
|
||||||
if (!node.apiList) return;
|
if (!node.apiList) return;
|
||||||
showHome.value = false;
|
showHome.value = false;
|
||||||
const apiList: apiObjType[] = node.apiList as apiObjType[];
|
const apiList: apiObjType[] = node.apiList as apiObjType[];
|
||||||
const table: any = [];
|
const table: any = [];
|
||||||
// 将对象形式的数据转换为表格需要的形式
|
// 将对象形式的数据转换为表格需要的形式
|
||||||
apiList?.forEach((apiItem) => {
|
apiList?.forEach((apiItem) => {
|
||||||
const { method, url } = apiItem as any;
|
const { method, url } = apiItem as any;
|
||||||
for (const key in method) {
|
for (const key in method) {
|
||||||
if (Object.prototype.hasOwnProperty.call(method, key)) {
|
if (Object.prototype.hasOwnProperty.call(method, key)) {
|
||||||
table.push({
|
table.push({
|
||||||
...method[key],
|
...method[key],
|
||||||
url,
|
url,
|
||||||
method: key,
|
method: key,
|
||||||
id: method[key].operationId,
|
id: method[key].operationId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
tableData.value = table;
|
tableData.value = table;
|
||||||
};
|
};
|
||||||
|
|
||||||
const activeKey = ref<'does' | 'test'>('does');
|
const activeKey = ref<'does' | 'test'>('does');
|
||||||
const schemas = ref({}); // 对应一级api相关的类
|
const schemas = ref({}); // 对应一级api相关的类
|
||||||
const initSelectedApi: apiDetailsType = {
|
const initSelectedApi: apiDetailsType = {
|
||||||
url: '',
|
url: '',
|
||||||
method: '',
|
method: '',
|
||||||
summary: '',
|
summary: '',
|
||||||
parameters: [],
|
parameters: [],
|
||||||
responses: {},
|
responses: {},
|
||||||
requestBody: {},
|
requestBody: {},
|
||||||
};
|
};
|
||||||
const selectedApi = ref<apiDetailsType>(initSelectedApi);
|
const selectedApi = ref<apiDetailsType>(initSelectedApi);
|
||||||
|
|
||||||
|
@ -131,62 +138,95 @@ const changedApis = ref({}); // 勾选发生变化的项,以对应的id作为k
|
||||||
init();
|
init();
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
getSelectKeys();
|
getSelectKeys();
|
||||||
watch(tableData, () => {
|
watch(tableData, () => {
|
||||||
activeKey.value = 'does';
|
activeKey.value = 'does';
|
||||||
selectedApi.value = initSelectedApi;
|
selectedApi.value = initSelectedApi;
|
||||||
});
|
});
|
||||||
watch(
|
watch(
|
||||||
() => selectedApi.value.url,
|
() => selectedApi.value.url,
|
||||||
() => (activeKey.value = 'does'),
|
() => (activeKey.value = 'does'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 右侧api选中项
|
* 右侧api选中项
|
||||||
*/
|
*/
|
||||||
function getSelectKeys() {
|
function getSelectKeys() {
|
||||||
if (props.mode === 'appManger') {
|
if (props.mode === 'appManger') {
|
||||||
getApiGranted_api(props.code as string).then((resp) => {
|
getApiGranted_api(props.code as string).then((resp) => {
|
||||||
selectedKeys.value = resp.result as string[];
|
selectedKeys.value = resp.result as string[];
|
||||||
selectSourceKeys.value = [...(resp.result as string[])];
|
selectSourceKeys.value = [...(resp.result as string[])];
|
||||||
changedApis.value = {};
|
changedApis.value = {};
|
||||||
});
|
});
|
||||||
} else if (props.mode === 'api') {
|
} else if (props.mode === 'api') {
|
||||||
apiOperations_api().then((resp) => {
|
apiOperations_api().then((resp) => {
|
||||||
selectedKeys.value = resp.result as string[];
|
selectedKeys.value = resp.result as string[];
|
||||||
selectSourceKeys.value = [...(resp.result as string[])];
|
selectSourceKeys.value = [...(resp.result as string[])];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => selectedKeys.value,
|
() => selectedKeys.value,
|
||||||
(val: any) => {
|
(val: any) => {
|
||||||
// console.log('selectedKeys: ', val);
|
// console.log('selectedKeys: ', val);
|
||||||
department.setSelectedKeys(val);
|
department.setSelectedKeys(val);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
watch(
|
watch(
|
||||||
() => changedApis.value,
|
() => changedApis.value,
|
||||||
(val: any) => {
|
(val: any) => {
|
||||||
// console.log('changedApis: ', val);
|
// console.log('changedApis: ', val);
|
||||||
department.setChangedApis(val);
|
department.setChangedApis(val);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.api-page-container {
|
.api-page-container {
|
||||||
.content {
|
height: 100%;
|
||||||
background-color: #fff;
|
|
||||||
margin: 0 !important;
|
.api-page-content {
|
||||||
.tree-content {
|
background-color: #fff;
|
||||||
padding-bottom: 30px;
|
margin: 0 !important;
|
||||||
height: calc(100vh - 230px);
|
|
||||||
overflow-y: auto;
|
.api-page-body {
|
||||||
border-right: 1px solid #e9e9e9;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.tree-content {
|
||||||
|
height: calc(100vh - 230px);
|
||||||
|
width: 280px;
|
||||||
|
overflow-y: auto;
|
||||||
|
border-right: 1px solid #e9e9e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-page-detail {
|
||||||
|
position: absolute;
|
||||||
|
left: 296px;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
.url-page {
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.api-details {
|
||||||
|
max-height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.api-details-tabs {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -34,6 +34,11 @@ export type modeType = 'api'| 'appManger' | 'home'
|
||||||
export type schemaObjType = {
|
export type schemaObjType = {
|
||||||
paramsName: string;
|
paramsName: string;
|
||||||
paramsType: string;
|
paramsType: string;
|
||||||
|
id: string;
|
||||||
|
type?: string;
|
||||||
|
schema?: string;
|
||||||
|
|
||||||
|
required?: boolean
|
||||||
desc?: string;
|
desc?: string;
|
||||||
children?: schemaObjType[];
|
children?: schemaObjType[];
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { schemaObjType } from "./typing";
|
import { schemaObjType } from "./typing";
|
||||||
|
import {randomString} from "@/utils/utils";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -8,28 +9,38 @@ import { schemaObjType } from "./typing";
|
||||||
*/
|
*/
|
||||||
export function findData(schemas: object, schemaName: string , paths:string[]=[]) {
|
export function findData(schemas: object, schemaName: string , paths:string[]=[]) {
|
||||||
const basicType = ['string', 'integer', 'boolean','number'];
|
const basicType = ['string', 'integer', 'boolean','number'];
|
||||||
|
|
||||||
if (!schemaName || !schemas[schemaName]) {
|
if (!schemaName || !schemas[schemaName]) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const result: schemaObjType[] = [];
|
const result: schemaObjType[] = [];
|
||||||
const schema = schemas[schemaName];
|
const schema = schemas[schemaName];
|
||||||
|
const required = schema.required || []
|
||||||
|
|
||||||
Object.entries(schema.properties).forEach((item: [string, any]) => {
|
Object.entries(schema.properties).forEach((item: [string, any]) => {
|
||||||
|
const [paramsName, extra] = item
|
||||||
|
|
||||||
const paramsType =
|
const paramsType =
|
||||||
(item[1].$ref && item[1].$ref.split('/').pop()) ||
|
(extra.$ref && extra.$ref.split('/').pop()) ||
|
||||||
(item[1].items?.$ref && item[1].items.$ref.split('/').pop()) ||
|
(extra.items?.$ref && extra.items.$ref.split('/').pop()) ||
|
||||||
item[1].item?.type ||
|
extra.item?.type ||
|
||||||
item[1].type ||
|
extra.type ||
|
||||||
'';
|
'';
|
||||||
|
const schema = extra.format ? `${paramsType}(${extra.format})` : ''
|
||||||
|
|
||||||
const schemaObj: schemaObjType = {
|
const schemaObj: schemaObjType = {
|
||||||
paramsName: item[0],
|
paramsName: paramsName,
|
||||||
paramsType,
|
paramsType,
|
||||||
desc: item[1].description || '',
|
required: required.includes(paramsName),
|
||||||
|
desc: extra.description || '',
|
||||||
|
schema: schema,
|
||||||
|
type: !basicType.includes(paramsType) ? paramsType : '',
|
||||||
|
id: randomString()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!basicType.includes(paramsType) && paths.filter(path=>path === schemaName).length >=2 ){
|
if (!basicType.includes(paramsType) && (paths.filter(path=>path === schemaName).length <=2) ){
|
||||||
paths.push(schemaName)
|
paths.push(schemaName)
|
||||||
schemaObj.children = findData(schemas, paramsType);
|
schemaObj.children = findData(schemas, paramsType, paths);
|
||||||
}
|
}
|
||||||
result.push(schemaObj);
|
result.push(schemaObj);
|
||||||
});
|
});
|
||||||
|
@ -47,54 +58,89 @@ export function getCodeText(
|
||||||
schemas: object,
|
schemas: object,
|
||||||
arr: schemaObjType[],
|
arr: schemaObjType[],
|
||||||
level: number,
|
level: number,
|
||||||
): object {
|
paths: string[] = []
|
||||||
const result = {};
|
): any {
|
||||||
|
const tips: Array<string | undefined> = []
|
||||||
|
let result = {}
|
||||||
arr.forEach((item) => {
|
arr.forEach((item) => {
|
||||||
|
let value: any = ""
|
||||||
|
tips.push(item.desc)
|
||||||
switch (item.paramsType) {
|
switch (item.paramsType) {
|
||||||
case 'string':
|
case 'string':
|
||||||
result[item.paramsName] = '';
|
value = ''
|
||||||
break;
|
break;
|
||||||
case 'integer':
|
case 'integer':
|
||||||
result[item.paramsName] = 0;
|
case 'number':
|
||||||
|
value = 0
|
||||||
break;
|
break;
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
result[item.paramsName] = true;
|
value = true
|
||||||
break;
|
break;
|
||||||
case 'array':
|
case 'array':
|
||||||
result[item.paramsName] = [];
|
value = []
|
||||||
break;
|
break;
|
||||||
case 'object':
|
case 'object':
|
||||||
result[item.paramsName] = '';
|
value = {}
|
||||||
break;
|
|
||||||
case 'number':
|
|
||||||
result[item.paramsName] = 0;
|
|
||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
const properties = schemas[item.paramsType]?.properties as object || {};
|
if (item.children) {
|
||||||
const newArr = Object.entries(properties).map(
|
if (paths.filter(path=> path === item.paramsName).length >=2) {
|
||||||
(item: [string, any]) => {
|
break
|
||||||
return{
|
}
|
||||||
paramsName: item[0],
|
paths.push(item.paramsName)
|
||||||
paramsType: level
|
const _result = getCodeText(
|
||||||
? (item[1].$ref && item[1].$ref.split('/').pop()) ||
|
schemas,
|
||||||
(item[1].items?.$ref &&
|
item.children,
|
||||||
item[1].items.$ref.split('/').pop()) ||
|
level - 1,
|
||||||
item[1].item?.type ||
|
paths
|
||||||
item[1].type ||
|
)
|
||||||
''
|
value = _result.codeText
|
||||||
: item[1].type,
|
tips.push(..._result.codeTips)
|
||||||
}},
|
tips.push(undefined)
|
||||||
);
|
} else {
|
||||||
result[item.paramsName] = getCodeText(
|
if (paths.filter(path=> path === item.paramsName).length >=2) {
|
||||||
schemas,
|
break
|
||||||
newArr,
|
}
|
||||||
level - 1,
|
paths.push(item.paramsName)
|
||||||
);
|
const properties = schemas[item.paramsType]?.properties as object || {};
|
||||||
|
const newArr = Object.entries(properties).map(
|
||||||
|
(item: [string, any]) => {
|
||||||
|
const [paramsName, extra] = item
|
||||||
|
|
||||||
|
const paramsType =
|
||||||
|
(extra.$ref && extra.$ref.split('/').pop()) ||
|
||||||
|
(extra.items?.$ref && extra.items.$ref.split('/').pop()) ||
|
||||||
|
extra.item?.type ||
|
||||||
|
extra.type ||
|
||||||
|
'';
|
||||||
|
|
||||||
|
return{
|
||||||
|
paramsName: paramsName,
|
||||||
|
desc: extra.description || '',
|
||||||
|
paramsType: paramsType,
|
||||||
|
schema: extra.format ? `${paramsType}(${extra.format})` : ''
|
||||||
|
}},
|
||||||
|
);
|
||||||
|
|
||||||
|
const _result = getCodeText(
|
||||||
|
schemas,
|
||||||
|
newArr,
|
||||||
|
level - 1,
|
||||||
|
paths
|
||||||
|
)
|
||||||
|
value = _result.codeText
|
||||||
|
tips.push(..._result.codeTips)
|
||||||
|
tips.push(undefined)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
result[item.paramsName] = value
|
||||||
});
|
});
|
||||||
|
return {
|
||||||
|
codeText: result,
|
||||||
|
codeTips: tips
|
||||||
|
|
||||||
return result;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -124,4 +170,4 @@ export function dealNoRef(type:string,schema?:any):any{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue